修复项目逻辑错误

新增数据库检查
This commit is contained in:
CN-JS-HuiBai
2026-04-10 23:42:55 +08:00
parent 2cd6c6ef27
commit 7362bcf206
7 changed files with 266 additions and 377 deletions

View File

@@ -587,7 +587,7 @@
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">开启后,未登录访客仍可看到大屏总览,但点击单台服务器时需要先登录。</p>
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="showServerIpInput">服务器详情内是否显示 IPv4 和 IPv6 地址</label>
<label for="showServerIpInput">是否在服务器详情中显示公网 IP</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>
@@ -595,23 +595,14 @@
</select>
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">开启后,点击服务器详情时会显示该服务器的公网 IP 地址。</p>
</div>
<div style="margin-top: 25px; padding-top: 15px; border-top: 1px dashed var(--border-color);">
<h4 style="margin-bottom: 12px; color: var(--text-secondary); font-size: 0.95rem;">高级指标配置 (可选)</h4>
<div class="form-group">
<label for="ipMetricNameInput">IP 发现指标 (Metric Name)</label>
<input type="text" id="ipMetricNameInput" placeholder="例如: node_network_address_info"
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%;">
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">留空则使用 Prometheus Target 自动发现。若需兼容 <code>node_exporter</code> 自定义指标(如 textfile请在此输入指标名。</p>
</div>
<div class="form-group" style="margin-top: 12px;">
<label for="ipLabelNameInput">IP 提取标签 (Label Name)</label>
<input type="text" id="ipLabelNameInput" placeholder="address"
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%;">
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">从该指标的哪个标签提取 IP默认为 <code>address</code></p>
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="ipMetricNameInput">自定义 IP 采集指标 (可选)</label>
<input type="text" id="ipMetricNameInput" placeholder="例node_network_address_info">
<p style="font-size: 0.72rem; color: var(--text-muted); margin-top: 6px;">如果您的 Prometheus 中有专门记录 IP 的指标,请在此输入。留空则尝试自动发现。</p>
</div>
<div class="form-group" style="margin-top: 15px;">
<label for="ipLabelNameInput">IP 指标中的 Label 名称</label>
<input type="text" id="ipLabelNameInput" placeholder="默认address">
</div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
<button class="btn btn-add" id="btnSaveSecuritySettings">保存安全设置</button>
@@ -620,6 +611,26 @@
</div>
</div>
<!-- Custom Detail Metrics Tab -->
<div class="tab-content" id="tab-details-metrics">
<div class="metrics-settings-form">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h3 style="margin: 0;">服务器详情指标配置</h3>
<button class="btn btn-add" id="btnAddCustomMetric" style="padding: 6px 12px; font-size: 0.8rem;">
<i class="fas fa-plus"></i> 添加指标
</button>
</div>
<div id="customMetricsList" class="custom-metrics-list" style="max-height: 400px; overflow-y: auto; padding-right: 5px;">
<!-- Dynamic rows will be added here -->
</div>
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
<button class="btn btn-add" id="btnSaveCustomMetrics">保存指标配置</button>
</div>
</div>
</div>
<!-- Latency Routes Tab -->
<div class="tab-content" id="tab-latency">
<div class="latency-settings-form">

View File

@@ -121,9 +121,13 @@
icpFilingInput: document.getElementById('icpFilingInput'),
psFilingInput: document.getElementById('psFilingInput'),
icpFilingDisplay: document.getElementById('icpFilingDisplay'),
psFilingDisplay: document.getElementById('psFilingDisplay'),
psFilingText: document.getElementById('psFilingText'),
copyrightYear: document.getElementById('copyrightYear')
ps_filingDisplay: document.getElementById('psFilingDisplay'),
ps_filingText: document.getElementById('psFilingText'),
copyrightYear: document.getElementById('copyrightYear'),
customMetricsList: document.getElementById('customMetricsList'),
btnAddCustomMetric: document.getElementById('btnAddCustomMetric'),
btnSaveCustomMetrics: document.getElementById('btnSaveCustomMetrics'),
customDataContainer: document.getElementById('customDataContainer')
};
// ---- State ----
@@ -193,6 +197,71 @@
// Network chart
networkChart = new AreaChart(dom.networkCanvas);
// ---- Custom Metrics Helpers ----
function addMetricRow(config = {}) {
const row = document.createElement('div');
row.className = 'metric-row';
row.style = 'background: rgba(255,255,255,0.03); padding: 12px; border-radius: 8px; margin-bottom: 10px; border: 1px solid var(--border-color);';
row.innerHTML = `
<div style="display: grid; grid-template-columns: 1fr 1.5fr 1fr; gap: 10px; margin-bottom: 8px;">
<input type="text" placeholder="显示名 (如:内核)" class="metric-display-name" value="${config.name || ''}" style="padding: 6px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: 4px; color: var(--text-primary); font-size: 0.85rem;">
<input type="text" placeholder="指标名 (PromQL)" class="metric-query" value="${config.metric || ''}" style="padding: 6px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: 4px; color: var(--text-primary); font-size: 0.85rem;">
<input type="text" placeholder="取值标签" class="metric-label" value="${config.label || ''}" style="padding: 6px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: 4px; color: var(--text-primary); font-size: 0.85rem;">
</div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<label style="font-size: 0.75rem; color: var(--text-muted); display: flex; align-items: center; cursor: pointer;">
<input type="checkbox" class="metric-is-ip" ${config.is_ip ? 'checked' : ''} style="margin-right: 5px;"> 设为 IP 发现源
</label>
<button class="btn-remove-metric" style="background: none; border: none; color: #ff4d4f; cursor: pointer; font-size: 0.75rem;">
<i class="fas fa-trash"></i> 删除
</button>
</div>
`;
row.querySelector('.btn-remove-metric').onclick = () => row.remove();
dom.customMetricsList.appendChild(row);
}
function loadCustomMetricsUI(metrics) {
if (!dom.customMetricsList) return;
dom.customMetricsList.innerHTML = '';
let list = [];
try {
list = typeof metrics === 'string' ? JSON.parse(metrics) : (metrics || []);
} catch(e) {}
if (Array.isArray(list)) {
list.forEach(m => addMetricRow(m));
}
if (list.length === 0) {
// Add a placeholder/default row if empty
}
}
function getCustomMetricsFromUI() {
const rows = dom.customMetricsList.querySelectorAll('.metric-row');
const metrics = [];
rows.forEach(row => {
const name = row.querySelector('.metric-display-name').value.trim();
const metric = row.querySelector('.metric-query').value.trim();
const label = row.querySelector('.metric-label').value.trim();
const is_ip = row.querySelector('.metric-is-ip').checked;
if (metric) {
metrics.push({ name, metric, label, is_ip });
}
});
return metrics;
}
// Bind Events
if (dom.btnAddCustomMetric) dom.btnAddCustomMetric.onclick = () => addMetricRow();
if (dom.btnSaveCustomMetrics) {
dom.btnSaveCustomMetrics.onclick = saveSiteSettings;
}
})();
// Initial map
initMap2D();
@@ -547,6 +616,9 @@
// Apply security dependency
updateSecurityDependency();
// Load custom metrics
loadCustomMetricsUI(window.SITE_SETTINGS.custom_metrics);
}
loadSiteSettings();
@@ -1715,7 +1787,25 @@
</div>
`).join('');
// Handle partitions integration: Move the expandable partition section UNDER the Disk Usage metric
// Render Custom Data
const customDataContainer = dom.customDataContainer;
if (customDataContainer) {
customDataContainer.innerHTML = '';
if (data.custom_data && data.custom_data.length > 0) {
data.custom_data.forEach(item => {
const card = document.createElement('div');
card.className = 'detail-metric-card';
card.style.flex = '1 1 calc(50% - 10px)';
card.innerHTML = `
<span class="detail-metric-label">${item.name}</span>
<span class="detail-metric-value">${item.value || '-'}</span>
`;
customDataContainer.appendChild(card);
});
}
}
// Partitions
if (data.partitions && data.partitions.length > 0) {
dom.detailPartitionsContainer.style.display = 'block';
dom.partitionSummary.textContent = `${data.partitions.length} 个本地分区`;
@@ -1997,8 +2087,11 @@
// Update IP visibility input
if (dom.showServerIpInput) dom.showServerIpInput.value = settings.show_server_ip ? "1" : "0";
if (dom.ipMetricNameInput) dom.ipMetricNameInput.value = settings.ip_metric_name || '';
if (dom.ipLabelNameInput) dom.ipLabelNameInput.value = settings.ip_label_name || 'address';
if (dom.ipLabelNameInput) dom.ipLabelNameInput.value = settings.ip_label_name || '';
// Load Custom Metrics
loadCustomMetricsUI(settings.custom_metrics);
// Sync security tab dependency
updateSecurityDependency();
} catch (err) {
@@ -2118,7 +2211,8 @@
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,
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()
};
// UI Feedback for both potential save buttons