import bcrypt from 'bcryptjs'; import jwt from 'jsonwebtoken'; import crypto from 'node:crypto'; import { z } from 'zod'; import * as usersRepo from '../repositories/users.repository.js'; import * as tokensRepo from '../repositories/tokens.repository.js'; import { TokenType } from '../generated/prisma/enums.js'; const JWT_SECRET = process.env.JWT_SECRET ?? 'changeme'; const JWT_EXPIRES_IN = '15m'; const REFRESH_EXPIRES_DAYS = 7; export const registerSchema = z.object({ name: z.string().check(z.minLength(2)), email: z.email(), password: z.string().check(z.minLength(6)), }); export const loginSchema = z.object({ email: z.email(), password: z.string().check(z.minLength(1)), }); export async function register(input: z.infer) { const existing = await usersRepo.findUserByEmail(input.email); if (existing) throw new Error('Email already in use'); const hashed = await bcrypt.hash(input.password, 10); const now = new Date(); const user = await usersRepo.createUser({ id: crypto.randomUUID(), name: input.name, email: input.email, password: hashed, updatedAt: now, }); return { id: user.id, name: user.name, email: user.email }; } export async function login(input: z.infer) { const user = await usersRepo.findUserByEmail(input.email); if (!user) throw new Error('Invalid credentials'); const valid = await bcrypt.compare(input.password, user.password); if (!valid) throw new Error('Invalid credentials'); const accessToken = jwt.sign({ sub: user.id }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }); const refreshToken = crypto.randomUUID(); const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + REFRESH_EXPIRES_DAYS); await tokensRepo.createToken({ id: crypto.randomUUID(), token: refreshToken, type: TokenType.REFRESH, userId: user.id, expiresAt, }); return { accessToken, refreshToken }; } export async function logout(refreshToken: string) { await tokensRepo.deleteToken(refreshToken); } export function verifyAccessToken(token: string): { sub: string } { return jwt.verify(token, JWT_SECRET) as { sub: string }; }