/* * SPEC: PJ2026-010103 HWPOD 服务。 * 实现引用: draft-2026-07-20-hwpod-temporal-split。 * 责任: 复用完整 Cloud Console 壳的 HWPOD L1 Web、原生会话和 API proxy。 */ import { fileURLToPath, URL } from "node:url"; import type { ServerResponse } from "node:http"; import vue from "@vitejs/plugin-vue"; import { defineConfig, type Plugin } from "vite"; import { parseWorkbenchNativeRuntimeConfig } from "../vite.config"; export default defineConfig(({ command }) => ({ root: fileURLToPath(new URL("..", import.meta.url)), cacheDir: ".state/vite/hwpod", resolve: { alias: { "@": fileURLToPath(new URL("../src", import.meta.url)) } }, build: { outDir: "hwpod/dist", emptyOutDir: true }, server: command === "serve" ? { host: requiredString(process.env.HWPOD_WEB_HOST, "HWPOD_WEB_HOST"), port: requiredPort(process.env.HWPOD_WEB_PORT), strictPort: true, proxy: hwpodNativeProxy(requiredApiUrl()) } : undefined, plugins: command === "serve" ? [nativeRuntimeConfigPlugin(process.env.HWPOD_WEB_RUNTIME_CONFIG), nativeSessionPlugin(process.env.HWPOD_WEB_ACCESS_PROFILE_JSON), healthPlugin(), vue()] : [vue()] })); export function hwpodNativeProxy(target: string) { return { "/v1/hwpod-node/ws": { target, changeOrigin: false, ws: true }, "/v1/hwpod": { target, changeOrigin: false }, "/v1/hwlab-node": { target, changeOrigin: false }, "/health/ready": { target, changeOrigin: false }, }; } function healthPlugin(): Plugin { return { name: "hwpod-native-health", configureServer(server) { server.middlewares.use((request, response, next) => { if (request.url !== "/health/live") return next(); response.statusCode = 200; response.setHeader("content-type", "application/json"); response.end(JSON.stringify({ ok: true, serviceId: "hwlab-hwpod-web", status: "live", valuesPrinted: false })); }); } }; } function nativeRuntimeConfigPlugin(serialized: string | undefined): Plugin { const config = parseWorkbenchNativeRuntimeConfig(requiredString(serialized, "HWPOD_WEB_RUNTIME_CONFIG")); const inlineJson = JSON.stringify(config).replace(/ { if (request.url !== "/v1/web-performance" || request.method !== "POST") return next(); response.writeHead(204, { "cache-control": "no-store" }); response.end(); }); }, transformIndexHtml() { return [{ tag: "script", injectTo: "head-prepend", children: `window.HWLAB_CLOUD_WEB_CONFIG=${inlineJson};` }]; }, }; } function nativeSessionPlugin(serialized: string | undefined): Plugin { const profile = parseHwpodNativeAccessProfile(serialized); const session = { authenticated: true, authMethod: "native-development", identityAuthority: "yaml-native-profile", sessionKind: "native-hwpod", user: { id: "native-hwpod", username: "native-hwpod", displayName: "HWPOD Native", role: "admin", status: "active" }, capabilities: { admin: "available" }, access: { nav: { profileId: profile.id, allowedIds: profile.allowedNavIds, valuesRedacted: true } }, valuesRedacted: true, }; return { name: "hwpod-native-session", configureServer(server) { server.middlewares.use((request, response, next) => { const pathname = new URL(request.url ?? "/", "http://hwpod-native").pathname; if (pathname === "/auth/session" && request.method === "GET") return sendJson(response, 200, session); if (pathname === "/auth/logout" && request.method === "POST") return sendJson(response, 200, { ok: true, authenticated: true, valuesRedacted: true }); return next(); }); } }; } export function parseHwpodNativeAccessProfile(serialized: string | undefined) { let value: unknown; try { value = JSON.parse(requiredString(serialized, "HWPOD_WEB_ACCESS_PROFILE_JSON")); } catch (error) { throw new Error(`HWPOD_WEB_ACCESS_PROFILE_JSON must be valid JSON: ${error instanceof Error ? error.message : String(error)}`); } if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("HWPOD_WEB_ACCESS_PROFILE_JSON must be an object"); const raw = value as Record; const id = requiredString(raw.id, "HWPOD_WEB_ACCESS_PROFILE_JSON.id"); const startPath = requiredString(raw.startPath, "HWPOD_WEB_ACCESS_PROFILE_JSON.startPath"); const allowedNavIds = Array.isArray(raw.allowedNavIds) ? raw.allowedNavIds.map((item) => requiredString(item, "HWPOD_WEB_ACCESS_PROFILE_JSON.allowedNavIds[]")) : []; if (allowedNavIds.length === 0) throw new Error("HWPOD_WEB_ACCESS_PROFILE_JSON.allowedNavIds must not be empty"); if (!startPath.startsWith("/")) throw new Error("HWPOD_WEB_ACCESS_PROFILE_JSON.startPath must be an absolute application path"); return { id, allowedNavIds, startPath }; } function sendJson(response: ServerResponse, status: number, body: unknown) { response.statusCode = status; response.setHeader("content-type", "application/json"); response.setHeader("cache-control", "no-store"); response.end(JSON.stringify(body)); } function requiredApiUrl() { const value = process.env.HWPOD_NATIVE_API_URL; if (!value) throw new Error("HWPOD_NATIVE_API_URL is required"); return value; } function requiredPort(value: string | undefined) { const port = Number.parseInt(String(value ?? ""), 10); if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("HWPOD_WEB_PORT is required and must be valid"); return port; } function requiredString(value: unknown, name: string) { const text = typeof value === "string" ? value.trim() : ""; if (!text) throw new Error(`${name} is required`); return text; }