create-arktos
Version:
🚀 A modern Node.js backend boilerplate with TypeScript, Express, JWT authentication, Prisma ORM, PostgreSQL, and Resend email service. Includes complete authentication flow, security middleware, and database management.
139 lines (119 loc) • 5.47 kB
text/typescript
import { z } from 'zod';
// Environment validation schema
export const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.string().default('3001'),
// Database
DATABASE_URL: z.string().min(1, 'Database URL is required'),
DIRECT_URL: z.string().optional(),
// JWT
JWT_SECRET: z.string().min(32, 'JWT Secret must be at least 32 characters'),
JWT_REFRESH_SECRET: z.string().min(32, 'JWT Refresh Secret must be at least 32 characters'),
JWT_EXPIRES_IN: z.string().default('15m'),
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
// Email
RESEND_API_KEY: z.string().optional(),
FROM_EMAIL: z.string().email().default('noreply@yourapp.com'),
FROM_NAME: z.string().default('Arktos'),
// App URLs
FRONTEND_URL: z.string().url().default('http://localhost:3000'),
BACKEND_URL: z.string().url().default('http://localhost:3001'),
APP_NAME: z.string().default('Arktos'),
// Security
BCRYPT_SALT_ROUNDS: z.string().default('12'),
PASSWORD_MIN_LENGTH: z.string().default('8'),
MAX_LOGIN_ATTEMPTS: z.string().default('5'),
ACCOUNT_LOCK_TIME: z.string().default('900000'),
// CORS
CORS_ORIGIN: z.string().default('http://localhost:3000'),
CORS_CREDENTIALS: z.string().default('true'),
// Rate Limiting
RATE_LIMIT_WINDOW: z.string().default('900000'),
RATE_LIMIT_MAX: z.string().default('100'),
// Logging
LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug']).default('info'),
LOG_FILE: z.string().default('logs/app.log'),
});
// Authentication schemas
export const loginSchema = z.object({
email: z.string().email('Invalid email format'),
password: z.string().min(1, 'Password is required'),
});
export const registerSchema = z.object({
email: z.string().email('Invalid email format'),
password: z
.string()
.min(8, 'Password must be at least 8 characters long')
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 'Password must contain at least one lowercase letter, one uppercase letter, and one number'),
firstName: z.string().min(1, 'First name is required').max(50, 'First name too long').optional(),
lastName: z.string().min(1, 'Last name is required').max(50, 'Last name too long').optional(),
username: z
.string()
.min(3, 'Username must be at least 3 characters')
.max(30, 'Username too long')
.regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, underscores, and hyphens')
.optional(),
});
export const refreshTokenSchema = z.object({
refreshToken: z.string().min(1, 'Refresh token is required'),
});
export const resendVerificationSchema = z.object({
email: z.string().email('Invalid email format'),
});
export const forgotPasswordSchema = z.object({
email: z.string().email('Invalid email format'),
});
export const resetPasswordSchema = z.object({
token: z.string().min(1, 'Reset token is required'),
newPassword: z
.string()
.min(8, 'Password must be at least 8 characters long')
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 'Password must contain at least one lowercase letter, one uppercase letter, and one number'),
});
export const changePasswordSchema = z.object({
currentPassword: z.string().min(1, 'Current password is required'),
newPassword: z
.string()
.min(8, 'Password must be at least 8 characters long')
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/, 'Password must contain at least one lowercase letter, one uppercase letter, and one number'),
}).refine((data) => {
return data.newPassword !== data.currentPassword;
}, {
message: 'New password must be different from current password',
path: ['newPassword'],
});
// Profile schemas
export const updateProfileSchema = z.object({
firstName: z.string().min(1, 'First name is required').max(50, 'First name too long').optional(),
lastName: z.string().min(1, 'Last name is required').max(50, 'Last name too long').optional(),
username: z
.string()
.min(3, 'Username must be at least 3 characters')
.max(30, 'Username too long')
.regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, underscores, and hyphens')
.optional(),
avatar: z.string().url('Avatar must be a valid URL').optional(),
});
// Pagination schema
export const paginationSchema = z.object({
page: z.string().default('1'),
limit: z.string().default('10'),
});
// Contact/Support schemas
export const contactSchema = z.object({
name: z.string().min(1, 'Name is required').max(100, 'Name too long'),
email: z.string().email('Invalid email format'),
subject: z.string().min(1, 'Subject is required').max(200, 'Subject too long'),
message: z.string().min(10, 'Message too short').max(2000, 'Message too long'),
});
export type EnvSchema = z.infer<typeof envSchema>;
export type LoginSchema = z.infer<typeof loginSchema>;
export type RegisterSchema = z.infer<typeof registerSchema>;
export type RefreshTokenSchema = z.infer<typeof refreshTokenSchema>;
export type ResendVerificationSchema = z.infer<typeof resendVerificationSchema>;
export type ForgotPasswordSchema = z.infer<typeof forgotPasswordSchema>;
export type ResetPasswordSchema = z.infer<typeof resetPasswordSchema>;
export type ChangePasswordSchema = z.infer<typeof changePasswordSchema>;
export type UpdateProfileSchema = z.infer<typeof updateProfileSchema>;
export type PaginationSchema = z.infer<typeof paginationSchema>;
export type ContactSchema = z.infer<typeof contactSchema>;