Replace Resend with n8n webhook relay for emails

- email.ts: POST to N8N_EMAIL_WEBHOOK_URL instead of Resend API
- env.ts: replace RESEND_API_KEY with N8N_EMAIL_WEBHOOK_URL
- .env.example: document new env var
- Add n8n workflow JSON (Webhook -> Gmail -> Respond) ready to import
This commit is contained in:
2026-04-30 12:02:04 +01:00
parent adcedb7cfd
commit 5912955b9c
4 changed files with 89 additions and 24 deletions

View File

@@ -12,9 +12,9 @@ NODE_ENV=development
SUPABASE_URL=https://tu-supabase.tudominio.com
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Resend — servicio de email
# Crear cuenta en https://resend.com y configurar dominio
RESEND_API_KEY=re_...
# n8n — relé de email vía webhook (n8n se encarga de enviar por Gmail/SMTP)
# La URL es la del webhook configurado en el workflow de n8n.
N8N_EMAIL_WEBHOOK_URL=https://n8n.tudominio.com/webhook/naturcalabacera-email
EMAIL_FROM=Naturcalabacera <reservas@tudominio.com>
# Destinatarios de notificaciones (parametrizables, sin hardcodear)

View File

@@ -22,8 +22,8 @@ export const env = {
SUPABASE_URL: required('SUPABASE_URL'),
SUPABASE_SERVICE_ROLE_KEY: required('SUPABASE_SERVICE_ROLE_KEY'),
// Email — Resend
RESEND_API_KEY: required('RESEND_API_KEY'),
// Email — n8n webhook (relé a Gmail)
N8N_EMAIL_WEBHOOK_URL: required('N8N_EMAIL_WEBHOOK_URL'),
EMAIL_FROM: optional('EMAIL_FROM', 'Naturcalabacera <reservas@naturcalabacera.com>'),
// Destinatarios de notificaciones (configurables, sin hardcodear)

View File

@@ -7,40 +7,34 @@ interface SendEmailOptions {
replyTo?: string;
}
interface ResendResponse {
id?: string;
error?: { message: string; name: string };
}
/**
* Envía un email usando la API de Resend.
* Docs: https://resend.com/docs/api-reference/emails/send-email
* Envía un email a través del webhook de n8n.
* El workflow de n8n recibe { to, subject, html } y lo envía vía Gmail.
*/
export async function sendEmail(options: SendEmailOptions): Promise<{ success: boolean; id?: string; error?: string }> {
try {
const res = await fetch('https://api.resend.com/emails', {
const to = Array.isArray(options.to) ? options.to.join(', ') : options.to;
const res = await fetch(env.N8N_EMAIL_WEBHOOK_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.RESEND_API_KEY}`,
'Content-Type': 'application/json',
},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
from: env.EMAIL_FROM,
to: Array.isArray(options.to) ? options.to : [options.to],
to,
subject: options.subject,
html: options.html,
...(options.replyTo && { reply_to: options.replyTo }),
from: env.EMAIL_FROM,
replyTo: options.replyTo,
}),
});
const data = await res.json() as ResendResponse;
if (!res.ok || data.error) {
const errorMsg = data.error?.message ?? `HTTP ${res.status}`;
if (!res.ok) {
const text = await res.text().catch(() => '');
const errorMsg = `HTTP ${res.status}${text ? `${text.slice(0, 200)}` : ''}`;
console.error('[email] Error al enviar:', errorMsg);
return { success: false, error: errorMsg };
}
const data = await res.json().catch(() => ({})) as { id?: string };
return { success: true, id: data.id };
} catch (err) {
const errorMsg = err instanceof Error ? err.message : 'Error desconocido';