| 12345678910111213141516171819202122232425262728 |
- import fs from 'node:fs/promises';
- import crypto from 'node:crypto';
- const args=process.argv.slice(2);
- if(args.length&&(args.length!==2||args[0]!=='--manifest'||!/^deploy\/[a-zA-Z0-9_.-]+\.json$/.test(args[1])))throw Error('USAGE');
- const base='https://kangshifu.fmode.cn/',manifest=JSON.parse(await fs.readFile(new URL('../'+(args[1]||'deploy/board-local-20261111.json'),import.meta.url)));
- const sha=b=>crypto.createHash('sha256').update(b).digest('hex');
- const normal=b=>b.includes(0)?b:Buffer.from(b.toString('utf8').replaceAll('\r\n','\n'));
- const receipt={ok:false,release:manifest.release,checkedAt:new Date().toISOString(),modules:[],checks:[],businessWrites:0};
- let next=0;
- try{
- await Promise.all(Array.from({length:6},async()=>{while(next<manifest.files.length){const file=manifest.files[next++],url=new URL(file.path,base);url.searchParams.set('boardRelease',manifest.release+'-'+Date.now());
- const r=await fetch(url,{headers:{'cache-control':'no-cache'},signal:AbortSignal.timeout(45000)}),bytes=Buffer.from(await r.arrayBuffer());
- const contentType=r.headers.get('content-type')||'',javascript=/\.m?js$/.test(file.path);
- const mimeMatches=!javascript||/^(?:application|text)\/javascript(?:\s*;|$)/i.test(contentType);
- receipt.modules.push({path:file.path,status:r.status,contentType,mimeMatches,sha256:sha(bytes),normalizedSha256:sha(normal(bytes)),matches:r.ok&&mimeMatches&&sha(normal(bytes))===file.sha256});
- }}));
- const home=await fetch(base+'index.html',{headers:{'cache-control':'no-cache'}});receipt.homepageSha256=sha(Buffer.from(await home.arrayBuffer()));
- receipt.checks.push({name:'homepage-preserved',ok:receipt.homepageSha256===manifest.homepageSha256});
- for(const name of ['ksfGetPublicBoard','ksfGetPublicMemberGroups']){
- const r=await fetch(base+'api/functions',{method:'POST',headers:{'content-type':'application/json','X-Parse-Application-Id':'FKieRbIoGt_main'},body:JSON.stringify({path:'/'+name,params:{}}),signal:AbortSignal.timeout(45000)}),j=await r.json();
- receipt.checks.push({name,ok:r.ok&&j.result?.ok===true});
- if(name==='ksfGetPublicMemberGroups'){const data=j.result?.data||j.result;receipt.memberCount=data?.memberCount;receipt.groupRevision=data?.revision;receipt.checks.push({name:'roster-available-to-groups',ok:data?.memberCount>=27});}
- }
- const r=await fetch(base+'api/library',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({op:'summary'}),signal:AbortSignal.timeout(45000)}),j=await r.json();
- receipt.generation=j.generation;receipt.counts=j.counts;receipt.checks.push({name:'research-library-readable',ok:r.ok&&j.ok===true});
- receipt.ok=receipt.modules.length===manifest.files.length&&receipt.modules.every(m=>m.matches)&&receipt.checks.every(c=>c.ok);
- }catch(e){receipt.error=e.message;}
- console.log(JSON.stringify(receipt,null,2));if(!receipt.ok)process.exitCode=1;
|