Files
prompt/apps/web/src/app/api/prompts/route.ts
dsyoon 27540269b7 Initial commit: add FastAPI MVP (모프) and existing web app
Includes FastAPI+Jinja2+HTMX+SQLite implementation with seed categories, plus deployment templates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 17:17:22 +09:00

81 lines
2.5 KiB
TypeScript

import { NextResponse } from "next/server";
import { z } from "zod";
import { prisma } from "@/lib/prisma";
const QuerySchema = z.object({
query: z.string().trim().min(1).max(200).optional(),
entity: z.string().trim().min(1).max(120).optional(), // entity slug
tag: z.string().trim().min(1).max(120).optional(), // tag slug
model: z.string().trim().min(1).max(120).optional(), // model slug
sort: z.enum(["new", "popular"]).optional().default("new"),
limit: z.coerce.number().int().min(1).max(50).optional().default(20),
offset: z.coerce.number().int().min(0).max(5000).optional().default(0),
});
export async function GET(req: Request) {
const url = new URL(req.url);
const parsed = QuerySchema.safeParse({
query: url.searchParams.get("query") ?? undefined,
entity: url.searchParams.get("entity") ?? undefined,
tag: url.searchParams.get("tag") ?? undefined,
model: url.searchParams.get("model") ?? undefined,
sort: url.searchParams.get("sort") ?? undefined,
limit: url.searchParams.get("limit") ?? undefined,
offset: url.searchParams.get("offset") ?? undefined,
});
if (!parsed.success) {
return NextResponse.json(
{ ok: false, error: "INVALID_QUERY", detail: parsed.error.flatten() },
{ status: 400 },
);
}
const { query, entity, tag, model, sort, limit, offset } = parsed.data;
const where = {
isPublished: true,
...(entity ? { entity: { slug: entity } } : {}),
...(model ? { model: { slug: model } } : {}),
...(tag
? { tags: { some: { tag: { slug: tag } } } }
: {}),
...(query
? {
OR: [
{ title: { contains: query, mode: "insensitive" as const } },
{
descriptionMd: { contains: query, mode: "insensitive" as const },
},
],
}
: {}),
};
const orderBy =
sort === "popular"
? ({ likes: { _count: "desc" } } as const)
: ({ createdAt: "desc" } as const);
const prompts = await prisma.prompt.findMany({
where,
orderBy,
take: limit,
skip: offset,
select: {
id: true,
title: true,
descriptionMd: true,
createdAt: true,
updatedAt: true,
entity: { select: { slug: true, name: true } },
model: { select: { slug: true, name: true, provider: true } },
tags: { select: { tag: { select: { slug: true, name: true } } } },
_count: { select: { likes: true, comments: true, bookmarks: true } },
},
});
return NextResponse.json({ ok: true, items: prompts });
}