feat(MikrotikConfig): enhance MikroTik configuration generation with support for 'text' and 'json' formats; add new API endpoints for interface and recursive route generation
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m37s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m37s
This commit is contained in:
@@ -9,13 +9,17 @@ const { streamToString } = require('../services/s3Service');
|
||||
const { generateMikrotikConfig } = require('../utils/mikrotikConfig');
|
||||
const { getS3JsonWithHeaders } = require('../utils/s3Helpers');
|
||||
|
||||
// GET /api/filters/generate-config
|
||||
// GET /api/filters/generate-config?format=text|json
|
||||
async function generateConfig(req, res) {
|
||||
try {
|
||||
const format = (req.query.format || 'text').toLowerCase();
|
||||
if (!['text', 'json'].includes(format)) {
|
||||
return sendError(res, 400, 'format must be "text" or "json"', 'E_BAD_REQUEST');
|
||||
}
|
||||
await getS3JsonWithHeaders('filters.json', req, res, {
|
||||
transform: (filters) => {
|
||||
const config = generateMikrotikConfig(filters);
|
||||
return { config };
|
||||
const result = generateMikrotikConfig(filters, { format });
|
||||
return format === 'json' ? result : { config: result };
|
||||
},
|
||||
defaultValue: [],
|
||||
});
|
||||
@@ -54,10 +58,18 @@ async function exportConfig(req, res) {
|
||||
}
|
||||
|
||||
// POST /api/server-filters/generate-config
|
||||
// Body: { filters, format?: 'text'|'json' }
|
||||
async function generateServerFilterConfig(req, res) {
|
||||
const { filters } = req.body;
|
||||
const config = generateMikrotikConfig(filters);
|
||||
res.json({ config });
|
||||
const { filters, format = 'text' } = req.body || {};
|
||||
const fmt = String(format || 'text').toLowerCase();
|
||||
if (!['text', 'json'].includes(fmt)) {
|
||||
return sendError(res, 400, 'format must be "text" or "json"', 'E_BAD_REQUEST');
|
||||
}
|
||||
const result = generateMikrotikConfig(Array.isArray(filters) ? filters : [], { format: fmt });
|
||||
if (fmt === 'json') {
|
||||
return res.json(result);
|
||||
}
|
||||
res.json({ config: result });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Роуты для генерации конфигурации MikroTik (интерфейсы, маршруты)
|
||||
* Поддерживает форматы: text (как раньше) и json (структурированные операции для API)
|
||||
*/
|
||||
|
||||
const { GetObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const { s3, BUCKET_NAME, streamToString } = require('../services/s3Service');
|
||||
const { sendError } = require('../middleware/errorHandler');
|
||||
const { decrypt } = require('../utils/encryption');
|
||||
const {
|
||||
buildMikrotikInterfaceBlocks,
|
||||
buildMikrotikRecursiveRoutes,
|
||||
buildMikrotikConfig,
|
||||
} = require('../utils/mikrotikInterfaceGenerator');
|
||||
const { readS3TextObject } = require('../services/s3Service');
|
||||
|
||||
async function fetchJsonFromS3(key, defaultValue = null) {
|
||||
try {
|
||||
const data = await s3.send(new GetObjectCommand({ Bucket: BUCKET_NAME, Key: key }));
|
||||
const body = await streamToString(data.Body);
|
||||
const parsed = JSON.parse(body || 'null');
|
||||
return parsed ?? defaultValue;
|
||||
} catch (e) {
|
||||
if (e?.name === 'NoSuchKey' || e?.$metadata?.httpStatusCode === 404) {
|
||||
return defaultValue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchIpsecPasswordMap(passwordIds) {
|
||||
const ids = [...new Set((passwordIds || []).filter(Boolean))];
|
||||
if (ids.length === 0) return {};
|
||||
|
||||
const data = await readS3TextObject('network-config/ipsec-passwords.json').catch(() => ({ body: '[]' }));
|
||||
let passwords = [];
|
||||
try {
|
||||
passwords = JSON.parse(data.body || '[]');
|
||||
if (!Array.isArray(passwords)) passwords = [];
|
||||
} catch {
|
||||
passwords = [];
|
||||
}
|
||||
|
||||
const map = {};
|
||||
for (const p of passwords) {
|
||||
if (ids.includes(p.id) && p.encryptedPassword) {
|
||||
try {
|
||||
map[p.id] = decrypt(p.encryptedPassword) || '';
|
||||
} catch {
|
||||
map[p.id] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/mikrotik/generate
|
||||
* Body: { format?: 'text'|'json', serverId?: string, type?: 'interfaces'|'recursive'|'all', config?: object, servers?: array }
|
||||
* Если config/servers не переданы — загружаются из S3
|
||||
*/
|
||||
async function generateMikrotikConfig(req, res) {
|
||||
try {
|
||||
const { format = 'text', serverId, type = 'all', config: bodyConfig, servers: bodyServers } = req.body || {};
|
||||
|
||||
if (!['text', 'json'].includes(format)) {
|
||||
return sendError(res, 400, 'format must be "text" or "json"', 'E_BAD_REQUEST');
|
||||
}
|
||||
|
||||
let config = bodyConfig;
|
||||
let servers = bodyServers;
|
||||
|
||||
if (!config) {
|
||||
config = await fetchJsonFromS3('network-config.json', { gateways: [], tunnelInterfaces: [], ipPools: [] });
|
||||
if (!config || typeof config !== 'object') config = { gateways: [], tunnelInterfaces: [], ipPools: [] };
|
||||
}
|
||||
|
||||
if (!Array.isArray(servers)) {
|
||||
servers = await fetchJsonFromS3('servers.json', []);
|
||||
if (!Array.isArray(servers)) servers = [];
|
||||
}
|
||||
|
||||
const passwordIds = (config.tunnelInterfaces || [])
|
||||
.filter(i => i.ipsecPasswordId && String(i.ipsecPasswordId).trim() !== '')
|
||||
.map(i => i.ipsecPasswordId.trim());
|
||||
const passwordMap = await fetchIpsecPasswordMap(passwordIds);
|
||||
|
||||
const options = { format, serverId };
|
||||
const includeInterfaces = type === 'interfaces' || type === 'all';
|
||||
const includeRecursive = type === 'recursive' || type === 'all';
|
||||
|
||||
const blocks = await buildMikrotikConfig(config, servers, passwordMap, {
|
||||
...options,
|
||||
includeInterfaces,
|
||||
includeRecursive,
|
||||
});
|
||||
|
||||
res.json({ blocks });
|
||||
} catch (error) {
|
||||
console.error('Error generating MikroTik config:', error);
|
||||
return sendError(res, 500, error.message || 'Error generating configuration', 'E_SERVER');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/mikrotik/generate-interfaces?format=text|json&serverId=...
|
||||
* Генерация только интерфейсов
|
||||
*/
|
||||
async function generateInterfaces(req, res) {
|
||||
try {
|
||||
const format = (req.query.format || 'text').toLowerCase();
|
||||
const serverId = req.query.serverId || null;
|
||||
|
||||
if (!['text', 'json'].includes(format)) {
|
||||
return sendError(res, 400, 'format must be "text" or "json"', 'E_BAD_REQUEST');
|
||||
}
|
||||
|
||||
const config = await fetchJsonFromS3('network-config.json', { gateways: [], tunnelInterfaces: [] });
|
||||
const servers = await fetchJsonFromS3('servers.json', []);
|
||||
const passwordIds = (config.tunnelInterfaces || [])
|
||||
.filter(i => i.ipsecPasswordId && String(i.ipsecPasswordId).trim() !== '')
|
||||
.map(i => i.ipsecPasswordId.trim());
|
||||
const passwordMap = await fetchIpsecPasswordMap(passwordIds);
|
||||
|
||||
const blocks = await buildMikrotikInterfaceBlocks(config, servers, passwordMap, {
|
||||
format,
|
||||
serverId: serverId || undefined,
|
||||
});
|
||||
|
||||
res.json({ blocks });
|
||||
} catch (error) {
|
||||
console.error('Error generating interfaces:', error);
|
||||
return sendError(res, 500, error.message || 'Error generating interfaces', 'E_SERVER');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/mikrotik/generate-recursive-routes?format=text|json&serverId=...
|
||||
* Генерация только рекурсивных маршрутов
|
||||
*/
|
||||
async function generateRecursiveRoutes(req, res) {
|
||||
try {
|
||||
const format = (req.query.format || 'text').toLowerCase();
|
||||
const serverId = req.query.serverId || null;
|
||||
|
||||
if (!['text', 'json'].includes(format)) {
|
||||
return sendError(res, 400, 'format must be "text" or "json"', 'E_BAD_REQUEST');
|
||||
}
|
||||
|
||||
const config = await fetchJsonFromS3('network-config.json', { gateways: [], tunnelInterfaces: [] });
|
||||
const servers = await fetchJsonFromS3('servers.json', []);
|
||||
|
||||
const blocks = await buildMikrotikRecursiveRoutes(config, servers, {
|
||||
format,
|
||||
serverId: serverId || undefined,
|
||||
});
|
||||
|
||||
res.json({ blocks });
|
||||
} catch (error) {
|
||||
console.error('Error generating recursive routes:', error);
|
||||
return sendError(res, 500, error.message || 'Error generating routes', 'E_SERVER');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateMikrotikConfig,
|
||||
generateInterfaces,
|
||||
generateRecursiveRoutes,
|
||||
};
|
||||
Reference in New Issue
Block a user