From e72951d32d9f0282d56c52a102e0d5d7f9d94b4a Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Mon, 9 Mar 2026 17:00:29 -0600 Subject: [PATCH 01/11] Enhance buscar-bibliografia function with Open Library integration and parameter handling --- .../functions/buscar-bibliografia/index.ts | 153 ++++++++++++++++-- 1 file changed, 141 insertions(+), 12 deletions(-) diff --git a/supabase/functions/buscar-bibliografia/index.ts b/supabase/functions/buscar-bibliografia/index.ts index 9b47e34..f45b5a5 100644 --- a/supabase/functions/buscar-bibliografia/index.ts +++ b/supabase/functions/buscar-bibliografia/index.ts @@ -6,6 +6,11 @@ import { HttpError, sendError, sendSuccess } from "../_shared/utils.ts"; console.log("Starting buscar-bibliografia function"); type GoogleBooksVolume = Record; +type OpenLibraryDoc = Record; + +type EndpointResult = + | { endpoint: "google"; item: GoogleBooksVolume } + | { endpoint: "open_library"; item: OpenLibraryDoc }; interface GoogleBooksVolumesListResponse { kind?: string; @@ -13,17 +18,54 @@ interface GoogleBooksVolumesListResponse { items?: GoogleBooksVolume[]; } +interface OpenLibrarySearchResponse { + num_found?: number; + start?: number; + docs?: OpenLibraryDoc[]; +} + +const ISO6391 = z.string().regex( + /^[a-z]{2}$/i, + "Debe ser ISO 639-1 (2 letras)", +); +const ISO6392 = z.string().regex( + /^[a-z]{3}$/i, + "Debe ser ISO 639-2 (3 letras)", +); + const SearchTermsSchema = z .object({ q: z.string().min(1, "q es requerido"), - maxResults: z.number().int().min(0).max(40), - orderBy: z.enum(["newest", "relevance"]).optional(), }) - .passthrough(); + .strict(); + +const GoogleParamsSchema = z + .object({ + orderBy: z.enum(["newest", "relevance"]).optional(), + langRestrict: ISO6391.optional(), + startIndex: z.number().int().min(0).optional(), + }) + .passthrough() + .optional() + .default({}); + +const OpenLibraryParamsSchema = z + .object({ + language: ISO6392.optional(), + page: z.number().int().min(1).optional(), + sort: z.string().optional(), + }) + .passthrough() + .optional() + .default({}); const BodySchema = z .object({ searchTerms: SearchTermsSchema, + + // Parámetros por endpoint + google: GoogleParamsSchema, + openLibrary: OpenLibraryParamsSchema, }) .strict(); @@ -57,6 +99,40 @@ function buildUrlWithSearchTerms( return url.toString(); } +function pickSerializableParams( + input: Record, +): Record< + string, + string | number | boolean | Array +> { + const out: Record< + string, + string | number | boolean | Array + > = {}; + + for (const [key, value] of Object.entries(input)) { + if (value === undefined || value === null) continue; + + if (Array.isArray(value)) { + const arr = value + .filter((v) => v !== undefined && v !== null) + .filter((v) => + ["string", "number", "boolean"].includes(typeof v) + ) as Array< + string | number | boolean + >; + if (arr.length) out[key] = arr; + continue; + } + + if (["string", "number", "boolean"].includes(typeof value)) { + out[key] = value as string | number | boolean; + } + } + + return out; +} + Deno.serve(async (req: Request): Promise => { const url = new URL(req.url); const functionName = url.pathname.split("/").pop(); @@ -117,17 +193,40 @@ Deno.serve(async (req: Request): Promise => { } const baseUrl = "https://www.googleapis.com/books/v1/volumes"; - const requestUrl = buildUrlWithSearchTerms(baseUrl, { + const googleParams = pickSerializableParams({ + ...body.google, ...body.searchTerms, key: GOOGLE_API_KEY, + maxResults: 20, + startIndex: (body.google as Record)?.startIndex ?? 0, }); + // No mandar parámetros exclusivos de Open Library a Google + delete (googleParams as Record)["page"]; + delete (googleParams as Record)["language"]; - const googleResp = await fetch(requestUrl, { - method: "GET", - headers: { - Accept: "application/json", - }, + const googleUrl = buildUrlWithSearchTerms(baseUrl, googleParams); + + const openLibraryBaseUrl = "https://openlibrary.org/search.json"; + const openLibraryParams = pickSerializableParams({ + ...body.openLibrary, + q: body.searchTerms.q, + limit: 20, + page: (body.openLibrary as Record)?.page ?? 1, }); + // No mandar parámetros exclusivos de Google a Open Library + delete (openLibraryParams as Record)["langRestrict"]; + delete (openLibraryParams as Record)["orderBy"]; + delete (openLibraryParams as Record)["startIndex"]; + + const openLibraryUrl = buildUrlWithSearchTerms( + openLibraryBaseUrl, + openLibraryParams, + ); + + const [googleResp, openLibraryResp] = await Promise.all([ + fetch(googleUrl, { headers: { Accept: "application/json" } }), + fetch(openLibraryUrl, { headers: { Accept: "application/json" } }), + ]); if (!googleResp.ok) { const text = await googleResp.text().catch(() => ""); @@ -143,10 +242,40 @@ Deno.serve(async (req: Request): Promise => { ); } - const data = (await googleResp.json()) as GoogleBooksVolumesListResponse; - const items = Array.isArray(data?.items) ? data.items : []; + if (!openLibraryResp.ok) { + const text = await openLibraryResp.text().catch(() => ""); + throw new HttpError( + 502, + "Error al consultar Open Library.", + "OPEN_LIBRARY_REQUEST_FAILED", + { + status: openLibraryResp.status, + statusText: openLibraryResp.statusText, + body: text || null, + }, + ); + } - return sendSuccess(items); + const googleData = + (await googleResp.json()) as GoogleBooksVolumesListResponse; + const openLibraryData = + (await openLibraryResp.json()) as OpenLibrarySearchResponse; + + const googleItems = Array.isArray(googleData?.items) + ? googleData.items.slice(0, 20) + : []; + const openLibraryDocs = Array.isArray(openLibraryData?.docs) + ? openLibraryData.docs.slice(0, 20) + : []; + + const results: EndpointResult[] = [ + ...googleItems.map((item) => ({ endpoint: "google", item } as const)), + ...openLibraryDocs.map(( + item, + ) => ({ endpoint: "open_library", item } as const)), + ]; + + return sendSuccess(results); } catch (error) { if (error instanceof HttpError) { console.error( From 5958ec15d264df85adcd0f2604584401a5b04ddc Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Mon, 9 Mar 2026 17:16:12 -0600 Subject: [PATCH 02/11] =?UTF-8?q?hotfix:=20identificaci=C3=B3n=20de=20la?= =?UTF-8?q?=20aplicaci=C3=B3n=20para=20Open=20Library?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- supabase/functions/buscar-bibliografia/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/supabase/functions/buscar-bibliografia/index.ts b/supabase/functions/buscar-bibliografia/index.ts index f45b5a5..46fda32 100644 --- a/supabase/functions/buscar-bibliografia/index.ts +++ b/supabase/functions/buscar-bibliografia/index.ts @@ -225,7 +225,12 @@ Deno.serve(async (req: Request): Promise => { const [googleResp, openLibraryResp] = await Promise.all([ fetch(googleUrl, { headers: { Accept: "application/json" } }), - fetch(openLibraryUrl, { headers: { Accept: "application/json" } }), + fetch(openLibraryUrl, { + headers: { + Accept: "application/json", + "User-Agent": "Acad-IA info.ingenieria.lci@gmail.com", + }, + }), ]); if (!googleResp.ok) { From 1b715baff6b7bfc73e01f3335d1cf3bf7b4e6020 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 01:36:11 +0000 Subject: [PATCH 03/11] =?UTF-8?q?A=C3=B1adir=20.gitea/workflows/ci.yaml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .gitea/workflows/ci.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..ed25089 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,28 @@ +name: CI + +on: + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: supabase/setup-cli@v1 + with: + version: latest + + - name: Start Supabase local development setup + run: supabase db start + + - name: Verify generated types are checked in + run: | + supabase gen types typescript --local > types.gen.ts + if ! git diff --ignore-space-at-eol --exit-code --quiet types.gen.ts; then + echo "Detected uncommitted changes after build. See status below:" + git diff + exit 1 + fi \ No newline at end of file From 13645dc00a1f709c43d94af22ad769884eba8dbd Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 07:51:37 -0600 Subject: [PATCH 04/11] Refactor Database type definitions by removing unused fields and adding new entities for person, professor, and student --- supabase/functions/_shared/database.types.ts | 234 +++++++------------ 1 file changed, 79 insertions(+), 155 deletions(-) diff --git a/supabase/functions/_shared/database.types.ts b/supabase/functions/_shared/database.types.ts index 4176947..5a6fadd 100644 --- a/supabase/functions/_shared/database.types.ts +++ b/supabase/functions/_shared/database.types.ts @@ -1,4 +1,4 @@ -export type Json = +export type Json = | string | number | boolean @@ -81,56 +81,6 @@ export type Database = { }, ] } - asignatura_mensajes_ia: { - Row: { - campos: string[] - conversacion_asignatura_id: string - enviado_por: string - estado: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion: string - fecha_creacion: string - id: string - is_refusal: boolean - mensaje: string - propuesta: Json | null - respuesta: string | null - } - Insert: { - campos?: string[] - conversacion_asignatura_id: string - enviado_por?: string - estado?: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion?: string - fecha_creacion?: string - id?: string - is_refusal?: boolean - mensaje: string - propuesta?: Json | null - respuesta?: string | null - } - Update: { - campos?: string[] - conversacion_asignatura_id?: string - enviado_por?: string - estado?: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion?: string - fecha_creacion?: string - id?: string - is_refusal?: boolean - mensaje?: string - propuesta?: Json | null - respuesta?: string | null - } - Relationships: [ - { - foreignKeyName: "asignatura_mensajes_ia_conversacion_asignatura_id_fkey" - columns: ["conversacion_asignatura_id"] - isOneToOne: false - referencedRelation: "conversaciones_asignatura" - referencedColumns: ["id"] - }, - ] - } asignaturas: { Row: { actualizado_en: string @@ -141,7 +91,6 @@ export type Database = { creado_en: string creado_por: string | null creditos: number - criterios_de_evaluacion: Json datos: Json estado: Database["public"]["Enums"]["estado_asignatura"] estructura_id: string | null @@ -166,7 +115,6 @@ export type Database = { creado_en?: string creado_por?: string | null creditos: number - criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null @@ -191,7 +139,6 @@ export type Database = { creado_en?: string creado_por?: string | null creditos?: number - criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null @@ -229,13 +176,6 @@ export type Database = { referencedRelation: "estructuras_asignatura" referencedColumns: ["id"] }, - { - foreignKeyName: "asignaturas_estructura_id_fkey" - columns: ["estructura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["estructura_id"] - }, { foreignKeyName: "asignaturas_linea_plan_fk_compuesta" columns: ["linea_plan_id", "plan_estudio_id"] @@ -301,13 +241,6 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, - { - foreignKeyName: "bibliografia_asignatura_asignatura_id_fkey" - columns: ["asignatura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["asignatura_id"] - }, { foreignKeyName: "bibliografia_asignatura_creado_por_fkey" columns: ["creado_por"] @@ -362,13 +295,6 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, - { - foreignKeyName: "cambios_asignatura_asignatura_id_fkey" - columns: ["asignatura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["asignatura_id"] - }, { foreignKeyName: "cambios_asignatura_cambiado_por_fkey" columns: ["cambiado_por"] @@ -515,13 +441,6 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, - { - foreignKeyName: "conversaciones_asignatura_asignatura_id_fkey" - columns: ["asignatura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["asignatura_id"] - }, { foreignKeyName: "conversaciones_asignatura_creado_por_fkey" columns: ["creado_por"] @@ -633,8 +552,7 @@ export type Database = { definicion: Json id: string nombre: string - template_id: string | null - tipo: Database["public"]["Enums"]["tipo_estructura_plan"] | null + version: string | null } Insert: { actualizado_en?: string @@ -642,8 +560,7 @@ export type Database = { definicion?: Json id?: string nombre: string - template_id?: string | null - tipo?: Database["public"]["Enums"]["tipo_estructura_plan"] | null + version?: string | null } Update: { actualizado_en?: string @@ -651,8 +568,7 @@ export type Database = { definicion?: Json id?: string nombre?: string - template_id?: string | null - tipo?: Database["public"]["Enums"]["tipo_estructura_plan"] | null + version?: string | null } Relationships: [] } @@ -776,13 +692,6 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, - { - foreignKeyName: "interacciones_ia_asignatura_id_fkey" - columns: ["asignatura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["asignatura_id"] - }, { foreignKeyName: "interacciones_ia_plan_estudio_id_fkey" columns: ["plan_estudio_id"] @@ -889,55 +798,26 @@ export type Database = { }, ] } - plan_mensajes_ia: { + person: { Row: { - campos: string[] - conversacion_plan_id: string - enviado_por: string - estado: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion: string - fecha_creacion: string - id: string - is_refusal: boolean - mensaje: string - propuesta: Json | null - respuesta: string | null + email: string | null + first_name: string | null + id: number + last_name: string | null } Insert: { - campos?: string[] - conversacion_plan_id: string - enviado_por?: string - estado?: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion?: string - fecha_creacion?: string - id?: string - is_refusal?: boolean - mensaje: string - propuesta?: Json | null - respuesta?: string | null + email?: string | null + first_name?: string | null + id?: number + last_name?: string | null } Update: { - campos?: string[] - conversacion_plan_id?: string - enviado_por?: string - estado?: Database["public"]["Enums"]["estado_mensaje_ia"] - fecha_actualizacion?: string - fecha_creacion?: string - id?: string - is_refusal?: boolean - mensaje?: string - propuesta?: Json | null - respuesta?: string | null + email?: string | null + first_name?: string | null + id?: number + last_name?: string | null } - Relationships: [ - { - foreignKeyName: "plan_mensajes_ia_conversacion_plan_id_fkey" - columns: ["conversacion_plan_id"] - isOneToOne: false - referencedRelation: "conversaciones_plan" - referencedColumns: ["id"] - }, - ] + Relationships: [] } planes_estudio: { Row: { @@ -1045,6 +925,36 @@ export type Database = { }, ] } + professor: { + Row: { + department: string | null + email: string | null + employee_number: string | null + first_name: string | null + id: number + last_name: string | null + salary: number | null + } + Insert: { + department?: string | null + email?: string | null + employee_number?: string | null + first_name?: string | null + id?: number + last_name?: string | null + salary?: number | null + } + Update: { + department?: string | null + email?: string | null + employee_number?: string | null + first_name?: string | null + id?: number + last_name?: string | null + salary?: number | null + } + Relationships: [] + } responsables_asignatura: { Row: { asignatura_id: string @@ -1075,13 +985,6 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, - { - foreignKeyName: "responsables_asignatura_asignatura_id_fkey" - columns: ["asignatura_id"] - isOneToOne: false - referencedRelation: "plantilla_asignatura" - referencedColumns: ["asignatura_id"] - }, { foreignKeyName: "responsables_asignatura_usuario_id_fkey" columns: ["usuario_id"] @@ -1112,6 +1015,36 @@ export type Database = { } Relationships: [] } + student: { + Row: { + email: string | null + enrollment_number: string | null + first_name: string | null + id: number + last_name: string | null + major: string | null + semester: number | null + } + Insert: { + email?: string | null + enrollment_number?: string | null + first_name?: string | null + id?: number + last_name?: string | null + major?: string | null + semester?: number | null + } + Update: { + email?: string | null + enrollment_number?: string | null + first_name?: string | null + id?: number + last_name?: string | null + major?: string | null + semester?: number | null + } + Relationships: [] + } tareas_revision: { Row: { asignado_a: string @@ -1347,14 +1280,6 @@ export type Database = { } } Views: { - plantilla_asignatura: { - Row: { - asignatura_id: string | null - estructura_id: string | null - template_id: string | null - } - Relationships: [] - } plantilla_plan: { Row: { estructura_id: string | null @@ -1373,13 +1298,13 @@ export type Database = { Args: { p_append: Json; p_id: string } Returns: undefined } + custom_access_token_hook: { Args: { event: Json }; Returns: Json } unaccent: { Args: { "": string }; Returns: string } unaccent_immutable: { Args: { "": string }; Returns: string } } Enums: { estado_asignatura: "borrador" | "revisada" | "aprobada" | "generando" estado_conversacion: "ACTIVA" | "ARCHIVANDO" | "ARCHIVADA" | "ERROR" - estado_mensaje_ia: "PROCESANDO" | "COMPLETADO" | "ERROR" estado_tarea_revision: "PENDIENTE" | "COMPLETADA" | "OMITIDA" fuente_cambio: "HUMANO" | "IA" nivel_plan_estudio: @@ -1554,7 +1479,6 @@ export const Constants = { Enums: { estado_asignatura: ["borrador", "revisada", "aprobada", "generando"], estado_conversacion: ["ACTIVA", "ARCHIVANDO", "ARCHIVADA", "ERROR"], - estado_mensaje_ia: ["PROCESANDO", "COMPLETADO", "ERROR"], estado_tarea_revision: ["PENDIENTE", "COMPLETADA", "OMITIDA"], fuente_cambio: ["HUMANO", "IA"], nivel_plan_estudio: [ From 0c2310d0d735fa98ddf8914efcf42d71d5e6e41c Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 07:51:41 -0600 Subject: [PATCH 05/11] Fix path for generated types in CI workflow --- .gitea/workflows/ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index ed25089..2c24f53 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -20,9 +20,9 @@ jobs: - name: Verify generated types are checked in run: | - supabase gen types typescript --local > types.gen.ts - if ! git diff --ignore-space-at-eol --exit-code --quiet types.gen.ts; then + supabase gen types typescript --local > supabase/functions/_shared/database.types.ts + if ! git diff --ignore-space-at-eol --exit-code --quiet supabase/functions/_shared/database.types.ts; then echo "Detected uncommitted changes after build. See status below:" git diff exit 1 - fi \ No newline at end of file + fi From 7152f91dfcc2c40f6c524eea5e5afd8cfcb57a82 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 08:00:34 -0600 Subject: [PATCH 06/11] update generated supabase types --- supabase/functions/_shared/database.types.ts | 264 +++++++++++-------- 1 file changed, 158 insertions(+), 106 deletions(-) diff --git a/supabase/functions/_shared/database.types.ts b/supabase/functions/_shared/database.types.ts index 5a6fadd..9684acc 100644 --- a/supabase/functions/_shared/database.types.ts +++ b/supabase/functions/_shared/database.types.ts @@ -7,30 +7,10 @@ export type Json = | Json[] export type Database = { - graphql_public: { - Tables: { - [_ in never]: never - } - Views: { - [_ in never]: never - } - Functions: { - graphql: { - Args: { - extensions?: Json - operationName?: string - query?: string - variables?: Json - } - Returns: Json - } - } - Enums: { - [_ in never]: never - } - CompositeTypes: { - [_ in never]: never - } + // Allows to automatically instantiate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "12.2.3 (519615d)" } public: { Tables: { @@ -81,6 +61,56 @@ export type Database = { }, ] } + asignatura_mensajes_ia: { + Row: { + campos: string[] + conversacion_asignatura_id: string + enviado_por: string + estado: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion: string + fecha_creacion: string + id: string + is_refusal: boolean + mensaje: string + propuesta: Json | null + respuesta: string | null + } + Insert: { + campos?: string[] + conversacion_asignatura_id: string + enviado_por?: string + estado?: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion?: string + fecha_creacion?: string + id?: string + is_refusal?: boolean + mensaje: string + propuesta?: Json | null + respuesta?: string | null + } + Update: { + campos?: string[] + conversacion_asignatura_id?: string + enviado_por?: string + estado?: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion?: string + fecha_creacion?: string + id?: string + is_refusal?: boolean + mensaje?: string + propuesta?: Json | null + respuesta?: string | null + } + Relationships: [ + { + foreignKeyName: "asignatura_mensajes_ia_conversacion_asignatura_id_fkey" + columns: ["conversacion_asignatura_id"] + isOneToOne: false + referencedRelation: "conversaciones_asignatura" + referencedColumns: ["id"] + }, + ] + } asignaturas: { Row: { actualizado_en: string @@ -91,6 +121,7 @@ export type Database = { creado_en: string creado_por: string | null creditos: number + criterios_de_evaluacion: Json datos: Json estado: Database["public"]["Enums"]["estado_asignatura"] estructura_id: string | null @@ -115,6 +146,7 @@ export type Database = { creado_en?: string creado_por?: string | null creditos: number + criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null @@ -139,6 +171,7 @@ export type Database = { creado_en?: string creado_por?: string | null creditos?: number + criterios_de_evaluacion?: Json datos?: Json estado?: Database["public"]["Enums"]["estado_asignatura"] estructura_id?: string | null @@ -176,6 +209,13 @@ export type Database = { referencedRelation: "estructuras_asignatura" referencedColumns: ["id"] }, + { + foreignKeyName: "asignaturas_estructura_id_fkey" + columns: ["estructura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["estructura_id"] + }, { foreignKeyName: "asignaturas_linea_plan_fk_compuesta" columns: ["linea_plan_id", "plan_estudio_id"] @@ -241,6 +281,13 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, + { + foreignKeyName: "bibliografia_asignatura_asignatura_id_fkey" + columns: ["asignatura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["asignatura_id"] + }, { foreignKeyName: "bibliografia_asignatura_creado_por_fkey" columns: ["creado_por"] @@ -295,6 +342,13 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, + { + foreignKeyName: "cambios_asignatura_asignatura_id_fkey" + columns: ["asignatura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["asignatura_id"] + }, { foreignKeyName: "cambios_asignatura_cambiado_por_fkey" columns: ["cambiado_por"] @@ -441,6 +495,13 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, + { + foreignKeyName: "conversaciones_asignatura_asignatura_id_fkey" + columns: ["asignatura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["asignatura_id"] + }, { foreignKeyName: "conversaciones_asignatura_creado_por_fkey" columns: ["creado_por"] @@ -552,7 +613,8 @@ export type Database = { definicion: Json id: string nombre: string - version: string | null + template_id: string | null + tipo: Database["public"]["Enums"]["tipo_estructura_plan"] | null } Insert: { actualizado_en?: string @@ -560,7 +622,8 @@ export type Database = { definicion?: Json id?: string nombre: string - version?: string | null + template_id?: string | null + tipo?: Database["public"]["Enums"]["tipo_estructura_plan"] | null } Update: { actualizado_en?: string @@ -568,7 +631,8 @@ export type Database = { definicion?: Json id?: string nombre?: string - version?: string | null + template_id?: string | null + tipo?: Database["public"]["Enums"]["tipo_estructura_plan"] | null } Relationships: [] } @@ -692,6 +756,13 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, + { + foreignKeyName: "interacciones_ia_asignatura_id_fkey" + columns: ["asignatura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["asignatura_id"] + }, { foreignKeyName: "interacciones_ia_plan_estudio_id_fkey" columns: ["plan_estudio_id"] @@ -798,26 +869,55 @@ export type Database = { }, ] } - person: { + plan_mensajes_ia: { Row: { - email: string | null - first_name: string | null - id: number - last_name: string | null + campos: string[] + conversacion_plan_id: string + enviado_por: string + estado: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion: string + fecha_creacion: string + id: string + is_refusal: boolean + mensaje: string + propuesta: Json | null + respuesta: string | null } Insert: { - email?: string | null - first_name?: string | null - id?: number - last_name?: string | null + campos?: string[] + conversacion_plan_id: string + enviado_por?: string + estado?: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion?: string + fecha_creacion?: string + id?: string + is_refusal?: boolean + mensaje: string + propuesta?: Json | null + respuesta?: string | null } Update: { - email?: string | null - first_name?: string | null - id?: number - last_name?: string | null + campos?: string[] + conversacion_plan_id?: string + enviado_por?: string + estado?: Database["public"]["Enums"]["estado_mensaje_ia"] + fecha_actualizacion?: string + fecha_creacion?: string + id?: string + is_refusal?: boolean + mensaje?: string + propuesta?: Json | null + respuesta?: string | null } - Relationships: [] + Relationships: [ + { + foreignKeyName: "plan_mensajes_ia_conversacion_plan_id_fkey" + columns: ["conversacion_plan_id"] + isOneToOne: false + referencedRelation: "conversaciones_plan" + referencedColumns: ["id"] + }, + ] } planes_estudio: { Row: { @@ -925,36 +1025,6 @@ export type Database = { }, ] } - professor: { - Row: { - department: string | null - email: string | null - employee_number: string | null - first_name: string | null - id: number - last_name: string | null - salary: number | null - } - Insert: { - department?: string | null - email?: string | null - employee_number?: string | null - first_name?: string | null - id?: number - last_name?: string | null - salary?: number | null - } - Update: { - department?: string | null - email?: string | null - employee_number?: string | null - first_name?: string | null - id?: number - last_name?: string | null - salary?: number | null - } - Relationships: [] - } responsables_asignatura: { Row: { asignatura_id: string @@ -985,6 +1055,13 @@ export type Database = { referencedRelation: "asignaturas" referencedColumns: ["id"] }, + { + foreignKeyName: "responsables_asignatura_asignatura_id_fkey" + columns: ["asignatura_id"] + isOneToOne: false + referencedRelation: "plantilla_asignatura" + referencedColumns: ["asignatura_id"] + }, { foreignKeyName: "responsables_asignatura_usuario_id_fkey" columns: ["usuario_id"] @@ -1015,36 +1092,6 @@ export type Database = { } Relationships: [] } - student: { - Row: { - email: string | null - enrollment_number: string | null - first_name: string | null - id: number - last_name: string | null - major: string | null - semester: number | null - } - Insert: { - email?: string | null - enrollment_number?: string | null - first_name?: string | null - id?: number - last_name?: string | null - major?: string | null - semester?: number | null - } - Update: { - email?: string | null - enrollment_number?: string | null - first_name?: string | null - id?: number - last_name?: string | null - major?: string | null - semester?: number | null - } - Relationships: [] - } tareas_revision: { Row: { asignado_a: string @@ -1280,6 +1327,14 @@ export type Database = { } } Views: { + plantilla_asignatura: { + Row: { + asignatura_id: string | null + estructura_id: string | null + template_id: string | null + } + Relationships: [] + } plantilla_plan: { Row: { estructura_id: string | null @@ -1298,13 +1353,13 @@ export type Database = { Args: { p_append: Json; p_id: string } Returns: undefined } - custom_access_token_hook: { Args: { event: Json }; Returns: Json } unaccent: { Args: { "": string }; Returns: string } unaccent_immutable: { Args: { "": string }; Returns: string } } Enums: { estado_asignatura: "borrador" | "revisada" | "aprobada" | "generando" estado_conversacion: "ACTIVA" | "ARCHIVANDO" | "ARCHIVADA" | "ERROR" + estado_mensaje_ia: "PROCESANDO" | "COMPLETADO" | "ERROR" estado_tarea_revision: "PENDIENTE" | "COMPLETADA" | "OMITIDA" fuente_cambio: "HUMANO" | "IA" nivel_plan_estudio: @@ -1472,13 +1527,11 @@ export type CompositeTypes< : never export const Constants = { - graphql_public: { - Enums: {}, - }, public: { Enums: { estado_asignatura: ["borrador", "revisada", "aprobada", "generando"], estado_conversacion: ["ACTIVA", "ARCHIVANDO", "ARCHIVADA", "ERROR"], + estado_mensaje_ia: ["PROCESANDO", "COMPLETADO", "ERROR"], estado_tarea_revision: ["PENDIENTE", "COMPLETADA", "OMITIDA"], fuente_cambio: ["HUMANO", "IA"], nivel_plan_estudio: [ @@ -1533,4 +1586,3 @@ export const Constants = { }, }, } as const - From c2a7eea20abdda80e6720ce14231130d63eff82e Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 08:10:50 -0600 Subject: [PATCH 07/11] Add GraphQL public type definitions and update constants --- supabase/functions/_shared/database.types.ts | 32 +++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/supabase/functions/_shared/database.types.ts b/supabase/functions/_shared/database.types.ts index 9684acc..a889116 100644 --- a/supabase/functions/_shared/database.types.ts +++ b/supabase/functions/_shared/database.types.ts @@ -7,10 +7,30 @@ export type Json = | Json[] export type Database = { - // Allows to automatically instantiate createClient with right options - // instead of createClient(URL, KEY) - __InternalSupabase: { - PostgrestVersion: "12.2.3 (519615d)" + graphql_public: { + Tables: { + [_ in never]: never + } + Views: { + [_ in never]: never + } + Functions: { + graphql: { + Args: { + extensions?: Json + operationName?: string + query?: string + variables?: Json + } + Returns: Json + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } } public: { Tables: { @@ -1527,6 +1547,9 @@ export type CompositeTypes< : never export const Constants = { + graphql_public: { + Enums: {}, + }, public: { Enums: { estado_asignatura: ["borrador", "revisada", "aprobada", "generando"], @@ -1586,3 +1609,4 @@ export const Constants = { }, }, } as const + From b04961a944f605b1a88381f564a6fad2372ccf42 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 08:16:42 -0600 Subject: [PATCH 08/11] Enhance CI workflow to preview generated types and improve error messaging --- .gitea/workflows/ci.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 2c24f53..9e41e93 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -21,8 +21,13 @@ jobs: - name: Verify generated types are checked in run: | supabase gen types typescript --local > supabase/functions/_shared/database.types.ts + + echo "----- Generated types preview -----" + head -n 40 supabase/functions/_shared/database.types.ts + echo "-----------------------------------" + if ! git diff --ignore-space-at-eol --exit-code --quiet supabase/functions/_shared/database.types.ts; then - echo "Detected uncommitted changes after build. See status below:" - git diff - exit 1 + echo "Detected uncommitted changes after build. See diff below:" + git diff supabase/functions/_shared/database.types.ts + exit 1 fi From 3959b97aa1d7a2cbef468940e6f9469d26e808dc Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 08:25:59 -0600 Subject: [PATCH 09/11] Improve CI workflow by adding directory listing for debugging and removing generated types preview --- .gitea/workflows/ci.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 9e41e93..286de96 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -18,16 +18,13 @@ jobs: - name: Start Supabase local development setup run: supabase db start + - name: List directory contents for debugging + run: ls -R + - name: Verify generated types are checked in run: | supabase gen types typescript --local > supabase/functions/_shared/database.types.ts - - echo "----- Generated types preview -----" - head -n 40 supabase/functions/_shared/database.types.ts - echo "-----------------------------------" - if ! git diff --ignore-space-at-eol --exit-code --quiet supabase/functions/_shared/database.types.ts; then echo "Detected uncommitted changes after build. See diff below:" - git diff supabase/functions/_shared/database.types.ts exit 1 fi From 2ff1a5e8a99a616ae5fd261040cf3155dd7c32fc Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 08:36:02 -0600 Subject: [PATCH 10/11] Refactor CI workflow to reset database for clean state and remove directory listing step --- .gitea/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 286de96..3a14df4 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -18,8 +18,8 @@ jobs: - name: Start Supabase local development setup run: supabase db start - - name: List directory contents for debugging - run: ls -R + - name: Reset database to ensure a clean state + run: supabase db reset --no-seed - name: Verify generated types are checked in run: | From 347f932a21bd40b706313b4a5fa724b6de4d2e57 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 10 Mar 2026 09:27:51 -0600 Subject: [PATCH 11/11] Disable JWT verification for openai-webhook-responses function --- supabase/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supabase/config.toml b/supabase/config.toml index ca14823..87d7933 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -57,7 +57,7 @@ uri = "pg-functions://postgres/public/custom_access_token_hook" [functions.openai-webhook-responses] enabled = true -verify_jwt = true +verify_jwt = false import_map = "./functions/openai-webhook-responses/deno.json" # Uncomment to specify a custom file path to the entrypoint. # Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx