feat: Enhance domain configuration to support hyphen subdomains and update related methods

This commit is contained in:
Alexa
2026-06-25 11:46:34 +03:00
parent a68e9b169f
commit 9c921715c7
4 changed files with 87 additions and 8 deletions
+18 -3
View File
@@ -12,14 +12,22 @@ public sealed class DomainConfig
{
public string Apex { get; }
public string Scheme { get; }
public string HostStyle { get; }
public bool UsesHyphenSubdomains => HostStyle == "hyphen";
public DomainConfig(string apex, string scheme = "https")
public DomainConfig(string apex, string scheme = "https", string hostStyle = "dot")
{
Apex = apex;
Scheme = NormalizeScheme(scheme);
HostStyle = NormalizeHostStyle(hostStyle);
}
public string Sub(string prefix)
{
if (string.IsNullOrWhiteSpace(prefix)) return Apex;
return UsesHyphenSubdomains ? $"{prefix}-{Apex}" : $"{prefix}.{Apex}";
}
public string Sub(string prefix) => $"{prefix}.{Apex}";
public string Url(string prefix, string path = "")
{
var host = string.IsNullOrEmpty(prefix) ? Apex : Sub(prefix);
@@ -28,11 +36,18 @@ public sealed class DomainConfig
}
public static bool MatchesSubdomain(string host, string prefix)
=> host.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase);
=> host.StartsWith(prefix + ".", StringComparison.OrdinalIgnoreCase)
|| host.StartsWith(prefix + "-", StringComparison.OrdinalIgnoreCase);
private static string NormalizeScheme(string? value)
{
var scheme = string.IsNullOrWhiteSpace(value) ? "https" : value.Trim().TrimEnd(':', '/', '\\').ToLowerInvariant();
return scheme is "http" or "https" ? scheme : "https";
}
private static string NormalizeHostStyle(string? value)
{
var style = string.IsNullOrWhiteSpace(value) ? "dot" : value.Trim().ToLowerInvariant();
return style is "hyphen" or "dash" or "flat" ? "hyphen" : "dot";
}
}
@@ -54,7 +54,7 @@ public static class MiddlewarePipelineExtensions
// Public-facing site at the apex (e.g. localhost + www variant).
// React-router routes are client-side so spaFallback must be true.
app.MountStaticHost(domainCfg.Apex, "site", spaFallback: true);
app.MountStaticHost($"www.{domainCfg.Apex}", "site", spaFallback: true);
app.MountStaticHost(domainCfg.Sub("www"), "site", spaFallback: true);
// Old feed.* subdomain kept as legacy; new visitors land on the apex.
app.MountStaticHost(domainCfg.Sub("feed"), "feed");
@@ -115,7 +115,23 @@ public static class ServiceCollectionExtensions
?? Environment.GetEnvironmentVariable("DORKNET_DOMAIN")
?? "localhost";
var scheme = builder.Configuration["Domain:Scheme"] ?? "https";
var allowedHosts = new List<string> { apex, $"*.{apex}", "localhost", "127.0.0.1" };
var hostStyle =
builder.Configuration["Domain:HostStyle"]
?? Environment.GetEnvironmentVariable("DORKNET_DOMAIN_STYLE")
?? "dot";
var domain = new DomainConfig(apex, scheme, hostStyle);
var allowedHosts = new List<string> { apex, "localhost", "127.0.0.1" };
if (domain.UsesHyphenSubdomains)
{
allowedHosts.AddRange(KnownServiceSubdomains
.Where(s => !string.IsNullOrWhiteSpace(s))
.Select(domain.Sub));
allowedHosts.Add(domain.Sub("www"));
}
else
{
allowedHosts.Add($"*.{apex}");
}
var bootstrapApex = builder.Configuration["Domain:BootstrapApex"];
if (!string.IsNullOrWhiteSpace(bootstrapApex))
{
@@ -123,16 +139,57 @@ public static class ServiceCollectionExtensions
allowedHosts.Add($"*.{bootstrapApex}");
}
builder.Services.AddSingleton(new DomainConfig(apex, scheme));
builder.Services.AddSingleton(domain);
builder.Services.Configure<Microsoft.AspNetCore.HostFiltering.HostFilteringOptions>(opt =>
{
opt.AllowedHosts = allowedHosts.Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
opt.AllowEmptyHosts = true;
opt.IncludeFailureMessage = true;
});
Console.WriteLine($"[domain] apex={apex}, allowedHosts=[{string.Join(", ", allowedHosts)}]");
Console.WriteLine($"[domain] apex={apex}, style={domain.HostStyle}, allowedHosts=[{string.Join(", ", allowedHosts)}]");
}
private static readonly string[] KnownServiceSubdomains =
{
"admin",
"api",
"accounts",
"auth",
"bugreporting",
"cards",
"cdn",
"chat",
"clubs",
"cms",
"commerce",
"data",
"datacollection",
"discovery",
"econ",
"feed",
"gamelogs",
"geo",
"img",
"leaderboard",
"link",
"lists",
"match",
"moderation",
"notify",
"ns",
"platformnotifications",
"playersettings",
"roomcomments",
"roomieintegrations",
"rooms",
"storage",
"strings",
"strings-cdn",
"studio",
"thorn",
"videos",
};
// ── Services ──────────────────────────────────────────────────────────────
private static void AddCoreServices(IServiceCollection services)
{
+8 -1
View File
@@ -54,11 +54,18 @@ export function adminApex(): string {
const host = window.location.hostname.toLowerCase();
if (host === 'localhost' || host.endsWith('.localhost')) return 'localhost';
if (host.endsWith('-dev.dork.sh')) return 'dev.dork.sh';
const parts = host.split('.');
return parts.length > 2 ? parts.slice(1).join('.') : host;
}
function serviceHost(subdomain: string, apex: string): string {
return apex === 'dev.dork.sh'
? `${subdomain}-${apex}`
: `${subdomain}.${apex}`;
}
function subdomainUrl(subdomain: string, path: string | null | undefined, query?: string, preserveSlashes = false): string | null {
if (!path) return null;
const apex = adminApex();
@@ -67,7 +74,7 @@ function subdomainUrl(subdomain: string, path: string | null | undefined, query?
const encodedPath = preserveSlashes
? path.split('/').map(encodeURIComponent).join('/')
: encodeURIComponent(path);
return `${scheme}://${subdomain}.${apex}/${encodedPath}${suffix}`;
return `${scheme}://${serviceHost(subdomain, apex)}/${encodedPath}${suffix}`;
}
export interface Room {