import fs from "node:fs";
|
import crypto from "node:crypto";
|
import { spawnSync } from "node:child_process";
|
|
const CLIENT="D:/mysql-8.0.40-winx64/bin/mysql.exe";
|
const mode=process.argv[2];
|
const baselinePath=process.argv[3];
|
const sqlPath=process.argv[4];
|
const outputPath=process.argv[5];
|
const preflightPath=process.argv[6];
|
if(!["preflight","raw"].includes(mode)) throw new Error("mode must be preflight or raw");
|
if(!baselinePath||!sqlPath||!outputPath) throw new Error("missing nonsecret path argument");
|
if(!fs.existsSync(baselinePath)) throw new Error("EXECUTION_BASELINE_MISSING");
|
if(!fs.existsSync(sqlPath)) throw new Error("SQL_INPUT_MISSING");
|
if(fs.existsSync(outputPath)) throw new Error("OUTPUT_ALREADY_EXISTS_NO_OVERWRITE");
|
function parseCsv(text){const rows=[];let row=[],cell="",quoted=false;for(let i=0;i<text.length;i++){const ch=text[i];if(quoted){if(ch==='"'&&text[i+1]==='"'){cell+='"';i++;}else if(ch==='"')quoted=false;else cell+=ch;}else if(ch==='"')quoted=true;else if(ch===','){row.push(cell);cell="";}else if(ch==='\n'){row.push(cell.replace(/\r$/, ""));if(row.some((value)=>value!==""))rows.push(row);row=[];cell="";}else cell+=ch;}if(cell!==""||row.length){row.push(cell.replace(/\r$/, ""));rows.push(row);}const headers=rows[0].map((value)=>value.replace(/^\uFEFF/,""));return rows.slice(1).map((values)=>Object.fromEntries(headers.map((header,index)=>[header,values[index]??""])));}
|
if(mode==="raw"){if(!preflightPath||!fs.existsSync(preflightPath))throw new Error("PREFLIGHT_OUTPUT_MISSING");const rows=parseCsv(fs.readFileSync(preflightPath,"utf8"));const gateFields=["column_present","type_compatible","source_user_gate_pass","dialect_gate_pass","read_only_gate_pass","engine_gate_pass","currentity_gate_pass"];if(rows.length!==11||rows.some((row)=>gateFields.some((field)=>row[field]!=="1")))throw new Error("PREFLIGHT_11_ROWS_OR_GATE_FAILURE");}
|
let sourceSecret=process.env.KLINE_SOURCE_MYSQL_PASSWORD??"";
|
if(!sourceSecret)throw new Error("HELD_BY_MISSING_SECURE_CREDENTIAL");
|
const parentMysqlPwd=process.env.MYSQL_PWD;
|
const childEnv={...process.env,MYSQL_PWD:sourceSecret};
|
delete childEnv.KLINE_SOURCE_MYSQL_PASSWORD;
|
const args=["--protocol=TCP","--host=317w7246e5.vicp.fun","--port=50176","--user=root","--database=trading_xuntou","--default-character-set=utf8mb4","--batch","--raw","--column-names","--connect-timeout=10","--init-command=SET SESSION TRANSACTION READ ONLY"];
|
if(args.some((arg)=>/^--password/i.test(arg)||arg===sourceSecret))throw new Error("SECRET_IN_ARGUMENTS");
|
const sql=fs.readFileSync(sqlPath,"utf8");
|
let result;
|
try{result=spawnSync(CLIENT,args,{input:sql,encoding:"utf8",env:childEnv,maxBuffer:512*1024*1024,windowsHide:true});}
|
finally{delete childEnv.MYSQL_PWD;sourceSecret="";}
|
if(process.env.MYSQL_PWD!==parentMysqlPwd)throw new Error("PARENT_MYSQL_PWD_MUTATED");
|
if(result.error||result.status!==0){const stderrHash=crypto.createHash("sha256").update(result.stderr??"").digest("hex");throw new Error("MYSQL_READONLY_EXECUTION_FAILED stderr_sha256="+stderrHash);}
|
const lines=result.stdout.replace(/\r\n/g,"\n").replace(/\n$/,"").split("\n");
|
if(lines.length<2)throw new Error("MYSQL_TABULAR_OUTPUT_EMPTY");
|
const width=lines[0].split("\t").length;
|
const matrix=lines.map((line)=>line.split("\t"));
|
if(matrix.some((row)=>row.length!==width))throw new Error("MYSQL_TABULAR_COLUMN_COUNT_MISMATCH");
|
const csvCell=(value)=>/[",\r\n]/.test(value)?'"'+value.replaceAll('"','""')+'"':value;
|
const csv="\uFEFF"+matrix.map((row)=>row.map(csvCell).join(",")).join("\r\n")+"\r\n";
|
fs.writeFileSync(outputPath,csv,"utf8");
|
console.log(JSON.stringify({mode,rows:matrix.length-1,columns:width,output_sha256:crypto.createHash("sha256").update(Buffer.from(csv,"utf8")).digest("hex"),secret_persisted:false}));
|