修复数据库查询错误导致的故障
This commit is contained in:
@@ -621,7 +621,7 @@
|
||||
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
|
||||
<button class="btn btn-add" id="btnSaveSecuritySettings">保存安全设置</button>
|
||||
</div>
|
||||
<div class="form-message" id="siteSettingsMessage"></div>
|
||||
<div class="form-message" id="securitySettingsMessage"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -642,6 +642,7 @@
|
||||
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
|
||||
<button class="btn btn-add" id="btnSaveCustomMetrics">保存指标配置</button>
|
||||
</div>
|
||||
<div class="form-message" id="customMetricsMessage"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
116
public/js/app.js
116
public/js/app.js
@@ -54,6 +54,8 @@
|
||||
btnSaveSiteSettings: document.getElementById('btnSaveSiteSettings'),
|
||||
btnSaveSecuritySettings: document.getElementById('btnSaveSecuritySettings'),
|
||||
siteSettingsMessage: document.getElementById('siteSettingsMessage'),
|
||||
securitySettingsMessage: document.getElementById('securitySettingsMessage'),
|
||||
customMetricsMessage: document.getElementById('customMetricsMessage'),
|
||||
logoText: document.getElementById('logoText'),
|
||||
logoIconContainer: document.getElementById('logoIconContainer'),
|
||||
defaultThemeInput: document.getElementById('defaultThemeInput'),
|
||||
@@ -121,8 +123,8 @@
|
||||
icpFilingInput: document.getElementById('icpFilingInput'),
|
||||
psFilingInput: document.getElementById('psFilingInput'),
|
||||
icpFilingDisplay: document.getElementById('icpFilingDisplay'),
|
||||
ps_filingDisplay: document.getElementById('psFilingDisplay'),
|
||||
ps_filingText: document.getElementById('psFilingText'),
|
||||
psFilingDisplay: document.getElementById('psFilingDisplay'),
|
||||
psFilingText: document.getElementById('psFilingText'),
|
||||
copyrightYear: document.getElementById('copyrightYear'),
|
||||
customMetricsList: document.getElementById('customMetricsList'),
|
||||
btnAddCustomMetric: document.getElementById('btnAddCustomMetric'),
|
||||
@@ -181,26 +183,7 @@
|
||||
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 ----
|
||||
|
||||
function addMetricRow(config = {}) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'metric-row';
|
||||
@@ -231,7 +214,7 @@
|
||||
let list = [];
|
||||
try {
|
||||
list = typeof metrics === 'string' ? JSON.parse(metrics) : (metrics || []);
|
||||
} catch(e) {}
|
||||
} catch (e) { }
|
||||
|
||||
if (Array.isArray(list)) {
|
||||
list.forEach(m => addMetricRow(m));
|
||||
@@ -257,12 +240,24 @@
|
||||
return metrics;
|
||||
}
|
||||
|
||||
// Bind Events
|
||||
if (dom.btnAddCustomMetric) dom.btnAddCustomMetric.onclick = () => addMetricRow();
|
||||
if (dom.btnSaveCustomMetrics) {
|
||||
dom.btnSaveCustomMetrics.onclick = saveSiteSettings;
|
||||
// ---- 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);
|
||||
|
||||
// Initial map
|
||||
initMap2D();
|
||||
|
||||
@@ -319,8 +314,12 @@
|
||||
if (dom.btnSaveSiteSettings) {
|
||||
dom.btnSaveSiteSettings.addEventListener('click', saveSiteSettings);
|
||||
}
|
||||
// Custom Metrics
|
||||
if (dom.btnAddCustomMetric) {
|
||||
dom.btnAddCustomMetric.addEventListener('click', () => addMetricRow());
|
||||
}
|
||||
if (dom.btnSaveCustomMetrics) {
|
||||
dom.btnSaveCustomMetrics.onclick = saveSiteSettings;
|
||||
dom.btnSaveCustomMetrics.addEventListener('click', saveSiteSettings);
|
||||
}
|
||||
if (dom.btnAddRoute) {
|
||||
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) {
|
||||
showSiteMessage('Please login first', 'error');
|
||||
showSiteMessage('请先登录后操作', 'error', messageTarget);
|
||||
openLoginModal();
|
||||
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 = {
|
||||
page_name: dom.pageNameInput.value.trim(),
|
||||
title: dom.siteTitleInput ? dom.siteTitleInput.value.trim() : dom.pageNameInput.value.trim(),
|
||||
@@ -2230,14 +2242,6 @@
|
||||
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 {
|
||||
const response = await fetch('/api/settings', {
|
||||
method: 'POST',
|
||||
@@ -2246,31 +2250,28 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showSiteMessage('设置保存成功', 'success');
|
||||
// Update global object and UI immediately
|
||||
window.SITE_SETTINGS = { ...window.SITE_SETTINGS, ...settings };
|
||||
const data = await response.json();
|
||||
window.SITE_SETTINGS = data.settings;
|
||||
applySiteSettings(window.SITE_SETTINGS);
|
||||
showSiteMessage('设置保存成功', 'success', messageTarget);
|
||||
|
||||
const savedTheme = localStorage.getItem('theme');
|
||||
const themeToApply = savedTheme || settings.default_theme || 'dark';
|
||||
applyTheme(themeToApply);
|
||||
|
||||
// Apply settings to UI (logo, name, etc.)
|
||||
applySiteSettings(window.SITE_SETTINGS);
|
||||
|
||||
// Refresh overview and historical charts to reflect new source selections
|
||||
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')
|
||||
.then(res => res.json())
|
||||
.then(data => updateDashboard(data))
|
||||
.catch(() => {});
|
||||
} else {
|
||||
const err = await response.json();
|
||||
showSiteMessage(`保存失败: ${err.error || '未知错误'}`, 'error');
|
||||
showSiteMessage(`保存失败: ${err.error || '未知错误'}`, 'error', messageTarget);
|
||||
if (response.status === 401) openLoginModal();
|
||||
}
|
||||
} catch (err) {
|
||||
showSiteMessage(`保存失败: ${err.message}`, 'error');
|
||||
showSiteMessage(`请求失败: ${err.message}`, 'error', messageTarget);
|
||||
console.error('Save settings error:', err);
|
||||
} finally {
|
||||
saveButtons.forEach(btn => {
|
||||
@@ -2428,14 +2429,23 @@
|
||||
}
|
||||
};
|
||||
|
||||
function showSiteMessage(text, type) {
|
||||
dom.siteSettingsMessage.textContent = text;
|
||||
dom.siteSettingsMessage.className = `form-message ${type}`;
|
||||
setTimeout(hideSiteMessage, 5000);
|
||||
function showSiteMessage(text, type, target = null) {
|
||||
const el = target || dom.siteSettingsMessage;
|
||||
if (!el) return;
|
||||
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() {
|
||||
dom.siteSettingsMessage.className = 'form-message';
|
||||
function hideSiteMessage(target = null) {
|
||||
const el = target || dom.siteSettingsMessage;
|
||||
if (el) el.className = 'form-message';
|
||||
}
|
||||
|
||||
async function saveChangePassword() {
|
||||
|
||||
Reference in New Issue
Block a user