import { z } from 'zod'; // Reserved usernames for security and system purposes const FORBIDDEN_USERNAMES = new Set([ // System/Admin accounts 'admin', 'administrator', 'moderator', 'mod', 'owner', 'root', 'system', 'support', 'staff', 'team', 'official', 'verified', 'bot', 'api', 'service', // Company/Brand protection 'thrillwiki', 'lovable', 'supabase', 'cloudflare', // Common system routes/pages 'www', 'mail', 'email', 'ftp', 'blog', 'forum', 'shop', 'store', 'app', 'mobile', 'help', 'support', 'contact', 'about', 'terms', 'privacy', 'security', 'legal', 'login', 'signup', 'register', 'signin', 'signout', 'logout', 'auth', 'oauth', 'profile', 'profiles', 'user', 'users', 'account', 'accounts', 'settings', 'dashboard', 'console', 'panel', 'manage', 'management', // Technical terms 'null', 'undefined', 'true', 'false', 'delete', 'remove', 'test', 'demo', 'localhost', 'example', 'temp', 'temporary', 'guest', 'anonymous', 'anon', // Offensive prevention (basic) 'fuck', 'shit', 'damn', 'hell', 'ass', 'bitch', 'bastard', 'crap', 'nazi', 'hitler', 'stalin', 'terrorist', 'kill', 'death', 'murder', // Impersonation prevention 'ceo', 'president', 'manager', 'director', 'executive', 'founder' ]); export const usernameSchema = z .string() .min(3, 'Username must be at least 3 characters') .max(30, 'Username must be less than 30 characters') .regex( /^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/, 'Username must start and end with letters/numbers, and can only contain letters, numbers, hyphens, and underscores' ) .refine( (val) => !/[-_]{2,}/.test(val), 'Username cannot contain consecutive hyphens or underscores' ) .transform(val => val.toLowerCase()) .refine(val => !FORBIDDEN_USERNAMES.has(val), 'This username is not allowed'); // Display name validation with content filtering export const displayNameSchema = z .string() .max(100, 'Display name must be less than 100 characters') .refine(val => { if (!val) return true; const lowerVal = val.toLowerCase(); // Check for basic offensive content in display names const offensiveTerms = ['nazi', 'hitler', 'terrorist', 'kill', 'murder', 'fuck', 'shit']; return !offensiveTerms.some(term => lowerVal.includes(term)); }, 'Display name contains inappropriate content') .optional(); // Password validation schema with complexity requirements export const passwordSchema = z.object({ currentPassword: z.string().min(1, 'Current password is required'), newPassword: z.string() .min(8, 'Password must be at least 8 characters') .max(128, 'Password must be less than 128 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(/[^A-Za-z0-9]/, 'Password must contain at least one special character'), confirmPassword: z.string() }).refine(data => data.newPassword === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"] }); // Bio field validation with sanitization export const bioSchema = z.string() .max(500, 'Bio must be less than 500 characters') .transform(val => val?.trim()) .refine( val => !val || !/[<>]/.test(val), 'Bio cannot contain HTML tags' ) .optional(); // Personal location field validation with sanitization export const personalLocationSchema = z.string() .max(100, 'Location must be less than 100 characters') .transform(val => val?.trim()) .refine( val => !val || !/[<>{}]/.test(val), 'Location cannot contain special characters' ) .optional(); // Preferred pronouns validation export const preferredPronounsSchema = z .string() .trim() .max(20, { message: "Pronouns must be less than 20 characters" }) .optional(); export const profileEditSchema = z.object({ username: usernameSchema, display_name: displayNameSchema, bio: bioSchema, }); export type ProfileEditForm = z.infer;