feat(mikrotikBackupScheduler, MikrotikBackupsManager): enhance backup server management by integrating UI settings and adding server selection functionality
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m58s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m58s
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
const { readServersFromS3 } = require('../routes/serversRoutes');
|
||||
const { createRosClient } = require('./mikrotikApplyService');
|
||||
const { saveBackupForServer } = require('../routes/mikrotikBackupRoutes');
|
||||
const { decrypt } = require('../utils/encryption');
|
||||
const { readS3TextObject } = require('../services/s3Service');
|
||||
|
||||
const DEFAULT_INTERVAL_MIN = 60;
|
||||
|
||||
@@ -27,6 +29,29 @@ function parseServerIds(envValue) {
|
||||
return parts.length > 0 ? parts : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать список серверов для автобэкапов из UI-настроек (rt_ui_settings.json).
|
||||
* Если поле mikrotikBackupServers отсутствует или пустое — вернуть null.
|
||||
*/
|
||||
async function getServerIdsFromUiSettings(logger) {
|
||||
try {
|
||||
const data = await readS3TextObject('bgp_data/rt_ui_settings.json').catch(() => null);
|
||||
if (!data || !data.body) return null;
|
||||
const parsed = JSON.parse(data.body || '{}');
|
||||
const list = parsed && Array.isArray(parsed.mikrotikBackupServers)
|
||||
? parsed.mikrotikBackupServers
|
||||
: null;
|
||||
if (!list || list.length === 0) return null;
|
||||
return list.map((v) => String(v)).filter(Boolean);
|
||||
} catch (e) {
|
||||
(logger || console).warn(
|
||||
{ component: 'mikrotik-backup', err: e && e.message },
|
||||
'Failed to read mikrotikBackupServers from ui-settings',
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchRouterExport(client) {
|
||||
// Согласно официальной документации, /rest/export позволяет экспортировать конфиг.
|
||||
// Параметр "compact" без значения эквивалентен /export compact.
|
||||
@@ -61,7 +86,9 @@ async function runBackupOnce(logger) {
|
||||
const log = logger || console;
|
||||
try {
|
||||
const allServers = await readServersFromS3();
|
||||
const wantedIds = parseServerIds(process.env.MIKROTIK_BACKUP_SERVERS);
|
||||
const uiIds = await getServerIdsFromUiSettings(log);
|
||||
const envIds = parseServerIds(process.env.MIKROTIK_BACKUP_SERVERS);
|
||||
const wantedIds = (uiIds && uiIds.length > 0) ? uiIds : envIds;
|
||||
|
||||
const jumphosts = (allServers || []).filter((s) => {
|
||||
if (!s || String(s.type || '').toLowerCase() !== 'jumphost') return false;
|
||||
@@ -87,32 +114,32 @@ async function runBackupOnce(logger) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!server.encryptedMikrotikPassword) {
|
||||
log.warn({ component: 'mikrotik-backup', serverId: id }, 'Skip backup: no encryptedMikrotikPassword configured');
|
||||
let password = '';
|
||||
if (server.encryptedMikrotikPassword) {
|
||||
try {
|
||||
password = decrypt(server.encryptedMikrotikPassword);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
{ component: 'mikrotik-backup', serverId: id, err: e && e.message },
|
||||
'Failed to decrypt MikroTik password, skipping backup',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
log.warn(
|
||||
{ component: 'mikrotik-backup', serverId: id },
|
||||
'Skip backup: no encryptedMikrotikPassword configured',
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Пароль будет расшифрован на стороне RouterOS — мы используем тот же механизм,
|
||||
// что и в apply/test-connection: encryptedMikrotikPassword должен быть уже расшифрован ранее.
|
||||
// Здесь для простоты предполагаем, что пароль можно получить так же, как в apply.
|
||||
// Чтобы не дублировать логику расшифровки, используем те же поля, что и в mikrotikConfigRoutes.getMikrotikCredentials.
|
||||
const creds = {
|
||||
host,
|
||||
port: Number(port) || 80,
|
||||
user,
|
||||
// Пароль фактически должен быть расшифрован при сохранении/загрузке,
|
||||
// однако в текущей архитектуре encryptedMikrotikPassword расшифровывается в роуте.
|
||||
// Чтобы не нарушать безопасность и не тянуть сюда ключ, требуем наличия plain-поля mikrotikPassword,
|
||||
// если оно временно присутствует (например, для локального использования или через ENV/секреты).
|
||||
password: server.mikrotikPassword || '',
|
||||
password,
|
||||
secure: false,
|
||||
};
|
||||
|
||||
if (!creds.password) {
|
||||
log.warn({ component: 'mikrotik-backup', serverId: id }, 'Skip backup: no plain mikrotikPassword available');
|
||||
continue;
|
||||
}
|
||||
|
||||
const client = createRosClient(creds);
|
||||
log.info({ component: 'mikrotik-backup', serverId: id, host: creds.host }, 'Starting automatic backup via REST /export');
|
||||
const configText = await fetchRouterExport(client);
|
||||
|
||||
Reference in New Issue
Block a user