From fbfddf3c7a1743fdff4e48f6d71ece3de9055cca Mon Sep 17 00:00:00 2001 From: arkonsadter Date: Fri, 16 Jan 2026 15:40:14 +0600 Subject: [PATCH] Changed design and bug fixes --- .gitignore | 1 + backend/tickets.json | 59 +- backend/users.json | 76 +- frontend/src/App.jsx | 890 ++++++++---------- frontend/src/components/Console.jsx | 16 +- frontend/src/components/CreateTicketModal.jsx | 20 +- frontend/src/components/FileEditorModal.jsx | 16 +- frontend/src/components/FileManager.jsx | 74 +- frontend/src/components/FileViewerModal.jsx | 14 +- frontend/src/components/Profile.jsx | 189 ++-- frontend/src/components/Stats.jsx | 36 +- frontend/src/components/TicketChat.jsx | 34 +- frontend/src/components/Tickets.jsx | 38 +- frontend/src/components/UserManagement.jsx | 24 +- frontend/src/index.css | 238 ++++- frontend/tailwind.config.js | 39 +- 16 files changed, 920 insertions(+), 844 deletions(-) diff --git a/.gitignore b/.gitignore index f4e6995..864791d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ frontend/.env.production.local # Build frontend/dist/ backend/build/ +backend/users1.json.backup diff --git a/backend/tickets.json b/backend/tickets.json index 9e26dfe..5e3eb95 100644 --- a/backend/tickets.json +++ b/backend/tickets.json @@ -1 +1,58 @@ -{} \ No newline at end of file +{ + "1": { + "id": "1", + "title": "ававп", + "description": "вап", + "author": "Root", + "status": "closed", + "created_at": "2026-01-16T09:30:34.640417", + "updated_at": "2026-01-16T09:35:07.708933", + "messages": [ + { + "author": "Root", + "text": "вап", + "timestamp": "2026-01-16T09:30:34.640417" + }, + { + "author": "Root", + "text": "ап", + "timestamp": "2026-01-16T09:30:38.095207" + }, + { + "author": "Root", + "text": "вапвп", + "timestamp": "2026-01-16T09:30:40.438134" + }, + { + "author": "Root", + "text": "вапвпвап", + "timestamp": "2026-01-16T09:30:42.195576" + }, + { + "author": "Root", + "text": "вавап", + "timestamp": "2026-01-16T09:30:44.371194" + }, + { + "author": "Root", + "text": "вапвап", + "timestamp": "2026-01-16T09:30:45.779656" + }, + { + "author": "Root", + "text": "вавап", + "timestamp": "2026-01-16T09:30:47.736753" + }, + { + "author": "system", + "text": "Статус изменён на: В работе", + "timestamp": "2026-01-16T09:35:01.602729" + }, + { + "author": "system", + "text": "Статус изменён на: Закрыт", + "timestamp": "2026-01-16T09:35:07.708933" + } + ] + } +} \ No newline at end of file diff --git a/backend/users.json b/backend/users.json index 87b7b55..9e26dfe 100644 --- a/backend/users.json +++ b/backend/users.json @@ -1,75 +1 @@ -{ - "Root": { - "username": "Root", - "password": "$2b$12$PAaomoUWn3Ip5ov.S/uYPeTIRiDMq7DbA57ahyYQnw3QHT2zuYMlG", - "role": "owner", - "servers": [], - "permissions": { - "manage_users": true, - "manage_roles": true, - "manage_servers": true, - "manage_tickets": true, - "manage_files": true, - "delete_users": true, - "view_all_resources": true - }, - "resource_access": { - "servers": [], - "tickets": [], - "files": [] - } - }, - "MihailPrud": { - "username": "MihailPrud", - "password": "$2b$12$GfbQN4scE.b.mtUHofWWE.Dn1tQpT1zwLAxeICv90sHP4zGv0dc2G", - "role": "owner", - "servers": [ - "test", - "nya" - ], - "permissions": { - "manage_users": true, - "manage_roles": true, - "manage_servers": true, - "manage_tickets": true, - "manage_files": true, - "delete_users": true, - "view_all_resources": true - }, - "resource_access": { - "servers": [ - "test", - "nya" - ], - "tickets": [], - "files": [] - } - }, - "arkonsad": { - "username": "arkonsad", - "password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392", - "role": "banned", - "servers": [ - "123", - "sdfsdf" - ], - "permissions": { - "manage_users": false, - "manage_roles": false, - "manage_servers": false, - "manage_tickets": false, - "manage_files": false, - "delete_users": false, - "view_all_resources": false - }, - "resource_access": { - "servers": [ - "123", - "sdfsdf" - ], - "tickets": [], - "files": [] - }, - "ban_reason": "Заблокирован администратором" - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3bd7311..40179b8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,5 +1,9 @@ import { useState, useEffect } from 'react'; -import { Server, Play, Square, Terminal, FolderOpen, HardDrive, Settings, Plus, Users as UsersIcon, LogOut, Menu, X, MessageSquare, UserCircle, Shield } from 'lucide-react'; +import { + Server, Play, Square, Terminal, FolderOpen, Settings, Plus, + Users as UsersIcon, LogOut, Menu, X, MessageSquare, UserCircle, + Shield, Activity, HardDrive, Cpu, BarChart3, Home +} from 'lucide-react'; import Console from './components/Console'; import FileManager from './components/FileManager'; import Stats from './components/Stats'; @@ -11,11 +15,9 @@ import Tickets from './components/Tickets'; import Profile from './components/Profile'; import Auth from './components/Auth'; import ErrorBoundary from './components/ErrorBoundary'; -import ThemeSelector from './components/ThemeSelector'; import NotificationSystem, { notify } from './components/NotificationSystem'; import axios from 'axios'; import { API_URL } from './config'; -import { getTheme } from './themes'; function App() { const [token, setToken] = useState(localStorage.getItem('token')); @@ -24,30 +26,22 @@ function App() { const [selectedServer, setSelectedServer] = useState(null); const [activeTab, setActiveTab] = useState('console'); const [showCreateModal, setShowCreateModal] = useState(false); - const [showUsers, setShowUsers] = useState(false); const [showUserManagement, setShowUserManagement] = useState(false); const [showTickets, setShowTickets] = useState(false); const [showProfile, setShowProfile] = useState(false); - const [viewingUsername, setViewingUsername] = useState(null); const [connectionError, setConnectionError] = useState(false); - const [theme, setTheme] = useState(localStorage.getItem('theme') || 'modern'); const [sidebarOpen, setSidebarOpen] = useState(true); - - const currentTheme = getTheme(theme); + const [currentView, setCurrentView] = useState('dashboard'); useEffect(() => { - // Проверяем callback от OpenID Connect const urlParams = new URLSearchParams(window.location.search); const callbackToken = urlParams.get('token'); const callbackUsername = urlParams.get('username'); if (callbackToken && callbackUsername) { - // Сохраняем токен и обновляем состояние localStorage.setItem('token', callbackToken); setToken(callbackToken); setUser({ username: callbackUsername }); - - // Очищаем URL window.history.replaceState({}, document.title, window.location.pathname); return; } @@ -99,6 +93,7 @@ function App() { localStorage.setItem('token', data.access_token); setToken(data.access_token); setUser({ username: data.username, role: data.role }); + notify.success(`Добро пожаловать, ${data.username}!`); }; const handleLogout = () => { @@ -107,530 +102,461 @@ function App() { setUser(null); setServers([]); setSelectedServer(null); + notify.info('Вы вышли из системы'); }; - const handleServerDeleted = () => { - setSelectedServer(null); - loadServers(); - }; - - const handleThemeChange = (newTheme) => { - setTheme(newTheme); - localStorage.setItem('theme', newTheme); - }; - - const getRoleName = (role) => { - switch (role) { - case 'admin': - return 'Админ'; - case 'support': - return 'Поддержка'; - case 'banned': - return 'Забанен'; - default: - return 'Пользователь'; - } - }; - - const handleUsernameChange = (newToken, newUsername) => { - setToken(newToken); - setUser({ ...user, username: newUsername }); - loadServers(); - }; - - const handleViewProfile = (username) => { - setViewingUsername(username); - setShowProfile(true); - setShowUsers(false); - }; - - const startServer = async (serverName) => { + const handleServerAction = async (serverName, action) => { try { - const response = await axios.post( - `${API_URL}/api/servers/${serverName}/start`, + await axios.post( + `${API_URL}/api/servers/${serverName}/${action}`, {}, { headers: { Authorization: `Bearer ${token}` } } ); - console.log('Сервер запущен:', response.data); - notify('success', 'Сервер запущен', `Сервер "${serverName}" успешно запущен`); - setTimeout(() => { - loadServers(); - }, 1000); + notify.success(`Сервер ${action === 'start' ? 'запущен' : 'остановлен'}`); + loadServers(); } catch (error) { - console.error('Ошибка запуска сервера:', error); - notify('error', 'Ошибка запуска', error.response?.data?.detail || 'Не удалось запустить сервер'); - alert(error.response?.data?.detail || 'Ошибка запуска сервера'); - } - }; - - const stopServer = async (serverName) => { - try { - const response = await axios.post( - `${API_URL}/api/servers/${serverName}/stop`, - {}, - { headers: { Authorization: `Bearer ${token}` } } - ); - console.log('Сервер остановлен:', response.data); - notify('info', 'Сервер остановлен', `Сервер "${serverName}" успешно остановлен`); - setTimeout(() => { - loadServers(); - }, 1000); - } catch (error) { - console.error('Ошибка остановки сервера:', error); - notify('error', 'Ошибка остановки', error.response?.data?.detail || 'Не удалось остановить сервер'); - alert(error.response?.data?.detail || 'Ошибка остановки сервера'); + notify.error(`Ошибка: ${error.response?.data?.detail || error.message}`); } }; if (!token) { - return ; - } - - if (showUsers) { return ( -
-
-
-
-
-
-
- -
-
-

MC Panel

-

Управление серверами

-
-
-
-
-
- - {user?.username} - - - {getRoleName(user?.role)} - -
- - - -
-
-
-
- -
- ); - } - - if (showTickets) { - return ( -
-
-
-
-
-
-
- -
-
-

MC Panel

-

Управление серверами

-
-
-
-
-
- - {user?.username} - - - {getRoleName(user?.role)} - -
- - - -
-
-
-
- -
- ); - } - - if (showProfile) { - return ( -
-
-
-
-
-
-
- -
-
-

MC Panel

-

Управление серверами

-
-
-
-
-
- - {user?.username} - - - {getRoleName(user?.role)} - -
- - - -
-
-
-
- -
+ + + + ); } return ( -
- - {/* Header */} -
-
-
-
+ +
+ {/* Sidebar */} +
-
-
+ )} -
- {/* Sidebar */} - - {/* Main Content */} + {/* Main content */}
- {selectedServer ? ( - <> - {/* Server Header with Controls */} -
-
- - - {servers.find(s => s.name === selectedServer)?.displayName || selectedServer} - - s.name === selectedServer)?.status === 'running' - ? 'bg-green-600 text-white' - : 'bg-gray-600 text-white' - }`}> - {servers.find(s => s.name === selectedServer)?.status === 'running' ? 'Запущен' : 'Остановлен'} - + {/* Header */} +
+
+

+ {currentView === 'dashboard' && 'Панель управления'} + {currentView === 'server' && selectedServer?.displayName} + {currentView === 'management' && 'Управление пользователями'} + {currentView === 'tickets' && 'Тикеты поддержки'} + {currentView === 'profile' && 'Профиль'} +

+ {currentView === 'server' && selectedServer && ( +

+ {selectedServer.name} • {selectedServer.status === 'running' ? 'Запущен' : 'Остановлен'} +

+ )} +
+ + {currentView === 'server' && selectedServer && ( +
+ {selectedServer.status === 'stopped' ? ( + + ) : ( + + )} +
+ )} +
+ + {/* Content area */} +
+ {connectionError && ( +
+

⚠️ Ошибка подключения к серверу

+
+ )} + + {currentView === 'dashboard' && ( +
+
+
+
+ +
+
+
{servers.length}
+
Всего серверов
+
+
+ +
+
+ +
+
+
+ {servers.filter(s => s.status === 'running').length} +
+
Запущено
+
+
+ +
+
+ +
+
+
+ {servers.filter(s => s.status === 'stopped').length} +
+
Остановлено
+
+
+ +
+
+ +
+
+
{user?.username}
+
{user?.role}
+
+
-
- {servers.find(s => s.name === selectedServer)?.status === 'stopped' ? ( + +
+
+

Ваши серверы

- ) : ( - +
+ +
+ {servers.map((server) => ( +
{ + setSelectedServer(server); + setCurrentView('server'); + }} + className="server-card" + > +
+
+
+ +
+
+

{server.displayName}

+

{server.name}

+
+
+ + {server.status === 'running' ? 'Запущен' : 'Остановлен'} + +
+ +
+ {server.status === 'stopped' ? ( + + ) : ( + + )} + +
+
+ ))} +
+ + {servers.length === 0 && ( +
+ +

Нет серверов

+

Создайте свой первый сервер

+ +
)}
+ )} - {/* Tabs */} -
- {[ - { id: 'console', icon: Terminal, label: 'Консоль' }, - { id: 'files', icon: FolderOpen, label: 'Файлы' }, - { id: 'stats', icon: HardDrive, label: 'Статистика' }, - { id: 'settings', icon: Settings, label: 'Настройки' }, - ].map((tab) => ( + {currentView === 'server' && selectedServer && ( +
+ {/* Tabs */} +
- ))} -
+ + + +
- {/* Content */} -
- - {activeTab === 'console' && } - {activeTab === 'files' && } - {activeTab === 'stats' && } + {/* Tab content */} +
+ {activeTab === 'console' && } + {activeTab === 'files' && } + {activeTab === 'stats' && } {activeTab === 'settings' && ( - )} - -
- - ) : ( -
-
-
- -

Выберите сервер

-

- Выберите сервер из списка слева или создайте новый -

-
- )} + )} +
-
- {showCreateModal && ( - setShowCreateModal(false)} - onCreated={loadServers} - /> - )} + {/* Modals */} + {showCreateModal && ( + setShowCreateModal(false)} + onSuccess={() => { + setShowCreateModal(false); + loadServers(); + }} + /> + )} - {showUserManagement && ( -
-
-
-

- - Управление пользователями -

- -
-
- + {showUserManagement && ( +
setShowUserManagement(false)}> +
e.stopPropagation()}> +
-
- )} -
+ )} + + {showTickets && ( +
setShowTickets(false)}> +
e.stopPropagation()}> + setShowTickets(false)} /> +
+
+ )} + + {showProfile && ( +
setShowProfile(false)}> +
e.stopPropagation()}> + setShowProfile(false)} /> +
+
+ )} + + +
+ ); } diff --git a/frontend/src/components/Console.jsx b/frontend/src/components/Console.jsx index 2f279c2..2d4dd70 100644 --- a/frontend/src/components/Console.jsx +++ b/frontend/src/components/Console.jsx @@ -3,7 +3,7 @@ import { Send } from 'lucide-react'; import axios from 'axios'; import { API_URL, WS_URL } from '../config'; -export default function Console({ serverName, token, theme }) { +export default function Console({ serverName, token }) { const [logs, setLogs] = useState([]); const [command, setCommand] = useState(''); const logsEndRef = useRef(null); @@ -84,11 +84,11 @@ export default function Console({ serverName, token, theme }) { }; return ( -
+
{/* Консоль */} -
+
{logs.length === 0 ? ( -
Консоль пуста. Запустите сервер для просмотра логов.
+
Консоль пуста. Запустите сервер для просмотра логов.
) : ( logs.map((log, index) => (
@@ -100,17 +100,17 @@ export default function Console({ serverName, token, theme }) {
{/* Поле ввода команды */} -
+ setCommand(e.target.value)} - placeholder="Введите команду и нажмите Enter для отправки, используйте стрелки для навигации между предыдущими командами" - className={`flex-1 ${theme.input} ${theme.border} border rounded-lg px-4 py-2.5 ${theme.text} placeholder:text-gray-600 focus:outline-none focus:ring-2 focus:ring-green-500 transition`} + placeholder="Введите команду..." + className="input flex-1" /> @@ -43,7 +43,7 @@ export default function CreateTicketModal({ token, theme, onClose, onCreated })
-
-