verify-development-public.mjs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import crypto from 'node:crypto';
  4. import {fileURLToPath} from 'node:url';
  5. import {validateDevelopmentReferences} from '../payload/src/core/development-references.js';
  6. const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
  7. const manifest=JSON.parse(fs.readFileSync(path.join(root,'deploy/industry-ui-files.json')));
  8. const argv=process.argv.slice(2);
  9. if(argv.length&&!(argv.length===2&&argv[0]==='--origin'))throw Error('USAGE');
  10. const origin=new URL(argv[1]||'https://kangshifu.fmode.cn');
  11. if(!['https:','http:'].includes(origin.protocol)||origin.pathname!=='/'||origin.username||origin.password)throw Error('INVALID_ORIGIN');
  12. const sha=b=>crypto.createHash('sha256').update(b).digest('hex');
  13. const result={ok:false,release:manifest.release,checkedAt:new Date().toISOString(),origin:origin.origin,files:[],checks:[],productionWrites:0,paidCalls:0};
  14. const check=(name,ok)=>result.checks.push({name,ok:!!ok});
  15. try{
  16. let data;
  17. for(const file of manifest.files){
  18. const url=new URL(file.path,origin);url.searchParams.set('r15verify',String(Date.now()));
  19. const response=await fetch(url,{cache:'no-store',signal:AbortSignal.timeout(30000)});
  20. const raw=Buffer.from(await response.arrayBuffer()),contentType=response.headers.get('content-type')||'';
  21. const normalizedSha256=sha(Buffer.from(raw.toString('utf8').replaceAll('\r\n','\n')));
  22. const matches=response.ok&&contentType.includes(file.path.endsWith('.json')?'application/json':'javascript')&&normalizedSha256===file.sha256;
  23. result.files.push({path:file.path,status:response.status,bytes:raw.length,sha256:sha(raw),normalizedSha256,contentType,matches});
  24. if(file.path.endsWith('.json')&&matches)data=validateDevelopmentReferences(JSON.parse(raw));
  25. }
  26. check('six-public-files-match',result.files.length===6&&result.files.every(r=>r.matches));
  27. check('verified-research-publication',data?.records.length===58&&data.sources.length===14);
  28. const home=await fetch(new URL('index.html',origin),{cache:'no-store',signal:AbortSignal.timeout(30000)});
  29. result.homepageSha256=sha(Buffer.from(await home.arrayBuffer()));check('homepage-unchanged',home.ok&&result.homepageSha256===manifest.homepageSha256);
  30. const response=await fetch(new URL('api/library',origin),{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({op:'summary'}),signal:AbortSignal.timeout(30000)});
  31. const summary=await response.json();result.generation=summary.generation;result.counts=summary.counts;
  32. check('VOC-library-read-only',response.ok&&summary.ok&&summary.readOnly===true&&summary.databaseWrites===0);
  33. check('reference-generation-matches-library',data?.libraryGeneration===summary.generation);
  34. check('product-review-price-counts-preserved',summary.counts?.products===4674&&summary.counts.reviews===3454&&summary.counts.prices===4172);
  35. result.ok=result.checks.every(r=>r.ok);
  36. }catch(e){result.error='PUBLIC_VERIFICATION_FAILED';}
  37. console.log(JSON.stringify(result,null,2));if(!result.ok)process.exitCode=1;