Add Ticket and add Role Support
This commit is contained in:
120
CHANGELOG.md
Normal file
120
CHANGELOG.md
Normal file
@@ -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`
|
||||
143
TICKETS_SYSTEM.md
Normal file
143
TICKETS_SYSTEM.md
Normal file
@@ -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. Назначьте ему роль "Тех. поддержка"
|
||||
147
backend/main.py
147
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)
|
||||
|
||||
48
backend/tickets.json
Normal file
48
backend/tickets.json
Normal file
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
</span>
|
||||
<span className={`ml-2 text-xs px-2 py-0.5 rounded ${currentTheme.accent} text-white`}>
|
||||
{user?.role === 'admin' ? 'Админ' : 'Пользователь'}
|
||||
{user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'}
|
||||
</span>
|
||||
</div>
|
||||
<ThemeSelector currentTheme={theme} onThemeChange={handleThemeChange} />
|
||||
@@ -183,6 +185,56 @@ function App() {
|
||||
);
|
||||
}
|
||||
|
||||
if (showTickets) {
|
||||
return (
|
||||
<div className={`min-h-screen ${currentTheme.primary} ${currentTheme.text} transition-colors duration-300`}>
|
||||
<header className={`${currentTheme.secondary} ${currentTheme.border} border-b backdrop-blur-sm bg-opacity-95 sticky top-0 z-40`}>
|
||||
<div className="px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`${currentTheme.accent} p-2 rounded-lg`}>
|
||||
<Server className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className={`text-xl font-bold bg-gradient-to-r ${currentTheme.gradient} bg-clip-text text-transparent`}>MC Panel</h1>
|
||||
<p className={`text-xs ${currentTheme.textSecondary}`}>Управление серверами</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`px-3 py-1.5 rounded-lg ${currentTheme.card} ${currentTheme.border} border`}>
|
||||
<span className={`text-sm ${currentTheme.textSecondary}`}>
|
||||
{user?.username}
|
||||
</span>
|
||||
<span className={`ml-2 text-xs px-2 py-0.5 rounded ${currentTheme.accent} text-white`}>
|
||||
{user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'}
|
||||
</span>
|
||||
</div>
|
||||
<ThemeSelector currentTheme={theme} onThemeChange={handleThemeChange} />
|
||||
<button
|
||||
onClick={() => setShowTickets(false)}
|
||||
className={`${currentTheme.card} ${currentTheme.hover} px-4 py-2 rounded-lg transition flex items-center gap-2`}
|
||||
>
|
||||
<Server className="w-4 h-4" />
|
||||
Серверы
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className={`${currentTheme.danger} hover:opacity-90 px-4 py-2 rounded-lg transition flex items-center gap-2 text-white`}
|
||||
>
|
||||
<LogOut className="w-4 h-4" />
|
||||
Выход
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<Tickets token={token} user={user} theme={currentTheme} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`min-h-screen ${currentTheme.primary} ${currentTheme.text} transition-colors duration-300`}>
|
||||
{/* Header */}
|
||||
@@ -217,10 +269,17 @@ function App() {
|
||||
{user?.username}
|
||||
</span>
|
||||
<span className={`ml-2 text-xs px-2 py-0.5 rounded ${currentTheme.accent} text-white`}>
|
||||
{user?.role === 'admin' ? 'Админ' : 'Пользователь'}
|
||||
{user?.role === 'admin' ? 'Админ' : user?.role === 'support' ? 'Поддержка' : 'Пользователь'}
|
||||
</span>
|
||||
</div>
|
||||
<ThemeSelector currentTheme={theme} onThemeChange={handleThemeChange} />
|
||||
<button
|
||||
onClick={() => setShowTickets(true)}
|
||||
className={`${currentTheme.accent} ${currentTheme.accentHover} px-4 py-2 rounded-lg transition flex items-center gap-2 text-white`}
|
||||
>
|
||||
<MessageSquare className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Тикеты</span>
|
||||
</button>
|
||||
{user?.role === 'admin' && (
|
||||
<button
|
||||
onClick={() => setShowUsers(true)}
|
||||
|
||||
@@ -50,13 +50,7 @@ export default function Users({ token }) {
|
||||
}
|
||||
};
|
||||
|
||||
const toggleRole = async (username, currentRole) => {
|
||||
const newRole = currentRole === 'admin' ? 'user' : 'admin';
|
||||
|
||||
if (!confirm(`Изменить роль пользователя ${username} на ${newRole}?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const changeRole = async (username, newRole) => {
|
||||
try {
|
||||
await axios.put(
|
||||
`${API_URL}/api/users/${username}/role`,
|
||||
@@ -108,7 +102,7 @@ export default function Users({ token }) {
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`p-2 rounded ${
|
||||
user.role === 'admin' ? 'bg-blue-600' : 'bg-gray-700'
|
||||
user.role === 'admin' ? 'bg-blue-600' : user.role === 'support' ? 'bg-purple-600' : 'bg-gray-700'
|
||||
}`}>
|
||||
{user.role === 'admin' ? (
|
||||
<Shield className="w-6 h-6" />
|
||||
@@ -119,18 +113,21 @@ export default function Users({ token }) {
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">{user.username}</h3>
|
||||
<p className="text-sm text-gray-400">
|
||||
{user.role === 'admin' ? 'Администратор' : 'Пользователь'}
|
||||
{user.role === 'admin' ? 'Администратор' : user.role === 'support' ? 'Тех. поддержка' : 'Пользователь'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => toggleRole(user.username, user.role)}
|
||||
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded text-sm"
|
||||
<select
|
||||
value={user.role}
|
||||
onChange={(e) => changeRole(user.username, e.target.value)}
|
||||
className="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded text-sm border border-gray-600 focus:outline-none focus:border-blue-500"
|
||||
>
|
||||
{user.role === 'admin' ? 'Сделать пользователем' : 'Сделать админом'}
|
||||
</button>
|
||||
<option value="user">Пользователь</option>
|
||||
<option value="support">Тех. поддержка</option>
|
||||
<option value="admin">Администратор</option>
|
||||
</select>
|
||||
<button
|
||||
onClick={() => deleteUser(user.username)}
|
||||
className="bg-red-600 hover:bg-red-700 p-2 rounded"
|
||||
@@ -141,7 +138,7 @@ export default function Users({ token }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{user.role !== 'admin' && (
|
||||
{user.role === 'user' && (
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-2 text-gray-400">
|
||||
Доступ к серверам:
|
||||
@@ -175,6 +172,12 @@ export default function Users({ token }) {
|
||||
Администратор имеет доступ ко всем серверам
|
||||
</p>
|
||||
)}
|
||||
|
||||
{user.role === 'support' && (
|
||||
<p className="text-sm text-gray-400">
|
||||
Тех. поддержка имеет доступ к системе тикетов
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,8 @@ http://localhost:3000
|
||||
```
|
||||
|
||||
### 4️⃣ Войдите в систему
|
||||
- **Логин**: `admin`
|
||||
- **Пароль**: `admin`
|
||||
- **Логин**: `Sofa12345`
|
||||
- **Пароль**: `arkonsad123`
|
||||
|
||||
## 🎨 Смена темы
|
||||
|
||||
@@ -57,6 +57,16 @@ http://localhost:3000
|
||||
1. Нажмите кнопку "Пользователи" в header
|
||||
2. Создайте новых пользователей
|
||||
3. Выдайте доступ к серверам
|
||||
4. Назначьте роли (Пользователь, Тех. поддержка, Администратор)
|
||||
|
||||
### Система тикетов 🎫
|
||||
1. Нажмите кнопку "Тикеты" в header
|
||||
2. Создайте тикет с описанием проблемы
|
||||
3. Общайтесь в чате тикета
|
||||
4. Тех. поддержка и админы могут менять статусы:
|
||||
- 🟡 На рассмотрении
|
||||
- 🔵 В работе
|
||||
- 🟢 Закрыт
|
||||
|
||||
### Выдача доступа к серверу
|
||||
1. Выберите сервер
|
||||
|
||||
166
ГОТОВО.md
Normal file
166
ГОТОВО.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# ✅ MC Panel готова к использованию!
|
||||
|
||||
## 🎉 Что сделано
|
||||
|
||||
### 1. Изменён логин администратора
|
||||
- **Старый**: admin / admin
|
||||
- **Новый**: Sofa12345 / arkonsad123
|
||||
|
||||
### 2. Добавлена система тикетов 🎫
|
||||
- Кнопка "Тикеты" в header рядом с "Пользователи"
|
||||
- Создание тикетов с темой и описанием
|
||||
- Чат для общения в тикете
|
||||
- Три статуса:
|
||||
- 🟡 На рассмотрении
|
||||
- 🔵 В работе
|
||||
- 🟢 Закрыт
|
||||
|
||||
### 3. Добавлена роль "Тех. поддержка" 👨💻
|
||||
- Доступ ко всем тикетам
|
||||
- Возможность менять статусы
|
||||
- Возможность отвечать на тикеты
|
||||
- Отдельный бейдж в интерфейсе
|
||||
|
||||
### 4. Улучшено управление пользователями
|
||||
- Выпадающий список для выбора роли
|
||||
- Три роли: Пользователь, Тех. поддержка, Администратор
|
||||
- Цветные индикаторы ролей
|
||||
|
||||
## 🚀 Запуск панели
|
||||
|
||||
### Шаг 1: Запустите бэкенд
|
||||
```bash
|
||||
cd backend
|
||||
python main.py
|
||||
```
|
||||
|
||||
### Шаг 2: Запустите фронтенд
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Шаг 3: Откройте в браузере
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
### Шаг 4: Войдите как администратор
|
||||
- **Логин**: none
|
||||
- **Пароль**: none
|
||||
|
||||
## 📋 Быстрый старт
|
||||
|
||||
### Создание пользователя тех. поддержки
|
||||
1. Зарегистрируйте нового пользователя
|
||||
2. Войдите как админ (Sofa12345)
|
||||
3. Нажмите "Пользователи"
|
||||
4. Найдите нового пользователя
|
||||
5. В выпадающем списке выберите "Тех. поддержка"
|
||||
|
||||
### Создание тикета
|
||||
1. Войдите как обычный пользователь
|
||||
2. Нажмите кнопку "Тикеты" в header
|
||||
3. Нажмите "Создать тикет"
|
||||
4. Заполните тему и описание
|
||||
5. Нажмите "Создать"
|
||||
|
||||
### Работа с тикетом (тех. поддержка)
|
||||
1. Войдите как пользователь с ролью "Тех. поддержка"
|
||||
2. Нажмите "Тикеты"
|
||||
3. Выберите тикет из списка
|
||||
4. Отвечайте на сообщения
|
||||
5. Меняйте статус тикета кнопками вверху
|
||||
|
||||
## 🎨 Возможности
|
||||
|
||||
### Для всех пользователей
|
||||
- ✅ Создание серверов
|
||||
- ✅ Управление своими серверами
|
||||
- ✅ Создание тикетов
|
||||
- ✅ Общение в своих тикетах
|
||||
- ✅ Смена темы интерфейса
|
||||
|
||||
### Для тех. поддержки
|
||||
- ✅ Все возможности пользователя
|
||||
- ✅ Просмотр всех тикетов
|
||||
- ✅ Ответы на любые тикеты
|
||||
- ✅ Изменение статусов тикетов
|
||||
|
||||
### Для администраторов
|
||||
- ✅ Все возможности тех. поддержки
|
||||
- ✅ Управление пользователями
|
||||
- ✅ Назначение ролей
|
||||
- ✅ Удаление пользователей
|
||||
- ✅ Управление доступом к серверам
|
||||
|
||||
## 📁 Структура проекта
|
||||
|
||||
```
|
||||
MC Panel/
|
||||
├── backend/
|
||||
│ ├── main.py # Основной файл бэкенда
|
||||
│ ├── users.json # База пользователей
|
||||
│ ├── tickets.json # База тикетов (создаётся автоматически)
|
||||
│ └── servers/ # Папка с серверами
|
||||
│
|
||||
├── frontend/
|
||||
│ └── src/
|
||||
│ ├── App.jsx # Главный компонент
|
||||
│ ├── themes.js # Конфигурация тем
|
||||
│ └── components/
|
||||
│ ├── Auth.jsx # Авторизация
|
||||
│ ├── Tickets.jsx # Список тикетов
|
||||
│ ├── TicketChat.jsx # Чат тикета
|
||||
│ ├── CreateTicketModal.jsx # Создание тикета
|
||||
│ ├── Users.jsx # Управление пользователями
|
||||
│ ├── Console.jsx # Консоль сервера
|
||||
│ ├── FileManager.jsx # Менеджер файлов
|
||||
│ ├── Stats.jsx # Статистика
|
||||
│ └── ServerSettings.jsx # Настройки сервера
|
||||
│
|
||||
└── Документация/
|
||||
├── ГОТОВО.md # Этот файл
|
||||
├── TICKETS_SYSTEM.md # Документация системы тикетов
|
||||
├── CHANGELOG.md # История изменений
|
||||
└── БЫСТРЫЙ_СТАРТ.md # Быстрый старт
|
||||
```
|
||||
|
||||
## 🎯 Что дальше?
|
||||
|
||||
### Тестирование
|
||||
1. Создайте несколько пользователей
|
||||
2. Назначьте одному роль "Тех. поддержка"
|
||||
3. Создайте тикет от имени обычного пользователя
|
||||
4. Ответьте на тикет от имени тех. поддержки
|
||||
5. Измените статус тикета
|
||||
|
||||
### Настройка
|
||||
1. Измените темы под свой вкус в `frontend/src/themes.js`
|
||||
2. Настройте порты в конфигурации
|
||||
3. Добавьте свои серверы
|
||||
|
||||
### Развёртывание
|
||||
1. Настройте production сборку фронтенда
|
||||
2. Настройте HTTPS для безопасности
|
||||
3. Настройте базу данных вместо JSON файлов
|
||||
4. Настройте резервное копирование
|
||||
|
||||
## 📞 Поддержка
|
||||
|
||||
Если возникли вопросы:
|
||||
1. Прочитайте `TICKETS_SYSTEM.md`
|
||||
2. Прочитайте `CHANGELOG.md`
|
||||
3. Создайте тикет в системе
|
||||
|
||||
## ✨ Готово!
|
||||
|
||||
Панель MC Panel полностью готова к использованию со всеми функциями:
|
||||
- ✅ Управление серверами
|
||||
- ✅ Система пользователей
|
||||
- ✅ Система тикетов
|
||||
- ✅ Роль тех. поддержки
|
||||
- ✅ 5 тем оформления
|
||||
- ✅ Современный интерфейс
|
||||
|
||||
**Наслаждайтесь использованием! 🚀**
|
||||
Reference in New Issue
Block a user