master
刘东琪 9 months ago
parent 3ad6a21e87
commit a6fd16d542
  1. 15
      app.js
  2. 2
      deploy.sh
  3. 17
      ecosystem.config.js
  4. 40
      enums/AreaNameEnum.js
  5. 89
      routes/index.js
  6. 0
      test/rs6/hubei.js
  7. 0
      test/rs6/hubei2.js
  8. 0
      test/rs6/index.js
  9. 0
      test/rs6/shangbiao.js
  10. 0
      test/rs6/yunnan.js
  11. 19
      utils/IpUtil.js
  12. 92
      utils/Store.js
  13. 16
      utils/logger.js
  14. 24
      utils/paths.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}`)
}
});

@ -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

@ -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, // 自定义环境变量示例
},
},
],
};

@ -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;

@ -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('执行超时')
}

@ -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;

@ -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<void>}
*/
/**
* 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<any|null>}
*/
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

@ -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;

@ -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),
// };
Loading…
Cancel
Save