Files
UniVerse/backend/UniVerse.Api/Program.cs
T
serega404 f18abbca3a
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 12s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 2m56s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 15s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 8s
feat: Добавил генерацию api документации для git
2026-05-13 17:06:51 +03:00

208 lines
6.9 KiB
C#

using System.Text;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi;
using Quartz;
using Serilog;
using UniVerse.Api.BackgroundServices;
using UniVerse.Api.Filters;
using UniVerse.Api.Middleware;
using UniVerse.Application.Interfaces;
using UniVerse.Infrastructure.Services;
using UniVerse.Infrastructure.Data;
using UniVerse.Infrastructure.ExternalServices;
using UniVerse.Infrastructure.Notifications;
var builder = WebApplication.CreateBuilder(args);
var useAspire = builder.Configuration.GetValue<bool>("Aspire:Enabled");
var isOpenApiGeneration = AppDomain.CurrentDomain.GetAssemblies()
.Any(assembly => assembly.GetName().Name == "GetDocument.Insider");
if (useAspire)
{
builder.AddServiceDefaults();
}
// --- Serilog ---
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
builder.Host.UseSerilog();
// --- DbContext ---
builder.Services.AddDbContext<AppDbContext>(options =>
{
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"),
npgsql =>
{
npgsql.EnableRetryOnFailure(3);
npgsql.MigrationsAssembly("UniVerse.Infrastructure"); // Указывает EF Core, в какой сборке искать/хранить миграции.
});
});
// --- Authentication ---
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"]!))
};
});
builder.Services.AddAuthorization();
// --- CORS ---
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins(
builder.Configuration.GetSection("Cors:Origins").Get<string[]>()
?? ["http://localhost:5173", "http://localhost:3000"])
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
// --- Services DI ---
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<ITagService, TagService>();
builder.Services.AddScoped<ILocationService, LocationService>();
builder.Services.AddScoped<ICourseService, CourseService>();
builder.Services.AddScoped<ILectureService, LectureService>();
builder.Services.AddScoped<IReviewService, ReviewService>();
builder.Services.AddScoped<IGamificationService, GamificationService>();
builder.Services.AddScoped<IAchievementService, AchievementService>();
builder.Services.AddScoped<ILlmAnalysisService, LlmAnalysisService>();
builder.Services.AddScoped<IScheduleSyncService, ScheduleSyncService>();
builder.Services.AddScoped<INotificationService, NotificationService>();
builder.Services.AddScoped<INotificationProvider, EmailNotificationProvider>();
builder.Services.AddSingleton<INotificationScheduler, QuartzNotificationScheduler>();
builder.Services.AddTransient<NotificationJob>();
builder.Services.Configure<EmailNotificationOptions>(builder.Configuration.GetSection("Email:Smtp"));
builder.Services.AddQuartz();
if (!isOpenApiGeneration)
{
builder.Services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
}
// --- HTTP Clients ---
builder.Services.AddHttpClient<ILlmClient, LlmClient>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["Llm:BaseUrl"] ?? "https://api.openai.com/v1/");
client.Timeout = TimeSpan.FromSeconds(60);
});
builder.Services.AddHttpClient<IModeusApiClient, ModeusApiClient>(client =>
{
client.BaseAddress = new Uri(builder.Configuration["ModeusApi:BaseUrl"] ?? "https://schedule.rdcenter.ru");
client.Timeout = TimeSpan.FromSeconds(30);
});
// --- Background Services ---
if (!isOpenApiGeneration)
{
builder.Services.AddHostedService<LlmProcessingBackgroundService>();
builder.Services.AddHostedService<AchievementCatalogHostedService>();
}
// --- Controllers ---
builder.Services.AddControllers()
.AddJsonOptions(o =>
{
o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
o.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
// --- Swagger ---
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "UniVerse API",
Version = "v1",
Description =
"REST API веб-платформы UniVerse.\n\n" +
"Аутентификация: JWT Bearer (получить через `POST /api/v1/auth/login/microsoft` или `POST /api/v1/auth/login/dev` в Development).",
Contact = new OpenApiContact
{
Name = "UniVerse Dev"
}
});
// Bearer security scheme definition (used per-endpoint by AuthorizeOperationFilter)
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "Введите JWT access token, полученный из `/api/v1/auth/login/microsoft`.\n\nПример: `eyJhbGci...`"
});
// Include XML doc comments generated from controller /// summaries
var xmlFile = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
options.IncludeXmlComments(xmlPath);
// Per-endpoint security requirement + role documentation (replaces global AddSecurityRequirement)
options.OperationFilter<AuthorizeOperationFilter>();
});
var app = builder.Build();
if (useAspire)
{
app.MapDefaultEndpoints();
}
// --- Middleware Pipeline ---
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseMiddleware<ExceptionHandlingMiddleware>();
if (app.Environment.IsDevelopment())
{
app.UseSwagger(c =>
{
c.RouteTemplate = "api/docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "api/docs";
c.SwaggerEndpoint("v1/swagger.json", "UniVerse API v1");
});
}
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();