UNPKG

starkon

Version:

Create a Next.js project with Starkon

219 lines (193 loc) 7.52 kB
import { z } from 'zod' // Password güvenlik seviyesi enum'u export enum PasswordStrength { WEAK = 'weak', MEDIUM = 'medium', STRONG = 'strong', } // Güçlü şifre regex pattern'ı const STRONG_PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/ // Email domain whitelist (opsiyonel) const ALLOWED_EMAIL_DOMAINS = ['gmail.com', 'outlook.com', 'example.com'] as const // Login şeması - development friendly export const loginSchema = z.object({ email: z .string({ required_error: 'E-posta adresi gereklidir', }) .min(1, { message: 'E-posta adresi gereklidir' }) .email({ message: 'Geçerli bir e-posta adresi girin' }) .max(254, { message: 'E-posta adresi çok uzun' }) .transform((email) => email.toLowerCase().trim()) .refine( (email) => { // Development ortamında domain kontrolü yapmıyoruz if (process.env.NODE_ENV === 'development') return true const domain = email.split('@')[1] return ALLOWED_EMAIL_DOMAINS.includes(domain as (typeof ALLOWED_EMAIL_DOMAINS)[number]) }, { message: 'Bu e-posta domaini desteklenmiyor' }, ), password: z .string({ required_error: 'Şifre gereklidir', }) .min(1, { message: 'Şifre gereklidir' }) .min(6, { message: 'Şifre en az 6 karakter olmalıdır' }) // Development için kısaltıldı .max(128, { message: 'Şifre çok uzun' }) .refine( (password) => { // Development ortamında basit şifrelere izin veriyoruz if (process.env.NODE_ENV === 'development') return true return STRONG_PASSWORD_REGEX.test(password) }, { message: 'Şifre en az bir büyük harf, bir küçük harf, bir rakam ve bir özel karakter içermelidir', }, ), rememberMe: z.boolean().optional().default(false), }) // Register şeması - geliştirilmiş export const registerSchema = z .object({ name: z .string({ required_error: 'Ad soyad gereklidir', }) .min(1, { message: 'Ad soyad gereklidir' }) .min(2, { message: 'Ad soyad en az 2 karakter olmalıdır' }) .max(100, { message: 'Ad soyad çok uzun' }) .regex(/^[a-zA-ZçğıöşüÇĞIİÖŞÜ\s]+$/, { message: 'Ad soyad sadece harf içerebilir' }) .transform((name) => name.trim().replace(/\s+/g, ' ')), email: z .string({ required_error: 'E-posta adresi gereklidir', }) .min(1, { message: 'E-posta adresi gereklidir' }) .email({ message: 'Geçerli bir e-posta adresi girin' }) .max(254, { message: 'E-posta adresi çok uzun' }) .transform((email) => email.toLowerCase().trim()), password: z .string({ required_error: 'Şifre gereklidir', }) .min(1, { message: 'Şifre gereklidir' }) .min(8, { message: 'Şifre en az 8 karakter olmalıdır' }) .max(128, { message: 'Şifre çok uzun' }) .regex(/[A-Z]/, { message: 'Şifre en az bir büyük harf içermelidir' }) .regex(/[a-z]/, { message: 'Şifre en az bir küçük harf içermelidir' }) .regex(/[0-9]/, { message: 'Şifre en az bir rakam içermelidir' }) .regex(/[@$!%*?&]/, { message: 'Şifre en az bir özel karakter içermelidir' }), confirmPassword: z .string({ required_error: 'Şifre tekrarı gereklidir', }) .min(1, { message: 'Şifre tekrarı gereklidir' }), terms: z.literal(true, { errorMap: () => ({ message: 'Kullanım şartlarını kabul etmelisiniz' }), }), newsletter: z.boolean().optional().default(false), }) .superRefine((data, ctx) => { // Şifre eşleşme kontrolü if (data.password !== data.confirmPassword) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Şifreler eşleşmiyor', path: ['confirmPassword'], }) } }) // Forgot password şeması export const forgotPasswordSchema = z.object({ email: z .string({ required_error: 'E-posta adresi gereklidir', }) .min(1, { message: 'E-posta adresi gereklidir' }) .email({ message: 'Geçerli bir e-posta adresi girin' }) .transform((email) => email.toLowerCase().trim()), }) // Reset password şeması - tamamlanmış versiyonu export const resetPasswordSchema = z .object({ token: z .string({ required_error: 'Reset token gereklidir', }) .min(1, { message: 'Reset token gereklidir' }), newPassword: z .string({ required_error: 'Şifre gereklidir', }) .min(1, { message: 'Şifre gereklidir' }) .min(8, { message: 'Şifre en az 8 karakter olmalıdır' }) .max(128, { message: 'Şifre çok uzun' }) .regex(/[A-Za-z]/, { message: 'Şifre en az bir harf içermelidir' }) .regex(/[0-9]/, { message: 'Şifre en az bir rakam içermelidir' }), confirmPassword: z .string({ required_error: 'Şifre tekrarı gereklidir', }) .min(1, { message: 'Şifre tekrarı gereklidir' }), }) .superRefine((data, ctx) => { // Şifre eşleşme kontrolü if (data.newPassword !== data.confirmPassword) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Şifreler eşleşmiyor', path: ['confirmPassword'], }) } }) // Reset password şeması - i18n destekli versiyon export const createResetPasswordSchema = (t: (key: string) => string) => z .object({ newPassword: z .string() .min(1, { message: t('auth.resetPassword.validation.newPasswordRequired') }) .min(8, { message: t('auth.resetPassword.validation.passwordTooShort') }) .regex(/[A-Za-z]/, { message: t('auth.resetPassword.validation.passwordRequirements') }) .regex(/[0-9]/, { message: t('auth.resetPassword.validation.passwordRequirements') }), confirmPassword: z.string().min(1, { message: t('auth.resetPassword.validation.confirmPasswordRequired') }), }) .superRefine((data, ctx) => { if (data.newPassword !== data.confirmPassword) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: t('auth.resetPassword.validation.passwordsDoNotMatch'), path: ['confirmPassword'], }) } }) // Type exports export type LoginFormValues = z.infer<typeof loginSchema> export type RegisterFormValues = z.infer<typeof registerSchema> export type ResetPasswordRequest = z.infer<typeof resetPasswordSchema> export type ForgotPasswordRequest = z.infer<typeof forgotPasswordSchema> export type ResetPasswordFormValues = z.infer<typeof resetPasswordSchema> export type ForgotPasswordFormValues = z.infer<typeof forgotPasswordSchema> // Password strength checker utility export const checkPasswordStrength = (password: string): PasswordStrength => { let score = 0 if (password.length >= 8) score += 1 if (/[a-z]/.test(password)) score += 1 if (/[A-Z]/.test(password)) score += 1 if (/[0-9]/.test(password)) score += 1 if (/[@$!%*?&]/.test(password)) score += 1 if (password.length >= 12) score += 1 if (score < 3) return PasswordStrength.WEAK if (score < 5) return PasswordStrength.MEDIUM return PasswordStrength.STRONG } // Login için özel validator helper export const validateLoginData = (data: unknown): LoginFormValues => { const result = loginSchema.safeParse(data) if (!result.success) { console.error('Login validation failed:', result.error.errors) throw new Error('Form validation failed') } return result.data }