Premier commit

This commit is contained in:
2026-09-11 14:55:41 +02:00
commit 8ed04aae89
134 changed files with 36627 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using SIFinance.Api;
using SIFinance.Modules.Achats.Endpoints;
using SIFinance.Modules.Finance.Endpoints;
using SIFinance.Modules.SousTraitants.Endpoints;
using SIFinance.Shared;
using SIFinance.Shared.Entities;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddDbContext<SIFinanceDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddHostedService<DbKeepAliveService>();
builder.Services.AddHttpClient();
builder.Services.Configure<AzureAdOptions>(builder.Configuration.GetSection(AzureAdOptions.SectionName));
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection(JwtOptions.SectionName));
builder.Services.AddSingleton<EntraGraphTokenProvider>();
builder.Services.AddSingleton<JwtTokenService>();
builder.Services.AddHostedService<EntraSyncService>();
// Valide le jeton "maison" émis par SIFinance.Api après l'échange OAuth2 avec Entra ID
// (voir EntraAuthEndpoints) — pas de validation directe de jeton Entra ID : le backend
// pilote lui-même le flux de connexion, comme l'appli NDF existante (JWT maison +
// synchro périodique du groupe Entra ID via EntraSyncService), plutôt que MSAL.js +
// Microsoft.Identity.Web côté frontend.
var jwtSecret = builder.Configuration["Jwt:Secret"]
?? throw new InvalidOperationException("Configuration Jwt:Secret manquante — voir appsettings.Development.json.");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(jwtSecret)),
};
});
builder.Services.AddAuthorization();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<CurrentUserService>();
builder.Services.Configure<SharePointOptions>(builder.Configuration.GetSection(SharePointOptions.SectionName));
// Pas de client Graph enregistré directement en DI : tant que TenantId/ClientId/
// ClientSecret ne sont pas renseignés (ou invalides), ClientSecretCredential lève une
// ArgumentException dès sa construction — avant même le premier appel Graph. Si le
// client était un singleton construit par le conteneur DI, cette exception surviendrait
// pendant la résolution de la requête, hors de portée d'un try/catch dans l'endpoint,
// et remonterait comme une erreur 500 brute. SharePointGraphClientFactory construit le
// client à la demande, à l'intérieur du bloc try/catch de l'endpoint.
builder.Services.AddSingleton<SharePointGraphClientFactory>();
builder.Services.AddSingleton<SharePointUploadService>();
builder.Services.AddSingleton<NotificationEmailService>();
const string FrontendCorsPolicy = "Frontend";
builder.Services.AddCors(options =>
{
options.AddPolicy(FrontendCorsPolicy, policy =>
policy.WithOrigins(
"http://localhost:5173", "http://localhost:5174",
"http://127.0.0.1:5173", "http://127.0.0.1:5174")
.AllowAnyHeader()
.AllowAnyMethod()
// Le navigateur ne mémorise le résultat d'un preflight OPTIONS que sur la durée
// indiquée ici (par défaut ~5s) : on l'étend pour éviter un aller-retour OPTIONS
// avant chaque POST/PUT/PATCH répété (création/édition) pendant la session.
.SetPreflightMaxAge(TimeSpan.FromMinutes(30)));
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
if (!app.Environment.IsDevelopment())
{
app.UseHttpsRedirection();
}
app.UseCors(FrontendCorsPolicy);
app.UseAuthentication();
app.UseAuthorization();
app.MapAchatsEndpoints();
app.MapContratsEndpoints();
app.MapBudgetsEndpoints();
app.MapFacturesEndpoints();
app.MapAvoirsEndpoints();
app.MapPaiementsEndpoints();
app.MapDashboardEndpoints();
app.MapSharePointEndpoints();
app.MapEntraAuthEndpoints();
app.MapOrganismesEndpoints();
app.MapContratsPrestatairesEndpoints();
app.Run();