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 = () => {
|
||||
if (!newFilter.community.trim() || !newFilter.gateway.trim()) {
|
||||
setError('Поля Community и Gateway должны быть заполнены.');
|
||||
@@ -511,29 +647,38 @@ function FilterManager() {
|
||||
{server.description && (
|
||||
<p className="text-muted small mb-3">{server.description}</p>
|
||||
)}
|
||||
<div className="d-flex gap-2">
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
onClick={() => handleLoadServerConfig(server.id)}
|
||||
>
|
||||
<IconEye className="me-1" />
|
||||
Просмотр
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-outline-success btn-sm"
|
||||
onClick={() => handleSaveServerConfig(server.id)}
|
||||
>
|
||||
<IconSave className="me-1" />
|
||||
Сохранить
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-outline-danger btn-sm"
|
||||
onClick={() => handleDeleteServer(server.id)}
|
||||
>
|
||||
<IconTrash className="me-1" />
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
<button
|
||||
className="btn btn-outline-primary btn-sm"
|
||||
onClick={() => handleLoadServerConfig(server.id)}
|
||||
>
|
||||
<IconEye className="me-1" />
|
||||
Просмотр
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-outline-success btn-sm"
|
||||
onClick={() => handleSaveServerConfig(server.id)}
|
||||
>
|
||||
<IconSave className="me-1" />
|
||||
Сохранить
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-outline-warning btn-sm"
|
||||
onClick={() => handleCopyConfigToAllServers(server.id)}
|
||||
disabled={loading || servers.filter(s => s.id !== server.id && s.enabled).length === 0}
|
||||
title="Скопировать конфигурацию на все остальные серверы"
|
||||
>
|
||||
<IconCopy className="me-1" />
|
||||
Копировать на все
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-outline-danger btn-sm"
|
||||
onClick={() => handleDeleteServer(server.id)}
|
||||
>
|
||||
<IconTrash className="me-1" />
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user