feat: Add functionality to copy server configuration to all servers and deploy configuration to enabled servers in FilterManager for improved server management
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m54s
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m54s
This commit is contained in:
+168
-23
@@ -165,6 +165,142 @@ function FilterManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCopyConfigToAllServers = async (sourceServerId) => {
|
||||||
|
const sourceServer = servers.find(s => s.id === sourceServerId);
|
||||||
|
if (!sourceServer) {
|
||||||
|
setError('Исходный сервер не найден.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetServers = servers.filter(s => s.id !== sourceServerId && s.enabled);
|
||||||
|
if (targetServers.length === 0) {
|
||||||
|
setError('Нет других активных серверов для копирования конфигурации.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = window.confirm(
|
||||||
|
`Скопировать конфигурацию с сервера "${sourceServer.name}" на ${targetServers.length} сервер(ов)?\n\n` +
|
||||||
|
`Целевые серверы: ${targetServers.map(s => s.name).join(', ')}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirm) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// Получаем конфигурацию исходного сервера
|
||||||
|
const sourceResponse = await axios.get(`${API_URL}/server-configs/${sourceServerId}`);
|
||||||
|
const sourceConfig = sourceResponse.data.config;
|
||||||
|
|
||||||
|
if (!sourceConfig || sourceConfig === '// Конфигурация не найдена') {
|
||||||
|
setError(`У сервера "${sourceServer.name}" нет сохраненной конфигурации.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
// Копируем конфигурацию на все целевые серверы
|
||||||
|
for (const targetServer of targetServers) {
|
||||||
|
try {
|
||||||
|
await axios.post(`${API_URL}/server-configs/${targetServer.id}`, { config: sourceConfig });
|
||||||
|
results.push({ server: targetServer.name, status: 'success' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error copying config to ${targetServer.name}:`, error);
|
||||||
|
results.push({ server: targetServer.name, status: 'error', error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const successCount = results.filter(r => r.status === 'success').length;
|
||||||
|
const errorCount = results.filter(r => r.status === 'error').length;
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
setSuccess(`Конфигурация успешно скопирована на ${successCount} сервер(ов)!`);
|
||||||
|
} else {
|
||||||
|
setError(
|
||||||
|
`Копирование завершено с ошибками:\n` +
|
||||||
|
`✅ Успешно: ${successCount}\n` +
|
||||||
|
`❌ Ошибки: ${errorCount}\n\n` +
|
||||||
|
`Ошибки:\n` +
|
||||||
|
results.filter(r => r.status === 'error')
|
||||||
|
.map(r => `• ${r.server}: ${r.error}`)
|
||||||
|
.join('\n')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setSuccess('');
|
||||||
|
setError('');
|
||||||
|
}, 8000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error copying config to all servers:', error);
|
||||||
|
setError('Не удалось скопировать конфигурацию на серверы.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeployToAllServers = async () => {
|
||||||
|
if (servers.length === 0) {
|
||||||
|
setError('Нет серверов для развертывания.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabledServers = servers.filter(s => s.enabled);
|
||||||
|
if (enabledServers.length === 0) {
|
||||||
|
setError('Нет включенных серверов для развертывания.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = window.confirm(
|
||||||
|
`Вы уверены, что хотите развернуть конфигурацию на ${enabledServers.length} сервер(ов)?\n\n` +
|
||||||
|
`Серверы: ${enabledServers.map(s => s.name).join(', ')}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirm) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const config = await generateMikrotikConfig();
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const server of enabledServers) {
|
||||||
|
try {
|
||||||
|
await axios.post(`${API_URL}/server-configs/${server.id}`, { config });
|
||||||
|
results.push({ server: server.name, status: 'success' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error deploying to ${server.name}:`, error);
|
||||||
|
results.push({ server: server.name, status: 'error', error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const successCount = results.filter(r => r.status === 'success').length;
|
||||||
|
const errorCount = results.filter(r => r.status === 'error').length;
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
setSuccess(`Конфигурация успешно развернута на ${successCount} сервер(ов)!`);
|
||||||
|
} else {
|
||||||
|
setError(
|
||||||
|
`Развертывание завершено с ошибками:\n` +
|
||||||
|
`✅ Успешно: ${successCount}\n` +
|
||||||
|
`❌ Ошибки: ${errorCount}\n\n` +
|
||||||
|
`Ошибки:\n` +
|
||||||
|
results.filter(r => r.status === 'error')
|
||||||
|
.map(r => `• ${r.server}: ${r.error}`)
|
||||||
|
.join('\n')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setSuccess('');
|
||||||
|
setError('');
|
||||||
|
}, 8000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deploying to all servers:', error);
|
||||||
|
setError('Не удалось развернуть конфигурацию на серверы.');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddFilter = () => {
|
const handleAddFilter = () => {
|
||||||
if (!newFilter.community.trim() || !newFilter.gateway.trim()) {
|
if (!newFilter.community.trim() || !newFilter.gateway.trim()) {
|
||||||
setError('Поля Community и Gateway должны быть заполнены.');
|
setError('Поля Community и Gateway должны быть заполнены.');
|
||||||
@@ -511,29 +647,38 @@ function FilterManager() {
|
|||||||
{server.description && (
|
{server.description && (
|
||||||
<p className="text-muted small mb-3">{server.description}</p>
|
<p className="text-muted small mb-3">{server.description}</p>
|
||||||
)}
|
)}
|
||||||
<div className="d-flex gap-2">
|
<div className="d-flex gap-2">
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline-primary btn-sm"
|
className="btn btn-outline-primary btn-sm"
|
||||||
onClick={() => handleLoadServerConfig(server.id)}
|
onClick={() => handleLoadServerConfig(server.id)}
|
||||||
>
|
>
|
||||||
<IconEye className="me-1" />
|
<IconEye className="me-1" />
|
||||||
Просмотр
|
Просмотр
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline-success btn-sm"
|
className="btn btn-outline-success btn-sm"
|
||||||
onClick={() => handleSaveServerConfig(server.id)}
|
onClick={() => handleSaveServerConfig(server.id)}
|
||||||
>
|
>
|
||||||
<IconSave className="me-1" />
|
<IconSave className="me-1" />
|
||||||
Сохранить
|
Сохранить
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline-danger btn-sm"
|
className="btn btn-outline-warning btn-sm"
|
||||||
onClick={() => handleDeleteServer(server.id)}
|
onClick={() => handleCopyConfigToAllServers(server.id)}
|
||||||
>
|
disabled={loading || servers.filter(s => s.id !== server.id && s.enabled).length === 0}
|
||||||
<IconTrash className="me-1" />
|
title="Скопировать конфигурацию на все остальные серверы"
|
||||||
Удалить
|
>
|
||||||
</button>
|
<IconCopy className="me-1" />
|
||||||
</div>
|
Копировать на все
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-danger btn-sm"
|
||||||
|
onClick={() => handleDeleteServer(server.id)}
|
||||||
|
>
|
||||||
|
<IconTrash className="me-1" />
|
||||||
|
Удалить
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user