From a6fd16d5429a6e5f39f0a854a70cf8bead003d3c Mon Sep 17 00:00:00 2001 From: liudongqi Date: Wed, 4 Sep 2024 00:14:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.js | 15 +++--- deploy.sh | 2 +- ecosystem.config.js | 17 +++++++ enums/AreaNameEnum.js | 40 +++++++++++++++ routes/index.js | 89 +++++++++++++------------------- {rs6 => test/rs6}/hubei.js | 0 {rs6 => test/rs6}/hubei2.js | 0 {rs6 => test/rs6}/index.js | 0 {rs6 => test/rs6}/shangbiao.js | 0 {rs6 => test/rs6}/yunnan.js | 0 utils/IpUtil.js | 19 +++++++ utils/Store.js | 92 ++++++++++++++++++++++++++++++++++ utils/logger.js | 16 ------ utils/paths.js | 24 --------- 14 files changed, 213 insertions(+), 101 deletions(-) create mode 100644 ecosystem.config.js create mode 100644 enums/AreaNameEnum.js rename {rs6 => test/rs6}/hubei.js (100%) rename {rs6 => test/rs6}/hubei2.js (100%) rename {rs6 => test/rs6}/index.js (100%) rename {rs6 => test/rs6}/shangbiao.js (100%) rename {rs6 => test/rs6}/yunnan.js (100%) create mode 100644 utils/IpUtil.js create mode 100644 utils/Store.js delete mode 100644 utils/logger.js delete mode 100644 utils/paths.js diff --git a/app.js b/app.js index 374788e..46f90a2 100644 --- a/app.js +++ b/app.js @@ -1,9 +1,11 @@ const express = require('express'); const bodyParser = require('body-parser'); - - +let IpUtil = require('./utils/IpUtil'); const rs = require("./routes"); +// 读取环境变量 +const port = process.env.PORT || 8081; +const nodeEnv = process.env.NODE_ENV || 'development'; /** * 初始化框架,并将初始化后的函数给予 '当前页面'全局变量 app @@ -25,8 +27,9 @@ app.use(bodyParser.urlencoded({extended: true})); app.use("/", rs); -const server = app.listen(8081, "0.0.0.0", () => { - const host = server.address().address; - const port = server.address().port; - console.log("Node.JS 服务器已启动,访问地址: http://%s:%s", host, port) +app.listen(port, "0.0.0.0", () => { + const ips = IpUtil.getLocalIPs(); + for (let ip of ips) { + console.log(`${nodeEnv} Node.JS 服务器已启动,访问地址: http://${ip}:${port}`) + } }); \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index 5ef0a32..98c544d 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,6 @@ #!/bin/sh APP_NAME="qry-python-js" - +nvm use 20 if pm2 status | grep -q "$APP_NAME.*online"; then echo "Application $APP_NAME is running." pm2 restart $APP_NAME diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..5bf8b4e --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,17 @@ +module.exports = { + apps: [ + { + name: 'qry-python-js', // 应用的名称 + script: './app.js', // Express 应用的入口文件 + instances: 'max', // 启动的实例数('max' 表示使用所有可用的 CPU 核心) + exec_mode: 'cluster', // 使用集群模式 + env: { + NODE_ENV: 'development', // 开发环境的环境变量 + }, + env_production: { + NODE_ENV: 'production', // 生产环境的环境变量 + PORT: 80, // 自定义环境变量示例 + }, + }, + ], +}; diff --git a/enums/AreaNameEnum.js b/enums/AreaNameEnum.js new file mode 100644 index 0000000..6ae2746 --- /dev/null +++ b/enums/AreaNameEnum.js @@ -0,0 +1,40 @@ +const path = require("node:path"); + + +const AreaNameEnum = Object.freeze({ + YUN_NAN: Object.freeze({ + AREA_CODE: '5300', + AREA_NAME: 'yunnan', + JS_FILE: path.resolve(__dirname, '../public/yunnan/MO5zzCMcub4d.b4c45da.js'), + }), + HU_BEI: Object.freeze({ + AREA_CODE: '4200', + AREA_NAME: 'hubei', + JS_FILE: path.resolve(__dirname, '../public/hubei/5PXGXoOF7eGJ.ed63b8f.js'), + }), + + getByAreaCode(areaCode) { + if (areaCode == null) { + return Object(); + } + for (const key in this) { + if (this[key] && this[key].AREA_CODE === areaCode.toString()) { + return this[key]; + } + } + return Object(); + }, + getByAreaName(areaName) { + if (areaName == null) { + return Object(); + } + for (const key in this) { + if (this[key] && this[key].AREA_NAME === areaName.toString()) { + return this[key]; + } + } + return Object(); + }, +}); + +module.exports = AreaNameEnum; diff --git a/routes/index.js b/routes/index.js index bec0401..d7fa0e8 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,21 +2,12 @@ const express = require("express"); const router = express.Router(); const {jsdomFromText, browser} = require("sdenv"); -const {Script} = require("vm"); +const {Script} = require("node:vm"); const fs = require("node:fs"); -const path = require("node:path"); -const crypto = require("crypto") - - -let sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - - -const storeCookie = new Map; - -const areaJsMap = { - "yunnan": path.resolve(__dirname, '../public/yunnan/MO5zzCMcub4d.b4c45da.js'), - "hubei": path.resolve(__dirname, '../public/hubei/5PXGXoOF7eGJ.ed63b8f.js'), -} +const crypto = require("node:crypto") +const AreaNameEnum = require('../enums/AreaNameEnum'); +const Store = require("../utils/Store"); +let store = new Store(); router.post('/rsCookie', async (req, res) => { @@ -38,7 +29,7 @@ router.post('/rsCookie', async (req, res) => { try { let jsText; if (jsStr == null) { - let jsPath = areaJsMap[areaName]; + let jsPath = AreaNameEnum.getByAreaName(areaName).JS_FILE if (jsPath == null) { console.error('未找到js文件') return res.send('未找到js文件') @@ -64,12 +55,28 @@ router.post('/rsCookie', async (req, res) => { }) +function loadJs(window) { + // 加载js + let js = ''; + // 加载 页面上的js + const allScript = window.document.querySelectorAll('script[r="m"]'); + allScript.forEach(script => { + let attr = script.textContent; + if (attr) { + js += attr + } else { + js += jsText + } + js += ";\n" + }) + return js; +} + async function handle(url, htmlStr, jsText, cookie, userAgent) { let uuid = crypto.randomUUID() - // 获取 origin let baseUrl = new URL(url).origin; - + // 初始化 jsDom 和 cookieJar const [jsDom, cookieJar] = await jsdomFromText({ url: url, referrer: url, @@ -83,52 +90,26 @@ async function handle(url, htmlStr, jsText, cookie, userAgent) { } // 加载dom const dom = await jsDom(htmlStr); - window = dom.window - // 加载 页面上的js - const allScript = window.document.querySelectorAll('script[r="m"]'); - - // let $ = cheerio.load(htmlStr); - // let nextAll = $('script[r=m]'); - - let js = ''; - allScript.forEach(script=>{ - let attr = script.textContent; - if (attr) { - js += attr - } else { - js += jsText - } - js += ";\n" - }) - // allScript.each((script) => { - // - // }) - - // 执行完成后的钩子 + // js执行成功后的瑞树会跳转页面 会触发onbeforeunload钩子 window.onbeforeunload = async (url) => { const cookies = cookieJar.getCookieStringSync(baseUrl); // console.debug(`${url} 生成cookie:`, cookies); - storeCookie.set(uuid, cookies) + store.set(uuid, cookies) // window.close(); - return cookies; } - - browser(dom.window, 'chrome'); - + // 初始化浏览器 + browser(window, 'chrome'); + // 加载js + let js = loadJs(window); + // 执行 js let script = new Script(js); let internalVMContext = dom.getInternalVMContext(); - // 执行 js script.runInContext(internalVMContext); - - for (let i = 0; i < 10; i++) { - await sleep(100) - let hasCookie = storeCookie.has(uuid) - if (hasCookie) { - let newVar = storeCookie.get(uuid); - storeCookie.delete(uuid) - return newVar; - } + // 等待 onbeforeunload 钩子触发后的回掉 + let val = await store.waitGetAndDelete(uuid, 100, 10) + if (val != null) { + return val; } throw new Error('执行超时') } diff --git a/rs6/hubei.js b/test/rs6/hubei.js similarity index 100% rename from rs6/hubei.js rename to test/rs6/hubei.js diff --git a/rs6/hubei2.js b/test/rs6/hubei2.js similarity index 100% rename from rs6/hubei2.js rename to test/rs6/hubei2.js diff --git a/rs6/index.js b/test/rs6/index.js similarity index 100% rename from rs6/index.js rename to test/rs6/index.js diff --git a/rs6/shangbiao.js b/test/rs6/shangbiao.js similarity index 100% rename from rs6/shangbiao.js rename to test/rs6/shangbiao.js diff --git a/rs6/yunnan.js b/test/rs6/yunnan.js similarity index 100% rename from rs6/yunnan.js rename to test/rs6/yunnan.js diff --git a/utils/IpUtil.js b/utils/IpUtil.js new file mode 100644 index 0000000..1607e9c --- /dev/null +++ b/utils/IpUtil.js @@ -0,0 +1,19 @@ +const os = require('node:os'); + +class IpUtil { + static networkInterfaces = os.networkInterfaces(); // 私有静态属性,用于缓存网络接口信息 + + static getLocalIPs() { + let ipAddresses = []; + for (const face in this.networkInterfaces) { + for (const address of this.networkInterfaces[face]) { + if (address.family === 'IPv4' && !address.internal) { + ipAddresses.push(address.address); + } + } + } + return ipAddresses; + } +} + +module.exports = IpUtil; diff --git a/utils/Store.js b/utils/Store.js new file mode 100644 index 0000000..a711b10 --- /dev/null +++ b/utils/Store.js @@ -0,0 +1,92 @@ +/** + * // 使用示例 + * async function f() { + * let store = new Store(); + * + * setTimeout(() => { + * store.set('111', '111') + * }, 3000) + * + * console.log('尝试获取值') + * let val = await store.waitGetAndDelete('111',1000,10); + * + * console.log(val) + * console.log(store) + * } + * f() + * @returns {Promise} + */ + +/** + * Store + */ +class Store { + store = new Map() + + sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + + set(key, value) { + this.store.set(key, value) + } + + get(key) { + return this.store.get(key) + } + + /** + * 获取等待 + * @param key + * @param timeout 等待时长 + * @param reTry 重试次数 + * @returns {Promise} + */ + async waitGet(key, timeout = 100, reTry = 10) { + if (timeout <= 0) { + timeout = 10; + } + // 没有时长限制的等待 + if (reTry === -1) { + while (true) { + await this.sleep(timeout) + let val = this.store.get(key); + if (val != null) { + return val; + } + } + } + // 有时长限制的等待 + for (let i = reTry; i > 0; i--) { + let val = this.store.get(key) + if (val != null) { + return val + } + await this.sleep(timeout) + } + return null; + } + + async waitGetAndDelete(key, timeout = 100, reTry = 10) { + let value = await this.waitGet(key, timeout, reTry); + if (value != null) { + this.store.delete(key) + } + return value + } + + getAndDelete(key) { + let value = this.store.get(key); + this.store.delete(key) + return value + } + + has(key) { + return this.store.has(key) + } + + delete(key) { + return this.store.delete(key) + } +} + +module.exports = Store + diff --git a/utils/logger.js b/utils/logger.js deleted file mode 100644 index d9b8059..0000000 --- a/utils/logger.js +++ /dev/null @@ -1,16 +0,0 @@ -// const paths = require('./paths'); -// const pkg = require(paths.package); -// const log4js = require('log4js'); -// -// log4js.configure({ -// appenders: { -// console: { type: 'console' } -// }, -// categories: { -// default: { appenders: ['console'], level: 'info' } -// } -// }); -// const logger = log4js.getLogger(pkg.name); -// logger.level = pkg.logLevel || 'debug'; -// -// module.exports = logger; diff --git a/utils/paths.js b/utils/paths.js deleted file mode 100644 index ff5c843..0000000 --- a/utils/paths.js +++ /dev/null @@ -1,24 +0,0 @@ -// const path = require('path'); -// const fs = require('fs'); -// -// const appDirectory = (() => { -// // 返回项目根目录 -// const plist = path.resolve(__dirname).split(path.sep); -// while (!fs.existsSync(path.resolve(plist.join(path.sep), 'package.json'))) { -// plist.pop(); -// if (plist.length === 0) return false; -// } -// return plist.join(path.sep); -// })(); -// const resolveApp = (...relativePath) => path.resolve(appDirectory, ...relativePath); -// -// module.exports = { -// basePath: resolveApp('.'), -// modulePath: resolveApp('node_modules'), -// binPath: resolveApp('node_modules', '.bin/'), -// package: resolveApp('package.json'), -// resolve: resolveApp, -// handlerPath: resolveApp('handler'), -// configPath: resolveApp('config'), -// configResolve: (...p) => resolveApp('config', ...p), -// };