import { z } from "zod"; import { glowColorSchema } from "./profile-appearance"; export const displayNameSchema = z .string() .trim() .min(2, "Use at least 2 characters") .max(32, "Use at most 32 characters") .refine( (value) => !/[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069]/u.test(value), "Use a single line without hidden control characters", ); export const usernameSchema = z.string().trim().toLowerCase().regex(/^[a-z0-9_]{3,32}$/, "Use 3–32 letters, numbers or underscores."); export const newPasswordSchema = z .string() .min(12, "Use at least 12 characters") .refine( (value) => new TextEncoder().encode(value).length <= 72, "Use at most 72 UTF-8 bytes for your password", ); const email = z.string().trim().email().max(254); export const authActionSchema = z.discriminatedUnion("action", [ z .object({ action: z.literal("login"), email, password: z.string().min(1).max(1024), next: z.string().optional(), }) .strict(), z .object({ action: z.literal("signup"), email, password: newPasswordSchema, displayName: displayNameSchema, next: z.string().optional(), }) .strict(), z.object({ action: z.literal("recover"), email }).strict(), ]); export const profileUpdateSchema = z .object({ displayName: displayNameSchema.optional(), username: usernameSchema.optional(), glowColor: glowColorSchema.optional(), avatarPath: z.string().max(128).nullable().optional(), }) .strict() .refine( (value) => value.displayName !== undefined || value.username !== undefined || value.glowColor !== undefined || value.avatarPath !== undefined, "Choose a profile change", ); export const initialPasswordSchema = z .object({ password: newPasswordSchema }) .strict(); export const passwordUpdateSchema = z .object({ password: newPasswordSchema, currentPassword: z.string().min(1).max(1024).optional(), nonce: z .string() .trim() .regex(/^[0-9]{6,10}$/) .optional(), }) .strict(); /** Only supported product destinations can follow an authentication link. */ export function authDestination(value: string | null | undefined): string { if ( value === "/settings" || value === "/dashboard" || value === "/servers" || value === "/support" || value === "/auth/reset-password" ) return value; if (value && /^\/dashboard\/[0-9a-f-]{36}$/i.test(value)) return value; if (value && /^\/support\/[0-9a-f-]{36}$/i.test(value)) return value; if (value && /^\/tournaments\?map=[0-9a-f-]{36}$/i.test(value)) return value; if (value && /^\/pair\?code=[A-F0-9]{8}$/.test(value)) return value; if (value && /^\/moderator-application\/[a-f0-9]{64}$/.test(value)) return value; return "/dashboard"; }