import { Router } from 'express'; import bcrypt from 'bcryptjs'; import { z } from 'zod'; import db from '../db/index.js'; import { signToken, requireAuth } from '../middleware/auth.js'; import { HttpError } from '../middleware/errorHandler.js'; const router = Router(); const RegisterSchema = z.object({ email: z.string().email(), password: z.string().min(8), displayName: z.string().min(1).optional(), }); const LoginSchema = z.object({ email: z.string().email(), password: z.string().min(1), }); router.post('/register', (req, res, next) => { try { const body = RegisterSchema.parse(req.body); const exists = db.prepare('SELECT id FROM users WHERE email = ?').get(body.email); if (exists) throw new HttpError(409, 'Email already registered'); // Le premier utilisateur inscrit devient automatiquement administrateur const isFirst = db.prepare('SELECT COUNT(*) AS n FROM users').get().n === 0; const role = isFirst ? 'admin' : 'user'; const hash = bcrypt.hashSync(body.password, 10); const result = db .prepare('INSERT INTO users (email, password_hash, display_name, role) VALUES (?, ?, ?, ?)') .run(body.email, hash, body.displayName || null, role); const userId = result.lastInsertRowid; // Auto-create le premier profil famille (= l'utilisateur lui-même) const fullName = body.displayName || 'Mon profil'; const prenom = fullName.includes(' ') ? fullName.split(' ')[0] : null; const invResult = db.prepare( `INSERT INTO investisseurs (user_id, nom, prenom, type, type_fiscal, is_principal) VALUES (?, ?, ?, 'famille', 'PP', 1)` ).run(userId, fullName, prenom); // Auto-créer un compte courant pour le profil principal db.prepare( 'INSERT INTO comptes (user_id, nom, type, investisseur_id) VALUES (?,?,?,?)' ).run(userId, `Compte courant — ${fullName}`, 'compte_courant', invResult.lastInsertRowid); const token = signToken({ sub: userId, email: body.email }); res.status(201).json({ token, user: { id: userId, email: body.email, displayName: body.displayName || null, role }, }); } catch (e) { next(e); } }); router.post('/login', (req, res, next) => { try { const body = LoginSchema.parse(req.body); const user = db .prepare('SELECT id, email, password_hash, display_name, role FROM users WHERE email = ?') .get(body.email); if (!user) throw new HttpError(401, 'Invalid credentials'); const ok = bcrypt.compareSync(body.password, user.password_hash); if (!ok) throw new HttpError(401, 'Invalid credentials'); const token = signToken({ sub: user.id, email: user.email }); res.json({ token, user: { id: user.id, email: user.email, displayName: user.display_name, role: user.role }, }); } catch (e) { next(e); } }); router.get('/me', requireAuth, (req, res) => { const user = db .prepare('SELECT id, email, display_name, role FROM users WHERE id = ?') .get(req.user.id); res.json({ user }); }); const UpdateMeSchema = z.object({ displayName: z.string().min(1).max(80).optional(), email: z.string().email().optional(), currentPassword: z.string().optional(), newPassword: z.string().min(8).optional(), }); router.put('/me', requireAuth, (req, res, next) => { try { const body = UpdateMeSchema.parse(req.body); const current = db .prepare('SELECT id, email, password_hash, display_name FROM users WHERE id = ?') .get(req.user.id); let newHash = undefined; if (body.newPassword) { if (!body.currentPassword) throw new HttpError(400, 'Mot de passe actuel requis'); const ok = bcrypt.compareSync(body.currentPassword, current.password_hash); if (!ok) throw new HttpError(401, 'Mot de passe actuel incorrect'); newHash = bcrypt.hashSync(body.newPassword, 10); } if (body.email && body.email !== current.email) { const taken = db.prepare('SELECT id FROM users WHERE email = ? AND id != ?').get(body.email, req.user.id); if (taken) throw new HttpError(409, 'Email déjà utilisé par un autre compte'); } const newEmail = body.email ?? current.email; const newDisplayName = body.displayName !== undefined ? body.displayName : current.display_name; const newPasswordHash = newHash ?? current.password_hash; db.prepare( "UPDATE users SET email=?, display_name=?, password_hash=?, updated_at=datetime('now') WHERE id=?" ).run(newEmail, newDisplayName, newPasswordHash, req.user.id); const token = newEmail !== current.email ? signToken({ sub: req.user.id, email: newEmail }) : undefined; res.json({ user: { id: req.user.id, email: newEmail, display_name: newDisplayName }, ...(token ? { token } : {}), }); } catch (e) { next(e); } }); export default router;