import { existsSync, readdirSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; import { join, resolve } from "node:path"; import { createServer } from "vite"; const host = "127.0.0.1"; const port = Number(process.env.DADA_MANUAL_PREVIEW_PORT ?? 43123); const projectId = "00000000-0000-4000-8000-000000000802"; const sourceRoot = process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材"); const stickerPaths = new Map(); for (let part = 1, index = 1; part <= 25; part += 1) { const partRoot = join(sourceRoot, `sticker_part${part}`); if (!existsSync(partRoot)) throw new Error(`Missing sticker source part ${part}.`); const files = readdirSync(partRoot).filter((name) => name.toLowerCase().endsWith(".png")).sort(); for (const file of files) { const id = `STK${String(index).padStart(index < 1_000 ? 3 : 4, "0")}`; stickerPaths.set(id, join(partRoot, file)); index += 1; } } if (stickerPaths.size !== 1_407) throw new Error(`Expected 1,407 stickers, found ${stickerPaths.size}.`); const session = { audience: "user", authenticated: true, credits: { available_balance: 10, reserved_balance: 0 }, csrf_token: "csrf-wp5-02-manual-000000000000000000000000000000", expires_at: "2026-09-03T08:00:00.000Z", user: { creator_name: "Dada Creator", role: "user", social_id: "@dada", status: "active", user_id: "00000000-0000-4000-8000-000000000802" }, }; const backgroundId = "00000000-0000-4000-8000-000000000803"; let stateVersion = 1; let canvasState = { background: { adjustments: { brightness: 0, contrast: 0, crop: null, filter: "none", fit: "fill", saturation: 0, sharpness: 0, temperature: 0 }, asset_id: backgroundId }, elements: [], pixel_height: 1440, pixel_width: 1080, ratio: "3:4", schema_version: 1, }; const backgroundSvg = ``; function send(response, status, body, contentType) { response.statusCode = status; response.setHeader("Content-Type", contentType); response.end(body); } function sendJson(response, value, status = 200) { send(response, status, JSON.stringify(value), "application/json; charset=utf-8"); } function readJson(request) { return new Promise((resolveBody, reject) => { const chunks = []; request.on("data", (chunk) => chunks.push(chunk)); request.on("end", () => { try { resolveBody(JSON.parse(Buffer.concat(chunks).toString("utf8"))); } catch (error) { reject(error); } }); request.on("error", reject); }); } const mockApi = { name: "wp5-02-manual-preview-api", configureServer(server) { server.middlewares.use((request, response, next) => { const url = new URL(request.url ?? "/", `http://${host}:${port}`); if (request.method === "GET" && url.pathname === "/api/v1/auth/session") return sendJson(response, session); if (request.method === "GET" && url.pathname === "/api/v1/assets/recent") return sendJson(response, { items: [] }); if (request.method === "GET" && url.pathname === `/api/v1/projects/${projectId}`) return sendJson(response, { canvas_state: canvasState, created_at: "2026-08-03T08:00:00.000Z", current_image_id: backgroundId, images: [{ created_at: "2026-08-03T08:00:00.000Z", generation_id: "00000000-0000-4000-8000-000000000804", image_id: backgroundId }], name: "普通贴纸目录人工检查", pixel_height: 1440, pixel_width: 1080, project_id: projectId, ratio: "3:4", state_version: stateVersion, }); if (request.method === "PUT" && url.pathname === `/api/v1/projects/${projectId}/state`) { void readJson(request).then((body) => { canvasState = body.canvas_state; stateVersion += 1; sendJson(response, { save_status: "saved", state_version: stateVersion }); }).catch(() => sendJson(response, { error: "invalid_state" }, 400)); return; } if (request.method === "GET" && url.pathname === `/api/v1/private-assets/projects/${projectId}/images/${backgroundId}`) return send(response, 200, backgroundSvg, "image/svg+xml"); const publicMatch = url.pathname.match(/^\/api\/v1\/assets\/public\/([^/]+)\/([^/]+)$/); if (request.method === "GET" && publicMatch) { const assetId = decodeURIComponent(publicMatch[2]); const path = stickerPaths.get(assetId); if (!path) return sendJson(response, { error: "not_found" }, 404); return send(response, 200, readFileSync(path), "image/png"); } next(); }); }, }; const vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), plugins: [mockApi], root: resolve("apps/web"), server: { host, port, strictPort: true } }); await vite.listen(); console.log(`WP5-02 manual preview: http://${host}:${port}/app/projects/${projectId}/editor`); for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => { void vite.close().finally(() => process.exit(0)); });