Nice little directory browser :D
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 FluentResults;
21using Microsoft.AspNetCore.Http.Features;
22using Utatane.Components;
23using Microsoft.AspNetCore.ResponseCompression;
24using Microsoft.AspNetCore.Rewrite;
25using Microsoft.Extensions.FileProviders;
26using Utatane;
27
28var builder = WebApplication.CreateBuilder(new WebApplicationOptions() {
29 Args = args,
30 WebRootPath = "public"
31});
32
33// Add services to the container.
34// Add services to the container.
35builder.Services.AddRazorComponents();
36builder.Services.AddResponseCompression(options => {
37 options.EnableForHttps = true;
38 options.Providers.Add<BrotliCompressionProvider>();
39 options.Providers.Add<GzipCompressionProvider>();
40 options.MimeTypes = ResponseCompressionDefaults.MimeTypes;
41} );
42builder.Services.Configure<BrotliCompressionProviderOptions>(options => {
43 options.Level = CompressionLevel.SmallestSize;
44});
45
46builder.Services.Configure<GzipCompressionProviderOptions>(options => {
47 options.Level = CompressionLevel.SmallestSize;
48});
49
50var app = builder.Build();
51
52// Configure the HTTP request pipeline.
53if (!app.Environment.IsDevelopment()) {
54 app.UseExceptionHandler("/Error", createScopeForErrors: true);
55 // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
56 app.UseHsts();
57}
58
59app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
60app.UseHttpsRedirection();
61
62app.UseAntiforgery();
63app.UseResponseCompression();
64
65app.Use(async (context, next) => {
66 // context.Response.Body is a direct line to the client, so
67 // swap it out for our own in-memory stream for now
68 Stream responseStream = context.Response.Body;
69 using var memoryStream = new MemoryStream();
70 context.Response.Body = memoryStream;
71
72 // let downstream render the response & write to our stream
73 await next(context);
74
75 if (
76 context.Response.ContentType?.StartsWith("text/html") != true
77 /* || TODO: don't compress when serving HTML files found by Utatane!! */ ) {
78 // oops my bad gangalang
79 // ok now put it back
80 memoryStream.Position = 0;
81 await memoryStream.CopyToAsync(responseStream);
82 context.Response.Body = responseStream;
83
84 return;
85 }
86
87 memoryStream.Position = 0;
88 String html = await new StreamReader(memoryStream).ReadToEndAsync();
89 String minified = Utils.OptimizeHtml(html);
90
91 context.Response.ContentLength = Encoding.UTF8.GetByteCount(minified);
92 await responseStream.WriteAsync(Encoding.UTF8.GetBytes(minified));
93 context.Response.Body = responseStream;
94});
95
96// check paths exist
97app.Use(async (context, next) => {
98 if (
99 context.Request.Path.StartsWithSegments("/api/files")
100 || context.Request.Path.StartsWithSegments("/.nhnd")
101 ) {
102 await next(context);
103 return;
104 }
105
106 var pathCheck = Utils.CheckJoinedPathIsBased(context.Request.Path);
107 if (pathCheck.IsFailed) {
108 context.Response.StatusCode = StatusCodes.Status404NotFound;
109 return;
110 }
111
112 await next(context);
113});
114
115app.UseRewriter(new RewriteOptions().AddRedirect(@"^favicon\.ico$", "/.nhnd/favicon.ico"));
116
117app.UseStaticFiles(new StaticFileOptions {
118 RequestPath = "/.nhnd"
119});
120app.MapRazorComponents<App>();
121
122app.Run();