chore: ZXDB env validation, MySQL setup, API & UI

This sanity commit wires up the initial ZXDB integration and a minimal UI to explore it.

Key changes:
- Add Zod-based env parsing (`src/env.ts`) validating `ZXDB_URL` as a mysql:// URL (t3.gg style).
- Configure Drizzle ORM with mysql2 connection pool (`src/server/db.ts`) driven by `ZXDB_URL`.
- Define minimal ZXDB schema models (`src/server/schema/zxdb.ts`): `entries` and helper `search_by_titles`.
- Implement repository search with pagination using helper table (`src/server/repo/zxdb.ts`).
- Expose Next.js API route `GET /api/zxdb/search` with Zod query validation and Node runtime (`src/app/api/zxdb/search/route.ts`).
- Create new app section “ZXDB Explorer” at `/zxdb` with search UI, results table, and pagination (`src/app/zxdb/*`).
- Add navbar link to ZXDB (`src/components/Navbar.tsx`).
- Update example.env with readonly-role notes and example `ZXDB_URL`.
- Add drizzle-kit config scaffold (`drizzle.config.ts`).
- Update package.json deps: drizzle-orm, mysql2, zod; devDeps: drizzle-kit. Lockfile updated.
- Extend .gitignore to exclude large ZXDB structure dump.

Notes:
- Ensure ZXDB data and helper tables are loaded (see `ZXDB/scripts/ZXDB_help_search.sql`).
- This commit provides structure-only browsing; future work can enrich schema (authors, labels, publishers) and UI filters.

Signed-off-by: Junie@lucy.xalior.com
This commit is contained in:
2025-12-12 14:06:58 +00:00
parent 4222eba8ba
commit dbbad09b1b
14 changed files with 1119 additions and 3 deletions

View File

@@ -0,0 +1,31 @@
import { NextRequest } from "next/server";
import { z } from "zod";
import { searchEntries } 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(),
});
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,
});
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);
return new Response(JSON.stringify(data), {
headers: { "content-type": "application/json" },
});
}
// Ensure Node.js runtime (required for mysql2)
export const runtime = "nodejs";