// Opt-in, reviewed recovery of authentic cached previews. No model calls, deletes, // download-source changes or URL-prefix substitution. The caller durably saves // the returned plan before apply, and provides exact storage/DB adapters. import { createHash } from 'node:crypto' export const OPERATION_ID = 'material-preview-cache-20261023' export const digest = value => createHash('sha256').update(value).digest('hex') export const json = value => JSON.stringify(value, (_, v) => typeof v === 'bigint' ? v.toString() : v) const check = (condition, message) => { if (!condition) throw new Error(message) } const idPattern = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/ export function validateRecovery(manifest) { check(manifest?.version === 1 && manifest.reviewed === true && manifest.operationId === OPERATION_ID, 'reviewed recovery required') check(manifest.previewQuality === 'temporary-160px-derived' && manifest.materials?.length === 11, 'exact eleven temporary previews required') check(idPattern.test(manifest.actorId), 'actor required') const ids = new Set() for (const row of manifest.materials) { check(idPattern.test(row.materialId) && !ids.has(row.materialId), 'unique material identity required') ids.add(row.materialId) const old = new URL(row.oldPreviewUrl) check(old.protocol === 'https:' && !old.username && !old.password && !old.search && !old.hash, 'exact original URL required') check(/^[a-f0-9]{64}$/.test(row.sourceSha256) && /^[a-f0-9]{64}$/.test(row.cacheSha256), 'source and cache hashes required') const bytes = Buffer.from(row.bodyBase64, 'base64') check(bytes.length === row.bytes && bytes.length >= 100 && bytes.length <= 16384 && digest(bytes) === row.cacheSha256, 'cache bytes mismatch') check(bytes.subarray(0, 4).toString() === 'RIFF' && bytes.subarray(8, 12).toString() === 'WEBP', 'WebP required') check(row.width === 160 && row.height === 160 && row.provenance === '20261019-sharp-rotate-inside160-webp80', 'verified derivation required') check(row.destinationKey === `由壹点AI/public-display/${OPERATION_ID}/${row.materialId}-${row.cacheSha256}.webp`, 'scoped immutable destination required') check(row.newPreviewUrl === `https://s3.jiansun.vip/gongxiangpan/${row.destinationKey}`, 'canonical destination required') } return manifest } function verifyActor(actor) { check(actor && actor.isActive === true && actor.isSuperAdmin === true && actor.status === 'active' && actor.archivedAt === null && actor.employmentStatus === 'active', 'active authorized recovery actor required') } function verifyRecord(record, row) { check(record?.id === row.materialId && record.productModel === row.name && record.materialType === '材质' && record.deletedAt === null && record.previewUrl === row.oldPreviewUrl, 'source record changed') } export async function planRecovery(manifest, db) { validateRecovery(manifest) verifyActor(await db.actor(manifest.actorId)) const records = await db.records(manifest.materials.map(r => r.materialId)) check(records.length === 11, 'eleven source records required') const ordered = manifest.materials.map(row => { const record = records.find(r => r.id === row.materialId) verifyRecord(record, row) return record }) return { version: 1, operationId: OPERATION_ID, manifestSha256: digest(json(manifest)), capturedAt: new Date().toISOString(), records: ordered } } export async function applyRecovery({ manifest, plan, persistedPlanSha256, db, objects }) { validateRecovery(manifest) check(plan.operationId === OPERATION_ID && plan.manifestSha256 === digest(json(manifest)) && persistedPlanSha256 === digest(json(plan)) && plan.records?.length === 11, 'durably persisted matching plan required') const originalPlan = await planRecovery(manifest, db) check(json(originalPlan.records) === json(plan.records), 'source snapshot changed before upload') check(await db.operationCount(OPERATION_ID) === 0, 'operation already recorded; inspect before continuing') const uploaded = [] // Every object is publicly readable and hash-verified before the DB transaction. // A conflict leaves harmless, named unreferenced objects, never deletes anything. for (const row of manifest.materials) { const body = Buffer.from(row.bodyBase64, 'base64') const existing = await objects.get(row.destinationKey) if (existing === null) { await objects.putIfAbsent(row.destinationKey, body, { operationId: OPERATION_ID, materialId: row.materialId, sha256: row.cacheSha256, quality: manifest.previewQuality }) uploaded.push(row.destinationKey) } else check(digest(existing) === row.cacheSha256, 'existing destination differs; refusing overwrite') check(digest(await objects.get(row.destinationKey)) === row.cacheSha256, 'internal object readback mismatch') check(digest(await objects.publicGet(row.newPreviewUrl)) === row.cacheSha256, 'public object readback mismatch') } await db.transaction(async tx => { verifyActor(await tx.actor(manifest.actorId)) check(await tx.operationCount(OPERATION_ID) === 0, 'operation concurrently recorded') const current = await tx.records(manifest.materials.map(r => r.materialId)) const ordered = manifest.materials.map(row => current.find(r => r.id === row.materialId)) check(json(ordered) === json(plan.records), 'source snapshot changed; transaction aborted') for (const row of manifest.materials) { const before = plan.records.find(r => r.id === row.materialId) check(await tx.compareAndSet(row, before.updatedAt) === 1, 'concurrent record change; transaction aborted') await tx.log({ materialId: row.materialId, userId: manifest.actorId, action: 'PREVIEW_CACHE_RECOVERY', changes: json({ operationId: OPERATION_ID, previewQuality: manifest.previewQuality, oldPreviewUrl: row.oldPreviewUrl, newPreviewUrl: row.newPreviewUrl, originalSourceSha256: row.sourceSha256, cacheSha256: row.cacheSha256, width: 160, height: 160, downloadsAndAssetsUnchanged: true, snapshotSha256: persistedPlanSha256 }) }) } }) const after = await db.records(manifest.materials.map(r => r.materialId)) for (const before of plan.records) { const actual = after.find(r => r.id === before.id) const row = manifest.materials.find(r => r.materialId === before.id) check(actual?.previewUrl === row.newPreviewUrl, 'post-commit preview mismatch; inspect operation logs') const immutable = ({ previewUrl, updatedAt, ...rest }) => rest check(json(immutable(actual)) === json(immutable(before)), 'post-commit unrelated data mismatch; inspect operation logs') } return { operationId: OPERATION_ID, updated: 11, uploaded, after, temporaryPreviewSize: 160, originalSourceRestored: false, downloadsAndAssetsUnchanged: true, modelCalls: 0 } }