ManimCat / src /render-failure /service.ts
Bin29's picture
Sync from main: 68df783 feat: support multimodal studio reference images
d47b053
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<RenderFailureEventRow | null> {
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<RenderFailureListResult> {
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<RenderFailureEventRow[]> {
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<boolean> {
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
}
}