pl / src /api /prompts.ts
ghuser1's picture
Upload 26 files
cfea436 verified
Raw
History Blame Contribute Delete
4.44 kB
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<string, typeof imgs> = {};
for (const im of imgs) {
(grouped[im.promptId] ||= []).push(im);
}
return c.json(
rows.map((r) => ({
...r,
tags: safeJson<string[]>(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<string[]>(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<T>(s: string, fb: T): T {
try { return JSON.parse(s) as T; } catch { return fb; }
}