using PerfReviewSummarizer.Api.Models; using PerfReviewSummarizer.Api.Services; var builder = WebApplication.CreateBuilder(args); // Добавляем сервисы builder.Services.AddScoped(); builder.Services.AddHttpClient(); builder.Services.AddHttpClient(); builder.Services.AddSingleton(); builder.Services.AddScoped(sp => sp.GetRequiredService().CreateAiService()); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddOpenApi(); builder.Services.AddSwaggerGen(); builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); // Настройка статических файлов app.UseStaticFiles(); // Настройка Swagger app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "PerfReviewSummarizer API v1"); c.RoutePrefix = "swagger"; // Swagger UI на /swagger }); app.UseCors(); // Эндпоинт для получения summary app.MapPost("/api/summary", async (SummaryRequest request, IGitService gitService, IConfiguration configuration) => { try { var repositoryPath = request.RepositoryPath ?? configuration["GitRepository:DefaultPath"] ?? "."; DateTime? startDate = request.StartDate; DateTime? endDate = request.EndDate; // Если указан период, вычисляем даты if (!string.IsNullOrEmpty(request.Period) && !startDate.HasValue && !endDate.HasValue) { var now = DateTime.Now; if (request.Period.ToLower() == "quarter") { // Текущий квартал var quarter = (now.Month - 1) / 3; startDate = new DateTime(now.Year, quarter * 3 + 1, 1); endDate = startDate.Value.AddMonths(3).AddDays(-1); } else if (request.Period.ToLower() == "halfyear") { // Текущее полугодие var half = (now.Month - 1) / 6; startDate = new DateTime(now.Year, half * 6 + 1, 1); endDate = startDate.Value.AddMonths(6).AddDays(-1); } } var summary = await gitService.GenerateSummaryAsync(repositoryPath, startDate, endDate); return Results.Ok(summary); } catch (DirectoryNotFoundException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (Exception ex) { return Results.Problem($"Ошибка при обработке запроса: {ex.Message}"); } }) .WithName("GetSummary") .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); // Эндпоинт для получения списка коммитов app.MapGet("/api/commits", async (string? repositoryPath, DateTime? startDate, DateTime? endDate, IGitService gitService, IConfiguration configuration) => { try { var repoPath = repositoryPath ?? configuration["GitRepository:DefaultPath"] ?? "."; var commits = await gitService.GetCommitsAsync(repoPath, startDate, endDate); return Results.Ok(commits); } catch (DirectoryNotFoundException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (Exception ex) { return Results.Problem($"Ошибка при обработке запроса: {ex.Message}"); } }) .WithName("GetCommits") .Produces>(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); // Эндпоинт для получения AI summary (OpenAI или Gemini в зависимости от конфигурации) app.MapPost("/api/summary/ai", async (OpenAiSummaryRequest request, IGitService gitService, IAiService aiService, IConfiguration configuration) => { try { var repositoryPath = request.RepositoryPath ?? configuration["GitRepository:DefaultPath"] ?? "."; DateTime? startDate = request.StartDate; DateTime? endDate = request.EndDate; // Если указан период, вычисляем даты if (!string.IsNullOrEmpty(request.Period) && !startDate.HasValue && !endDate.HasValue) { var now = DateTime.Now; if (request.Period.ToLower() == "quarter") { var quarter = (now.Month - 1) / 3; startDate = new DateTime(now.Year, quarter * 3 + 1, 1); endDate = startDate.Value.AddMonths(3).AddDays(-1); } else if (request.Period.ToLower() == "halfyear") { var half = (now.Month - 1) / 6; startDate = new DateTime(now.Year, half * 6 + 1, 1); endDate = startDate.Value.AddMonths(6).AddDays(-1); } } // Получаем коммиты var commits = await gitService.GetCommitsAsync(repositoryPath, startDate, endDate); if (!commits.Any()) { return Results.Ok(new OpenAiSummaryResponse { Summary = "Нет коммитов за указанный период для анализа", CommitsCount = 0, PeriodInfo = new PeriodInfo { StartDate = startDate ?? DateTime.MinValue, EndDate = endDate ?? DateTime.MaxValue, Description = "Период без коммитов" } }); } var commitMessages = commits.Select(c => c.Message).ToList(); var aiSummary = await aiService.GenerateSummaryFromCommitMessagesAsync(commitMessages); return Results.Ok(new OpenAiSummaryResponse { Summary = aiSummary, CommitsCount = commits.Count, CommitMessages = commitMessages, PeriodInfo = new PeriodInfo { StartDate = startDate ?? commits.Min(c => c.When.DateTime), EndDate = endDate ?? commits.Max(c => c.When.DateTime), Description = $"С {startDate?.ToString("yyyy-MM-dd") ?? "начала"} по {endDate?.ToString("yyyy-MM-dd") ?? "конца"}" } }); } catch (DirectoryNotFoundException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (InvalidOperationException ex) { return Results.BadRequest(new { error = ex.Message }); } catch (Exception ex) { return Results.Problem($"Ошибка при обработке запроса: {ex.Message}"); } }) .WithName("GetAiSummary") .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); // Health check app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow })) .WithName("HealthCheck") .Produces(StatusCodes.Status200OK); // Корневой эндпоинт - отдаем HTML страницу app.MapGet("/", () => { var htmlPath = Path.Combine(app.Environment.WebRootPath ?? "", "index.html"); if (File.Exists(htmlPath)) { return Results.File(htmlPath, "text/html"); } return Results.Ok(new { message = "PerfReviewSummarizer API", version = "1.0.0", endpoints = new[] { "POST /api/summary - Получить summary за период", "GET /api/commits?repositoryPath=&startDate=&endDate= - Получить список коммитов", "GET /health - Health check", "GET /swagger - Swagger UI" } }); }) .WithName("Root"); app.Run();