215 lines
12 KiB
JavaScript
215 lines
12 KiB
JavaScript
import 'dotenv/config';
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import helmet from 'helmet';
|
|
import morgan from 'morgan';
|
|
import rateLimit from 'express-rate-limit';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
import authRouter from './routes/auth.js';
|
|
import investisseursRouter from './routes/investisseurs.js';
|
|
import plateformesRouter from './routes/plateformes.js';
|
|
import categoriesRouter from './routes/categories.js';
|
|
import depotsRetraitsRouter from './routes/depotsRetraits.js';
|
|
import investissementsRouter from './routes/investissements.js';
|
|
import investissementsPeRouter from './routes/investissementsPe.js';
|
|
import remboursementsRouter from './routes/remboursements.js';
|
|
import simulRouter from './routes/simul.js';
|
|
import dashboardRouter from './routes/dashboard.js';
|
|
import dashboardPeRouter from './routes/dashboardPe.js';
|
|
import taxreportRouter from './routes/taxreport.js';
|
|
import taxreportPeRouter from './routes/taxreportPe.js';
|
|
import platefomeTaxRouter from './routes/plateforme-tax.js';
|
|
import importsRouter from './routes/imports.js';
|
|
import pfuRouter from './routes/pfu.js';
|
|
import notationRouter from './routes/notation.js';
|
|
import garantiesRouter from './routes/garanties.js';
|
|
import objectifsRouter from './routes/objectifs.js';
|
|
import reinvestissementsRouter from './routes/reinvestissements.js';
|
|
import fraisOperationsRouter from './routes/fraisOperations.js';
|
|
import correctionsRouter from './routes/corrections.js';
|
|
import comptesRouter from './routes/comptes.js';
|
|
import preferencesRouter from './routes/preferences.js';
|
|
import iconsRouter from './routes/icons.js';
|
|
import { errorHandler } from './middleware/errorHandler.js';
|
|
import { requireAuth, requireAdmin } from './middleware/auth.js';
|
|
import { startAutoStatutJob } from './jobs/autoStatut.js';
|
|
import { startAutoExportJob } from './jobs/autoExport.js';
|
|
import { startAutoTicketStatusJob } from './jobs/autoTicketStatus.js';
|
|
import { startAutoCleanNotifsJob } from './jobs/autoCleanNotifs.js';
|
|
import { startCheckDonneesIncompletesJob } from './jobs/checkDonneesIncompletes.js';
|
|
import adminRouter from './routes/admin.js';
|
|
import invitationsRouter from './routes/invitations.js';
|
|
import auditLogsRouter from './routes/auditLogs.js';
|
|
import smtpRouter from './routes/smtp.js';
|
|
import generalRouter from './routes/general.js';
|
|
import adminAiRouter from './routes/adminAi.js';
|
|
import tauxCreditImpotRouter from './routes/tauxCreditImpot.js';
|
|
import referentielRouter from './routes/referentiel.js';
|
|
import referentielPublicRouter from './routes/referentielPublic.js';
|
|
import institutionsReferentielRouter from './routes/institutionsReferentiel.js';
|
|
import institutionsReferentielPublicRouter from './routes/institutionsReferentielPublic.js';
|
|
import refCategoriesInstitutionsRouter from './routes/ref-categories-institutions.js';
|
|
import enveloppesReferentielRouter from './routes/enveloppesReferentiel.js';
|
|
import enveloppesReferentielPublicRouter from './routes/enveloppesReferentielPublic.js';
|
|
import supportsReferentielRouter from './routes/supportsReferentiel.js';
|
|
import empruntsRouter from './routes/emprunts.js';
|
|
import refCategoriesEnveloppesRouter from './routes/ref-categories-enveloppes.js';
|
|
import refCategoriesRouter from './routes/ref-categories.js';
|
|
import refSecteursRouter from './routes/ref-secteurs.js';
|
|
import categoriesInvRouter from './routes/categories-inv.js';
|
|
import secteursInvRouter from './routes/secteurs-inv.js';
|
|
import associationsInvRouter from './routes/associations-inv.js';
|
|
import notificationsRouter from './routes/notifications.js';
|
|
import ticketsRouter from './routes/tickets.js';
|
|
import apiKeysRouter from './routes/apiKeys.js';
|
|
import v1Router from './routes/v1/index.js';
|
|
import documentsRouter from './routes/documents.js';
|
|
import aiRouter from './routes/ai.js';
|
|
import workspacesRouter from './routes/workspaces.js';
|
|
import adminWorkspacesRouter from './routes/adminWorkspaces.js';
|
|
import { requireApiKey } from './middleware/apiKey.js';
|
|
import { swaggerSpec, swaggerUi } from './swagger.js';
|
|
import db from './db/index.js';
|
|
import { getSmtpConfig } from './utils/mailer.js';
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 4000;
|
|
|
|
app.use(helmet({ crossOriginResourcePolicy: { policy: 'cross-origin' } }));
|
|
app.use(cors());
|
|
app.use(express.json({ limit: '5mb' }));
|
|
app.use(morgan('dev'));
|
|
|
|
// ── Logos plateformes — servis sans authentification ─────────────────────
|
|
const _dataDir = process.env.DATA_DIR
|
|
? path.resolve(process.env.DATA_DIR)
|
|
: path.resolve(__dirname, '../../data'); // fallback dev : 2 niveaux au-dessus de src/
|
|
const logosDir = path.join(_dataDir, 'logos');
|
|
app.use('/api/logos', express.static(logosDir, { maxAge: '1d', fallthrough: false }));
|
|
const iconsDir2 = path.join(_dataDir, 'icons');
|
|
app.use('/api/icons-files', express.static(iconsDir2, { maxAge: '1h', fallthrough: false }));
|
|
|
|
// Basic rate-limit on auth endpoints
|
|
// Seuil relevé (50 → 300) : usage local mono-utilisateur/multi-investisseur avec
|
|
// beaucoup d'allers-retours de test (login, 2FA, création de comptes admin/invitation…),
|
|
// l'ancien seuil de 50 req/15min était atteint trop facilement en usage normal.
|
|
const authLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 300,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
});
|
|
|
|
app.get('/api/health', (_, res) => res.json({ ok: true, ts: new Date().toISOString() }));
|
|
|
|
// Informations publiques de la plateforme (utilisées sur la page de login)
|
|
app.get('/api/app-info', (_, res) => {
|
|
try {
|
|
const cfg = getSmtpConfig();
|
|
const icon = db.prepare(`SELECT filename FROM app_icons WHERE name = 'logo-app' LIMIT 1`).get();
|
|
const row = db.prepare('SELECT allow_registration, min_password_length, mcp_url FROM smtp_config WHERE id = 1').get();
|
|
// Thème de couleur (Admin > Général) : appliqué à TOUS les utilisateurs,
|
|
// y compris sur la page de connexion (avant authentification) — d'où son
|
|
// exposition ici, comme allowRegistration/mcpUrl déjà publics.
|
|
const theme = db.prepare('SELECT color_theme FROM parametres_theme WHERE id = 1').get();
|
|
res.json({
|
|
appName: cfg.appName || 'Crowdlending Tracker',
|
|
iconUrl: icon ? `/api/icons-files/${icon.filename}` : null,
|
|
allowRegistration: row ? row.allow_registration !== 0 : true,
|
|
minPasswordLength: row ? (row.min_password_length || 8) : 8,
|
|
mcpUrl: row ? (row.mcp_url || '') : '',
|
|
colorTheme: theme?.color_theme || 'classique',
|
|
});
|
|
} catch {
|
|
res.json({ appName: 'Crowdlending Tracker', iconUrl: null, allowRegistration: true, minPasswordLength: 8, mcpUrl: '', colorTheme: 'classique' });
|
|
}
|
|
});
|
|
|
|
app.use('/api/auth', authLimiter, authRouter);
|
|
|
|
// ── API publique v1 (lecture seule, authentification par clé API) ────────
|
|
// Montée ici, avant les routes internes ci-dessous : la ligne
|
|
// `app.use('/api', requireAuth, associationsInvRouter)` plus bas capte tout
|
|
// préfixe /api/* avec le JWT interne, ce qui intercepterait /api/v1/* et
|
|
// /api/docs si elles étaient montées après.
|
|
app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
app.get('/api/openapi.json', (_, res) => res.json(swaggerSpec));
|
|
app.use('/api/v1', requireApiKey, v1Router);
|
|
|
|
// All routes below require authentication
|
|
app.use('/api/investisseurs', requireAuth, investisseursRouter);
|
|
app.use('/api/plateformes', requireAuth, plateformesRouter);
|
|
app.use('/api/categories', requireAuth, categoriesRouter);
|
|
app.use('/api/depots-retraits', requireAuth, depotsRetraitsRouter);
|
|
app.use('/api/investissements', requireAuth, investissementsRouter);
|
|
app.use('/api/investissements-pe', requireAuth, investissementsPeRouter);
|
|
app.use('/api/remboursements', requireAuth, remboursementsRouter);
|
|
app.use('/api/simul', requireAuth, simulRouter);
|
|
app.use('/api/dashboard', requireAuth, dashboardRouter);
|
|
app.use('/api/dashboard-pe', requireAuth, dashboardPeRouter);
|
|
app.use('/api/taxreport', requireAuth, taxreportRouter);
|
|
app.use('/api/taxreport-pe', requireAuth, taxreportPeRouter);
|
|
app.use('/api/plateforme-tax', requireAuth, platefomeTaxRouter);
|
|
app.use('/api/imports', requireAuth, importsRouter);
|
|
app.use('/api/pfu', requireAuth, pfuRouter);
|
|
app.use('/api/notation', requireAuth, notationRouter);
|
|
app.use('/api/garanties', requireAuth, garantiesRouter);
|
|
app.use('/api/objectifs', requireAuth, objectifsRouter);
|
|
app.use('/api/reinvestissements', requireAuth, reinvestissementsRouter);
|
|
app.use('/api/frais-operations', requireAuth, fraisOperationsRouter);
|
|
app.use('/api/corrections', requireAuth, correctionsRouter);
|
|
app.use('/api/comptes', requireAuth, comptesRouter);
|
|
app.use('/api/emprunts', requireAuth, empruntsRouter);
|
|
app.use('/api/preferences', requireAuth, preferencesRouter);
|
|
app.use('/api/icons', requireAuth, iconsRouter);
|
|
app.use('/api/admin', requireAuth, requireAdmin, adminRouter);
|
|
app.use('/api/admin/audit-logs', requireAuth, requireAdmin, auditLogsRouter);
|
|
app.use('/api/admin/smtp', requireAuth, requireAdmin, smtpRouter);
|
|
app.use('/api/admin/general', requireAuth, requireAdmin, generalRouter);
|
|
app.use('/api/admin/ai', requireAuth, requireAdmin, adminAiRouter);
|
|
// Invitations : routes admin protégées + routes publiques (register/validate)
|
|
app.use('/api/admin/invitations', requireAuth, requireAdmin, invitationsRouter);
|
|
app.use('/api/invitations', invitationsRouter);
|
|
app.use('/api/taux-credit-impot', requireAuth, tauxCreditImpotRouter);
|
|
app.use('/api/referentiel', requireAuth, requireAdmin, referentielRouter);
|
|
app.use('/api/referentiel-public', requireAuth, referentielPublicRouter);
|
|
// Chantier "Référentiel Institutions financières" (26/09/26, demande Olivier) — Phase 1 :
|
|
// catalogue admin transverse aux workspaces, cf. institutionsReferentiel.js.
|
|
app.use('/api/institutions-referentiel', requireAuth, requireAdmin, institutionsReferentielRouter);
|
|
app.use('/api/institutions-referentiel-public', requireAuth, institutionsReferentielPublicRouter);
|
|
app.use('/api/ref-categories-institutions', requireAuth, requireAdmin, refCategoriesInstitutionsRouter);
|
|
// Chantier "Référentiel Enveloppes & Supports d'épargne" (28/09/26, demande Olivier) —
|
|
// catalogue admin transverse aux workspaces, catalogue seul pour l'instant (pas de
|
|
// rattachement à l'existant), cf. enveloppesReferentiel.js.
|
|
app.use('/api/enveloppes-referentiel', requireAuth, requireAdmin, enveloppesReferentielRouter);
|
|
app.use('/api/enveloppes-referentiel-public', requireAuth, enveloppesReferentielPublicRouter);
|
|
app.use('/api/supports-referentiel', requireAuth, requireAdmin, supportsReferentielRouter);
|
|
app.use('/api/ref-categories-enveloppes', requireAuth, requireAdmin, refCategoriesEnveloppesRouter);
|
|
app.use('/api/ref-categories', requireAuth, requireAdmin, refCategoriesRouter);
|
|
app.use('/api/ref-secteurs', requireAuth, requireAdmin, refSecteursRouter);
|
|
app.use('/api/categories-inv', requireAuth, categoriesInvRouter);
|
|
app.use('/api/secteurs-inv', requireAuth, secteursInvRouter);
|
|
app.use('/api', requireAuth, associationsInvRouter);
|
|
app.use('/api/notifications', notificationsRouter);
|
|
app.use('/api/tickets', requireAuth, ticketsRouter);
|
|
app.use('/api/api-keys', requireAuth, apiKeysRouter);
|
|
app.use('/api/documents', requireAuth, documentsRouter);
|
|
app.use('/api/ai', requireAuth, aiRouter);
|
|
app.use('/api/workspaces', requireAuth, workspacesRouter);
|
|
app.use('/api/admin/workspaces', requireAuth, requireAdmin, adminWorkspacesRouter);
|
|
|
|
app.use(errorHandler);
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`Crowdlending API listening on http://localhost:${PORT}`);
|
|
startAutoStatutJob();
|
|
startAutoExportJob();
|
|
startAutoTicketStatusJob();
|
|
startAutoCleanNotifsJob();
|
|
startCheckDonneesIncompletesJob();
|
|
});
|