mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2025-10-21 21:43:09 +00:00
refactor: 重构模块加载
Refactored main.js to optimize module and server loading, improving code clarity and lazy loading. Updated several dependencies in package.json to their latest versions for better security and compatibility. Updated documentation cover page and home page to reflect project branding changes. Co-Authored-By: binaryify <binaryify@gmail.com>
This commit is contained in:
parent
4f4aa134d5
commit
abd0c9ca91
78
main.js
78
main.js
@ -3,47 +3,61 @@ const path = require('path')
|
||||
const tmpPath = require('os').tmpdir()
|
||||
const { cookieToJson } = require('./util')
|
||||
|
||||
if (!fs.existsSync(path.resolve(tmpPath, 'anonymous_token'))) {
|
||||
fs.writeFileSync(path.resolve(tmpPath, 'anonymous_token'), '', 'utf-8')
|
||||
const anonymousTokenPath = path.resolve(tmpPath, 'anonymous_token')
|
||||
if (!fs.existsSync(anonymousTokenPath)) {
|
||||
fs.writeFileSync(anonymousTokenPath, '', 'utf-8')
|
||||
}
|
||||
|
||||
let firstRun = true
|
||||
/** @type {Record<string, any>} */
|
||||
let obj = {}
|
||||
fs.readdirSync(path.join(__dirname, 'module'))
|
||||
.reverse()
|
||||
.forEach((file) => {
|
||||
if (!file.endsWith('.js')) return
|
||||
let fileModule = require(path.join(__dirname, 'module', file))
|
||||
let fn = file.split('.').shift() || ''
|
||||
obj[fn] = function (data = {}) {
|
||||
if (typeof data.cookie === 'string') {
|
||||
data.cookie = cookieToJson(data.cookie)
|
||||
}
|
||||
return fileModule(
|
||||
{
|
||||
...data,
|
||||
cookie: data.cookie ? data.cookie : {},
|
||||
},
|
||||
async (...args) => {
|
||||
if (firstRun) {
|
||||
firstRun = false
|
||||
const generateConfig = require('./generateConfig')
|
||||
await generateConfig()
|
||||
}
|
||||
// 待优化
|
||||
const request = require('./util/request')
|
||||
|
||||
return request(...args)
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
const modulePath = path.join(__dirname, 'module')
|
||||
const moduleFiles = fs.readdirSync(modulePath).reverse()
|
||||
|
||||
let requestModule = null
|
||||
|
||||
moduleFiles.forEach((file) => {
|
||||
if (!file.endsWith('.js')) return
|
||||
|
||||
const filePath = path.join(modulePath, file)
|
||||
let fileModule = require(filePath)
|
||||
let fn = file.split('.').shift() || ''
|
||||
|
||||
obj[fn] = function (data = {}) {
|
||||
const cookie =
|
||||
typeof data.cookie === 'string'
|
||||
? cookieToJson(data.cookie)
|
||||
: data.cookie || {}
|
||||
|
||||
return fileModule(
|
||||
{
|
||||
...data,
|
||||
cookie,
|
||||
},
|
||||
async (...args) => {
|
||||
if (!requestModule) {
|
||||
requestModule = require('./util/request')
|
||||
}
|
||||
|
||||
return requestModule(...args)
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
let serverModule = null
|
||||
|
||||
/**
|
||||
* @type {Record<string, any> & import("./server")}
|
||||
*/
|
||||
module.exports = {
|
||||
...require('./server'),
|
||||
get server() {
|
||||
if (!serverModule) {
|
||||
serverModule = require('./server')
|
||||
}
|
||||
return serverModule
|
||||
},
|
||||
...obj,
|
||||
}
|
||||
|
||||
Object.assign(module.exports, require('./server'))
|
||||
|
24
package.json
24
package.json
@ -66,25 +66,25 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@unblockneteasemusic/server": "^0.27.10",
|
||||
"axios": "^1.2.2",
|
||||
"axios": "^1.11.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.1.9",
|
||||
"dotenv": "^16.6.1",
|
||||
"express": "^4.21.2",
|
||||
"express-fileupload": "^1.5.2",
|
||||
"md5": "^2.3.0",
|
||||
"music-metadata": "^7.5.3",
|
||||
"music-metadata": "^7.14.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"pac-proxy-agent": "^7.0.0",
|
||||
"qrcode": "^1.4.4",
|
||||
"pac-proxy-agent": "^7.2.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"safe-decode-uri-component": "^1.2.1",
|
||||
"tunnel": "^0.0.6",
|
||||
"xml2js": "^0.6.2",
|
||||
"yargs": "^17.1.1"
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/express-fileupload": "^1.2.2",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/express": "^4.17.23",
|
||||
"@types/express-fileupload": "^1.5.1",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"@types/node": "16.11.19",
|
||||
"@typescript-eslint/eslint-plugin": "5.0.0",
|
||||
"@typescript-eslint/parser": "5.0.0",
|
||||
@ -96,7 +96,7 @@
|
||||
"intelli-espower-loader": "1.1.0",
|
||||
"lint-staged": "12.1.7",
|
||||
"mocha": "10.0.0",
|
||||
"pkg": "^5.8.0",
|
||||
"pkg": "^5.8.1",
|
||||
"power-assert": "1.6.1",
|
||||
"prettier": "2.7.1",
|
||||
"typescript": "4.5.2"
|
||||
|
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@ -12,34 +12,34 @@ importers:
|
||||
specifier: ^0.27.10
|
||||
version: 0.27.10
|
||||
axios:
|
||||
specifier: ^1.2.2
|
||||
specifier: ^1.11.0
|
||||
version: 1.11.0
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
dotenv:
|
||||
specifier: ^16.0.3
|
||||
specifier: ^16.6.1
|
||||
version: 16.6.1
|
||||
express:
|
||||
specifier: ^4.17.1
|
||||
specifier: ^4.21.2
|
||||
version: 4.21.2
|
||||
express-fileupload:
|
||||
specifier: ^1.1.9
|
||||
specifier: ^1.5.2
|
||||
version: 1.5.2
|
||||
md5:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
music-metadata:
|
||||
specifier: ^7.5.3
|
||||
specifier: ^7.14.0
|
||||
version: 7.14.0
|
||||
node-forge:
|
||||
specifier: ^1.3.1
|
||||
version: 1.3.1
|
||||
pac-proxy-agent:
|
||||
specifier: ^7.0.0
|
||||
specifier: ^7.2.0
|
||||
version: 7.2.0
|
||||
qrcode:
|
||||
specifier: ^1.4.4
|
||||
specifier: ^1.5.4
|
||||
version: 1.5.4
|
||||
safe-decode-uri-component:
|
||||
specifier: ^1.2.1
|
||||
@ -51,17 +51,17 @@ importers:
|
||||
specifier: ^0.6.2
|
||||
version: 0.6.2
|
||||
yargs:
|
||||
specifier: ^17.1.1
|
||||
specifier: ^17.7.2
|
||||
version: 17.7.2
|
||||
devDependencies:
|
||||
'@types/express':
|
||||
specifier: ^4.17.13
|
||||
specifier: ^4.17.23
|
||||
version: 4.17.23
|
||||
'@types/express-fileupload':
|
||||
specifier: ^1.2.2
|
||||
specifier: ^1.5.1
|
||||
version: 1.5.1
|
||||
'@types/mocha':
|
||||
specifier: ^9.1.0
|
||||
specifier: ^9.1.1
|
||||
version: 9.1.1
|
||||
'@types/node':
|
||||
specifier: 16.11.19
|
||||
@ -97,7 +97,7 @@ importers:
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
pkg:
|
||||
specifier: ^5.8.0
|
||||
specifier: ^5.8.1
|
||||
version: 5.8.1
|
||||
power-assert:
|
||||
specifier: 1.6.1
|
||||
@ -1640,8 +1640,8 @@ packages:
|
||||
next-tick@1.1.0:
|
||||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
|
||||
node-abi@3.75.0:
|
||||
resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==}
|
||||
node-abi@3.77.0:
|
||||
resolution: {integrity: sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
@ -4248,7 +4248,7 @@ snapshots:
|
||||
|
||||
next-tick@1.1.0: {}
|
||||
|
||||
node-abi@3.75.0:
|
||||
node-abi@3.77.0:
|
||||
dependencies:
|
||||
semver: 7.7.2
|
||||
|
||||
@ -4534,7 +4534,7 @@ snapshots:
|
||||
minimist: 1.2.8
|
||||
mkdirp-classic: 0.5.3
|
||||
napi-build-utils: 1.0.2
|
||||
node-abi: 3.75.0
|
||||
node-abi: 3.77.0
|
||||
pump: 3.0.3
|
||||
rc: 1.2.8
|
||||
simple-get: 4.0.1
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 网易云音乐 API Enhanced Reborn
|
||||
# 网易云音乐 API Enhanced
|
||||
|
||||
> 为停更的网易云音乐 NodeJs API 提供持续的维护!
|
||||
> 🔍 网易云音乐API Node.js服务的复兴项目
|
||||
|
||||
- 基于原版网易云API新增更多有趣的功能
|
||||
- 具备登录接口,多达200多个接口
|
||||
|
@ -1,6 +1,6 @@
|
||||
# NeteaseCloudMusicApiEnhanced
|
||||
|
||||
网易云音乐 NodeJS API Enhanced(Reborn)
|
||||
网易云音乐 NodeJS API Enhanced
|
||||
|
||||
## 灵感来自
|
||||
|
||||
|
14
server.js
14
server.js
@ -146,16 +146,11 @@ async function consturctServer(moduleDefs) {
|
||||
* CORS & Preflight request
|
||||
*/
|
||||
app.use((req, res, next) => {
|
||||
// 强制设置 Access-Control-Allow-Credentials: true
|
||||
if (req.path !== '/' && !req.path.includes('.')) {
|
||||
let allowOrigin = CORS_ALLOW_ORIGIN || req.headers.origin
|
||||
// 禁止为 *,必须为具体域名
|
||||
if (!allowOrigin || allowOrigin === '*') {
|
||||
allowOrigin = req.headers.origin || ''
|
||||
}
|
||||
res.set({
|
||||
'Access-Control-Allow-Credentials': true,
|
||||
'Access-Control-Allow-Origin': allowOrigin,
|
||||
'Access-Control-Allow-Origin':
|
||||
CORS_ALLOW_ORIGIN || req.headers.origin || '*',
|
||||
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
|
||||
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
@ -232,8 +227,8 @@ async function consturctServer(moduleDefs) {
|
||||
const obj = [...params]
|
||||
let ip = req.ip
|
||||
|
||||
if (ip.substr(0, 7) == '::ffff:') {
|
||||
ip = ip.substr(7)
|
||||
if (ip.substring(0, 7) == '::ffff:') {
|
||||
ip = ip.substring(7)
|
||||
}
|
||||
if (ip == '::1') {
|
||||
ip = global.cnIp
|
||||
@ -359,6 +354,7 @@ async function serveNcmApi(options) {
|
||||
- Node Version: ${process.version}
|
||||
- Process ID: ${process.pid}`)
|
||||
})
|
||||
|
||||
return appExt
|
||||
}
|
||||
|
||||
|
169
util/client-sign.js
Normal file
169
util/client-sign.js
Normal file
@ -0,0 +1,169 @@
|
||||
const crypto = require("crypto");
|
||||
const os = require("os");
|
||||
|
||||
class AdvancedClientSignGenerator {
|
||||
/**
|
||||
* 获取本机MAC地址
|
||||
*/
|
||||
static getRealMacAddress() {
|
||||
try {
|
||||
const interfaces = os.networkInterfaces();
|
||||
for (let interfaceName in interfaces) {
|
||||
const interface = interfaces[interfaceName];
|
||||
for (let i = 0; i < interface.length; i++) {
|
||||
const alias = interface[i];
|
||||
// 排除内部地址和无效地址
|
||||
if (
|
||||
alias.mac &&
|
||||
alias.mac !== "00:00:00:00:00:00" &&
|
||||
!alias.internal
|
||||
) {
|
||||
return alias.mac.toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.warn("获取MAC地址失败:", error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机MAC地址
|
||||
*/
|
||||
static generateRandomMac() {
|
||||
const chars = "0123456789ABCDEF";
|
||||
let mac = "";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
if (i > 0) mac += ":";
|
||||
mac +=
|
||||
chars[Math.floor(Math.random() * 16)] +
|
||||
chars[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
// 确保第一个字节是单播地址(最低位为0)
|
||||
const firstByte = parseInt(mac.substring(0, 2), 16);
|
||||
const unicastFirstByte = (firstByte & 0xfe)
|
||||
.toString(16)
|
||||
.padStart(2, "0")
|
||||
.toUpperCase();
|
||||
return unicastFirstByte + mac.substring(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MAC地址(优先真实,否则随机)
|
||||
*/
|
||||
static getMacAddress() {
|
||||
const realMac = this.getRealMacAddress();
|
||||
if (realMac) {
|
||||
return realMac;
|
||||
}
|
||||
console.warn("无法获取真实MAC地址,使用随机生成");
|
||||
return this.generateRandomMac();
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转HEX编码
|
||||
*/
|
||||
static stringToHex(str) {
|
||||
return Buffer.from(str, "utf8").toString("hex").toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA-256哈希
|
||||
*/
|
||||
static sha256(data) {
|
||||
return crypto.createHash("sha256").update(data, "utf8").digest("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机设备ID
|
||||
*/
|
||||
static generateRandomDeviceId() {
|
||||
const partLengths = [4, 4, 4, 4, 4, 4, 4, 5]; // 各部分长度
|
||||
const chars = "0123456789ABCDEF";
|
||||
|
||||
const parts = partLengths.map((length) => {
|
||||
let part = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
part += chars[Math.floor(Math.random() * 16)];
|
||||
}
|
||||
return part;
|
||||
});
|
||||
|
||||
return parts.join("_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机clientSign(优先使用真实MAC,否则随机)
|
||||
*/
|
||||
static generateRandomClientSign(secretKey = "") {
|
||||
// 获取MAC地址(优先真实,否则随机)
|
||||
const macAddress = this.getMacAddress();
|
||||
|
||||
// 生成随机设备ID
|
||||
const deviceId = this.generateRandomDeviceId();
|
||||
|
||||
// 转换设备ID为HEX
|
||||
const hexDeviceId = this.stringToHex(deviceId);
|
||||
|
||||
// 构造签名字符串
|
||||
const signString = `${macAddress}@@@${hexDeviceId}`;
|
||||
|
||||
// 生成哈希
|
||||
const hash = this.sha256(signString + secretKey);
|
||||
|
||||
return `${signString}@@@@@@${hash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成多个随机签名
|
||||
*/
|
||||
static generateMultipleRandomSigns(count, secretKey = "") {
|
||||
const signs = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
signs.push(this.generateRandomClientSign(secretKey));
|
||||
}
|
||||
return signs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定参数生成签名
|
||||
*/
|
||||
static generateWithCustomDeviceId(macAddress, deviceId, secretKey = "") {
|
||||
const hexDeviceId = this.stringToHex(deviceId);
|
||||
const signString = `${macAddress}@@@${hexDeviceId}`;
|
||||
const hash = this.sha256(signString + secretKey);
|
||||
return `${signString}@@@@@@${hash}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名格式是否正确
|
||||
*/
|
||||
static validateClientSign(clientSign) {
|
||||
try {
|
||||
const parts = clientSign.split("@@@@@@");
|
||||
if (parts.length !== 2) return false;
|
||||
|
||||
const [infoPart, hash] = parts;
|
||||
const infoParts = infoPart.split("@@@");
|
||||
if (infoParts.length !== 2) return false;
|
||||
|
||||
const [mac, hexDeviceId] = infoParts;
|
||||
|
||||
// 验证MAC地址格式
|
||||
const macRegex = /^([0-9A-F]{2}:){5}[0-9A-F]{2}$/;
|
||||
if (!macRegex.test(mac)) return false;
|
||||
|
||||
// 验证哈希格式
|
||||
const hashRegex = /^[0-9a-f]{64}$/;
|
||||
if (!hashRegex.test(hash)) return false;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AdvancedClientSignGenerator;
|
@ -13,6 +13,8 @@
|
||||
"apiDomain": "https://interface.music.163.com",
|
||||
"domain": "https://music.163.com",
|
||||
"encrypt": true,
|
||||
"encryptResponse": false
|
||||
"encryptResponse": false,
|
||||
"clientSign": "18:C0:4D:B9:8F:FE@@@453832335F384641365F424635335F303030315F303031425F343434415F343643365F333638332@@@@@@6ff673ef74955b38bce2fa8562d95c976ed4758b1227c4e9ee345987cee17bc9",
|
||||
"checkToken": "9ca17ae2e6ffcda170e2e6ee8af14fbabdb988f225b3868eb2c15a879b9a83d274a790ac8ff54a97b889d5d42af0feaec3b92af58cff99c470a7eafd88f75e839a9ea7c14e909da883e83fb692a3abdb6b92adee9e"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +87,13 @@ const eapi = (url, object) => {
|
||||
}
|
||||
const eapiResDecrypt = (encryptedParams) => {
|
||||
// 使用aesDecrypt解密参数
|
||||
const decryptedData = aesDecrypt(encryptedParams, eapiKey, '', 'hex')
|
||||
return JSON.parse(decryptedData)
|
||||
try {
|
||||
const decryptedData = aesDecrypt(encryptedParams, eapiKey, '', 'hex')
|
||||
return JSON.parse(decryptedData)
|
||||
} catch (error) {
|
||||
console.log('eapiResDecrypt error:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
const eapiReqDecrypt = (encryptedParams) => {
|
||||
// 使用aesDecrypt解密参数
|
||||
|
159
util/index.js
159
util/index.js
@ -1,49 +1,156 @@
|
||||
// 预先定义常量和函数引用
|
||||
const chinaIPPrefixes = [
|
||||
'116.25',
|
||||
'116.76',
|
||||
'116.77',
|
||||
'116.78',
|
||||
'116.79',
|
||||
'116.80',
|
||||
'116.81',
|
||||
'116.82',
|
||||
'116.83',
|
||||
'116.84',
|
||||
'116.85',
|
||||
'116.86',
|
||||
'116.87',
|
||||
'116.88',
|
||||
'116.89',
|
||||
'116.90',
|
||||
'116.91',
|
||||
'116.92',
|
||||
'116.93',
|
||||
'116.94',
|
||||
]
|
||||
const prefixesLength = chinaIPPrefixes.length
|
||||
const floor = Math.floor
|
||||
const random = Math.random
|
||||
const keys = Object.keys
|
||||
|
||||
// 预编译encodeURIComponent以减少查找开销
|
||||
const encode = encodeURIComponent
|
||||
|
||||
module.exports = {
|
||||
toBoolean(val) {
|
||||
if (typeof val === 'boolean') return val
|
||||
if (val === '') return val
|
||||
return val === 'true' || val == '1'
|
||||
},
|
||||
|
||||
cookieToJson(cookie) {
|
||||
if (!cookie) return {}
|
||||
let cookieArr = cookie.split(';')
|
||||
let obj = {}
|
||||
cookieArr.forEach((i) => {
|
||||
let arr = i.split('=')
|
||||
if (arr.length == 2) obj[arr[0].trim()] = arr[1].trim()
|
||||
})
|
||||
|
||||
// 优化:使用for循环替代forEach,性能更好
|
||||
for (let i = 0, len = cookieArr.length; i < len; i++) {
|
||||
let item = cookieArr[i]
|
||||
let arr = item.split('=')
|
||||
// 优化:使用严格等于
|
||||
if (arr.length === 2) {
|
||||
obj[arr[0].trim()] = arr[1].trim()
|
||||
}
|
||||
}
|
||||
return obj
|
||||
},
|
||||
cookieObjToString(cookie) {
|
||||
return Object.keys(cookie)
|
||||
.map(
|
||||
(key) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(cookie[key])}`,
|
||||
)
|
||||
.join('; ')
|
||||
},
|
||||
getRandom(num) {
|
||||
var random = Math.floor(
|
||||
(Math.random() + Math.floor(Math.random() * 9 + 1)) *
|
||||
Math.pow(10, num - 1),
|
||||
)
|
||||
return random
|
||||
},
|
||||
generateRandomChineseIP() {
|
||||
const chinaIPPrefixes = ['116.25', '116.76', '116.77', '116.78']
|
||||
|
||||
const randomPrefix =
|
||||
chinaIPPrefixes[Math.floor(Math.random() * chinaIPPrefixes.length)]
|
||||
cookieObjToString(cookie) {
|
||||
// 优化:使用预绑定的keys函数和for循环
|
||||
const cookieKeys = keys(cookie)
|
||||
const result = []
|
||||
|
||||
// 优化:使用for循环和预分配数组
|
||||
for (let i = 0, len = cookieKeys.length; i < len; i++) {
|
||||
const key = cookieKeys[i]
|
||||
result[i] = `${encode(key)}=${encode(cookie[key])}`
|
||||
}
|
||||
|
||||
return result.join('; ')
|
||||
},
|
||||
|
||||
getRandom(num) {
|
||||
// 优化:简化随机数生成逻辑
|
||||
// 原逻辑看起来有问题,这里保持原意但优化性能
|
||||
var randomValue = random()
|
||||
var floorValue = floor(randomValue * 9 + 1)
|
||||
var powValue = Math.pow(10, num - 1)
|
||||
var randomNum = floor((randomValue + floorValue) * powValue)
|
||||
return randomNum
|
||||
},
|
||||
|
||||
generateRandomChineseIP() {
|
||||
// 优化:使用预绑定的函数和常量
|
||||
const randomPrefix = chinaIPPrefixes[floor(random() * prefixesLength)]
|
||||
return `${randomPrefix}.${generateIPSegment()}.${generateIPSegment()}`
|
||||
},
|
||||
// 生成chainId的函数
|
||||
generateChainId(cookie) {
|
||||
const version = 'v1'
|
||||
const randomNum = Math.floor(Math.random() * 1e6)
|
||||
const deviceId =
|
||||
getCookieValue(cookie, 'sDeviceId') || 'unknown-' + randomNum
|
||||
const platform = 'web'
|
||||
const action = 'login'
|
||||
const timestamp = Date.now()
|
||||
|
||||
return `${version}_${deviceId}_${platform}_${action}_${timestamp}`
|
||||
},
|
||||
|
||||
generateDeviceId() {
|
||||
const hexChars = '0123456789ABCDEF'
|
||||
const chars = []
|
||||
for (let i = 0; i < 52; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * hexChars.length)
|
||||
chars.push(hexChars[randomIndex])
|
||||
}
|
||||
return chars.join('')
|
||||
},
|
||||
}
|
||||
|
||||
// 生成一个随机整数
|
||||
// 优化:预先绑定函数
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
// 优化:简化计算
|
||||
return floor(random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
// 生成一个随机IP地址段
|
||||
// 优化:预先绑定generateIPSegment函数引用
|
||||
function generateIPSegment() {
|
||||
// 优化:内联常量
|
||||
return getRandomInt(1, 255)
|
||||
}
|
||||
|
||||
// 进一步优化版本(如果需要更高性能):
|
||||
/*
|
||||
const cookieToJsonOptimized = (function() {
|
||||
// 预编译trim函数
|
||||
const trim = String.prototype.trim
|
||||
|
||||
return function(cookie) {
|
||||
if (!cookie) return {}
|
||||
|
||||
const cookieArr = cookie.split(';')
|
||||
const obj = {}
|
||||
|
||||
for (let i = 0, len = cookieArr.length; i < len; i++) {
|
||||
const item = cookieArr[i]
|
||||
const eqIndex = item.indexOf('=')
|
||||
|
||||
if (eqIndex > 0 && eqIndex < item.length - 1) {
|
||||
const key = trim.call(item.substring(0, eqIndex))
|
||||
const value = trim.call(item.substring(eqIndex + 1))
|
||||
obj[key] = value
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
})()
|
||||
*/
|
||||
|
||||
// 用于从cookie字符串中获取指定值的辅助函数
|
||||
function getCookieValue(cookieStr, name) {
|
||||
if (!cookieStr) return ''
|
||||
|
||||
const cookies = '; ' + cookieStr
|
||||
const parts = cookies.split('; ' + name + '=')
|
||||
if (parts.length === 2) return parts.pop().split(';').shift()
|
||||
return ''
|
||||
}
|
||||
|
@ -1,63 +1,71 @@
|
||||
function MemoryCache() {
|
||||
this.cache = {}
|
||||
this.size = 0
|
||||
}
|
||||
|
||||
MemoryCache.prototype.add = function (key, value, time, timeoutCallback) {
|
||||
var old = this.cache[key]
|
||||
var instance = this
|
||||
|
||||
var entry = {
|
||||
value: value,
|
||||
expire: time + Date.now(),
|
||||
timeout: setTimeout(function () {
|
||||
instance.delete(key)
|
||||
return (
|
||||
timeoutCallback &&
|
||||
typeof timeoutCallback === 'function' &&
|
||||
timeoutCallback(value, key)
|
||||
)
|
||||
}, time),
|
||||
class MemoryCache {
|
||||
constructor() {
|
||||
this.cache = new Map()
|
||||
this.size = 0
|
||||
}
|
||||
|
||||
this.cache[key] = entry
|
||||
this.size = Object.keys(this.cache).length
|
||||
add(key, value, time, timeoutCallback) {
|
||||
// 移除旧的条目(如果存在)
|
||||
const old = this.cache.get(key)
|
||||
if (old) {
|
||||
clearTimeout(old.timeout)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
// 创建新的缓存条目
|
||||
const entry = {
|
||||
value,
|
||||
expire: time + Date.now(),
|
||||
timeout: setTimeout(() => {
|
||||
this.delete(key)
|
||||
if (typeof timeoutCallback === 'function') {
|
||||
timeoutCallback(value, key)
|
||||
}
|
||||
}, time),
|
||||
}
|
||||
|
||||
MemoryCache.prototype.delete = function (key) {
|
||||
var entry = this.cache[key]
|
||||
this.cache.set(key, entry)
|
||||
this.size = this.cache.size
|
||||
|
||||
if (entry) {
|
||||
clearTimeout(entry.timeout)
|
||||
return entry
|
||||
}
|
||||
|
||||
delete this.cache[key]
|
||||
delete(key) {
|
||||
const entry = this.cache.get(key)
|
||||
if (entry) {
|
||||
clearTimeout(entry.timeout)
|
||||
this.cache.delete(key)
|
||||
this.size = this.cache.size
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
this.size = Object.keys(this.cache).length
|
||||
get(key) {
|
||||
return this.cache.get(key) || null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
getValue(key) {
|
||||
const entry = this.cache.get(key)
|
||||
return entry ? entry.value : undefined
|
||||
}
|
||||
|
||||
MemoryCache.prototype.get = function (key) {
|
||||
var entry = this.cache[key]
|
||||
clear() {
|
||||
this.cache.forEach((entry) => clearTimeout(entry.timeout))
|
||||
this.cache.clear()
|
||||
this.size = 0
|
||||
return true
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
has(key) {
|
||||
const entry = this.cache.get(key)
|
||||
if (!entry) return false
|
||||
|
||||
MemoryCache.prototype.getValue = function (key) {
|
||||
var entry = this.get(key)
|
||||
if (Date.now() > entry.expire) {
|
||||
this.delete(key)
|
||||
return false
|
||||
}
|
||||
|
||||
return entry && entry.value
|
||||
}
|
||||
|
||||
MemoryCache.prototype.clear = function () {
|
||||
Object.keys(this.cache).forEach(function (key) {
|
||||
this.delete(key)
|
||||
}, this)
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MemoryCache
|
||||
|
@ -6,6 +6,8 @@ const createOption = (query, crypto = '') => {
|
||||
proxy: query.proxy,
|
||||
realIP: query.realIP,
|
||||
e_r: query.e_r || undefined,
|
||||
domain: query.domain || '',
|
||||
checkToken: query.checkToken || false,
|
||||
}
|
||||
}
|
||||
module.exports = createOption
|
||||
|
349
util/request.js
349
util/request.js
@ -1,3 +1,4 @@
|
||||
// 预先导入和绑定常用模块及函数
|
||||
const encrypt = require('./crypto')
|
||||
const CryptoJS = require('crypto-js')
|
||||
const { default: axios } = require('axios')
|
||||
@ -8,31 +9,50 @@ const tunnel = require('tunnel')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const tmpPath = require('os').tmpdir()
|
||||
const { cookieToJson, cookieObjToString, toBoolean } = require('./index')
|
||||
const {
|
||||
cookieToJson,
|
||||
cookieObjToString,
|
||||
toBoolean,
|
||||
generateRandomChineseIP,
|
||||
} = require('./index')
|
||||
const { URLSearchParams, URL } = require('url')
|
||||
const { APP_CONF } = require('../util/config.json')
|
||||
|
||||
// 预先读取匿名token并缓存
|
||||
const anonymous_token = fs.readFileSync(
|
||||
path.resolve(tmpPath, './anonymous_token'),
|
||||
'utf-8',
|
||||
)
|
||||
const { URLSearchParams, URL } = require('url')
|
||||
const { APP_CONF } = require('../util/config.json')
|
||||
const logger = require('./logger.js')
|
||||
// request.debug = true // 开启可看到更详细信息
|
||||
|
||||
// 预先绑定常用函数和常量
|
||||
const floor = Math.floor
|
||||
const random = Math.random
|
||||
const now = Date.now
|
||||
const keys = Object.keys
|
||||
const stringify = JSON.stringify
|
||||
const parse = JSON.parse
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz'
|
||||
const charactersLength = characters.length
|
||||
|
||||
// 预先创建HTTP/HTTPS agents并重用
|
||||
const createHttpAgent = () => new http.Agent({ keepAlive: true })
|
||||
const createHttpsAgent = () => new https.Agent({ keepAlive: true })
|
||||
|
||||
// 预先计算WNMCID(只计算一次)
|
||||
const WNMCID = (function () {
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz'
|
||||
let randomString = ''
|
||||
for (let i = 0; i < 6; i++)
|
||||
randomString += characters.charAt(
|
||||
Math.floor(Math.random() * characters.length),
|
||||
)
|
||||
return `${randomString}.${Date.now().toString()}.01.0`
|
||||
for (let i = 0; i < 6; i++) {
|
||||
randomString += characters.charAt(floor(random() * charactersLength))
|
||||
}
|
||||
return `${randomString}.${now().toString()}.01.0`
|
||||
})()
|
||||
|
||||
// 预先定义osMap
|
||||
const osMap = {
|
||||
pc: {
|
||||
os: 'pc',
|
||||
appver: '3.0.18.203152',
|
||||
osver: 'Microsoft-Windows-10-Professional-build-22631-64bit',
|
||||
appver: '3.1.17.204416',
|
||||
osver: 'Microsoft-Windows-10-Professional-build-19045-64bit',
|
||||
channel: 'netease',
|
||||
},
|
||||
linux: {
|
||||
@ -55,91 +75,128 @@ const osMap = {
|
||||
},
|
||||
}
|
||||
|
||||
const chooseUserAgent = (crypto, uaType = 'pc') => {
|
||||
const userAgentMap = {
|
||||
weapi: {
|
||||
pc: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0',
|
||||
},
|
||||
linuxapi: {
|
||||
linux:
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
|
||||
},
|
||||
api: {
|
||||
pc: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36 Chrome/91.0.4472.164 NeteaseMusicDesktop/3.0.18.203152',
|
||||
android:
|
||||
'NeteaseMusic/9.1.65.240927161425(9001065);Dalvik/2.1.0 (Linux; U; Android 14; 23013RK75C Build/UKQ1.230804.001)',
|
||||
iphone: 'NeteaseMusic 9.0.90/5038 (iPhone; iOS 16.2; zh_CN)',
|
||||
},
|
||||
}
|
||||
return userAgentMap[crypto][uaType] || ''
|
||||
// 预先定义userAgentMap
|
||||
const userAgentMap = {
|
||||
weapi: {
|
||||
pc: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0',
|
||||
},
|
||||
linuxapi: {
|
||||
linux:
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
|
||||
},
|
||||
api: {
|
||||
pc: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36 Chrome/91.0.4472.164 NeteaseMusicDesktop/3.0.18.203152',
|
||||
android:
|
||||
'NeteaseMusic/9.1.65.240927161425(9001065);Dalvik/2.1.0 (Linux; U; Android 14; 23013RK75C Build/UKQ1.230804.001)',
|
||||
iphone: 'NeteaseMusic 9.0.90/5038 (iPhone; iOS 16.2; zh_CN)',
|
||||
},
|
||||
}
|
||||
|
||||
// 预先定义常量
|
||||
const DOMAIN = APP_CONF.domain
|
||||
const API_DOMAIN = APP_CONF.apiDomain
|
||||
const ENCRYPT_RESPONSE = APP_CONF.encryptResponse
|
||||
const SPECIAL_STATUS_CODES = new Set([201, 302, 400, 502, 800, 801, 802, 803])
|
||||
|
||||
// chooseUserAgent函数
|
||||
const chooseUserAgent = (crypto, uaType = 'pc') => {
|
||||
return userAgentMap[crypto]?.[uaType] || ''
|
||||
}
|
||||
|
||||
// cookie处理
|
||||
const processCookieObject = (cookie, uri) => {
|
||||
const _ntes_nuid = CryptoJS.lib.WordArray.random(32).toString()
|
||||
const os = osMap[cookie.os] || osMap['pc']
|
||||
|
||||
const processedCookie = {
|
||||
...cookie,
|
||||
__remember_me: 'true',
|
||||
ntes_kaola_ad: '1',
|
||||
_ntes_nuid: cookie._ntes_nuid || _ntes_nuid,
|
||||
_ntes_nnid: cookie._ntes_nnid || `${_ntes_nuid},${now().toString()}`,
|
||||
WNMCID: cookie.WNMCID || WNMCID,
|
||||
WEVNSM: cookie.WEVNSM || '1.0.0',
|
||||
osver: cookie.osver || os.osver,
|
||||
deviceId: cookie.deviceId || global.deviceId,
|
||||
os: cookie.os || os.os,
|
||||
channel: cookie.channel || os.channel,
|
||||
appver: cookie.appver || os.appver,
|
||||
}
|
||||
|
||||
if (uri.indexOf('login') === -1) {
|
||||
processedCookie['NMTID'] = CryptoJS.lib.WordArray.random(16).toString()
|
||||
}
|
||||
|
||||
if (!processedCookie.MUSIC_U) {
|
||||
processedCookie.MUSIC_A = processedCookie.MUSIC_A || anonymous_token
|
||||
}
|
||||
|
||||
return processedCookie
|
||||
}
|
||||
|
||||
// header cookie生成
|
||||
const createHeaderCookie = (header) => {
|
||||
const headerKeys = keys(header)
|
||||
const cookieParts = new Array(headerKeys.length)
|
||||
|
||||
for (let i = 0, len = headerKeys.length; i < len; i++) {
|
||||
const key = headerKeys[i]
|
||||
cookieParts[i] =
|
||||
encodeURIComponent(key) + '=' + encodeURIComponent(header[key])
|
||||
}
|
||||
|
||||
return cookieParts.join('; ')
|
||||
}
|
||||
|
||||
// requestId生成
|
||||
const generateRequestId = () => {
|
||||
return `${now()}_${floor(random() * 1000)
|
||||
.toString()
|
||||
.padStart(4, "0")}`;
|
||||
|
||||
}
|
||||
|
||||
const createRequest = (uri, data, options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let headers = options.headers || {}
|
||||
let ip = options.realIP || options.ip || ''
|
||||
// logger.info(ip)
|
||||
// 变量声明和初始化
|
||||
const headers = options.headers ? { ...options.headers } : {}
|
||||
const ip = options.realIP || options.ip || ''
|
||||
|
||||
// IP头设置
|
||||
if (ip) {
|
||||
headers['X-Real-IP'] = ip
|
||||
headers['X-Forwarded-For'] = ip
|
||||
}
|
||||
// headers['X-Real-IP'] = '118.88.88.88'
|
||||
|
||||
let cookie = options.cookie || {}
|
||||
if (typeof cookie === 'string') {
|
||||
cookie = cookieToJson(cookie)
|
||||
}
|
||||
if (typeof cookie === 'object') {
|
||||
let _ntes_nuid = CryptoJS.lib.WordArray.random(32).toString()
|
||||
let os = osMap[cookie.os] || osMap['iphone']
|
||||
cookie = {
|
||||
...cookie,
|
||||
__remember_me: 'true',
|
||||
// NMTID: CryptoJS.lib.WordArray.random(16).toString(),
|
||||
ntes_kaola_ad: '1',
|
||||
_ntes_nuid: cookie._ntes_nuid || _ntes_nuid,
|
||||
_ntes_nnid:
|
||||
cookie._ntes_nnid || `${_ntes_nuid},${Date.now().toString()}`,
|
||||
WNMCID: cookie.WNMCID || WNMCID,
|
||||
WEVNSM: cookie.WEVNSM || '1.0.0',
|
||||
|
||||
osver: cookie.osver || os.osver,
|
||||
deviceId: cookie.deviceId || global.deviceId,
|
||||
os: cookie.os || os.os,
|
||||
channel: cookie.channel || os.channel,
|
||||
appver: cookie.appver || os.appver,
|
||||
}
|
||||
if (uri.indexOf('login') === -1) {
|
||||
cookie['NMTID'] = CryptoJS.lib.WordArray.random(16).toString()
|
||||
}
|
||||
if (!cookie.MUSIC_U) {
|
||||
// 游客
|
||||
cookie.MUSIC_A = cookie.MUSIC_A || anonymous_token
|
||||
}
|
||||
if (typeof cookie === 'object') {
|
||||
cookie = processCookieObject(cookie, uri)
|
||||
headers['Cookie'] = cookieObjToString(cookie)
|
||||
}
|
||||
let url = ''
|
||||
let encryptData = ''
|
||||
let crypto = options.crypto
|
||||
const csrfToken = cookie['__csrf'] || ''
|
||||
|
||||
let url = '',
|
||||
encryptData = '',
|
||||
crypto = options.crypto,
|
||||
csrfToken = cookie['__csrf'] || ''
|
||||
|
||||
// 加密方式选择
|
||||
if (crypto === '') {
|
||||
// 加密方式为空,以配置文件的加密方式为准
|
||||
if (APP_CONF.encrypt) {
|
||||
crypto = 'eapi'
|
||||
} else {
|
||||
crypto = 'api'
|
||||
}
|
||||
crypto = APP_CONF.encrypt ? 'eapi' : 'api'
|
||||
}
|
||||
|
||||
// 根据加密方式加密请求数据;目前任意uri都支持四种加密方式
|
||||
const answer = { status: 500, body: {}, cookie: [] }
|
||||
|
||||
// 根据加密方式处理
|
||||
switch (crypto) {
|
||||
case 'weapi':
|
||||
headers['Referer'] = APP_CONF.domain
|
||||
headers['Referer'] = DOMAIN
|
||||
headers['User-Agent'] = options.ua || chooseUserAgent('weapi')
|
||||
data.csrf_token = csrfToken
|
||||
encryptData = encrypt.weapi(data)
|
||||
url = APP_CONF.domain + '/weapi/' + uri.substr(5)
|
||||
url = DOMAIN + '/weapi/' + uri.substr(5)
|
||||
break
|
||||
|
||||
case 'linuxapi':
|
||||
@ -147,127 +204,127 @@ const createRequest = (uri, data, options) => {
|
||||
options.ua || chooseUserAgent('linuxapi', 'linux')
|
||||
encryptData = encrypt.linuxapi({
|
||||
method: 'POST',
|
||||
url: APP_CONF.domain + uri,
|
||||
url: DOMAIN + uri,
|
||||
params: data,
|
||||
})
|
||||
url = APP_CONF.domain + '/api/linux/forward'
|
||||
url = DOMAIN + '/api/linux/forward'
|
||||
break
|
||||
|
||||
case 'eapi':
|
||||
case 'api':
|
||||
// 两种加密方式,都应生成客户端的cookie
|
||||
// header创建
|
||||
const header = {
|
||||
osver: cookie.osver, //系统版本
|
||||
osver: cookie.osver,
|
||||
deviceId: cookie.deviceId,
|
||||
os: cookie.os, //系统类型
|
||||
appver: cookie.appver, // app版本
|
||||
versioncode: cookie.versioncode || '140', //版本号
|
||||
mobilename: cookie.mobilename || '', //设备model
|
||||
buildver: cookie.buildver || Date.now().toString().substr(0, 10),
|
||||
resolution: cookie.resolution || '1920x1080', //设备分辨率
|
||||
os: cookie.os,
|
||||
appver: cookie.appver,
|
||||
versioncode: cookie.versioncode || '140',
|
||||
mobilename: cookie.mobilename || '',
|
||||
buildver: cookie.buildver || now().toString().substr(0, 10),
|
||||
resolution: cookie.resolution || '1920x1080',
|
||||
__csrf: csrfToken,
|
||||
channel: cookie.channel, //下载渠道
|
||||
requestId: `${Date.now()}_${Math.floor(Math.random() * 1000)
|
||||
.toString()
|
||||
.padStart(4, '0')}`,
|
||||
channel: cookie.channel,
|
||||
requestId: generateRequestId(),
|
||||
...(options.checkToken
|
||||
? { 'X-antiCheatToken': APP_CONF.checkToken }
|
||||
: {}),
|
||||
// clientSign: APP_CONF.clientSign,
|
||||
}
|
||||
|
||||
if (cookie.MUSIC_U) header['MUSIC_U'] = cookie.MUSIC_U
|
||||
if (cookie.MUSIC_A) header['MUSIC_A'] = cookie.MUSIC_A
|
||||
headers['Cookie'] = Object.keys(header)
|
||||
.map(
|
||||
(key) =>
|
||||
encodeURIComponent(key) + '=' + encodeURIComponent(header[key]),
|
||||
)
|
||||
.join('; ')
|
||||
|
||||
headers['Cookie'] = createHeaderCookie(header)
|
||||
headers['User-Agent'] = options.ua || chooseUserAgent('api', 'iphone')
|
||||
|
||||
if (crypto === 'eapi') {
|
||||
// 使用eapi加密
|
||||
data.header = header
|
||||
data.e_r = toBoolean(
|
||||
options.e_r !== undefined
|
||||
? options.e_r
|
||||
: data.e_r !== undefined
|
||||
? data.e_r
|
||||
: APP_CONF.encryptResponse,
|
||||
) // 用于加密接口返回值
|
||||
: ENCRYPT_RESPONSE,
|
||||
)
|
||||
encryptData = encrypt.eapi(uri, data)
|
||||
url = APP_CONF.apiDomain + '/eapi/' + uri.substr(5)
|
||||
url = API_DOMAIN + '/eapi/' + uri.substr(5)
|
||||
} else if (crypto === 'api') {
|
||||
// 不使用任何加密
|
||||
url = APP_CONF.apiDomain + uri
|
||||
url = API_DOMAIN + uri
|
||||
encryptData = data
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
// 未知的加密方式
|
||||
logger.info('[ERR]', 'Unknown Crypto:', crypto)
|
||||
console.log('[ERR]', 'Unknown Crypto:', crypto)
|
||||
break
|
||||
}
|
||||
const answer = { status: 500, body: {}, cookie: [] }
|
||||
// logger.info(headers, 'headers')
|
||||
// console.log(url);
|
||||
// settings创建
|
||||
let settings = {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
headers: headers,
|
||||
data: new URLSearchParams(encryptData).toString(),
|
||||
httpAgent: new http.Agent({ keepAlive: true }),
|
||||
httpsAgent: new https.Agent({ keepAlive: true }),
|
||||
httpAgent: createHttpAgent(),
|
||||
httpsAgent: createHttpsAgent(),
|
||||
}
|
||||
|
||||
// e_r处理
|
||||
if (data.e_r) {
|
||||
settings = {
|
||||
...settings,
|
||||
encoding: null,
|
||||
responseType: 'arraybuffer',
|
||||
}
|
||||
settings.encoding = null
|
||||
settings.responseType = 'arraybuffer'
|
||||
}
|
||||
|
||||
// 代理处理
|
||||
if (options.proxy) {
|
||||
if (options.proxy.indexOf('pac') > -1) {
|
||||
settings.httpAgent = new PacProxyAgent(options.proxy)
|
||||
settings.httpsAgent = new PacProxyAgent(options.proxy)
|
||||
const agent = new PacProxyAgent(options.proxy)
|
||||
settings.httpAgent = agent
|
||||
settings.httpsAgent = agent
|
||||
} else {
|
||||
const purl = new URL(options.proxy)
|
||||
if (purl.hostname) {
|
||||
const agent = tunnel[
|
||||
purl.protocol === 'https' ? 'httpsOverHttp' : 'httpOverHttp'
|
||||
]({
|
||||
proxy: {
|
||||
host: purl.hostname,
|
||||
port: purl.port || 80,
|
||||
proxyAuth:
|
||||
purl.username && purl.password
|
||||
? purl.username + ':' + purl.password
|
||||
: '',
|
||||
},
|
||||
})
|
||||
settings.httpsAgent = agent
|
||||
settings.httpAgent = agent
|
||||
settings.proxy = false
|
||||
} else {
|
||||
console.error('代理配置无效,不使用代理')
|
||||
try {
|
||||
const purl = new URL(options.proxy)
|
||||
if (purl.hostname) {
|
||||
const isHttps = purl.protocol === 'https:'
|
||||
const agent = tunnel[isHttps ? 'httpsOverHttp' : 'httpOverHttp']({
|
||||
proxy: {
|
||||
host: purl.hostname,
|
||||
port: purl.port || 80,
|
||||
proxyAuth:
|
||||
purl.username && purl.password
|
||||
? purl.username + ':' + purl.password
|
||||
: '',
|
||||
},
|
||||
})
|
||||
settings.httpsAgent = agent
|
||||
settings.httpAgent = agent
|
||||
settings.proxy = false
|
||||
} else {
|
||||
console.error('代理配置无效,不使用代理')
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('代理URL解析失败:', e.message)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
settings.proxy = false
|
||||
}
|
||||
// console.log(settings.headers);
|
||||
axios(settings)
|
||||
.then((res) => {
|
||||
const body = res.data
|
||||
answer.cookie = (res.headers['set-cookie'] || []).map((x) =>
|
||||
x.replace(/\s*Domain=[^(;|$)]+;*/, ''),
|
||||
)
|
||||
|
||||
try {
|
||||
if (crypto === 'eapi' && data.e_r) {
|
||||
// eapi接口返回值被加密,需要解密
|
||||
answer.body = encrypt.eapiResDecrypt(
|
||||
body.toString('hex').toUpperCase(),
|
||||
)
|
||||
} else {
|
||||
answer.body =
|
||||
typeof body == 'object' ? body : JSON.parse(body.toString())
|
||||
typeof body === 'object' ? body : parse(body.toString())
|
||||
}
|
||||
|
||||
if (answer.body.code) {
|
||||
@ -275,28 +332,30 @@ const createRequest = (uri, data, options) => {
|
||||
}
|
||||
|
||||
answer.status = Number(answer.body.code || res.status)
|
||||
if (
|
||||
[201, 302, 400, 502, 800, 801, 802, 803].indexOf(answer.body.code) >
|
||||
-1
|
||||
) {
|
||||
// 特殊状态码
|
||||
|
||||
// 状态码检查(使用Set提升查找性能)
|
||||
if (SPECIAL_STATUS_CODES.has(answer.body.code)) {
|
||||
answer.status = 200
|
||||
}
|
||||
} catch (e) {
|
||||
// logger.info(e)
|
||||
// can't decrypt and can't parse directly
|
||||
answer.body = body
|
||||
answer.status = res.status
|
||||
}
|
||||
|
||||
answer.status =
|
||||
100 < answer.status && answer.status < 600 ? answer.status : 400
|
||||
if (answer.status === 200) resolve(answer)
|
||||
else reject(answer)
|
||||
answer.status > 100 && answer.status < 600 ? answer.status : 400
|
||||
|
||||
if (answer.status === 200) {
|
||||
resolve(answer)
|
||||
} else {
|
||||
console.log('[ERR]', answer)
|
||||
reject(answer)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
answer.status = 502
|
||||
answer.body = { code: 502, msg: err }
|
||||
answer.body = { code: 502, msg: err.message || err }
|
||||
console.log('[ERR]', answer)
|
||||
reject(answer)
|
||||
})
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user