feat(mikrotik): optimize address list entry addition with chunked execution and improved error handling
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m46s

This commit is contained in:
2026-02-24 15:10:54 +07:00
parent 9de7fd104c
commit cc92502de2
+57 -5
View File
@@ -1477,6 +1477,7 @@ async function getAddressLists(req, res) {
const ADDRESS_LIST_PATH = 'ip/firewall/address-list';
const ADDRESS_LIST_REMOVE_CHUNK_SIZE = 100;
const ADDRESS_LIST_ADD_CONCURRENCY = 8;
const ADDRESS_LIST_ADD_EXECUTE_CHUNK_SIZE = 80;
function chunkArray(items, size) {
if (!Array.isArray(items) || items.length === 0) return [];
@@ -1529,8 +1530,8 @@ async function removeAddressListIds(client, ids) {
return removed;
}
async function addAddressListEntries(client, entries) {
const normalized = [...new Map(
function normalizeAddressListEntries(entries) {
return [...new Map(
(Array.isArray(entries) ? entries : [])
.map((entry) => {
const address = String(entry?.address || '').trim();
@@ -1544,14 +1545,36 @@ async function addAddressListEntries(client, entries) {
})
.filter(Boolean)
).values()];
}
if (normalized.length === 0) return { added: 0, failed: 0 };
function escapeRouterOsString(value) {
return `"${String(value || '')
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')}"`;
}
function buildAddressListAddScript(entriesChunk) {
return entriesChunk
.map((params) => {
const parts = [
'/ip/firewall/address-list/add',
`address=${escapeRouterOsString(params.address)}`,
`list=${escapeRouterOsString(params.list)}`,
];
if (params.comment != null && String(params.comment).trim()) {
parts.push(`comment=${escapeRouterOsString(params.comment)}`);
}
return parts.join(' ');
})
.join(';\n');
}
async function addAddressListEntriesParallel(client, normalized) {
if (!Array.isArray(normalized) || normalized.length === 0) return { added: 0, failed: 0 };
let index = 0;
let added = 0;
let failed = 0;
const workersCount = Math.min(ADDRESS_LIST_ADD_CONCURRENCY, normalized.length);
const workers = Array.from({ length: workersCount }, async () => {
while (true) {
const current = index;
@@ -1567,11 +1590,40 @@ async function addAddressListEntries(client, entries) {
}
}
});
await Promise.all(workers);
return { added, failed };
}
async function addAddressListEntries(client, entries) {
const normalized = normalizeAddressListEntries(entries);
if (normalized.length === 0) return { added: 0, failed: 0 };
let added = 0;
let failed = 0;
const chunks = chunkArray(normalized, ADDRESS_LIST_ADD_EXECUTE_CHUNK_SIZE);
for (const chunk of chunks) {
const script = buildAddressListAddScript(chunk);
try {
// Один execute на пачку резко снижает HTTP overhead.
await client.command('execute', { script });
added += chunk.length;
continue;
} catch (err) {
console.warn(
`[address-lists] execute add fallback (${chunk.length} entries):`,
err?.message || String(err)
);
}
const fallbackResult = await addAddressListEntriesParallel(client, chunk);
added += fallbackResult.added;
failed += fallbackResult.failed;
}
return { added, failed };
}
/**
* POST /api/mikrotik/address-lists/apply-summary
* Body: { serverId, removeIds: string[], addEntries: { address, list, comment? }[] }