Files
SchotzIAD/backend/SchotzIadKmTracker.Api/Program.cs
T
MadJax 0e6a97ca61 Renommage : TrajetKmTracker -> SchotzIadKmTracker (backend, specs, frontend)
- Backend : projets, solution, namespaces et références
  TrajetKmTracker.Api/.Tests -> SchotzIadKmTracker.Api/.Tests
  (dossiers, .csproj, .sln, .http, namespaces C#). GUID UserSecretsId préservé.
- Specs : dossier .kiro/specs/trajet-km-tracker -> schotziad-km-tracker
  et mise à jour des références internes (chemins, tags de propriétés).
- Frontend : projet Angular trajet-km-tracker -> schotziad-km-tracker
  (package.json, angular.json, chemin dist, Dockerfile), titre applicatif
  'SchotzIAD KM Tracker'.
- Docker et scripts (backend/Dockerfile, frontend/Dockerfile, run-dev.sh) mis à jour.
- Suppression du fichier résiduel package.json.bak.

Backend (solution) et frontend (build production) compilent sans erreur.
2026-09-14 07:50:26 +02:00

175 lines
6.6 KiB
C#

using FluentValidation;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SchotzIadKmTracker.Api.Data;
using SchotzIadKmTracker.Api.Validators;
using SchotzIadKmTracker.Api.Configuration;
using SchotzIadKmTracker.Api.Repositories;
using SchotzIadKmTracker.Api.Services;
// Licence QuestPDF (usage communautaire, gratuit) — requise avant toute génération de PDF.
QuestPDF.Settings.License = QuestPDF.Infrastructure.LicenseType.Community;
var builder = WebApplication.CreateBuilder(args);
// --- Options de configuration liées depuis appsettings.json ---
builder.Services.Configure<BANOptions>(
builder.Configuration.GetSection(BANOptions.SectionName));
builder.Services.Configure<ORSOptions>(
builder.Configuration.GetSection(ORSOptions.SectionName));
// --- Client typé BAN (Base Adresses Nationale) ---
// BaseAddress et Timeout proviennent de BANOptions (appsettings.json).
var banOptions = builder.Configuration
.GetSection(BANOptions.SectionName)
.Get<BANOptions>() ?? new BANOptions();
builder.Services.AddHttpClient<IBANClient, BANClient>(client =>
{
client.BaseAddress = new Uri(banOptions.BaseUrl);
client.Timeout = TimeSpan.FromSeconds(banOptions.TimeoutSeconds);
});
// --- Client typé ORS (OpenRouteService) ---
// BaseAddress, Timeout et en-tête de clé d'API proviennent de ORSOptions (appsettings.json).
var orsOptions = builder.Configuration
.GetSection(ORSOptions.SectionName)
.Get<ORSOptions>() ?? new ORSOptions();
builder.Services.AddHttpClient<IORSClient, ORSClient>(client =>
{
if (!string.IsNullOrWhiteSpace(orsOptions.BaseUrl))
{
// Le slash final garantit la résolution correcte des chemins relatifs.
var baseUrl = orsOptions.BaseUrl.EndsWith('/')
? orsOptions.BaseUrl
: orsOptions.BaseUrl + "/";
client.BaseAddress = new Uri(baseUrl);
}
client.Timeout = TimeSpan.FromSeconds(orsOptions.TimeoutSeconds);
if (!string.IsNullOrWhiteSpace(orsOptions.ApiKey))
{
// ORS attend la clé brute dans l'en-tête Authorization (sans schéma « Bearer »).
// Les clés ORS (JWT) contiennent des caractères refusés par la validation stricte
// de DefaultRequestHeaders.Add ; on utilise TryAddWithoutValidation pour l'insérer
// telle quelle et éviter une FormatException à la création du client.
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", orsOptions.ApiKey);
}
});
// --- Calculateur de distance (orchestration BAN + ORS + fallback Haversine) ---
builder.Services.AddScoped<IDistanceCalculator, DistanceCalculatorService>();
// --- Cache des adresses géocodées ---
builder.Services.AddScoped<IAdresseCacheRepository, AdresseCacheRepository>();
builder.Services.AddScoped<IAdresseCacheService, AdresseCacheService>();
// --- Services de base ---
builder.Services.AddControllers();
// --- Health checks (endpoint GET /health, utilisé par Docker Compose) ---
builder.Services.AddHealthChecks();
// --- Validation des requêtes (FluentValidation) ---
// Enregistre tous les validateurs de l'assembly et active la validation automatique côté MVC.
builder.Services.AddValidatorsFromAssemblyContaining<CreateTrajetDtoValidator>();
builder.Services.AddFluentValidationAutoValidation();
// --- Base de données (EF Core + SQLite) ---
// La chaîne de connexion provient de la configuration ("ConnectionStrings:Default").
var connectionString = builder.Configuration.GetConnectionString("Default")
?? "Data Source=trajets.db";
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(connectionString));
// --- Repositories & services métier ---
builder.Services.AddScoped<SchotzIadKmTracker.Api.Repositories.IVehiculeRepository,
SchotzIadKmTracker.Api.Repositories.VehiculeRepository>();
builder.Services.AddScoped<IVehiculeService, VehiculeService>();
builder.Services.AddScoped<IProfilService, ProfilService>();
builder.Services.AddScoped<IAdresseNommeeService, AdresseNommeeService>();
builder.Services.AddScoped<IBaremeService, BaremeService>();
builder.Services.AddScoped<IMontantRegleService, MontantRegleService>();
builder.Services.AddScoped<IRemboursementService, RemboursementService>();
builder.Services.AddScoped<ITotauxService, TotauxService>();
builder.Services.AddScoped<ITrajetRepository, TrajetRepository>();
builder.Services.AddScoped<ITrajetService, TrajetService>();
builder.Services.AddScoped<IResumeService, ResumeService>();
builder.Services.AddScoped<IPdfService, PdfService>();
// Exploration des endpoints + Swagger / OpenAPI
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "SchotzIAD KM Tracker API",
Version = "v1",
Description = "API de saisie, calcul et consultation des trajets kilométriques."
});
});
// ProblemDetails : réponses d'erreur normalisées (RFC 7807)
builder.Services.AddProblemDetails();
// CORS pour le frontend Angular.
// Les origines autorisées sont configurables via appsettings ("Cors:AllowedOrigins").
const string CorsPolicyName = "AngularFrontend";
var allowedOrigins = builder.Configuration
.GetSection("Cors:AllowedOrigins")
.Get<string[]>() ?? new[] { "http://localhost:4200" };
builder.Services.AddCors(options =>
{
options.AddPolicy(CorsPolicyName, policy =>
{
policy.WithOrigins(allowedOrigins)
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
// --- Pipeline HTTP ---
// Gestion centralisée des exceptions -> ProblemDetails
app.UseExceptionHandler();
app.UseStatusCodePages();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(CorsPolicyName);
app.UseAuthorization();
app.MapControllers();
// Endpoint de santé — utilisé par le healthcheck Docker Compose (depends_on: service_healthy).
app.MapHealthChecks("/health");
// --- Application automatique des migrations EF Core au démarrage ---
// Au premier lancement, la base (ex. /data/trajets.db sur le volume monté) est créée ;
// aux lancements suivants, les migrations en attente sont appliquées sans intervention manuelle.
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.MigrateAsync();
}
app.Run();
// Exposé pour les tests d'intégration (WebApplicationFactory<Program>).
public partial class Program { }