release: opensource snapshot 2026-02-27 19:25:00
This commit is contained in:
118
scripts/check-image-urls-contract.ts
Normal file
118
scripts/check-image-urls-contract.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
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()
|
||||
})
|
||||
Reference in New Issue
Block a user