import React, { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import axios from 'axios'; import { API_URL } from '../../config/api'; import { Modal } from '../../components/Modal'; import { useToast } from '../../components/Toast'; interface ServerData { id: number; status: string; ipAddress: string | null; rootPassword: string | null; createdAt: string; tariff: { name: string; price: number; description: string; }; os: { name: string; type: string; }; nextPaymentDate: string | null; autoRenew: boolean; stats?: { data?: { cpu: number; memory: { usage: number; }; disk: { usage: number; }; status: string; }; }; } const ServerPanel: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { addToast } = useToast(); const [server, setServer] = useState(null); const [loading, setLoading] = useState(true); const [actionLoading, setActionLoading] = useState(false); const [error, setError] = useState(null); const [showPassword, setShowPassword] = useState(false); // Модальные окна const [showPasswordConfirm, setShowPasswordConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const loadServer = React.useCallback(async () => { try { const token = localStorage.getItem('access_token'); const headers = { Authorization: `Bearer ${token}` }; const response = await axios.get(`${API_URL}/api/server/${id}/status`, { headers }); setServer(response.data); setError(null); } catch (err: unknown) { console.error('Ошибка загрузки сервера:', err); if (axios.isAxiosError(err) && err.response?.status === 404) { setError('Сервер не найден'); } else { setError('Не удалось загрузить данные сервера'); } } finally { setLoading(false); } }, [id]); useEffect(() => { loadServer(); const interval = setInterval(loadServer, 10000); // Обновляем каждые 10 секунд return () => clearInterval(interval); }, [loadServer]); const handleAction = async (action: 'start' | 'stop' | 'restart') => { try { setActionLoading(true); const token = localStorage.getItem('access_token'); const headers = { Authorization: `Bearer ${token}` }; await axios.post(`${API_URL}/api/server/${id}/${action}`, {}, { headers }); setTimeout(loadServer, 2000); // Обновляем через 2 секунды addToast(`Команда "${action}" отправлена успешно`, 'success'); } catch (err) { console.error(`Ошибка выполнения ${action}:`, err); addToast(`Не удалось выполнить команду "${action}"`, 'error'); } finally { setActionLoading(false); } }; const handlePasswordChange = async () => { setShowPasswordConfirm(false); try { setActionLoading(true); const token = localStorage.getItem('access_token'); const headers = { Authorization: `Bearer ${token}` }; const response = await axios.post(`${API_URL}/api/server/${id}/password`, {}, { headers }); if (response.data.status === 'success') { addToast('Пароль успешно изменён! Новый пароль отображается ниже.', 'success'); loadServer(); } } catch (err) { console.error('Ошибка смены пароля:', err); addToast('Не удалось сменить пароль', 'error'); } finally { setActionLoading(false); } }; const handleDelete = async () => { setShowDeleteConfirm(false); try { setActionLoading(true); const token = localStorage.getItem('access_token'); const headers = { Authorization: `Bearer ${token}` }; await axios.delete(`${API_URL}/api/server/${id}`, { headers }); addToast('Сервер успешно удалён', 'success'); navigate('/dashboard/servers'); } catch (err) { console.error('Ошибка удаления сервера:', err); addToast('Не удалось удалить сервер', 'error'); } finally { setActionLoading(false); } }; const getStatusColor = (status: string) => { switch (status) { case 'running': return 'bg-green-100 text-green-800'; case 'stopped': return 'bg-gray-100 text-gray-800'; case 'creating': return 'bg-blue-100 text-blue-800'; case 'suspended': return 'bg-red-100 text-red-800'; default: return 'bg-gray-100 text-gray-800'; } }; const getStatusText = (status: string) => { switch (status) { case 'running': return 'Работает'; case 'stopped': return 'Остановлен'; case 'creating': return 'Создаётся'; case 'suspended': return 'Приостановлен'; default: return status; } }; if (loading) { return (
Загрузка...
); } if (error || !server) { return (

{error || 'Сервер не найден'}

); } return (
{/* Header */}

Сервер #{server.id}

{server.tariff.name} - {server.os.name}

{getStatusText(server.status)}
{/* Server Info */}

Информация

IP адрес: {server.ipAddress || 'Создаётся...'}
Root пароль:
{showPassword ? server.rootPassword : '••••••••'}
Создан: {new Date(server.createdAt).toLocaleString('ru-RU')}
{server.nextPaymentDate && (
След. платёж: {new Date(server.nextPaymentDate).toLocaleDateString('ru-RU')}
)}
Автопродление: {server.autoRenew ? '✅ Включено' : '❌ Выключено'}
{/* Stats */}

Статистика

{server.stats?.data ? (
CPU {server.stats.data.cpu?.toFixed(1)}%
RAM {((server.stats.data.memory?.usage || 0) / 1024 / 1024).toFixed(0)} MB
) : (

Статистика недоступна

)}
{/* Control Buttons */}

Управление

{/* SSH Access */}

SSH Доступ

ssh root@{server.ipAddress || 'создаётся...'}

Пароль: {showPassword ? server.rootPassword : '••••••••'}

{/* Danger Zone */}

⚠️ Опасная зона

Удаление сервера - необратимое действие. Все данные будут утеряны!

{/* Модальные окна */} setShowPasswordConfirm(false)} title="Смена root-пароля" type="warning" onConfirm={handlePasswordChange} confirmText="Да, сменить пароль" cancelText="Отмена" >

Вы уверены, что хотите сменить root-пароль для этого сервера?

Новый пароль будет сгенерирован автоматически и отображён на этой странице.

setShowDeleteConfirm(false)} title="Удаление сервера" type="danger" onConfirm={handleDelete} confirmText="Да, удалить навсегда" cancelText="Отмена" >

ВЫ УВЕРЕНЫ?

Это действие необратимо! Сервер будет удалён навсегда.

Все данные, файлы и настройки будут потеряны без возможности восстановления.

); }; export default ServerPanel;