import { NextResponse } from "next/server"; import { z } from "zod"; import { categories } from "@/lib/tool-types"; import { submitTool } from "@/lib/tools"; const githubRepoUrl = z .string() .url() .refine((value) => { try { const url = new URL(value); return ( url.protocol === "https:" && url.hostname === "github.com" && !url.username && !url.password && !url.search && !url.hash && /^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?\/?$/.test(url.pathname) ); } catch { return false; } }, "Enter a public GitHub repository URL."); const optionalWebsiteUrl = z .union([z.literal(""), z.string().url()]) .refine((value) => { if (!value) return true; const url = new URL(value); return url.protocol === "https:" && !url.username && !url.password; }, "Website must be a public HTTPS URL."); const submissionSchema = z.object({ name: z.string().trim().min(2).max(80), summary: z.string().trim().min(20).max(300), category: z.enum(categories), repoUrl: githubRepoUrl, websiteUrl: optionalWebsiteUrl, submittedBy: z.string().trim().min(2).max(60), }); export async function POST(request: Request) { let body: unknown; try { body = await request.json(); } catch { return NextResponse.json({ error: "Request body must be valid JSON." }, { status: 400 }); } const parsed = submissionSchema.safeParse(body); if (!parsed.success) { return NextResponse.json( { error: "Please review the submission fields and try again.", fields: parsed.error.flatten().fieldErrors }, { status: 400 }, ); } try { const tool = await submitTool(parsed.data); return NextResponse.json({ tool }, { status: 201 }); } catch (error) { console.error("Tool submission failed", error); return NextResponse.json( { error: "The tool could not be saved. Please try again later." }, { status: 500 }, ); } }