Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/config/runtime.ts
T
2026-06-20 16:26:54 +08:00

118 lines
4.4 KiB
TypeScript

import type { WorkbenchTraceTimelinePolicy } from "@/components/workbench/trace-lifecycle";
export interface DisplayTimeConfig {
timeZone: string;
locale: string;
label: string;
}
const DEFAULT_DISPLAY_DATE_TIME_OPTIONS: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false
};
const TRACE_ID_PATTERN = /^[A-Za-z0-9_.:-]{3,160}$/u;
const TRACE_EXPLORER_TEMPLATE_TOKEN = "{trace_id}";
const TRACE_EXPLORER_TEMPLATE_PLACEHOLDER_PATTERN = /\{([A-Za-z0-9_:-]+)\}/gu;
export function displayTimeConfig(): DisplayTimeConfig {
const config = window.HWLAB_CLOUD_WEB_CONFIG?.displayTime;
const timeZone = requiredDisplayTimeField(config?.timeZone, "timeZone");
const locale = requiredDisplayTimeField(config?.locale, "locale");
const label = requiredDisplayTimeField(config?.label, "label");
validateDisplayTimeZone(timeZone);
validateDisplayLocale(locale);
return { timeZone, locale, label };
}
export function displayTimeLabel(): string {
return displayTimeConfig().label;
}
export function formatDisplayDateTime(value: string | Date, options: Intl.DateTimeFormatOptions = {}): string {
const date = value instanceof Date ? value : new Date(value);
if (!Number.isFinite(date.getTime())) return String(value);
const config = displayTimeConfig();
const dateTimeOptions = Object.keys(options).length > 0 ? options : DEFAULT_DISPLAY_DATE_TIME_OPTIONS;
return new Intl.DateTimeFormat(config.locale, { hour12: false, ...dateTimeOptions, timeZone: config.timeZone }).format(date);
}
export function formatDisplayClock(value: string | Date): string {
return formatDisplayDateTime(value, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}
function requiredDisplayTimeField(value: unknown, field: string): string {
if (typeof value !== "string" || value.trim().length === 0) {
throw new Error(`HWLAB Cloud Web displayTime.${field} is required`);
}
return value.trim();
}
function validateDisplayTimeZone(timeZone: string): void {
try {
new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date(0));
} catch {
throw new Error(`HWLAB Cloud Web displayTime.timeZone is invalid: ${timeZone}`);
}
}
function validateDisplayLocale(locale: string): void {
try {
new Intl.DateTimeFormat(locale).format(new Date(0));
} catch {
throw new Error(`HWLAB Cloud Web displayTime.locale is invalid: ${locale}`);
}
}
export function workbenchTraceTimelinePolicy(): WorkbenchTraceTimelinePolicy {
const config = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.traceTimeline;
return {
autoExpandRunning: config?.autoExpandRunning === true,
autoCollapseTerminal: config?.autoCollapseTerminal === true
};
}
export function traceExplorerHref(traceId: string | null | undefined): string | null {
const safeTraceId = normalizedTraceId(traceId);
if (!safeTraceId) return null;
const template = window.HWLAB_CLOUD_WEB_CONFIG?.workbench?.traceExplorerUrlTemplate;
if (typeof template !== "string" || !isSafeTraceExplorerUrlTemplate(template)) return null;
const rendered = template.trim().replaceAll(TRACE_EXPLORER_TEMPLATE_TOKEN, encodeURIComponent(safeTraceId));
try {
const url = new URL(rendered, runtimeOrigin());
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
return rendered;
} catch {
return null;
}
}
function normalizedTraceId(value: string | null | undefined): string | null {
const traceId = typeof value === "string" ? value.trim() : "";
return TRACE_ID_PATTERN.test(traceId) ? traceId : null;
}
function isSafeTraceExplorerUrlTemplate(template: string): boolean {
const trimmed = template.trim();
if (!trimmed.includes(TRACE_EXPLORER_TEMPLATE_TOKEN) || trimmed.startsWith("//")) return false;
const placeholders = Array.from(trimmed.matchAll(TRACE_EXPLORER_TEMPLATE_PLACEHOLDER_PATTERN), (match) => match[0]);
if (placeholders.length === 0 || placeholders.some((placeholder) => placeholder !== TRACE_EXPLORER_TEMPLATE_TOKEN)) return false;
try {
const rendered = trimmed.replaceAll(TRACE_EXPLORER_TEMPLATE_TOKEN, "trc_template_probe");
const url = new URL(rendered, runtimeOrigin());
return url.protocol === "http:" || url.protocol === "https:";
} catch {
return false;
}
}
function runtimeOrigin(): string {
return typeof window.location?.origin === "string" && window.location.origin.length > 0
? window.location.origin
: "http://hwlab-cloud-web.local";
}