Files
freno-dev/src/server/api/schemas/user.ts
2026-01-01 02:22:33 -05:00

194 lines
5.6 KiB
TypeScript

import { z } from "zod";
import { validatePassword } from "~/lib/validation";
import { VALIDATION_CONFIG } from "~/config";
/**
* User API Validation Schemas
*
* Zod schemas for user-related operations like authentication,
* profile updates, and password management
*/
// ============================================================================
// Custom Password Validator
// ============================================================================
/**
* Secure password validation with strength requirements
* Minimum length from config, uppercase, lowercase, number, and special character
*/
const securePasswordSchema = z
.string()
.min(
VALIDATION_CONFIG.MIN_PASSWORD_LENGTH,
`Password must be at least ${VALIDATION_CONFIG.MIN_PASSWORD_LENGTH} characters`
)
.refine(
(password) => {
const result = validatePassword(password);
return result.isValid;
},
(password) => {
const result = validatePassword(password);
return {
message:
result.errors.join(", ") || "Password does not meet requirements"
};
}
);
// ============================================================================
// Authentication Schemas
// ============================================================================
/**
* User registration schema
*/
export const registerUserSchema = z
.object({
email: z.string().email(),
password: securePasswordSchema,
passwordConfirmation: z.string().min(VALIDATION_CONFIG.MIN_PASSWORD_LENGTH)
})
.refine((data) => data.password === data.passwordConfirmation, {
message: "Passwords do not match",
path: ["passwordConfirmation"]
});
/**
* User login schema
*/
export const loginUserSchema = z.object({
email: z.string().email(),
password: z.string().min(1, "Password is required")
});
/**
* OAuth provider schema
*/
export const oauthProviderSchema = z.enum(["google", "github"]);
// ============================================================================
// Profile Management Schemas
// ============================================================================
/**
* Update email schema
*/
export const updateEmailSchema = z.object({
email: z.string().email()
});
/**
* Update display name schema
*/
export const updateDisplayNameSchema = z.object({
displayName: z.string().min(1).max(50)
});
/**
* Update profile image schema
*/
export const updateProfileImageSchema = z.object({
imageUrl: z.string().url()
});
// ============================================================================
// Password Management Schemas
// ============================================================================
/**
* Change password schema (requires old password)
*/
export const changePasswordSchema = z
.object({
oldPassword: z.string().min(1, "Current password is required"),
newPassword: securePasswordSchema,
newPasswordConfirmation: z
.string()
.min(VALIDATION_CONFIG.MIN_PASSWORD_LENGTH)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",
path: ["newPasswordConfirmation"]
})
.refine((data) => data.oldPassword !== data.newPassword, {
message: "New password must be different from current password",
path: ["newPassword"]
});
/**
* Set password schema (for OAuth users adding password)
*/
export const setPasswordSchema = z
.object({
newPassword: securePasswordSchema,
newPasswordConfirmation: z
.string()
.min(VALIDATION_CONFIG.MIN_PASSWORD_LENGTH)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",
path: ["newPasswordConfirmation"]
});
/**
* Request password reset schema
*/
export const requestPasswordResetSchema = z.object({
email: z.string().email()
});
/**
* Reset password schema (with token)
*/
export const resetPasswordSchema = z
.object({
token: z.string().min(1),
newPassword: securePasswordSchema,
newPasswordConfirmation: z
.string()
.min(VALIDATION_CONFIG.MIN_PASSWORD_LENGTH)
})
.refine((data) => data.newPassword === data.newPasswordConfirmation, {
message: "Passwords do not match",
path: ["newPasswordConfirmation"]
});
// ============================================================================
// Account Management Schemas
// ============================================================================
/**
* Delete account schema
*/
export const deleteAccountSchema = z.object({
password: z.string().min(1, "Password is required to delete account")
});
/**
* Email verification schema
*/
export const verifyEmailSchema = z.object({
token: z.string().min(1)
});
// ============================================================================
// Type Exports
// ============================================================================
export type RegisterUserInput = z.infer<typeof registerUserSchema>;
export type LoginUserInput = z.infer<typeof loginUserSchema>;
export type OAuthProvider = z.infer<typeof oauthProviderSchema>;
export type UpdateEmailInput = z.infer<typeof updateEmailSchema>;
export type UpdateDisplayNameInput = z.infer<typeof updateDisplayNameSchema>;
export type UpdateProfileImageInput = z.infer<typeof updateProfileImageSchema>;
export type ChangePasswordInput = z.infer<typeof changePasswordSchema>;
export type SetPasswordInput = z.infer<typeof setPasswordSchema>;
export type RequestPasswordResetInput = z.infer<
typeof requestPasswordResetSchema
>;
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;
export type DeleteAccountInput = z.infer<typeof deleteAccountSchema>;
export type VerifyEmailInput = z.infer<typeof verifyEmailSchema>;