Insert row

This commit is contained in:
2026-05-29 09:24:16 -04:00
parent bce0763785
commit a565502989
2 changed files with 108 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
/**
* Netlify Function (Node runtime).
*
* Inserts a row into the Supabase `funder_data_submissions` table
* when the user submits the funder discovery form. Runs in parallel
* with the Anthropic agent stream and is intentionally independent of
* it — a Supabase failure must never block (or be observed by) the
* streaming brief.
*
* Environment variables (set in .env.local for local `netlify dev`,
* and in the Netlify UI for production):
* - SUPABASE_URL e.g. https://<project>.supabase.co
* - SUPABASE_SERVICE_ROLE_KEY service role key (server-side only)
*
* The service role key bypasses RLS, so this function must only be
* called from trusted server-side code (which it is — this endpoint
* is the only caller). Do NOT expose this key to the browser.
*/
export const handler = async (event) => {
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
body: JSON.stringify({ error: 'Method not allowed' }),
}
}
const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY
if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) {
return {
statusCode: 500,
body: JSON.stringify({
error: 'Server is missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY',
}),
}
}
let payload
try {
payload = JSON.parse(event.body || '{}')
} catch {
return {
statusCode: 400,
body: JSON.stringify({ error: 'Invalid JSON body' }),
}
}
const row = {
name: payload.userName ?? null,
email: payload.userEmail ?? null,
station_name: payload.stationName ?? null,
station_location: payload.stationLocation ?? null,
station_website: payload.stationWebsite ?? null,
}
try {
const res = await fetch(
`${SUPABASE_URL.replace(/\/$/, '')}/rest/v1/funder_data_submissions`,
{
method: 'POST',
headers: {
apikey: SUPABASE_SERVICE_ROLE_KEY,
Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`,
'Content-Type': 'application/json',
Prefer: 'return=minimal',
},
body: JSON.stringify(row),
},
)
if (!res.ok) {
const text = await res.text()
console.error('Supabase insert failed', res.status, text)
return {
statusCode: 502,
body: JSON.stringify({
error: `Supabase insert failed: ${res.status} ${text}`,
}),
}
}
return {
statusCode: 204,
body: '',
}
} catch (err) {
console.error('Supabase request errored', err)
return {
statusCode: 500,
body: JSON.stringify({
error: `Supabase request errored: ${err?.message || String(err)}`,
}),
}
}
}
+11
View File
@@ -174,6 +174,17 @@ export default function App() {
setIsThinking(true) setIsThinking(true)
setIsStreaming(false) setIsStreaming(false)
// Fire-and-forget: save the submission to Supabase in parallel
// with the Anthropic agent stream. Any failure is logged but must
// never block or surface in the streaming UX.
fetch('/.netlify/functions/save-submission', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form),
}).catch((err) => {
console.error('Failed to save submission to Supabase', err)
})
try { try {
let resume = null let resume = null
// Loop across segments. Each iteration is one HTTP request; // Loop across segments. Each iteration is one HTTP request;