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
|
FROM base AS deps
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
COPY apps/lab-interactif/package.json apps/lab-interactif/package-lock.json ./apps/lab-interactif/
|
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
|
FROM base AS build
|
||||||
ARG PUBLIC_SITE_URL=https://www.lelectronrare.fr
|
ARG PUBLIC_SITE_URL=https://www.lelectronrare.fr
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"motion": "^12.23.24",
|
"motion": "^12.23.24",
|
||||||
"nodemailer": "^6.9.16",
|
|
||||||
"postprocessing": "^6.39.0",
|
"postprocessing": "^6.39.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/check": "^0.9.6",
|
"@astrojs/check": "^0.9.6",
|
||||||
"@types/nodemailer": "^6.4.16",
|
|
||||||
"@storybook/addon-a11y": "^9.1.5",
|
"@storybook/addon-a11y": "^9.1.5",
|
||||||
"@storybook/addon-docs": "^9.1.5",
|
"@storybook/addon-docs": "^9.1.5",
|
||||||
"@storybook/addon-onboarding": "^9.1.5",
|
"@storybook/addon-onboarding": "^9.1.5",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import nodemailer from 'nodemailer';
|
|
||||||
|
|
||||||
interface ContactSubmission {
|
interface ContactSubmission {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -68,21 +67,10 @@ export const POST: APIRoute = async ({ request, clientAddress }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const SMTP_HOST = process.env.SMTP_HOST;
|
const MAIL_API_URL = process.env.MAIL_API_URL || 'http://mail-api:3200/api/mail/send';
|
||||||
const SMTP_PORT = Number(process.env.SMTP_PORT || '587');
|
const MAIL_FROM = process.env.MAIL_FROM || 'contact@lelectronrare.fr';
|
||||||
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_TO = process.env.MAIL_TO || '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;
|
let body: ContactSubmission;
|
||||||
try {
|
try {
|
||||||
body = await request.json();
|
body = await request.json();
|
||||||
@@ -124,13 +112,13 @@ export const POST: APIRoute = async ({ request, clientAddress }) => {
|
|||||||
|
|
||||||
const text = `Nouveau message depuis lelectronrare.fr
|
const text = `Nouveau message depuis lelectronrare.fr
|
||||||
|
|
||||||
Nom : ${name}
|
Nom : ${name}
|
||||||
Email : ${email}
|
Email : ${email}
|
||||||
Téléphone : ${phone || '-'}
|
Téléphone : ${phone || '-'}
|
||||||
Organisation: ${organization || '-'}
|
Organisation: ${organization || '-'}
|
||||||
Type : ${needType || '-'}
|
Type : ${needType || '-'}
|
||||||
Page : ${pagePath || '-'}
|
Page : ${pagePath || '-'}
|
||||||
IP : ${ip}
|
IP : ${ip}
|
||||||
|
|
||||||
Message :
|
Message :
|
||||||
${message || '(vide)'}
|
${message || '(vide)'}
|
||||||
@@ -148,34 +136,39 @@ ${message || '(vide)'}
|
|||||||
</table>
|
</table>
|
||||||
<h3 style="margin-top:20px;font-size:14px;color:#666;">Message</h3>
|
<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>
|
<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>`;
|
</body></html>`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const transporter = nodemailer.createTransport({
|
const resp = await fetch(MAIL_API_URL, {
|
||||||
host: SMTP_HOST,
|
method: 'POST',
|
||||||
port: SMTP_PORT,
|
headers: { 'Content-Type': 'application/json' },
|
||||||
secure: SMTP_PORT === 465,
|
body: JSON.stringify({
|
||||||
auth: { user: SMTP_USER, pass: SMTP_PASS },
|
from: MAIL_FROM,
|
||||||
|
to: MAIL_TO,
|
||||||
|
subject,
|
||||||
|
text,
|
||||||
|
html,
|
||||||
|
replyTo: `${name.replace(/[<>"]/g, '')} <${email}>`,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
await transporter.sendMail({
|
if (!resp.ok) {
|
||||||
from: MAIL_FROM,
|
console.error('[submit-lead] mail-api returned', resp.status);
|
||||||
to: MAIL_TO,
|
return new Response(JSON.stringify({ error: 'send failed' }), {
|
||||||
replyTo: `${name.replace(/[<>"]/g, '')} <${email}>`,
|
status: 502,
|
||||||
subject,
|
headers: corsHeaders(origin),
|
||||||
text,
|
});
|
||||||
html,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
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 }), {
|
return new Response(JSON.stringify({ ok: true }), {
|
||||||
status: 201,
|
status: 201,
|
||||||
headers: corsHeaders(origin),
|
headers: corsHeaders(origin),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[submit-lead] SMTP error', err instanceof Error ? err.message : 'unknown');
|
console.error('[submit-lead] mail-api unreachable', err instanceof Error ? err.message : 'unknown');
|
||||||
return new Response(JSON.stringify({ error: 'send failed' }), {
|
return new Response(JSON.stringify({ error: 'mail service unreachable' }), {
|
||||||
status: 502,
|
status: 502,
|
||||||
headers: corsHeaders(origin),
|
headers: corsHeaders(origin),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user