docs: add comprehensive roadmap for email service improvements

This commit is contained in:
Unchained
2026-03-28 17:06:36 +02:00
parent 06ff847e7e
commit 28b864ecdb
10 changed files with 1172 additions and 59 deletions
+36 -19
View File
@@ -4,9 +4,12 @@ import {
OrderCancelledWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import { getOrderCancelledEmails } from "@/lib/email-templates";
import { Resend } from "resend";
// N8N webhook URL for Order Cancelled
const N8N_WEBHOOK_URL = "https://n8n.nodecrew.me/webhook/saleor-order-cancelled";
const resend = new Resend(process.env.RESEND_API_KEY);
const ADMIN_EMAILS = ["me@hytham.me", "tamara@hytham.me"];
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
name: "Order Cancelled in Saleor",
@@ -17,29 +20,43 @@ export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhoo
});
export default orderCancelledWebhook.createHandler(async (req, res, ctx) => {
const { payload, event, authData } = ctx;
const { payload } = ctx;
const order = payload.order;
if (!order) {
console.error("No order data in webhook payload");
return res.status(200).end();
}
console.log(`Order cancelled: ${payload.order?.number} for ${payload.order?.userEmail}`);
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
console.log(`Order cancelled: ${order.number} for ${order.userEmail} (lang: ${order.languageCode})`);
try {
// Forward to N8N
const response = await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": "order.cancelled",
},
body: JSON.stringify(payload),
});
// Get email templates
const { customerSubject, customerHtml, adminSubject, adminHtml } = getOrderCancelledEmails(order);
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
} else {
console.log(`Successfully forwarded to N8N: ${response.status}`);
// Send customer email
if (order.userEmail) {
const customerResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: order.userEmail,
subject: customerSubject,
html: customerHtml,
});
console.log(`Customer cancelled email sent: ${customerResult.data?.id || 'failed'}`);
}
// Send admin emails
for (const adminEmail of ADMIN_EMAILS) {
const adminResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: adminEmail,
subject: adminSubject,
html: adminHtml,
});
console.log(`Admin cancelled email sent to ${adminEmail}: ${adminResult.data?.id || 'failed'}`);
}
} catch (error) {
console.error(`Failed to forward to N8N:`, error);
console.error("Failed to send cancelled emails:", error);
}
return res.status(200).end();
+36 -19
View File
@@ -4,9 +4,12 @@ import {
OrderCreatedWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import { getOrderCreatedEmails } from "@/lib/email-templates";
import { Resend } from "resend";
// N8N webhook URL for Order Created
const N8N_WEBHOOK_URL = "https://n8n.nodecrew.me/webhook/saleor-order-created";
const resend = new Resend(process.env.RESEND_API_KEY);
const ADMIN_EMAILS = ["me@hytham.me", "tamara@hytham.me"];
export const orderCreatedWebhook = new SaleorAsyncWebhook<OrderCreatedWebhookPayloadFragment>({
name: "Order Created in Saleor",
@@ -17,29 +20,43 @@ export const orderCreatedWebhook = new SaleorAsyncWebhook<OrderCreatedWebhookPay
});
export default orderCreatedWebhook.createHandler(async (req, res, ctx) => {
const { payload, event, authData } = ctx;
const { payload } = ctx;
const order = payload.order;
if (!order) {
console.error("No order data in webhook payload");
return res.status(200).end();
}
console.log(`Order created: ${payload.order?.number} for ${payload.order?.userEmail}`);
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
console.log(`Order created: ${order.number} for ${order.userEmail} (lang: ${order.languageCode})`);
try {
// Forward to N8N
const response = await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": "order.created",
},
body: JSON.stringify(payload),
});
// Get email templates
const { customerSubject, customerHtml, adminSubject, adminHtml } = getOrderCreatedEmails(order);
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
} else {
console.log(`Successfully forwarded to N8N: ${response.status}`);
// Send customer email
if (order.userEmail) {
const customerResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: order.userEmail,
subject: customerSubject,
html: customerHtml,
});
console.log(`Customer email sent: ${customerResult.data?.id || 'failed'}`);
}
// Send admin emails
for (const adminEmail of ADMIN_EMAILS) {
const adminResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: adminEmail,
subject: adminSubject,
html: adminHtml,
});
console.log(`Admin email sent to ${adminEmail}: ${adminResult.data?.id || 'failed'}`);
}
} catch (error) {
console.error(`Failed to forward to N8N:`, error);
console.error("Failed to send emails:", error);
}
return res.status(200).end();
+40 -19
View File
@@ -4,9 +4,12 @@ import {
OrderFulfilledWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import { getOrderShippedEmails } from "@/lib/email-templates";
import { Resend } from "resend";
// N8N webhook URL for Order Fulfilled/Shipped
const N8N_WEBHOOK_URL = "https://n8n.nodecrew.me/webhook/saleor-order-shipped";
const resend = new Resend(process.env.RESEND_API_KEY);
const ADMIN_EMAILS = ["me@hytham.me", "tamara@hytham.me"];
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
name: "Order Fulfilled in Saleor",
@@ -17,29 +20,47 @@ export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhoo
});
export default orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
const { payload, event, authData } = ctx;
const { payload } = ctx;
const order = payload.order;
if (!order) {
console.error("No order data in webhook payload");
return res.status(200).end();
}
console.log(`Order fulfilled: ${payload.order?.number} for ${payload.order?.userEmail}`);
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
// Get the first fulfillment with tracking info
const fulfillment = (order as any).fulfillments?.[0];
const trackingNumber = fulfillment?.trackingNumber || "";
console.log(`Order fulfilled: ${order.number} for ${order.userEmail} (lang: ${order.languageCode}, tracking: ${trackingNumber})`);
try {
// Forward to N8N
const response = await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": "order.fulfilled",
},
body: JSON.stringify(payload),
});
// Get email templates
const { customerSubject, customerHtml, adminSubject, adminHtml } = getOrderShippedEmails(order, fulfillment);
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
} else {
console.log(`Successfully forwarded to N8N: ${response.status}`);
// Send customer email
if (order.userEmail) {
const customerResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: order.userEmail,
subject: customerSubject,
html: customerHtml,
});
console.log(`Customer shipped email sent: ${customerResult.data?.id || 'failed'}`);
}
// Send admin emails
for (const adminEmail of ADMIN_EMAILS) {
const adminResult = await resend.emails.send({
from: "Manoon Oils <support@mail.manoonoils.com>",
to: adminEmail,
subject: adminSubject,
html: adminHtml,
});
console.log(`Admin shipped email sent to ${adminEmail}: ${adminResult.data?.id || 'failed'}`);
}
} catch (error) {
console.error(`Failed to forward to N8N:`, error);
console.error("Failed to send shipped emails:", error);
}
return res.status(200).end();