From ce739d12327c7d8ea869181092b7eca359cd342d Mon Sep 17 00:00:00 2001 From: CN-JS-HuiBai Date: Wed, 22 Apr 2026 21:23:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=B8=BA=E9=9D=99=E6=80=81?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E8=AE=BE=E7=BD=AE=E7=8B=AC=E7=AB=8B=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 5 +++++ public/js/app.js | 10 ++++++---- server/db-schema-check.js | 4 +++- server/index.js | 27 ++++++++++++++++++++------- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/public/index.html b/public/index.html index 3321a85..ad7fb33 100644 --- a/public/index.html +++ b/public/index.html @@ -596,6 +596,11 @@ +
+ + +

开启后,页面中的 JS/CSS/图片等资源将尝试从该 CDN 加载。请确保 CDN 已正确镜像相关资源。

+
diff --git a/public/js/app.js b/public/js/app.js index fcdacec..f97bf7e 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -131,7 +131,8 @@ customMetricsList: document.getElementById('customMetricsList'), btnAddCustomMetric: document.getElementById('btnAddCustomMetric'), btnSaveCustomMetrics: document.getElementById('btnSaveCustomMetrics'), - customDataContainer: document.getElementById('customDataContainer') + customDataContainer: document.getElementById('customDataContainer'), + cdnUrlInput: document.getElementById('cdnUrlInput') }; // ---- State ---- @@ -2146,13 +2147,13 @@ if (dom.faviconUrlInput) dom.faviconUrlInput.value = settings.favicon_url || ''; if (dom.showPageNameInput) dom.showPageNameInput.value = settings.show_page_name !== undefined ? settings.show_page_name.toString() : "1"; if (dom.requireLoginForServerDetailsInput) dom.requireLoginForServerDetailsInput.value = settings.require_login_for_server_details ? "1" : "0"; + if (dom.showServerIpInput) dom.showServerIpInput.value = settings.show_server_ip ? "1" : "0"; + if (dom.cdnUrlInput) dom.cdnUrlInput.value = settings.cdn_url || ''; // Handle Theme Priority: localStorage > Site Default const savedTheme = localStorage.getItem('theme'); const themeToApply = savedTheme || settings.default_theme || 'dark'; applyTheme(themeToApply); - - // Listen for system theme changes if set to auto (cleanup existing listener first) if (siteThemeQuery && siteThemeHandler) { siteThemeQuery.removeEventListener('change', siteThemeHandler); } @@ -2309,7 +2310,8 @@ show_server_ip: dom.showServerIpInput ? (dom.showServerIpInput.value === "1") : false, ip_metric_name: dom.ipMetricNameInput ? dom.ipMetricNameInput.value.trim() : null, ip_label_name: dom.ipLabelNameInput ? dom.ipLabelNameInput.value.trim() : 'address', - custom_metrics: getCustomMetricsFromUI() + custom_metrics: getCustomMetricsFromUI(), + cdn_url: dom.cdnUrlInput ? dom.cdnUrlInput.value.trim() : '' }; try { diff --git a/server/db-schema-check.js b/server/db-schema-check.js index 5218975..c73fc73 100644 --- a/server/db-schema-check.js +++ b/server/db-schema-check.js @@ -73,6 +73,7 @@ const SCHEMA = { ip_metric_name VARCHAR(100) DEFAULT NULL, ip_label_name VARCHAR(100) DEFAULT 'address', custom_metrics JSON DEFAULT NULL, + cdn_url VARCHAR(500) DEFAULT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `, @@ -103,7 +104,8 @@ const SCHEMA = { { name: 'show_server_ip', sql: "ALTER TABLE site_settings ADD COLUMN show_server_ip TINYINT(1) DEFAULT 0 AFTER ps_filing" }, { name: 'ip_metric_name', sql: "ALTER TABLE site_settings ADD COLUMN ip_metric_name VARCHAR(100) DEFAULT NULL AFTER show_server_ip" }, { name: 'ip_label_name', sql: "ALTER TABLE site_settings ADD COLUMN ip_label_name VARCHAR(100) DEFAULT 'address' AFTER ip_metric_name" }, - { name: 'custom_metrics', sql: "ALTER TABLE site_settings ADD COLUMN custom_metrics JSON DEFAULT NULL AFTER ip_label_name" } + { name: 'custom_metrics', sql: "ALTER TABLE site_settings ADD COLUMN custom_metrics JSON DEFAULT NULL AFTER ip_label_name" }, + { name: 'cdn_url', sql: "ALTER TABLE site_settings ADD COLUMN cdn_url VARCHAR(500) DEFAULT NULL AFTER custom_metrics" } ] }, traffic_stats: { diff --git a/server/index.js b/server/index.js index 33675c8..02843e9 100644 --- a/server/index.js +++ b/server/index.js @@ -151,7 +151,8 @@ function getPublicSiteSettings(settings = {}) { show_server_ip: settings.show_server_ip ? 1 : 0, ip_metric_name: settings.ip_metric_name || null, ip_label_name: settings.ip_label_name || 'address', - custom_metrics: settings.custom_metrics || [] + custom_metrics: settings.custom_metrics || [], + cdn_url: settings.cdn_url || null }; } @@ -780,6 +781,14 @@ const serveIndex = async (req, res) => { // Replace with + injection html = html.replace('', '' + injection); + + // Apply CDN URL if set + if (settings.cdn_url) { + const cdnBase = settings.cdn_url.replace(/\/+$/, ''); + // Replace relative paths for css, js, fonts, vendor, images + // Matches href="/css/...", src="/js/...", etc. + html = html.replace(/(href|src)="\/+(css|js|fonts|vendor|images|assets)\//g, `$1="${cdnBase}/$2/`); + } res.send(html); } catch (err) { @@ -943,7 +952,8 @@ app.post('/api/settings', requireAuth, async (req, res) => { const { page_name, show_page_name, title, logo_url, logo_url_dark, favicon_url, default_theme, show_95_bandwidth, p95_type, require_login_for_server_details, - icp_filing, ps_filing, show_server_ip, ip_metric_name, ip_label_name, custom_metrics + icp_filing, ps_filing, show_server_ip, ip_metric_name, ip_label_name, custom_metrics, + cdn_url } = req.body; // 3. Prepare parameters, prioritizing body but falling back to current @@ -969,7 +979,8 @@ app.post('/api/settings', requireAuth, async (req, res) => { show_server_ip: show_server_ip !== undefined ? (show_server_ip ? 1 : 0) : (current.show_server_ip || 0), ip_metric_name: ip_metric_name !== undefined ? ip_metric_name : (current.ip_metric_name || null), ip_label_name: ip_label_name !== undefined ? ip_label_name : (current.ip_label_name || 'address'), - custom_metrics: custom_metrics !== undefined ? JSON.stringify(custom_metrics) : (current.custom_metrics || '[]') + custom_metrics: custom_metrics !== undefined ? JSON.stringify(custom_metrics) : (current.custom_metrics || '[]'), + cdn_url: cdn_url !== undefined ? cdn_url : (current.cdn_url || null) }; await db.query(` @@ -978,8 +989,8 @@ app.post('/api/settings', requireAuth, async (req, res) => { default_theme, show_95_bandwidth, p95_type, require_login_for_server_details, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing, show_server_ip, ip_metric_name, ip_label_name, - custom_metrics - ) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + custom_metrics, cdn_url + ) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE page_name = VALUES(page_name), show_page_name = VALUES(show_page_name), @@ -1000,13 +1011,15 @@ app.post('/api/settings', requireAuth, async (req, res) => { show_server_ip = VALUES(show_server_ip), ip_metric_name = VALUES(ip_metric_name), ip_label_name = VALUES(ip_label_name), - custom_metrics = VALUES(custom_metrics)`, + custom_metrics = VALUES(custom_metrics), + cdn_url = VALUES(cdn_url)`, [ settings.page_name, settings.show_page_name, settings.title, settings.logo_url, settings.logo_url_dark, settings.favicon_url, settings.default_theme, settings.show_95_bandwidth, settings.p95_type, settings.require_login_for_server_details, settings.blackbox_source_id, settings.latency_source, settings.latency_dest, settings.latency_target, settings.icp_filing, settings.ps_filing, settings.show_server_ip, - settings.ip_metric_name, settings.ip_label_name, settings.custom_metrics + settings.ip_metric_name, settings.ip_label_name, settings.custom_metrics, + settings.cdn_url ] );