Browse Source

✨ feat: 代理支持多端不同设置

Pchen. 4 days ago
parent
commit
ece4d3480e

+ 7 - 0
apis/Lepao/Proxy/Admin/Config.js

@@ -53,6 +53,12 @@ class AdminLepaoProxyConfig extends API {
                 `UPDATE lepao_proxy_settings SET proxy_enabled = ?, area = ?, area_ex = ?, isp = ?, distinct_extract = ?, updated_at = ? WHERE id = 1`,
                 [enabled, areaStr, areaExStr, ispVal, distinct, now]
             )
+            await db.query(
+                `INSERT INTO lepao_proxy_project_settings (scope_key, proxy_enabled, updated_at)
+                 VALUES (?, ?, ?)
+                 ON DUPLICATE KEY UPDATE proxy_enabled = VALUES(proxy_enabled), updated_at = VALUES(updated_at)`,
+                [QgProxyManager.getProjectKey(), enabled, now]
+            )
 
             if (invalidate_cache === true || invalidate_cache === 1 || invalidate_cache === '1') {
                 await Redis.del(QgProxyManager.REDIS_CURRENT)
@@ -62,6 +68,7 @@ class AdminLepaoProxyConfig extends API {
                 event: 'config_change',
                 detail: {
                     proxy_enabled: enabled,
+                    project_scope_key: QgProxyManager.getProjectKey(),
                     area: areaStr,
                     area_ex: areaExStr,
                     isp: ispVal,

+ 3 - 0
apis/Lepao/Proxy/Admin/Status.js

@@ -53,7 +53,10 @@ class AdminLepaoProxyStatus extends API {
             return res.json({
                 ...BaseStdResponse.OK,
                 data: {
+                    project_scope_key: snap.project_scope_key,
                     proxy_enabled: snap.proxy_enabled,
+                    proxy_enabled_default: snap.proxy_enabled_default,
+                    project_proxy_updated_at: snap.project_proxy_updated_at,
                     area: snap.area,
                     area_ex: snap.area_ex,
                     isp: snap.isp,

+ 1 - 0
config.example.json

@@ -1,6 +1,7 @@
 {
     "port": 30003,
     "serverRole": "all",
+    "qgProxyScope": "YOUR_DEPLOYMENT_KEY",
     "database": {
         "host": "YOUR_MYSQL_HOST",
         "database": "YOUR_DB_NAME",

+ 64 - 4
lib/Lepao/QgProxyManager.js

@@ -17,6 +17,7 @@ const LOCK_WAIT_ROUNDS = 40
 const LOCK_WAIT_MS = 150
 
 let warnedTlsRejectUnauthorized = false
+let projectSettingsTableEnsured = false
 
 const logger = new Logger(path.join(__dirname, '../logs/QgProxyManager.log'), 'INFO')
 
@@ -50,6 +51,36 @@ function hasTunnelServer() {
     return tunnelServer.length > 0
 }
 
+function normalizeProjectKey(raw) {
+    return String(raw || '')
+        .trim()
+        .replace(/[^\w\u4e00-\u9fa5:.-]/g, '_')
+        .slice(0, 128)
+}
+
+function getProjectKey() {
+    return (
+        normalizeProjectKey(process.env.RUNFORGE_PROXY_SCOPE) ||
+        normalizeProjectKey(config.qgProxyScope) ||
+        normalizeProjectKey(config.qgChannelProxy?.scopeKey) ||
+        normalizeProjectKey(config.server) ||
+        normalizeProjectKey(config.port) ||
+        'default'
+    )
+}
+
+async function ensureProjectSettingsTable() {
+    if (projectSettingsTableEnsured) return
+    await db.query(
+        `CREATE TABLE IF NOT EXISTS lepao_proxy_project_settings (
+            scope_key VARCHAR(128) NOT NULL PRIMARY KEY,
+            proxy_enabled TINYINT NOT NULL,
+            updated_at BIGINT NOT NULL
+        )`
+    )
+    projectSettingsTableEnsured = true
+}
+
 async function ensureSettingsRow() {
     const now = Date.now()
     await db.query(
@@ -57,6 +88,12 @@ async function ensureSettingsRow() {
          VALUES (1, 0, '', '', NULL, 1, ?)`,
         [now]
     )
+    await ensureProjectSettingsTable()
+    await db.query(
+        `INSERT IGNORE INTO lepao_proxy_project_settings (scope_key, proxy_enabled, updated_at)
+         SELECT ?, proxy_enabled, ? FROM lepao_proxy_settings WHERE id = 1`,
+        [getProjectKey(), now]
+    )
 }
 
 async function loadSettings() {
@@ -64,7 +101,25 @@ async function loadSettings() {
     const rows = await db.query(
         `SELECT proxy_enabled, area, area_ex, isp, distinct_extract, updated_at FROM lepao_proxy_settings WHERE id = 1`
     )
-    return rows?.[0] || null
+    const row = rows?.[0] || null
+    if (!row) return null
+
+    const projectRows = await db.query(
+        `SELECT proxy_enabled, updated_at FROM lepao_proxy_project_settings WHERE scope_key = ? LIMIT 1`,
+        [getProjectKey()]
+    )
+    const project = projectRows?.[0] || null
+    return {
+        ...row,
+        project_scope_key: getProjectKey(),
+        project_proxy_enabled: project?.proxy_enabled ?? row.proxy_enabled,
+        project_updated_at: project?.updated_at ?? row.updated_at
+    }
+}
+
+function getProjectProxyEnabledFromSettings(settings) {
+    if (!settings) return false
+    return Number(settings.project_proxy_enabled) === 1
 }
 
 function parseDeadlineMs(deadlineStr) {
@@ -252,7 +307,7 @@ async function fetchResourceAreas() {
 async function isOutboundProxyEnabled() {
     if (!hasExtractCredentials() && !(hasTunnelServer() && hasProxyAuth())) return false
     const row = await loadSettings()
-    return row && Number(row.proxy_enabled) === 1
+    return getProjectProxyEnabledFromSettings(row)
 }
 
 /**
@@ -266,7 +321,7 @@ async function getOutboundAxiosFragment(opt = {}) {
     if (!hasExtractCredentials() && !(hasTunnelServer() && hasProxyAuth())) return { proxy: false }
 
     const settings = await loadSettings()
-    if (!settings || Number(settings.proxy_enabled) !== 1) return { proxy: false }
+    if (!getProjectProxyEnabledFromSettings(settings)) return { proxy: false }
 
     // 隧道代理模式:直接使用隧道入口地址,不需要 /pool 提取。
     const tunnelFrag = buildTunnelProxyOpts(settings)
@@ -440,7 +495,10 @@ async function getStatusSnapshot() {
     }
 
     return {
-        proxy_enabled: row ? Number(row.proxy_enabled) === 1 : false,
+        project_scope_key: getProjectKey(),
+        proxy_enabled: getProjectProxyEnabledFromSettings(row),
+        proxy_enabled_default: row ? Number(row.proxy_enabled) === 1 : false,
+        project_proxy_updated_at: row?.project_updated_at ?? 0,
         area: row?.area ?? '',
         area_ex: row?.area_ex ?? '',
         isp: row?.isp == null ? null : Number(row.isp),
@@ -458,6 +516,8 @@ module.exports = {
     getQgConfig,
     hasExtractCredentials,
     hasProxyAuth,
+    getProjectKey,
+    getProjectProxyEnabledFromSettings,
     ensureSettingsRow,
     loadSettings,
     isOutboundProxyEnabled,

+ 1 - 0
lib/Lepao/lepaoProxyLogDisplay.js

@@ -46,6 +46,7 @@ function summarizeLogRow(record) {
         if (d.after) lines.push(`阶段:${d.after}`)
         if (d.code) lines.push(`错误码:${d.code}`)
     } else if (event === 'config_change') {
+        if (d.project_scope_key) lines.push(`作用项目:${d.project_scope_key}`)
         lines.push(`代理开关:${d.proxy_enabled === 1 ? '开' : '关'}`)
         if (d.area !== undefined) lines.push(`地区 area:「${d.area || '(空)'}」`)
         if (d.area_ex !== undefined) lines.push(`排除 area_ex:「${d.area_ex || '(空)'}」`)