From e2f86b76db5a0ffe92643a020085e929e19b7627 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 15 Nov 2025 14:11:48 +0000 Subject: [PATCH] Serve assets. --- Marechai.Server/Helpers/Photos.cs | 70 ++++++++++++++-------------- Marechai.Server/Helpers/SvgRender.cs | 28 +++++------ Marechai.Server/Program.cs | 70 ++++++++++++++++++++-------- Marechai.Server/appsettings.json | 3 +- 4 files changed, 102 insertions(+), 69 deletions(-) diff --git a/Marechai.Server/Helpers/Photos.cs b/Marechai.Server/Helpers/Photos.cs index 5f8ad6ae..ef7cd51f 100644 --- a/Marechai.Server/Helpers/Photos.cs +++ b/Marechai.Server/Helpers/Photos.cs @@ -36,11 +36,11 @@ public class Photos { public delegate Task ConversionFinished(bool result); - public static void EnsureCreated(string webRootPath, bool scan, string item) + public static void EnsureCreated(string assetRootPath, bool scan, string item) { List paths = []; - string photosRoot = Path.Combine(webRootPath, "assets", scan ? "scan" : "photos"); + string photosRoot = Path.Combine(assetRootPath, scan ? "scan" : "photos"); string itemPhotosRoot = Path.Combine(photosRoot, item); string itemThumbsRoot = Path.Combine(itemPhotosRoot, "thumbs"); string itemOriginalPhotosRoot = Path.Combine(itemPhotosRoot, "originals"); @@ -88,14 +88,14 @@ public class Photos foreach(string path in paths.Where(path => !Directory.Exists(path))) Directory.CreateDirectory(path); } - public static bool Convert(string webRootPath, Guid id, string originalPath, string sourceFormat, + public static bool Convert(string assetRootPath, Guid id, string originalPath, string sourceFormat, string outputFormat, string resolution, bool thumbnail, bool scan, string item) { outputFormat = outputFormat.ToLowerInvariant(); resolution = resolution.ToLowerInvariant(); sourceFormat = sourceFormat.ToLowerInvariant(); - string outputPath = Path.Combine(webRootPath, "assets", scan ? "scans" : "photos", item); + string outputPath = Path.Combine(assetRootPath, scan ? "scans" : "photos", item); int width, height; if(thumbnail) outputPath = Path.Combine(outputPath, "thumbs"); @@ -268,12 +268,12 @@ public class Photos } } - public void ConversionWorker(string webRootPath, Guid id, string originalFilePath, string sourceFormat, bool scan, + public void ConversionWorker(string assetRootPath, Guid id, string originalFilePath, string sourceFormat, bool scan, string item) { List pool = [ - new(() => FinishedRenderingJpeg4kThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpeg4kThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -282,7 +282,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJpeg1440Thumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpeg1440Thumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -291,7 +291,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJpegHdThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpegHdThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -300,7 +300,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJpeg4K?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpeg4K?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -309,7 +309,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingJpeg1440?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpeg1440?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -318,7 +318,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingJpegHd?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJpegHd?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -327,7 +327,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingJp2k4kThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2k4kThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -336,7 +336,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJp2k1440Thumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2k1440Thumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -345,7 +345,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJp2kHdThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2kHdThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -354,7 +354,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingJp2k4k?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2k4k?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -363,7 +363,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingJp2k1440?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2k1440?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -372,7 +372,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingJp2kHd?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingJp2kHd?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -381,7 +381,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingWebp4kThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebp4kThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -390,7 +390,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingWebp1440Thumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebp1440Thumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -399,7 +399,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingWebpHdThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebpHdThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -408,7 +408,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingWebp4k?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebp4k?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -417,7 +417,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingWebp1440?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebp1440?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -426,7 +426,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingWebpHd?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingWebpHd?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -435,7 +435,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingHeif4kThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeif4kThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -444,7 +444,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingHeif1440Thumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeif1440Thumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -453,7 +453,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingHeifHdThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeifHdThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -462,7 +462,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingHeif4K?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeif4K?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -471,7 +471,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingHeif1440?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeif1440?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -480,7 +480,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingHeifHd?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingHeifHd?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -489,7 +489,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingAvif4kThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvif4kThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -498,7 +498,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingAvif1440Thumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvif1440Thumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -507,7 +507,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingAvifHdThumbnail?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvifHdThumbnail?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -516,7 +516,7 @@ public class Photos true, scan, item))), - new(() => FinishedRenderingAvif4K?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvif4K?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -525,7 +525,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingAvif1440?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvif1440?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, @@ -534,7 +534,7 @@ public class Photos false, scan, item))), - new(() => FinishedRenderingAvifHd?.Invoke(Convert(webRootPath, + new(() => FinishedRenderingAvifHd?.Invoke(Convert(assetRootPath, id, originalFilePath, sourceFormat, diff --git a/Marechai.Server/Helpers/SvgRender.cs b/Marechai.Server/Helpers/SvgRender.cs index ca4ed801..8a5ca573 100644 --- a/Marechai.Server/Helpers/SvgRender.cs +++ b/Marechai.Server/Helpers/SvgRender.cs @@ -33,11 +33,11 @@ namespace Marechai.Server.Helpers; public static class SvgRender { - public static void RenderCountries() + public static void RenderCountries(string assetRootPath) { - if(!Directory.Exists("wwwroot/assets/flags/countries")) return; + if(!Directory.Exists($"{assetRootPath}/flags/countries")) return; - foreach(string file in Directory.GetFiles("wwwroot/assets/flags/countries/", + foreach(string file in Directory.GetFiles($"{assetRootPath}/flags/countries/", "*.svg", SearchOption.TopDirectoryOnly)) { @@ -50,8 +50,8 @@ public static class SvgRender "png", "webp" }) { - if(!Directory.Exists(Path.Combine("wwwroot/assets/flags/countries", format))) - Directory.CreateDirectory(Path.Combine("wwwroot/assets/flags/countries", format)); + if(!Directory.Exists(Path.Combine($"{assetRootPath}/flags/countries", format))) + Directory.CreateDirectory(Path.Combine($"{assetRootPath}/flags/countries", format)); SKEncodedImageFormat skFormat; @@ -72,14 +72,14 @@ public static class SvgRender 1, 2, 3 }) { - if(!Directory.Exists(Path.Combine("wwwroot/assets/flags/countries", format, $"{multiplier}x"))) + if(!Directory.Exists(Path.Combine($"{assetRootPath}/flags/countries", format, $"{multiplier}x"))) { - Directory.CreateDirectory(Path.Combine("wwwroot/assets/flags/countries", + Directory.CreateDirectory(Path.Combine($"{assetRootPath}/flags/countries", format, $"{multiplier}x")); } - string rendered = Path.Combine("wwwroot/assets/flags/countries", + string rendered = Path.Combine($"{assetRootPath}/flags/countries", format, $"{multiplier}x", flagName + $".{format}"); @@ -102,11 +102,11 @@ public static class SvgRender } } - public static void ImportCompanyLogos(MarechaiContext context) + public static void ImportCompanyLogos(string assetRootPath, MarechaiContext context) { - if(!Directory.Exists("wwwroot/assets/incoming")) return; + if(!Directory.Exists($"{assetRootPath}/incoming")) return; - foreach(string file in Directory.GetFiles("wwwroot/assets/incoming", + foreach(string file in Directory.GetFiles($"{assetRootPath}/incoming", "company_*.svg", SearchOption.TopDirectoryOnly)) { @@ -153,8 +153,8 @@ public static class SvgRender }) { string outDir = minSize == 32 - ? Path.Combine("wwwroot/assets/logos/thumbs", format) - : Path.Combine("wwwroot/assets/logos", format); + ? Path.Combine($"{assetRootPath}/logos/thumbs", format) + : Path.Combine($"{assetRootPath}/logos", format); if(!Directory.Exists(outDir)) Directory.CreateDirectory(outDir); @@ -200,7 +200,7 @@ public static class SvgRender } } - File.Move(file, $"wwwroot/assets/logos/{guid}.svg"); + File.Move(file, $"{assetRootPath}/logos/{guid}.svg"); } } diff --git a/Marechai.Server/Program.cs b/Marechai.Server/Program.cs index 425a23fa..dd42e12e 100644 --- a/Marechai.Server/Program.cs +++ b/Marechai.Server/Program.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; @@ -12,9 +13,11 @@ using Markdig; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.StaticFiles; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using Version = Marechai.Server.Interop.Version; @@ -140,14 +143,36 @@ file class Program } } + WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + + string assetRootPath = builder.Configuration["AssetRootPath"]; + + if(string.IsNullOrEmpty(assetRootPath)) + { + Console.WriteLine("\e[31;1mAsset root path not set, cannot continue, check configuration...\e[0m"); + + return; + } + + Directory.CreateDirectory(assetRootPath); + DateTime start = DateTime.Now; Console.WriteLine("\e[31;1mRendering new country flags...\e[0m"); - SvgRender.RenderCountries(); + SvgRender.RenderCountries(assetRootPath); DateTime end = DateTime.Now; Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds); - WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + start = DateTime.Now; + Console.WriteLine("\e[31;1mEnsuring photo folders exist...\e[0m"); + Photos.EnsureCreated(assetRootPath, false, "machines"); + Console.WriteLine("\e[31;1mEnsuring scan folders exist...\e[0m"); + Photos.EnsureCreated(assetRootPath, true, "books"); + Photos.EnsureCreated(assetRootPath, true, "documents"); + Photos.EnsureCreated(assetRootPath, true, "magazines"); + end = DateTime.Now; + + Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds); // Add services to the container. builder.Services.AddControllers() @@ -210,8 +235,7 @@ file class Program builder.Services.AddScoped(); // Read allowed CORS origins from configuration - string[] allowedOrigins = builder.Configuration.GetSection("CORS:AllowedOrigins").Get() ?? - Array.Empty(); + string[] allowedOrigins = builder.Configuration.GetSection("CORS:AllowedOrigins").Get() ?? []; builder.Services.AddCors(options => { @@ -241,6 +265,28 @@ file class Program app.MapControllers(); + // Set up custom content types - associating file extension to MIME type + var provider = new FileExtensionContentTypeProvider + { + Mappings = + { + // Add new mappings + [".avif"] = "image/avif", // AVIF image format + [".heic"] = "image/heic", // HEIC image format + [".heif"] = "image/heif", // HEIF image format + [".jxl"] = "image/jxl", // JPEG-XL image format + [".webp"] = "image/webp", // WebP image format + [".svg"] = "image/svg+xml" // SVG image format + } + }; + + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(assetRootPath), + RequestPath = "/assets", + ContentTypeProvider = provider + }); + using(IServiceScope scope = app.Services.CreateScope()) { IServiceProvider services = scope.ServiceProvider; @@ -257,7 +303,7 @@ file class Program start = DateTime.Now; Console.WriteLine("\e[31;1mImporting company logos...\e[0m"); - SvgRender.ImportCompanyLogos(context); + SvgRender.ImportCompanyLogos(assetRootPath, context); end = DateTime.Now; Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds); @@ -278,20 +324,6 @@ file class Program end = DateTime.Now; Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds); - - start = DateTime.Now; - Console.WriteLine("\e[31;1mEnsuring photo folders exist...\e[0m"); - Photos.EnsureCreated("wwwroot", false, "machines"); - end = DateTime.Now; - - start = DateTime.Now; - Console.WriteLine("\e[31;1mEnsuring scan folders exist...\e[0m"); - Photos.EnsureCreated("wwwroot", true, "books"); - Photos.EnsureCreated("wwwroot", true, "documents"); - Photos.EnsureCreated("wwwroot", true, "magazines"); - end = DateTime.Now; - - Console.WriteLine("\e[31;1mTook \e[32;1m{0} seconds\e[31;1m...\e[0m", (end - start).TotalSeconds); } catch(Exception ex) { diff --git a/Marechai.Server/appsettings.json b/Marechai.Server/appsettings.json index e999753e..5f314816 100644 --- a/Marechai.Server/appsettings.json +++ b/Marechai.Server/appsettings.json @@ -64,5 +64,6 @@ "Name": "NormalUser", "Description": "A normal user role." } - ] + ], + "AssetRootPath": "/var/marechai/assets" } \ No newline at end of file