Files
Felis/panel/src/lib/config.ts
T
flyemoji eee00c2772 feat(panel): add three-sided web console (User, Admin, SysAdmin)
Vite + TypeScript + Tailwind single-page console presenting the three operator tiers and consuming the external felis-api face. Build output and design notes are not tracked.
2026-06-26 23:32:39 +09:00

41 lines
1.5 KiB
TypeScript

// Runtime config: the panel is domain-agnostic at BUILD time (CI red line — no
// rootDomain literal may appear in source). apiBase + rootDomain are fetched from
// /config.json at boot, so one build serves any deployment (mirrors the Go side's
// --set root_domain portability, spec §16/§27). A dev fallback reads VITE_* env.
export interface RuntimeConfig {
apiBase: string;
rootDomain: string;
}
const FALLBACK: RuntimeConfig = {
apiBase: import.meta.env.VITE_API_BASE ?? "/api/v1",
rootDomain: import.meta.env.VITE_ROOT_DOMAIN ?? "localhost",
};
let cached: RuntimeConfig | null = null;
/** loadConfig fetches /config.json once and caches it; failures fall back to env
* so the SPA still mounts (and surfaces the misconfig in the UI). */
export async function loadConfig(): Promise<RuntimeConfig> {
if (cached) return cached;
try {
const res = await fetch("/config.json", { cache: "no-store" });
if (!res.ok) throw new Error(`config.json ${res.status}`);
const raw = (await res.json()) as Partial<RuntimeConfig>;
cached = {
apiBase: raw.apiBase ?? FALLBACK.apiBase,
rootDomain: raw.rootDomain ?? FALLBACK.rootDomain,
};
} catch {
cached = FALLBACK;
}
return cached;
}
/** hostFor composes a server's public hostname from its subdomain and the runtime
* rootDomain — never a hardcoded domain (red line). */
export function hostFor(subdomain: string, cfg: RuntimeConfig): string {
return `${subdomain}.${cfg.rootDomain}`;
}