fix: configure HWLAB external entry
This commit is contained in:
@@ -6,6 +6,9 @@ displayTime:
|
||||
timeZone: Asia/Shanghai
|
||||
locale: zh-CN
|
||||
label: 北京时间
|
||||
applications:
|
||||
hwlab:
|
||||
publicUrl: https://hwlab.pikapython.com
|
||||
publicExposure:
|
||||
enabled: true
|
||||
publicBaseUrl: https://unidesk.pikapython.com
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from "react";
|
||||
import { LoadingTitle } from "./loading-indicator";
|
||||
import { externalApplicationConfig } from "./runtime-config";
|
||||
|
||||
type AnyRecord = Record<string, any>;
|
||||
|
||||
const h = React.createElement;
|
||||
|
||||
export const HWLAB_URL = "http://74.48.78.17:6666";
|
||||
export const HWLAB_URL = externalApplicationConfig("hwlab").publicUrl;
|
||||
|
||||
function StatusBadge({ status, children }: AnyRecord) {
|
||||
const normalized = String(status || "unknown").toLowerCase();
|
||||
@@ -81,7 +82,7 @@ export function HwlabPage(): any {
|
||||
h("article", { className: "metric-card" },
|
||||
h("div", { className: "metric-label" }, "目标 URL"),
|
||||
h("div", { className: "metric-value hwlab-url" }, HWLAB_URL),
|
||||
h("div", { className: "metric-hint" }, "fixed destination"),
|
||||
h("div", { className: "metric-hint" }, "config/frontend.yaml"),
|
||||
),
|
||||
h("article", { className: "metric-card" },
|
||||
h("div", { className: "metric-label" }, "后端依赖"),
|
||||
|
||||
@@ -11,6 +11,7 @@ interface RuntimeConfig {
|
||||
frontendPublicUrl: string;
|
||||
providerIngressPublicUrl: string;
|
||||
displayTime: DisplayTimeConfig;
|
||||
applications: FrontendApplicationsConfig;
|
||||
authUsername: string;
|
||||
authPassword: string;
|
||||
providerToken: string | null;
|
||||
@@ -46,6 +47,14 @@ interface DisplayTimeConfig {
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface FrontendApplicationsConfig {
|
||||
hwlab: ExternalApplicationConfig;
|
||||
}
|
||||
|
||||
interface ExternalApplicationConfig {
|
||||
publicUrl: string;
|
||||
}
|
||||
|
||||
interface SessionPayload {
|
||||
username: string;
|
||||
expiresAt: number;
|
||||
@@ -96,6 +105,7 @@ const clientConfig = JSON.stringify({
|
||||
frontendPublicUrl: config.frontendPublicUrl,
|
||||
providerIngressPublicUrl: config.providerIngressPublicUrl,
|
||||
displayTime: config.displayTime,
|
||||
applications: config.applications,
|
||||
authUsername: config.authUsername,
|
||||
sessionTtlSeconds: config.sessionTtlSeconds,
|
||||
apiBaseUrl: "/api",
|
||||
@@ -214,21 +224,42 @@ function validateLocale(value: string, path: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
function readDisplayTimeConfig(): DisplayTimeConfig {
|
||||
function validateHttpUrl(value: string, path: string): void {
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(value);
|
||||
} catch {
|
||||
throw new Error(`${path} must be a valid URL`);
|
||||
}
|
||||
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
||||
throw new Error(`${path} must use http or https`);
|
||||
}
|
||||
}
|
||||
|
||||
function readFrontendFileConfig(): { displayTime: DisplayTimeConfig; applications: FrontendApplicationsConfig } {
|
||||
const configPath = join(import.meta.dir, "../../../..", "config", "frontend.yaml");
|
||||
const parsed = Bun.YAML.parse(readFileSync(configPath, "utf8")) as unknown;
|
||||
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("config/frontend.yaml must be an object");
|
||||
const root = parsed as Record<string, unknown>;
|
||||
if (root.kind !== "UniDeskFrontendConfig") throw new Error("config/frontend.yaml.kind must be UniDeskFrontendConfig");
|
||||
const displayTime = recordField(root, "displayTime", "config/frontend.yaml");
|
||||
const result = {
|
||||
const displayTimeConfig = {
|
||||
timeZone: requiredStringField(displayTime, "timeZone", "config/frontend.yaml.displayTime"),
|
||||
locale: requiredStringField(displayTime, "locale", "config/frontend.yaml.displayTime"),
|
||||
label: requiredStringField(displayTime, "label", "config/frontend.yaml.displayTime"),
|
||||
};
|
||||
validateTimeZone(result.timeZone, "config/frontend.yaml.displayTime.timeZone");
|
||||
validateLocale(result.locale, "config/frontend.yaml.displayTime.locale");
|
||||
return result;
|
||||
validateTimeZone(displayTimeConfig.timeZone, "config/frontend.yaml.displayTime.timeZone");
|
||||
validateLocale(displayTimeConfig.locale, "config/frontend.yaml.displayTime.locale");
|
||||
const applications = recordField(root, "applications", "config/frontend.yaml");
|
||||
const hwlab = recordField(applications, "hwlab", "config/frontend.yaml.applications");
|
||||
const hwlabPublicUrl = requiredStringField(hwlab, "publicUrl", "config/frontend.yaml.applications.hwlab");
|
||||
validateHttpUrl(hwlabPublicUrl, "config/frontend.yaml.applications.hwlab.publicUrl");
|
||||
return {
|
||||
displayTime: displayTimeConfig,
|
||||
applications: {
|
||||
hwlab: { publicUrl: hwlabPublicUrl },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function readNumberEnv(name: string): number {
|
||||
@@ -247,6 +278,7 @@ function deployEnv(name: "REF" | "SERVICE_ID" | "REPO" | "COMMIT" | "REQUESTED_C
|
||||
}
|
||||
|
||||
function readConfig(): RuntimeConfig {
|
||||
const frontendFileConfig = readFrontendFileConfig();
|
||||
const serviceId = deployEnv("SERVICE_ID") || "frontend";
|
||||
const repo = deployEnv("REPO");
|
||||
const commit = deployEnv("COMMIT");
|
||||
@@ -256,7 +288,8 @@ function readConfig(): RuntimeConfig {
|
||||
coreInternalUrl: requiredEnv("CORE_INTERNAL_URL"),
|
||||
frontendPublicUrl: requiredEnv("FRONTEND_PUBLIC_URL"),
|
||||
providerIngressPublicUrl: requiredEnv("PROVIDER_INGRESS_PUBLIC_URL"),
|
||||
displayTime: readDisplayTimeConfig(),
|
||||
displayTime: frontendFileConfig.displayTime,
|
||||
applications: frontendFileConfig.applications,
|
||||
authUsername: requiredEnv("AUTH_USERNAME"),
|
||||
authPassword: requiredEnv("AUTH_PASSWORD"),
|
||||
providerToken: optionalEnv("PROVIDER_TOKEN")
|
||||
|
||||
@@ -6,6 +6,10 @@ export interface DisplayTimeConfig {
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface ExternalApplicationConfig {
|
||||
publicUrl: string;
|
||||
}
|
||||
|
||||
let cachedFrontendConfig: AnyRecord | null = null;
|
||||
let cachedDisplayTimeConfig: DisplayTimeConfig | null = null;
|
||||
|
||||
@@ -69,3 +73,25 @@ export function displayTimeConfig(): DisplayTimeConfig {
|
||||
cachedDisplayTimeConfig = config;
|
||||
return config;
|
||||
}
|
||||
|
||||
export function externalApplicationConfig(id: string): ExternalApplicationConfig {
|
||||
const applications = frontendConfig().applications;
|
||||
if (typeof applications !== "object" || applications === null || Array.isArray(applications)) {
|
||||
throw new Error("frontend data-config.applications is required");
|
||||
}
|
||||
const application = (applications as AnyRecord)[id];
|
||||
if (typeof application !== "object" || application === null || Array.isArray(application)) {
|
||||
throw new Error(`frontend data-config.applications.${id} is required`);
|
||||
}
|
||||
const publicUrl = requiredStringField(application as AnyRecord, "publicUrl", `frontend data-config.applications.${id}`);
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(publicUrl);
|
||||
} catch {
|
||||
throw new Error(`frontend data-config.applications.${id}.publicUrl must be a valid URL`);
|
||||
}
|
||||
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
||||
throw new Error(`frontend data-config.applications.${id}.publicUrl must use http or https`);
|
||||
}
|
||||
return { publicUrl: parsed.toString().replace(/\/$/u, "") };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user