From 8cc357590237f4a2f38e5be82d75f368ff0ca1f2 Mon Sep 17 00:00:00 2001 From: shats Date: Wed, 18 Feb 2026 14:11:27 +0700 Subject: [PATCH] feat(NetworkMapScheduler): add next scheduled run time to settings response and update frontend to display next run time --- backend/routes/schedulerRoutes.js | 3 ++- backend/services/networkMapScheduler.js | 16 ++++++++++++++ frontend/src/NetworkMapDashboard.jsx | 27 ++---------------------- frontend/src/NetworkMapSchedulerPage.jsx | 13 ++++++++++++ 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/backend/routes/schedulerRoutes.js b/backend/routes/schedulerRoutes.js index 9c4919f..90910dc 100644 --- a/backend/routes/schedulerRoutes.js +++ b/backend/routes/schedulerRoutes.js @@ -32,7 +32,8 @@ async function getNetworkMapSchedulerSettings(req, res) { try { const ui = await readUiSettings(); const settings = networkMapScheduler.getSettingsFromUiSettings(ui); - return res.json(settings); + const nextRunAt = networkMapScheduler.getNextRunAt(settings); + return res.json({ ...settings, nextRunAt: nextRunAt || undefined }); } catch (e) { console.error('[scheduler] getSettings', e); return sendError(res, 500, 'Не удалось прочитать настройки', 'E_SCHEDULER'); diff --git a/backend/services/networkMapScheduler.js b/backend/services/networkMapScheduler.js index cfa4c16..71cf2e9 100644 --- a/backend/services/networkMapScheduler.js +++ b/backend/services/networkMapScheduler.js @@ -320,6 +320,8 @@ async function writeCache(pingMap, speedMap) { } let schedulerTimer = null; +/** Время последнего запуска по расписанию (для расчёта следующего) */ +let lastScheduledRunAt = null; function getSettingsFromUiSettings(uiSettings) { return { @@ -341,6 +343,7 @@ function initNetworkMapScheduler(logger, port) { const baseUrl = `http://127.0.0.1:${port}`; async function tick() { + lastScheduledRunAt = Date.now(); let uiSettings; try { const raw = await readS3TextObject(UI_SETTINGS_KEY).catch(() => ({ body: '{}' })); @@ -418,6 +421,18 @@ function pushLog(msg) { logBuffer.push(msg); } +/** + * Время следующего запуска по расписанию (timestamp или null если выключено). + * @param {object} settings - результат getSettingsFromUiSettings() + * @returns {number|null} + */ +function getNextRunAt(settings) { + if (!settings || !settings.enabled) return null; + const intervalMs = (settings.intervalMinutes || DEFAULT_INTERVAL_MINUTES) * 60 * 1000; + const from = lastScheduledRunAt || Date.now(); + return from + intervalMs; +} + module.exports = { runJob, initNetworkMapScheduler, @@ -427,6 +442,7 @@ module.exports = { setLogMaxLines, pushLog, getSettingsFromUiSettings, + getNextRunAt, loadUiSettings: loadUiSettings, buildConnections, LogBuffer, diff --git a/frontend/src/NetworkMapDashboard.jsx b/frontend/src/NetworkMapDashboard.jsx index 495a952..4a5c55b 100644 --- a/frontend/src/NetworkMapDashboard.jsx +++ b/frontend/src/NetworkMapDashboard.jsx @@ -278,31 +278,8 @@ export default function NetworkMapDashboard() { fetchData(); }, [fetchData]); - useEffect(() => { - if (!loading && servers.length > 0 && connections.length > 0) { - const cacheMaxAgeMs = Math.max((pingCacheSeconds || 60) * 1000, 60 * 1000); - if (cacheAppliedAtRef.current && Date.now() - cacheAppliedAtRef.current < cacheMaxAgeMs) { - return; - } - requestPings(); - } - return () => { - pingAbortRef.current = true; - }; - }, [loading, connections.length, pingCacheSeconds, requestPings]); - - useEffect(() => { - if (!loading && connections.length > 0) { - const cacheMaxAgeMs = Math.max((pingCacheSeconds || 60) * 1000, 60 * 1000); - if (cacheAppliedAtRef.current && Date.now() - cacheAppliedAtRef.current < cacheMaxAgeMs) { - return; - } - requestSpeeds(); - } - return () => { - speedAbortRef.current = true; - }; - }, [loading, connections.length, pingCacheSeconds, requestSpeeds]); + // Пинг и скорость не запускаются автоматически при открытии карты (даже если кеш просрочен). + // Обновление только по кнопкам «Пинг» и «Скорость» или по планировщику. const handleRefreshPings = () => { requestPings(); diff --git a/frontend/src/NetworkMapSchedulerPage.jsx b/frontend/src/NetworkMapSchedulerPage.jsx index a788512..9981f9c 100644 --- a/frontend/src/NetworkMapSchedulerPage.jsx +++ b/frontend/src/NetworkMapSchedulerPage.jsx @@ -23,6 +23,7 @@ export default function NetworkMapSchedulerPage() { runSpeedTest: true, logMaxLines: 500, }); + const [nextRunAt, setNextRunAt] = useState(null); const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); @@ -42,6 +43,7 @@ export default function NetworkMapSchedulerPage() { runSpeedTest: data.runSpeedTest !== false, logMaxLines: Math.max(LOG_LINES_MIN, Math.min(LOG_LINES_MAX, Number(data.logMaxLines) || 500)), }); + setNextRunAt(typeof data.nextRunAt === 'number' ? data.nextRunAt : null); } catch (e) { setError(e?.response?.data?.message || e?.message || 'Не удалось загрузить настройки'); } @@ -72,6 +74,12 @@ export default function NetworkMapSchedulerPage() { return () => clearInterval(t); }, [fetchLogs]); + useEffect(() => { + if (!settings.enabled || !nextRunAt) return; + const t = setInterval(fetchSettings, 60 * 1000); + return () => clearInterval(t); + }, [settings.enabled, nextRunAt, fetchSettings]); + useEffect(() => { if (autoScroll && logEndRef.current) { logEndRef.current.scrollIntoView({ behavior: 'smooth' }); @@ -182,6 +190,11 @@ export default function NetworkMapSchedulerPage() { Включить планировщик
Периодически обновлять пинг и скорость по связям из «Сетевые настройки»
+ {settings.enabled && nextRunAt != null && ( +
+ Следующий запуск: {new Date(nextRunAt).toLocaleString('ru-RU', { dateStyle: 'short', timeStyle: 'medium' })} +
+ )}