diff --git a/public/css/style.css b/public/css/style.css
index 88c7544..3952dab 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -1873,6 +1873,11 @@ input:checked+.slider:before {
}
/* ---- Animations ---- */
+@keyframes spin {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+}
+
@keyframes fadeInUp {
from {
opacity: 0;
diff --git a/public/index.html b/public/index.html
index a296192..163cd2b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -242,6 +242,13 @@
网络流量趋势 (24h)
+
{
+ const icon = dom.btnRefreshNetwork.querySelector('svg');
+ if (icon) icon.style.animation = 'spin 0.8s ease-in-out';
+
+ // Force refresh all Prometheus 24h data and overview
+ await Promise.all([
+ fetchNetworkHistory(true),
+ fetchMetrics(true)
+ ]);
+
+ if (icon) {
+ setTimeout(() => {
+ icon.style.animation = '';
+ }, 800);
+ }
+ });
+ }
+
// Keyboard shortcut
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
@@ -693,9 +713,10 @@
}
// ---- Fetch Metrics ----
- async function fetchMetrics() {
+ async function fetchMetrics(force = false) {
try {
- const response = await fetch('/api/metrics/overview');
+ const url = `/api/metrics/overview${force ? '?force=true' : ''}`;
+ const response = await fetch(url);
const data = await response.json();
allServersData = data.servers || [];
updateDashboard(data);
@@ -1619,9 +1640,10 @@
};
// ---- Network History ----
- async function fetchNetworkHistory() {
+ async function fetchNetworkHistory(force = false) {
try {
- const response = await fetch('/api/metrics/network-history');
+ const url = `/api/metrics/network-history${force ? '?force=true' : ''}`;
+ const response = await fetch(url);
const data = await response.json();
networkChart.setData(data);
if (dom.trafficP95 && networkChart.p95) {
diff --git a/server/index.js b/server/index.js
index 071fa35..f5220a0 100644
--- a/server/index.js
+++ b/server/index.js
@@ -663,7 +663,7 @@ app.post('/api/settings', requireAuth, async (req, res) => {
// ==================== Metrics Aggregation ====================
// Reusable function to get overview metrics
-async function getOverview() {
+async function getOverview(force = false) {
const [sources] = await db.query('SELECT * FROM prometheus_sources WHERE is_server_source = 1 AND type != "blackbox"');
if (sources.length === 0) {
return {
@@ -680,8 +680,12 @@ async function getOverview() {
const allMetrics = await Promise.all(sources.map(async (source) => {
const cacheKey = `source_metrics:${source.url}:${source.name}`;
- const cached = await cache.get(cacheKey);
- if (cached) return cached;
+ if (force) {
+ await cache.del(cacheKey);
+ } else {
+ const cached = await cache.get(cacheKey);
+ if (cached) return cached;
+ }
try {
const metrics = await prometheusService.getOverviewMetrics(source.url, source.name);
@@ -790,7 +794,8 @@ async function getOverview() {
// Get all aggregated metrics from all Prometheus sources
app.get('/api/metrics/overview', async (req, res) => {
try {
- const overview = await getOverview();
+ const force = req.query.force === 'true';
+ const overview = await getOverview(force);
res.json(overview);
} catch (err) {
console.error('Error fetching overview metrics:', err);
@@ -801,9 +806,15 @@ app.get('/api/metrics/overview', async (req, res) => {
// Get network traffic history (past 24h) from Prometheus
app.get('/api/metrics/network-history', async (req, res) => {
try {
+ const force = req.query.force === 'true';
const cacheKey = 'network_history_all';
- const cached = await cache.get(cacheKey);
- if (cached) return res.json(cached);
+
+ if (force) {
+ await cache.del(cacheKey);
+ } else {
+ const cached = await cache.get(cacheKey);
+ if (cached) return res.json(cached);
+ }
const [sources] = await db.query('SELECT * FROM prometheus_sources WHERE is_server_source = 1 AND type != "blackbox"');
if (sources.length === 0) {