// Setup type definitions for built-in Supabase Runtime APIs import "@supabase/functions-js/edge-runtime.d.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; type WebhookPayload = { type: "INSERT" | "UPDATE" | "DELETE"; table: string; schema: string; record: Record | null; old_record: Record | null; }; const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? ""; const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? ""; const OPENAI_API_KEY = Deno.env.get("OPENAI_API_KEY") ?? ""; const OPENAI_BASE_URL = Deno.env.get("OPENAI_BASE_URL") ?? "https://api.openai.com/v1"; const ALLOWED_SCHEMA = "public"; const ALLOWED_TABLES = new Set(["planes_estudio", "asignaturas"]); const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, { auth: { persistSession: false }, }); function jsonResponse(status: number, body: Record) { return new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" }, }); } async function createConversationId(metadata: Record) { const response = await fetch(`${OPENAI_BASE_URL}/conversations`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${OPENAI_API_KEY}`, }, body: JSON.stringify({ metadata }), }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OpenAI error: ${response.status} ${errorText}`); } const data = await response.json(); const conversationId = data?.id as string | undefined; if (!conversationId) { throw new Error("OpenAI response missing conversation id"); } return conversationId; } Deno.serve(async (req) => { if (req.method !== "POST") { return jsonResponse(405, { error: "Method not allowed" }); } if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) { return jsonResponse(500, { error: "Supabase env vars missing" }); } if (!OPENAI_API_KEY) { return jsonResponse(500, { error: "OPENAI_API_KEY missing" }); } let payload: WebhookPayload; try { payload = await req.json(); } catch { return jsonResponse(400, { error: "Invalid JSON body" }); } const { type, table, schema, record } = payload; if (schema !== ALLOWED_SCHEMA || !ALLOWED_TABLES.has(table)) { return jsonResponse(400, { error: "Table not allowed" }); } if (type !== "INSERT") { return jsonResponse(200, { status: "ignored", reason: "event_type" }); } if (!record || typeof record !== "object") { return jsonResponse(400, { error: "Missing record" }); } const recordId = record["id"] as string | number | undefined; if (!recordId) { return jsonResponse(400, { error: "Record id missing" }); } const existingConversationId = record["conversation_id"] as | string | null | undefined; if (existingConversationId) { return jsonResponse(200, { status: "skipped", conversation_id: existingConversationId, }); } let conversationId: string; try { conversationId = await createConversationId({ table, record_id: String(recordId), }); } catch (error) { return jsonResponse(502, { error: (error as Error).message }); } const { error } = await supabase .from(table) .update({ conversation_id: conversationId }) .eq("id", recordId); if (error) { return jsonResponse(500, { error: error.message }); } return jsonResponse(200, { status: "updated", table, record_id: recordId, conversation_id: conversationId, }); });