using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using System.Text.Json; using TodoList.Application; using TodoList.Application.DynamicApi; using TodoList.Maui.Models; using AppSettings = TodoList.Maui.Models.AppSettings; namespace TodoList.Maui.Services; public class EmbeddedWebServerService : IEmbeddedWebServerService { private WebApplication? _webApp; private readonly AppSettings _appSettings; public bool IsRunning => _webApp != null; public string BaseUrl => _appSettings.WebServer.HostUrl; public EmbeddedWebServerService(AppSettings appSettings) { _appSettings = appSettings; } public async Task StartAsync() { if (_webApp != null) return; var builder = WebApplication.CreateSlimBuilder(); builder.WebHost.UseUrls(_appSettings.WebServer.HostUrl); builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddApplicationServices(_appSettings.WebServer.ConnectionString); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); if (_appSettings.WebServer.IsUsingStatic) { ServeStaticFiles(app); } app.UseCors("AllowAll"); app.UseHttpsRedirection(); app.UseAuthorization(); app.UseDynamicApi(); app.MapControllers(); _webApp = app; await _webApp.StartAsync(); } private void ServeStaticFiles(WebApplication app) { var wwwrootPath = Path.Combine(AppContext.BaseDirectory, "wwwroot"); if (!Directory.Exists(wwwrootPath)) { Console.WriteLine("[EmbeddedWebServer] wwwroot directory not found. Static file serving disabled."); return; } try { var fileProvider = new PhysicalFileProvider(wwwrootPath); var defaultFilesOptions = new DefaultFilesOptions { FileProvider = fileProvider, RequestPath = "" }; app.UseDefaultFiles(defaultFilesOptions); var staticFileOptions = new StaticFileOptions { FileProvider = fileProvider, RequestPath = "", OnPrepareResponse = ctx => { ctx.Context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate"; ctx.Context.Response.Headers["Pragma"] = "no-cache"; ctx.Context.Response.Headers["Expires"] = "0"; } }; app.UseStaticFiles(staticFileOptions); app.Use(async (context, next) => { if (context.Request.Path.HasValue) { var path = context.Request.Path.Value; if (path != "/" && !path.StartsWith("/assets", StringComparison.OrdinalIgnoreCase) && !path.StartsWith("/api", StringComparison.OrdinalIgnoreCase)) { var ext = Path.GetExtension(path); if (string.IsNullOrEmpty(ext)) { context.Request.Path = "/index.html"; } } } await next(); }); Console.WriteLine($"[EmbeddedWebServer] Serving static files from: {wwwrootPath}"); } catch (Exception ex) { Console.WriteLine($"[EmbeddedWebServer] Failed to serve static files: {ex.Message}"); } } public async Task StopAsync() { if (_webApp == null) return; await _webApp.StopAsync(); await _webApp.DisposeAsync(); _webApp = null; } }