fix(panel): 服务器地址改为一键复制并带上非默认游戏端口,玩家列表去掉无意义的连接地址列,管理员列改称内部地址

This commit is contained in:
Lemon-miaow committed 2026-09-25 12:51:16 +08:00
1 parent bb9c2168df
commit f156e385f0
20 files changed
+379 -59

No files matched your search

+27
View File
@@ -105,3 +105,30 @@ describe("versionLabel", () => {
expect(versionLabel({ version: "dev", release: "dev", dev: true })).toBe("dev");
});
});
describe("joinAddress", () => {
const base = { apiBase: "/api/v1", rootDomain: "mc.example" };
it("is the bare hostname on the default port", async () => {
const { joinAddress } = await freshConfig();
expect(joinAddress("survival", base)).toBe("survival.mc.example");
expect(joinAddress("survival", { ...base, gamePort: 25565 })).toBe("survival.mc.example");
});
it("adds a port the proxy moved off the default", async () => {
const { joinAddress } = await freshConfig();
expect(joinAddress("survival", { ...base, gamePort: 25570 })).toBe("survival.mc.example:25570");
});
it("reads the port from config.json and drops one no client could dial", async () => {
serve({ apiBase: "/api/v1", rootDomain: "mc.example", gamePort: 25570 });
let mod = await freshConfig();
expect((await mod.loadConfig()).gamePort).toBe(25570);
for (const bad of [0, 70000, "25570", 1.5]) {
serve({ apiBase: "/api/v1", rootDomain: "mc.example", gamePort: bad });
mod = await freshConfig();
expect((await mod.loadConfig()).gamePort).toBeUndefined();
}
});
});
+17
View File
@@ -22,6 +22,8 @@ export interface RuntimeConfig {
panelHostname?: string;
/** Operator-console hostname (op.console.<root>), absent when unconfigured. */
adminHostname?: string;
/** The public Minecraft port, absent when it is the default 25565. */
gamePort?: number;
/** What the server runs, for the version badge. */
build?: BuildInfo;
/** /config.json could not be read, so these are build-time defaults and
@@ -35,6 +37,13 @@ const FALLBACK: RuntimeConfig = {
fallback: true,
};
// A port that could not be dialed is dropped; the bare hostname is the safer guess.
function parsePort(raw: unknown): number | undefined {
return Number.isInteger(raw) && (raw as number) > 0 && (raw as number) <= 65535
? (raw as number)
: undefined;
}
function parseBuild(raw: unknown): BuildInfo | undefined {
if (!raw || typeof raw !== "object") return undefined;
const b = raw as Partial<BuildInfo>;
@@ -73,6 +82,7 @@ export async function loadConfig(): Promise<RuntimeConfig> {
rootDomain: raw.rootDomain,
panelHostname: raw.panelHostname,
adminHostname: raw.adminHostname,
gamePort: parsePort(raw.gamePort),
build: parseBuild(raw.build),
};
} catch (err) {
@@ -87,3 +97,10 @@ export async function loadConfig(): Promise<RuntimeConfig> {
export function hostFor(subdomain: string, cfg: RuntimeConfig): string {
return `${subdomain}.${cfg.rootDomain}`;
}
/** joinAddress is what a player types into Minecraft to reach a server: its
* hostname, with the port only when the proxy listens off the default 25565. */
export function joinAddress(subdomain: string, cfg: RuntimeConfig): string {
const host = hostFor(subdomain, cfg);
return cfg.gamePort && cfg.gamePort !== 25565 ? `${host}:${cfg.gamePort}` : host;
}