| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- const axios = require('axios')
- const https = require('https')
- const path = require('path')
- const Logger = require('../../lib/Logger')
- const { getJkesSettings, normalizeApiBase } = require('./jkesSettings')
- const logger = new Logger(path.join(__dirname, '../../logs/JKES.log'), 'INFO')
- let jkesHttpsAgent
- function getJkesHttpsAgent() {
- if (!jkesHttpsAgent) {
- const s = getJkesSettings()
- jkesHttpsAgent = new https.Agent({
- keepAlive: true,
- minVersion: 'TLSv1.2',
- rejectUnauthorized: s.tlsRejectUnauthorized !== false
- })
- }
- return jkesHttpsAgent
- }
- function buildJkesHeaders(token) {
- const tokenClean = String(token ?? '').trim()
- const s = getJkesSettings()
- return {
- 'x-auth-token': tokenClean,
- 'content-type': 'application/json',
- 'Accept-Encoding': 'gzip,compress,br,deflate',
- 'User-Agent': s.userAgent,
- Referer: s.referer
- }
- }
- function isJkesLoginExpiredPayload(payload) {
- if (!payload || typeof payload !== 'object') return false
- const code = Number(payload.code)
- const msg = String(payload.message ?? payload.msg ?? '').trim()
- return (
- (code === 500 && msg.includes('无效用户')) ||
- (code === 401 && msg.includes('您的认证已经失效'))
- )
- }
- function makeJkesLoginExpiredError(payload) {
- const msg = '乐跑账号登录失效,请重新使用乐跑登录器进行登录'
- const err = new Error(msg)
- err.code = 'JKES_AUTH_EXPIRED'
- err.loginExpired = true
- err.retryable = false
- err.payload = payload
- return err
- }
- /**
- * @param {string} url 如 /sys/user/getMyInfo
- * @param {object} [data] POST body
- * @param {string} token
- */
- async function jkesRequest(url, data, token) {
- const tokenClean = String(token ?? '').trim()
- if (!tokenClean) {
- logger.error('[JKES] 缺少 token')
- return null
- }
- const s = getJkesSettings()
- const pathPart = url.startsWith('/') ? url : `/${url}`
- const fullUrl = `${normalizeApiBase(s.apiBase)}${pathPart}`
- const body = data === undefined || data === null ? {} : data
- let parsed
- try {
- parsed = new URL(fullUrl)
- } catch (e) {
- logger.error(`[JKES] 非法 URL: ${fullUrl}`)
- return null
- }
- if (parsed.protocol !== 'https:') {
- logger.error(`[JKES] 必须使用 https: ${fullUrl}`)
- return null
- }
- try {
- logger.info(`[JKES] POST ${pathPart}`)
- const axiosOpts = {
- headers: buildJkesHeaders(tokenClean),
- timeout: Number(s.requestTimeoutMs) || 30000,
- validateStatus: () => true,
- httpsAgent: getJkesHttpsAgent(),
- beforeRedirect: (options) => {
- if (options.protocol !== 'https:') {
- logger.error(`[JKES] 拒绝跟随非 HTTPS 重定向: ${options.href}`)
- throw new Error('JKES 重定向目标必须为 https')
- }
- }
- }
- if (!s.useSystemProxy) {
- axiosOpts.proxy = false
- }
- const res = await axios.post(fullUrl, body, axiosOpts)
- const payload = res.data
- const payloadPreview =
- typeof payload === 'object' && payload !== null
- ? JSON.stringify(payload)
- : String(payload ?? '')
- if (isJkesLoginExpiredPayload(payload)) {
- throw makeJkesLoginExpiredError(payload)
- }
- if (res.status !== 200) {
- logger.error(
- `[JKES] HTTP ${res.status} ${pathPart} 响应: ${payloadPreview.slice(0, 1000)}`
- )
- if (typeof payload === 'object' && payload !== null) {
- return payload
- }
- return null
- }
- return payload
- } catch (error) {
- if (error?.loginExpired) throw error
- const st = error.response?.status
- const dataErr = error.response?.data
- const errStr =
- typeof dataErr === 'object' && dataErr !== null
- ? JSON.stringify(dataErr)
- : String(dataErr ?? '')
- logger.error(
- `[JKES] 请求异常 ${pathPart} http=${st ?? 'n/a'} body=${errStr.slice(0, 500)} ${error.message}`
- )
- return null
- }
- }
- module.exports = {
- jkesRequest,
- isJkesLoginExpiredPayload,
- makeJkesLoginExpiredError,
- get BASE_URL() {
- return normalizeApiBase(getJkesSettings().apiBase)
- }
- }
|