feat: add d601 dev core manifests

This commit is contained in:
Codex
2026-05-17 18:16:30 +00:00
parent 40d03621c5
commit 465f4a626b
13 changed files with 448 additions and 19 deletions
+30
View File
@@ -155,6 +155,36 @@ code, pre, textarea { font-family: "Cascadia Mono", "IBM Plex Mono", "Liberation
border-bottom: 1px solid var(--line);
}
.dev-shell .topbar {
border-bottom-color: rgba(215, 161, 58, 0.72);
}
.dev-env-ribbon {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin-top: 5px;
color: #ffe2a3;
font-size: 11px;
letter-spacing: 0;
}
.dev-env-ribbon b,
.dev-env-ribbon span {
display: inline-flex;
align-items: center;
min-height: 20px;
padding: 2px 7px;
border: 1px solid rgba(215, 161, 58, 0.62);
background: rgba(215, 161, 58, 0.12);
}
.dev-env-ribbon b {
color: #111820;
background: #d7a13a;
}
.eyebrow, .panel-eyebrow {
margin: 0 0 2px;
color: var(--accent);
+26 -4
View File
@@ -36,6 +36,7 @@ function readRootJsonAttribute(name: string, fallback: any): any {
}
const cfg: AnyRecord = readRootJsonAttribute("data-config", { apiBaseUrl: "/api", authUsername: "admin" });
const environmentIdentity: AnyRecord = cfg.environment && typeof cfg.environment === "object" ? cfg.environment : {};
const initialCodeQueueOverview = readRootJsonAttribute("data-codex-overview", null);
const h = React.createElement;
const { useEffect, useMemo } = React;
@@ -71,6 +72,15 @@ function isDocumentVisible(): boolean {
return typeof document === "undefined" || document.visibilityState !== "hidden";
}
function isDevEnvironment(identity: AnyRecord): boolean {
return identity?.environment === "dev" || identity?.namespace === "unidesk-dev";
}
function shortCommit(value: any): string {
const text = typeof value === "string" ? value : "";
return text.length >= 7 ? text.slice(0, 7) : text || "unknown";
}
function shellRefreshIntervalMs(moduleId: string, tabId: string): number {
if (moduleId === "ops" && tabId === "status") return 5_000;
if (moduleId === "nodes" && tabId === "monitor") return 5_000;
@@ -515,8 +525,10 @@ function LoginScreen({ onLogin }: AnyRecord) {
);
}
function TopBar({ connection, lastRefresh, onRefresh, onLogout, session, clock, activeStatusItems = [], onNotificationToggle, unreadCount = 0 }: AnyRecord) {
function TopBar({ connection, lastRefresh, onRefresh, onLogout, session, clock, activeStatusItems = [], onNotificationToggle, unreadCount = 0, environment = {} }: AnyRecord) {
const devMode = isDevEnvironment(environment);
const statusItems = [
...(devMode ? [{ key: "environment", label: "环境", value: `${environment.namespace || "unidesk-dev"}`, tone: "warn" }] : []),
{ key: "core", label: "核心", value: connection.text, tone: connection.ok ? "ok" : "fail", testId: "conn-text" },
...(Array.isArray(activeStatusItems) ? activeStatusItems : []),
{ key: "refresh", label: "刷新", value: lastRefresh ? fmtClock(lastRefresh) : "未刷新" },
@@ -524,7 +536,16 @@ function TopBar({ connection, lastRefresh, onRefresh, onLogout, session, clock,
{ key: "user", label: "用户", value: session?.user?.username || "--", tone: "user" },
];
return h("header", { className: "topbar" },
h("div", null, h("p", { className: "eyebrow" }, "Distributed Work Platform"), h("h1", null, "UniDesk 控制平面")),
h("div", null,
h("p", { className: "eyebrow" }, "Distributed Work Platform"),
h("h1", null, "UniDesk 控制平面"),
devMode ? h("div", { className: "dev-env-ribbon", "data-testid": "dev-environment-ribbon" },
h("b", null, "DEV"),
h("span", null, environment.namespace || "unidesk-dev"),
h("span", null, environment.deployRef || "origin/deploy/dev"),
h("span", null, shortCommit(environment.commit || environment.requestedCommit)),
) : null,
),
h(TopStatusBar, {
className: "global-top-status",
title: "状态",
@@ -2350,10 +2371,11 @@ function Shell({ session, onLogout }: AnyRecord) {
const { unreadCount, notifications } = useNotification();
const latestNotification = notifications.length > 0 ? notifications[notifications.length - 1] : null;
return h("div", { className: `shell ${railCollapsed ? "rail-collapsed" : ""}`, "data-testid": "app-shell" },
const devMode = isDevEnvironment(environmentIdentity);
return h("div", { className: `shell ${railCollapsed ? "rail-collapsed" : ""} ${devMode ? "dev-shell" : ""}`, "data-testid": "app-shell" },
h(Sidebar, { activeModule, activeTabs, onNavigate: navigate, collapsed: railCollapsed, onToggle: () => setRailCollapsed((value: boolean) => !value) }),
h("main", { className: "workspace" },
h(TopBar, { connection, lastRefresh, onRefresh: refresh, onLogout: () => onLogout(true), session, clock, activeStatusItems, onNotificationToggle: () => setNotificationOpen((v: boolean) => !v), unreadCount }),
h(TopBar, { connection, lastRefresh, onRefresh: refresh, onLogout: () => onLogout(true), session, clock, activeStatusItems, onNotificationToggle: () => setNotificationOpen((v: boolean) => !v), unreadCount, environment: environmentIdentity }),
h(TabBar, { module, activeTab, onNavigate: navigate }),
h(LoadingContext.Provider, { value: refreshing },
h(WorkArea, { activeModule, activeTab, data: effectiveData, session, refresh, onRaw: openRaw, onNavigate: navigate }),
+41 -1
View File
@@ -14,6 +14,7 @@ interface RuntimeConfig {
sessionSecret: string;
sessionTtlSeconds: number;
logFile: string;
environment: RuntimeIdentity;
deploy: {
serviceId: string;
repo: string;
@@ -22,6 +23,17 @@ interface RuntimeConfig {
};
}
interface RuntimeIdentity {
environment: string;
namespace: string;
databaseName: string;
deployRef: string;
serviceId: string;
repo: string;
commit: string;
requestedCommit: string;
}
interface SessionPayload {
username: string;
expiresAt: number;
@@ -62,6 +74,7 @@ const clientConfig = JSON.stringify({
authUsername: config.authUsername,
sessionTtlSeconds: config.sessionTtlSeconds,
apiBaseUrl: "/api",
environment: config.environment,
});
const indexHtmlTemplate = readFileSync(join(publicDir, "index.html"), "utf8");
const indexHtmlRootMarker = '<div id="root" data-config="__UNIDESK_CONFIG__"></div>';
@@ -149,6 +162,16 @@ function readConfig(): RuntimeConfig {
sessionSecret: requiredEnv("SESSION_SECRET"),
sessionTtlSeconds: readNumberEnv("SESSION_TTL_SECONDS"),
logFile: requiredEnv("LOG_FILE"),
environment: {
environment: process.env.UNIDESK_ENV?.trim() || "prod",
namespace: process.env.UNIDESK_NAMESPACE?.trim() || "",
databaseName: process.env.UNIDESK_DATABASE_NAME?.trim() || process.env.UNIDESK_DEV_DATABASE_NAME?.trim() || "",
deployRef: process.env.UNIDESK_DEPLOY_REF?.trim() || "",
serviceId: process.env.UNIDESK_DEPLOY_SERVICE_ID?.trim() || "frontend",
repo: process.env.UNIDESK_DEPLOY_REPO?.trim() || "",
commit: process.env.UNIDESK_DEPLOY_COMMIT?.trim() || "",
requestedCommit: process.env.UNIDESK_DEPLOY_REQUESTED_COMMIT?.trim() || "",
},
deploy: {
serviceId: process.env.UNIDESK_DEPLOY_SERVICE_ID || "frontend",
repo: process.env.UNIDESK_DEPLOY_REPO || "",
@@ -158,6 +181,23 @@ function readConfig(): RuntimeConfig {
};
}
function isDevIdentity(identity: RuntimeIdentity): boolean {
return identity.environment === "dev" || identity.namespace === "unidesk-dev";
}
function frontendHealthPayload(): Record<string, unknown> {
const base = { ok: true, service: "unidesk-frontend", frontendPublicUrl: config.frontendPublicUrl, deploy: config.deploy };
if (!isDevIdentity(config.environment)) return base;
return {
...base,
environment: config.environment.environment,
namespace: config.environment.namespace,
databaseName: config.environment.databaseName,
serviceId: config.deploy.serviceId,
deployRef: config.environment.deployRef,
};
}
function createLogger(service: string, logFile: string) {
const writer = createHourlyJsonlWriter({
baseLogFile: logFile,
@@ -723,7 +763,7 @@ async function handleRequest(req: Request): Promise<Response> {
logger("debug", "request", { path: url.pathname });
try {
if (url.pathname === "/health") {
return jsonResponse({ ok: true, service: "unidesk-frontend", frontendPublicUrl: config.frontendPublicUrl, deploy: config.deploy });
return jsonResponse(frontendHealthPayload());
}
if (url.pathname === "/login" && req.method === "POST") return login(req);
if (url.pathname === "/logout" && req.method === "POST") return logout();