diff --git a/server/routes/folders.ts b/server/routes/folders.ts new file mode 100644 index 0000000..c00f23f --- /dev/null +++ b/server/routes/folders.ts @@ -0,0 +1,44 @@ +import { Hono } from 'hono' +import { eq, and } from 'drizzle-orm' +import { db, folders, users } from '../db' +import { requireAuth } from '../middleware/auth' + +export const foldersRouter = new Hono() +foldersRouter.use('*', requireAuth) + +async function checkCloud(c: any): Promise { + const userId = c.get('userId') + const [user] = db.select().from(users).where(eq(users.id, userId)).all() + if (!user?.cloudEnabled) { + await c.json({ error: '请先激活云存储(填写邀请码)' }, 403) + return null + } + return userId +} + +foldersRouter.put('/:id', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + const f = await c.req.json() + + db.insert(folders).values({ + id: c.req.param('id'), userId, name: f.name, + parentId: f.parentId ?? null, order: f.order ?? 0, createdAt: f.createdAt, + }).onConflictDoUpdate({ + target: folders.id, + set: { name: f.name, parentId: f.parentId ?? null, order: f.order ?? 0 }, + }).run() + + return c.json({ ok: true }) +}) + +foldersRouter.delete('/:id', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + + db.delete(folders) + .where(and(eq(folders.id, c.req.param('id')), eq(folders.userId, userId))) + .run() + + return c.json({ ok: true }) +}) diff --git a/server/routes/notes.ts b/server/routes/notes.ts new file mode 100644 index 0000000..e7aed9e --- /dev/null +++ b/server/routes/notes.ts @@ -0,0 +1,102 @@ +import { Hono } from 'hono' +import { eq, and } from 'drizzle-orm' +import { db, notes, folders, users } from '../db' +import { requireAuth } from '../middleware/auth' + +export const notesRouter = new Hono() +notesRouter.use('*', requireAuth) + +async function checkCloud(c: any): Promise { + const userId = c.get('userId') + const [user] = db.select().from(users).where(eq(users.id, userId)).all() + if (!user?.cloudEnabled) { + await c.json({ error: '请先激活云存储(填写邀请码)' }, 403) + return null + } + return userId +} + +notesRouter.get('/sync', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + + const userNotes = db.select().from(notes).where(eq(notes.userId, userId)).all() + const userFolders = db.select().from(folders).where(eq(folders.userId, userId)).all() + + return c.json({ + notes: userNotes.map(n => ({ ...n, tags: JSON.parse(n.tags) })), + folders: userFolders, + }) +}) + +notesRouter.post('/sync', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + + const { notes: clientNotes, folders: clientFolders } = await c.req.json<{ + notes: any[] + folders: any[] + }>() + + for (const n of clientNotes ?? []) { + db.insert(notes).values({ + id: n.id, userId, title: n.title, content: n.content, + folderId: n.folderId ?? null, tags: JSON.stringify(n.tags ?? []), + starred: n.starred ? 1 : 0, wordCount: n.wordCount ?? 0, + deletedAt: n.deletedAt ?? null, createdAt: n.createdAt, updatedAt: n.updatedAt, + }).onConflictDoUpdate({ + target: notes.id, + set: { + title: n.title, content: n.content, folderId: n.folderId ?? null, + tags: JSON.stringify(n.tags ?? []), starred: n.starred ? 1 : 0, + wordCount: n.wordCount ?? 0, deletedAt: n.deletedAt ?? null, updatedAt: n.updatedAt, + }, + }).run() + } + + for (const f of clientFolders ?? []) { + db.insert(folders).values({ + id: f.id, userId, name: f.name, parentId: f.parentId ?? null, + order: f.order ?? 0, createdAt: f.createdAt, + }).onConflictDoUpdate({ + target: folders.id, + set: { name: f.name, parentId: f.parentId ?? null, order: f.order ?? 0 }, + }).run() + } + + return c.json({ synced: (clientNotes?.length ?? 0) + (clientFolders?.length ?? 0) }) +}) + +notesRouter.put('/:id', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + const n = await c.req.json() + + db.insert(notes).values({ + id: c.req.param('id'), userId, title: n.title, content: n.content, + folderId: n.folderId ?? null, tags: JSON.stringify(n.tags ?? []), + starred: n.starred ? 1 : 0, wordCount: n.wordCount ?? 0, + deletedAt: n.deletedAt ?? null, createdAt: n.createdAt, updatedAt: n.updatedAt, + }).onConflictDoUpdate({ + target: notes.id, + set: { + title: n.title, content: n.content, folderId: n.folderId ?? null, + tags: JSON.stringify(n.tags ?? []), starred: n.starred ? 1 : 0, + wordCount: n.wordCount ?? 0, deletedAt: n.deletedAt ?? null, updatedAt: n.updatedAt, + }, + }).run() + + return c.json({ ok: true }) +}) + +notesRouter.delete('/:id', async (c) => { + const userId = await checkCloud(c) + if (!userId) return + + db.update(notes) + .set({ deletedAt: Date.now() }) + .where(and(eq(notes.id, c.req.param('id')), eq(notes.userId, userId))) + .run() + + return c.json({ ok: true }) +})