refactor: contact form delegates to mail-api
Replace nodemailer SMTP with HTTP fetch to mail-api container (http://mail-api:3200/api/mail/send) which already holds the Brevo SMTP creds. Single source of truth for mail credentials, no duplication across electron-rare-site and mail-api. Drop nodemailer + @types/nodemailer from deps. Restore npm ci in Dockerfile since the lock no longer drifts. Override defaults via env: MAIL_API_URL, MAIL_FROM, MAIL_TO. mail-api intra-Docker network reachable because both services are on the traefik network.
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ WORKDIR /app
|
||||
FROM base AS deps
|
||||
COPY package.json package-lock.json ./
|
||||
COPY apps/lab-interactif/package.json apps/lab-interactif/package-lock.json ./apps/lab-interactif/
|
||||
RUN npm install --no-audit --no-fund && npm install --no-audit --no-fund --prefix apps/lab-interactif
|
||||
RUN npm ci && npm ci --prefix apps/lab-interactif
|
||||
|
||||
FROM base AS build
|
||||
ARG PUBLIC_SITE_URL=https://www.lelectronrare.fr
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"motion": "^12.23.24",
|
||||
"nodemailer": "^6.9.16",
|
||||
"postprocessing": "^6.39.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
@@ -42,7 +41,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/check": "^0.9.6",
|
||||
"@types/nodemailer": "^6.4.16",
|
||||
"@storybook/addon-a11y": "^9.1.5",
|
||||
"@storybook/addon-docs": "^9.1.5",
|
||||
"@storybook/addon-onboarding": "^9.1.5",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import nodemailer from 'nodemailer';
|
||||
|
||||
interface ContactSubmission {
|
||||
name: string;
|
||||
@@ -68,21 +67,10 @@ export const POST: APIRoute = async ({ request, clientAddress }) => {
|
||||
});
|
||||
}
|
||||
|
||||
const SMTP_HOST = process.env.SMTP_HOST;
|
||||
const SMTP_PORT = Number(process.env.SMTP_PORT || '587');
|
||||
const SMTP_USER = process.env.SMTP_USER;
|
||||
const SMTP_PASS = process.env.SMTP_PASS;
|
||||
const MAIL_FROM = process.env.MAIL_FROM || SMTP_USER || '';
|
||||
const MAIL_API_URL = process.env.MAIL_API_URL || 'http://mail-api:3200/api/mail/send';
|
||||
const MAIL_FROM = process.env.MAIL_FROM || 'contact@lelectronrare.fr';
|
||||
const MAIL_TO = process.env.MAIL_TO || 'contact@lelectronrare.fr';
|
||||
|
||||
if (!SMTP_HOST || !SMTP_USER || !SMTP_PASS) {
|
||||
console.error('[submit-lead] Missing SMTP env vars (SMTP_HOST/SMTP_USER/SMTP_PASS)');
|
||||
return new Response(JSON.stringify({ error: 'server misconfigured' }), {
|
||||
status: 500,
|
||||
headers: corsHeaders(origin),
|
||||
});
|
||||
}
|
||||
|
||||
let body: ContactSubmission;
|
||||
try {
|
||||
body = await request.json();
|
||||
@@ -124,13 +112,13 @@ export const POST: APIRoute = async ({ request, clientAddress }) => {
|
||||
|
||||
const text = `Nouveau message depuis lelectronrare.fr
|
||||
|
||||
Nom : ${name}
|
||||
Email : ${email}
|
||||
Téléphone : ${phone || '-'}
|
||||
Nom : ${name}
|
||||
Email : ${email}
|
||||
Téléphone : ${phone || '-'}
|
||||
Organisation: ${organization || '-'}
|
||||
Type : ${needType || '-'}
|
||||
Page : ${pagePath || '-'}
|
||||
IP : ${ip}
|
||||
Type : ${needType || '-'}
|
||||
Page : ${pagePath || '-'}
|
||||
IP : ${ip}
|
||||
|
||||
Message :
|
||||
${message || '(vide)'}
|
||||
@@ -148,34 +136,39 @@ ${message || '(vide)'}
|
||||
</table>
|
||||
<h3 style="margin-top:20px;font-size:14px;color:#666;">Message</h3>
|
||||
<pre style="background:#f5f5f5;padding:12px;border-radius:6px;white-space:pre-wrap;font-family:inherit;font-size:13px;line-height:1.5;">${escapeHtml(message || '(vide)')}</pre>
|
||||
<p style="margin-top:20px;font-size:11px;color:#aaa;">Pour répondre, utilise simplement Reply — l'adresse du visiteur est en Reply-To.</p>
|
||||
<p style="margin-top:20px;font-size:11px;color:#aaa;">Reply-To pointe vers l'email du visiteur — répondre directement.</p>
|
||||
</body></html>`;
|
||||
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: SMTP_HOST,
|
||||
port: SMTP_PORT,
|
||||
secure: SMTP_PORT === 465,
|
||||
auth: { user: SMTP_USER, pass: SMTP_PASS },
|
||||
const resp = await fetch(MAIL_API_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
from: MAIL_FROM,
|
||||
to: MAIL_TO,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
replyTo: `${name.replace(/[<>"]/g, '')} <${email}>`,
|
||||
}),
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: MAIL_FROM,
|
||||
to: MAIL_TO,
|
||||
replyTo: `${name.replace(/[<>"]/g, '')} <${email}>`,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
});
|
||||
if (!resp.ok) {
|
||||
console.error('[submit-lead] mail-api returned', resp.status);
|
||||
return new Response(JSON.stringify({ error: 'send failed' }), {
|
||||
status: 502,
|
||||
headers: corsHeaders(origin),
|
||||
});
|
||||
}
|
||||
|
||||
console.info('[submit-lead] Mail sent', { needType, hasOrg: !!organization });
|
||||
console.info('[submit-lead] Mail relayed via mail-api', { needType, hasOrg: !!organization });
|
||||
return new Response(JSON.stringify({ ok: true }), {
|
||||
status: 201,
|
||||
headers: corsHeaders(origin),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[submit-lead] SMTP error', err instanceof Error ? err.message : 'unknown');
|
||||
return new Response(JSON.stringify({ error: 'send failed' }), {
|
||||
console.error('[submit-lead] mail-api unreachable', err instanceof Error ? err.message : 'unknown');
|
||||
return new Response(JSON.stringify({ error: 'mail service unreachable' }), {
|
||||
status: 502,
|
||||
headers: corsHeaders(origin),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user