/** * Роуты планировщика карты сети: настройки, логи, ручной запуск, кеш. */ const { sendError, sendOk } = require('../middleware/errorHandler'); const { readS3TextObject, writeS3JsonObject } = require('../services/s3Service'); const networkMapScheduler = require('../services/networkMapScheduler'); const pingServicesScheduler = require('../services/pingServicesScheduler'); const uptimeMonitorScheduler = require('../services/uptimeMonitorScheduler'); const UI_SETTINGS_KEY = 'bgp_data/rt_ui_settings.json'; const NETWORK_MAP_CACHE_KEY = 'network-map-cache/latest.json'; const SCHEDULER_KEYS = [ 'networkMapSchedulerEnabled', 'networkMapSchedulerIntervalMinutes', 'networkMapSchedulerRunPing', 'networkMapSchedulerRunSpeedTest', 'networkMapSchedulerLogMaxLines', ]; const PING_SERVICES_SCHEDULER_KEYS = [ 'pingServicesSchedulerEnabled', 'pingServicesSchedulerIntervalMinutes', ]; const UPTIME_MONITOR_SCHEDULER_KEYS = [ 'uptimeMonitorSchedulerEnabled', 'uptimeMonitorSchedulerIntervalMinutes', ]; async function readUiSettings() { try { const data = await readS3TextObject(UI_SETTINGS_KEY).catch(() => ({ body: '{}' })); const parsed = JSON.parse(data?.body || '{}'); return parsed && typeof parsed === 'object' ? parsed : {}; } catch (_) { return {}; } } /** GET /api/scheduler/network-map/settings */ async function getNetworkMapSchedulerSettings(req, res) { try { const ui = await readUiSettings(); const settings = networkMapScheduler.getSettingsFromUiSettings(ui); 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'); } } /** PATCH /api/scheduler/network-map/settings - обновить только ключи планировщика в ui-settings */ async function patchNetworkMapSchedulerSettings(req, res) { try { const body = req.body || {}; const current = await readUiSettings(); for (const key of SCHEDULER_KEYS) { if (Object.prototype.hasOwnProperty.call(body, key)) { current[key] = body[key]; } } if (Object.prototype.hasOwnProperty.call(body, 'networkMapSchedulerLogMaxLines')) { networkMapScheduler.setLogMaxLines(body.networkMapSchedulerLogMaxLines); } await writeS3JsonObject(UI_SETTINGS_KEY, current); const settings = networkMapScheduler.getSettingsFromUiSettings(current); return res.json(settings); } catch (e) { console.error('[scheduler] patchSettings', e); return sendError(res, 500, 'Не удалось сохранить настройки', 'E_SCHEDULER'); } } /** GET /api/scheduler/network-map/logs */ async function getNetworkMapSchedulerLogs(req, res) { try { const lines = networkMapScheduler.getLogs(); return res.json({ lines }); } catch (e) { console.error('[scheduler] getLogs', e); return sendError(res, 500, 'Не удалось прочитать логи', 'E_SCHEDULER'); } } /** POST /api/scheduler/network-map/run-now - один запуск по текущим настройкам */ async function runNetworkMapSchedulerNow(req, res) { try { const port = Number(process.env.PORT) || 3001; const baseUrl = `http://127.0.0.1:${port}`; const ui = await readUiSettings(); const settings = networkMapScheduler.getSettingsFromUiSettings(ui); networkMapScheduler.runJob({ baseUrl, runPing: settings.runPing, runSpeedTest: settings.runSpeedTest, trigger: 'manual', }).catch((err) => { console.error('[scheduler] run-now failed', err); const msg = err?.message || String(err); try { networkMapScheduler.pushLog(`Ошибка выполнения: ${msg}`); } catch (_) {} }); return res.json({ ok: true, message: 'Запуск выполняется в фоне' }); } catch (e) { console.error('[scheduler] run-now', e); const msg = e?.message || String(e); try { networkMapScheduler.pushLog(`Ошибка запуска: ${msg}`); } catch (_) {} return sendError(res, 500, 'Не удалось запустить', 'E_SCHEDULER'); } } /** GET /api/network-map-cache - кеш пингов и скоростей для карты сети */ async function getNetworkMapCache(req, res) { try { const data = await readS3TextObject(NETWORK_MAP_CACHE_KEY).catch(() => null); if (!data?.body) { return res.json({ pingMap: {}, speedMap: {}, updatedAt: null }); } const parsed = JSON.parse(data.body); return res.json({ pingMap: parsed.pingMap && typeof parsed.pingMap === 'object' ? parsed.pingMap : {}, speedMap: parsed.speedMap && typeof parsed.speedMap === 'object' ? parsed.speedMap : {}, updatedAt: typeof parsed.updatedAt === 'number' ? parsed.updatedAt : null, }); } catch (e) { console.error('[network-map-cache] get', e); return res.json({ pingMap: {}, speedMap: {}, updatedAt: null }); } } // === Ping services scheduler === /** GET /api/scheduler/ping-services/settings */ async function getPingServicesSchedulerSettings(req, res) { try { const ui = await readUiSettings(); const settings = pingServicesScheduler.getSettingsFromUiSettings(ui); const nextRunAt = pingServicesScheduler.getNextRunAt(settings); return res.json({ ...settings, nextRunAt: nextRunAt || undefined }); } catch (e) { console.error('[scheduler] getPingServicesSettings', e); return sendError(res, 500, 'Не удалось прочитать настройки', 'E_SCHEDULER'); } } /** PATCH /api/scheduler/ping-services/settings */ async function patchPingServicesSchedulerSettings(req, res) { try { const body = req.body || {}; const current = await readUiSettings(); for (const key of PING_SERVICES_SCHEDULER_KEYS) { if (Object.prototype.hasOwnProperty.call(body, key)) { current[key] = body[key]; } } await writeS3JsonObject(UI_SETTINGS_KEY, current); const settings = pingServicesScheduler.getSettingsFromUiSettings(current); return res.json(settings); } catch (e) { console.error('[scheduler] patchPingServicesSettings', e); return sendError(res, 500, 'Не удалось сохранить настройки', 'E_SCHEDULER'); } } /** POST /api/scheduler/ping-services/run-now */ async function runPingServicesSchedulerNow(req, res) { try { const { refreshPingServicesCache } = require('./miscRoutes'); refreshPingServicesCache().catch((err) => { console.error('[scheduler] ping-services run-now failed', err); }); return res.json({ ok: true, message: 'Обновление кеша пинг-сервисов запущено в фоне' }); } catch (e) { console.error('[scheduler] ping-services run-now', e); return sendError(res, 500, 'Не удалось запустить', 'E_SCHEDULER'); } } // === Uptime monitor scheduler === /** GET /api/scheduler/uptime-monitor/settings */ async function getUptimeMonitorSchedulerSettings(req, res) { try { const ui = await readUiSettings(); const settings = uptimeMonitorScheduler.getSettingsFromUiSettings(ui); const nextRunAt = uptimeMonitorScheduler.getNextRunAt(settings); return res.json({ ...settings, nextRunAt: nextRunAt || undefined }); } catch (e) { console.error('[scheduler] getUptimeMonitorSettings', e); return sendError(res, 500, 'Не удалось прочитать настройки', 'E_SCHEDULER'); } } /** PATCH /api/scheduler/uptime-monitor/settings */ async function patchUptimeMonitorSchedulerSettings(req, res) { try { const body = req.body || {}; const current = await readUiSettings(); for (const key of UPTIME_MONITOR_SCHEDULER_KEYS) { if (Object.prototype.hasOwnProperty.call(body, key)) { current[key] = body[key]; } } await writeS3JsonObject(UI_SETTINGS_KEY, current); const settings = uptimeMonitorScheduler.getSettingsFromUiSettings(current); return res.json(settings); } catch (e) { console.error('[scheduler] patchUptimeMonitorSettings', e); return sendError(res, 500, 'Не удалось сохранить настройки', 'E_SCHEDULER'); } } /** POST /api/scheduler/uptime-monitor/run-now */ async function runUptimeMonitorSchedulerNow(req, res) { try { const port = Number(process.env.PORT) || 3001; const baseUrl = `http://127.0.0.1:${port}`; const resServers = await fetch(`${baseUrl}/api/servers`).catch(() => null); const data = resServers?.ok ? await resServers.json() : []; const servers = Array.isArray(data) ? data : []; const jumphosts = servers.filter( (s) => s && ['jumphost', 'home'].includes(String(s.type || '').toLowerCase()) ); const serverIds = jumphosts.map((s) => s.id || s.dns || s.ip).filter(Boolean); for (const serverId of serverIds) { fetch(`${baseUrl}/api/uptime/check`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ serverId }), }).catch((err) => console.warn('[scheduler] uptime check', serverId, err?.message)); } return res.json({ ok: true, message: 'Проверки Uptime Monitor запущены в фоне' }); } catch (e) { console.error('[scheduler] uptime-monitor run-now', e); return sendError(res, 500, 'Не удалось запустить', 'E_SCHEDULER'); } } module.exports = { getNetworkMapSchedulerSettings, patchNetworkMapSchedulerSettings, getNetworkMapSchedulerLogs, runNetworkMapSchedulerNow, getNetworkMapCache, getPingServicesSchedulerSettings, patchPingServicesSchedulerSettings, runPingServicesSchedulerNow, getUptimeMonitorSchedulerSettings, patchUptimeMonitorSchedulerSettings, runUptimeMonitorSchedulerNow, };