You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.5 KiB
167 lines
5.5 KiB
const express = require("express"); |
|
const router = express.Router(); |
|
|
|
const {jsdomFromText, browser} = require("sdenv"); |
|
const {Script} = require("node:vm"); |
|
const fs = require("node:fs"); |
|
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) => { |
|
let uuid = crypto.randomUUID().replace(/-/g, ""); |
|
let start = new Date(); |
|
try { |
|
let url = req.body['url']; |
|
let areaName = req.body['areaName']; |
|
let htmlStr = req.body['htmlBase64']; |
|
let jsStr = req.body['jsBase64']; |
|
let cookie = req.body['cookieBase64']; |
|
let userAgent = req.body['userAgentBase64']; |
|
console.log(`${uuid};接收到 ${areaName} 请求:${url}`) |
|
// fs.writeFileSync(`./back/${uuid}.url`, `${url}\n ${userAgent}\n ${cookie} \n`); |
|
if (url == null || url === '') { |
|
return res.status(500).send('error url') |
|
} |
|
if (htmlStr == null || htmlStr === '') { |
|
return res.status(500).send('error html') |
|
} |
|
|
|
let jsText; |
|
let loadHtmlJs; |
|
if (jsStr == null || jsStr === "") { |
|
let jsPath = AreaNameEnum.getByAreaName(areaName).JS_FILE |
|
if (jsPath == null) { |
|
console.error('未找到js文件') |
|
return res.send('未找到js文件') |
|
} |
|
jsText = fs.readFileSync(jsPath).toString('utf8'); |
|
loadHtmlJs = true; |
|
} else { |
|
jsText = Buffer.from(jsStr, 'base64').toString('utf-8') |
|
loadHtmlJs = false |
|
} |
|
|
|
|
|
let cookies = await handle(url, |
|
Buffer.from(htmlStr, 'base64').toString('utf-8'), |
|
jsText, |
|
cookie != null && cookie !== "" ? Buffer.from(cookie, 'base64').toString('utf-8') : null, |
|
userAgent != null && userAgent !== "" ? Buffer.from(userAgent, 'base64').toString('utf-8') : null, |
|
uuid, loadHtmlJs) |
|
|
|
console.log(`${uuid};返回cookie ---->`, cookies.split('; ')) |
|
|
|
res.status(200).send(cookies); |
|
} catch (e) { |
|
console.error(e.message) |
|
return res.status(500).send(e.message) |
|
} finally { |
|
console.log(`${uuid};rsCookie ${new Date() - start} ms`) |
|
} |
|
|
|
}) |
|
|
|
function loadJs(window, jsText) { |
|
// 加载js |
|
let js = ''; |
|
// 加载 页面上的js |
|
const allScript = window.document.querySelectorAll('script[r="m"]'); |
|
for (let i = 0; i < allScript.length; i++) { |
|
const script = allScript[i]; |
|
let attr = script.textContent; |
|
if (attr) { |
|
js += attr |
|
} else { |
|
js += jsText |
|
} |
|
js += ";\n" |
|
} |
|
return js; |
|
} |
|
|
|
function CookieStr2List(cookies) { |
|
let list = [] |
|
for (let cookie of cookies.trim().split("; ")) { |
|
list.push(cookie); |
|
} |
|
return list |
|
} |
|
|
|
async function handle(url, htmlStr, jsText, cookie, userAgent, uuid, loadHtmlJs) { |
|
// 获取 origin |
|
let baseUrl = new URL(url).origin; |
|
// 初始化 jsDom 和 cookieJar |
|
const [jsDom, cookieJar] = await jsdomFromText({ |
|
url: url, |
|
referrer: url, |
|
userAgent: userAgent, |
|
contentType: "text/html", |
|
runScripts: "outside-only", // runScripts: 'dangerously'/'outside-only' |
|
}) |
|
// 设置 cookie |
|
if (cookie != null) { |
|
let cookieList = CookieStr2List(cookie); |
|
console.log(`${uuid};cookie 加载长度--->`, cookieList, baseUrl) |
|
// fs.writeFileSync(`./back/${uuid}.cookie`, cookie) |
|
for (let i = 0; i < cookieList.length; i++) { |
|
cookieJar.setCookieSync(cookieList[i], baseUrl); |
|
} |
|
} |
|
// 设置cookie的回掉 |
|
const superSetCookie = cookieJar.setCookie; |
|
cookieJar.setCookie = function (cookie, currentUrl, options, callback) { |
|
console.log(`${uuid};正在设置 Cookie:`, cookie, currentUrl); |
|
// store.set(uuid, cookie) |
|
return superSetCookie.call(this, cookie, currentUrl, options, callback); |
|
}; |
|
// 加载dom |
|
let dom = await jsDom(htmlStr); |
|
// console.log('html 加载长度--->', dom.serialize()) |
|
console.log(`${uuid};html 加载长度--->`, dom.serialize().length) |
|
// fs.writeFileSync(`./back/${uuid}.html`, dom.serialize()) |
|
|
|
window = dom.window |
|
// js执行成功后会跳转页面 会触发onbeforeunload钩子 |
|
window.onbeforeunload = async (url) => { |
|
const cookies = cookieJar.getCookieStringSync(baseUrl); |
|
console.debug(`${url} 生成cookie:`, cookies); |
|
store.set(uuid, cookies) |
|
// window.close(); |
|
} |
|
// 初始化浏览器 |
|
browser(window, 'chrome'); |
|
// 加载js |
|
let js; |
|
if (loadHtmlJs) { |
|
js = loadJs(window, jsText); |
|
} else { |
|
js = jsText; |
|
} |
|
console.log(`${uuid};js 加载长度--->`, js.length) |
|
// fs.writeFileSync(`./back/${uuid}.js`, js) |
|
|
|
// 执行 js |
|
let script = new Script(js); |
|
let internalVMContext = dom.getInternalVMContext(); |
|
script.runInContext(internalVMContext); |
|
// 等待 onbeforeunload 钩子触发后的回掉 |
|
// let val = await store.waitGetAndDelete(uuid, 100, 10) |
|
|
|
// bug 关闭后 部分参数是需要永久保存在内存中的 比如 cookie 实例 下次调用会报错 |
|
// internalVMContext.close() |
|
// window.close() |
|
// dom = null |
|
|
|
// if (val != null) { |
|
// return val; |
|
// } |
|
// console.log(`${uuid} 执行超时`) |
|
return cookieJar.getCookieStringSync(baseUrl); |
|
// throw new Error(`${uuid}执行超时`) |
|
} |
|
|
|
|
|
module.exports = router |