Chat-With-AI / src /core /security /validation.ts
NathMen12's picture
Upload 56 files
ef73937 verified
Raw
History Blame Contribute Delete
3.95 kB
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<T>(schema: z.ZodSchema<T>, 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 };
}