using MaBibli.Api.Data; using MaBibli.Api.Endpoints; using MaBibli.Api.Services.Catalogue; using MaBibli.Api.Services.Identite; using MaBibli.Api.Services.Isbn; using MaBibli.Api.Services.Prets; using MaBibli.Api.Services.Revues; using MaBibli.Api.Services.Series; using MaBibli.Api.Services.Souhaits; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenApi(); builder.Services.AddDbContext(options => options.UseSqlite(builder.Configuration.GetConnectionString("MaBibli"))); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Identité : lue dans les en-têtes injectés par SSOwat, avec repli sur un utilisateur simulé // configuré en développement (il n'y a pas de portail YunoHost en local). builder.Services.AddHttpContextAccessor(); builder.Services.Configure(builder.Configuration.GetSection(OptionsIdentite.Section)); builder.Services.AddScoped(); // Lookup ISBN : clients typés via IHttpClientFactory (pas de HttpClient instancié à la main). // Timeout court : une source lente ne doit pas bloquer la cascade, l'appelant bascule sur la suivante. var timeoutSources = TimeSpan.FromSeconds(10); builder.Services.AddHttpClient(http => { http.BaseAddress = new Uri(BnfClient.UrlBase); http.Timeout = timeoutSources; http.DefaultRequestHeaders.UserAgent.ParseAdd("MaBibli/0.1 (bibliotheque personnelle auto-hebergee)"); }); builder.Services.AddHttpClient(http => { http.BaseAddress = new Uri(OpenLibraryClient.UrlBase); http.Timeout = timeoutSources; http.DefaultRequestHeaders.UserAgent.ParseAdd("MaBibli/0.1 (bibliotheque personnelle auto-hebergee)"); }); builder.Services.AddScoped(); var app = builder.Build(); // Une seule base SQLite, migrée au démarrage : pas d'étape manuelle à l'installation YunoHost. using (var portee = app.Services.CreateScope()) { var db = portee.ServiceProvider.GetRequiredService(); db.Database.Migrate(); // Les colonnes normalisées (sans accents) ne peuvent pas être calculées en SQL : la // migration ne les remplit qu'approximativement, ce passage les remet d'aplomb et réunit // les auteurs que la normalisation révèle identiques. Idempotent, donc sans coût ensuite. await new ServiceRenormalisation( db, portee.ServiceProvider.GetRequiredService>()) .ExecuterAsync(); } if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } // Le client Blazor WebAssembly est servi comme fichiers statiques par cette API : // un seul processus tourne sur le serveur. // // ⚠️ Les empreintes WASM sont désactivées (voir CLAUDE.md) : tous ces fichiers portent des noms // stables. Sans en-tête Cache-Control, un navigateur peut garder une version périmée en cache // heuristique (par exemple MaBibli.Client.styles.css) même si le service worker, lui, revalide // déjà à l'installation — mais seulement quand il parvient à s'activer. `no-cache` force une // revalidation conditionnelle (ETag/Last-Modified, déjà posés par défaut) à chaque requête : // pas de re-téléchargement si le fichier n'a pas changé, mais jamais de contenu périmé servi // sans repasser par le serveur. var optionsFichiersStatiques = new StaticFileOptions { OnPrepareResponse = ctx => ctx.Context.Response.Headers.CacheControl = "no-cache, must-revalidate", }; app.UseBlazorFrameworkFiles(); app.UseStaticFiles(optionsFichiersStatiques); app.MapIsbnEndpoints(); app.MapLivresEndpoints(); app.MapAuteursEndpoints(); app.MapPretsEndpoints(); app.MapSouhaitsEndpoints(); app.MapSeriesEndpoints(); app.MapRevuesEndpoints(); app.MapBibliographieEndpoints(); app.MapIdentiteEndpoints(); app.MapFallbackToFile("index.html"); app.Run();