feat: unify workspace sidebar and create Foundry graph pages
This commit is contained in:
+31
-2
@@ -4,11 +4,25 @@ import { spawn } from "node:child_process";
|
||||
import { once } from "node:events";
|
||||
import { fileURLToPath } from "node:url";
|
||||
const host = "http://127.0.0.1:3005";
|
||||
const upstream = createServer((req, res) => {
|
||||
const graphs = new Map();
|
||||
const upstream = createServer(async (req, res) => {
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
if (req.url.endsWith("/auth/logout")) {
|
||||
res.statusCode = 204;
|
||||
res.end();
|
||||
} else if (req.url === "/projects/endoleap/workspaces/space/materials" && req.method === "POST") {
|
||||
assert.equal(req.headers.authorization, "Bearer test-only-session");
|
||||
let raw = "";
|
||||
for await (const chunk of req) raw += chunk;
|
||||
const input = JSON.parse(raw);
|
||||
assert.deepEqual(input, { kind: "board", title: "未命名图谱", content: { schema_version: 1 } });
|
||||
const graph = { ...input, id: "foundry-generated-id", workspace_id: "space", version: 1 };
|
||||
graphs.set(graph.id, graph);
|
||||
res.statusCode = 201;
|
||||
res.end(JSON.stringify({ id: graph.id, workspace_id: graph.workspace_id, kind: graph.kind, version: graph.version }));
|
||||
} else if (req.url === "/projects/endoleap/workspaces/space/materials/foundry-generated-id") {
|
||||
assert.equal(req.headers.authorization, "Bearer test-only-session");
|
||||
res.end(JSON.stringify(graphs.get("foundry-generated-id")));
|
||||
} else if (req.url.includes("/workspaces/space/materials")) {
|
||||
res.end(JSON.stringify({ url: req.url, method: req.method }));
|
||||
} else if (req.url.endsWith("/auth/login")) {
|
||||
@@ -69,7 +83,7 @@ try {
|
||||
await new Promise((r) => setTimeout(r, 100));
|
||||
}
|
||||
assert(ready, "Frontend did not start");
|
||||
for (const path of ["/", "/login", "/auth/callback", "/dashboard", "/dashboard?demo=1"]) {
|
||||
for (const path of ["/", "/login", "/auth/callback", "/dashboard", "/dashboard?demo=1", "/dashboard/space/foundry-generated-id"]) {
|
||||
const page = await fetch(host + path);
|
||||
assert.equal(page.status, 200, path);
|
||||
const html = await page.text();
|
||||
@@ -102,6 +116,21 @@ try {
|
||||
200,
|
||||
);
|
||||
assert.equal((await fetch(host + "/api/foundry/profile")).status, 401);
|
||||
const create = await fetch(host + "/api/foundry/workspaces/space/materials", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", Cookie: session, Origin: host },
|
||||
body: JSON.stringify({ kind: "board", title: "未命名图谱", content: { schema_version: 1 } }),
|
||||
});
|
||||
assert.equal(create.status, 201);
|
||||
const graph = await create.json();
|
||||
assert.deepEqual(graph, { id: "foundry-generated-id", workspace_id: "space", kind: "board", version: 1 });
|
||||
const opened = await fetch(host + `/api/foundry/workspaces/${graph.workspace_id}/materials/${graph.id}`, { headers: { Cookie: session } });
|
||||
assert.equal(opened.status, 200);
|
||||
const stored = await opened.json();
|
||||
assert.equal(stored.title, "未命名图谱");
|
||||
assert.equal(stored.workspace_id, "space");
|
||||
assert.equal(stored.content.schema_version, 1);
|
||||
|
||||
for (const origin of ["https://example.com", "null"])
|
||||
assert.equal(
|
||||
(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createBoard, loadWorkspaceHome } from "../src/lib/workspace.ts";
|
||||
import { boardHref, createBoard, DEFAULT_BOARD_TITLE, loadWorkspaceHome } from "../src/lib/workspace.ts";
|
||||
|
||||
test("profile and workspace reads both start before either completes", async () => {
|
||||
const calls = [];
|
||||
@@ -80,3 +80,23 @@ test("an existing workspace posts a board directly", async () => {
|
||||
);
|
||||
assert.deepEqual(calls, ["workspaces/existing/materials"]);
|
||||
});
|
||||
|
||||
|
||||
test("new graphs use a default name and retain the Foundry-assigned identity", async () => {
|
||||
const created = await createBoard(async (path, options) => {
|
||||
assert.equal(path, "workspaces/space/materials");
|
||||
assert.deepEqual(JSON.parse(options.body), {
|
||||
kind: "board", title: DEFAULT_BOARD_TITLE, content: { schema_version: 1 },
|
||||
});
|
||||
return { id: "server-generated-id", workspace_id: "space", kind: "board", version: 1 };
|
||||
}, " ", "space", () => assert.fail("unexpected workspace creation"));
|
||||
assert.equal(created.id, "server-generated-id");
|
||||
assert.equal(created.workspace_id, "space");
|
||||
assert.equal(created.title, "未命名图谱");
|
||||
assert.equal(boardHref(created.workspace_id, created.id), "/dashboard/space/server-generated-id");
|
||||
});
|
||||
|
||||
test("graph URLs preserve workspace identity and safely encode route segments", () => {
|
||||
assert.equal(boardHref("space/a", "id?b"), "/dashboard/space%2Fa/id%3Fb");
|
||||
assert.equal(boardHref("demo", "board", true), "/dashboard/demo/board?demo=1");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user