feat: Add filters API endpoints for retrieving, updating, generating, and exporting MikroTik configurations
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 7m37s

This commit is contained in:
2025-07-14 08:09:05 +07:00
parent a4c5b1dd98
commit 93ace3c619
4 changed files with 1027 additions and 7 deletions
+200 -3
View File
@@ -195,18 +195,89 @@ app.post('/api/servers', async (req, res) => {
}
});
// --- Filters Routes (JSON format) ---
// Get filters from S3
app.get('/api/filters', async (req, res) => {
const params = {
Bucket: BUCKET_NAME,
Key: 'filters.json',
};
try {
const data = await s3.getObject(params).promise();
const fileContent = data.Body.toString('utf-8');
let filters = [];
try {
filters = JSON.parse(fileContent);
// Ensure it's an array
if (!Array.isArray(filters)) {
filters = [];
}
} catch (parseError) {
console.error('Error parsing filters.json:', parseError);
filters = [];
}
res.json(filters);
} catch (error) {
if (error.code === 'NoSuchKey') {
res.json([]); // Return empty array if file does not exist
} else {
console.error(error);
res.status(500).send('Error reading from S3');
}
}
});
// Update filters in S3
app.post('/api/filters', async (req, res) => {
const { domains: filters } = req.body; // Keep name 'domains' for consistency
// Validate filters structure
if (!Array.isArray(filters)) {
return res.status(400).send('Filters must be an array');
}
// Validate each filter has required fields
for (let i = 0; i < filters.length; i++) {
const filter = filters[i];
if (!filter.community || !filter.gateway) {
return res.status(400).send(`Filter at index ${i} is missing required fields`);
}
}
const params = {
Bucket: BUCKET_NAME,
Key: 'filters.json',
Body: JSON.stringify(filters, null, 2), // Pretty print JSON
ContentType: 'application/json',
};
try {
await s3.putObject(params).promise();
res.send('File updated successfully');
} catch (error) {
console.error(error);
res.status(500).send('Error writing to S3');
}
});
// Новый эндпоинт для получения дат последнего изменения файлов S3
app.get('/api/s3/last-modified', async (req, res) => {
try {
const [domainsHead, asnsHead, serversHead] = await Promise.all([
const [domainsHead, asnsHead, serversHead, filtersHead] = await Promise.all([
s3.headObject({ Bucket: BUCKET_NAME, Key: 'domains.txt' }).promise(),
s3.headObject({ Bucket: BUCKET_NAME, Key: 'asns.txt' }).promise(),
s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise()
s3.headObject({ Bucket: BUCKET_NAME, Key: 'servers.json' }).promise(),
s3.headObject({ Bucket: BUCKET_NAME, Key: 'filters.json' }).promise()
]);
res.json({
domainsLastModified: domainsHead.LastModified ? domainsHead.LastModified.toISOString() : null,
asnsLastModified: asnsHead.LastModified ? asnsHead.LastModified.toISOString() : null,
serversLastModified: serversHead.LastModified ? serversHead.LastModified.toISOString() : null
serversLastModified: serversHead.LastModified ? serversHead.LastModified.toISOString() : null,
filtersLastModified: filtersHead.LastModified ? filtersHead.LastModified.toISOString() : null
});
} catch (error) {
console.error(error);
@@ -214,6 +285,132 @@ app.get('/api/s3/last-modified', async (req, res) => {
}
});
// Generate MikroTik configuration from filters
app.get('/api/filters/generate-config', async (req, res) => {
const params = {
Bucket: BUCKET_NAME,
Key: 'filters.json',
};
try {
const data = await s3.getObject(params).promise();
const fileContent = data.Body.toString('utf-8');
let filters = [];
try {
filters = JSON.parse(fileContent);
if (!Array.isArray(filters)) {
filters = [];
}
} catch (parseError) {
console.error('Error parsing filters.json:', parseError);
filters = [];
}
if (filters.length === 0) {
return res.json({ config: '// Нет фильтров для генерации конфигурации' });
}
// Group filters by gateway
const gatewayGroups = {};
filters.forEach(filter => {
if (!gatewayGroups[filter.gateway]) {
gatewayGroups[filter.gateway] = [];
}
gatewayGroups[filter.gateway].push(filter.community);
});
let config = '// Конфигурация фильтра frouting для MikroTik 7.14+\n';
config += '// Сгенерировано автоматически\n\n';
Object.entries(gatewayGroups).forEach(([gateway, communities]) => {
config += `if (\n`;
communities.forEach((community, index) => {
config += `(bgp-communities includes ${community})`;
if (index < communities.length - 1) {
config += ` \nor `;
}
});
config += `\n)\n{\n set gw ${gateway}; accept;\n}\nelse\n{\n reject;\n}\n\n`;
});
res.json({ config });
} catch (error) {
if (error.code === 'NoSuchKey') {
res.json({ config: '// Файл filters.json не найден' });
} else {
console.error(error);
res.status(500).send('Error generating configuration');
}
}
});
// Export MikroTik configuration to S3
app.post('/api/filters/export-config', async (req, res) => {
const params = {
Bucket: BUCKET_NAME,
Key: 'filters.json',
};
try {
const data = await s3.getObject(params).promise();
const fileContent = data.Body.toString('utf-8');
let filters = [];
try {
filters = JSON.parse(fileContent);
if (!Array.isArray(filters)) {
filters = [];
}
} catch (parseError) {
console.error('Error parsing filters.json:', parseError);
filters = [];
}
if (filters.length === 0) {
return res.json({ success: false, message: 'Нет фильтров для экспорта' });
}
// Group filters by gateway
const gatewayGroups = {};
filters.forEach(filter => {
if (!gatewayGroups[filter.gateway]) {
gatewayGroups[filter.gateway] = [];
}
gatewayGroups[filter.gateway].push(filter.community);
});
let config = '// Конфигурация фильтра frouting для MikroTik 7.14+\n';
config += '// Сгенерировано автоматически\n';
config += `// Дата: ${new Date().toISOString()}\n\n`;
Object.entries(gatewayGroups).forEach(([gateway, communities]) => {
config += `if (\n`;
communities.forEach((community, index) => {
config += `(bgp-communities includes ${community})`;
if (index < communities.length - 1) {
config += ` \nor `;
}
});
config += `\n)\n{\n set gw ${gateway}; accept;\n}\nelse\n{\n reject;\n}\n\n`;
});
// Save configuration to S3
const exportParams = {
Bucket: BUCKET_NAME,
Key: 'mikrotik-frouting-config.txt',
Body: config,
ContentType: 'text/plain',
};
await s3.putObject(exportParams).promise();
res.json({ success: true, message: 'Конфигурация экспортирована в S3' });
} catch (error) {
console.error(error);
res.status(500).send('Error exporting configuration');
}
});
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {