Files
genesis-2/supabase/functions/create-chat-conversation/index.ts
T
alexrg 0b7d2e83d7 Refactor OpenAI service to manage env and convo creation
- Add fromEnv factory to OpenAIService to handle env vars
- Move conversation creation into OpenAIService
- Update create- chat-conversation function to use new OpenAIService
  methods
- Add CORS headers to responses
2026-02-03 09:38:56 -06:00

124 lines
3.3 KiB
TypeScript

// Setup type definitions for built-in Supabase Runtime APIs
import "@supabase/functions-js/edge-runtime.d.ts";
import { corsHeaders } from "../_shared/cors.ts";
import { OpenAIService } from "../_shared/openai-service.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
type WebhookPayload = {
type: "INSERT" | "UPDATE" | "DELETE";
table: string;
schema: string;
record: Record<string, unknown> | null;
old_record: Record<string, unknown> | null;
};
const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? "";
const SUPABASE_SERVICE_ROLE_KEY =
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";
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<string, unknown>) {
return new Response(JSON.stringify(body), {
status,
headers: { "Content-Type": "application/json", ...corsHeaders },
});
}
async function createConversationId(
openaiService: OpenAIService,
metadata: Record<string, string>,
) {
const conversation = await openaiService.createConversation(metadata);
const conversationId = conversation?.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" });
}
const openaiService = OpenAIService.fromEnv();
if (!(openaiService instanceof OpenAIService)) {
return jsonResponse(500, { error: openaiService.message });
}
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(openaiService, {
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,
});
});