134 lines
4.8 KiB
JavaScript
134 lines
4.8 KiB
JavaScript
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;
|