fix(panel): 服务器地址改为一键复制并带上非默认游戏端口,玩家列表去掉无意义的连接地址列,管理员列改称内部地址
This commit is contained in:
20 files changed
+379
-59
No files matched your search
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user