import bcrypt from 'bcryptjs'; import jwt, { type SignOptions } from 'jsonwebtoken'; import { AppDataSource } from '../config/data-source.js'; import { env } from '../config/env.js'; import { User, type UserRole } from '../entities/User.js'; const repo = () => AppDataSource.getRepository(User); export const sanitizeUser = (user: User) => ({ id: user.id, fullName: user.fullName, email: user.email, role: user.role, isActive: user.isActive, defaultCurrency: user.defaultCurrency, integrationsEnabled: Boolean(user.integrationsEnabled), reportPreferences: user.reportPreferences ?? { enabled: false, frequency: 'monthly', thresholdAmount: 0, sendToEmail: user.email, categoryIds: [] }, createdAt: user.createdAt }); export const hashPassword = async (password: string) => bcrypt.hash(password, 10); export const comparePassword = async (password: string, hash: string) => bcrypt.compare(password, hash); export const signToken = (payload: { id: string; email: string; role: UserRole }) => jwt.sign(payload, env.JWT_SECRET, { expiresIn: env.JWT_EXPIRES_IN as SignOptions['expiresIn'] }); export const findUserByEmail = (email: string) => repo().findOne({ where: { email: email.toLowerCase() } }); export const createUser = async (input: { fullName: string; email: string; password: string; role?: UserRole; defaultCurrency?: string; }) => { const existing = await repo().findOne({ where: { email: input.email.toLowerCase() } }); if (existing) throw new Error('Email address is already in use'); const user = repo().create({ fullName: input.fullName, email: input.email.toLowerCase(), passwordHash: await hashPassword(input.password), role: input.role ?? 'USER', defaultCurrency: input.defaultCurrency ?? env.DEFAULT_CURRENCY, integrationsEnabled: false, reportPreferences: { enabled: false, frequency: 'monthly', thresholdAmount: 0, sendToEmail: input.email.toLowerCase(), categoryIds: [] } }); return repo().save(user); };