Possibilité d'avoir une clé API Globale par famille
This commit is contained in:
1 parent
9eb19efd92
commit
441da1975a
12 files changed
+224
-62
No files matched your search
@@ -17,7 +17,7 @@ function generateKey() {
|
||||
/* ── GET /api/api-keys ── liste des clés de l'utilisateur connecté ──────── */
|
||||
router.get('/', (req, res) => {
|
||||
const rows = db.prepare(`
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id,
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id, k.scope_all,
|
||||
i.nom AS investisseur_nom, k.created_at, k.last_used_at, k.revoked_at
|
||||
FROM api_keys k
|
||||
JOIN investisseurs i ON i.id = k.investisseur_id
|
||||
@@ -27,29 +27,37 @@ router.get('/', (req, res) => {
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
/* ── POST /api/api-keys ── créer une nouvelle clé (nom + investisseur) ──── */
|
||||
/* ── POST /api/api-keys ── créer une nouvelle clé (nom + investisseur, ou
|
||||
scope_all pour "Famille et entreprises") ──────────────────────────────
|
||||
scope_all=true n'est autorisé que si investisseur_id désigne le profil
|
||||
principal — enforcement serveur, indépendant de ce que montre l'UI, pour
|
||||
qu'un appel direct à l'API ne puisse pas contourner cette règle. */
|
||||
router.post('/', (req, res, next) => {
|
||||
try {
|
||||
const nom = (req.body?.nom || '').trim();
|
||||
const investisseur_id = Number(req.body?.investisseur_id);
|
||||
const scope_all = !!req.body?.scope_all;
|
||||
|
||||
if (!nom) throw new HttpError(400, 'Le nom de la clé est requis');
|
||||
if (nom.length > 100) throw new HttpError(400, 'Le nom de la clé est trop long (100 caractères max)');
|
||||
if (!Number.isInteger(investisseur_id)) throw new HttpError(400, 'investisseur_id est requis');
|
||||
|
||||
const inv = db.prepare('SELECT id FROM investisseurs WHERE id = ? AND user_id = ?')
|
||||
const inv = db.prepare('SELECT id, is_principal FROM investisseurs WHERE id = ? AND user_id = ?')
|
||||
.get(investisseur_id, req.user.id);
|
||||
if (!inv) throw new HttpError(404, 'Investisseur introuvable');
|
||||
if (scope_all && !inv.is_principal) {
|
||||
throw new HttpError(403, 'Seul le profil principal peut créer une clé « Famille et entreprises »');
|
||||
}
|
||||
|
||||
const { full, hash, prefix } = generateKey();
|
||||
|
||||
const info = db.prepare(`
|
||||
INSERT INTO api_keys (user_id, investisseur_id, nom, key_prefix, key_hash, scopes)
|
||||
VALUES (?, ?, ?, ?, ?, 'read')
|
||||
`).run(req.user.id, investisseur_id, nom, prefix, hash);
|
||||
INSERT INTO api_keys (user_id, investisseur_id, nom, key_prefix, key_hash, scopes, scope_all)
|
||||
VALUES (?, ?, ?, ?, ?, 'read', ?)
|
||||
`).run(req.user.id, investisseur_id, nom, prefix, hash, scope_all ? 1 : 0);
|
||||
|
||||
const saved = db.prepare(`
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id,
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id, k.scope_all,
|
||||
i.nom AS investisseur_nom, k.created_at, k.last_used_at, k.revoked_at
|
||||
FROM api_keys k JOIN investisseurs i ON i.id = k.investisseur_id
|
||||
WHERE k.id = ?
|
||||
@@ -74,7 +82,7 @@ router.patch('/:id', (req, res, next) => {
|
||||
db.prepare('UPDATE api_keys SET nom = ? WHERE id = ?').run(nom, req.params.id);
|
||||
|
||||
const saved = db.prepare(`
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id,
|
||||
SELECT k.id, k.nom, k.key_prefix, k.scopes, k.investisseur_id, k.scope_all,
|
||||
i.nom AS investisseur_nom, k.created_at, k.last_used_at, k.revoked_at
|
||||
FROM api_keys k JOIN investisseurs i ON i.id = k.investisseur_id
|
||||
WHERE k.id = ?
|
||||
|
||||
@@ -27,9 +27,17 @@ const router = Router();
|
||||
* 200: { description: Synthèse KPI }
|
||||
*/
|
||||
router.get('/', (req, res) => {
|
||||
const invId = req.investisseurId;
|
||||
const annee = req.query.annee ? Number(req.query.annee) : null;
|
||||
|
||||
// Clé "Famille et entreprises" (scope_all) → agrège tous les investisseurs
|
||||
// du foyer (req.userId) ; clé mono-investisseur → filtre sur req.investisseurId.
|
||||
// Dans les deux cas un seul paramètre suffit : soit l'id investisseur, soit
|
||||
// le user_id pour la sous-requête IN (…) — en miroir de ?scope=all côté JWT.
|
||||
const invCond = (col) => req.investisseurScopeAll
|
||||
? `${col} IN (SELECT id FROM investisseurs WHERE user_id = ?)`
|
||||
: `${col} = ?`;
|
||||
const invParam = req.investisseurScopeAll ? req.userId : req.investisseurId;
|
||||
|
||||
// ── Investissements : mêmes formules que le KPI "Capital investi" / "Capital
|
||||
// en risque" de l'app interne (Dashboard.jsx → capitalDeploye = encours +
|
||||
// en_defaut). "capital_investi" et "capital_en_risque" sont des soldes
|
||||
@@ -51,11 +59,11 @@ router.get('/', (req, res) => {
|
||||
- COALESCE((SELECT SUM(rb.capital) FROM remboursements rb WHERE rb.investissement_id = i.id AND rb.type = 'normal'), 0)
|
||||
END), 0) AS capital_en_risque,
|
||||
COALESCE(SUM(CASE WHEN i.statut='rembourse' THEN i.montant_investi END), 0) AS rembourse
|
||||
FROM investissements i WHERE i.investisseur_id = ?
|
||||
`).get(invId);
|
||||
FROM investissements i WHERE ${invCond('i.investisseur_id')}
|
||||
`).get(invParam);
|
||||
|
||||
const interetsConds = ['i.investisseur_id = ?'];
|
||||
const interetsParams = [invId];
|
||||
const interetsConds = [invCond('i.investisseur_id')];
|
||||
const interetsParams = [invParam];
|
||||
if (annee) { interetsConds.push(`strftime('%Y', r.date_remb) = ?`); interetsParams.push(String(annee)); }
|
||||
|
||||
const interets = db.prepare(`
|
||||
@@ -73,8 +81,8 @@ router.get('/', (req, res) => {
|
||||
SELECT
|
||||
COALESCE(SUM(CASE WHEN type='depot' THEN montant END), 0) AS total_depots,
|
||||
COALESCE(SUM(CASE WHEN type='retrait' THEN montant END), 0) AS total_retraits
|
||||
FROM depots_retraits WHERE investisseur_id = ?
|
||||
`).get(invId);
|
||||
FROM depots_retraits WHERE ${invCond('investisseur_id')}
|
||||
`).get(invParam);
|
||||
|
||||
res.json({ investissements, interets: { ...interets, annee: annee || null }, cash });
|
||||
});
|
||||
|
||||
@@ -14,14 +14,21 @@ const router = Router();
|
||||
* 200: { description: Liste des mouvements }
|
||||
*/
|
||||
router.get('/', (req, res) => {
|
||||
// Clé "Famille et entreprises" (scope_all) → tous les investisseurs du
|
||||
// foyer ; clé mono-investisseur → filtre sur req.investisseurId.
|
||||
const invCond = req.investisseurScopeAll
|
||||
? 'dr.investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)'
|
||||
: 'dr.investisseur_id = ?';
|
||||
const invParam = req.investisseurScopeAll ? req.userId : req.investisseurId;
|
||||
|
||||
const rows = db.prepare(`
|
||||
SELECT dr.id, dr.date_operation, p.nom AS plateforme_nom, dr.type,
|
||||
dr.montant, dr.libelle
|
||||
FROM depots_retraits dr
|
||||
JOIN plateformes p ON p.id = dr.plateforme_id
|
||||
WHERE dr.investisseur_id = ?
|
||||
WHERE ${invCond}
|
||||
ORDER BY dr.date_operation DESC
|
||||
`).all(req.investisseurId);
|
||||
`).all(invParam);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
|
||||
@@ -27,8 +27,12 @@ const LIST_COLUMNS = `
|
||||
*/
|
||||
router.get('/', (req, res) => {
|
||||
const { statut } = req.query;
|
||||
const conds = ['i.investisseur_id = ?'];
|
||||
const args = [req.investisseurId];
|
||||
// Clé "Famille et entreprises" (scope_all) → tous les investisseurs du
|
||||
// foyer ; clé mono-investisseur → filtre sur req.investisseurId.
|
||||
const conds = [req.investisseurScopeAll
|
||||
? 'i.investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)'
|
||||
: 'i.investisseur_id = ?'];
|
||||
const args = [req.investisseurScopeAll ? req.userId : req.investisseurId];
|
||||
if (statut) { conds.push('i.statut = ?'); args.push(statut); }
|
||||
|
||||
const rows = db.prepare(`
|
||||
@@ -59,12 +63,17 @@ router.get('/', (req, res) => {
|
||||
*/
|
||||
router.get('/:id', (req, res, next) => {
|
||||
try {
|
||||
const invCond = req.investisseurScopeAll
|
||||
? 'i.investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)'
|
||||
: 'i.investisseur_id = ?';
|
||||
const invParam = req.investisseurScopeAll ? req.userId : req.investisseurId;
|
||||
|
||||
const inv = db.prepare(`
|
||||
SELECT ${LIST_COLUMNS}, i.notes
|
||||
FROM investissements i
|
||||
JOIN plateformes p ON p.id = i.plateforme_id
|
||||
WHERE i.id = ? AND i.investisseur_id = ?
|
||||
`).get(req.params.id, req.investisseurId);
|
||||
WHERE i.id = ? AND ${invCond}
|
||||
`).get(req.params.id, invParam);
|
||||
if (!inv) throw new HttpError(404, 'Investissement introuvable');
|
||||
|
||||
const remboursements = db.prepare(`
|
||||
|
||||
@@ -7,16 +7,30 @@ const router = Router();
|
||||
* @openapi
|
||||
* /investisseur:
|
||||
* get:
|
||||
* summary: Profil de l'investisseur lié à la clé API
|
||||
* summary: Profil investisseur (ou liste, pour une clé "Famille et entreprises")
|
||||
* description: >
|
||||
* Avec une clé scopée à un seul investisseur, renvoie son profil (objet).
|
||||
* Avec une clé "Famille et entreprises" (scope_all), renvoie la liste des
|
||||
* investisseurs du foyer (tableau) — il n'y a plus un profil unique à
|
||||
* renvoyer.
|
||||
* tags: [Investisseur]
|
||||
* security: [{ ApiKeyAuth: [] }]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Profil investisseur
|
||||
* description: Profil investisseur, ou liste de profils si scope_all
|
||||
* 401:
|
||||
* description: Clé API invalide ou manquante
|
||||
*/
|
||||
router.get('/', (req, res) => {
|
||||
if (req.investisseurScopeAll) {
|
||||
const investisseurs = db.prepare(`
|
||||
SELECT id, nom, prenom, type, type_fiscal, notes, created_at
|
||||
FROM investisseurs WHERE user_id = ?
|
||||
ORDER BY is_principal DESC, id ASC
|
||||
`).all(req.userId);
|
||||
return res.json(investisseurs);
|
||||
}
|
||||
|
||||
const inv = db.prepare(`
|
||||
SELECT id, nom, prenom, type, type_fiscal, notes, created_at
|
||||
FROM investisseurs WHERE id = ?
|
||||
|
||||
@@ -22,8 +22,12 @@ const router = Router();
|
||||
*/
|
||||
router.get('/', (req, res) => {
|
||||
const { date_debut, date_fin } = req.query;
|
||||
const conds = ['i.investisseur_id = ?'];
|
||||
const args = [req.investisseurId];
|
||||
// Clé "Famille et entreprises" (scope_all) → tous les investisseurs du
|
||||
// foyer ; clé mono-investisseur → filtre sur req.investisseurId.
|
||||
const conds = [req.investisseurScopeAll
|
||||
? 'i.investisseur_id IN (SELECT id FROM investisseurs WHERE user_id = ?)'
|
||||
: 'i.investisseur_id = ?'];
|
||||
const args = [req.investisseurScopeAll ? req.userId : req.investisseurId];
|
||||
if (date_debut) { conds.push('r.date_remb >= ?'); args.push(date_debut); }
|
||||
if (date_fin) { conds.push('r.date_remb <= ?'); args.push(date_fin); }
|
||||
|
||||
|
||||
Reference in new issue
Block a user