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,158 @@
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { AuthMiddleware } from "../_shared/authentication.ts";
import { supabaseAdmin } from "../_shared/supabaseAdmin.ts";
const ATTACHMENTS_BUCKET =
Deno.env.get("VITE_ATTACHMENTS_BUCKET") || "attachments";
type NoteAttachment = {
path?: string | null;
src?: string | null;
};
type NoteRecord = {
id?: number | string | null;
attachments?: NoteAttachment[] | null;
};
type WebhookPayload = {
type?: string | null;
old_record?: NoteRecord | null;
record?: NoteRecord | null;
};
const deleteNoteAttachments = async (req: Request) => {
if (req.method !== "POST") {
return jsonResponse({ error: "Method Not Allowed" }, 405);
}
const payload = (await req.json()) as WebhookPayload;
const paths = getPathsToDelete(payload);
if (paths.length === 0) {
return jsonResponse({
status: "skipped",
reason: "no_paths_to_delete",
});
}
const { error } = await supabaseAdmin.storage
.from(ATTACHMENTS_BUCKET)
.remove(paths);
if (error) {
console.error("Failed to delete note attachments", {
type: payload.type ?? null,
paths,
error,
});
return jsonResponse({ error: "Failed to delete note attachments" }, 500);
}
return jsonResponse({
status: "ok",
});
};
Deno.serve(async (req: Request) =>
AuthMiddleware(req, async (req: Request) => deleteNoteAttachments(req)),
);
const getPathsToDelete = (payload: WebhookPayload): string[] => {
const oldPaths = extractAttachmentPaths(payload.old_record?.attachments);
const newPaths = extractAttachmentPaths(payload.record?.attachments);
if (payload.type === "UPDATE") {
const newPathsSet = new Set(newPaths);
return oldPaths.filter((path) => !newPathsSet.has(path));
}
if (payload.type === "DELETE") {
return oldPaths;
}
return [];
};
const extractAttachmentPaths = (
attachments?: NoteAttachment[] | null,
): string[] => {
const paths = attachments
?.map((attachment) => extractAttachmentPath(attachment))
.filter((path): path is string => path != null && path.length > 0);
return paths ? Array.from(new Set(paths)) : [];
};
const extractAttachmentPath = (attachment?: NoteAttachment | null) => {
if (!attachment) {
return null;
}
if (attachment.path) {
return normalizeStoragePath(attachment.path);
}
if (!attachment.src) {
return null;
}
const pathname = getPathname(attachment.src);
if (!pathname) {
return null;
}
const bucketSegment = `/${ATTACHMENTS_BUCKET}/`;
const bucketIndex = pathname.lastIndexOf(bucketSegment);
if (bucketIndex < 0) {
return null;
}
const path = pathname.slice(bucketIndex + bucketSegment.length);
return normalizeStoragePath(path);
};
const getPathname = (value: string) => {
try {
return new URL(value, "http://localhost").pathname;
} catch {
return null;
}
};
const safelyDecodePath = (path: string) => {
try {
return decodeURIComponent(path);
} catch {
return path;
}
};
const normalizeStoragePath = (path: string) => {
const trimmedPath = path.trim();
if (trimmedPath.length === 0) {
return null;
}
const parsedPath = getPathname(trimmedPath);
const candidatePath = parsedPath ?? trimmedPath;
const bucketSegment = `/${ATTACHMENTS_BUCKET}/`;
const bucketIndex = candidatePath.lastIndexOf(bucketSegment);
const withoutBucket =
bucketIndex >= 0
? candidatePath.slice(bucketIndex + bucketSegment.length)
: candidatePath.replace(/^\/+/, "").replace(/^attachments\//, "");
if (withoutBucket.length === 0) {
return null;
}
return safelyDecodePath(withoutBucket);
};
const jsonResponse = (data: unknown, status = 200) =>
new Response(JSON.stringify(data), {
status,
headers: { "Content-Type": "application/json" },
});