import { isRole, type LoginUserRequest, type RegisterUserRequest, type UpdateUserRequest, } from "../../shared/user.ts"; import { VALIDATION } from "../../shared/validation.ts"; export function isLoginUserRequest(obj: unknown): obj is LoginUserRequest { return !!obj && typeof obj === "object" && "username" in obj && typeof obj.username === "string" && "password" in obj && typeof obj.password === "string"; } /** `obj[key]` when `obj` is an object that has it, undefined otherwise. */ export function fieldOf(obj: unknown, key: string): unknown { return obj !== null && typeof obj === "object" && key in obj ? (obj as Record)[key] : undefined; } export function isRegisterUserRequest( obj: unknown, ): obj is RegisterUserRequest { return validateRegisterUserRequest(obj) === null; } /** Returns a human-readable error string, or null if the request is valid. */ export function validateRegisterUserRequest(obj: unknown): string | null { if ( !obj || typeof obj !== "object" || !("username" in obj) || typeof obj.username !== "string" || !("password" in obj) || typeof obj.password !== "string" || !("inviteToken" in obj) || typeof obj.inviteToken !== "string" || !("email" in obj) || typeof obj.email !== "string" ) return "Invalid request"; const { username, password, email } = obj as RegisterUserRequest; if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { return "Invalid email address"; } if ( !new RegExp( `^[a-zA-Z0-9_]{${VALIDATION.USERNAME_MIN},${VALIDATION.USERNAME_MAX}}$`, ) .test(username) ) { return `Username must be ${VALIDATION.USERNAME_MIN}–${VALIDATION.USERNAME_MAX} characters and contain only letters, numbers, or underscores`; } if (password.length < VALIDATION.PASSWORD_MIN) { return `Password must be at least ${VALIDATION.PASSWORD_MIN} characters`; } if (password.length > VALIDATION.PASSWORD_MAX) { return `Password must be at most ${VALIDATION.PASSWORD_MAX} characters`; } return null; } export function isUpdateUserRequest(obj: unknown): obj is UpdateUserRequest { if (!obj || typeof obj !== "object") return false; const o = obj as Record; if ("username" in o) { if (typeof o.username !== "string") return false; if (!/^[a-zA-Z0-9_]{1,32}$/.test(o.username as string)) return false; } if ("password" in o) { if (typeof o.password !== "string") return false; const len = (o.password as string).length; if (len < VALIDATION.PASSWORD_MIN || len > VALIDATION.PASSWORD_MAX) { return false; } } if ("role" in o && !isRole(o.role)) return false; if ( "description" in o && typeof o.description !== "string" && o.description !== null ) return false; if ("email" in o) { if (typeof o.email !== "string") return false; if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o.email as string)) return false; } if ( typeof o.description === "string" && (o.description as string).length > VALIDATION.USER_DESCRIPTION_MAX ) return false; return true; }