支持为静态资源设置独立地址

This commit is contained in:
CN-JS-HuiBai
2026-04-22 21:23:12 +08:00
parent ba633c8be4
commit ce739d1232
4 changed files with 34 additions and 12 deletions

View File

@@ -596,6 +596,11 @@
<label for="icpFilingInput">ICP 备案号 (如京ICP备12345678号)</label> <label for="icpFilingInput">ICP 备案号 (如京ICP备12345678号)</label>
<input type="text" id="icpFilingInput" placeholder="请输入 ICP 备案号"> <input type="text" id="icpFilingInput" placeholder="请输入 ICP 备案号">
</div> </div>
<div class="form-group" style="margin-top: 15px;">
<label for="cdnUrlInput">静态资源 CDN 地址 (例如: https://cdn.example.com)</label>
<input type="url" id="cdnUrlInput" placeholder="留空则使用本地服务器资源">
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">开启后,页面中的 JS/CSS/图片等资源将尝试从该 CDN 加载。请确保 CDN 已正确镜像相关资源。</p>
</div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;"> <div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
<button class="btn btn-add" id="btnSaveSiteSettings">保存基础设置</button> <button class="btn btn-add" id="btnSaveSiteSettings">保存基础设置</button>
</div> </div>

View File

@@ -131,7 +131,8 @@
customMetricsList: document.getElementById('customMetricsList'), customMetricsList: document.getElementById('customMetricsList'),
btnAddCustomMetric: document.getElementById('btnAddCustomMetric'), btnAddCustomMetric: document.getElementById('btnAddCustomMetric'),
btnSaveCustomMetrics: document.getElementById('btnSaveCustomMetrics'), btnSaveCustomMetrics: document.getElementById('btnSaveCustomMetrics'),
customDataContainer: document.getElementById('customDataContainer') customDataContainer: document.getElementById('customDataContainer'),
cdnUrlInput: document.getElementById('cdnUrlInput')
}; };
// ---- State ---- // ---- State ----
@@ -2146,13 +2147,13 @@
if (dom.faviconUrlInput) dom.faviconUrlInput.value = settings.favicon_url || ''; 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.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.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 // Handle Theme Priority: localStorage > Site Default
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
const themeToApply = savedTheme || settings.default_theme || 'dark'; const themeToApply = savedTheme || settings.default_theme || 'dark';
applyTheme(themeToApply); applyTheme(themeToApply);
// Listen for system theme changes if set to auto (cleanup existing listener first)
if (siteThemeQuery && siteThemeHandler) { if (siteThemeQuery && siteThemeHandler) {
siteThemeQuery.removeEventListener('change', siteThemeHandler); siteThemeQuery.removeEventListener('change', siteThemeHandler);
} }
@@ -2309,7 +2310,8 @@
show_server_ip: dom.showServerIpInput ? (dom.showServerIpInput.value === "1") : false, show_server_ip: dom.showServerIpInput ? (dom.showServerIpInput.value === "1") : false,
ip_metric_name: dom.ipMetricNameInput ? dom.ipMetricNameInput.value.trim() : null, ip_metric_name: dom.ipMetricNameInput ? dom.ipMetricNameInput.value.trim() : null,
ip_label_name: dom.ipLabelNameInput ? dom.ipLabelNameInput.value.trim() : 'address', 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 { try {

View File

@@ -73,6 +73,7 @@ const SCHEMA = {
ip_metric_name VARCHAR(100) DEFAULT NULL, ip_metric_name VARCHAR(100) DEFAULT NULL,
ip_label_name VARCHAR(100) DEFAULT 'address', ip_label_name VARCHAR(100) DEFAULT 'address',
custom_metrics JSON DEFAULT NULL, custom_metrics JSON DEFAULT NULL,
cdn_url VARCHAR(500) DEFAULT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ) 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: '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_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: '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: { traffic_stats: {

View File

@@ -151,7 +151,8 @@ function getPublicSiteSettings(settings = {}) {
show_server_ip: settings.show_server_ip ? 1 : 0, show_server_ip: settings.show_server_ip ? 1 : 0,
ip_metric_name: settings.ip_metric_name || null, ip_metric_name: settings.ip_metric_name || null,
ip_label_name: settings.ip_label_name || 'address', 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 <head> with <head> + injection // Replace <head> with <head> + injection
html = html.replace('<head>', '<head>' + injection); html = html.replace('<head>', '<head>' + 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); res.send(html);
} catch (err) { } catch (err) {
@@ -943,7 +952,8 @@ app.post('/api/settings', requireAuth, async (req, res) => {
const { const {
page_name, show_page_name, title, logo_url, logo_url_dark, favicon_url, 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, 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; } = req.body;
// 3. Prepare parameters, prioritizing body but falling back to current // 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), 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_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'), 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(` 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, default_theme, show_95_bandwidth, p95_type, require_login_for_server_details,
blackbox_source_id, latency_source, latency_dest, latency_target, blackbox_source_id, latency_source, latency_dest, latency_target,
icp_filing, ps_filing, show_server_ip, ip_metric_name, ip_label_name, icp_filing, ps_filing, show_server_ip, ip_metric_name, ip_label_name,
custom_metrics custom_metrics, cdn_url
) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
page_name = VALUES(page_name), page_name = VALUES(page_name),
show_page_name = VALUES(show_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), show_server_ip = VALUES(show_server_ip),
ip_metric_name = VALUES(ip_metric_name), ip_metric_name = VALUES(ip_metric_name),
ip_label_name = VALUES(ip_label_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.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.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.blackbox_source_id, settings.latency_source, settings.latency_dest, settings.latency_target,
settings.icp_filing, settings.ps_filing, settings.show_server_ip, 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
] ]
); );