diff --git a/public/index.html b/public/index.html
index 9b97a33..4a3f272 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,6 +6,7 @@
界面外观 (Appearance)
diff --git a/public/js/app.js b/public/js/app.js
index 5836ae3..d1ed0b5 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -41,6 +41,8 @@
formMessage: document.getElementById('formMessage'),
sourceItems: document.getElementById('sourceItems'),
serverSourceOption: document.getElementById('serverSourceOption'),
+ faviconUrlInput: document.getElementById('faviconUrlInput'),
+ logoUrlDarkInput: document.getElementById('logoUrlDarkInput'),
// Site Settings
modalTabs: document.querySelectorAll('.modal-tab'),
tabContents: document.querySelectorAll('.tab-content'),
@@ -53,6 +55,7 @@
logoIconContainer: document.getElementById('logoIconContainer'),
defaultThemeInput: document.getElementById('defaultThemeInput'),
show95BandwidthInput: document.getElementById('show95BandwidthInput'),
+ siteFavicon: document.getElementById('siteFavicon'),
// Auth & Theme elements
themeToggle: document.getElementById('themeToggle'),
sunIcon: document.querySelector('.sun-icon'),
@@ -487,6 +490,8 @@
if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
if (dom.icpFilingInput) dom.icpFilingInput.value = window.SITE_SETTINGS.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = window.SITE_SETTINGS.ps_filing || '';
+ if (dom.logoUrlDarkInput) dom.logoUrlDarkInput.value = window.SITE_SETTINGS.logo_url_dark || '';
+ if (dom.faviconUrlInput) dom.faviconUrlInput.value = window.SITE_SETTINGS.favicon_url || '';
// Latency routes loaded separately in openSettings or on startup
}
@@ -551,6 +556,10 @@
localStorage.setItem('theme', theme);
updateThemeIcons(theme);
updateMap2DTheme(theme);
+ // After theme toggle, re-apply site settings to handle potential logo change
+ if (window.SITE_SETTINGS) {
+ applySiteSettings(window.SITE_SETTINGS);
+ }
}
function applyTheme(theme) {
@@ -564,6 +573,19 @@
document.documentElement.classList.toggle('light-theme', isLight);
updateThemeIcons(actualTheme);
updateMap2DTheme(actualTheme);
+
+ // After theme change, re-apply site settings to handle potential logo change
+ if (window.SITE_SETTINGS) {
+ applySiteSettings(window.SITE_SETTINGS);
+ }
+ }
+
+ function updateFavicon(url) {
+ if (!url) return;
+ const link = dom.siteFavicon || document.querySelector("link[rel*='icon']");
+ if (link) {
+ link.href = url;
+ }
}
function updateThemeIcons(theme) {
@@ -1655,6 +1677,8 @@
}
if (dom.icpFilingInput) dom.icpFilingInput.value = settings.icp_filing || '';
if (dom.psFilingInput) dom.psFilingInput.value = settings.ps_filing || '';
+ if (dom.logoUrlDarkInput) dom.logoUrlDarkInput.value = settings.logo_url_dark || '';
+ if (dom.faviconUrlInput) dom.faviconUrlInput.value = settings.favicon_url || '';
// Apply to UI
applySiteSettings(settings);
@@ -1693,8 +1717,14 @@
}
// Logo Icon
- if (settings.logo_url) {
- dom.logoIconContainer.innerHTML = `
})
`;
+ let logoToUse = settings.logo_url;
+ const currentTheme = document.documentElement.classList.contains('light-theme') ? 'light' : 'dark';
+ if (currentTheme === 'dark' && settings.logo_url_dark) {
+ logoToUse = settings.logo_url_dark;
+ }
+
+ if (logoToUse) {
+ dom.logoIconContainer.innerHTML = `
})
`;
} else {
// Restore default SVG
dom.logoIconContainer.innerHTML = `
@@ -1713,6 +1743,9 @@
`;
}
+ // Favicon
+ updateFavicon(settings.favicon_url);
+
// P95 setting
if (settings.show_95_bandwidth !== undefined || settings.p95_type !== undefined) {
if (networkChart) {
@@ -1786,7 +1819,9 @@
show_95_bandwidth: dom.show95BandwidthInput.value === "1" ? 1 : 0,
p95_type: dom.p95TypeSelect.value,
ps_filing: dom.psFilingInput ? dom.psFilingInput.value : '',
- icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value : ''
+ icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value : '',
+ logo_url_dark: dom.logoUrlDarkInput ? dom.logoUrlDarkInput.value.trim() : '',
+ favicon_url: dom.faviconUrlInput ? dom.faviconUrlInput.value.trim() : ''
};
// If user sets default to auto, we should clear their manual override or set it to auto
diff --git a/server/db-integrity-check.js b/server/db-integrity-check.js
index 9527efd..f17e77d 100644
--- a/server/db-integrity-check.js
+++ b/server/db-integrity-check.js
@@ -97,6 +97,16 @@ async function checkAndFixDatabase() {
await db.query("ALTER TABLE site_settings ADD COLUMN ps_filing VARCHAR(255) AFTER icp_filing");
console.log(`[Database Integrity] ✅ Column 'ps_filing' added.`);
}
+ if (!columnNames.includes('logo_url_dark')) {
+ console.log(`[Database Integrity] ⚠️ Missing column 'logo_url_dark' in 'site_settings'. Adding it...`);
+ await db.query("ALTER TABLE site_settings ADD COLUMN logo_url_dark TEXT AFTER logo_url");
+ console.log(`[Database Integrity] ✅ Column 'logo_url_dark' added.`);
+ }
+ if (!columnNames.includes('favicon_url')) {
+ console.log(`[Database Integrity] ⚠️ Missing column 'favicon_url' in 'site_settings'. Adding it...`);
+ await db.query("ALTER TABLE site_settings ADD COLUMN favicon_url TEXT AFTER logo_url_dark");
+ console.log(`[Database Integrity] ✅ Column 'favicon_url' added.`);
+ }
} catch (err) {
console.error('[Database Integrity] ❌ Error checking integrity:', err.message);
}
@@ -135,6 +145,8 @@ async function createTable(tableName) {
page_name VARCHAR(255) DEFAULT '数据可视化展示大屏',
title VARCHAR(255) DEFAULT '数据可视化展示大屏',
logo_url TEXT,
+ logo_url_dark TEXT,
+ favicon_url TEXT,
default_theme VARCHAR(20) DEFAULT 'dark',
show_95_bandwidth TINYINT(1) DEFAULT 0,
p95_type VARCHAR(20) DEFAULT 'tx',
diff --git a/server/index.js b/server/index.js
index 8ab7f57..dff8037 100644
--- a/server/index.js
+++ b/server/index.js
@@ -262,6 +262,8 @@ app.post('/api/setup/init', async (req, res) => {
page_name VARCHAR(255) DEFAULT '数据可视化展示大屏',
title VARCHAR(255) DEFAULT '数据可视化展示大屏',
logo_url TEXT,
+ logo_url_dark TEXT,
+ favicon_url TEXT,
default_theme VARCHAR(20) DEFAULT 'dark',
show_95_bandwidth TINYINT(1) DEFAULT 0,
p95_type VARCHAR(20) DEFAULT 'tx',
@@ -409,7 +411,9 @@ const serveIndex = async (req, res) => {
let settings = {
page_name: '数据可视化展示大屏',
title: '数据可视化展示大屏',
- logo_url: null,
+ logo_url: null,
+ logo_url_dark: null,
+ favicon_url: null,
default_theme: 'dark',
blackbox_source_id: null,
latency_source: null,
@@ -563,6 +567,8 @@ app.get('/api/settings', async (req, res) => {
page_name: '数据可视化展示大屏',
title: '数据可视化展示大屏',
logo_url: null,
+ logo_url_dark: null,
+ favicon_url: null,
show_95_bandwidth: 0,
p95_type: 'tx',
blackbox_source_id: null,
@@ -582,15 +588,17 @@ app.get('/api/settings', async (req, res) => {
// Update site settings
app.post('/api/settings', requireAuth, async (req, res) => {
- const { page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing } = req.body;
+ const { page_name, title, logo_url, logo_url_dark, favicon_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing } = req.body;
try {
await db.query(
- `INSERT INTO site_settings (id, page_name, title, logo_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing)
- VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ `INSERT INTO site_settings (id, page_name, title, logo_url, logo_url_dark, favicon_url, default_theme, show_95_bandwidth, p95_type, blackbox_source_id, latency_source, latency_dest, latency_target, icp_filing, ps_filing)
+ VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
page_name = VALUES(page_name),
title = VALUES(title),
logo_url = VALUES(logo_url),
+ logo_url_dark = VALUES(logo_url_dark),
+ favicon_url = VALUES(favicon_url),
default_theme = VALUES(default_theme),
show_95_bandwidth = VALUES(show_95_bandwidth),
p95_type = VALUES(p95_type),
@@ -601,7 +609,7 @@ app.post('/api/settings', requireAuth, async (req, res) => {
icp_filing = VALUES(icp_filing),
ps_filing = VALUES(ps_filing)`,
[
- page_name, title, logo_url, default_theme,
+ page_name, title, logo_url, logo_url_dark, favicon_url, default_theme,
show_95_bandwidth ? 1 : 0, p95_type || 'tx',
blackbox_source_id || null, latency_source || null,
latency_dest || null, latency_target || null,