mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Single-origin URL rewriting; configurable SQLite path; default InjectAuthValues=false
- ClientMod: add SingleOriginBaseUrl config + URI rewrite path so the client can talk to one https://<apex>/__dn/<service>/... base instead of demanding wildcard subdomains. ServerHost rewrite still the fallback when SingleOriginBaseUrl is empty. - ClientMod config: InjectAuthValues now defaults to false. The server's PhotonCustomAuthController already fail-opens (Photon:RequireCustomAuth=false), so anonymous bootstrap is the cleaner default and avoids Photon's CustomAuthFailed loop when a WAF returns 403 to Photon's probe. - Server: SQLite path now reads Database:SqlitePath config (Database__SqlitePath env). Falls back to <bin>/data/dorknet.db so existing Docker / standalone deploys are unchanged. Lets the Windows launcher put the db under %APPDATA%\DorkNet. - Server: DomainConfig + ConfigService updates for the single-origin apex. - .gitignore: exclude local-releases.zip.
This commit is contained in:
@@ -65,7 +65,15 @@ builder.Services.AddDbContext<DorkNetDbContext>(opt =>
|
||||
}
|
||||
else
|
||||
{
|
||||
var dbPath = Path.Combine(AppContext.BaseDirectory, "data", "dorknet.db");
|
||||
// Honor an explicit path the host can set via env var
|
||||
// (Database__SqlitePath) or appsettings (Database:SqlitePath).
|
||||
// The Windows launcher sets this to %APPDATA%\DorkNet\dorknet.db
|
||||
// so server installs don't accumulate state under <bin>\data\.
|
||||
// Existing Docker / standalone deploys that mounted <bin>\data\
|
||||
// keep working because that's still the fallback.
|
||||
var dbPath = builder.Configuration["Database:SqlitePath"];
|
||||
if (string.IsNullOrWhiteSpace(dbPath))
|
||||
dbPath = Path.Combine(AppContext.BaseDirectory, "data", "dorknet.db");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dbPath)!);
|
||||
opt.UseSqlite($"Data Source={dbPath}", lite => lite.MigrationsAssembly("DorkNet.Server"));
|
||||
}
|
||||
@@ -94,14 +102,17 @@ var domainScheme =
|
||||
var domainPort =
|
||||
builder.Configuration["Domain:Port"]
|
||||
?? Environment.GetEnvironmentVariable("DORKNET_DOMAIN_PORT");
|
||||
builder.Services.AddSingleton(new DomainConfig(apex, domainScheme, domainPort));
|
||||
var singleOriginBaseUrl =
|
||||
builder.Configuration["Domain:SingleOriginBaseUrl"]
|
||||
?? Environment.GetEnvironmentVariable("DORKNET_SINGLE_ORIGIN_BASE_URL");
|
||||
builder.Services.AddSingleton(new DomainConfig(apex, domainScheme, domainPort, singleOriginBaseUrl));
|
||||
builder.Services.Configure<Microsoft.AspNetCore.HostFiltering.HostFilteringOptions>(opt =>
|
||||
{
|
||||
opt.AllowedHosts = new[] { apex, $"*.{apex}", "localhost", "127.0.0.1" };
|
||||
opt.AllowEmptyHosts = true;
|
||||
opt.IncludeFailureMessage = true;
|
||||
});
|
||||
Console.WriteLine($"[domain] apex={apex}, scheme={domainScheme}, port={domainPort}, allowedHosts=[{apex}, *.{apex}, localhost]");
|
||||
Console.WriteLine($"[domain] apex={apex}, scheme={domainScheme}, port={domainPort}, singleOrigin={singleOriginBaseUrl}, allowedHosts=[{apex}, *.{apex}, localhost]");
|
||||
|
||||
// ── Services ──────────────────────────────────────────────────────────────────
|
||||
builder.Services.AddScoped<PlayerService>();
|
||||
@@ -317,6 +328,30 @@ var app = builder.Build();
|
||||
// hardcoded rec.net/localhost literals.
|
||||
var domainCfg = app.Services.GetRequiredService<DomainConfig>();
|
||||
|
||||
// Easy Launcher single-origin mode. localtunnel gives us one public
|
||||
// hostname, so the launcher name-server emits URLs like
|
||||
// https://abc.loca.lt/__dn/api/...; this middleware maps those path
|
||||
// prefixes back into the same internal host layout used by advanced
|
||||
// wildcard deployments. If Domain:SingleOriginBaseUrl is unset this is
|
||||
// inert, preserving the normal subdomain behavior.
|
||||
app.Use(async (ctx, next) =>
|
||||
{
|
||||
if (domainCfg.SingleOriginEnabled &&
|
||||
TryMapSingleOriginPath(ctx.Request.Path, out var service, out var rest))
|
||||
{
|
||||
ctx.Request.Host = new HostString(service == "www" ? domainCfg.Apex : domainCfg.Sub(service));
|
||||
ctx.Request.Path = string.IsNullOrEmpty(rest) ? "/" : rest;
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// Keep endpoint selection after the single-origin path rewrite. Without an
|
||||
// explicit routing point here, WebApplication can auto-insert routing before
|
||||
// the rewrite middleware, causing /__dn/api/... requests to stay route misses
|
||||
// even though the trace logs show the rewritten /api/... path.
|
||||
app.UseRouting();
|
||||
|
||||
// Request/response tracing — emits one structured log per non-health request,
|
||||
// with the response body included for any 4xx/5xx so we can see exactly what
|
||||
// the game is asking for. This runs early enough to catch route misses,
|
||||
@@ -1071,6 +1106,22 @@ app.MapHub<DorkNet.Server.Hubs.NotifyHub>("/hub/v1", opts =>
|
||||
|
||||
app.Run();
|
||||
|
||||
static bool TryMapSingleOriginPath(PathString path, out string service, out PathString rest)
|
||||
{
|
||||
service = "";
|
||||
rest = PathString.Empty;
|
||||
if (!path.StartsWithSegments("/__dn", out var tail)) return false;
|
||||
|
||||
var value = tail.Value ?? "";
|
||||
if (value.Length <= 1) return false;
|
||||
var slash = value.IndexOf('/', 1);
|
||||
service = slash < 0 ? value[1..] : value[1..slash];
|
||||
if (string.IsNullOrWhiteSpace(service)) return false;
|
||||
|
||||
rest = slash < 0 ? "/" : value[slash..];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsHealthProbe(HttpRequest request)
|
||||
{
|
||||
return request.Path.Equals("/healthz", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -23,7 +23,9 @@ public class ConfigService(IConfiguration config, DomainConfig domain)
|
||||
return new RecRoomConfig
|
||||
{
|
||||
MessageOfTheDay = config["Server:MOTD"] ?? "Welcome to the private server!",
|
||||
CdnBaseUri = domain.SubUrl("cdn"),
|
||||
CdnBaseUri = domain.SingleOriginEnabled
|
||||
? $"{domain.SingleOriginBaseUrl}/__dn/cdn"
|
||||
: domain.SubUrl("cdn"),
|
||||
PhotonConfig = photon,
|
||||
ServiceUrls = BuildServiceUrlMap(domain),
|
||||
ConfigTable = new Dictionary<string, string>
|
||||
@@ -110,6 +112,12 @@ public class ConfigService(IConfiguration config, DomainConfig domain)
|
||||
var map = new Dictionary<string, string>(ServiceSubdomains.Length);
|
||||
foreach (var (service, sub) in ServiceSubdomains)
|
||||
{
|
||||
if (domain.SingleOriginEnabled)
|
||||
{
|
||||
var prefix = string.IsNullOrEmpty(sub) ? "www" : sub;
|
||||
map[service] = $"{domain.SingleOriginBaseUrl}/__dn/{prefix}";
|
||||
continue;
|
||||
}
|
||||
var host = string.IsNullOrEmpty(sub) ? domain.Apex : domain.Sub(sub);
|
||||
map[service] = domain.Url(host);
|
||||
}
|
||||
|
||||
@@ -13,12 +13,17 @@ public sealed class DomainConfig
|
||||
public string Apex { get; }
|
||||
public string Scheme { get; }
|
||||
public string? Port { get; }
|
||||
public string? SingleOriginBaseUrl { get; }
|
||||
public bool SingleOriginEnabled => !string.IsNullOrWhiteSpace(SingleOriginBaseUrl);
|
||||
|
||||
public DomainConfig(string apex, string scheme = "https", string? port = null)
|
||||
public DomainConfig(string apex, string scheme = "https", string? port = null, string? singleOriginBaseUrl = null)
|
||||
{
|
||||
Apex = apex;
|
||||
Scheme = string.IsNullOrWhiteSpace(scheme) ? "https" : scheme.TrimEnd(':', '/', '\\');
|
||||
Port = string.IsNullOrWhiteSpace(port) ? null : port.Trim().TrimStart(':');
|
||||
SingleOriginBaseUrl = string.IsNullOrWhiteSpace(singleOriginBaseUrl)
|
||||
? null
|
||||
: singleOriginBaseUrl.Trim().TrimEnd('/');
|
||||
}
|
||||
|
||||
public string Sub(string prefix) => $"{prefix}.{Apex}";
|
||||
|
||||
Reference in New Issue
Block a user