← back to Tk11438 Postgres Migration

verification/crezana-proposal/readiness.cjs

37 lines

const fs=require('fs'),crypto=require('crypto'),cp=require('child_process');
const root='/Users/macstudio3/Projects/crezana-internal';
const {Client}=require(root+'/node_modules/pg');
const raw=fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8');
const m=raw.match(/^DW_ADMIN_DB_PASSWORD=(.+)$/m);
const password=process.env.DW_ADMIN_DB_PASSWORD||(m?m[1].replace(/^["']|["']$/g,'').trim():'');
if(!password)throw Error('DB password unavailable');
const sum=x=>crypto.createHash('sha256').update(x).digest('hex');
async function read(host){
 const c=new Client({host,port:5432,user:'dw_admin',database:'dw_unified',password,connectionTimeoutMillis:5000,options:'-c default_transaction_read_only=on',application_name:'tk11438-crezana-preflight'});
 try{
  await c.connect();
  const id=(await c.query("SELECT current_user AS role, current_database() AS database, (SELECT oid FROM pg_database WHERE datname=current_database()) AS oid, pg_postmaster_start_time() AS server_start, inet_client_addr() IS NULL AS unix_socket, current_setting('default_transaction_read_only') AS readonly")).rows[0];
  const schema=(await c.query("SELECT column_name,data_type,is_nullable FROM information_schema.columns WHERE table_schema='public' AND table_name='vendor_requests' ORDER BY ordinal_position")).rows;
  const indexes=(await c.query("SELECT indexname,indexdef FROM pg_indexes WHERE schemaname='public' AND tablename='vendor_requests' ORDER BY indexname")).rows;
  const rows=(await c.query("SELECT count(*)::int AS count,max(id)::text AS max_id FROM vendor_requests WHERE vendor_code='crezana'")).rows[0];
  return {identity:id,columns:schema.length,indexes:indexes.length,schema_hash:sum(JSON.stringify(schema)),indexes_hash:sum(JSON.stringify(indexes)),requests:rows};
 }finally{await c.end().catch(()=>{});}
}
(async()=>{
 const tcp=await read('127.0.0.1'),socket=await read('/tmp');
 for(const key of ['role','database','oid'])if(tcp.identity[key]!==socket.identity[key])throw Error('Identity mismatch '+key);
 if(+tcp.identity.server_start!==+socket.identity.server_start)throw Error('Server mismatch');
 if(tcp.identity.unix_socket||!socket.identity.unix_socket||socket.identity.readonly!=='on')throw Error('Transport/read-only mismatch');
 if(tcp.schema_hash!==socket.schema_hash||tcp.indexes_hash!==socket.indexes_hash||JSON.stringify(tcp.requests)!==JSON.stringify(socket.requests))throw Error('Schema/request snapshot mismatch');
 let missing;try{await read('/tmp/tk11438-nonexistent-socket');missing='UNEXPECTED_SUCCESS';}catch(e){missing=e.code;}
 if(missing!=='ENOENT')throw Error('Missing socket failed differently: '+missing);
 const source=fs.readFileSync(root+'/lib/vendor-requests.js','utf8');
 const old="host: '127.0.0.1', port: 5432, user: 'dw_admin', database: 'dw_unified', password: PGPASS, max: 3";
 const next="host: process.env.PGHOST || (process.platform === 'darwin' ? '/tmp' : '127.0.0.1'), port: 5432, user: 'dw_admin', database: 'dw_unified', password: PGPASS, max: 3";
 if(source.split(old).length!==2)throw Error('Unexpected patch target');
 const prepared=source.replace(old,next);fs.writeFileSync('/tmp/tk11438-crezana-prepared.cjs',prepared);
 cp.execFileSync(process.execPath,['--check','/tmp/tk11438-crezana-prepared.cjs']);
 const out={at:new Date().toISOString(),tcp,socket,missing_socket:missing,prepared_source_sha256:sum(prepared),source_sha256:sum(source),syntax:'PASS',applied:false,restarted:false,email_sends:0,scope:'Independent pg client SELECTs with default_transaction_read_only; prepared file syntax only; application module never imported'};
 fs.writeFileSync('/tmp/tk11438-crezana-readiness.json',JSON.stringify(out,null,2)+'\n');console.log(JSON.stringify(out,null,2));
})().catch(e=>{console.error(e.code||e.message);process.exitCode=1;});