const CryptoJS = require("crypto-js"); const { deflate, unzip } = require("zlib"); const md5 = require("./md5.min.js"); const axios = require("axios"); const HtmlParser = require("node-html-parser"); const https = require("https"); const qs = require("qs"); const fs = require("fs"); const path = require("path"); const { NodeSSH } = require("node-ssh"); const SftpClient = require("ssh2-sftp-client"); const { exec } = require("child_process"); const { spawn } = require("child_process"); const ProgressBar = require("progress"); const Multiprogress = require("multi-progress"); const config = require("./config.json"); const servers = require("./servers.json"); function encryptPasswd(sessionId, password) { var key = CryptoJS.enc.Hex.parse(md5(sessionId)); var pdb = password; if (pdb && pdb != "") { var iv = CryptoJS.lib.WordArray.random(128 / 8); var pda = aesEncrypt(pdb, key, iv); return pda; } } function aesEncrypt(word, key, iv) { var encPrefix = "__RIS_ENC{"; var encSuffix = "}"; srcs = CryptoJS.enc.Utf8.parse(word); var encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7, }); var hash = CryptoJS.HmacSHA256( encrypted.ciphertext.toString().toUpperCase(), key ); return ( encPrefix + encrypted.ciphertext.toString().toUpperCase() + "@" + iv.toString() + "@" + hash.toString() + encSuffix ); } let sessionId; const instance = axios.create({ httpsAgent: new https.Agent({ rejectUnauthorized: false, }), withCredentials: true, maxRedirects: 0, }); instance.interceptors.request.use( function (config) { config.headers = config.headers || {}; config.headers.accept = config.headers.accept || "text/html"; if (sessionId) { config.headers["Cookie"] = "SESSION=" + sessionId; } return config; }, function (error) { return Promise.reject(error); } ); const getSessionId = () => { return Promise.resolve() .then(() => { return instance.get(config.loginUrl); }) .then((res) => { sessionId = res.headers["set-cookie"][0].match(/SESSION=(.*?);/)[1]; const root = HtmlParser.parse(res.data); const csrf = root .querySelector("#loginForm [name=_csrf]") .getAttribute("value"); return instance.post( config.loginUrl, qs.stringify({ language: "zh_CN", _csrf: csrf, username: config.username, password: encryptPasswd(sessionId, config.password), captcha: "", captchaPage: "", }) ); }) .then((res) => { console.log(res.headers); console.log(res.data); sessionId = res.headers["set-cookie"][0].match(/SESSION=(.*?);/)[1]; }) .catch((e) => { sessionId = e.response.headers["set-cookie"][0].match(/SESSION=(.*?);/)[1]; fs.writeFileSync("sessionId", sessionId); console.log("login sessionId=" + sessionId); return Promise.resolve(sessionId); }); }; if (fs.existsSync("sessionId")) { sessionId = fs.readFileSync("sessionId").toString().trim(); console.log("using exist sessionId: " + sessionId); } const checkSessionId = () => { if (!sessionId) { return Promise.reject(); } else { return new Promise((resolve, reject) => { instance .get(config.checkAccessUrl, { headers: { accept: "application/json", }, }) .then((res) => { resolve(); }) .catch((e) => { console.error("sessionId expired"); reject(); }); }); } }; const getAccess = (server) => { return instance .post( config.accessUrl, { misc: { resolution: "1159x897:maximize", isDualAuth: false, anyAccount: "admin", anyPassword: server.password, recheckCode: "", }, sessRemark: "", account: "any", proto: "sftp", dev: server.id, }, { headers: { accept: "application/json", }, } ) .then((res) => { return Promise.resolve(res.data); }) .catch((e) => { console.log(e); if (e.response.status === 401) { sessionId = null; } return Promise.reject(e); }); }; const build = () => { return new Promise((resolve, reject) => { const ls = spawn("sh", ["build.sh"], { cwd: config.projectDir }); ls.stdout.on("data", (data) => { process.stdout.write(data); }); ls.stderr.on("data", (data) => { process.stderr.write(data); }); ls.on("error", (error) => { console.log(`error: ${error.message}`); }); ls.on("close", (code) => { console.log(`build finished with code ${code}`); resolve(); }); }); }; let multi = new Multiprogress(process.stdout); // build() Promise.resolve() .then(() => { return new Promise((resolve, reject) => { checkSessionId() .then(() => { resolve(); }) .catch(() => { sessionId = null; getSessionId().then(() => { resolve(); }); }); }); }) .then(() => { upload(servers[0]); // upload(servers[5].id); // upload(servers[6].id); }); const upload = async (server) => { let { url } = await getAccess(server); const buffer = Buffer.from(url.match(/accessclient:\/\/(.*)/)[1], "base64"); let accessJson = await new Promise((resolve, reject) => { unzip(buffer, (err, buffer) => { if (err) { console.error("An error occurred:", err); process.exitCode = 1; } let accessJson = JSON.parse(buffer.toString()); console.log(accessJson); resolve(accessJson); }); }); // let file = "/Users/drew/Desktop/building3.tar.gz"; let file = path.resolve(config.projectDir, "build.tar.gz"); let stat = fs.statSync(file); let bar = multi.newBar(" uploading [:bar] :rate/bps :percent :etas", { complete: "=", incomplete: " ", width: 20, total: stat.size, }); return const ssh = new NodeSSH(); await ssh.connect({ host: accessJson.Host, username: accessJson.User, password: accessJson.PWD, }); // await ssh.putFile(file, "/home/admin/build.tar.gz", null, { // concurrency: 1, // chunkSize: 3276800, // step: (total_transferred, chunk, total) => { // bar.tick(chunk); // }, // }); ssh.connection.sftp((err, sftp) => { if (err) throw err; let rs = fs.createReadStream(file); let ws = sftp.createWriteStream("/home/admin/build.tar.gz"); rs.on("data", (buffer) => { bar.tick(buffer.length); }); rs.on("close", () => { console.log("stream close"); }); rs.on("end", () => { console.log("stream end"); }); rs.pipe(ws); }); // let sftp = new SftpClient(); // await sftp.connect({ // host: accessJson.Host, // username: accessJson.User, // password: accessJson.PWD, // }); // await sftp.fastPut(file, "/home/admin/build.tar.gz", { // concurrency: 64, // integer. Number of concurrent reads // chunkSize: 32768, // integer. Size of each read in bytes // // mode: 0o755, // mixed. Integer or string representing the file mode to set // step: (total_transferred, chunk, total) => { // bar.tick(chunk); // }, // }); // console.log("finish"); // sftp.end(); // ssh = new NodeSSH(); // await ssh.connect({ // host: config.ip, // username: `${config.username}/${server.ip}/any`, // password: config.password, // }); // let shell = await ssh.requestShell(); // shell.on("close", () => { // console.log("Stream :: close"); // }); // shell.on("data", (data) => { // let str = data.toString(); // process.stdout.write(data); // if (/^login\:/.test(str)) { // shell.write("admin\n"); // } // if (/^admin@.*password:/.test(str)) { // shell.write(`${server.password}\n`); // setTimeout(() => { // shell.write("su root\n"); // setTimeout(() => { // shell.write(`${server.password}\n`); // setTimeout(() => { // shell.write("./deploy.sh\n"); // }, 100); // }, 100); // }, 100); // } // }); };