import { mutation, query } from "./_generated/server"; import { v } from "convex/values"; import { nowIso } from "./_utils/time"; import type { MutationCtx, QueryCtx } from "./_generated/server"; async function assertWorkspaceMember( ctx: QueryCtx | MutationCtx, userId: string, workspaceId: string, ) { const membership = await ctx.db .query("workspace_members") .withIndex("by_workspace_user", (q) => q.eq("workspace_id", workspaceId).eq("user_id", userId), ) .first(); if (!membership) { throw new Error("无权访问该工作空间"); } return membership; } export const getById = query({ args: { userId: v.string(), workspaceId: v.string(), id: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); return await ctx.db .query("pages") .withIndex("by_page_id", (q) => q.eq("id", args.id)) .first(); }, }); export const listByWorkspace = query({ args: { userId: v.string(), workspaceId: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); return await ctx.db .query("pages") .withIndex("by_workspace", (q) => q.eq("workspace_id", args.workspaceId)) .collect(); }, }); export const create = mutation({ args: { userId: v.string(), id: v.string(), workspaceId: v.string(), parentId: v.union(v.string(), v.null()), title: v.string(), accessScope: v.union( v.literal("private"), v.literal("shared"), v.literal("public"), ), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const ts = nowIso(); await ctx.db.insert("pages", { id: args.id, workspace_id: args.workspaceId, parent_id: args.parentId, title: args.title, access_scope: args.accessScope, block_ids: [], child_page_ids: [], asset_ids: [], created_by: args.userId, created_at: ts, updated_at: ts, deleted_at: null, }); return { ok: true, id: args.id }; }, }); export const rename = mutation({ args: { userId: v.string(), workspaceId: v.string(), id: v.string(), title: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const page = await ctx.db .query("pages") .withIndex("by_page_id", (q) => q.eq("id", args.id)) .first(); if (!page) throw new Error("页面不存在"); await ctx.db.patch(page._id, { title: args.title, updated_at: nowIso(), }); return { ok: true }; }, }); export const move = mutation({ args: { userId: v.string(), workspaceId: v.string(), id: v.string(), parentId: v.union(v.string(), v.null()), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const page = await ctx.db .query("pages") .withIndex("by_page_id", (q) => q.eq("id", args.id)) .first(); if (!page) throw new Error("页面不存在"); await ctx.db.patch(page._id, { parent_id: args.parentId, updated_at: nowIso(), }); return { ok: true }; }, }); export const archive = mutation({ args: { userId: v.string(), workspaceId: v.string(), id: v.string(), }, handler: async (ctx, args) => { await assertWorkspaceMember(ctx, args.userId, args.workspaceId); const page = await ctx.db .query("pages") .withIndex("by_page_id", (q) => q.eq("id", args.id)) .first(); if (!page) throw new Error("页面不存在"); const ts = nowIso(); await ctx.db.patch(page._id, { deleted_at: ts, updated_at: ts, }); return { ok: true }; }, });