修复数据库查询错误导致的故障

This commit is contained in:
CN-JS-HuiBai
2026-04-11 17:41:44 +08:00
parent bdc723d197
commit 27c2fb0b95
2 changed files with 70 additions and 59 deletions

View File

@@ -621,7 +621,7 @@
<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="btnSaveSecuritySettings">保存安全设置</button> <button class="btn btn-add" id="btnSaveSecuritySettings">保存安全设置</button>
</div> </div>
<div class="form-message" id="siteSettingsMessage"></div> <div class="form-message" id="securitySettingsMessage"></div>
</div> </div>
</div> </div>
@@ -642,6 +642,7 @@
<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="btnSaveCustomMetrics">保存指标配置</button> <button class="btn btn-add" id="btnSaveCustomMetrics">保存指标配置</button>
</div> </div>
<div class="form-message" id="customMetricsMessage"></div>
</div> </div>
</div> </div>

View File

@@ -54,6 +54,8 @@
btnSaveSiteSettings: document.getElementById('btnSaveSiteSettings'), btnSaveSiteSettings: document.getElementById('btnSaveSiteSettings'),
btnSaveSecuritySettings: document.getElementById('btnSaveSecuritySettings'), btnSaveSecuritySettings: document.getElementById('btnSaveSecuritySettings'),
siteSettingsMessage: document.getElementById('siteSettingsMessage'), siteSettingsMessage: document.getElementById('siteSettingsMessage'),
securitySettingsMessage: document.getElementById('securitySettingsMessage'),
customMetricsMessage: document.getElementById('customMetricsMessage'),
logoText: document.getElementById('logoText'), logoText: document.getElementById('logoText'),
logoIconContainer: document.getElementById('logoIconContainer'), logoIconContainer: document.getElementById('logoIconContainer'),
defaultThemeInput: document.getElementById('defaultThemeInput'), defaultThemeInput: document.getElementById('defaultThemeInput'),
@@ -121,8 +123,8 @@
icpFilingInput: document.getElementById('icpFilingInput'), icpFilingInput: document.getElementById('icpFilingInput'),
psFilingInput: document.getElementById('psFilingInput'), psFilingInput: document.getElementById('psFilingInput'),
icpFilingDisplay: document.getElementById('icpFilingDisplay'), icpFilingDisplay: document.getElementById('icpFilingDisplay'),
ps_filingDisplay: document.getElementById('psFilingDisplay'), psFilingDisplay: document.getElementById('psFilingDisplay'),
ps_filingText: document.getElementById('psFilingText'), psFilingText: document.getElementById('psFilingText'),
copyrightYear: document.getElementById('copyrightYear'), copyrightYear: document.getElementById('copyrightYear'),
customMetricsList: document.getElementById('customMetricsList'), customMetricsList: document.getElementById('customMetricsList'),
btnAddCustomMetric: document.getElementById('btnAddCustomMetric'), btnAddCustomMetric: document.getElementById('btnAddCustomMetric'),
@@ -181,26 +183,7 @@
throw lastError || new Error('All JSON sources failed'); throw lastError || new Error('All JSON sources failed');
} }
// ---- Initialize ----
function init() {
try {
console.log('[Init] Start...');
// Resource Gauges Time
updateGaugesTime();
setInterval(updateGaugesTime, 1000);
// Initial footer year
if (dom.copyrightYear) {
dom.copyrightYear.textContent = new Date().getFullYear();
}
// Initial theme check (localStorage handled after site settings load to ensure priority)
// Network chart
networkChart = new AreaChart(dom.networkCanvas);
// ---- Custom Metrics Helpers ---- // ---- Custom Metrics Helpers ----
function addMetricRow(config = {}) { function addMetricRow(config = {}) {
const row = document.createElement('div'); const row = document.createElement('div');
row.className = 'metric-row'; row.className = 'metric-row';
@@ -231,7 +214,7 @@
let list = []; let list = [];
try { try {
list = typeof metrics === 'string' ? JSON.parse(metrics) : (metrics || []); list = typeof metrics === 'string' ? JSON.parse(metrics) : (metrics || []);
} catch(e) {} } catch (e) { }
if (Array.isArray(list)) { if (Array.isArray(list)) {
list.forEach(m => addMetricRow(m)); list.forEach(m => addMetricRow(m));
@@ -257,12 +240,24 @@
return metrics; return metrics;
} }
// Bind Events // ---- Initialize ----
if (dom.btnAddCustomMetric) dom.btnAddCustomMetric.onclick = () => addMetricRow(); function init() {
if (dom.btnSaveCustomMetrics) { try {
dom.btnSaveCustomMetrics.onclick = saveSiteSettings; console.log('[Init] Start...');
// Resource Gauges Time
updateGaugesTime();
setInterval(updateGaugesTime, 1000);
// Initial footer year
if (dom.copyrightYear) {
dom.copyrightYear.textContent = new Date().getFullYear();
} }
// Initial theme check (localStorage handled after site settings load to ensure priority)
// Network chart
networkChart = new AreaChart(dom.networkCanvas);
// Initial map // Initial map
initMap2D(); initMap2D();
@@ -319,8 +314,12 @@
if (dom.btnSaveSiteSettings) { if (dom.btnSaveSiteSettings) {
dom.btnSaveSiteSettings.addEventListener('click', saveSiteSettings); dom.btnSaveSiteSettings.addEventListener('click', saveSiteSettings);
} }
// Custom Metrics
if (dom.btnAddCustomMetric) {
dom.btnAddCustomMetric.addEventListener('click', () => addMetricRow());
}
if (dom.btnSaveCustomMetrics) { if (dom.btnSaveCustomMetrics) {
dom.btnSaveCustomMetrics.onclick = saveSiteSettings; dom.btnSaveCustomMetrics.addEventListener('click', saveSiteSettings);
} }
if (dom.btnAddRoute) { if (dom.btnAddRoute) {
dom.btnAddRoute.addEventListener('click', addLatencyRoute); dom.btnAddRoute.addEventListener('click', addLatencyRoute);
@@ -2203,13 +2202,26 @@
} }
} }
async function saveSiteSettings() { async function saveSiteSettings(e) {
let messageTarget = dom.siteSettingsMessage;
if (e && e.target) {
if (e.target.id === 'btnSaveSecuritySettings') messageTarget = dom.securitySettingsMessage;
else if (e.target.id === 'btnSaveCustomMetrics') messageTarget = dom.customMetricsMessage;
}
if (!user) { if (!user) {
showSiteMessage('Please login first', 'error'); showSiteMessage('请先登录后操作', 'error', messageTarget);
openLoginModal(); openLoginModal();
return; return;
} }
const saveButtons = [dom.btnSaveSiteSettings, dom.btnSaveSecuritySettings, dom.btnSaveCustomMetrics].filter(b => b);
saveButtons.forEach(btn => {
btn.disabled = true;
btn.originalText = btn.textContent;
btn.textContent = '保存中...';
});
const settings = { const settings = {
page_name: dom.pageNameInput.value.trim(), page_name: dom.pageNameInput.value.trim(),
title: dom.siteTitleInput ? dom.siteTitleInput.value.trim() : dom.pageNameInput.value.trim(), title: dom.siteTitleInput ? dom.siteTitleInput.value.trim() : dom.pageNameInput.value.trim(),
@@ -2230,14 +2242,6 @@
custom_metrics: getCustomMetricsFromUI() custom_metrics: getCustomMetricsFromUI()
}; };
// UI Feedback for both potential save buttons
const saveButtons = [dom.btnSaveSiteSettings, dom.btnSaveSecuritySettings].filter(b => b);
saveButtons.forEach(btn => {
btn.disabled = true;
btn.originalText = btn.textContent;
btn.textContent = '保存中...';
});
try { try {
const response = await fetch('/api/settings', { const response = await fetch('/api/settings', {
method: 'POST', method: 'POST',
@@ -2246,31 +2250,28 @@
}); });
if (response.ok) { if (response.ok) {
showSiteMessage('设置保存成功', 'success'); const data = await response.json();
// Update global object and UI immediately window.SITE_SETTINGS = data.settings;
window.SITE_SETTINGS = { ...window.SITE_SETTINGS, ...settings }; applySiteSettings(window.SITE_SETTINGS);
showSiteMessage('设置保存成功', 'success', messageTarget);
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);
// Apply settings to UI (logo, name, etc.)
applySiteSettings(window.SITE_SETTINGS);
// Refresh overview and historical charts to reflect new source selections // Refresh overview and historical charts to reflect new source selections
fetchNetworkHistory(true); fetchNetworkHistory(true);
// We can't force the WS broadcast easily from client,
// but we can fetch the overview via REST API once to update UI
fetch('/api/metrics/overview?force=true') fetch('/api/metrics/overview?force=true')
.then(res => res.json()) .then(res => res.json())
.then(data => updateDashboard(data)) .then(data => updateDashboard(data))
.catch(() => {}); .catch(() => {});
} else { } else {
const err = await response.json(); const err = await response.json();
showSiteMessage(`保存失败: ${err.error || '未知错误'}`, 'error'); showSiteMessage(`保存失败: ${err.error || '未知错误'}`, 'error', messageTarget);
if (response.status === 401) openLoginModal(); if (response.status === 401) openLoginModal();
} }
} catch (err) { } catch (err) {
showSiteMessage(`保存失败: ${err.message}`, 'error'); showSiteMessage(`请求失败: ${err.message}`, 'error', messageTarget);
console.error('Save settings error:', err); console.error('Save settings error:', err);
} finally { } finally {
saveButtons.forEach(btn => { saveButtons.forEach(btn => {
@@ -2428,14 +2429,23 @@
} }
}; };
function showSiteMessage(text, type) { function showSiteMessage(text, type, target = null) {
dom.siteSettingsMessage.textContent = text; const el = target || dom.siteSettingsMessage;
dom.siteSettingsMessage.className = `form-message ${type}`; if (!el) return;
setTimeout(hideSiteMessage, 5000); el.textContent = text;
el.className = `form-message ${type}`;
// Clear existing timeout if any (simplified)
if (el._msgTimeout) clearTimeout(el._msgTimeout);
el._msgTimeout = setTimeout(() => {
el.className = 'form-message';
el._msgTimeout = null;
}, 5000);
} }
function hideSiteMessage() { function hideSiteMessage(target = null) {
dom.siteSettingsMessage.className = 'form-message'; const el = target || dom.siteSettingsMessage;
if (el) el.className = 'form-message';
} }
async function saveChangePassword() { async function saveChangePassword() {