Corrige le logo cassé dans l'email d'invitation derrière un reverse proxy

Les URL absolues de l'email (logo, lien d'inscription) étaient construites
côté serveur à partir de req.protocol : derrière un reverse proxy HTTPS
(sans TRUST_PROXY), le serveur voit « http » et produit des URL en contenu
mixte que le navigateur bloque — logo cassé dans l'aperçu et dans les
webmails. Le frontend transmet désormais window.location.origin (?base=),
c'est-à-dire l'adresse publique exacte vue par l'admin ; le serveur la
valide (http/https uniquement) et ne garde l'hôte de la requête qu'en
secours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
This commit is contained in:
Claude committed 2026-07-14 07:18:14 +00:00
1 parent 02cfa8f8bc
commit d4923e9dfc
2 files changed
+20 -5

No files matched your search

+14 -4
View File
@@ -230,14 +230,24 @@ adminRouter.post('/rencontres/:id/image', validate(idParam, 'params'), (req, res
// Ready-to-send HTML invitation email for a rencontre (admin + moderator).
// The registration link opens the public site with the inscription modal
// pre-opened (/?inscription=<id>). URLs are absolute, built from the
// request's host so they match however the site is reached (direct port
// or reverse proxy).
// pre-opened (/?inscription=<id>). URLs must be absolute for email
// clients: the frontend passes its own window.location.origin (?base=),
// which is exactly the public address the admin is browsing — reliable
// even behind a TLS-terminating reverse proxy where req.protocol would
// say "http". The request's host is only a fallback.
adminRouter.get('/rencontres/:id/email', validate(idParam, 'params'), async (req, res, next) => {
try {
const result = await query(`${RENC_SQL} WHERE r.id = $1 GROUP BY r.id`, [req.params.id]);
if (result.rowCount === 0) return res.status(404).json({ error: 'Rencontre introuvable' });
const baseUrl = `${req.protocol}://${req.get('host')}`;
let baseUrl = `${req.protocol}://${req.get('host')}`;
if (typeof req.query.base === 'string') {
try {
const u = new URL(req.query.base);
if (u.protocol === 'http:' || u.protocol === 'https:') baseUrl = u.origin;
} catch {
// invalid ?base= — keep the fallback
}
}
res.json(buildInvitationEmail({ rencontre: result.rows[0], baseUrl }));
} catch (err) {
next(err);