import { z } from "zod"; import { and, asc, eq } from "drizzle-orm"; import { withUser, parseBody, json, ApiError } from "@/lib/api"; import { getDb, modelPresets, promptPresets } from "@/db"; import { ids } from "@/lib/ids"; import { generationSettingsSchema } from "@/lib/chat/schemas"; export const dynamic = "force-dynamic"; /** * /api/presets?kind=model|prompt — CRUD for model presets and prompt presets. */ const kindSchema = z.enum(["model", "prompt"]); const modelPresetSchema = z.object({ name: z.string().min(1).max(80), description: z.string().max(400).nullable().optional(), icon: z.string().max(16).nullable().optional(), modelKey: z.string().min(3).max(120), systemPrompt: z.string().max(50_000).nullable().optional(), parameters: generationSettingsSchema.optional(), tools: z.record(z.string(), z.unknown()).optional(), fileSettings: z.record(z.string(), z.unknown()).optional(), }); const promptPresetSchema = z.object({ name: z.string().min(1).max(80), description: z.string().max(400).nullable().optional(), icon: z.string().max(16).nullable().optional(), systemPrompt: z.string().min(1).max(50_000), defaultModelKey: z.string().max(120).nullable().optional(), parameters: generationSettingsSchema.optional(), tools: z.record(z.string(), z.unknown()).optional(), }); export const GET = withUser(async ({ user }) => { const db = getDb(); const [models, prompts] = await Promise.all([ db.select().from(modelPresets).where(eq(modelPresets.userId, user.id)).orderBy(asc(modelPresets.sortOrder), asc(modelPresets.createdAt)), db.select().from(promptPresets).where(eq(promptPresets.userId, user.id)).orderBy(asc(promptPresets.sortOrder), asc(promptPresets.createdAt)), ]); return json({ modelPresets: models, promptPresets: prompts }); }); export const POST = withUser(async ({ req, user }) => { const kind = kindSchema.parse(new URL(req.url).searchParams.get("kind")); const db = getDb(); if (kind === "model") { const body = await parseBody(req, modelPresetSchema); const [row] = await db.insert(modelPresets).values({ id: ids.preset(), userId: user.id, ...body, parameters: body.parameters ?? {}, tools: body.tools ?? {}, fileSettings: body.fileSettings ?? {} }).returning(); return json({ preset: row }, { status: 201 }); } const body = await parseBody(req, promptPresetSchema); const [row] = await db.insert(promptPresets).values({ id: ids.prompt(), userId: user.id, ...body, parameters: body.parameters ?? {}, tools: body.tools ?? {} }).returning(); return json({ preset: row }, { status: 201 }); }); export const PATCH = withUser(async ({ req, user }) => { const url = new URL(req.url); const kind = kindSchema.parse(url.searchParams.get("kind")); const id = url.searchParams.get("id"); if (!id) throw new ApiError(400, "Missing id"); const db = getDb(); if (kind === "model") { const body = await parseBody(req, modelPresetSchema.partial()); const [row] = await db .update(modelPresets) .set({ ...body, updatedAt: new Date() }) .where(and(eq(modelPresets.id, id), eq(modelPresets.userId, user.id))) .returning(); if (!row) throw new ApiError(404, "Preset not found", "NOT_FOUND"); return json({ preset: row }); } const body = await parseBody(req, promptPresetSchema.partial()); const [row] = await db .update(promptPresets) .set({ ...body, updatedAt: new Date() }) .where(and(eq(promptPresets.id, id), eq(promptPresets.userId, user.id))) .returning(); if (!row) throw new ApiError(404, "Preset not found", "NOT_FOUND"); return json({ preset: row }); }); export const DELETE = withUser(async ({ req, user }) => { const url = new URL(req.url); const kind = kindSchema.parse(url.searchParams.get("kind")); const id = url.searchParams.get("id"); if (!id) throw new ApiError(400, "Missing id"); const db = getDb(); if (kind === "model") await db.delete(modelPresets).where(and(eq(modelPresets.id, id), eq(modelPresets.userId, user.id))); else await db.delete(promptPresets).where(and(eq(promptPresets.id, id), eq(promptPresets.userId, user.id))); return json({ ok: true }); });