openai-webhook-responces recibe peticiones y hace log de las responses

This commit is contained in:
2026-02-25 15:24:57 -06:00
parent 8aeb1d0a64
commit 9263587f0b
11 changed files with 183 additions and 46 deletions
@@ -4,45 +4,99 @@
// Setup type definitions for built-in Supabase Runtime APIs
import "@supabase/functions-js/edge-runtime.d.ts";
import express from "express";
import OpenAI from "openai";
import { ResponseMetadata } from "../_shared/utils.ts";
import { handlePlanesEstudioResponse } from "./planes_estudio/index.ts";
const app = express();
console.log("Starting OpenAI webhook responses function");
const client = new OpenAI({
webhookSecret: Deno.env.get("OPENAI_WEBHOOK_SECRET"),
});
app.use(express.text({ type: "application/json" }));
async function handleCompletedResponse(
event: OpenAI.Webhooks.ResponseCompletedWebhookEvent,
) {
const response_id = event.data.id;
const response = await client.responses.retrieve(response_id);
const metadata = response.metadata as ResponseMetadata | null;
if (!metadata || !metadata.tabla) {
console.warn("No se recibió metadata o tabla en la respuesta");
return;
}
switch (metadata.tabla) {
case "planes_estudio":
handlePlanesEstudioResponse(response);
break;
default:
console.warn("Tabla no reconocida:", metadata.tabla);
}
const output_text = response.output
.filter((item: any) => item.type === "message")
.flatMap((item: any) => item.content)
.filter((contentItem: any) => contentItem.type === "output_text")
.map((contentItem: any) => contentItem.text)
.join("");
console.log("Response output:", output_text);
}
Deno.serve(async (req) => {
const url = new URL(req.url);
const functionName = url.pathname.split("/").pop();
console.log(
`[${new Date().toISOString()}][${functionName}]: Request received`,
);
// Opcional: Solo permitir POST
if (req.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
app.post("/webhook", async (req: express.Request, res: express.Response) => {
try {
const event = await client.webhooks.unwrap(req.body, req.headers);
// OpenAI requiere el body como texto crudo (string) para verificar la firma criptográfica
const payload = await req.text();
if (event.type === "response.completed") {
const response_id = event.data.id;
const response = await client.responses.retrieve(response_id);
const output_text = response.output
.filter((item) => item.type === "message")
.flatMap((item) => item.content)
.filter((contentItem) => contentItem.type === "output_text")
.map((contentItem) => contentItem.text)
.join("");
// Validar firma y parsear el evento. Deno maneja req.headers compatiblemente
const event = await client.webhooks.unwrap(payload, req.headers);
console.log("Response output:", output_text);
switch (event.type) {
case "response.completed": {
EdgeRuntime.waitUntil(handleCompletedResponse(event));
break;
}
case "response.cancelled": {
console.warn("Response cancelled:", event.data);
break;
}
case "response.failed": {
console.error("Response failed:", event.data);
break;
}
case "response.incomplete": {
console.warn("Response incomplete:", event.data);
break;
}
default: {
throw new Error(`Unhandled event type: ${event.type}`);
}
}
res.status(200).send();
console.log(
`[${
new Date().toISOString()
}][${functionName}]: Request processed successfully`,
);
return new Response("OK", { status: 200 });
} catch (error) {
if (error instanceof OpenAI.InvalidWebhookSignatureError) {
console.error("Invalid signature", error);
res.status(400).send("Invalid signature");
console.error("Invalid signature:", error.message);
return new Response("Invalid signature", { status: 400 });
} else {
throw error;
console.error("Internal Error:", error);
return new Response("Internal Server Error", { status: 500 });
}
}
});
Deno.serve(async (req) => {
console.log("Inicio de openai-webhook-responses");
return await app.handle(req);
});