Files
waoowaoo/scripts/check-image-urls-contract.ts

119 lines
3.1 KiB
TypeScript

import { logInfo as _ulogInfo, logError as _ulogError } from '@/lib/logging/core'
import { prisma } from '@/lib/prisma'
import { decodeImageUrlsFromDb } from '@/lib/contracts/image-urls-contract'
type AppearanceRow = {
id: string
imageUrls: string | null
previousImageUrls: string | null
}
type DynamicModel = {
findMany: (args: unknown) => Promise<AppearanceRow[]>
}
const BATCH_SIZE = 500
const MODELS: Array<{ name: string; model: string }> = [
{ name: 'CharacterAppearance', model: 'characterAppearance' },
{ name: 'GlobalCharacterAppearance', model: 'globalCharacterAppearance' },
]
const prismaDynamic = prisma as unknown as Record<string, DynamicModel>
function print(message: string) {
process.stdout.write(`${message}\n`)
}
async function checkModel(modelName: string, modelKey: string) {
const model = prismaDynamic[modelKey]
if (!model) {
throw new Error(`Prisma model not found: ${modelKey}`)
}
let scanned = 0
let violations = 0
const samples: Array<{ id: string; field: 'imageUrls' | 'previousImageUrls'; message: string; value: string | null }> = []
let cursor: string | null = null
while (true) {
const rows = await model.findMany({
select: {
id: true,
imageUrls: true,
previousImageUrls: true,
},
...(cursor
? {
cursor: { id: cursor },
skip: 1,
}
: {}),
orderBy: { id: 'asc' },
take: BATCH_SIZE,
})
if (rows.length === 0) break
for (const row of rows) {
scanned += 1
for (const fieldName of ['imageUrls', 'previousImageUrls'] as const) {
try {
decodeImageUrlsFromDb(row[fieldName], `${modelName}.${fieldName}`)
} catch (error) {
violations += 1
if (samples.length < 20) {
samples.push({
id: row.id,
field: fieldName,
message: error instanceof Error ? error.message : String(error),
value: row[fieldName],
})
}
}
}
}
cursor = rows[rows.length - 1]?.id || null
}
const summary = `[check-image-urls-contract] ${modelName}: scanned=${scanned} violations=${violations}`
_ulogInfo(summary)
print(summary)
if (samples.length > 0) {
_ulogError(`[check-image-urls-contract] ${modelName}: samples=${JSON.stringify(samples, null, 2)}`)
}
return { scanned, violations }
}
async function main() {
let totalScanned = 0
let totalViolations = 0
for (const target of MODELS) {
const result = await checkModel(target.name, target.model)
totalScanned += result.scanned
totalViolations += result.violations
}
if (totalViolations > 0) {
_ulogError(`[check-image-urls-contract] failed scanned=${totalScanned} violations=${totalViolations}`)
print(`[check-image-urls-contract] failed scanned=${totalScanned} violations=${totalViolations}`)
process.exitCode = 1
return
}
print(`[check-image-urls-contract] ok scanned=${totalScanned}`)
}
main()
.catch((error) => {
_ulogError('[check-image-urls-contract] failed:', error)
process.exitCode = 1
})
.finally(async () => {
await prisma.$disconnect()
})