新功能:允许查看服务器地址
This commit is contained in:
@@ -574,6 +574,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<small style="display: block; margin-top: 6px; color: var(--text-muted);">选择参与 24 小时网络流量统计的 Prometheus 数据源。如果不勾选任何项,则统计所有数据源。</small>
|
<small style="display: block; margin-top: 6px; color: var(--text-muted);">选择参与 24 小时网络流量统计的 Prometheus 数据源。如果不勾选任何项,则统计所有数据源。</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" style="margin-top: 15px;">
|
||||||
|
<label for="showServerIpInput">服务器详情内是否显示 IPv4 和 IPv6 地址</label>
|
||||||
|
<select id="showServerIpInput"
|
||||||
|
style="padding: 10px 14px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-sm); color: var(--text-primary); width: 100%;">
|
||||||
|
<option value="1">显示 (Show)</option>
|
||||||
|
<option value="0">隐藏 (Hide)</option>
|
||||||
|
</select>
|
||||||
|
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">开启后,点击服务器详情时会显示该服务器的公网 IP 地址(需 node_exporter 提供支持)。</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>
|
||||||
|
|||||||
@@ -113,6 +113,7 @@
|
|||||||
globeCard: document.getElementById('globeCard'),
|
globeCard: document.getElementById('globeCard'),
|
||||||
btnExpandGlobe: document.getElementById('btnExpandGlobe'),
|
btnExpandGlobe: document.getElementById('btnExpandGlobe'),
|
||||||
btnRefreshNetwork: document.getElementById('btnRefreshNetwork'),
|
btnRefreshNetwork: document.getElementById('btnRefreshNetwork'),
|
||||||
|
showServerIpInput: document.getElementById('showServerIpInput'),
|
||||||
// Footer & Filing
|
// Footer & Filing
|
||||||
icpFilingInput: document.getElementById('icpFilingInput'),
|
icpFilingInput: document.getElementById('icpFilingInput'),
|
||||||
psFilingInput: document.getElementById('psFilingInput'),
|
psFilingInput: document.getElementById('psFilingInput'),
|
||||||
@@ -539,6 +540,7 @@
|
|||||||
if (dom.psFilingInput) dom.psFilingInput.value = window.SITE_SETTINGS.ps_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.logoUrlDarkInput) dom.logoUrlDarkInput.value = window.SITE_SETTINGS.logo_url_dark || '';
|
||||||
if (dom.faviconUrlInput) dom.faviconUrlInput.value = window.SITE_SETTINGS.favicon_url || '';
|
if (dom.faviconUrlInput) dom.faviconUrlInput.value = window.SITE_SETTINGS.favicon_url || '';
|
||||||
|
if (dom.showServerIpInput) dom.showServerIpInput.value = window.SITE_SETTINGS.show_server_ip ? "1" : "0";
|
||||||
// Latency routes loaded separately in openSettings or on startup
|
// Latency routes loaded separately in openSettings or on startup
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1592,6 +1594,28 @@
|
|||||||
dom.detailDiskTotal.textContent = formatBytes(data.totalDiskSize || 0);
|
dom.detailDiskTotal.textContent = formatBytes(data.totalDiskSize || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IP Addresses
|
||||||
|
const infoGrid = document.getElementById('detailInfoGrid');
|
||||||
|
if (infoGrid) {
|
||||||
|
// Remove any previously added IP items
|
||||||
|
infoGrid.querySelectorAll('.info-item-ip').forEach(el => el.remove());
|
||||||
|
|
||||||
|
if (window.SITE_SETTINGS && window.SITE_SETTINGS.show_server_ip) {
|
||||||
|
if (data.ipv4 && data.ipv4.length > 0) {
|
||||||
|
const ipv4Item = document.createElement('div');
|
||||||
|
ipv4Item.className = 'info-item info-item-ip';
|
||||||
|
ipv4Item.innerHTML = `<span class="info-label">IPv4 地址</span><span class="info-value">${escapeHtml(data.ipv4.join(', '))}</span>`;
|
||||||
|
infoGrid.appendChild(ipv4Item);
|
||||||
|
}
|
||||||
|
if (data.ipv6 && data.ipv6.length > 0) {
|
||||||
|
const ipv6Item = document.createElement('div');
|
||||||
|
ipv6Item.className = 'info-item info-item-ip';
|
||||||
|
ipv6Item.innerHTML = `<span class="info-label">IPv6 地址</span><span class="info-value" style="font-size: 0.65rem; word-break: break-all;">${escapeHtml(data.ipv6.join(', '))}</span>`;
|
||||||
|
infoGrid.appendChild(ipv6Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Define metrics to show
|
// Define metrics to show
|
||||||
const cpuValueHtml = `
|
const cpuValueHtml = `
|
||||||
<div style="display: flex; align-items: baseline; gap: 8px;">
|
<div style="display: flex; align-items: baseline; gap: 8px;">
|
||||||
@@ -2068,7 +2092,8 @@
|
|||||||
p95_type: dom.p95TypeSelect ? dom.p95TypeSelect.value : 'tx',
|
p95_type: dom.p95TypeSelect ? dom.p95TypeSelect.value : 'tx',
|
||||||
ps_filing: dom.psFilingInput ? dom.psFilingInput.value.trim() : '',
|
ps_filing: dom.psFilingInput ? dom.psFilingInput.value.trim() : '',
|
||||||
icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value.trim() : '',
|
icp_filing: dom.icpFilingInput ? dom.icpFilingInput.value.trim() : '',
|
||||||
network_data_sources: Array.from(dom.networkSourceSelector.querySelectorAll('input[type="checkbox"]:checked')).map(cb => cb.value).join(',')
|
network_data_sources: Array.from(dom.networkSourceSelector.querySelectorAll('input[type="checkbox"]:checked')).map(cb => cb.value).join(','),
|
||||||
|
show_server_ip: dom.showServerIpInput ? (dom.showServerIpInput.value === "1") : false
|
||||||
};
|
};
|
||||||
|
|
||||||
dom.btnSaveSiteSettings.disabled = true;
|
dom.btnSaveSiteSettings.disabled = true;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ const SCHEMA = {
|
|||||||
icp_filing VARCHAR(255),
|
icp_filing VARCHAR(255),
|
||||||
ps_filing VARCHAR(255),
|
ps_filing VARCHAR(255),
|
||||||
network_data_sources TEXT,
|
network_data_sources TEXT,
|
||||||
|
show_server_ip TINYINT(1) DEFAULT 0,
|
||||||
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
|
||||||
`,
|
`,
|
||||||
@@ -127,6 +128,10 @@ const SCHEMA = {
|
|||||||
{
|
{
|
||||||
name: 'network_data_sources',
|
name: 'network_data_sources',
|
||||||
sql: "ALTER TABLE site_settings ADD COLUMN network_data_sources TEXT AFTER ps_filing"
|
sql: "ALTER TABLE site_settings ADD COLUMN network_data_sources TEXT AFTER ps_filing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'show_server_ip',
|
||||||
|
sql: "ALTER TABLE site_settings ADD COLUMN show_server_ip TINYINT(1) DEFAULT 0 AFTER network_data_sources"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ function getPublicSiteSettings(settings = {}) {
|
|||||||
: 1,
|
: 1,
|
||||||
icp_filing: settings.icp_filing || null,
|
icp_filing: settings.icp_filing || null,
|
||||||
ps_filing: settings.ps_filing || null,
|
ps_filing: settings.ps_filing || null,
|
||||||
network_data_sources: settings.network_data_sources || null
|
network_data_sources: settings.network_data_sources || null,
|
||||||
|
show_server_ip: settings.show_server_ip ? 1 : 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +548,7 @@ app.post('/api/setup/init', ensureSetupAccess, async (req, res) => {
|
|||||||
icp_filing VARCHAR(255),
|
icp_filing VARCHAR(255),
|
||||||
ps_filing VARCHAR(255),
|
ps_filing VARCHAR(255),
|
||||||
network_data_sources TEXT,
|
network_data_sources TEXT,
|
||||||
|
show_server_ip TINYINT(1) DEFAULT 0,
|
||||||
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
|
||||||
`);
|
`);
|
||||||
@@ -561,6 +563,7 @@ app.post('/api/setup/init', ensureSetupAccess, async (req, res) => {
|
|||||||
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 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("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS require_login_for_server_details TINYINT(1) DEFAULT 1 AFTER p95_type");
|
||||||
await connection.query("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS network_data_sources TEXT AFTER ps_filing");
|
await connection.query("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS network_data_sources TEXT AFTER ps_filing");
|
||||||
|
await connection.query("ALTER TABLE site_settings ADD COLUMN IF NOT EXISTS show_server_ip TINYINT(1) DEFAULT 0 AFTER network_data_sources");
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
CREATE TABLE IF NOT EXISTS latency_routes (
|
CREATE TABLE IF NOT EXISTS latency_routes (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
@@ -894,7 +897,7 @@ 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, network_data_sources
|
icp_filing, ps_filing, network_data_sources, show_server_ip
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
// 3. Prepare parameters, prioritizing body but falling back to current
|
// 3. Prepare parameters, prioritizing body but falling back to current
|
||||||
@@ -917,7 +920,8 @@ app.post('/api/settings', requireAuth, async (req, res) => {
|
|||||||
latency_target: current.latency_target || null,
|
latency_target: current.latency_target || null,
|
||||||
icp_filing: icp_filing !== undefined ? icp_filing : (current.icp_filing || null),
|
icp_filing: icp_filing !== undefined ? icp_filing : (current.icp_filing || null),
|
||||||
ps_filing: ps_filing !== undefined ? ps_filing : (current.ps_filing || null),
|
ps_filing: ps_filing !== undefined ? ps_filing : (current.ps_filing || null),
|
||||||
network_data_sources: network_data_sources !== undefined ? network_data_sources : (current.network_data_sources || null)
|
network_data_sources: network_data_sources !== undefined ? network_data_sources : (current.network_data_sources || null),
|
||||||
|
show_server_ip: show_server_ip !== undefined ? (show_server_ip ? 1 : 0) : (current.show_server_ip || 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
await db.query(`
|
await db.query(`
|
||||||
@@ -925,8 +929,8 @@ app.post('/api/settings', requireAuth, async (req, res) => {
|
|||||||
id, page_name, show_page_name, title, logo_url, logo_url_dark, favicon_url,
|
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,
|
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, network_data_sources
|
icp_filing, ps_filing, network_data_sources, show_server_ip
|
||||||
) 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),
|
||||||
@@ -944,12 +948,13 @@ app.post('/api/settings', requireAuth, async (req, res) => {
|
|||||||
latency_target = VALUES(latency_target),
|
latency_target = VALUES(latency_target),
|
||||||
icp_filing = VALUES(icp_filing),
|
icp_filing = VALUES(icp_filing),
|
||||||
ps_filing = VALUES(ps_filing),
|
ps_filing = VALUES(ps_filing),
|
||||||
network_data_sources = VALUES(network_data_sources)`,
|
network_data_sources = VALUES(network_data_sources),
|
||||||
|
show_server_ip = VALUES(show_server_ip)`,
|
||||||
[
|
[
|
||||||
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.network_data_sources
|
settings.icp_filing, settings.ps_filing, settings.network_data_sources, settings.show_server_ip
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -559,7 +559,9 @@ async function getServerDetails(baseUrl, instance, job) {
|
|||||||
sockstatTcpMem: `node_sockstat_TCP_mem{instance="${node}",job="${job}"} * 4096`,
|
sockstatTcpMem: `node_sockstat_TCP_mem{instance="${node}",job="${job}"} * 4096`,
|
||||||
// Get individual partitions (excluding virtual and FUSE mounts)
|
// Get individual partitions (excluding virtual and FUSE mounts)
|
||||||
partitions_size: `node_filesystem_size_bytes{instance="${node}", job="${job}", fstype!~"tmpfs|autofs|proc|sysfs|fuse.*", mountpoint!~"/tmp.*|/var/lib/docker/.*|/run/.*"}`,
|
partitions_size: `node_filesystem_size_bytes{instance="${node}", job="${job}", fstype!~"tmpfs|autofs|proc|sysfs|fuse.*", mountpoint!~"/tmp.*|/var/lib/docker/.*|/run/.*"}`,
|
||||||
partitions_free: `node_filesystem_free_bytes{instance="${node}", job="${job}", fstype!~"tmpfs|autofs|proc|sysfs|fuse.*", mountpoint!~"/tmp.*|/var/lib/docker/.*|/run/.*"}`
|
partitions_free: `node_filesystem_free_bytes{instance="${node}", job="${job}", fstype!~"tmpfs|autofs|proc|sysfs|fuse.*", mountpoint!~"/tmp.*|/var/lib/docker/.*|/run/.*"}`,
|
||||||
|
ipv4: `node_network_address_info{instance="${node}", job="${job}", address!~"127\\\\..*|10\\\\..*|172\\\\.(1[6-9]|2[0-9]|3[0-1])\\\\..*|192\\\\.168\\\\..*", address=~"\\\\d+\\\\.\\\\d+\\\\.\\\\d+\\\\.\\\\d+"}`,
|
||||||
|
ipv6: `node_network_address_info{instance="${node}", job="${job}", address=~".*:.*", address!~"fe80:.*|::1"}`
|
||||||
};
|
};
|
||||||
|
|
||||||
const results = {};
|
const results = {};
|
||||||
@@ -571,6 +573,8 @@ async function getServerDetails(baseUrl, instance, job) {
|
|||||||
mountpoint: r.metric.mountpoint,
|
mountpoint: r.metric.mountpoint,
|
||||||
value: parseFloat(r.value[1]) || 0
|
value: parseFloat(r.value[1]) || 0
|
||||||
}));
|
}));
|
||||||
|
} else if (key === 'ipv4' || key === 'ipv6') {
|
||||||
|
results[key] = res.map(r => r.metric.address).filter(a => a);
|
||||||
} else {
|
} else {
|
||||||
results[key] = res.length > 0 ? parseFloat(res[0].value[1]) : 0;
|
results[key] = res.length > 0 ? parseFloat(res[0].value[1]) : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user