113 lines
7.7 KiB
JavaScript
113 lines
7.7 KiB
JavaScript
import { existsSync, 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 ?? 43122);
|
|
const projectId = "00000000-0000-4000-8000-000000000904";
|
|
const dynamicRoot = process.env.DADA_DYNAMIC_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_interactive", "单模板归档", "templates");
|
|
const stickerRoot = process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材");
|
|
const textRoot = process.env.DADA_TEXT_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_text");
|
|
|
|
const publicAssets = {
|
|
STK001: [join(stickerRoot, "sticker_part1", "01af6384c2a962d17f55736f9895b505.png"), "image/png"],
|
|
STK002: [join(stickerRoot, "sticker_part2", "011db0fb6ac4184e4a708374003bce66.png"), "image/png"],
|
|
"15974853bc3294ef68e7e6d58fe74fd7": [join(dynamicRoot, "DYN002", "fonts", "15974853bc3294ef68e7e6d58fe74fd7", "0202b90o6r57rxed4027b5689e0dxe7e142r0yho9d0lljuqj.ttf"), "font/ttf"],
|
|
"46f8336813e4c48d06a1aef294fdccf6": [join(dynamicRoot, "DYN016", "fonts", "46f8336813e4c48d06a1aef294fdccf6", "9fbfbb420cea1df916d7c7c7ac90b1c88b61e117-PingFang-SC-Semibold-2.ttf"), "font/ttf"],
|
|
"53ca6b704728520da50c145eabb2e635": [join(dynamicRoot, "DYN007", "fonts", "53ca6b704728520da50c145eabb2e635", "fab6c26a0b21d5e9b57fb5238843ac1fb77a2ce6-HYZhengYuan.ttf"), "font/ttf"],
|
|
"cca5efc0e02fb1bf62349bd68ef30fc1": [join(dynamicRoot, "DYN015", "fonts", "cca5efc0e02fb1bf62349bd68ef30fc1", "e11ced673fc7e63e8b0b4730166d29845d8bebae-NotoSansCJKsc-Regular.otf"), "font/otf"],
|
|
"dd25b35dcb7ba4476cbaa9a9592e39e2": [join(dynamicRoot, "DYN001", "fonts", "dd25b35dcb7ba4476cbaa9a9592e39e2", "0202b90o6r57rxed4027b5689e0dxe7e142r0ygbx80porvko.ttf"), "font/ttf"],
|
|
"e4210c9872f0c279b35273f230809821": [join(dynamicRoot, "DYN011", "fonts", "e4210c9872f0c279b35273f230809821", "06b980259e2104e1211a6819a61bc5ddeca77dcb-DJB-Get-Digital-1.ttf"), "font/ttf"],
|
|
"f4bfd4132df2d6be97ceabadf3853505": [join(dynamicRoot, "DYN008", "fonts", "f4bfd4132df2d6be97ceabadf3853505", "6ce05a147aedabbb610d9cb3e75bbe60c064c3f5-BarlowCondensed-SemiBold.ttf"), "font/ttf"],
|
|
"DYN001-image28": [join(dynamicRoot, "DYN001", "resource", "image28.png"), "image/png"],
|
|
"DYN002-image29": [join(dynamicRoot, "DYN002", "resource", "image29.png"), "image/png"],
|
|
"DYN003-image30": [join(dynamicRoot, "DYN003", "resource", "image30.png"), "image/png"],
|
|
"DYN004-image32": [join(dynamicRoot, "DYN004", "resource", "image32.png"), "image/png"],
|
|
"DYN008-backendui0": [join(dynamicRoot, "DYN008", "resource", "backendui0.png"), "image/png"],
|
|
"DYN011-backendui0": [join(dynamicRoot, "DYN011", "resource", "backendui0.png"), "image/png"],
|
|
"DYN015-imager2": [join(dynamicRoot, "DYN015", "resource", "imager2_2.png"), "image/png"],
|
|
"DYN016-image21": [join(dynamicRoot, "DYN016", "resource", "image21.png"), "image/png"],
|
|
FONT081: [join(textRoot, "字体", "面板全量采集", "font_panel_full_20260722", "resources", "font_packages", "FONT081_Lexend Deca", "font_files", "02034l0o6r57rxed4027b5689e0dxe7e142r0vi8920akeqto.ttf"), "font/ttf"],
|
|
};
|
|
|
|
if (!Object.values(publicAssets).every(([path]) => existsSync(path))) throw new Error("WP4-04 original preview assets are unavailable.");
|
|
|
|
const session = {
|
|
audience: "user", authenticated: true, credits: { available_balance: 10, reserved_balance: 0 },
|
|
csrf_token: "csrf-wp4-manual-preview-0000000000000000000000000000", 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-000000000901" },
|
|
};
|
|
const backgroundId = "00000000-0000-4000-8000-000000000902";
|
|
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 = `<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="1440"><rect width="1080" height="1440" fill="#2b3038"/><rect x="0" y="0" width="216" height="1440" fill="#04d960"/><rect x="216" y="0" width="216" height="1440" fill="#0abf58"/><rect x="432" y="0" width="216" height="1440" fill="#5fd994"/><rect x="648" y="0" width="216" height="1440" fill="#a0f2c4"/><rect x="864" y="0" width="216" height="1440" fill="#d5f2e2"/><rect x="0" y="0" width="1080" height="1440" fill="#15181d" opacity=".58"/></svg>`;
|
|
|
|
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: "wp4-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-000000000903", 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");
|
|
if (request.method === "POST" && url.pathname === "/api/v1/location/reverse-geocode") return sendJson(response, { formatted_value: "浙江省温州市", service_mode: "mock", status: "resolved" });
|
|
const publicMatch = url.pathname.match(/^\/api\/v1\/assets\/public\/([^/]+)\/([^/]+)$/);
|
|
if (request.method === "GET" && publicMatch) {
|
|
const assetId = decodeURIComponent(publicMatch[2]);
|
|
const asset = publicAssets[assetId];
|
|
if (!asset) return sendJson(response, { error: "not_found" }, 404);
|
|
return send(response, 200, readFileSync(asset[0]), asset[1]);
|
|
}
|
|
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(`WP4-04 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)); });
|