From db2eddca4be487ff764528163ca4924b1388059a Mon Sep 17 00:00:00 2001 From: arkonsadter Date: Wed, 14 Jan 2026 21:26:23 +0600 Subject: [PATCH] Add Ticket and add Role Support --- CHANGELOG.md | 120 +++++++++++++++++++++ TICKETS_SYSTEM.md | 143 +++++++++++++++++++++++++ backend/main.py | 147 +++++++++++++++++++++++++- backend/tickets.json | 48 +++++++++ backend/users.json | 20 ++-- frontend/src/App.jsx | 65 +++++++++++- frontend/src/components/Users.jsx | 33 +++--- БЫСТРЫЙ_СТАРТ.md | 14 ++- ГОТОВО.md | 166 ++++++++++++++++++++++++++++++ 9 files changed, 726 insertions(+), 30 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 TICKETS_SYSTEM.md create mode 100644 backend/tickets.json create mode 100644 ГОТОВО.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4cc1396 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,120 @@ +# 📝 История изменений MC Panel + +## Версия 2.0 - Система тикетов (14.01.2026) + +### ✨ Новые возможности + +#### 🎫 Система тикетов +- Полноценная система поддержки с чатом +- Три статуса: На рассмотрении, В работе, Закрыт +- Автоматическое обновление сообщений каждые 3 секунды +- Системные сообщения о смене статуса +- Кнопка "Тикеты" в header + +#### 👥 Новая роль "Тех. поддержка" +- Доступ ко всем тикетам +- Возможность менять статусы тикетов +- Возможность отвечать на тикеты +- Отдельный бейдж в интерфейсе + +#### 🔧 Улучшения управления пользователями +- Выпадающий список для выбора роли +- Три роли: Пользователь, Тех. поддержка, Администратор +- Цветные индикаторы ролей +- Описание прав для каждой роли + +### 🔐 Безопасность +- Изменён логин администратора: `Sofa12345` +- Изменён пароль администратора: `arkonsad123` + +### 📁 Новые файлы +- `backend/tickets.json` - хранилище тикетов +- `frontend/src/components/Tickets.jsx` - список тикетов +- `frontend/src/components/TicketChat.jsx` - чат тикета +- `frontend/src/components/CreateTicketModal.jsx` - создание тикета +- `TICKETS_SYSTEM.md` - документация системы тикетов + +--- + +## Версия 1.5 - Система тем (14.01.2026) + +### 🎨 Темы +- 5 тем: Тёмная, Светлая, Фиолетовая, Синяя, Зелёная +- Градиентный логотип "MC Panel" для каждой темы +- Селектор тем в header +- Автоматическое сохранение выбранной темы + +### 🎯 Дизайн +- Современный интерфейс в стиле TimeWeb Cloud +- Карточки с тенями и анимациями +- Плавные переходы между темами +- Адаптивный дизайн для мобильных + +### 📁 Файлы +- `frontend/src/themes.js` - конфигурация тем +- `frontend/src/components/ThemeSelector.jsx` - селектор тем + +--- + +## Версия 1.0 - Базовая панель (13.01.2026) + +### 🖥️ Управление серверами +- Создание и удаление серверов +- Запуск и остановка серверов +- Просмотр консоли в реальном времени +- Менеджер файлов с редактированием +- Мониторинг ресурсов (RAM, диск) +- Настройки сервера + +### 👥 Система пользователей +- Регистрация и авторизация +- JWT токены +- Роли: Админ и Пользователь +- Управление доступом к серверам +- Владельцы серверов + +### 🌐 Сетевой доступ +- Работа через Radmin VPN +- Автоматическое определение API URL +- Поддержка локальной и сетевой работы + +### 📁 Основные файлы +- `backend/main.py` - FastAPI бэкенд +- `frontend/src/App.jsx` - React фронтенд +- `backend/users.json` - хранилище пользователей +- `backend/servers/` - папка с серверами + +--- + +## 🚀 Планы на будущее + +### Версия 2.1 +- [ ] Уведомления о новых сообщениях в тикетах +- [ ] Прикрепление файлов к тикетам +- [ ] Фильтрация тикетов по статусу +- [ ] Поиск по тикетам + +### Версия 2.2 +- [ ] Статистика по тикетам +- [ ] Экспорт истории тикетов +- [ ] Шаблоны ответов для тех. поддержки +- [ ] Приоритеты тикетов + +### Версия 3.0 +- [ ] Плагины для серверов +- [ ] Автоматическое резервное копирование +- [ ] Планировщик задач +- [ ] Мониторинг производительности + +--- + +## 📞 Поддержка + +Если у вас возникли вопросы или проблемы: +1. Создайте тикет в системе поддержки +2. Опишите проблему подробно +3. Дождитесь ответа от тех. поддержки + +**Учётные данные администратора:** +- Логин: `Sofa12345` +- Пароль: `arkonsad123` diff --git a/TICKETS_SYSTEM.md b/TICKETS_SYSTEM.md new file mode 100644 index 0000000..9b024ad --- /dev/null +++ b/TICKETS_SYSTEM.md @@ -0,0 +1,143 @@ +# 🎫 Система тикетов + +## Что добавлено + +### ✅ Новые возможности + +1. **Система тикетов** - полноценная система поддержки с чатом +2. **Три статуса тикетов**: + - 🟡 **На рассмотрении** (pending) - новый тикет + - 🔵 **В работе** (in_progress) - тикет взят в работу + - 🟢 **Закрыт** (closed) - тикет решён + +3. **Новая роль "Тех. поддержка"** (support): + - Доступ ко всем тикетам + - Возможность менять статусы тикетов + - Возможность отвечать на тикеты + +4. **Кнопка "Тикеты"** в header рядом с кнопкой "Пользователи" + +### 📋 Возможности по ролям + +#### Обычные пользователи (user) +- ✅ Создавать тикеты +- ✅ Просматривать свои тикеты +- ✅ Отправлять сообщения в свои тикеты +- ❌ Менять статусы тикетов +- ❌ Видеть чужие тикеты + +#### Тех. поддержка (support) +- ✅ Просматривать все тикеты +- ✅ Отвечать на любые тикеты +- ✅ Менять статусы тикетов +- ✅ Закрывать тикеты +- ❌ Управлять пользователями +- ❌ Управлять серверами + +#### Администраторы (admin) +- ✅ Все возможности тех. поддержки +- ✅ Управление пользователями +- ✅ Управление серверами +- ✅ Назначение ролей + +## 🚀 Как использовать + +### Создание тикета +1. Нажмите кнопку "Тикеты" в header +2. Нажмите "Создать тикет" +3. Заполните тему и описание проблемы +4. Нажмите "Создать" + +### Работа с тикетом +1. Откройте список тикетов +2. Нажмите на нужный тикет +3. Пишите сообщения в чат +4. Тех. поддержка и админы могут менять статус тикета + +### Назначение роли "Тех. поддержка" +1. Войдите как администратор (none / none) +2. Нажмите кнопку "Пользователи" +3. Найдите нужного пользователя +4. В выпадающем списке выберите "Тех. поддержка" +5. Роль изменится автоматически + +## 📁 Новые файлы + +### Backend +- `backend/tickets.json` - хранилище тикетов (создаётся автоматически) +- Добавлены endpoints в `backend/main.py`: + - `GET /api/tickets` - список тикетов + - `POST /api/tickets/create` - создать тикет + - `GET /api/tickets/{id}` - получить тикет + - `POST /api/tickets/{id}/message` - добавить сообщение + - `PUT /api/tickets/{id}/status` - изменить статус + +### Frontend +- `frontend/src/components/Tickets.jsx` - список тикетов +- `frontend/src/components/TicketChat.jsx` - чат тикета +- `frontend/src/components/CreateTicketModal.jsx` - создание тикета + +## 🎨 Интерфейс + +### Список тикетов +- Карточки с информацией о тикете +- Цветные индикаторы статуса +- Количество сообщений +- Дата создания +- Автор тикета + +### Чат тикета +- Сообщения в реальном времени (обновление каждые 3 секунды) +- Системные сообщения о смене статуса +- Кнопки смены статуса (для тех. поддержки и админов) +- Отправка сообщений (если тикет не закрыт) + +## 🔧 Технические детали + +### Статусы тикетов +```javascript +pending // На рассмотрении (жёлтый) +in_progress // В работе (синий) +closed // Закрыт (зелёный) +``` + +### Роли пользователей +```javascript +user // Обычный пользователь +support // Тех. поддержка +admin // Администратор +``` + +### Структура тикета +```json +{ + "id": "1", + "title": "Проблема с сервером", + "description": "Описание проблемы", + "author": "username", + "status": "pending", + "created_at": "2024-01-14T12:00:00", + "updated_at": "2024-01-14T12:00:00", + "messages": [ + { + "author": "username", + "text": "Текст сообщения", + "timestamp": "2024-01-14T12:00:00" + } + ] +} +``` + +## ✅ Готово! + +Система тикетов полностью интегрирована в MC Panel. Пользователи могут создавать тикеты, а тех. поддержка и администраторы могут на них отвечать и управлять статусами. + +### Учётные данные по умолчанию +- **Логин**: Sofa12345 +- **Пароль**: arkonsad123 +- **Роль**: admin + +Для создания пользователя тех. поддержки: +1. Зарегистрируйте нового пользователя +2. Войдите как админ +3. Назначьте ему роль "Тех. поддержка" diff --git a/backend/main.py b/backend/main.py index 54e082b..02183c9 100644 --- a/backend/main.py +++ b/backend/main.py @@ -36,6 +36,7 @@ security = HTTPBearer(auto_error=False) SERVERS_DIR = Path("servers") SERVERS_DIR.mkdir(exist_ok=True) USERS_FILE = Path("users.json") +TICKETS_FILE = Path("tickets.json") server_processes: dict[str, subprocess.Popen] = {} server_logs: dict[str, list[str]] = {} @@ -46,13 +47,13 @@ IS_WINDOWS = sys.platform == 'win32' def init_users(): if not USERS_FILE.exists(): admin_user = { - "username": "admin", - "password": pwd_context.hash("admin"), + "username": "Sofa12345", + "password": pwd_context.hash("arkonsad123"), "role": "admin", "servers": [] } - save_users({"admin": admin_user}) - print("Создан пользователь по умолчанию: admin / admin") + save_users({"Sofa12345": admin_user}) + print("Создан пользователь по умолчанию: none / none") def load_users() -> dict: if USERS_FILE.exists(): @@ -80,6 +81,17 @@ def save_server_config(server_name: str, config: dict): with open(config_path, 'w', encoding='utf-8') as f: json.dump(config, f, indent=2, ensure_ascii=False) +# Функции для работы с тикетами +def load_tickets() -> dict: + if TICKETS_FILE.exists(): + with open(TICKETS_FILE, 'r', encoding='utf-8') as f: + return json.load(f) + return {} + +def save_tickets(tickets: dict): + with open(TICKETS_FILE, 'w', encoding='utf-8') as f: + json.dump(tickets, f, indent=2, ensure_ascii=False) + init_users() # Функции аутентификации @@ -752,6 +764,133 @@ async def rename_file(server_name: str, old_path: str, new_name: str, user: dict old_file_path.rename(new_file_path) return {"message": "Файл переименован"} +# API для тикетов +@app.get("/api/tickets") +async def get_tickets(user: dict = Depends(get_current_user)): + """Получить список тикетов""" + tickets = load_tickets() + + # Админы и тех. поддержка видят все тикеты + if user["role"] in ["admin", "support"]: + return list(tickets.values()) + + # Обычные пользователи видят только свои тикеты + user_tickets = [t for t in tickets.values() if t["author"] == user["username"]] + return user_tickets + +@app.post("/api/tickets/create") +async def create_ticket(data: dict, user: dict = Depends(get_current_user)): + """Создать новый тикет""" + tickets = load_tickets() + + # Генерируем ID тикета + ticket_id = str(len(tickets) + 1) + + ticket = { + "id": ticket_id, + "title": data.get("title", "").strip(), + "description": data.get("description", "").strip(), + "author": user["username"], + "status": "pending", # pending, in_progress, closed + "created_at": datetime.utcnow().isoformat(), + "updated_at": datetime.utcnow().isoformat(), + "messages": [ + { + "author": user["username"], + "text": data.get("description", "").strip(), + "timestamp": datetime.utcnow().isoformat() + } + ] + } + + tickets[ticket_id] = ticket + save_tickets(tickets) + + return {"message": "Тикет создан", "ticket": ticket} + +@app.get("/api/tickets/{ticket_id}") +async def get_ticket(ticket_id: str, user: dict = Depends(get_current_user)): + """Получить тикет по ID""" + tickets = load_tickets() + + if ticket_id not in tickets: + raise HTTPException(404, "Тикет не найден") + + ticket = tickets[ticket_id] + + # Проверка доступа + if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]: + raise HTTPException(403, "Нет доступа к этому тикету") + + return ticket + +@app.post("/api/tickets/{ticket_id}/message") +async def add_ticket_message(ticket_id: str, data: dict, user: dict = Depends(get_current_user)): + """Добавить сообщение в тикет""" + tickets = load_tickets() + + if ticket_id not in tickets: + raise HTTPException(404, "Тикет не найден") + + ticket = tickets[ticket_id] + + # Проверка доступа + if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]: + raise HTTPException(403, "Нет доступа к этому тикету") + + message = { + "author": user["username"], + "text": data.get("text", "").strip(), + "timestamp": datetime.utcnow().isoformat() + } + + ticket["messages"].append(message) + ticket["updated_at"] = datetime.utcnow().isoformat() + + tickets[ticket_id] = ticket + save_tickets(tickets) + + return {"message": "Сообщение добавлено", "ticket": ticket} + +@app.put("/api/tickets/{ticket_id}/status") +async def update_ticket_status(ticket_id: str, data: dict, user: dict = Depends(get_current_user)): + """Изменить статус тикета (только для админов и тех. поддержки)""" + if user["role"] not in ["admin", "support"]: + raise HTTPException(403, "Недостаточно прав") + + tickets = load_tickets() + + if ticket_id not in tickets: + raise HTTPException(404, "Тикет не найден") + + new_status = data.get("status") + if new_status not in ["pending", "in_progress", "closed"]: + raise HTTPException(400, "Неверный статус") + + ticket = tickets[ticket_id] + ticket["status"] = new_status + ticket["updated_at"] = datetime.utcnow().isoformat() + + # Добавляем системное сообщение о смене статуса + status_names = { + "pending": "На рассмотрении", + "in_progress": "В работе", + "closed": "Закрыт" + } + + message = { + "author": "system", + "text": f"Статус изменён на: {status_names[new_status]}", + "timestamp": datetime.utcnow().isoformat() + } + + ticket["messages"].append(message) + + tickets[ticket_id] = ticket + save_tickets(tickets) + + return {"message": "Статус обновлён", "ticket": ticket} + if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/backend/tickets.json b/backend/tickets.json new file mode 100644 index 0000000..ae79366 --- /dev/null +++ b/backend/tickets.json @@ -0,0 +1,48 @@ +{ + "1": { + "id": "1", + "title": "Пошёл нахуй", + "description": "Свин", + "author": "arkonsad", + "status": "closed", + "created_at": "2026-01-14T15:20:26.344010", + "updated_at": "2026-01-14T15:22:02.654579", + "messages": [ + { + "author": "arkonsad", + "text": "Свин", + "timestamp": "2026-01-14T15:20:26.344010" + }, + { + "author": "Sofa12345", + "text": "Ты че", + "timestamp": "2026-01-14T15:21:19.943424" + }, + { + "author": "Sofa12345", + "text": "ахуел", + "timestamp": "2026-01-14T15:21:24.251787" + }, + { + "author": "arkonsad", + "text": "покушай говна", + "timestamp": "2026-01-14T15:21:46.676746" + }, + { + "author": "system", + "text": "Статус изменён на: В работе", + "timestamp": "2026-01-14T15:21:48.504108" + }, + { + "author": "Sofa12345", + "text": "тварина ты ебаная", + "timestamp": "2026-01-14T15:21:58.245227" + }, + { + "author": "system", + "text": "Статус изменён на: Закрыт", + "timestamp": "2026-01-14T15:22:02.654579" + } + ] + } +} \ No newline at end of file diff --git a/backend/users.json b/backend/users.json index 3b68cc1..a3c02cf 100644 --- a/backend/users.json +++ b/backend/users.json @@ -1,10 +1,4 @@ { - "admin": { - "username": "admin", - "password": "$2b$12$0AJU/Cc6vI.gqUY6BfU8E.6adiK3QS/1EyZJ98MAExiHAf4HOhn4C", - "role": "admin", - "servers": [] - }, "MihailPrud": { "username": "MihailPrud", "password": "$2b$12$GfbQN4scE.b.mtUHofWWE.Dn1tQpT1zwLAxeICv90sHP4zGv0dc2G", @@ -13,5 +7,19 @@ "test", "nya" ] + }, + "arkonsad": { + "username": "arkonsad", + "password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392", + "role": "user", + "servers": [ + "123" + ] + }, + "Sofa12345": { + "username": "Sofa12345", + "password": "$2b$12$Fph20p2mwgOAqoT77wSA3.n1S7NiHLa28aiNOwWcz3PfNhgC5pp5.", + "role": "admin", + "servers": [] } } \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 2fe0d47..aa2fd33 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,11 +1,12 @@ import { useState, useEffect } from 'react'; -import { Server, Play, Square, Terminal, FolderOpen, HardDrive, Settings, Plus, Users as UsersIcon, LogOut, Menu, X } from 'lucide-react'; +import { Server, Play, Square, Terminal, FolderOpen, HardDrive, Settings, Plus, Users as UsersIcon, LogOut, Menu, X, MessageSquare } from 'lucide-react'; import Console from './components/Console'; import FileManager from './components/FileManager'; import Stats from './components/Stats'; import ServerSettings from './components/ServerSettings'; import CreateServerModal from './components/CreateServerModal'; import Users from './components/Users'; +import Tickets from './components/Tickets'; import Auth from './components/Auth'; import ErrorBoundary from './components/ErrorBoundary'; import ThemeSelector from './components/ThemeSelector'; @@ -21,6 +22,7 @@ function App() { const [activeTab, setActiveTab] = useState('console'); const [showCreateModal, setShowCreateModal] = useState(false); const [showUsers, setShowUsers] = useState(false); + const [showTickets, setShowTickets] = useState(false); const [connectionError, setConnectionError] = useState(false); const [theme, setTheme] = useState(localStorage.getItem('theme') || 'dark'); const [sidebarOpen, setSidebarOpen] = useState(true); @@ -156,7 +158,7 @@ function App() { {user?.username} - {user?.role === 'admin' ? 'Админ' : 'Пользователь'} + {user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'} @@ -183,6 +185,56 @@ function App() { ); } + if (showTickets) { + return ( +
+
+
+
+
+
+
+ +
+
+

MC Panel

+

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

+
+
+
+
+
+ + {user?.username} + + + {user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'} + +
+ + + +
+
+
+
+ +
+ ); + } + return (
{/* Header */} @@ -217,10 +269,17 @@ function App() { {user?.username} - {user?.role === 'admin' ? 'Админ' : 'Пользователь'} + {user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'}
+ {user?.role === 'admin' && (