|
@@ -0,0 +1,121 @@
|
|
|
|
|
+const axios = require('axios')
|
|
|
|
|
+const HttpsProxyAgent = require('https-proxy-agent')
|
|
|
|
|
+const config = require('../../config.json')
|
|
|
|
|
+const API = require('../../lib/API')
|
|
|
|
|
+const { BaseStdResponse } = require('../../BaseStdResponse')
|
|
|
|
|
+const QgProxyManager = require('../../lib/Lepao/QgProxyManager')
|
|
|
|
|
+
|
|
|
|
|
+function buildAxiosOutboundConfig(fragment) {
|
|
|
|
|
+ if (!fragment || fragment.proxy === false || !fragment.proxy) {
|
|
|
|
|
+ return { proxy: false }
|
|
|
|
|
+ }
|
|
|
|
|
+ const { host, port, auth } = fragment.proxy
|
|
|
|
|
+ let userPart = ''
|
|
|
|
|
+ if (auth && String(auth.username || '').length > 0) {
|
|
|
|
|
+ const u = encodeURIComponent(auth.username)
|
|
|
|
|
+ const p = encodeURIComponent(auth.password != null ? String(auth.password) : '')
|
|
|
|
|
+ userPart = `${u}:${p}@`
|
|
|
|
|
+ }
|
|
|
|
|
+ const proxyUrl = `http://${userPart}${host}:${port}`
|
|
|
|
|
+ const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'
|
|
|
|
|
+ return {
|
|
|
|
|
+ proxy: false,
|
|
|
|
|
+ httpsAgent: new HttpsProxyAgent(proxyUrl, { rejectUnauthorized })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function resolveSelfEchoUrl() {
|
|
|
|
|
+ const base = String(config.url || '').trim()
|
|
|
|
|
+ if (!base) return `http://127.0.0.1:${config.port}/Corn/ProxySelfIpEcho`
|
|
|
|
|
+ return `${base.replace(/\/+$/, '')}/Corn/ProxySelfIpEcho`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+class ProxySelfCheck extends API {
|
|
|
|
|
+ constructor() {
|
|
|
|
|
+ super()
|
|
|
|
|
+ this.noEncrypt()
|
|
|
|
|
+ this.setPath('/Corn/ProxySelfCheck')
|
|
|
|
|
+ this.setMethod('GET')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async onRequest(req, res) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const qgOn = await QgProxyManager.isOutboundProxyEnabled()
|
|
|
|
|
+ if (!qgOn) {
|
|
|
|
|
+ await QgProxyManager.recordLog({
|
|
|
|
|
+ event: 'proxy_self_check_skip',
|
|
|
|
|
+ detail: { reason: 'proxy_disabled' }
|
|
|
|
|
+ })
|
|
|
|
|
+ return res.json({
|
|
|
|
|
+ ...BaseStdResponse.OK,
|
|
|
|
|
+ data: { skipped: true, reason: 'proxy_disabled' }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const frag = await QgProxyManager.getOutboundAxiosFragment({ forceRefresh: false })
|
|
|
|
|
+ if (frag.proxy === false) {
|
|
|
|
|
+ await QgProxyManager.recordLog({
|
|
|
|
|
+ event: 'proxy_self_check_skip',
|
|
|
|
|
+ detail: { reason: 'no_proxy_available' }
|
|
|
|
|
+ })
|
|
|
|
|
+ return res.json({
|
|
|
|
|
+ ...BaseStdResponse.OK,
|
|
|
|
|
+ data: { skipped: true, reason: 'no_proxy_available' }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const outbound = buildAxiosOutboundConfig(frag)
|
|
|
|
|
+ const echoUrl = resolveSelfEchoUrl()
|
|
|
|
|
+ const rsp = await axios.get(echoUrl, {
|
|
|
|
|
+ timeout: 15000,
|
|
|
|
|
+ validateStatus: () => true,
|
|
|
|
|
+ ...outbound
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const body = rsp.data || {}
|
|
|
|
|
+ const now = Date.now()
|
|
|
|
|
+ const cached = await QgProxyManager.getCachedParsed()
|
|
|
|
|
+ const proxyIp = body?.data?.ip || null
|
|
|
|
|
+
|
|
|
|
|
+ await QgProxyManager.recordLog({
|
|
|
|
|
+ event: 'proxy_self_check',
|
|
|
|
|
+ server: cached?.server || `${frag.proxy.host}:${frag.proxy.port}`,
|
|
|
|
|
+ deadline: cached?.deadline || null,
|
|
|
|
|
+ detail: {
|
|
|
|
|
+ code: body?.code,
|
|
|
|
|
+ http_status: rsp.status,
|
|
|
|
|
+ target: echoUrl,
|
|
|
|
|
+ proxy_ip: proxyIp,
|
|
|
|
|
+ x_forwarded_for: body?.data?.x_forwarded_for || '',
|
|
|
|
|
+ checked_at: now
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return res.json({
|
|
|
|
|
+ ...BaseStdResponse.OK,
|
|
|
|
|
+ data: {
|
|
|
|
|
+ proxy_ip: proxyIp,
|
|
|
|
|
+ http_status: rsp.status,
|
|
|
|
|
+ target: echoUrl
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ const msg = e?.message || String(e)
|
|
|
|
|
+ await QgProxyManager.recordLog({
|
|
|
|
|
+ event: 'proxy_self_check_fail',
|
|
|
|
|
+ detail: {
|
|
|
|
|
+ message: msg,
|
|
|
|
|
+ code: e?.code,
|
|
|
|
|
+ status: e?.response?.status
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ this.logger?.error?.(`[ProxySelfCheck] ${e?.stack || e}`)
|
|
|
|
|
+ return res.json({
|
|
|
|
|
+ ...BaseStdResponse.ERR,
|
|
|
|
|
+ msg: `代理自检失败: ${msg}`
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+module.exports.ProxySelfCheck = ProxySelfCheck
|