Nice little directory browser :D
at master 140 lines 4.3 kB view raw
1/* 2 This file is part of Utatane. 3 4 Utatane is free software: you can redistribute it and/or modify it under 5 the terms of the GNU Affero General Public License as published by the Free 6 Software Foundation, either version 3 of the License, or (at your option) 7 any later version. 8 9 Utatane is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 more details. 13 14 You should have received a copy of the GNU Affero General Public License 15 along with Utatane. If not, see <http://www.gnu.org/licenses/>. 16*/ 17 18using System.IO.Compression; 19using System.Text; 20using Utatane.Components; 21using Microsoft.AspNetCore.ResponseCompression; 22using Microsoft.AspNetCore.Rewrite; 23using Utatane; 24 25var builder = WebApplication.CreateBuilder(new WebApplicationOptions() { 26 Args = args, 27 WebRootPath = "public" 28}); 29 30// Add services to the container. 31// Add services to the container. 32builder.Services.AddRazorComponents(); 33builder.Services.AddResponseCompression(options => { 34 options.EnableForHttps = true; 35 options.Providers.Add<BrotliCompressionProvider>(); 36 options.Providers.Add<GzipCompressionProvider>(); 37 options.MimeTypes = ResponseCompressionDefaults.MimeTypes; 38} ); 39builder.Services.Configure<BrotliCompressionProviderOptions>(options => { 40 options.Level = CompressionLevel.SmallestSize; 41}); 42 43builder.Services.Configure<GzipCompressionProviderOptions>(options => { 44 options.Level = CompressionLevel.SmallestSize; 45}); 46 47var app = builder.Build(); 48 49// Configure the HTTP request pipeline. 50if (!app.Environment.IsDevelopment()) { 51 app.UseExceptionHandler("/Error", createScopeForErrors: true); 52 // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 53 app.UseHsts(); 54} 55 56app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); 57app.UseHttpsRedirection(); 58 59app.UseAntiforgery(); 60app.UseResponseCompression(); 61 62// HTML compression 63app.Use(async (context, next) => { 64 // context.Response.Body is a direct line to the client, so 65 // swap it out for our own in-memory stream for now 66 Stream responseStream = context.Response.Body; 67 using var memoryStream = new MemoryStream(); 68 context.Response.Body = memoryStream; 69 70 // let downstream render the response & write to our stream 71 await next(context); 72 73 if ( 74 context.Response.ContentType?.StartsWith("text/html") != true 75 /* || TODO: don't compress when serving HTML files found by Utatane!! */ ) { 76 // oops my bad gangalang 77 // ok now put it back 78 memoryStream.Position = 0; 79 await memoryStream.CopyToAsync(responseStream); 80 context.Response.Body = responseStream; 81 82 return; 83 } 84 85 memoryStream.Position = 0; 86 String html = await new StreamReader(memoryStream).ReadToEndAsync(); 87 String minified = Utils.OptimizeHtml(html); 88 89 context.Response.ContentLength = Encoding.UTF8.GetByteCount(minified); 90 await responseStream.WriteAsync(Encoding.UTF8.GetBytes(minified)); 91 context.Response.Body = responseStream; 92}); 93 94// check paths exist 95app.Use(async (context, next) => { 96 if ( 97 context.Request.Path.StartsWithSegments("/api/files") 98 || context.Request.Path.StartsWithSegments("/.nhnd") 99 ) { 100 await next(context); 101 return; 102 } 103 104 var resolved = Utils.VerifyPath(context.Request.Path); 105 106 if (resolved.IsFailed) { 107 context.Response.StatusCode = StatusCodes.Status404NotFound; 108 return; 109 } 110 111 // if we're a file AND we aren't a link to a directory 112 if (resolved.Value.IsT2 && resolved.Value.AsT2().UnravelLink() is not DirectoryInfo) { 113 FileInfo file = new FileInfo(resolved.Value.AsT2().UnravelLink().FullName); 114 115 await Results.File( 116 file.FullName, 117 MimeMapping.MimeUtility.GetMimeMapping(file.Name), 118 file.Name, 119 lastModified: file.LastWriteTimeUtc, 120 enableRangeProcessing: true 121 ).ExecuteAsync(context); 122 123 return; 124 } 125 126 // Console.WriteLine($"dir={resolved.Value.IsT1} file={resolved.Value.IsT2}"); 127 128 // we are either a directory or a link to one 129 130 await next(context); 131}); 132 133app.UseRewriter(new RewriteOptions().AddRedirect(@"^favicon\.ico$", "/.nhnd/favicon.ico")); 134 135app.UseStaticFiles(new StaticFileOptions { 136 RequestPath = "/.nhnd" 137}); 138app.MapRazorComponents<App>(); 139 140app.Run();