refactor: Update filter configuration generation and improve UI layout in FilterManager for better readability and consistency
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m15s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m15s
This commit is contained in:
+66
-13
@@ -308,7 +308,7 @@ app.get('/api/filters/generate-config', async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filters.length === 0) {
|
if (filters.length === 0) {
|
||||||
return res.json({ config: '// Нет фильтров для генерации конфигурации' });
|
return res.json({ config: '// No filters to generate configuration' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group filters by gateway
|
// Group filters by gateway
|
||||||
@@ -320,10 +320,13 @@ app.get('/api/filters/generate-config', async (req, res) => {
|
|||||||
gatewayGroups[filter.gateway].push(filter.community);
|
gatewayGroups[filter.gateway].push(filter.community);
|
||||||
});
|
});
|
||||||
|
|
||||||
let config = '// Конфигурация фильтра frouting для MikroTik 7.14+\n';
|
let config = '// Frouting filter configuration for MikroTik 7.14+\n';
|
||||||
config += '// Сгенерировано автоматически\n\n';
|
config += '// Generated automatically\n';
|
||||||
|
config += `// Date: ${new Date().toISOString()}\n\n`;
|
||||||
|
|
||||||
Object.entries(gatewayGroups).forEach(([gateway, communities]) => {
|
// Если есть только один gateway, создаем простую конфигурацию
|
||||||
|
if (Object.keys(gatewayGroups).length === 1) {
|
||||||
|
const [gateway, communities] = Object.entries(gatewayGroups)[0];
|
||||||
config += `if (\n`;
|
config += `if (\n`;
|
||||||
communities.forEach((community, index) => {
|
communities.forEach((community, index) => {
|
||||||
config += `(bgp-communities includes ${community})`;
|
config += `(bgp-communities includes ${community})`;
|
||||||
@@ -331,13 +334,37 @@ app.get('/api/filters/generate-config', async (req, res) => {
|
|||||||
config += ` \nor `;
|
config += ` \nor `;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
config += `\n)\n{\n set gw ${gateway}; accept;\n}\nelse\n{\n reject;\n}\n\n`;
|
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 });
|
res.json({ config });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'NoSuchKey') {
|
if (error.code === 'NoSuchKey') {
|
||||||
res.json({ config: '// Файл filters.json не найден' });
|
res.json({ config: '// filters.json file not found' });
|
||||||
} else {
|
} else {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
res.status(500).send('Error generating configuration');
|
res.status(500).send('Error generating configuration');
|
||||||
@@ -380,11 +407,13 @@ app.post('/api/filters/export-config', async (req, res) => {
|
|||||||
gatewayGroups[filter.gateway].push(filter.community);
|
gatewayGroups[filter.gateway].push(filter.community);
|
||||||
});
|
});
|
||||||
|
|
||||||
let config = '// Конфигурация фильтра frouting для MikroTik 7.14+\n';
|
let config = '// Frouting filter configuration for MikroTik 7.14+\n';
|
||||||
config += '// Сгенерировано автоматически\n';
|
config += '// Generated automatically\n';
|
||||||
config += `// Дата: ${new Date().toISOString()}\n\n`;
|
config += `// Date: ${new Date().toISOString()}\n\n`;
|
||||||
|
|
||||||
Object.entries(gatewayGroups).forEach(([gateway, communities]) => {
|
// Если есть только один gateway, создаем простую конфигурацию
|
||||||
|
if (Object.keys(gatewayGroups).length === 1) {
|
||||||
|
const [gateway, communities] = Object.entries(gatewayGroups)[0];
|
||||||
config += `if (\n`;
|
config += `if (\n`;
|
||||||
communities.forEach((community, index) => {
|
communities.forEach((community, index) => {
|
||||||
config += `(bgp-communities includes ${community})`;
|
config += `(bgp-communities includes ${community})`;
|
||||||
@@ -392,8 +421,32 @@ app.post('/api/filters/export-config', async (req, res) => {
|
|||||||
config += ` \nor `;
|
config += ` \nor `;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
config += `\n)\n{\n set gw ${gateway}; accept;\n}\nelse\n{\n reject;\n}\n\n`;
|
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`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Save configuration to S3
|
// Save configuration to S3
|
||||||
const exportParams = {
|
const exportParams = {
|
||||||
|
|||||||
@@ -405,35 +405,31 @@ function FilterManager() {
|
|||||||
<h3 className="card-title">Фильтры frouting ({filteredFilters.length})</h3>
|
<h3 className="card-title">Фильтры frouting ({filteredFilters.length})</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="table-responsive">
|
<div className="table-responsive">
|
||||||
<table className="table table-vcenter card-table">
|
<table className="table table-vcenter table-sm card-table align-middle">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th style={{width: '180px', whiteSpace: 'nowrap', verticalAlign: 'middle', fontWeight: 500, fontSize: '15px'}} className="cursor-pointer align-middle"
|
||||||
className="cursor-pointer"
|
onClick={() => handleSort('community')}>
|
||||||
onClick={() => handleSort('community')}
|
|
||||||
>
|
|
||||||
Community
|
Community
|
||||||
{sortField === 'community' && (
|
{sortField === 'community' && (
|
||||||
<IconChevronDown className={`ms-1 ${sortOrder === 'desc' ? 'rotate-180' : ''}`} />
|
<IconChevronDown className={`ms-1 ${sortOrder === 'desc' ? 'rotate-180' : ''}`} />
|
||||||
)}
|
)}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th style={{width: '180px', whiteSpace: 'nowrap', verticalAlign: 'middle', fontWeight: 500, fontSize: '15px'}} className="cursor-pointer align-middle"
|
||||||
className="cursor-pointer"
|
onClick={() => handleSort('gateway')}>
|
||||||
onClick={() => handleSort('gateway')}
|
|
||||||
>
|
|
||||||
Gateway
|
Gateway
|
||||||
{sortField === 'gateway' && (
|
{sortField === 'gateway' && (
|
||||||
<IconChevronDown className={`ms-1 ${sortOrder === 'desc' ? 'rotate-180' : ''}`} />
|
<IconChevronDown className={`ms-1 ${sortOrder === 'desc' ? 'rotate-180' : ''}`} />
|
||||||
)}
|
)}
|
||||||
</th>
|
</th>
|
||||||
<th>Описание</th>
|
<th style={{minWidth: '180px', maxWidth: '300px', verticalAlign: 'middle', fontWeight: 500, fontSize: '15px'}} className="align-middle">Описание</th>
|
||||||
<th className="w-1">Действия</th>
|
<th className="w-1 align-middle text-end" style={{width: '110px'}}></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{filteredFilters.map((filter, index) => (
|
{filteredFilters.map((filter, index) => (
|
||||||
<tr key={index}>
|
<tr key={index} style={{verticalAlign: 'middle', height: '48px'}}>
|
||||||
<td>
|
<td className="align-middle font-monospace" style={{fontSize: '15px'}}>
|
||||||
{editingFilter === filter.community ? (
|
{editingFilter === filter.community ? (
|
||||||
<input
|
<input
|
||||||
ref={editInputRef}
|
ref={editInputRef}
|
||||||
@@ -448,12 +444,13 @@ function FilterManager() {
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
||||||
|
style={{minWidth: '120px'}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="font-monospace">{filter.community}</span>
|
<span>{filter.community}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="align-middle" style={{fontSize: '15px'}}>
|
||||||
{editingFilter === filter.community ? (
|
{editingFilter === filter.community ? (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -467,12 +464,13 @@ function FilterManager() {
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
||||||
|
style={{minWidth: '120px'}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="badge bg-primary">{filter.gateway}</span>
|
<span className="badge bg-blue-lt text-blue" style={{fontSize: '15px', letterSpacing: '0.5px'}}>{filter.gateway}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="align-middle text-muted" style={{fontSize: '14px', maxWidth: '300px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}>
|
||||||
{editingFilter === filter.community ? (
|
{editingFilter === filter.community ? (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -486,13 +484,14 @@ function FilterManager() {
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
onKeyDown={(e) => handleEditKeyDown(e, filter.community)}
|
||||||
|
style={{minWidth: '120px'}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span>{filter.description}</span>
|
<span>{filter.description}</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td className="align-middle text-end" style={{paddingRight: '0.5rem'}}>
|
||||||
<div className="btn-list">
|
<div className="btn-list mb-0" style={{gap: '0.25rem'}}>
|
||||||
{editingFilter === filter.community ? (
|
{editingFilter === filter.community ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user