request.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. const axios = require('axios')
  2. const https = require('https')
  3. const path = require('path')
  4. const Logger = require('../../lib/Logger')
  5. const { getJkesSettings, normalizeApiBase } = require('./jkesSettings')
  6. const logger = new Logger(path.join(__dirname, '../../logs/JKES.log'), 'INFO')
  7. let jkesHttpsAgent
  8. function getJkesHttpsAgent() {
  9. if (!jkesHttpsAgent) {
  10. const s = getJkesSettings()
  11. jkesHttpsAgent = new https.Agent({
  12. keepAlive: true,
  13. minVersion: 'TLSv1.2',
  14. rejectUnauthorized: s.tlsRejectUnauthorized !== false
  15. })
  16. }
  17. return jkesHttpsAgent
  18. }
  19. function buildJkesHeaders(token) {
  20. const tokenClean = String(token ?? '').trim()
  21. const s = getJkesSettings()
  22. return {
  23. 'x-auth-token': tokenClean,
  24. 'content-type': 'application/json',
  25. 'Accept-Encoding': 'gzip,compress,br,deflate',
  26. 'User-Agent': s.userAgent,
  27. Referer: s.referer
  28. }
  29. }
  30. /**
  31. * @param {string} url 如 /sys/user/getMyInfo
  32. * @param {object} [data] POST body
  33. * @param {string} token
  34. */
  35. async function jkesRequest(url, data, token) {
  36. const tokenClean = String(token ?? '').trim()
  37. if (!tokenClean) {
  38. logger.error('[JKES] 缺少 token')
  39. return null
  40. }
  41. const s = getJkesSettings()
  42. const pathPart = url.startsWith('/') ? url : `/${url}`
  43. const fullUrl = `${normalizeApiBase(s.apiBase)}${pathPart}`
  44. const body = data === undefined || data === null ? {} : data
  45. let parsed
  46. try {
  47. parsed = new URL(fullUrl)
  48. } catch (e) {
  49. logger.error(`[JKES] 非法 URL: ${fullUrl}`)
  50. return null
  51. }
  52. if (parsed.protocol !== 'https:') {
  53. logger.error(`[JKES] 必须使用 https: ${fullUrl}`)
  54. return null
  55. }
  56. try {
  57. logger.info(`[JKES] POST ${pathPart}`)
  58. const axiosOpts = {
  59. headers: buildJkesHeaders(tokenClean),
  60. timeout: Number(s.requestTimeoutMs) || 30000,
  61. validateStatus: () => true,
  62. httpsAgent: getJkesHttpsAgent(),
  63. beforeRedirect: (options) => {
  64. if (options.protocol !== 'https:') {
  65. logger.error(`[JKES] 拒绝跟随非 HTTPS 重定向: ${options.href}`)
  66. throw new Error('JKES 重定向目标必须为 https')
  67. }
  68. }
  69. }
  70. if (!s.useSystemProxy) {
  71. axiosOpts.proxy = false
  72. }
  73. const res = await axios.post(fullUrl, body, axiosOpts)
  74. const payload = res.data
  75. const payloadPreview =
  76. typeof payload === 'object' && payload !== null
  77. ? JSON.stringify(payload)
  78. : String(payload ?? '')
  79. if (res.status !== 200) {
  80. logger.error(
  81. `[JKES] HTTP ${res.status} ${pathPart} 响应: ${payloadPreview.slice(0, 1000)}`
  82. )
  83. if (typeof payload === 'object' && payload !== null) {
  84. return payload
  85. }
  86. return null
  87. }
  88. return payload
  89. } catch (error) {
  90. const st = error.response?.status
  91. const dataErr = error.response?.data
  92. const errStr =
  93. typeof dataErr === 'object' && dataErr !== null
  94. ? JSON.stringify(dataErr)
  95. : String(dataErr ?? '')
  96. logger.error(
  97. `[JKES] 请求异常 ${pathPart} http=${st ?? 'n/a'} body=${errStr.slice(0, 500)} ${error.message}`
  98. )
  99. return null
  100. }
  101. }
  102. module.exports = {
  103. jkesRequest,
  104. get BASE_URL() {
  105. return normalizeApiBase(getJkesSettings().apiBase)
  106. }
  107. }