using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace GmRelay.Web.Services.Portfolio.Covers; public static class PortfolioCoverStorageExtensions { public static IServiceCollection AddPortfolioCoverStorage( this IServiceCollection services, IConfiguration configuration) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); services .AddOptions() .Bind(configuration.GetSection(PortfolioCoverStorageOptions.SectionName)) .Validate( o => !string.IsNullOrWhiteSpace(o.StoragePath), "PortfolioCovers:StoragePath must be configured.") .ValidateOnStart(); services.AddSingleton(sp => { var options = sp.GetRequiredService< Microsoft.Extensions.Options.IOptions>().Value; var logger = sp.GetService()?.CreateLogger() ?? NullLogger.Instance; return new LocalPortfolioCoverStorage(options, logger); }); return services; } public static WebApplication UsePortfolioCoverFiles(this WebApplication app) { ArgumentNullException.ThrowIfNull(app); var options = app.Services.GetRequiredService< Microsoft.Extensions.Options.IOptions>().Value; var storagePath = Path.IsPathRooted(options.StoragePath) ? options.StoragePath : Path.Combine(app.Environment.ContentRootPath, options.StoragePath); Directory.CreateDirectory(storagePath); var contentTypeProvider = new FileExtensionContentTypeProvider(); if (!contentTypeProvider.Mappings.ContainsKey(".jpg")) { contentTypeProvider.Mappings[".jpg"] = "image/jpeg"; } if (!contentTypeProvider.Mappings.ContainsKey(".png")) { contentTypeProvider.Mappings[".png"] = "image/png"; } if (!contentTypeProvider.Mappings.ContainsKey(".webp")) { contentTypeProvider.Mappings[".webp"] = "image/webp"; } app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(storagePath), RequestPath = "/portfolio-covers", ContentTypeProvider = contentTypeProvider, OnPrepareResponse = ctx => { ctx.Context.Response.Headers["Cache-Control"] = "public, max-age=31536000, immutable"; } }); return app; } }