using LiteDB; using Microsoft.Extensions.FileProviders; using Campus_live_rewrite; using Campus_live_rewrite.api.dbs; using Campus_live_rewrite.Controllers; using Campus_live_rewrite.Commands; using Campus_live_rewrite.api.server; using System.Security.Principal; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Server.Kestrel.Core; using Campus_live_rewrite.api.server.Classes; using Newtonsoft.Json; internal class Program { private static async Task Main(string[] args) { // If a DB restore was staged from the admin panel, apply it NOW — before anything opens // a LiteDB file (they open lazily on first use, and can't be overwritten while held open). BackupService.RestorePendingIfAny(); var builder = WebApplication.CreateBuilder(args); builder.WebHost.ConfigureKestrel(serverOptions => { serverOptions.AllowSynchronousIO = true; // ---- DoS / resource-exhaustion caps ------------------------------------------- // Nothing bounded Kestrel before this, so a connection flood or a giant/slow body // could grow the process to max memory (the reported crash). These are hard ceilings // that never affect a normal client; they only stop abuse. // // * Body size: an explicit per-request cap (== the historical 30 MB default, so real // room/image uploads are unchanged) instead of relying on the implicit default. // * Concurrent connections: bound how many sockets can be held open at once, so a // flood of connections cannot balloon memory - excess connections are refused, the // server stays up. Applies separately to normal and upgraded (WebSocket) sockets. // * Header timeout: a client must finish sending request headers within 15s, which // kills slowloris-style "trickle the headers forever" connection hoarding. // * Keep-alive: reclaim idle connections after 60s instead of the 130s default. // (MinRequestBodyDataRate is left at Kestrel's default - 240 B/s over a 5s grace - // which already drops a client that trickles the BODY too slowly.) serverOptions.Limits.MaxRequestBodySize = ConfigCampus.MaxRequestBodyBytes; serverOptions.Limits.MaxConcurrentConnections = ConfigCampus.MaxConcurrentConnections; serverOptions.Limits.MaxConcurrentUpgradedConnections = ConfigCampus.MaxConcurrentUpgradedConnections; serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(15); serverOptions.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(60); }); builder.Services.AddHttpLogging(configureOptions => { }); builder.Services .AddControllers() .AddJsonOptions(o => { o.JsonSerializerOptions.PropertyNamingPolicy = null; }); builder.Services.AddSignalR(); builder.Services.AddSingleton(); builder.Host.UseContentRoot(Directory.GetCurrentDirectory()); builder.Services.Configure(options => { options.SuppressModelStateInvalidFilter = false; }); var app = builder.Build(); if (!Directory.Exists(Environment.CurrentDirectory + "/Data")) { Directory.CreateDirectory(Environment.CurrentDirectory + "/Data"); Directory.CreateDirectory(Environment.CurrentDirectory + "/Data/APIS"); Directory.CreateDirectory(Environment.CurrentDirectory + "/Data/DBs"); } if (!Directory.Exists(Environment.CurrentDirectory + "/Web")) { Directory.CreateDirectory(Environment.CurrentDirectory + "/Web"); Directory.CreateDirectory(Environment.CurrentDirectory + "/Web/Admin"); } // FIRST in the pipeline: a banned IP gets 502 for the whole domain and never // touches logging, routing or any controller. Loopback is exempt so the operator // cannot lock themselves out of the admin panel over the SSH tunnel. app.UseMiddleware(); // Per-IP flood + brute-force guard. Runs right after the ban check and BEFORE routing / // logging / controllers, so a flood is 429'd at the cheapest possible point and never // reaches LiteDB or the access log. General ceiling stops volumetric DoS; a tight ceiling // on /Auth/connect/token + /api/site/login stops credential brute-force and token farming. // Loopback exempt. Toggle with ConfigCampus.TrafficGuardEnabled. app.UseMiddleware(); // Patch gate: refuse game requests from any IP that has not completed the patch handshake // (POST /api/patch/verify with the correct X-Campus-Patch header). A wrong/old/no patch // never validates, so it is blocked here. Toggle with PatchGateMiddleware.Enable; exempts // /api/patch/verify + /admin + browser assets + loopback. app.UseMiddleware(); // Admin hardening: security headers + per-IP rate limit on /admin/* and /api/web/admin/* // (on top of the dev-token + admin-session auth). Runs after the gate so the login page is // still reachable to be throttled, and after the ban check so banned IPs never reach it. app.UseMiddleware(); // Lightweight per-request access log for the admin "Log" tab (/admin/api/apilog). // Rewritten to an in-memory ring buffer + single shared file writer (see Log.cs), so it // no longer opens/closes the file per request or drops lines under concurrency. Now cheap // enough to run UNCONDITIONALLY - the admin log tool needs data whether or not the heavy // VerboseRequestLogging body-logging (Log2, below) is on. This only records one line per // request ("