mirror of
https://github.com/SrIzan10/fireentity-movienights.git
synced 2026-06-06 00:56:52 +00:00
zod shit
This commit is contained in:
1
bun.lock
1
bun.lock
@@ -24,6 +24,7 @@
|
|||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tw-animate-css": "^1.3.7",
|
"tw-animate-css": "^1.3.7",
|
||||||
|
"zod": "^4.0.17",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"tw-animate-css": "^1.3.7"
|
"tw-animate-css": "^1.3.7",
|
||||||
|
"zod": "^4.0.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { auth } from "../../../../auth/auth";
|
import { auth } from "../../../../auth/auth";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
import { idSchema, validateParams } from "@/lib/validation";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
@@ -17,9 +18,15 @@ export async function POST(
|
|||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate params
|
||||||
|
const { data, error } = validateParams(await params, idSchema);
|
||||||
|
if (error) {
|
||||||
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
const movie = await prisma.movie.update({
|
const movie = await prisma.movie.update({
|
||||||
where: {
|
where: {
|
||||||
id: (await params).id,
|
id: data.id,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
approved: true,
|
approved: true,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { auth } from "../../auth/auth";
|
import { auth } from "../../auth/auth";
|
||||||
|
import { scheduleMovieSchema, scheduleIdSchema, validateRequestData, validateSearchParams } from "@/lib/validation";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
@@ -14,7 +15,13 @@ export async function POST(req: Request) {
|
|||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { movieId, date } = await req.json();
|
// Validate request data
|
||||||
|
const { data, error } = await validateRequestData(req, scheduleMovieSchema);
|
||||||
|
if (error) {
|
||||||
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { movieId, date } = data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const schedule = await prisma.movieSchedule.create({
|
const schedule = await prisma.movieSchedule.create({
|
||||||
@@ -60,12 +67,13 @@ export async function DELETE(req: Request) {
|
|||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams } = new URL(req.url);
|
// Validate search params
|
||||||
const scheduleId = searchParams.get('id');
|
const { data, error } = validateSearchParams(req.url, scheduleIdSchema);
|
||||||
|
if (error) {
|
||||||
if (!scheduleId) {
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
return NextResponse.json({ error: "Schedule ID required" }, { status: 400 });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { id: scheduleId } = data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await prisma.movieSchedule.delete({
|
await prisma.movieSchedule.delete({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { auth } from "../../../auth/auth";
|
import { auth } from "../../../auth/auth";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
|
import { idSchema, validateParams } from "@/lib/validation";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
@@ -16,7 +17,13 @@ export async function POST(
|
|||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const movieId = (await params).id;
|
// Validate params
|
||||||
|
const { data, error } = validateParams(await params, idSchema);
|
||||||
|
if (error) {
|
||||||
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const movieId = data.id;
|
||||||
const userId = session.user.id;
|
const userId = session.user.id;
|
||||||
|
|
||||||
// Check if the user has already voted for this movie
|
// Check if the user has already voted for this movie
|
||||||
|
|||||||
@@ -3,16 +3,23 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { auth } from "../auth/auth";
|
import { auth } from "../auth/auth";
|
||||||
|
import { movieSuggestionSchema, validateRequestData } from "@/lib/validation";
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
const { title, description, posterUrl, suggestedBy } = await req.json();
|
|
||||||
const session = await auth.api.getSession({ headers: await headers() });
|
const session = await auth.api.getSession({ headers: await headers() });
|
||||||
if (!session || !session.user?.id) {
|
if (!session || !session.user?.id) {
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate request data
|
||||||
|
const { data, error } = await validateRequestData(req, movieSuggestionSchema);
|
||||||
|
if (error) {
|
||||||
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
|
}
|
||||||
|
const { title, description, posterUrl, suggestedBy } = data as any;
|
||||||
|
|
||||||
// Check if there's already a pending request for this movie (not approved)
|
// Check if there's already a pending request for this movie (not approved)
|
||||||
const existingPendingMovies = await prisma.movie.findMany({
|
const existingPendingMovies = await prisma.movie.findMany({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -2,24 +2,27 @@
|
|||||||
import auth from "@/lib/auth-config";
|
import auth from "@/lib/auth-config";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { searchMovieSchema, validateSearchParams } from "@/lib/validation";
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: Request) {
|
||||||
const { searchParams } = new URL(req.url);
|
|
||||||
const query = searchParams.get("query");
|
|
||||||
const session = await auth.api.getSession({ headers: await headers() });
|
const session = await auth.api.getSession({ headers: await headers() });
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!query) {
|
// Validate search params
|
||||||
return NextResponse.json({ error: "Query is required" }, { status: 400 });
|
const { data, error } = validateSearchParams(req.url, searchMovieSchema);
|
||||||
|
if (error) {
|
||||||
|
return NextResponse.json({ error }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { query } = data;
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`https://api.themoviedb.org/3/search/movie?api_key=${process.env.TMDB_API_KEY}&query=${query}`
|
`https://api.themoviedb.org/3/search/movie?api_key=${process.env.TMDB_API_KEY}&query=${query}`
|
||||||
);
|
);
|
||||||
const data = await res.json();
|
const apiData = await res.json();
|
||||||
|
|
||||||
return NextResponse.json(data.results);
|
return NextResponse.json(apiData.results);
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/lib/validation.ts
Normal file
80
src/lib/validation.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// Movie suggestion schema
|
||||||
|
export const movieSuggestionSchema = z.object({
|
||||||
|
title: z.string().min(1, "Title is required").max(255, "Title must be less than 255 characters"),
|
||||||
|
description: z.string().min(1, "Description is required").max(1000, "Description must be less than 1000 characters"),
|
||||||
|
posterUrl: z.string().url("Invalid URL format"),
|
||||||
|
suggestedBy: z.string().min(1, "Suggested by is required").max(100, "Suggested by must be less than 100 characters"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Movie vote schema
|
||||||
|
export const movieVoteSchema = z.object({
|
||||||
|
movieId: z.string().min(1, "Movie ID is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schedule movie schema
|
||||||
|
export const scheduleMovieSchema = z.object({
|
||||||
|
movieId: z.string().min(1, "Movie ID is required"),
|
||||||
|
date: z.string().datetime("Invalid date format"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search movie schema
|
||||||
|
export const searchMovieSchema = z.object({
|
||||||
|
query: z.string().min(1, "Query is required").max(100, "Query must be less than 100 characters"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schedule ID schema
|
||||||
|
export const scheduleIdSchema = z.object({
|
||||||
|
id: z.string().min(1, "Schedule ID is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generic ID schema
|
||||||
|
export const idSchema = z.object({
|
||||||
|
id: z.string().min(1, "ID is required"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Utility function to validate request data
|
||||||
|
export async function validateRequestData(request: Request, schema: z.ZodSchema) {
|
||||||
|
try {
|
||||||
|
const data = await request.json();
|
||||||
|
return { data: schema.parse(data) as any, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
const errorMessage = error.errors.map(err => err.message).join(", ");
|
||||||
|
return { data: null, error: errorMessage };
|
||||||
|
}
|
||||||
|
return { data: null, error: "Invalid JSON" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to validate URL parameters
|
||||||
|
export function validateParams(params: Record<string, string>, schema: z.ZodSchema) {
|
||||||
|
try {
|
||||||
|
return { data: schema.parse(params) as any, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
const errorMessage = error.errors.map(err => err.message).join(", ");
|
||||||
|
return { data: null, error: errorMessage };
|
||||||
|
}
|
||||||
|
return { data: null, error: "Invalid parameters" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function to validate search parameters
|
||||||
|
export function validateSearchParams(url: string, schema: z.ZodSchema) {
|
||||||
|
try {
|
||||||
|
const { searchParams } = new URL(url);
|
||||||
|
const params: Record<string, string> = {};
|
||||||
|
for (const [key, value] of searchParams.entries()) {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
return { data: schema.parse(params) as any, error: null };
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
const errorMessage = error.errors.map(err => err.message).join(", ");
|
||||||
|
return { data: null, error: errorMessage };
|
||||||
|
}
|
||||||
|
return { data: null, error: "Invalid search parameters" };
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user