docs(app): explain common API session workspace and geometry helpers
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
/**
|
||||
* 工作区和画布目录的应用模型与CRUD辅助。这里保存路由/目录元数据约定,
|
||||
* 不拥有画布内容、ProjectEngine或本地history;服务响应更强schema校验由相应调用路径负责。
|
||||
*/
|
||||
import type { Profile } from "./api";
|
||||
|
||||
/** 工作区目录项,slug可用于路由,role供UI显示/能力提示,最终权限仍由服务端决定。 */
|
||||
export type Workspace = { id: string; slug?: string; name: string; role?: "admin" | "editor" | "viewer" };
|
||||
/** 画布目录记录:id与workspace是真实身份,parent_board_id/frame_id表示旧子图入口关系。
|
||||
* version是material元数据版本,不能自动当作canonical canvas版本或本地rootRevision。 */
|
||||
export type Board = {
|
||||
id: string;
|
||||
workspace_id: string;
|
||||
@@ -13,14 +20,19 @@ export type Board = {
|
||||
parent_board_id?: string;
|
||||
frame_id?: string;
|
||||
};
|
||||
/** 路由可用id或slug,空item/ref返回false;匹配本身不证明账户有访问权限。 */
|
||||
export function matchesRef(item: { id: string; slug?: string } | null | undefined, ref: string | undefined) { return Boolean(item && ref && (item.id === ref || item.slug === ref)); }
|
||||
/** 创建时空白标题回退值,与重命名必须非空的校验不同。 */
|
||||
export const DEFAULT_BOARD_TITLE = "未命名图谱";
|
||||
|
||||
/** 分别编码workspace和board路径段,demo只追加明确查询开关。 */
|
||||
export function boardHref(workspace: string, board: string, demo = false) {
|
||||
return `/dashboard/${encodeURIComponent(workspace)}/${encodeURIComponent(board)}${demo ? "?demo=1" : ""}`;
|
||||
}
|
||||
/** 注入通用API能力,不在该库固定fetch、凭证或错误展示。 */
|
||||
type Request = <T>(path: string, options?: RequestInit) => Promise<T>;
|
||||
|
||||
/** 并行读profile和工作区目录,兼容直接数组与{items}外壳;仅返回数据,不选择当前工作区。 */
|
||||
export async function loadWorkspaceHome(
|
||||
request: Request,
|
||||
signal?: AbortSignal,
|
||||
@@ -35,6 +47,8 @@ export async function loadWorkspaceHome(
|
||||
};
|
||||
}
|
||||
|
||||
/** 无workspaceId时先创建个人空间并回调宿主,再创建schema1旧画布目录项。
|
||||
* 两个POST不是一笔原子事务,后一步失败不会自动删除已创建空间;标题trim后允许默认名。 */
|
||||
export async function createBoard(
|
||||
request: Request,
|
||||
title: string,
|
||||
@@ -51,6 +65,7 @@ export async function createBoard(
|
||||
onWorkspaceCreated(workspace);
|
||||
}
|
||||
const name = title.trim() || DEFAULT_BOARD_TITLE;
|
||||
// 创建响应可缺workspace_id时补用本次目标id,返回已确定的标题供界面更新目录。
|
||||
const result = await request<{ id: string; workspace_id: string; kind: "board"; version: number; slug?: string }>("workspaces/" + id + "/materials", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
@@ -63,10 +78,13 @@ export async function createBoard(
|
||||
}
|
||||
|
||||
|
||||
/** trim并限制标题UTF-8字节≤300,先读取匹配的board,再用If-Match条件PATCH元数据。
|
||||
* PATCH保留现有description/content;返回version按接口+1约定推导,不解析PATCH回执验证该版本。 */
|
||||
export async function renameBoard(request: Request, workspaceId: string, boardId: string, title: string): Promise<Board> {
|
||||
const name = title.trim();
|
||||
if (!name || new TextEncoder().encode(name).length > 300) throw new Error("invalid_title");
|
||||
const path = `workspaces/${encodeURIComponent(workspaceId)}/materials/${encodeURIComponent(boardId)}`;
|
||||
// 校验读取的kind/id或slug/workspace,避免路由引用指向另一空间记录时误写。
|
||||
const current = await request<Board>(path);
|
||||
if (current.kind !== "board" || !matchesRef(current, boardId) || current.workspace_id !== workspaceId) throw new Error("invalid_reference");
|
||||
await request(path, {
|
||||
|
||||
Reference in New Issue
Block a user