import { eq, desc, and, like, or, count, sql } from 'drizzle-orm'; import { app, db, schema } from '../../app.ts'; import { MarkServices } from './services/mark.ts'; import z from 'zod'; app .route({ path: 'mark', key: 'list', description: 'mark list.', middleware: ['auth'], metadata: { args: { page: z.number().optional().describe('页码'), pageSize: z.number().optional().describe('每页数量'), search: z.string().optional().describe('搜索关键词'), markType: z.string().optional().describe('mark类型,simple,wallnote,md,draw等'), sort: z.enum(['DESC', 'ASC']).default('DESC').describe('排序字段'), } } }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; ctx.body = await MarkServices.getList({ uid: tokenUser.id, query: ctx.query, queryType: 'simple', }); }) .addTo(app); app .route({ path: 'mark', key: 'getVersion', middleware: ['auth'], metadata: { args: { id: z.string().describe('mark id'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const { id } = ctx.query; if (id) { const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); const markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } ctx.body = { version: Number(markModel.version), updatedAt: markModel.updatedAt, createdAt: markModel.createdAt, id: markModel.id, }; } else { ctx.throw(400, 'id is required'); } }) .addTo(app); app .route({ path: 'mark', key: 'get', middleware: ['auth'], metadata: { args: { id: z.string().describe('mark id'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const { id } = ctx.query; if (id) { const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); const markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } ctx.body = markModel; } else { ctx.throw(400, 'id is required'); } }) .addTo(app); app .route({ path: 'mark', key: 'update', middleware: ['auth'], isDebug: true, metadata: { args: { id: z.string().describe('mark id'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const { id, createdAt, updatedAt, uid: _, puid: _2, uname: _3, data, ...rest } = ctx.query.data || {}; let markModel: any; if (id) { const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } const version = Number(markModel.version) + 1; const updated = await db.update(schema.microMark) .set({ ...rest, data: { ...(markModel.data as any || {}), ...data, }, version, updatedAt: new Date().toISOString(), }) .where(eq(schema.microMark.id, id)) .returning(); markModel = updated[0]; } else { const inserted = await db.insert(schema.microMark).values({ data: data || {}, ...rest, uname: tokenUser.username, uid: tokenUser.id, puid: tokenUser.uid, }).returning(); markModel = inserted[0]; } ctx.body = markModel; }) .addTo(app); app .route({ path: 'mark', key: 'updateNode', middleware: ['auth'], metadata: { args: { id: z.string().describe('mark id'), operate: z.enum(['update', 'delete']).default('update').describe('节点操作类型,update或delete'), data: z.object({ id: z.string().describe('节点id'), node: z.any().describe('要更新的节点数据'), }).describe('要更新的节点数据'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const operate = ctx.query.operate || 'update'; const { id, node } = ctx.query.data || {}; const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); const markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } // Update JSON node logic with Drizzle const currentData = markModel.data as any || {}; const nodes = currentData.nodes || []; const nodeIndex = nodes.findIndex((n: any) => n.id === node.id); let updatedNodes; if (operate === 'delete') { updatedNodes = nodes.filter((n: any) => n.id !== node.id); } else if (nodeIndex >= 0) { updatedNodes = [...nodes]; updatedNodes[nodeIndex] = { ...nodes[nodeIndex], ...node }; } else { updatedNodes = [...nodes, node]; } const version = Number(markModel.version) + 1; const updated = await db.update(schema.microMark) .set({ data: { ...currentData, nodes: updatedNodes }, version, updatedAt: new Date().toISOString(), }) .where(eq(schema.microMark.id, id)) .returning(); ctx.body = updated[0]; }) .addTo(app); app .route({ path: 'mark', key: 'updateNodes', middleware: ['auth'], metadata: { args: { id: z.string().describe('mark id'), nodeOperateList: z.array(z.object({ operate: z.enum(['update', 'delete']).default('update').describe('节点操作类型,update或delete'), node: z.any().describe('要更新的节点数据'), })).describe('要更新的节点列表'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const { id, nodeOperateList } = ctx.query.data || {}; const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); const markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } if (!nodeOperateList || !Array.isArray(nodeOperateList) || nodeOperateList.length === 0) { ctx.throw(400, 'nodeOperateList is required'); } if (nodeOperateList.some((item: any) => !item.node)) { ctx.throw(400, 'nodeOperateList node is required'); } // Update multiple JSON nodes logic with Drizzle const currentData = markModel.data as any || {}; let nodes = currentData.nodes || []; for (const item of nodeOperateList) { const { node, operate = 'update' } = item; const nodeIndex = nodes.findIndex((n: any) => n.id === node.id); if (operate === 'delete') { nodes = nodes.filter((n: any) => n.id !== node.id); } else if (nodeIndex >= 0) { nodes[nodeIndex] = { ...nodes[nodeIndex], ...node }; } else { nodes.push(node); } } const version = Number(markModel.version) + 1; const updated = await db.update(schema.microMark) .set({ data: { ...currentData, nodes }, version, updatedAt: new Date().toISOString(), }) .where(eq(schema.microMark.id, id)) .returning(); ctx.body = updated[0]; }) .addTo(app); app .route({ path: 'mark', key: 'delete', middleware: ['auth'], metadata: { args: { id: z.string().describe('mark id'), } }, }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const { id } = ctx.query; const marks = await db.select().from(schema.microMark).where(eq(schema.microMark.id, id)).limit(1); const markModel = marks[0]; if (!markModel) { ctx.throw(404, 'mark not found'); } if (markModel.uid !== tokenUser.id) { ctx.throw(403, 'no permission'); } await db.delete(schema.microMark).where(eq(schema.microMark.id, id)); ctx.body = markModel; }) .addTo(app); app .route({ path: 'mark', key: 'getMenu', description: '获取菜单', middleware: ['auth'] }) .define(async (ctx) => { const tokenUser = ctx.state.tokenUser; const [rows, totalResult] = await Promise.all([ db.select({ id: schema.microMark.id, title: schema.microMark.title, summary: schema.microMark.summary, tags: schema.microMark.tags, thumbnail: schema.microMark.thumbnail, link: schema.microMark.link, createdAt: schema.microMark.createdAt, updatedAt: schema.microMark.updatedAt, }).from(schema.microMark).where(eq(schema.microMark.uid, tokenUser.id)), db.select({ count: count() }).from(schema.microMark).where(eq(schema.microMark.uid, tokenUser.id)) ]); ctx.body = { list: rows, total: totalResult[0]?.count || 0, }; }) .addTo(app);