snapshot of https://github.com/marmelab/atomic-crm.git (main, 2026-09-18; blobs > 131072B omitted)
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s

This commit is contained in:
upstream-mirror
2026-09-18 01:48:50 +05:00
commit 93ecc1a6f8
787 changed files with 75240 additions and 0 deletions

View File

@@ -0,0 +1,393 @@
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { addNoteToContact, findActiveSaleByEmail } from "./addNoteToContact.ts";
import {
getForwardedMailContent,
stripSubjectForwardingPrefix,
} from "./forwardedParser.ts";
import { extractMailContactData } from "./extractMailContactData.ts";
import { getExpectedAuthorization } from "./getExpectedAuthorization.ts";
import { getNoteContent } from "./getNoteContent.ts";
import {
extractAndUploadAttachments,
removeUploadedAttachments,
} from "./extractAndUploadAttachments.ts";
import { supabaseAdmin } from "../_shared/supabaseAdmin.ts";
const webhookUser = Deno.env.get("POSTMARK_WEBHOOK_USER");
const webhookPassword = Deno.env.get("POSTMARK_WEBHOOK_PASSWORD");
const INBOUND_EMAIL = (Deno.env.get("VITE_INBOUND_EMAIL") || "").toLowerCase();
if (!webhookUser || !webhookPassword) {
throw new Error(
"Missing POSTMARK_WEBHOOK_USER or POSTMARK_WEBHOOK_PASSWORD env variable",
);
}
const rawAuthorizedIPs = Deno.env.get("POSTMARK_WEBHOOK_AUTHORIZED_IPS");
if (!rawAuthorizedIPs) {
throw new Error("Missing POSTMARK_WEBHOOK_AUTHORIZED_IPS env variable");
}
Deno.serve(async (req) => {
let response: Response | undefined;
response = checkRequestTypeAndHeaders(req);
if (response) return response;
const json = await req.json();
response = checkBody(json);
if (response) return response;
const { FromFull, Attachments } = json;
let { ToFull, TextBody, Subject } = json;
const salesEmail = (FromFull.Email || "").toLowerCase();
if (!salesEmail) {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(
`Could not extract sales email from FromFull: ${FromFull}`,
{ status: 403 },
);
}
const allSales = await supabaseAdmin
.from("sales")
.select("email, secondary_emails");
if (allSales.error) {
console.error("Could not fetch sales emails:", allSales.error);
return new Response("Could not fetch sales from database", { status: 500 });
}
const salesEmails =
allSales.data
?.flatMap((s: { email: string; secondary_emails: string[] }) => [
s.email,
...s.secondary_emails,
])
.map((email: string) => email.toLowerCase()) ?? [];
const firstToEmail = (ToFull[0]?.Email || "").toLowerCase();
// If we have an INBOUND_EMAIL and the email is sent only to the inbound email address, and the sender is a known sales email,
// then we can try to extract the real recipient email from the body of the email
if (
INBOUND_EMAIL &&
ToFull.length === 1 &&
firstToEmail === INBOUND_EMAIL &&
salesEmails.includes(salesEmail)
) {
const emailRegex = /[\w.+%-]+@[\w.-]+\.[a-zA-Z]{2,}/g;
const emailsInBody = TextBody.match(emailRegex) || [];
const candidateEmails = emailsInBody
.map((email: string) => email.toLowerCase())
.filter(
(email: string) =>
email !== INBOUND_EMAIL && !salesEmails?.includes(email),
);
if (candidateEmails.length > 0) {
ToFull = [
{
Email: candidateEmails[0],
Name: "",
},
];
} else {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(
`Could not extract recipient email from transferred email body.`,
{ status: 403 },
);
}
TextBody = getForwardedMailContent(TextBody);
Subject = stripSubjectForwardingPrefix(Subject);
}
const noteContent = getNoteContent(Subject, TextBody);
const contacts = extractMailContactData(ToFull);
const { data: sales, error: fetchSalesError } =
await findActiveSaleByEmail(salesEmail);
if (fetchSalesError) {
console.error("Could not fetch the sender's sale:", fetchSalesError);
return new Response(
`Could not fetch sales from database, email: ${salesEmail}`,
{ status: 500 },
);
}
if (!sales) {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(
`Unable to find (active) sales in database, email: ${salesEmail}`,
{ status: 403 },
);
}
const attachments = await extractAndUploadAttachments(Attachments);
const failedContacts: string[] = [];
for (const {
firstName,
lastName,
email,
domain,
companyName,
website,
} of contacts) {
if (!email) {
// Return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
return new Response(`Could not extract email from ToFull: ${ToFull}`, {
status: 403,
});
}
const errorResponse = await addNoteToContact({
sales,
salesEmail,
email,
domain,
firstName,
lastName,
noteContent,
attachments,
companyName,
website,
});
if (errorResponse) {
console.error(
`Could not add the note for ${email}: ${await errorResponse.text()}`,
);
failedContacts.push(email);
}
}
if (failedContacts.length) {
// A partial failure answers 403 so Postmark does not redeliver: the notes
// already written are not idempotent and would be duplicated. When every
// contact failed there is no note to duplicate, so a retry is safe.
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
const status = failedContacts.length === contacts.length ? 500 : 403;
if (status === 500) {
await removeUploadedAttachments(attachments);
}
return new Response(
`Could not add the note for: ${failedContacts.join(", ")}`,
{ status },
);
}
return new Response("OK");
});
const checkRequestTypeAndHeaders = (req: Request) => {
// Only allow known IP addresses
// We can use the x-forwarded-for header as it is populated by Supabase
// https://supabase.com/docs/guides/api/securing-your-api#accessing-request-information
const forwardedFor = req.headers.get("x-forwarded-for");
if (!forwardedFor) {
return new Response("Unauthorized", { status: 401 });
}
const ips = forwardedFor.split(",").map((ip) => ip.trim());
const authorizedIPs = rawAuthorizedIPs
.split(",")
.map((ip: string) => ip.trim());
if (!ips.some((ip) => authorizedIPs.includes(ip))) {
return new Response("Unauthorized", { status: 401 });
}
// Only allow POST requests
if (req.method !== "POST") {
return new Response(null, { status: 405 });
}
// Check the Authorization header
const expectedAuthorization = getExpectedAuthorization(
webhookUser,
webhookPassword,
);
const authorization = req.headers.get("Authorization");
if (authorization !== expectedAuthorization) {
return new Response("Unauthorized", { status: 401 });
}
};
// deno-lint-ignore no-explicit-any
const checkBody = (json: any) => {
const { ToFull, FromFull, Subject, TextBody } = json;
// In case of incorrect request data, we
// return a 403 to let Postmark know that it's no use to retry this request
// https://postmarkapp.com/developer/webhooks/inbound-webhook#errors-and-retries
if (!ToFull || !ToFull.length)
return new Response("Missing parameter: ToFull", { status: 403 });
if (!FromFull)
return new Response("Missing parameter: FromFull", { status: 403 });
if (!Subject)
return new Response("Missing parameter: Subject", { status: 403 });
if (!TextBody)
return new Response("Missing parameter: TextBody", {
status: 403,
});
};
/* To invoke locally:
1. Run `make start`
2. Make sure to have a Sales with email "support@postmarkapp.com" (create it if needed)
3. OPTIONAL: Create a Contact with email "firstname.lastname@marmelab.com"
4. In another terminal, run `make start-supabase-functions`
5. In another terminal, make an HTTP request:
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/postmark' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic dGVzdHVzZXI6dGVzdHB3ZA==' \
--data '{
"FromName": "Postmarkapp Support",
"From": "support@postmarkapp.com",
"FromFull": {
"Email": "support@postmarkapp.com",
"Name": "Postmarkapp Support",
"MailboxHash": ""
},
"To": "\"Firstname Lastname\" <firstname.lastname@marmelab.com>",
"ToFull": [
{
"Email": "firstname.lastname@marmelab.com",
"Name": "Firstname Lastname",
"MailboxHash": "SampleHash"
}
],
"Cc": "\"First Cc\" <firstcc@postmarkapp.com>, secondCc@postmarkapp.com",
"CcFull": [
{
"Email": "firstcc@postmarkapp.com",
"Name": "First Cc",
"MailboxHash": ""
},
{
"Email": "secondCc@postmarkapp.com",
"Name": "",
"MailboxHash": ""
}
],
"Bcc": "\"First Bcc\" <firstbcc@postmarkapp.com>, secondbcc@postmarkapp.com",
"BccFull": [
{
"Email": "firstbcc@postmarkapp.com",
"Name": "First Bcc",
"MailboxHash": ""
},
{
"Email": "secondbcc@postmarkapp.com",
"Name": "",
"MailboxHash": ""
}
],
"OriginalRecipient": "firstname.lastname@marmelab.com",
"Subject": "Test subject",
"MessageID": "73e6d360-66eb-11e1-8e72-a8904824019b",
"ReplyTo": "replyto@postmarkapp.com",
"MailboxHash": "SampleHash",
"Date": "Fri, 1 Aug 2014 16:45:32 -04:00",
"TextBody": "This is a test text body.",
"HtmlBody": "<html><body><p>This is a test html body.</p></body></html>",
"StrippedTextReply": "This is the reply text",
"Tag": "TestTag",
"Headers": [
{
"Name": "X-Header-Test",
"Value": ""
},
{
"Name": "X-Spam-Status",
"Value": "No"
},
{
"Name": "X-Spam-Score",
"Value": "-0.1"
},
{
"Name": "X-Spam-Tests",
"Value": "DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS"
}
],
"Attachments": [
{
"Name": "test.txt",
"Content": "VGhpcyBpcyBhdHRhY2htZW50IGNvbnRlbnRzLCBiYXNlLTY0IGVuY29kZWQu",
"ContentType": "text/plain",
"ContentLength": 45
}
]
}'
To trigger the email forwarding feature, you can change the "To" and "ToFull" fields to have the INBOUND_EMAIL, and add an email address that is neither a sales nor the INBOUND_EMAIL, for example:
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/postmark' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic dGVzdHVzZXI6dGVzdHB3ZA==' \
--data '{
"FromName": "Postmarkapp Support",
"MessageStream": "inbound",
"From": "support@postmarkapp.com",
"FromFull": {
"Email": "support@postmarkapp.com",
"Name": "Postmarkapp Support",
"MailboxHash": ""
},
"To": "2aff30e603e54dc3eb556bd9e03ee099@inbound.postmarkapp.com",
"ToFull": [
{
"Email": "2aff30e603e54dc3eb556bd9e03ee099@inbound.postmarkapp.com",
"Name": "",
"MailboxHash": ""
}
],
"Cc": "",
"CcFull": [],
"Bcc": "",
"BccFull": [],
"OriginalRecipient": "2aff30e603e54dc3eb556bd9e03ee099@inbound.postmarkapp.com",
"Subject": "Fwd: Test for forwarding mail",
"MessageID": "32dcbecb-57d0-476e-9591-c747808cb599",
"ReplyTo": "",
"MailboxHash": "",
"Date": "Thu, 5 Mar 2026 10:41:26 +0100",
"TextBody": "---------- Forwarded message ---------\nFrom : Original Recipient <original.recipient@company.com>\nDate: Fri, 1 Aug 2014 16:45:32 -04:00\nSubject: Test for forwarding mail\nTo: Postmarkapp Support <support@postmarkapp.com>\n\n\nThe transferred message body\n",
"HtmlBody": "<div dir=\"ltr\"><br><br><div class=\"gmail_quote gmail_quote_container\"><div dir=\"ltr\" class=\"gmail_attr\">---------- Forwarded message ---------<br>From: <strong class=\"gmail_sendername\" dir=\"auto\">Original Recipient</strong> <span dir=\"auto\">&lt;<a href=\"mailto:original.recipient@company.com\">original.recipient@company.com</a>&gt;</span><br>Date: Fri, 1 Aug 2014 16:45:32 -04:00<br>Subject: Test for forwarding mail<br>To: Postmarkapp Support &lt;<a href=\"mailto:support@postmarkapp.com\">support@postmarkapp.com</a>&gt;<br></div><br><br><div dir=\"ltr\">The transferred message body</div>\n</div></div>\n",
"StrippedTextReply": "",
"Tag": "",
"Headers": [
{
"Name": "X-Header-Test",
"Value": ""
},
{
"Name": "X-Spam-Status",
"Value": "No"
},
{
"Name": "X-Spam-Score",
"Value": "-0.1"
},
{
"Name": "X-Spam-Tests",
"Value": "DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS"
}
],
"Attachments": []
}'
*/