feat: Implement server filters management API and integrate filter handling in FilterManager for enhanced server configuration capabilities
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m25s

This commit is contained in:
2025-07-14 11:13:35 +07:00
parent 077fdcd258
commit 022c8dddc5
2 changed files with 397 additions and 321 deletions
+131
View File
@@ -648,6 +648,137 @@ app.delete('/api/server-configs/:serverId', async (req, res) => {
}
});
// --- Server Filters Routes ---
// Get server filters
app.get('/api/server-filters/:serverId', async (req, res) => {
const { serverId } = req.params;
const params = {
Bucket: BUCKET_NAME,
Key: `server-filters-${serverId}.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 server filters:', 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 server filters from S3');
}
}
});
// Update server filters
app.post('/api/server-filters/:serverId', async (req, res) => {
const { serverId } = req.params;
const { filters } = req.body;
// 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: `server-filters-${serverId}.json`,
Body: JSON.stringify(filters, null, 2), // Pretty print JSON
ContentType: 'application/json',
};
try {
await s3.putObject(params).promise();
res.send('Server filters updated successfully');
} catch (error) {
console.error(error);
res.status(500).send('Error writing server filters to S3');
}
});
// Generate MikroTik configuration from server filters
app.post('/api/server-filters/generate-config', async (req, res) => {
const { filters } = req.body;
if (!Array.isArray(filters) || filters.length === 0) {
return res.json({ config: '// No filters to generate configuration' });
}
// 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 filter configuration for MikroTik 7.14+\n';
config += '// Generated automatically\n';
config += `// Date: ${new Date().toISOString()}\n\n`;
// Если есть только один gateway, создаем простую конфигурацию
if (Object.keys(gatewayGroups).length === 1) {
const [gateway, communities] = Object.entries(gatewayGroups)[0];
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`;
} else {
// Если несколько gateway, создаем каскадную структуру
const gatewayEntries = Object.entries(gatewayGroups);
gatewayEntries.forEach(([gateway, communities], gatewayIndex) => {
if (gatewayIndex === 0) {
config += `if (\n`;
} else {
config += `else 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}`;
if (gatewayIndex === gatewayEntries.length - 1) {
config += `\nelse\n{\n reject;\n}\n`;
} else {
config += `\n`;
}
});
}
res.json({ config });
});
// The "catchall" handler: for any request that doesn't
// match one above, send back React's index.html file.
app.get('*', (req, res) => {