feat(MikrotikConfigRoutes, FirewallPage): add apply-summary endpoint and frontend analysis feature for address lists
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m47s

This commit is contained in:
2026-02-21 19:01:28 +07:00
parent 4ad5821b5e
commit 8e8ce7cec0
3 changed files with 265 additions and 1 deletions
+71 -1
View File
@@ -15,7 +15,7 @@ const {
buildMikrotikRecursiveRoutes,
getParentGateway,
} = require('../utils/mikrotikInterfaceGenerator');
const { createRosClient, applyBlock, rosPrint } = require('../services/mikrotikApplyService');
const { createRosClient, applyBlock, rosPrint, rosAdd, rosRemove } = require('../services/mikrotikApplyService');
const IPSEC_PASSWORDS_KEY = 'network-config/ipsec-passwords.json';
const NETWORK_CONFIG_KEY = 'network-config.json';
@@ -1261,6 +1261,75 @@ async function getAddressLists(req, res) {
}
}
const ADDRESS_LIST_PATH = 'ip/firewall/address-list';
/**
* POST /api/mikrotik/address-lists/apply-summary
* Body: { serverId, removeIds: string[], addEntries: { address, list, comment? }[] }
* Удаляет записи по .id и добавляет новые (суммаризованные) в address-list.
*/
async function applyAddressListSummary(req, res) {
try {
const { serverId, removeIds = [], addEntries = [] } = req.body || {};
if (!serverId) {
return sendError(res, 400, 'serverId is required', 'E_BAD_REQUEST');
}
const ids = Array.isArray(removeIds) ? removeIds.filter((id) => id != null && String(id).trim()) : [];
const entries = Array.isArray(addEntries)
? addEntries.filter((e) => e && (e.address || e.list))
: [];
const servers = await readServersFromS3();
const server = servers.find((s) => (s.id || s.dns || s.ip) === serverId);
if (!server || (server.type !== 'jumphost' && server.type !== 'home')) {
return sendError(res, 400, 'Jumphost or home server not found', 'E_NOT_FOUND');
}
const creds = getMikrotikCredentials(server);
if (!creds) {
return sendError(res, 400, 'MikroTik credentials not configured for this server', 'E_CREDENTIALS');
}
const client = createRosClient(creds);
let removed = 0;
let added = 0;
for (const id of ids) {
try {
const pathId = String(id).trim();
await rosRemove(client, ADDRESS_LIST_PATH, pathId);
removed++;
} catch (err) {
console.warn(`[address-lists] remove ${id}:`, err?.message);
}
}
for (const entry of entries) {
try {
const params = {
address: String(entry.address || '').trim(),
list: String(entry.list || 'ban').trim(),
};
if (entry.comment != null && String(entry.comment).trim()) {
params.comment = String(entry.comment).trim();
}
if (!params.address || !params.list) continue;
await rosAdd(client, ADDRESS_LIST_PATH, params);
added++;
} catch (err) {
console.warn('[address-lists] add:', err?.message);
}
}
return res.json({ ok: true, removed, added });
} catch (error) {
const msg = error.response?.data?.detail || error.response?.data?.message || error.message || 'Ошибка применения суммаризации';
const status = error.response?.status;
console.error(JSON.stringify({ component: 'address-lists-apply', error: msg }));
return sendError(res, status && status >= 400 ? status : 502, msg, 'E_APPLY_SUMMARY');
}
}
module.exports = {
generateMikrotikConfig,
generateInterfaces,
@@ -1274,4 +1343,5 @@ module.exports = {
speedTestViaTunnel,
loadNetworkConfig,
getAddressLists,
applyAddressListSummary,
};
+1
View File
@@ -468,6 +468,7 @@ app.post('/api/mikrotik/speed-test', writeLimiter, mikrotikConfigRoutes.speedTes
app.post('/api/mikrotik/apply', mikrotikConfigRoutes.applyMikrotikConfig);
app.post('/api/mikrotik/run-script', mikrotikConfigRoutes.runScript);
app.get('/api/mikrotik/address-lists', mikrotikConfigRoutes.getAddressLists);
app.post('/api/mikrotik/address-lists/apply-summary', writeLimiter, mikrotikConfigRoutes.applyAddressListSummary);
// === TRAFFIC STATS (MikroTik interfaces by jumphost) ===
app.get('/api/traffic/interface-stats', trafficRoutes.getInterfaceStats);