import { z } from 'zod'; import { config } from '@config/index.js'; export const registerSchema = z.object({ username: z.string() .min(3, 'Username must be at least 3 characters') .max(16, 'Username must not exceed 16 characters') .regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, underscore and hyphen') .toLowerCase(), password: z.string() .min(8, 'Password must be at least 8 characters') .max(16, 'Password must not exceed 16 characters') .regex(/[A-Z]/, 'Password must contain at least one uppercase letter') .regex(/[a-z]/, 'Password must contain at least one lowercase letter') .regex(/[0-9]/, 'Password must contain at least one number') .regex(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/, 'Password must contain at least one special character'), captchaToken: z.string().min(1, 'Captcha verification required'), acceptTerms: z.boolean().refine(v => v === true, 'You must accept the terms of service'), }); export const loginSchema = z.object({ username: z.string().min(1, 'Username is required').toLowerCase(), password: z.string().min(1, 'Password is required'), captchaToken: z.string().min(1, 'Captcha verification required'), }); export const channelAddSchema = z.object({ inviteLink: z.string().url('Invalid URL format').regex(config.whatsappChannelRegex, 'Must be a valid WhatsApp channel link'), }); export const channelTagsSchema = z.object({ tags: z.array(z.string().min(1).max(30).toLowerCase()).max(10, 'Maximum 10 tags allowed'), }); export const profileUpdateSchema = z.object({ email: z.string().email('Invalid email format').optional().or(z.literal('')), currentPassword: z.string().optional(), newPassword: z.string() .min(8, 'Password must be at least 8 characters') .max(16, 'Password must not exceed 16 characters') .regex(/[A-Z]/, 'Password must contain at least one uppercase letter') .regex(/[a-z]/, 'Password must contain at least one lowercase letter') .regex(/[0-9]/, 'Password must contain at least one number') .regex(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/, 'Password must contain at least one special character') .optional(), }).refine(data => data.currentPassword || !data.newPassword, { message: 'Current password required to change password', path: ['currentPassword'], }); export const searchSchema = z.object({ q: z.string().max(100).optional(), tag: z.string().max(50).optional(), sort: z.enum(['trending', 'followers', 'votes', 'newest', 'oldest', 'alpha', 'updated']).default('newest'), page: z.coerce.number().min(1).default(1), limit: z.coerce.number().min(1).max(100).default(20), }); export const voteSchema = z.object({ channelId: z.string().min(1, 'Channel ID required'), }); export const adminUserSearchSchema = z.object({ q: z.string().max(100).optional(), isAdmin: z.coerce.boolean().optional(), isBanned: z.coerce.boolean().optional(), page: z.coerce.number().min(1).default(1), limit: z.coerce.number().min(1).max(100).default(20), }); export const adminChannelSearchSchema = z.object({ q: z.string().max(100).optional(), status: z.enum(['pending', 'scraping', 'validating', 'published', 'rejected', 'banned']).optional(), isBanned: z.coerce.boolean().optional(), page: z.coerce.number().min(1).default(1), limit: z.coerce.number().min(1).max(100).default(20), }); export const banSchema = z.object({ type: z.enum(['user', 'channel', 'ip']), targetId: z.string().min(1), targetValue: z.string().min(1), reason: z.string().min(1, 'Reason required').max(500), expiresAt: z.string().datetime().optional(), }); export function validateInput(schema: z.ZodSchema, data: unknown): { success: true; data: T } | { success: false; errors: z.ZodIssue[] } { const result = schema.safeParse(data); if (result.success) { return { success: true, data: result.data }; } return { success: false, errors: result.error.issues }; }