Files
upstream-atomic-crm/supabase/functions/postmark/extractAndUploadAttachments.ts
upstream-mirror 93ecc1a6f8
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
snapshot of https://github.com/marmelab/atomic-crm.git (main, 2026-09-18; blobs > 131072B omitted)
2026-09-18 01:48:50 +05:00

110 lines
3.3 KiB
TypeScript

import { decode } from "base64-arraybuffer";
import { supabaseAdmin } from "../_shared/supabaseAdmin.ts";
export type Attachment = {
title: string;
type: string;
path: string;
src: string;
};
/**
* Extracts the attachments from the email and upload them to Supabase Storage.
*
* Example:
* "Attachments": [
* {
* "Name": "test.txt",
* "Content": "VGhpcyBpcyBhdHRhY2htZW50IGNvbnRlbnRzLCBiYXNlLTY0IGVuY29kZWQu",
* "ContentType": "text/plain",
* "ContentLength": 45
* }
* ]
*
* Return Value:
* [{
* title: "test.txt",
* type: "text/plain",
* "path": "0.8262106278726917.txt",
* "src": "http://127.0.0.1:54321/storage/v1/object/public/attachments/0.8262106278726917.txt",
* }]
*
*/
export const extractAndUploadAttachments = async (
Attachments: {
Name: string;
Content: string;
ContentType: string;
ContentLength: number;
}[],
): Promise<Attachment[]> => {
return (
await Promise.all(
(Attachments || []).map(async (attachment) => {
const { Name, Content, ContentType } = attachment;
if (!Name || !Content || !ContentType) {
console.warn("Attachment is missing required fields, skipping", {
attachment,
});
return null;
}
const decodedContent = decode(Content);
if (!decodedContent) {
console.error("Failed to decode attachment content, skipping", {
attachment,
});
return null;
}
const fileParts = Name.split(".");
const fileExt = fileParts.length > 1 ? `.${Name.split(".").pop()}` : "";
const fileName = `${Math.random()}${fileExt}`;
const { error: uploadError } = await supabaseAdmin.storage
.from("attachments")
.upload(fileName, decodedContent);
if (uploadError) {
console.error("uploadError", uploadError);
throw new Error("Failed to upload attachment");
}
const { data } = supabaseAdmin.storage
.from("attachments")
.getPublicUrl(fileName);
return {
title: Name,
type: ContentType,
path: fileName,
src: fixPublicUrl(data.publicUrl),
};
}),
)
).filter(Boolean) as Attachment[];
};
/*
* Workaround fix for public URL not working on local environment
* See https://github.com/orgs/supabase/discussions/37271
*
* Replaces http://kong:8000/storage/v1/object/public/attachments/0.08968261048718773.txt with http://127.0.0.1:54321/storage/v1/object/public/attachments/0.08968261048718773.txt, using the SB_JWT_ISSUER env var (value http://127.0.0.1:54321/auth/v1) to get the external/public URL of the Supabase instance.
*/
const fixPublicUrl = (url: string) => {
const jwtIssuer = Deno.env.get("SB_JWT_ISSUER") ?? "";
const localUrl = jwtIssuer.replace("/auth/v1", "");
return url.replace("http://kong:8000", localUrl);
};
export const removeUploadedAttachments = async (attachments: Attachment[]) => {
if (!attachments.length) {
return;
}
const { error } = await supabaseAdmin.storage
.from("attachments")
.remove(attachments.map((attachment) => attachment.path));
if (error) {
console.error("Could not remove the uploaded attachments", error);
}
};