import { getSupabaseClient } from '../database' import { isRenderFailureFeatureEnabled } from './config' import type { CreateRenderFailureEventInput, RenderFailureEventRow, RenderFailureListResult, RenderFailureQuery } from './types' const TABLE = 'render_failure_events' const DEFAULT_PAGE = 1 const DEFAULT_PAGE_SIZE = 20 const MAX_PAGE_SIZE = 200 const DEFAULT_EXPORT_LIMIT = 1000 const MAX_EXPORT_LIMIT = 5000 function applyBaseFilters(builder: any, query: RenderFailureQuery): any { let next = builder if (query.from) { next = next.gte('created_at', `${query.from}T00:00:00.000Z`) } if (query.to) { next = next.lte('created_at', `${query.to}T23:59:59.999Z`) } if (query.errorType) { next = next.eq('error_type', query.errorType) } if (query.outputMode) { next = next.eq('output_mode', query.outputMode) } if (query.jobId) { next = next.eq('job_id', query.jobId) } if (typeof query.recovered === 'boolean') { next = next.eq('recovered', query.recovered) } return next } export async function createRenderFailureEvent( input: CreateRenderFailureEventInput ): Promise { if (!isRenderFailureFeatureEnabled()) { return null } const client = getSupabaseClient() if (!client) { return null } try { const { data, error } = await client.from(TABLE).insert(input).select('*').single() if (error) { console.error('[RenderFailure] Failed to create record:', error.message) return null } return data as RenderFailureEventRow } catch (error) { console.error('[RenderFailure] Failed to create record:', error) return null } } export async function listRenderFailureEvents( query: RenderFailureQuery = {} ): Promise { const page = Math.max(DEFAULT_PAGE, query.page ?? DEFAULT_PAGE) const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, query.pageSize ?? DEFAULT_PAGE_SIZE)) const empty: RenderFailureListResult = { records: [], total: 0, page, pageSize, hasMore: false } if (!isRenderFailureFeatureEnabled()) { return empty } const client = getSupabaseClient() if (!client) { return empty } const from = (page - 1) * pageSize const to = from + pageSize - 1 try { const countQuery = applyBaseFilters( client.from(TABLE).select('*', { count: 'exact', head: true }), query ) const { count, error: countError } = await countQuery if (countError) { console.error('[RenderFailure] Failed to count records:', countError.message) return empty } const dataQuery = applyBaseFilters(client.from(TABLE).select('*'), query) .order('created_at', { ascending: false }) .range(from, to) const { data, error } = await dataQuery if (error) { console.error('[RenderFailure] Failed to list records:', error.message) return empty } const records = (data ?? []) as RenderFailureEventRow[] const total = count ?? 0 return { records, total, page, pageSize, hasMore: from + records.length < total } } catch (error) { console.error('[RenderFailure] Failed to list records:', error) return empty } } export async function exportRenderFailureEvents( query: RenderFailureQuery = {} ): Promise { if (!isRenderFailureFeatureEnabled()) { return [] } const client = getSupabaseClient() if (!client) { return [] } const limit = Math.min(MAX_EXPORT_LIMIT, Math.max(1, query.limit ?? DEFAULT_EXPORT_LIMIT)) try { const dataQuery = applyBaseFilters(client.from(TABLE).select('*'), query) .order('created_at', { ascending: false }) .limit(limit) const { data, error } = await dataQuery if (error) { console.error('[RenderFailure] Failed to export records:', error.message) return [] } return (data ?? []) as RenderFailureEventRow[] } catch (error) { console.error('[RenderFailure] Failed to export records:', error) return [] } } export async function markRecoveredByJobId(jobId: string): Promise { if (!isRenderFailureFeatureEnabled()) { return false } const client = getSupabaseClient() if (!client) { return false } try { const { error } = await client .from(TABLE) .update({ recovered: true }) .eq('job_id', jobId) .eq('recovered', false) if (error) { console.error('[RenderFailure] Failed to mark recovered:', error.message) return false } return true } catch (error) { console.error('[RenderFailure] Failed to mark recovered:', error) return false } }