Add ACMCC app source, Supabase backend, and project config

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 20:19:26 -04:00
parent 313b51b412
commit 183fe0a93c
1422 changed files with 259271 additions and 0 deletions
@@ -0,0 +1,134 @@
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.1";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers":
"authorization, x-client-info, apikey, content-type",
};
function jsonResponse(body: unknown, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
function pick<T = unknown>(obj: Record<string, any>, keys: string[]): T | null {
for (const k of keys) {
if (obj[k] !== undefined && obj[k] !== null && obj[k] !== "") return obj[k] as T;
}
return null;
}
function buildTitle(row: Record<string, any>): string {
const explicit = pick<string>(row, ["title", "subject", "form_name", "form_title", "type"]);
if (explicit) return String(explicit);
const name = pick<string>(row, ["submitter_name", "name", "full_name", "from_name"]);
return name ? `Submission from ${name}` : "New submission";
}
function buildSummary(row: Record<string, any>): string | null {
const summary = pick<string>(row, ["summary", "message", "description", "body", "notes"]);
if (summary) return String(summary).slice(0, 2000);
// Fallback: stringify the payload field if present
const payload = row.data ?? row.payload ?? row.fields ?? row.submission_data;
if (payload && typeof payload === "object") {
try {
return JSON.stringify(payload).slice(0, 2000);
} catch {
return null;
}
}
return null;
}
Deno.serve(async (req) => {
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
try {
const targetUrl = Deno.env.get("SUPABASE_URL")!;
const targetKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
const sourceUrl = Deno.env.get("SOURCE_SUPABASE_URL");
const sourceKey = Deno.env.get("SOURCE_SUPABASE_SERVICE_ROLE_KEY");
if (!sourceUrl || !sourceKey) {
return jsonResponse({ success: false, error: "SOURCE_SUPABASE_URL / SOURCE_SUPABASE_SERVICE_ROLE_KEY not configured" }, 400);
}
const body = await req.json().catch(() => ({}));
const action = String(body.action || "sync");
const sourceTable = String(body.source_table || "form_submissions");
const limit = Math.min(Number(body.limit || 500), 1000);
const source = createClient(sourceUrl, sourceKey);
const target = createClient(targetUrl, targetKey);
// Probe: just return one row + columns so we can see the schema.
if (action === "probe") {
const { data, error } = await source.from(sourceTable).select("*").limit(3);
if (error) return jsonResponse({ success: false, error: error.message }, 500);
return jsonResponse({
success: true,
sample: data,
columns: data && data.length > 0 ? Object.keys(data[0]) : [],
});
}
// Determine high-water mark from existing inbox rows for this source
const { data: lastRow } = await target
.from("form_inbox")
.select("created_at")
.eq("source_type", "external_form")
.order("created_at", { ascending: false })
.limit(1);
const since = body.since || lastRow?.[0]?.created_at || null;
let query = source.from(sourceTable).select("*").order("created_at", { ascending: true }).limit(limit);
if (since) query = query.gt("created_at", since);
const { data: rows, error } = await query;
if (error) return jsonResponse({ success: false, error: error.message }, 500);
if (!rows || rows.length === 0) {
return jsonResponse({ success: true, inserted: 0, skipped: 0, since });
}
// Dedupe against existing source_ids
const sourceIds = rows.map((r: any) => r.id).filter(Boolean);
const { data: existing } = await target
.from("form_inbox")
.select("source_id")
.eq("source_type", "external_form")
.in("source_id", sourceIds);
const existingSet = new Set((existing || []).map((r: any) => r.source_id));
const toInsert = rows
.filter((r: any) => r.id && !existingSet.has(r.id))
.map((r: any) => ({
source_type: "external_form",
source_id: r.id,
title: buildTitle(r),
submitter_name: pick<string>(r, ["submitter_name", "name", "full_name", "from_name"]),
submitter_email: pick<string>(r, ["submitter_email", "email", "from_email", "contact_email"]),
summary: buildSummary(r),
status: "new",
created_at: r.created_at || new Date().toISOString(),
}));
if (toInsert.length === 0) {
return jsonResponse({ success: true, inserted: 0, skipped: rows.length, since });
}
const { error: insErr } = await target.from("form_inbox").insert(toInsert);
if (insErr) return jsonResponse({ success: false, error: insErr.message }, 500);
return jsonResponse({
success: true,
inserted: toInsert.length,
skipped: rows.length - toInsert.length,
since,
latest: rows[rows.length - 1]?.created_at,
});
} catch (e) {
return jsonResponse({ success: false, error: e instanceof Error ? e.message : String(e) }, 500);
}
});