Maj Section Général
This commit is contained in:
@@ -1858,4 +1858,18 @@ console.log('[DB] Migrations 2FA OK');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── Migration : colonnes paramètres généraux dans smtp_config ─────────────
|
||||
{
|
||||
const cols = db.prepare("PRAGMA table_info(smtp_config)").all().map(c => c.name);
|
||||
if (!cols.includes('allow_registration')) {
|
||||
db.exec("ALTER TABLE smtp_config ADD COLUMN allow_registration INTEGER NOT NULL DEFAULT 1");
|
||||
console.log('[DB] Colonne smtp_config.allow_registration ajoutée');
|
||||
}
|
||||
if (!cols.includes('min_password_length')) {
|
||||
db.exec("ALTER TABLE smtp_config ADD COLUMN min_password_length INTEGER NOT NULL DEFAULT 8");
|
||||
console.log('[DB] Colonne smtp_config.min_password_length ajoutée');
|
||||
}
|
||||
}
|
||||
|
||||
export default db;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* /api/admin/general — Paramètres généraux de l'application
|
||||
* Protégé par requireAuth + requireAdmin (appliqués dans server.js).
|
||||
*
|
||||
* GET / — retourne les 4 champs généraux
|
||||
* PATCH / — met à jour un ou plusieurs champs
|
||||
*
|
||||
* Route publique complémentaire : /api/app-info (server.js) expose
|
||||
* allowRegistration et minPasswordLength sans auth.
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import db from '../db/index.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
function ensureRow() {
|
||||
const exists = db.prepare('SELECT id FROM smtp_config WHERE id = 1').get();
|
||||
if (!exists) {
|
||||
db.prepare(`
|
||||
INSERT INTO smtp_config (id, enabled, host, port, secure, email, username, password,
|
||||
allow_unauth, app_name, app_url, allow_registration, min_password_length)
|
||||
VALUES (1, 0, '', 587, 0, '', '', '', 0, 'Crowdlending Tracker', '', 1, 8)
|
||||
`).run();
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/', (_req, res, next) => {
|
||||
try {
|
||||
ensureRow();
|
||||
const row = db.prepare('SELECT app_name, app_url, allow_registration, min_password_length FROM smtp_config WHERE id = 1').get();
|
||||
res.json({
|
||||
appName: row.app_name || 'Crowdlending Tracker',
|
||||
appUrl: row.app_url || '',
|
||||
allowRegistration: row.allow_registration !== 0,
|
||||
minPasswordLength: row.min_password_length || 8,
|
||||
});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
const PatchSchema = z.object({
|
||||
appName: z.string().min(1).max(100).optional(),
|
||||
appUrl: z.string().max(500).optional(),
|
||||
allowRegistration: z.boolean().optional(),
|
||||
minPasswordLength: z.number().int().min(6).max(64).optional(),
|
||||
});
|
||||
|
||||
router.patch('/', (req, res, next) => {
|
||||
try {
|
||||
ensureRow();
|
||||
const body = PatchSchema.parse(req.body);
|
||||
const row = db.prepare('SELECT app_name, app_url, allow_registration, min_password_length FROM smtp_config WHERE id = 1').get();
|
||||
|
||||
db.prepare(`
|
||||
UPDATE smtp_config SET
|
||||
app_name = ?,
|
||||
app_url = ?,
|
||||
allow_registration = ?,
|
||||
min_password_length = ?
|
||||
WHERE id = 1
|
||||
`).run(
|
||||
body.appName !== undefined ? body.appName : (row.app_name || 'Crowdlending Tracker'),
|
||||
body.appUrl !== undefined ? body.appUrl : (row.app_url || ''),
|
||||
body.allowRegistration !== undefined ? (body.allowRegistration ? 1 : 0) : (row.allow_registration !== 0 ? 1 : 0),
|
||||
body.minPasswordLength !== undefined ? body.minPasswordLength : (row.min_password_length || 8),
|
||||
);
|
||||
|
||||
res.json({ ok: true });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* /api/admin/smtp — Gestion de la configuration SMTP
|
||||
* Protégé par requireAuth + requireAdmin (appliqués dans server.js).
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import db from '../db/index.js';
|
||||
import { HttpError } from '../middleware/errorHandler.js';
|
||||
import { sendMail, buildEmailHtml, getSmtpConfig } from '../utils/mailer.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function ensureRow() {
|
||||
const exists = db.prepare('SELECT id FROM smtp_config WHERE id = 1').get();
|
||||
if (!exists) {
|
||||
db.prepare(`
|
||||
INSERT INTO smtp_config (id, enabled, host, port, secure, email, username, password,
|
||||
allow_unauth, app_name, app_url, allow_registration, min_password_length)
|
||||
VALUES (1, 0, '', 587, 0, '', '', '', 0, 'Crowdlending Tracker', '', 1, 8)
|
||||
`).run();
|
||||
}
|
||||
}
|
||||
|
||||
// ── GET /admin/smtp ─────────────────────────────────────────────────────────
|
||||
|
||||
router.get('/', (_req, res, next) => {
|
||||
try {
|
||||
ensureRow();
|
||||
const row = db.prepare('SELECT * FROM smtp_config WHERE id = 1').get();
|
||||
res.json({
|
||||
enabled: !!row.enabled,
|
||||
host: row.host || '',
|
||||
port: row.port || 587,
|
||||
secure: !!row.secure,
|
||||
email: row.email || '',
|
||||
username: row.username || '',
|
||||
hasPassword: !!(row.password),
|
||||
allowUnauth: !!row.allow_unauth,
|
||||
appName: row.app_name || 'Crowdlending Tracker',
|
||||
appUrl: row.app_url || '',
|
||||
allowRegistration: row.allow_registration !== 0,
|
||||
minPasswordLength: row.min_password_length || 8,
|
||||
});
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// ── PUT /admin/smtp ─────────────────────────────────────────────────────────
|
||||
|
||||
const SmtpSchema = z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
host: z.string().optional(),
|
||||
port: z.number().int().min(1).max(65535).optional(),
|
||||
secure: z.boolean().optional(),
|
||||
email: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
allowUnauth: z.boolean().optional(),
|
||||
appName: z.string().max(100).optional(),
|
||||
appUrl: z.string().max(500).optional(),
|
||||
});
|
||||
|
||||
router.put('/', (req, res, next) => {
|
||||
try {
|
||||
ensureRow();
|
||||
const body = SmtpSchema.parse(req.body);
|
||||
const row = db.prepare('SELECT * FROM smtp_config WHERE id = 1').get();
|
||||
|
||||
const updates = {
|
||||
enabled: body.enabled !== undefined ? (body.enabled ? 1 : 0) : row.enabled,
|
||||
host: body.host !== undefined ? body.host : row.host,
|
||||
port: body.port !== undefined ? body.port : row.port,
|
||||
secure: body.secure !== undefined ? (body.secure ? 1 : 0) : row.secure,
|
||||
email: body.email !== undefined ? body.email : row.email,
|
||||
username: body.username !== undefined ? body.username : row.username,
|
||||
password: body.password !== undefined ? body.password : row.password,
|
||||
allow_unauth: body.allowUnauth !== undefined ? (body.allowUnauth ? 1 : 0) : row.allow_unauth,
|
||||
app_name: body.appName !== undefined ? body.appName : row.app_name,
|
||||
app_url: body.appUrl !== undefined ? body.appUrl : row.app_url,
|
||||
};
|
||||
|
||||
db.prepare(`
|
||||
UPDATE smtp_config SET
|
||||
enabled=?, host=?, port=?, secure=?, email=?, username=?, password=?,
|
||||
allow_unauth=?, app_name=?, app_url=?
|
||||
WHERE id=1
|
||||
`).run(
|
||||
updates.enabled, updates.host, updates.port, updates.secure,
|
||||
updates.email, updates.username, updates.password, updates.allow_unauth,
|
||||
updates.app_name, updates.app_url,
|
||||
);
|
||||
|
||||
res.json({ ok: true });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// ── POST /admin/smtp/test ───────────────────────────────────────────────────
|
||||
|
||||
router.post('/test', async (req, res, next) => {
|
||||
try {
|
||||
const { to } = z.object({ to: z.string().email() }).parse(req.body);
|
||||
const cfg = getSmtpConfig();
|
||||
if (!cfg.host) throw new HttpError(400, 'Aucun serveur SMTP configuré.');
|
||||
|
||||
await sendMail({
|
||||
to,
|
||||
subject: `Test SMTP — ${cfg.appName}`,
|
||||
html: buildEmailHtml({
|
||||
title: 'Email de test',
|
||||
body: `<p>Si vous recevez cet email, votre configuration SMTP fonctionne correctement.</p>`,
|
||||
}),
|
||||
});
|
||||
res.json({ ok: true, msg: `Email de test envoyé à ${to}.` });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// ── GET /admin/smtp/env ─────────────────────────────────────────────────────
|
||||
|
||||
router.get('/env', (_req, res) => {
|
||||
res.json({
|
||||
host: process.env.SMTP_HOST || '',
|
||||
port: parseInt(process.env.SMTP_PORT || '587', 10),
|
||||
email: process.env.SMTP_EMAIL || '',
|
||||
username: process.env.SMTP_USERNAME || '',
|
||||
appName: process.env.APP_NAME || '',
|
||||
appUrl: process.env.APP_URL || '',
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
+10
-3
@@ -35,6 +35,8 @@ import { startAutoStatutJob } from './jobs/autoStatut.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 tauxCreditImpotRouter from './routes/tauxCreditImpot.js';
|
||||
import referentielRouter from './routes/referentiel.js';
|
||||
import referentielPublicRouter from './routes/referentielPublic.js';
|
||||
@@ -78,12 +80,15 @@ 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 FROM smtp_config WHERE id = 1').get();
|
||||
res.json({
|
||||
appName: cfg.appName || 'Crowdlending Tracker',
|
||||
iconUrl: icon ? `/api/icons-files/${icon.filename}` : null,
|
||||
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,
|
||||
});
|
||||
} catch {
|
||||
res.json({ appName: 'Crowdlending Tracker', iconUrl: null });
|
||||
res.json({ appName: 'Crowdlending Tracker', iconUrl: null, allowRegistration: true, minPasswordLength: 8 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,6 +116,8 @@ 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);
|
||||
// Invitations : routes admin protégées + routes publiques (register/validate)
|
||||
app.use('/api/admin/invitations', requireAuth, requireAdmin, invitationsRouter);
|
||||
app.use('/api/invitations', invitationsRouter);
|
||||
|
||||
Reference in New Issue
Block a user