|
|
@@ -0,0 +1,44 @@
|
|
|
+import { NextRequest, NextResponse } from "next/server";
|
|
|
+import { z } from "zod";
|
|
|
+import { tmdbFetch, filterAdultMovies } from "@/lib/tmdb/client";
|
|
|
+import type { TMDBSearchResponse } from "@/types/tmdb";
|
|
|
+
|
|
|
+const discoverParamsSchema = z.object({
|
|
|
+ with_genres: z
|
|
|
+ .string()
|
|
|
+ .regex(/^\d+(,\d+)*$/, "Genre IDs must be comma-separated numbers"),
|
|
|
+ page: z.coerce.number().int().min(1).max(500).optional().default(1),
|
|
|
+ sort_by: z
|
|
|
+ .enum(["popularity.desc", "popularity.asc", "vote_average.desc", "vote_average.asc"])
|
|
|
+ .optional()
|
|
|
+ .default("popularity.desc"),
|
|
|
+});
|
|
|
+
|
|
|
+export async function GET(request: NextRequest) {
|
|
|
+ const rawParams = Object.fromEntries(request.nextUrl.searchParams);
|
|
|
+ const parsed = discoverParamsSchema.safeParse(rawParams);
|
|
|
+
|
|
|
+ if (!parsed.success) {
|
|
|
+ return NextResponse.json(
|
|
|
+ { error: "Invalid parameters", details: parsed.error.flatten().fieldErrors },
|
|
|
+ { status: 400 },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ const { with_genres, page, sort_by } = parsed.data;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const data = await tmdbFetch<TMDBSearchResponse>("/discover/movie", {
|
|
|
+ with_genres,
|
|
|
+ page: String(page),
|
|
|
+ sort_by,
|
|
|
+ });
|
|
|
+
|
|
|
+ return NextResponse.json(filterAdultMovies(data), {
|
|
|
+ headers: { "Cache-Control": "public, s-maxage=300, stale-while-revalidate=60" },
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error("TMDB discover error:", error);
|
|
|
+ return NextResponse.json({ error: "Failed to discover movies" }, { status: 500 });
|
|
|
+ }
|
|
|
+}
|