Files
ratebubble/src/middleware/validation.middleware.ts

86 lines
1.9 KiB
TypeScript

import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import type { ApiResponse, GetInfoRequest } from '../types/index.js';
import { isSupportedContentUrl } from '../utils/contentUrl.js';
/**
* Validation schema for /api/getinfo endpoint
*/
const getInfoSchema = z.object({
url: z
.string()
.url('Invalid URL format')
.refine(
(url) => isSupportedContentUrl(url),
'URL must be Netflix /title/... or PrimeVideo /detail/...'
),
});
/**
* Validate request body for /api/getinfo
*/
export function validateGetInfo(
req: Request,
res: Response,
next: NextFunction
): void {
const result = getInfoSchema.safeParse(req.body);
if (!result.success) {
const errors = result.error.issues.map((issue) => ({
field: issue.path.join('.'),
message: issue.message,
}));
const response: ApiResponse<never> = {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request parameters',
details: { errors },
},
};
res.status(400).json(response);
return;
}
// Attach validated data to request
(req as Request & { validated: GetInfoRequest }).validated = result.data;
next();
}
/**
* Generic validation middleware factory
*/
export function validateBody<T extends z.ZodType>(
schema: T
): (req: Request, res: Response, next: NextFunction) => void {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
const errors = result.error.issues.map((issue) => ({
field: issue.path.join('.'),
message: issue.message,
}));
const response: ApiResponse<never> = {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request parameters',
details: { errors },
},
};
res.status(400).json(response);
return;
}
next();
};
}
export default validateGetInfo;