diff --git a/public/index.html b/public/index.html
index 9436e2d..5872f39 100644
--- a/public/index.html
+++ b/public/index.html
@@ -507,6 +507,15 @@
+
diff --git a/public/js/app.js b/public/js/app.js
index 04dfefe..161b2a9 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -44,6 +44,7 @@
faviconUrlInput: document.getElementById('faviconUrlInput'),
logoUrlDarkInput: document.getElementById('logoUrlDarkInput'),
showPageNameInput: document.getElementById('showPageNameInput'),
+ requireLoginForServerDetailsInput: document.getElementById('requireLoginForServerDetailsInput'),
// Site Settings
modalTabs: document.querySelectorAll('.modal-tab'),
tabContents: document.querySelectorAll('.tab-content'),
@@ -417,6 +418,10 @@
const job = row.getAttribute('data-job');
const source = row.getAttribute('data-source');
if (instance && job && source) {
+ if (requiresLoginForServerDetails() && !user) {
+ promptLogin('登录后可查看服务器详细指标');
+ return;
+ }
openServerDetail(instance, job, source);
}
}
@@ -528,6 +533,7 @@
if (dom.defaultThemeInput) dom.defaultThemeInput.value = window.SITE_SETTINGS.default_theme || 'dark';
if (dom.show95BandwidthInput) dom.show95BandwidthInput.value = window.SITE_SETTINGS.show_95_bandwidth ? "1" : "0";
if (dom.p95TypeSelect) dom.p95TypeSelect.value = window.SITE_SETTINGS.p95_type || 'tx';
+ if (dom.requireLoginForServerDetailsInput) dom.requireLoginForServerDetailsInput.value = window.SITE_SETTINGS.require_login_for_server_details ? "1" : "0";
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 || '';
@@ -692,6 +698,11 @@
return null;
}
+ function requiresLoginForServerDetails() {
+ if (!window.SITE_SETTINGS) return true;
+ return !!window.SITE_SETTINGS.require_login_for_server_details;
+ }
+
function renderLogoImage(url) {
if (!dom.logoIconContainer) return;
const safeUrl = sanitizeAssetUrl(url);
@@ -1895,6 +1906,7 @@
if (dom.logoUrlDarkInput) dom.logoUrlDarkInput.value = settings.logo_url_dark || '';
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";
// Handle Theme Priority: localStorage > Site Default
const savedTheme = localStorage.getItem('theme');
@@ -2016,6 +2028,7 @@
logo_url_dark: dom.logoUrlDarkInput ? dom.logoUrlDarkInput.value.trim() : '',
favicon_url: dom.faviconUrlInput ? dom.faviconUrlInput.value.trim() : '',
show_page_name: dom.showPageNameInput ? parseInt(dom.showPageNameInput.value) : 1,
+ require_login_for_server_details: dom.requireLoginForServerDetailsInput ? (dom.requireLoginForServerDetailsInput.value === "1") : true,
default_theme: dom.defaultThemeInput ? dom.defaultThemeInput.value : 'dark',
show_95_bandwidth: dom.show95BandwidthInput ? (dom.show95BandwidthInput.value === "1") : false,
p95_type: dom.p95TypeSelect ? dom.p95TypeSelect.value : 'tx',
diff --git a/server/db-integrity-check.js b/server/db-integrity-check.js
index 117ce47..8006f17 100644
--- a/server/db-integrity-check.js
+++ b/server/db-integrity-check.js
@@ -83,6 +83,7 @@ async function checkAndFixDatabase() {
await addColumn('show_page_name', "ALTER TABLE site_settings ADD COLUMN show_page_name TINYINT(1) DEFAULT 1 AFTER page_name");
await addColumn('show_95_bandwidth', "ALTER TABLE site_settings ADD COLUMN show_95_bandwidth TINYINT(1) DEFAULT 0 AFTER default_theme");
await addColumn('p95_type', "ALTER TABLE site_settings ADD COLUMN p95_type VARCHAR(20) DEFAULT 'tx' AFTER show_95_bandwidth");
+ await addColumn('require_login_for_server_details', "ALTER TABLE site_settings ADD COLUMN require_login_for_server_details TINYINT(1) DEFAULT 1 AFTER p95_type");
await addColumn('blackbox_source_id', "ALTER TABLE site_settings ADD COLUMN blackbox_source_id INT AFTER p95_type");
await addColumn('latency_source', "ALTER TABLE site_settings ADD COLUMN latency_source VARCHAR(100) AFTER blackbox_source_id");
await addColumn('latency_dest', "ALTER TABLE site_settings ADD COLUMN latency_dest VARCHAR(100) AFTER latency_source");
@@ -135,6 +136,7 @@ async function createTable(tableName) {
default_theme VARCHAR(20) DEFAULT 'dark',
show_95_bandwidth TINYINT(1) DEFAULT 0,
p95_type VARCHAR(20) DEFAULT 'tx',
+ require_login_for_server_details TINYINT(1) DEFAULT 1,
blackbox_source_id INT,
latency_source VARCHAR(100),
latency_dest VARCHAR(100),
diff --git a/server/index.js b/server/index.js
index ddf4151..39c0f24 100644
--- a/server/index.js
+++ b/server/index.js
@@ -140,11 +140,37 @@ function getPublicSiteSettings(settings = {}) {
default_theme: settings.default_theme || 'dark',
show_95_bandwidth: settings.show_95_bandwidth ? 1 : 0,
p95_type: settings.p95_type || 'tx',
+ require_login_for_server_details: settings.require_login_for_server_details !== undefined
+ ? (settings.require_login_for_server_details ? 1 : 0)
+ : 1,
icp_filing: settings.icp_filing || null,
ps_filing: settings.ps_filing || null
};
}
+async function getSiteSettingsRow() {
+ const [rows] = await db.query('SELECT * FROM site_settings WHERE id = 1');
+ return rows.length > 0 ? rows[0] : {};
+}
+
+async function requireServerDetailsAccess(req, res, next) {
+ try {
+ const settings = await getSiteSettingsRow();
+ const requiresLogin = settings.require_login_for_server_details !== undefined
+ ? !!settings.require_login_for_server_details
+ : true;
+
+ if (!requiresLogin) {
+ return next();
+ }
+
+ return requireAuth(req, res, next);
+ } catch (err) {
+ console.error('Server details access check failed:', err);
+ return res.status(500).json({ error: 'Failed to verify detail access' });
+ }
+}
+
function getCookieOptions(req, maxAgeSeconds) {
const options = ['Path=/', 'HttpOnly', 'SameSite=Strict'];
if (typeof maxAgeSeconds === 'number') {
@@ -508,11 +534,12 @@ app.post('/api/setup/init', ensureSetupAccess, async (req, res) => {
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',
- blackbox_source_id INT,
+ favicon_url TEXT,
+ default_theme VARCHAR(20) DEFAULT 'dark',
+ show_95_bandwidth TINYINT(1) DEFAULT 0,
+ p95_type VARCHAR(20) DEFAULT 'tx',
+ require_login_for_server_details TINYINT(1) DEFAULT 1,
+ blackbox_source_id INT,
latency_source VARCHAR(100),
latency_dest VARCHAR(100),
latency_target VARCHAR(255),
@@ -530,6 +557,7 @@ app.post('/api/setup/init', ensureSetupAccess, async (req, res) => {
await connection.query("ALTER TABLE prometheus_sources ADD COLUMN IF NOT EXISTS is_server_source TINYINT(1) DEFAULT 1 AFTER description");
await connection.query("ALTER TABLE prometheus_sources ADD COLUMN IF NOT EXISTS type VARCHAR(50) DEFAULT 'prometheus' AFTER is_server_source");
await connection.query("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS show_page_name TINYINT(1) DEFAULT 1 AFTER page_name");
+ await connection.query("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS require_login_for_server_details TINYINT(1) DEFAULT 1 AFTER p95_type");
await connection.query(`
CREATE TABLE IF NOT EXISTS latency_routes (
id INT AUTO_INCREMENT PRIMARY KEY,
@@ -878,11 +906,11 @@ app.post('/api/settings', requireAuth, async (req, res) => {
let current = rows.length > 0 ? rows[0] : {};
// 2. Destructure fields from body
- const {
- page_name, show_page_name, title, logo_url, logo_url_dark, favicon_url,
- default_theme, show_95_bandwidth, p95_type,
- icp_filing, ps_filing
- } = req.body;
+ 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
+ } = req.body;
// 3. Prepare parameters, prioritizing body but falling back to current
const settings = {
@@ -891,13 +919,16 @@ app.post('/api/settings', requireAuth, async (req, res) => {
title: title !== undefined ? title : (current.title || '数据可视化展示大屏'),
logo_url: logo_url !== undefined ? logo_url : (current.logo_url || null),
logo_url_dark: logo_url_dark !== undefined ? logo_url_dark : (current.logo_url_dark || null),
- favicon_url: favicon_url !== undefined ? favicon_url : (current.favicon_url || null),
- default_theme: default_theme !== undefined ? default_theme : (current.default_theme || 'dark'),
- show_95_bandwidth: show_95_bandwidth !== undefined ? (show_95_bandwidth ? 1 : 0) : (current.show_95_bandwidth || 0),
- p95_type: p95_type !== undefined ? p95_type : (current.p95_type || 'tx'),
- blackbox_source_id: current.blackbox_source_id || null, // UI doesn't send this
- latency_source: current.latency_source || null, // UI doesn't send this
- latency_dest: current.latency_dest || null, // UI doesn't send this
+ favicon_url: favicon_url !== undefined ? favicon_url : (current.favicon_url || null),
+ default_theme: default_theme !== undefined ? default_theme : (current.default_theme || 'dark'),
+ show_95_bandwidth: show_95_bandwidth !== undefined ? (show_95_bandwidth ? 1 : 0) : (current.show_95_bandwidth || 0),
+ p95_type: p95_type !== undefined ? p95_type : (current.p95_type || 'tx'),
+ require_login_for_server_details: require_login_for_server_details !== undefined
+ ? (require_login_for_server_details ? 1 : 0)
+ : (current.require_login_for_server_details !== undefined ? current.require_login_for_server_details : 1),
+ blackbox_source_id: current.blackbox_source_id || null, // UI doesn't send this
+ latency_source: current.latency_source || null, // UI doesn't send this
+ latency_dest: current.latency_dest || null, // UI doesn't send this
latency_target: current.latency_target || null, // UI doesn't send this
icp_filing: icp_filing !== undefined ? icp_filing : (current.icp_filing || null),
ps_filing: ps_filing !== undefined ? ps_filing : (current.ps_filing || null)
@@ -905,34 +936,35 @@ app.post('/api/settings', requireAuth, async (req, res) => {
// 4. Update database
await db.query(
- `INSERT INTO site_settings (
- id, page_name, show_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),
- show_page_name = VALUES(show_page_name),
- title = VALUES(title),
- logo_url = VALUES(logo_url),
+ `INSERT INTO site_settings (
+ id, 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,
+ blackbox_source_id, latency_source, latency_dest, latency_target,
+ icp_filing, ps_filing
+ ) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ page_name = VALUES(page_name),
+ show_page_name = VALUES(show_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),
- blackbox_source_id = VALUES(blackbox_source_id),
- latency_source = VALUES(latency_source),
- latency_dest = VALUES(latency_dest),
- latency_target = VALUES(latency_target),
- icp_filing = VALUES(icp_filing),
- ps_filing = VALUES(ps_filing)`,
- [
- 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.blackbox_source_id, settings.latency_source, settings.latency_dest, settings.latency_target,
- settings.icp_filing, settings.ps_filing
- ]
+ favicon_url = VALUES(favicon_url),
+ default_theme = VALUES(default_theme),
+ show_95_bandwidth = VALUES(show_95_bandwidth),
+ p95_type = VALUES(p95_type),
+ require_login_for_server_details = VALUES(require_login_for_server_details),
+ blackbox_source_id = VALUES(blackbox_source_id),
+ latency_source = VALUES(latency_source),
+ latency_dest = VALUES(latency_dest),
+ latency_target = VALUES(latency_target),
+ icp_filing = VALUES(icp_filing),
+ ps_filing = VALUES(ps_filing)`,
+ [
+ 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
+ ]
);
res.json({ success: true });
@@ -1153,7 +1185,7 @@ app.get('/api/metrics/cpu-history', async (req, res) => {
});
// Get detailed metrics for a specific server
-app.get('/api/metrics/server-details', requireAuth, async (req, res) => {
+app.get('/api/metrics/server-details', requireServerDetailsAccess, async (req, res) => {
const { instance, job, source } = req.query;
if (!instance || !job || !source) {
@@ -1178,7 +1210,7 @@ app.get('/api/metrics/server-details', requireAuth, async (req, res) => {
});
// Get historical metrics for a specific server
-app.get('/api/metrics/server-history', requireAuth, async (req, res) => {
+app.get('/api/metrics/server-history', requireServerDetailsAccess, async (req, res) => {
const { instance, job, source, metric, range, start, end } = req.query;
if (!instance || !job || !source || !metric) {