Browse Source

✨ feat: 增加代理日志输出

Pchen0 5 hours ago
parent
commit
d710947bd9
2 changed files with 62 additions and 13 deletions
  1. 32 10
      lib/Lepao/Worker.js
  2. 30 3
      lib/Lepao/lepaoSchoolHttp.js

+ 32 - 10
lib/Lepao/Worker.js

@@ -228,6 +228,14 @@ class Worker {
         }
     }
 
+    maskClientReason(reason) {
+        const s = String(reason || '')
+        if (s.includes('非法请求')) {
+            return '系统繁忙,请稍后再试'
+        }
+        return s
+    }
+
     extractApiErrorMessage(name, result) {
         if (!result) {
             this.logger.error(`${name} 接口无响应数据: ${this.safeStringify(result)}`)
@@ -246,7 +254,7 @@ class Worker {
 
         const reason = candidates.find(v => typeof v === 'string' && v.trim() !== '')
         if (reason) {
-            return reason
+            return this.maskClientReason(reason)
         }
 
         if (result.code !== undefined || result.status !== undefined) {
@@ -415,6 +423,7 @@ class Worker {
     }
 
     async request(traceId, name, url, raw, headers = {}) {
+        const ctx = arguments.length >= 6 ? arguments[5] : null
         return this.retry(async () => {
             this.log(traceId, 'REQ', name, raw)
 
@@ -438,7 +447,9 @@ class Worker {
                 postLepaoSchool(url, form, {
                     headers: mergedHeaders,
                     timeout: this.timeout,
-                    logger: this.logger
+                    logger: this.logger,
+                    outboundMode: ctx?.outboundMode || 'auto',
+                    mqTaskId: ctx?.taskId
                 }),
                 name
             )
@@ -694,7 +705,7 @@ class Worker {
                     await this.writeSuccessRedis(req.account)
                 }
                 if (!runResult.ok) {
-                    throw new Error(runResult.reason)
+                    throw new Error(this.maskClientReason(runResult.reason))
                 }
                 if (bindRes && bindRes.data && bindRes.data.record_id) {
                     const gyrRes = await this.handlers['lepao.uploadGyrOssFile']({ ...req, newPathData, record_id: bindRes.data.record_id }, ctx)
@@ -762,7 +773,7 @@ class Worker {
                     await this.enqueueTask(ctx.channel, 'lepao.sendNotice', {
                         account: req.account,
                         success: false,
-                        reason: err.message || '未知错误',
+                        reason: this.maskClientReason(err.message || '未知错误'),
                         traceId
                     }, { id: `${traceId}:notice:fail` })
                 }
@@ -1165,7 +1176,8 @@ class Worker {
                     'User-Agent': req.userAgent,
                     'charset': 'utf-8',
                     'Referer': 'https://servicewechat.com/wxf94c4ddb63d87ede/32/page-frame.html',
-                }
+                },
+                ctx
             )
         })
 
@@ -1204,7 +1216,9 @@ class Worker {
                 ctx.traceId,
                 'setZone',
                 this.api('/Run/setRunZone'),
-                raw
+                raw,
+                {},
+                ctx
             )
             return { run_zone_id: runZoneId }
         })
@@ -1231,7 +1245,9 @@ class Worker {
                 ctx.traceId,
                 'getOssSts',
                 this.api('/WpIndex/getOssSts'),
-                raw
+                raw,
+                {},
+                ctx
             )
 
             return res.data
@@ -1329,7 +1345,9 @@ class Worker {
                 ctx.traceId,
                 'bindData',
                 this.api('/Run2/gyroscope'),
-                data
+                data,
+                {},
+                ctx
             )
         })
 
@@ -1380,7 +1398,9 @@ class Worker {
                 ctx.traceId,
                 'bindData',
                 this.api('/Run/stopRunV278'),
-                data
+                data,
+                {},
+                ctx
             )
         })
     }
@@ -1437,8 +1457,10 @@ class Worker {
                 }
 
                 try {
+                    const proxyEnabled = await QgProxyManager.isOutboundProxyEnabled()
+                    const outboundMode = proxyEnabled ? 'proxy' : 'direct'
                     const result = await this.withTimeout(
-                        handler(data, { traceId, channel, taskId: id }),
+                        handler(data, { traceId, channel, taskId: id, outboundMode }),
                         type
                     )
 

+ 30 - 3
lib/Lepao/lepaoSchoolHttp.js

@@ -139,7 +139,7 @@ async function logSchoolOutbound(logger, phase, url, axiosMerge, opts = {}) {
  * 对 lepao.ctbu.edu.cn 的 POST:优先隧道代理;失败快速直连并记日志(隧道池出口由服务商后台切换)。
  */
 async function postLepaoSchool(url, data, options = {}) {
-    const { headers = {}, timeout = 15000, logger = null } = options
+    const { headers = {}, timeout = 15000, logger = null, outboundMode = 'auto', mqTaskId = null } = options
 
     const doPost = async (qgProxyFragment, requestTimeout = timeout) => {
         const outbound = buildAxiosOutboundConfig(qgProxyFragment)
@@ -150,6 +150,12 @@ async function postLepaoSchool(url, data, options = {}) {
         })
     }
 
+    // 强制直连:策略 A 用(任务内固定出站,禁止中途切换)
+    if (outboundMode === 'direct') {
+        await logSchoolOutbound(logger, '(强制直连)', url, { proxy: false })
+        return doPost({ proxy: false })
+    }
+
     if (debugProxyEnabled()) {
         const dbg = debugProxyAxiosFragment()
         await logSchoolOutbound(logger, 'Charles调试代理', url, dbg, { skipQgSnapshot: true })
@@ -167,10 +173,17 @@ async function postLepaoSchool(url, data, options = {}) {
     try {
         frag = await getOutboundWithBackoff({ forceRefresh: false }, 2)
     } catch (e0) {
+        if (outboundMode === 'proxy') {
+            const err = new Error(`代理模式提取失败: ${e0.message || e0}`)
+            err.code = 'PROXY_REQUIRED_EXTRACT_FAILED'
+            err.retryable = true
+            throw err
+        }
         logger?.error?.(`[lepaoSchoolHttp] 青果提取多次重试仍失败,改直连: ${e0.message || e0}`)
         await logSchoolOutbound(logger, '(青果提取异常→直连)', url, { proxy: false })
         await QgProxyManager.recordFallbackDirect({
             reason: 'qg_extract_error',
+            mq_task_id: mqTaskId,
             ...summarizeAxiosError(e0)
         })
         return doPost({ proxy: false })
@@ -187,16 +200,28 @@ async function postLepaoSchool(url, data, options = {}) {
 
     if (frag.proxy === false) {
         logger?.warn?.('[lepaoSchoolHttp] 无可用青果节点,对学校 POST 将直连')
+        if (outboundMode === 'proxy') {
+            const err = new Error('代理模式无可用节点')
+            err.code = 'PROXY_REQUIRED_NO_NODE'
+            err.retryable = true
+            throw err
+        }
         await logSchoolOutbound(logger, '(无缓存节点→直连)', url, { proxy: false })
-        await QgProxyManager.recordFallbackDirect({ reason: 'no_proxy_available' })
+        await QgProxyManager.recordFallbackDirect({ reason: 'no_proxy_available', mq_task_id: mqTaskId })
         return doPost({ proxy: false })
     }
 
     await logSchoolOutbound(logger, '首次请求', url, frag)
     try {
-        const proxyFirstTimeoutMs = 10000
+        const proxyFirstTimeoutMs = 20000
         return await doPost(frag, proxyFirstTimeoutMs)
     } catch (e1) {
+        if (outboundMode === 'proxy') {
+            const err = new Error(`代理模式请求失败: ${e1.message || e1}`)
+            err.code = 'PROXY_REQUIRED_REQUEST_FAILED'
+            err.retryable = true
+            throw err
+        }
         if (!isQgProxyEligibleFailure(e1)) throw e1
         logger?.warn?.(
             `[lepaoSchoolHttp] 经代理首次请求失败,将直接回退直连。err=${e1.message || e1} ${JSON.stringify(
@@ -213,6 +238,7 @@ async function postLepaoSchool(url, data, options = {}) {
             await QgProxyManager.recordFallbackDirect({
                 reason: 'tls_prefinish_reset_direct',
                 path: briefUrlPath(url),
+                mq_task_id: mqTaskId,
                 ...summarizeAxiosError(e1)
             })
             return doPost({ proxy: false })
@@ -222,6 +248,7 @@ async function postLepaoSchool(url, data, options = {}) {
         await QgProxyManager.recordFallbackDirect({
             reason: 'proxy_post_failed_then_direct',
             path: briefUrlPath(url),
+            mq_task_id: mqTaskId,
             ...summarizeAxiosError(e1)
         })
         return doPost({ proxy: false })