Files
probably-rendering-one-more…/apps/waterfall-wiki/scripts/import-character-packs.mjs
codex-bot 939e002646
waterfall-wiki CI and deployment / verify-and-deploy (push) Successful in 17m46s
Verify original character pack hashes while allowing independent editorial covers
2026-10-27 15:19:53 +08:00

33 lines
2.9 KiB
JavaScript

import {readFile,readdir} from 'node:fs/promises';
import {fileURLToPath} from 'node:url';
import {join,dirname} from 'node:path';
import {createHash} from 'node:crypto';
import {requiredPackImages} from './verify-character-pack.mjs';
const root=join(dirname(fileURLToPath(import.meta.url)),'..','seed','characters');
const base=process.env.PROMPT_IMPORT_BASE_URL||'http://127.0.0.1:3000';
const packs=JSON.parse(await readFile(join(root,'packs.json'),'utf8'));
async function verify(pack){
let cursor='',post;const seen=new Set();
do{const response=await fetch(base+'/api/feed?'+new URLSearchParams({section:'images',...(cursor?{cursor}:{})}));if(!response.ok)throw Error('Cannot verify character feed: '+response.status);const page=await response.json();post=page.posts.find(p=>p.slug===pack.slug);cursor=page.nextCursor||'';if(cursor&&seen.has(cursor))throw Error('Character feed cursor did not advance');seen.add(cursor);}while(!post&&cursor);
if(!post)throw Error('Character pack missing: '+pack.slug);
const files=(await readdir(join(root,pack.directory))).filter(n=>n.endsWith('.png')).sort();if(files.length!==pack.count)throw Error('Seed file count mismatch: '+pack.slug);
const hashes=await Promise.all(files.map(async file=>createHash('sha256').update(await readFile(join(root,pack.directory,file))).digest('hex')));
const originals=requiredPackImages(hashes,post.assets.filter(a=>a.kind==='image'));
for(const image of originals){const media=await fetch(`${base}/api/media/${image.id}`,{method:'HEAD'});if(!media.ok||media.headers.get('content-type')!=='image/png')throw Error('Original PNG unavailable');}
console.log(`Verified: /p/${pack.slug} (${originals.length} original PNGs preserved; editorial covers allowed)`);
}
for(const pack of packs){
const existing=await fetch(`${base}/p/${pack.slug}`);
if(existing.ok){console.log(`Already imported: /p/${pack.slug}`);await verify(pack);continue;}
if(existing.status!==404)throw new Error(`Cannot check pack ${pack.slug}: ${existing.status}`);
const files=(await readdir(join(root,pack.directory))).filter(n=>n.endsWith('.png')).sort();
if(files.length!==pack.count)throw new Error(`Expected ${pack.count} PNGs for ${pack.slug}, got ${files.length}`);
const data=new FormData();data.set('title',pack.title);data.set('slug',pack.slug);data.set('description',pack.description);
for(const file of files)data.append('files',new Blob([await readFile(join(root,pack.directory,file))],{type:'image/png'}),file);
const response=await fetch(`${base}/api/skills/street-people`,{method:'POST',body:data});
if(response.status!==201&&response.status!==409)throw new Error(`Import ${pack.slug}: ${response.status} ${await response.text()}`);
if(response.status===409&&!(await fetch(`${base}/p/${pack.slug}`)).ok)throw new Error(`Conflicting import ${pack.slug}`);
console.log(`Imported: /p/${pack.slug} (${files.length} transparent PNGs)`);
await verify(pack);
}