0.6 rust重构01

This commit is contained in:
lix-2026
2026-04-14 13:22:29 +08:00
parent 71fb1aee7e
commit 84a8454fa9
401 changed files with 5841 additions and 1512 deletions
+345
View File
@@ -0,0 +1,345 @@
import { randomUUID } from "crypto";
import { HttpError, requireAuthContext } from "@/lib/auth/authContext";
import { apiErrorResponse } from "@/lib/api-utils";
import type { PageOptionsState } from "@/types/page-options";
export type BridgeActor = {
actorType: string;
actorId: string;
sessionId: string | null;
};
export type BridgeSource = {
channel: string;
client: string;
};
export type BridgeTarget = {
workspaceId?: string | null;
pageId?: string | null;
blockId?: string | null;
};
export type BridgeRequestMeta = {
idempotencyKey: string | null;
validateOnly: boolean;
dryRun: boolean;
};
export type BridgeContext = {
deploymentId: string | null;
projectId: string | null;
workspaceId: string | null;
requestId: string;
traceId: string;
actor: BridgeActor;
source: BridgeSource;
tenantId: string | null;
authToken: string | null;
idempotencyKey: string | null;
validateOnly: boolean;
dryRun: boolean;
};
export type BridgeErrorCode =
| "VALIDATION_ERROR"
| "UNAUTHORIZED"
| "FORBIDDEN"
| "NOT_FOUND"
| "TRANSPORT_ERROR"
| "REJECTED";
export class DocumentBridgeError extends Error {
constructor(
message: string,
public readonly status: number,
public readonly code: BridgeErrorCode,
public readonly details?: unknown,
) {
super(message);
this.name = "DocumentBridgeError";
}
}
export type CommandEnvelope<T> = {
name: string;
commandId: string;
idempotencyKey: string | null;
actor: BridgeActor;
source: BridgeSource;
target: BridgeTarget | null;
payload: T;
reason: string | null;
refs: string[];
dryRun: boolean;
validateOnly: boolean;
};
export type QueryEnvelope<T> = {
name: string;
payload: T;
};
function readHeaderValue(headerList: Headers, ...candidates: string[]): string | null {
for (const candidate of candidates) {
const value = headerList.get(candidate);
if (typeof value === "string" && value.trim()) {
return value.trim();
}
}
return null;
}
function toBooleanFlag(raw: string | null): boolean {
if (!raw) return false;
return raw === "1" || raw.toLowerCase() === "true";
}
function makeFallbackId(prefix: string): string {
return `${prefix}_${randomUUID()}`;
}
export async function buildDocumentBridgeContext(input: {
request: Request;
workspaceId?: string | null;
idempotencyKey?: string | null;
validateOnly?: boolean;
dryRun?: boolean;
}): Promise<BridgeContext> {
let auth;
try {
auth = await requireAuthContext();
} catch (error) {
if (error instanceof HttpError) {
throw new DocumentBridgeError(error.message || "未登录", error.status, "UNAUTHORIZED");
}
throw new DocumentBridgeError("未登录", 401, "UNAUTHORIZED");
}
const headerList = input.request.headers;
const requestId =
readHeaderValue(headerList, "x-request-id", "x-mnote-request-id") ?? makeFallbackId("req");
const traceId =
readHeaderValue(headerList, "x-trace-id", "x-mnote-trace-id", "x-request-id") ?? makeFallbackId("trace");
const sessionId = readHeaderValue(headerList, "x-session-id", "x-mnote-session-id");
const sourceChannel = readHeaderValue(headerList, "x-source-channel") ?? "next-route";
const sourceClient = readHeaderValue(headerList, "x-source-client", "user-agent") ?? "wolai-frontend";
const deploymentId =
readHeaderValue(headerList, "x-deployment-id") ?? process.env.VERCEL_DEPLOYMENT_ID ?? null;
const projectId = readHeaderValue(headerList, "x-project-id") ?? process.env.VERCEL_PROJECT_ID ?? null;
const tenantId = readHeaderValue(headerList, "x-tenant-id");
const authToken = readHeaderValue(headerList, "authorization");
const idempotencyKey =
input.idempotencyKey ?? readHeaderValue(headerList, "idempotency-key", "x-idempotency-key");
const validateOnly = input.validateOnly ?? toBooleanFlag(readHeaderValue(headerList, "x-validate-only"));
const dryRun = input.dryRun ?? toBooleanFlag(readHeaderValue(headerList, "x-dry-run"));
return {
deploymentId,
projectId,
workspaceId: input.workspaceId ?? null,
requestId,
traceId,
actor: {
actorType: "user",
actorId: auth.userId,
sessionId,
},
source: {
channel: sourceChannel,
client: sourceClient,
},
tenantId,
authToken,
idempotencyKey,
validateOnly,
dryRun,
};
}
export function buildDocumentCommandEnvelope<T>(input: {
name: string;
payload: T;
context: BridgeContext;
target?: BridgeTarget | null;
reason?: string | null;
refs?: string[];
}): CommandEnvelope<T> {
return {
name: input.name,
commandId: makeFallbackId("cmd"),
idempotencyKey: input.context.idempotencyKey,
actor: input.context.actor,
source: input.context.source,
target: input.target ?? null,
payload: input.payload,
reason: input.reason ?? null,
refs: input.refs ?? [],
dryRun: input.context.dryRun,
validateOnly: input.context.validateOnly,
};
}
export function buildDocumentQueryEnvelope<T>(input: { name: string; payload: T }): QueryEnvelope<T> {
return {
name: input.name,
payload: input.payload,
};
}
export function assertDocumentId(documentId: string | null | undefined): string {
const normalized = typeof documentId === "string" ? documentId.trim() : "";
if (!normalized) {
throw new DocumentBridgeError("缺少 documentId", 400, "VALIDATION_ERROR", [
{ field: "documentId", reason: "required" },
]);
}
return normalized;
}
export function assertTitle(title: string | null | undefined): string {
if (typeof title !== "string") {
throw new DocumentBridgeError("缺少 title", 400, "VALIDATION_ERROR", [
{ field: "title", reason: "required" },
]);
}
const normalized = title.trim() || "无标题";
return normalized;
}
export function assertBlockId(blockId: string | null | undefined): string {
const normalized = typeof blockId === "string" ? blockId.trim() : "";
if (!normalized) {
throw new DocumentBridgeError("缺少 blockId", 400, "VALIDATION_ERROR", [
{ field: "blockId", reason: "required" },
]);
}
return normalized;
}
export function assertNextBlock(nextBlock: unknown): asserts nextBlock is Record<string, unknown> {
if (!nextBlock || typeof nextBlock !== "object" || Array.isArray(nextBlock)) {
throw new DocumentBridgeError("缺少 nextBlock", 400, "VALIDATION_ERROR", [
{ field: "nextBlock", reason: "required object" },
]);
}
}
export function assertStats(stats: unknown): asserts stats is {
wordCount: number;
characterCount: number;
blockCount: number;
todoTotal: number;
todoDone: number;
} {
if (!stats || typeof stats !== "object") {
throw new DocumentBridgeError("缺少 stats", 400, "VALIDATION_ERROR", [
{ field: "stats", reason: "required" },
]);
}
const record = stats as Record<string, unknown>;
const fields = ["wordCount", "characterCount", "blockCount", "todoTotal", "todoDone"] as const;
for (const field of fields) {
if (typeof record[field] !== "number" || !Number.isFinite(record[field] as number)) {
throw new DocumentBridgeError("stats 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "must be finite number" },
]);
}
}
}
const PAGE_FONT_VALUES = new Set<PageOptionsState["pageFont"]>(["default", "song", "kai"]);
const PAGE_LAYOUT_DENSITY_VALUES = new Set<PageOptionsState["layoutDensity"]>(["compact", "normal", "spacious"]);
const PAGE_OPTION_BOOLEAN_FIELDS = [
"wideLayout",
"smallText",
"showHeadingNumbers",
"showToc",
"showStructure",
"protectEditing",
"showWordCount",
"collapseBacklinks",
"hideChildPages",
"showBlockRefCount",
] as const satisfies readonly (keyof PageOptionsState)[];
const PAGE_OPTION_ALLOWED_FIELDS = new Set<keyof PageOptionsState>([
...PAGE_OPTION_BOOLEAN_FIELDS,
"pageFont",
"layoutDensity",
"embedDefaultBlockId",
]);
export function assertOptionsPatch(
options: unknown,
): asserts options is Partial<Pick<PageOptionsState, keyof PageOptionsState>> {
if (!options || typeof options !== "object" || Array.isArray(options)) {
throw new DocumentBridgeError("缺少 options", 400, "VALIDATION_ERROR", [
{ field: "options", reason: "required object" },
]);
}
const record = options as Record<string, unknown>;
const entries = Object.entries(record);
if (entries.length === 0) {
throw new DocumentBridgeError("缺少 options", 400, "VALIDATION_ERROR", [
{ field: "options", reason: "must not be empty" },
]);
}
for (const [field, value] of entries) {
if (!PAGE_OPTION_ALLOWED_FIELDS.has(field as keyof PageOptionsState)) {
throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "unexpected field" },
]);
}
if ((PAGE_OPTION_BOOLEAN_FIELDS as readonly string[]).includes(field)) {
if (typeof value !== "boolean") {
throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "must be boolean" },
]);
}
continue;
}
if (field === "pageFont") {
if (typeof value !== "string" || !PAGE_FONT_VALUES.has(value as PageOptionsState["pageFont"])) {
throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "must be one of default/song/kai" },
]);
}
continue;
}
if (field === "layoutDensity") {
if (
typeof value !== "string" ||
!PAGE_LAYOUT_DENSITY_VALUES.has(value as PageOptionsState["layoutDensity"])
) {
throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "must be one of compact/normal/spacious" },
]);
}
continue;
}
if (field === "embedDefaultBlockId" && value !== null && typeof value !== "string") {
throw new DocumentBridgeError("options 字段非法", 400, "VALIDATION_ERROR", [
{ field, reason: "must be string or null" },
]);
}
}
}
export function documentBridgeErrorResponse(error: unknown) {
if (error instanceof DocumentBridgeError) {
return apiErrorResponse(error.message, error.status, {
code: error.code,
details: error.details,
});
}
if (error instanceof HttpError) {
return apiErrorResponse(error.message || "未登录", error.status);
}
return apiErrorResponse(error instanceof Error ? error.message : "服务器错误", 500);
}