Context - Housekeeping commit to capture all current ZXDB Explorer work before index-page performance optimizations. Includes - Server-rendered entry detail page with ISR and parallelized DB queries. - Node runtime for ZXDB API routes and params validation updates for Next 15. - ZXDB repository extensions (facets, label queries, category queries). - Cross-linking and Link-based prefetch across ZXDB UI. - Cache headers on low-churn list APIs. Notes - Follow-up commit will focus specifically on speeding up index pages via SSR initial data and ISR. Signed-off-by: Junie@lucy.xalior.com
49 lines
1.7 KiB
TypeScript
49 lines
1.7 KiB
TypeScript
import { NextRequest } from "next/server";
|
|
import { z } from "zod";
|
|
import { searchEntries, getEntryFacets } from "@/server/repo/zxdb";
|
|
|
|
const querySchema = z.object({
|
|
q: z.string().optional(),
|
|
page: z.coerce.number().int().positive().optional(),
|
|
pageSize: z.coerce.number().int().positive().max(100).optional(),
|
|
genreId: z.coerce.number().int().positive().optional(),
|
|
languageId: z
|
|
.string()
|
|
.trim()
|
|
.length(2, "languageId must be a 2-char code")
|
|
.optional(),
|
|
machinetypeId: z.coerce.number().int().positive().optional(),
|
|
sort: z.enum(["title", "id_desc"]).optional(),
|
|
facets: z.coerce.boolean().optional(),
|
|
});
|
|
|
|
export async function GET(req: NextRequest) {
|
|
const { searchParams } = new URL(req.url);
|
|
const parsed = querySchema.safeParse({
|
|
q: searchParams.get("q") ?? undefined,
|
|
page: searchParams.get("page") ?? undefined,
|
|
pageSize: searchParams.get("pageSize") ?? undefined,
|
|
genreId: searchParams.get("genreId") ?? undefined,
|
|
languageId: searchParams.get("languageId") ?? undefined,
|
|
machinetypeId: searchParams.get("machinetypeId") ?? undefined,
|
|
sort: searchParams.get("sort") ?? undefined,
|
|
facets: searchParams.get("facets") ?? undefined,
|
|
});
|
|
if (!parsed.success) {
|
|
return new Response(
|
|
JSON.stringify({ error: parsed.error.flatten() }),
|
|
{ status: 400, headers: { "content-type": "application/json" } }
|
|
);
|
|
}
|
|
const data = await searchEntries(parsed.data);
|
|
const body = parsed.data.facets
|
|
? { ...data, facets: await getEntryFacets(parsed.data) }
|
|
: data;
|
|
return new Response(JSON.stringify(body), {
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}
|
|
|
|
// Ensure Node.js runtime (required for mysql2)
|
|
export const runtime = "nodejs";
|