ProxySelfCheck.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. const axios = require('axios')
  2. const HttpsProxyAgent = require('https-proxy-agent')
  3. const config = require('../../config.json')
  4. const API = require('../../lib/API')
  5. const { BaseStdResponse } = require('../../BaseStdResponse')
  6. const QgProxyManager = require('../../lib/Lepao/QgProxyManager')
  7. function buildAxiosOutboundConfig(fragment) {
  8. if (!fragment || fragment.proxy === false || !fragment.proxy) {
  9. return { proxy: false }
  10. }
  11. const { host, port, auth } = fragment.proxy
  12. let userPart = ''
  13. if (auth && String(auth.username || '').length > 0) {
  14. const u = encodeURIComponent(auth.username)
  15. const p = encodeURIComponent(auth.password != null ? String(auth.password) : '')
  16. userPart = `${u}:${p}@`
  17. }
  18. const proxyUrl = `http://${userPart}${host}:${port}`
  19. const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'
  20. return {
  21. proxy: false,
  22. httpsAgent: new HttpsProxyAgent(proxyUrl, { rejectUnauthorized })
  23. }
  24. }
  25. function resolveSelfEchoUrl() {
  26. const base = String(config.url || '').trim()
  27. if (!base) return `http://127.0.0.1:${config.port}/Corn/ProxySelfIpEcho`
  28. return `${base.replace(/\/+$/, '')}/Corn/ProxySelfIpEcho`
  29. }
  30. class ProxySelfCheck extends API {
  31. constructor() {
  32. super()
  33. this.noEncrypt()
  34. this.setPath('/Corn/ProxySelfCheck')
  35. this.setMethod('GET')
  36. }
  37. async onRequest(req, res) {
  38. try {
  39. const qgOn = await QgProxyManager.isOutboundProxyEnabled()
  40. if (!qgOn) {
  41. await QgProxyManager.recordLog({
  42. event: 'proxy_self_check_skip',
  43. detail: { reason: 'proxy_disabled' }
  44. })
  45. return res.json({
  46. ...BaseStdResponse.OK,
  47. data: { skipped: true, reason: 'proxy_disabled' }
  48. })
  49. }
  50. const frag = await QgProxyManager.getOutboundAxiosFragment({ forceRefresh: false })
  51. if (frag.proxy === false) {
  52. await QgProxyManager.recordLog({
  53. event: 'proxy_self_check_skip',
  54. detail: { reason: 'no_proxy_available' }
  55. })
  56. return res.json({
  57. ...BaseStdResponse.OK,
  58. data: { skipped: true, reason: 'no_proxy_available' }
  59. })
  60. }
  61. const outbound = buildAxiosOutboundConfig(frag)
  62. const echoUrl = resolveSelfEchoUrl()
  63. const rsp = await axios.get(echoUrl, {
  64. timeout: 15000,
  65. validateStatus: () => true,
  66. ...outbound
  67. })
  68. const body = rsp.data || {}
  69. const now = Date.now()
  70. const cached = await QgProxyManager.getCachedParsed()
  71. const proxyIp = body?.data?.ip || null
  72. await QgProxyManager.recordLog({
  73. event: 'proxy_self_check',
  74. server: cached?.server || `${frag.proxy.host}:${frag.proxy.port}`,
  75. deadline: cached?.deadline || null,
  76. detail: {
  77. code: body?.code,
  78. http_status: rsp.status,
  79. target: echoUrl,
  80. proxy_ip: proxyIp,
  81. x_forwarded_for: body?.data?.x_forwarded_for || '',
  82. checked_at: now
  83. }
  84. })
  85. return res.json({
  86. ...BaseStdResponse.OK,
  87. data: {
  88. proxy_ip: proxyIp,
  89. http_status: rsp.status,
  90. target: echoUrl
  91. }
  92. })
  93. } catch (e) {
  94. const msg = e?.message || String(e)
  95. await QgProxyManager.recordLog({
  96. event: 'proxy_self_check_fail',
  97. detail: {
  98. message: msg,
  99. code: e?.code,
  100. status: e?.response?.status
  101. }
  102. })
  103. this.logger?.error?.(`[ProxySelfCheck] ${e?.stack || e}`)
  104. return res.json({
  105. ...BaseStdResponse.ERR,
  106. msg: `代理自检失败: ${msg}`
  107. })
  108. }
  109. }
  110. }
  111. module.exports.ProxySelfCheck = ProxySelfCheck