feat: cut over rust web main shell
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const assert = require("node:assert");
|
||||
const {
|
||||
fetchWithTimeout,
|
||||
findFreePort,
|
||||
startGateway,
|
||||
waitForGateway,
|
||||
} = require("./task114-rust-web-gateway-entry-smoke.js");
|
||||
|
||||
const EXTERNAL_BASE_URL = (process.env.MNOTE_UI_BASE_URL || "").replace(/\/+$/, "");
|
||||
const CONFIGURED_DOCUMENT_ID = (process.env.MNOTE_DOCUMENT_ID || "").trim();
|
||||
const CONFIGURED_WORKSPACE_ID = (process.env.MNOTE_WORKSPACE_ID || "").trim();
|
||||
const CONFIGURED_TITLE = (process.env.MNOTE_DOCUMENT_TITLE || "").trim();
|
||||
|
||||
async function readJsonResponse(response, label) {
|
||||
const text = await response.text();
|
||||
let payload = null;
|
||||
try {
|
||||
payload = text ? JSON.parse(text) : null;
|
||||
} catch {
|
||||
throw new Error(`${label} 返回了非 JSON 内容: ${text.slice(0, 1200)}`);
|
||||
}
|
||||
assert(response.ok, `${label} 请求失败: ${response.status}; body: ${text.slice(0, 1200)}`);
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function createTempDocument(baseUrl) {
|
||||
const title = `task115-rust-web-${Date.now()}`;
|
||||
const requestBody = {
|
||||
action: "create",
|
||||
title,
|
||||
};
|
||||
if (CONFIGURED_WORKSPACE_ID) {
|
||||
requestBody.workspaceId = CONFIGURED_WORKSPACE_ID;
|
||||
}
|
||||
|
||||
const response = await fetchWithTimeout(`${baseUrl}/api/tree/commands`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
const payload = await readJsonResponse(response, "创建临时文档");
|
||||
const result = payload && typeof payload.result === "object" ? payload.result : null;
|
||||
const documentId = result && typeof result.documentId === "string" ? result.documentId : "";
|
||||
const workspaceId = result && typeof result.workspaceId === "string" ? result.workspaceId : "";
|
||||
assert(documentId, "创建临时文档缺少 documentId");
|
||||
assert(workspaceId, "创建临时文档缺少 workspaceId");
|
||||
return { documentId, workspaceId, title };
|
||||
}
|
||||
|
||||
async function purgeTempDocument(baseUrl, target) {
|
||||
const response = await fetchWithTimeout(`${baseUrl}/api/tree/commands`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
action: "purge",
|
||||
workspaceId: target.workspaceId,
|
||||
documentId: target.documentId,
|
||||
}),
|
||||
});
|
||||
await readJsonResponse(response, "清理临时文档");
|
||||
}
|
||||
|
||||
async function validateDocumentShell(baseUrl, target) {
|
||||
const targetPath = `/documents/${encodeURIComponent(target.documentId)}?workspaceId=${encodeURIComponent(target.workspaceId)}`;
|
||||
const response = await fetchWithTimeout(`${baseUrl}${targetPath}`);
|
||||
const html = await response.text();
|
||||
assert.equal(response.status, 200, `文档 shell 请求失败: ${response.status}; body: ${html.slice(0, 1200)}`);
|
||||
assert.equal(response.headers.get("x-mnote-web-owner"), "mnote-web");
|
||||
assert.equal(response.headers.get("x-mnote-web-shell"), "document");
|
||||
assert.match(response.headers.get("content-type") || "", /text\/html/);
|
||||
assert.match(html, /data-mnote-shell="document"/);
|
||||
assert.match(html, /data-testid="wolai-sidebar"/);
|
||||
assert.match(html, /data-testid="wolai-topbar"/);
|
||||
assert.match(html, /data-testid="wolai-floating-ai"/);
|
||||
assert.match(html, /data-page-aggregate-snapshot="mnote\.page_aggregate\.v1"/);
|
||||
assert.match(html, /id="__MNOTE_PAGE_AGGREGATE__"/);
|
||||
assert.match(html, /data-editor-host="leptos_tiptap_island"/);
|
||||
assert.doesNotMatch(html, /mnote-web-document-shell/);
|
||||
assert.doesNotMatch(html, /convex_upstream_error|未登录/);
|
||||
|
||||
const aggregate = await fetchWithTimeout(
|
||||
`${baseUrl}/api/page-aggregate/${encodeURIComponent(target.documentId)}?workspaceId=${encodeURIComponent(target.workspaceId)}`,
|
||||
);
|
||||
const aggregateText = await aggregate.text();
|
||||
assert.equal(aggregate.status, 200, `page aggregate 请求失败: ${aggregate.status}; body: ${aggregateText.slice(0, 1200)}`);
|
||||
const payload = JSON.parse(aggregateText);
|
||||
assert.equal(aggregate.headers.get("x-mnote-web-owner"), "mnote-web");
|
||||
assert.equal(payload.owner, "mnote-web");
|
||||
assert.equal(payload.schema, "mnote.page_aggregate.v1");
|
||||
assert.equal(payload.result.identity.documentId, target.documentId);
|
||||
assert.equal(payload.result.identity.workspaceId, target.workspaceId);
|
||||
assert.equal(payload.result.schema, "mnote.page_aggregate.v1");
|
||||
assert.equal(typeof payload.result.head.title, "string");
|
||||
assert(payload.result.head.title.trim(), "Page Aggregate 标题不能为空");
|
||||
if (target.expectedTitle) {
|
||||
assert.equal(payload.result.head.title, target.expectedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let gateway = null;
|
||||
let baseUrl = EXTERNAL_BASE_URL;
|
||||
let createdTarget = null;
|
||||
|
||||
if (!baseUrl) {
|
||||
const port = await findFreePort();
|
||||
baseUrl = `http://127.0.0.1:${port}`;
|
||||
gateway = startGateway(port);
|
||||
await waitForGateway(baseUrl);
|
||||
}
|
||||
|
||||
try {
|
||||
const target = CONFIGURED_DOCUMENT_ID
|
||||
? {
|
||||
documentId: CONFIGURED_DOCUMENT_ID,
|
||||
workspaceId: CONFIGURED_WORKSPACE_ID || "ws_demo",
|
||||
expectedTitle: CONFIGURED_TITLE || null,
|
||||
}
|
||||
: gateway
|
||||
? {
|
||||
documentId: "doc_1",
|
||||
workspaceId: "ws_demo",
|
||||
expectedTitle: CONFIGURED_TITLE || "服务端页面",
|
||||
}
|
||||
: await createTempDocument(baseUrl);
|
||||
if (!CONFIGURED_DOCUMENT_ID && !gateway) {
|
||||
createdTarget = target;
|
||||
target.expectedTitle = target.title;
|
||||
}
|
||||
|
||||
await validateDocumentShell(baseUrl, target);
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
ok: true,
|
||||
baseUrl,
|
||||
owner: "mnote-web",
|
||||
shell: "document",
|
||||
editorHost: "leptos_tiptap_island",
|
||||
documentId: target.documentId,
|
||||
workspaceId: target.workspaceId,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
if (createdTarget) {
|
||||
await purgeTempDocument(baseUrl, createdTarget);
|
||||
}
|
||||
if (gateway && gateway.pid) {
|
||||
gateway.kill("SIGTERM");
|
||||
setTimeout(() => {
|
||||
if (!gateway.killed) {
|
||||
gateway.kill("SIGKILL");
|
||||
}
|
||||
}, 2000).unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error instanceof Error ? error.stack || error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user