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

This commit is contained in:
2025-07-14 10:53:12 +07:00
parent a42e103bba
commit c25011e72b
+168 -23
View File
@@ -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>