import { Hono } from 'hono'; import { eq, desc, inArray } from 'drizzle-orm'; import { prompts, images } from '../db/schema'; import type { Env, Variables } from '../index'; export const promptsApi = new Hono<{ Bindings: Env; Variables: Variables }>(); const uid = () => crypto.randomUUID(); const now = () => Date.now(); // List promptsApi.get('/', async (c) => { const db = c.var.db; const rows = await db.select().from(prompts).orderBy(desc(prompts.updatedAt)); // Attach images per prompt const ids = rows.map((r) => r.id); const imgs = ids.length ? await db.select().from(images).where(inArray(images.promptId, ids)) : []; const grouped: Record = {}; for (const im of imgs) { (grouped[im.promptId] ||= []).push(im); } return c.json( rows.map((r) => ({ ...r, tags: safeJson(r.tags || '[]', []), images: (grouped[r.id] || []).sort((a, b) => a.createdAt - b.createdAt), })) ); }); // Get one promptsApi.get('/:id', async (c) => { const db = c.var.db; const id = c.req.param('id'); const row = (await db.select().from(prompts).where(eq(prompts.id, id))).at(0); if (!row) return c.json({ error: 'not_found' }, 404); const imgs = await db.select().from(images).where(eq(images.promptId, id)); return c.json({ ...row, tags: safeJson(row.tags || '[]', []), images: imgs.sort((a, b) => a.createdAt - b.createdAt), }); }); // Create promptsApi.post('/', async (c) => { const db = c.var.db; const body = await c.req.json<{ title: string; contentImage?: string; contentVideo?: string; tags?: string[]; sourceUrl?: string; }>(); if (!body.title?.trim()) return c.json({ error: 'title_required' }, 400); const ci = (body.contentImage || '').trim(); const cv = (body.contentVideo || '').trim(); if (!ci && !cv) return c.json({ error: 'prompt_required' }, 400); const id = uid(); const t = now(); await db.insert(prompts).values({ id, title: body.title.trim(), contentImage: ci, contentVideo: cv, tags: JSON.stringify(body.tags || []), sourceUrl: body.sourceUrl?.trim() || null, isFavorite: false, createdAt: t, updatedAt: t, }); return c.json({ id }); }); // Update promptsApi.put('/:id', async (c) => { const db = c.var.db; const id = c.req.param('id'); const body = await c.req.json<{ title?: string; contentImage?: string; contentVideo?: string; tags?: string[]; sourceUrl?: string; }>(); const exists = (await db.select().from(prompts).where(eq(prompts.id, id))).at(0); if (!exists) return c.json({ error: 'not_found' }, 404); const ci = body.contentImage !== undefined ? body.contentImage.trim() : exists.contentImage || ''; const cv = body.contentVideo !== undefined ? body.contentVideo.trim() : exists.contentVideo || ''; if (!ci && !cv) return c.json({ error: 'prompt_required' }, 400); const promptChanged = ci !== (exists.contentImage || '') || cv !== (exists.contentVideo || ''); await db .update(prompts) .set({ title: body.title?.trim() || exists.title, contentImage: ci, contentVideo: cv, tags: body.tags !== undefined ? JSON.stringify(body.tags) : exists.tags, sourceUrl: body.sourceUrl !== undefined ? body.sourceUrl.trim() || null : exists.sourceUrl, // 提示词变了,清掉旧译文 translationImage: promptChanged ? null : exists.translationImage, translationVideo: promptChanged ? null : exists.translationVideo, updatedAt: now(), }) .where(eq(prompts.id, id)); return c.json({ ok: true }); }); // Delete promptsApi.delete('/:id', async (c) => { const db = c.var.db; const id = c.req.param('id'); await db.delete(images).where(eq(images.promptId, id)); await db.delete(prompts).where(eq(prompts.id, id)); return c.json({ ok: true }); }); // Toggle favorite promptsApi.post('/:id/favorite', async (c) => { const db = c.var.db; const id = c.req.param('id'); const row = (await db.select().from(prompts).where(eq(prompts.id, id))).at(0); if (!row) return c.json({ error: 'not_found' }, 404); await db .update(prompts) .set({ isFavorite: !row.isFavorite, updatedAt: now() }) .where(eq(prompts.id, id)); return c.json({ isFavorite: !row.isFavorite }); }); function safeJson(s: string, fb: T): T { try { return JSON.parse(s) as T; } catch { return fb; } }