Initial commit

This commit is contained in:
Olivier CROGUENNEC
2026-06-13 14:57:15 +02:00
commit 48ed7fe65e
209 changed files with 49979 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
import jwt from 'jsonwebtoken';
import db from '../db/index.js';
const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-change-me';
export function signToken(payload) {
return jwt.sign(payload, JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN || '7d',
});
}
export function requireAuth(req, res, next) {
const header = req.headers.authorization || '';
const [scheme, token] = header.split(' ');
if (scheme !== 'Bearer' || !token) {
return res.status(401).json({ error: 'Missing or invalid Authorization header' });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = { id: decoded.sub, email: decoded.email };
next();
} catch {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
/** À utiliser après requireAuth. Refuse l'accès si l'utilisateur n'est pas admin. */
export function requireAdmin(req, res, next) {
const row = db.prepare('SELECT role FROM users WHERE id = ?').get(req.user.id);
if (!row || row.role !== 'admin') {
return res.status(403).json({ error: 'Accès réservé aux administrateurs' });
}
req.user.role = 'admin';
next();
}
+23
View File
@@ -0,0 +1,23 @@
import { ZodError } from 'zod';
// eslint-disable-next-line no-unused-vars
export function errorHandler(err, req, res, next) {
if (err instanceof ZodError) {
return res.status(400).json({
error: 'Validation error',
details: err.flatten(),
});
}
if (err.status) {
return res.status(err.status).json({ error: err.message });
}
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
export class HttpError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
@@ -0,0 +1,22 @@
import db from '../db/index.js';
import { HttpError } from './errorHandler.js';
/**
* Resolves the active investisseur from the X-Investisseur-Id header (or query),
* verifies it belongs to the authenticated user, and exposes it as req.investisseur.
*
* Routes that operate on investisseur-scoped data must use this middleware.
*/
export function requireInvestisseur(req, res, next) {
const raw = req.header('X-Investisseur-Id') || req.query.investisseurId;
const id = Number(raw);
if (!id) throw new HttpError(400, 'Missing investisseur id (header X-Investisseur-Id)');
const row = db
.prepare('SELECT id, nom, type_fiscal FROM investisseurs WHERE id = ? AND user_id = ?')
.get(id, req.user.id);
if (!row) throw new HttpError(403, 'Investisseur not found or not owned by user');
req.investisseur = row;
next();
}