Add Notification and new mini desing

This commit is contained in:
2026-01-15 13:26:04 +06:00
parent 303d38f28e
commit 8edd7131a2
56 changed files with 3554 additions and 5197 deletions

531
API.md Normal file
View File

@@ -0,0 +1,531 @@
# MC Panel API - Полная документация
**Версия:** 1.0.0
**Дата:** 15 января 2026
---
## 📋 Содержание
1. [Базовая информация](#базовая-информация)
2. [Быстрый старт](#быстрый-старт)
3. [Аутентификация](#аутентификация)
4. [Управление пользователями](#управление-пользователями)
5. [Личный кабинет](#личный-кабинет)
6. [Управление серверами](#управление-серверами)
7. [Управление файлами](#управление-файлами)
8. [Тикеты](#тикеты)
9. [OpenID Connect](#openid-connect)
10. [Коды ошибок](#коды-ошибок)
11. [Примеры интеграции](#примеры-интеграции)
12. [Postman коллекция](#postman-коллекция)
---
## Базовая информация
**Base URL:** `http://localhost:8000`
**Формат данных:** JSON
**Аутентификация:** Bearer Token (JWT)
Все защищенные эндпоинты требуют заголовок:
```
Authorization: Bearer <token>
```
### Заголовки запросов
```
Content-Type: application/json
Authorization: Bearer <token>
```
### Формат ответов
```json
{
"message": "Success message",
"data": {}
}
```
---
## Быстрый старт
### 1. Регистрация
```bash
curl -X POST http://localhost:8000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password123"}'
```
### 2. Вход
```bash
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password123"}'
```
**Ответ:**
```json
{
"access_token": "eyJhbGc...",
"token_type": "bearer",
"username": "admin",
"role": "admin"
}
```
### 3. Использование токена
```bash
TOKEN="your_token_here"
curl http://localhost:8000/api/servers \
-H "Authorization: Bearer $TOKEN"
```
---
## Аутентификация
### POST /api/auth/register
Регистрация нового пользователя.
**Body:**
```json
{
"username": "string",
"password": "string"
}
```
**Response (200):**
```json
{
"access_token": "string",
"token_type": "bearer",
"username": "string",
"role": "admin|user"
}
```
---
### POST /api/auth/login
Вход в систему.
**Body:**
```json
{
"username": "string",
"password": "string"
}
```
**Response (200):**
```json
{
"access_token": "string",
"token_type": "bearer",
"username": "string",
"role": "admin|user|support|banned"
}
```
**Errors:**
- `401` - Неверные учетные данные
---
### GET /api/auth/me
Получить информацию о текущем пользователе.
**Headers:** `Authorization: Bearer <token>`
**Response (200):**
```json
{
"username": "string",
"role": "admin|user|support|banned",
"servers": ["server1", "server2"]
}
```
---
## Управление пользователями
### GET /api/users
Список всех пользователей.
### PUT /api/users/{username}/role
Изменить роль пользователя (admin only).
**Body:** `{"role": "admin|user|support|banned"}`
### PUT /api/users/{username}/servers
Управление доступом к серверам.
**Body:** `{"servers": ["server1", "server2"]}`
### DELETE /api/users/{username}
Удалить пользователя (admin only).
---
## Личный кабинет
### GET /api/profile/stats
Статистика текущего пользователя.
### GET /api/profile/stats/{username}
Статистика другого пользователя (admin/support).
### PUT /api/profile/username
Изменить имя пользователя.
**Body:** `{"new_username": "string", "password": "string"}`
### PUT /api/profile/password
Изменить пароль.
**Body:** `{"old_password": "string", "new_password": "string"}`
---
## Управление серверами
### GET /api/servers
Список серверов пользователя.
### POST /api/servers/create
Создать новый сервер.
**Body:**
```json
{
"name": "server1",
"displayName": "My Server",
"startCommand": "java -Xmx2G -jar server.jar nogui"
}
```
### GET /api/servers/{server}/config
Получить конфигурацию сервера.
### PUT /api/servers/{server}/config
Обновить конфигурацию сервера.
### DELETE /api/servers/{server}
Удалить сервер (admin only).
### POST /api/servers/{server}/start
Запустить сервер.
### POST /api/servers/{server}/stop
Остановить сервер.
### POST /api/servers/{server}/command
Отправить команду серверу.
**Body:** `{"command": "say Hello"}`
### GET /api/servers/{server}/stats
Получить статистику сервера (CPU, RAM, Disk).
### WS /ws/servers/{server}/console
WebSocket для консоли сервера (логи в реальном времени).
---
## Управление файлами
### GET /api/servers/{server}/files?path={path}
Список файлов в директории.
### POST /api/servers/{server}/files/create
Создать файл или папку.
**Body:** `{"type": "file|folder", "name": "string", "path": "string"}`
### POST /api/servers/{server}/files/upload?path={path}
Загрузить файл (multipart/form-data).
### GET /api/servers/{server}/files/download?path={path}
Скачать файл.
### GET /api/servers/{server}/files/content?path={path}
Получить содержимое текстового файла.
### PUT /api/servers/{server}/files/content?path={path}
Сохранить содержимое файла.
**Body:** `{"content": "string"}`
### PUT /api/servers/{server}/files/rename?old_path={path}&new_name={name}
Переименовать файл.
### POST /api/servers/{server}/files/move
Переместить файл.
**Body:** `{"source": "path", "destination": "path"}`
### DELETE /api/servers/{server}/files?path={path}
Удалить файл или папку.
---
## Тикеты
### GET /api/tickets
Список тикетов (свои или все для admin/support).
### POST /api/tickets/create
Создать новый тикет.
**Body:** `{"title": "string", "description": "string"}`
### GET /api/tickets/{id}
Получить тикет по ID.
### POST /api/tickets/{id}/message
Добавить сообщение в тикет.
**Body:** `{"text": "string"}`
### PUT /api/tickets/{id}/status
Изменить статус тикета (admin/support).
**Body:** `{"status": "pending|in_progress|closed"}`
---
## OpenID Connect
### GET /api/auth/oidc/providers
Список доступных OIDC провайдеров.
### GET /api/auth/oidc/{provider}/login
Начать аутентификацию через OIDC (redirect).
### GET /api/auth/oidc/{provider}/callback
Callback от OIDC провайдера (redirect).
---
## Коды ошибок
| Код | Описание | Решение |
|-----|----------|---------|
| 200 | Успешно | - |
| 400 | Неверный запрос | Проверьте формат данных |
| 401 | Не авторизован | Войдите в систему |
| 403 | Доступ запрещен | Недостаточно прав |
| 404 | Не найдено | Проверьте URL |
| 500 | Ошибка сервера | Обратитесь к администратору |
---
## Примеры интеграции
### Python
```python
import requests
class MCPanelAPI:
def __init__(self, base_url, username, password):
self.base_url = base_url
self.token = None
self.login(username, password)
def login(self, username, password):
r = requests.post(f"{self.base_url}/api/auth/login",
json={"username": username, "password": password})
self.token = r.json()["access_token"]
def get_headers(self):
return {"Authorization": f"Bearer {self.token}"}
def get_servers(self):
r = requests.get(f"{self.base_url}/api/servers",
headers=self.get_headers())
return r.json()
def start_server(self, server_name):
r = requests.post(
f"{self.base_url}/api/servers/{server_name}/start",
headers=self.get_headers())
return r.json()
# Использование
api = MCPanelAPI("http://localhost:8000", "admin", "password")
servers = api.get_servers()
api.start_server("survival")
```
---
### JavaScript
```javascript
class MCPanelAPI {
constructor(baseURL) {
this.baseURL = baseURL;
this.token = null;
}
async login(username, password) {
const response = await fetch(`${this.baseURL}/api/auth/login`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username, password})
});
const data = await response.json();
this.token = data.access_token;
}
getHeaders() {
return {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
};
}
async getServers() {
const response = await fetch(`${this.baseURL}/api/servers`, {
headers: this.getHeaders()
});
return await response.json();
}
async startServer(serverName) {
const response = await fetch(
`${this.baseURL}/api/servers/${serverName}/start`,
{method: 'POST', headers: this.getHeaders()}
);
return await response.json();
}
}
// Использование
const api = new MCPanelAPI('http://localhost:8000');
await api.login('admin', 'password');
const servers = await api.getServers();
await api.startServer('survival');
```
---
### cURL примеры
```bash
# Вход
TOKEN=$(curl -s -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"pass"}' \
| jq -r '.access_token')
# Список серверов
curl http://localhost:8000/api/servers \
-H "Authorization: Bearer $TOKEN"
# Создать сервер
curl -X POST http://localhost:8000/api/servers/create \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"survival","displayName":"Survival","startCommand":"java -jar server.jar"}'
# Запустить сервер
curl -X POST http://localhost:8000/api/servers/survival/start \
-H "Authorization: Bearer $TOKEN"
# Отправить команду
curl -X POST http://localhost:8000/api/servers/survival/command \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"command":"say Hello"}'
# Список файлов
curl "http://localhost:8000/api/servers/survival/files?path=plugins" \
-H "Authorization: Bearer $TOKEN"
# Создать тикет
curl -X POST http://localhost:8000/api/tickets/create \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Problem","description":"Details"}'
```
---
## Postman коллекция
### Импорт коллекции
1. Откройте Postman
2. File → Import
3. Выберите файл `MC_Panel_API.postman_collection.json`
4. Коллекция готова к использованию
### Настройка переменных
В коллекции настройте переменные:
- `baseUrl` = `http://localhost:8000`
- `serverName` = `survival` (или имя вашего сервера)
- `token` = автоматически сохраняется после Login
### Использование
1. Выполните запрос "Login" для получения токена
2. Токен автоматически сохранится в переменную `token`
3. Все остальные запросы будут использовать этот токен
4. Используйте любые эндпоинты из коллекции
### Структура коллекции
- **Authentication** - регистрация, вход, получение пользователя
- **Users** - управление пользователями
- **Servers** - управление серверами
- **Files** - операции с файлами
- **Tickets** - система тикетов
- **Profile** - личный кабинет
- **OpenID Connect** - OIDC провайдеры
---
## Безопасность
### JWT Токены
- Срок действия: 7 дней
- Алгоритм: HS256
- Хранение: localStorage (фронтенд)
### Рекомендации
1. Используйте HTTPS в production
2. Измените SECRET_KEY в `backend/main.py`
3. Используйте сильные пароли (минимум 6 символов)
4. Регулярно обновляйте зависимости
5. Ограничьте CORS для конкретных доменов
---
## Лимиты и ограничения
- **Размер файла:** не ограничен (зависит от сервера)
- **Количество запросов:** не ограничено
- **Длина сообщения:** не ограничена
- **Количество серверов:** не ограничено
- **Срок хранения логов:** 1000 последних строк
---
## Changelog
### 1.0.0 (15.01.2026)
- ✨ Первый релиз API
- ✅ 37 эндпоинтов
- ✅ JWT аутентификация
- ✅ OpenID Connect
- ✅ WebSocket консоль
- ✅ Полное управление серверами
- ✅ Файловый менеджер
- ✅ Система тикетов
---
## Поддержка
- **Документация проекта:** ДОКУМЕНТАЦИЯ.md
- **Postman коллекция:** MC_Panel_API.postman_collection.json
- **Тикеты:** Используйте систему тикетов в панели
---
**Версия API:** 1.0.0
**Дата обновления:** 15 января 2026
**Спасибо за использование MC Panel API!** 🚀

View File

@@ -1,25 +0,0 @@
# Применение исправлений
## Что исправлено
### ✅ Фронтенд компоненты
Все компоненты обновлены для передачи токена:
- `Console.jsx`
- `Stats.jsx`
- `FileManager.jsx`
- `ServerSettings.jsx`
- `CreateServerModal.jsx`
### ⚠️ Нужно обновить вручную
#### 1. App.jsx (или App_final.jsx)
Найдите строку:
```jsx
{user?.role === 'admin' && (
<button
onClick={() => setShowCreateModal(true)}
className="bg-blue-600 hover:bg-blue-700 p-2 rounded"
title="Создать сервер"
>
<Plus className="w-4

View File

@@ -1,196 +0,0 @@
# Настройка системы авторизации
## Что добавлено
1. **Система авторизации** - вход и регистрация пользователей
2. **Роли пользователей** - администраторы и обычные пользователи
3. **Управление доступом** - админы могут выдавать доступ к серверам
4. **JWT токены** - безопасная авторизация
## Первый запуск
### 1. Установите новые зависимости
```bash
cd backend
pip install -r requirements.txt
```
Новые библиотеки:
- `passlib[bcrypt]` - хеширование паролей
- `python-jose[cryptography]` - JWT токены
### 2. Переименуйте файл бэкенда
**ВАЖНО:** Удалите старый `backend/main.py` и переименуйте `backend/main_new.py` в `backend/main.py`
```bash
cd backend
del main.py
ren main_new.py main.py
```
Или вручную в проводнике Windows.
### 3. Запустите бэкенд
```bash
cd backend
python main.py
```
При первом запуске создастся пользователь по умолчанию:
- **Логин:** admin
- **Пароль:** admin
### 4. Запустите фронтенд
```bash
cd frontend
npm run dev
```
## Использование
### Первый вход
1. Откройте http://localhost:3000
2. Войдите как `admin` / `admin`
3. **ВАЖНО:** Смените пароль администратора!
### Создание пользователей
1. Нажмите кнопку **"Пользователи"** в шапке
2. Новые пользователи могут зарегистрироваться самостоятельно
3. По умолчанию новые пользователи получают роль "Пользователь"
### Управление доступом к серверам
1. Перейдите в **"Пользователи"**
2. Найдите нужного пользователя
3. Нажмите на названия серверов чтобы выдать/забрать доступ
4. Зеленые кнопки = доступ есть
5. Серые кнопки = доступа нет
### Роли пользователей
**Администратор:**
- Видит все серверы
- Может создавать/удалять серверы
- Может управлять пользователями
- Может изменять настройки серверов
**Пользователь:**
- Видит только серверы с доступом
- Может запускать/останавливать свои серверы
- Может управлять файлами своих серверов
- Не может создавать серверы
- Не может изменять настройки
### Изменение роли
1. Перейдите в **"Пользователи"**
2. Нажмите **"Сделать админом"** или **"Сделать пользователем"**
3. Подтвердите действие
### Удаление пользователя
1. Перейдите в **"Пользователи"**
2. Нажмите кнопку с иконкой корзины
3. Подтвердите удаление
**Примечание:** Нельзя удалить самого себя или изменить свою роль.
## Безопасность
### Смена секретного ключа
Откройте `backend/main_new.py` (или `main.py` после переименования) и измените:
```python
SECRET_KEY = "your-secret-key-change-this-in-production-12345"
```
На случайную строку, например:
```python
SECRET_KEY = "super-secret-key-" + str(uuid.uuid4())
```
### Время жизни токена
По умолчанию токен действует 7 дней. Чтобы изменить:
```python
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7 дней
```
### Хранение паролей
Пароли хешируются с помощью bcrypt и хранятся в файле `backend/users.json`.
**Не удаляйте этот файл!** Иначе потеряете всех пользователей.
## Файлы
- `backend/users.json` - база пользователей
- `backend/main_new.py` - новый бэкенд с авторизацией
- `frontend/src/components/Auth.jsx` - форма входа/регистрации
- `frontend/src/components/Users.jsx` - управление пользователями
## API эндпоинты
### Авторизация
- `POST /api/auth/register` - регистрация
- `POST /api/auth/login` - вход
- `GET /api/auth/me` - информация о текущем пользователе
### Пользователи (только админы)
- `GET /api/users` - список пользователей
- `PUT /api/users/{username}/servers` - изменить доступ к серверам
- `PUT /api/users/{username}/role` - изменить роль
- `DELETE /api/users/{username}` - удалить пользователя
### Серверы (с проверкой доступа)
Все существующие эндпоинты теперь требуют токен в заголовке:
```
Authorization: Bearer <token>
```
## Troubleshooting
### Ошибка "Требуется авторизация"
Токен истек или недействителен. Выйдите и войдите заново.
### Не могу войти как admin
Удалите файл `backend/users.json` и перезапустите бэкенд. Создастся новый админ с паролем `admin`.
### Забыл пароль
Удалите файл `backend/users.json` - все пользователи будут удалены и создастся новый админ.
Или отредактируйте `users.json` вручную, удалив нужного пользователя.
### Пользователь не видит серверы
Проверьте что админ выдал ему доступ в разделе "Пользователи".
## Миграция со старой версии
Если у вас уже есть серверы:
1. Сделайте backup папки `backend/servers/`
2. Установите новые зависимости
3. Замените `main.py` на новый
4. Запустите бэкенд
5. Войдите как admin/admin
6. Все серверы будут доступны админу автоматически
7. Создайте пользователей и выдайте им доступ
---
**Готово!** Теперь у вас есть полноценная система авторизации и управления доступом! 🔐

View File

@@ -1,176 +0,0 @@
# ⛔ Роль "Забанен"
## Что добавлено
### Новая роль "Забанен" (banned)
Роль для блокировки пользователей, которые нарушили правила или должны быть временно/постоянно отстранены от использования панели.
## 🚫 Ограничения роли
### Полная блокировка доступа
Пользователи с ролью "Забанен" **не имеют доступа** ни к каким функциям панели:
- ❌ Создание серверов
- ❌ Управление серверами
- ❌ Просмотр консоли
- ❌ Менеджер файлов
- ❌ Создание тикетов
- ❌ Просмотр тикетов
- ❌ Личный кабинет
- ❌ Любые другие функции
### Что происходит при попытке входа
При попытке доступа к любому endpoint API пользователь получает ошибку:
```
403 Forbidden: "Ваш аккаунт заблокирован"
```
## 🎨 Визуальное отображение
### В списке пользователей (Users.jsx)
- 🔴 **Красная иконка** пользователя
- **Текст роли**: "Забанен"
- **Описание**: "⛔ Пользователь заблокирован и не имеет доступа к панели"
### В личном кабинете (Profile.jsx)
- 🔴 **Красный бейдж** с текстом "Забанен"
- **Описание**: "⛔ Аккаунт заблокирован, доступ запрещён"
### В header (App.jsx)
- **Бейдж**: "Забанен" (красный цвет)
## 🔧 Как использовать
### Заблокировать пользователя
1. Войдите как администратор (none / none)
2. Нажмите кнопку "Пользователи" в header
3. Найдите нужного пользователя
4. В выпадающем списке выберите "Забанен"
5. Роль изменится автоматически
### Разблокировать пользователя
1. Войдите как администратор
2. Нажмите кнопку "Пользователи"
3. Найдите заблокированного пользователя
4. В выпадающем списке выберите другую роль:
- "Пользователь" - обычный доступ
- "Тех. поддержка" - доступ к тикетам
- "Администратор" - полный доступ
## 📋 Технические детали
### Backend (main.py)
#### Проверка в get_current_user()
```python
# Проверка на бан
if user.get("role") == "banned":
raise HTTPException(status_code=403, detail="Ваш аккаунт заблокирован")
```
Эта проверка выполняется **перед каждым запросом** к API, что гарантирует полную блокировку доступа.
#### Обновление роли
```python
new_role = data.get("role")
if new_role not in ["admin", "user", "support", "banned"]:
raise HTTPException(400, "Неверная роль")
```
### Frontend
#### App.jsx
```javascript
const getRoleName = (role) => {
switch (role) {
case 'admin':
return 'Админ';
case 'support':
return 'Поддержка';
case 'banned':
return 'Забанен';
default:
return 'Пользователь';
}
};
```
#### Users.jsx
```javascript
<option value="banned">Забанен</option>
```
#### Profile.jsx
```javascript
case 'banned':
return 'bg-red-500/20 text-red-500 border-red-500/50';
```
## 🔐 Безопасность
### Защита на уровне API
- Проверка роли выполняется в функции `get_current_user()`
- Блокировка происходит **до** выполнения любого endpoint
- Невозможно обойти блокировку через API
### Защита на уровне UI
- Визуальное отображение статуса блокировки
- Красные индикаторы для предупреждения
- Понятные сообщения об ошибках
## ⚠️ Важные замечания
### Администраторы не могут быть заблокированы
Рекомендуется добавить проверку, чтобы администраторы не могли заблокировать сами себя или других администраторов.
### Логирование блокировок
Рекомендуется добавить логирование:
- Кто заблокировал пользователя
- Когда была выполнена блокировка
- Причина блокировки (опционально)
### Уведомления
Можно добавить:
- Email уведомление о блокировке
- Причину блокировки в профиле
- Дату окончания блокировки (для временных банов)
## 📊 Статистика
### Роли в системе
1. **Администратор** (admin) - полный доступ
2. **Тех. поддержка** (support) - доступ к тикетам
3. **Пользователь** (user) - доступ к своим серверам
4. **Забанен** (banned) - нет доступа ⛔
## ✅ Готово!
Роль "Забанен" полностью интегрирована в MC Panel. Администраторы могут блокировать пользователей, которые нарушают правила или должны быть отстранены от использования панели.
### Тестирование
1. **Создайте тестового пользователя**
- Зарегистрируйте нового пользователя
2. **Заблокируйте его**
- Войдите как админ
- Откройте "Пользователи"
- Измените роль на "Забанен"
3. **Попробуйте войти**
- Выйдите из админа
- Войдите как заблокированный пользователь
- Вы увидите ошибку "Ваш аккаунт заблокирован"
4. **Разблокируйте**
- Войдите как админ
- Измените роль обратно на "Пользователь"
## 🎯 Использование
Роль "Забанен" готова к использованию. Используйте её для:
- Блокировки нарушителей
- Временного отстранения пользователей
- Защиты панели от нежелательных действий
**Будьте осторожны с блокировками! 🚨**

145
BUGFIX.md
View File

@@ -1,145 +0,0 @@
# Исправление багов
## Исправленные проблемы
### 1. ✅ Обычные пользователи теперь могут создавать серверы
**Что изменилось:**
- Убрана проверка роли при создании сервера
- Любой авторизованный пользователь может создать сервер
- При создании сервера обычным пользователем, ему автоматически выдается доступ к этому серверу
- Кнопка "+" теперь видна всем пользователям
**Файлы:**
- `backend/main_new.py` - убрана проверка `if user["role"] != "admin"`
- `frontend/src/App_final.jsx` - кнопка создания доступна всем
### 2. ✅ Админ теперь может просматривать файлы, статистику, настройки и консоль
**Проблема:**
Компоненты не передавали токен авторизации в запросы к API.
**Что исправлено:**
- Все компоненты теперь принимают prop `token`
- Все запросы к API включают заголовок `Authorization: Bearer ${token}`
**Исправленные компоненты:**
- `Console.jsx` - добавлен токен в запросы команд
- `Stats.jsx` - добавлен токен в запросы статистики
- `FileManager.jsx` - добавлен токен во все файловые операции
- `ServerSettings.jsx` - добавлен токен в настройки
- `CreateServerModal.jsx` - добавлен токен при создании
## Что нужно сделать
### Если вы еще не переименовали файлы:
1. **Удалите старые файлы:**
```
backend/main.py (если есть)
frontend/src/App.jsx (если есть)
```
2. **Переименуйте новые файлы:**
```
backend/main_new.py → backend/main.py
frontend/src/App_final.jsx → frontend/src/App.jsx
```
3. **Перезапустите панель:**
```bash
START_PANEL.bat
```
### Если файлы уже переименованы:
Просто перезапустите панель - изменения уже применены в `main_new.py` и `App_final.jsx`.
## Проверка исправлений
### Тест 1: Создание сервера обычным пользователем
1. Зарегистрируйте нового пользователя
2. Войдите под ним
3. Нажмите кнопку "+" в списке серверов
4. Создайте сервер
5. ✅ Сервер должен появиться в списке
### Тест 2: Просмотр файлов админом
1. Войдите как admin
2. Выберите любой сервер
3. Перейдите на вкладку "Файлы"
4. ✅ Должен отобразиться список файлов
### Тест 3: Просмотр статистики
1. Выберите сервер
2. Перейдите на вкладку "Статистика"
3. ✅ Должна отобразиться статистика (CPU, RAM, Disk)
### Тест 4: Консоль
1. Запустите сервер
2. Перейдите на вкладку "Консоль"
3. ✅ Должны появиться логи сервера
4. Отправьте команду (например, "list")
5. ✅ Команда должна выполниться
### Тест 5: Настройки
1. Перейдите на вкладку "Настройки"
2. ✅ Должны отобразиться настройки сервера
3. Измените что-нибудь и сохраните
4. ✅ Изменения должны сохраниться
## Дополнительные улучшения
### Автоматический доступ к созданным серверам
Теперь когда обычный пользователь создает сервер:
1. Сервер создается
2. Пользователю автоматически выдается доступ к этому серверу
3. Сервер сразу появляется в его списке
Админу не нужно вручную выдавать доступ!
### Логирование ошибок
Все ошибки API теперь выводятся в консоль браузера (F12) для отладки.
## Если что-то не работает
### Ошибка "Требуется авторизация"
**Причина:** Токен не передается в запросах
**Решение:**
1. Убедитесь что используете обновленные файлы
2. Очистите кэш браузера (Ctrl+Shift+Delete)
3. Выйдите и войдите заново
### Ошибка "Нет доступа к этому серверу"
**Причина:** У пользователя нет прав на сервер
**Решение:**
1. Если вы админ - проверьте что сервер существует
2. Если вы пользователь - попросите админа выдать доступ
3. Или создайте свой сервер - доступ выдастся автоматически
### Пустой список файлов
**Причина:** Токен не передается или сервер пустой
**Решение:**
1. Проверьте консоль браузера (F12) на ошибки
2. Убедитесь что используете обновленный FileManager.jsx
3. Загрузите файлы через кнопку "Загрузить"
---
**Готово!** Все баги исправлены. Теперь:
- ✅ Любой пользователь может создавать серверы
- ✅ Админ может просматривать все вкладки
-Все запросы включают токен авторизации

View File

@@ -1,191 +0,0 @@
# 📝 История изменений MC Panel
## Версия 2.2 - Роль "Забанен" (14.01.2026)
### ✨ Новые возможности
#### ⛔ Роль "Забанен"
- Новая роль для блокировки пользователей
- Полная блокировка доступа к панели
- Проверка на уровне API (функция get_current_user)
- Красные индикаторы в интерфейсе
- Сообщение об ошибке при попытке входа
#### 🎨 Визуальное отображение
- Красная иконка в списке пользователей
- Красный бейдж "Забанен" в header
- Красный бейдж в личном кабинете
- Предупреждающие сообщения
#### 🔐 Безопасность
- Проверка роли перед каждым запросом к API
- Невозможно обойти блокировку
- Ошибка 403: "Ваш аккаунт заблокирован"
### 📁 Новые файлы
- `BANNED_ROLE.md` - документация роли "Забанен"
### 🔧 Изменения в коде
- `backend/main.py` - добавлена проверка на бан в get_current_user()
- `frontend/src/App.jsx` - добавлена функция getRoleName()
- `frontend/src/components/Users.jsx` - добавлена опция "Забанен"
- `frontend/src/components/Profile.jsx` - добавлено отображение роли "Забанен"
---
## Версия 2.1 - Личный кабинет (14.01.2026)
### ✨ Новые возможности
#### 👤 Личный кабинет
- Кнопка "Личный кабинет" в header рядом с "Тикеты"
- Три вкладки: Обзор, Имя пользователя, Пароль
- Статистика профиля (серверы, тикеты, роль)
- Список своих серверов
- Изменение имени пользователя с подтверждением паролем
- Изменение пароля с проверкой
- Показ/скрытие паролей в формах
#### 🔐 Безопасность
- Проверка уникальности имени пользователя
- Автоматическое обновление владельцев серверов при смене имени
- Автоматическое обновление доступов к серверам
- Новый JWT токен при смене имени
- Хеширование паролей (bcrypt)
#### 📊 Статистика профиля
- Общее количество серверов
- Мои серверы (владелец)
- Доступные серверы
- Статистика по тикетам (всего, на рассмотрении, в работе, закрыто)
- Информация о роли
### 📁 Новые файлы
- `frontend/src/components/Profile.jsx` - компонент личного кабинета
- `PROFILE_SYSTEM.md` - документация личного кабинета
### 🔧 API Endpoints
- `PUT /api/profile/username` - изменить имя пользователя
- `PUT /api/profile/password` - изменить пароль
- `GET /api/profile/stats` - получить статистику профиля
---
## Версия 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`

View File

@@ -1,155 +0,0 @@
# Руководство по отладке проблем
## Проблема: После запуска сервера пропадают файлы/настройки/статистика
### Причина
Процесс сервера блокирует выполнение или завершается с ошибкой.
### Диагностика
1. **Проверьте логи бэкенда** (терминал где запущен `python main.py`):
```
Сервер test_server запущен с PID 12345
Начало чтения вывода для сервера test_server
```
2. **Проверьте консоль браузера** (F12):
```javascript
// Должны быть логи:
Сервер запущен: {message: "Сервер запущен", pid: 12345}
```
3. **Проверьте, запустился ли Java процесс**:
```bash
# Windows
tasklist | findstr java
# Должен показать процесс java.exe
```
4. **Проверьте наличие server.jar**:
- Откройте папку `backend/servers/ИМЯ_СЕРВЕРА/`
- Убедитесь, что там есть файл `server.jar` или другой .jar файл
- Проверьте команду запуска в настройках сервера
### Решение
#### Если server.jar отсутствует:
1. Скачайте server.jar для Minecraft
2. Загрузите через менеджер файлов в панели
3. Убедитесь, что команда запуска правильная
#### Если Java не установлена:
1. Установите Java 17 или новее
2. Проверьте установку:
```bash
java -version
```
#### Если процесс запускается но сразу завершается:
1. Проверьте логи в консоли панели
2. Возможно нужно принять EULA:
- Откройте файл `eula.txt` через редактор в панели
- Измените `eula=false` на `eula=true`
- Сохраните и перезапустите сервер
#### Если команда запуска неправильная:
1. Перейдите в Настройки сервера
2. Измените команду запуска, например:
```
java -Xmx2G -Xms1G -jar server.jar nogui
```
3. Сохраните настройки
4. Запустите сервер
## Проблема: Сервер не останавливается
### Причина
Процесс не отвечает на команду stop.
### Решение
1. **Через панель**: Подождите 30 секунд, процесс будет принудительно завершен
2. **Вручную через Task Manager**:
- Откройте Диспетчер задач (Ctrl+Shift+Esc)
- Найдите процесс `java.exe`
- Завершите процесс
- Обновите страницу панели
## Проблема: Консоль не показывает логи
### Причина
WebSocket не подключается или процесс не выводит логи.
### Диагностика
1. **Проверьте консоль браузера**:
```
WebSocket подключен
```
2. **Проверьте логи бэкенда**:
```
WebSocket подключен для сервера: test_server
Отправка X существующих логов
```
### Решение
1. Перезапустите сервер
2. Обновите страницу панели (F5)
3. Проверьте, что сервер действительно запущен
## Проблема: Статистика показывает неправильный статус
### Причина
Процесс завершился, но панель не обновилась.
### Решение
1. Обновите страницу (F5)
2. Статус обновляется автоматически каждые 5 секунд
3. Проверьте логи бэкенда на наличие ошибок
## Полезные команды для отладки
### Проверка портов
```bash
# Windows
netstat -ano | findstr :8000
netstat -ano | findstr :3000
```
### Проверка процессов Java
```bash
# Windows
tasklist | findstr java
# Убить все процессы Java (ОСТОРОЖНО!)
taskkill /F /IM java.exe
```
### Очистка и перезапуск
1. Остановите все серверы в панели
2. Закройте бэкенд (Ctrl+C)
3. Закройте фронтенд (Ctrl+C)
4. Убейте все процессы Java если нужно
5. Запустите бэкенд заново
6. Запустите фронтенд заново
7. Обновите страницу в браузере
## Логи для отправки при обращении за помощью
Если проблема не решается, соберите следующую информацию:
1. **Логи бэкенда** (последние 50 строк из терминала)
2. **Консоль браузера** (F12 → Console, скриншот или текст)
3. **Network вкладка** (F12 → Network, покажите неудачные запросы)
4. **Содержимое папки сервера** (список файлов)
5. **Команда запуска** из настроек сервера

View File

@@ -1,163 +0,0 @@
# Финальные шаги для запуска панели с авторизацией
## Шаг 1: Переименуйте файлы
### Backend
1. Откройте папку `backend`
2. Удалите файл `main.py` (если есть)
3. Переименуйте `main_new.py` в `main.py`
### Frontend
1. Откройте папку `frontend/src`
2. Удалите файл `App.jsx` (если есть)
3. Переименуйте `App_final.jsx` в `App.jsx`
## Шаг 2: Установите зависимости
```bash
cd backend
pip install -r requirements.txt
```
Новые зависимости:
- passlib[bcrypt] - для хеширования паролей
- python-jose[cryptography] - для JWT токенов
## Шаг 3: Запустите панель
### Вариант 1: Автоматический запуск
```bash
START_PANEL.bat
```
### Вариант 2: Ручной запуск
**Терминал 1 - Бэкенд:**
```bash
cd backend
python main.py
```
**Терминал 2 - Фронтенд:**
```bash
cd frontend
npm run dev
```
## Шаг 4: Первый вход
1. Откройте http://localhost:3000
2. Войдите с учетными данными:
- **Логин:** admin
- **Пароль:** admin
3. Вы попадете в панель управления
## Что нового
### ✅ Система авторизации
- Вход и регистрация пользователей
- JWT токены для безопасности
- Автоматический выход при истечении токена
### ✅ Роли пользователей
- **Администратор** - полный доступ ко всем функциям
- **Пользователь** - доступ только к назначенным серверам
### ✅ Управление пользователями
- Кнопка "Пользователи" в шапке (только для админов)
- Выдача/отзыв доступа к серверам
- Изменение ролей пользователей
- Удаление пользователей
### ✅ Контроль доступа
- Пользователи видят только свои серверы
- Админы видят все серверы
- Проверка прав на каждое действие
## Использование
### Создание нового пользователя
**Вариант 1: Регистрация**
1. На странице входа нажмите "Регистрация"
2. Введите логин и пароль
3. Новый пользователь создастся с ролью "Пользователь"
**Вариант 2: Админ создает**
1. Попросите пользователя зарегистрироваться
2. Админ выдает ему доступ к нужным серверам
### Выдача доступа к серверу
1. Войдите как администратор
2. Нажмите кнопку "Пользователи"
3. Найдите нужного пользователя
4. Нажмите на название сервера (станет зеленым)
5. Пользователь сразу увидит этот сервер
### Изменение роли
1. В разделе "Пользователи"
2. Нажмите "Сделать админом" или "Сделать пользователем"
3. Подтвердите действие
## Безопасность
### Смените секретный ключ!
Откройте `backend/main.py` и измените:
```python
SECRET_KEY = "your-secret-key-change-this-in-production-12345"
```
На случайную строку длиной минимум 32 символа.
### Смените пароль администратора
1. Войдите как admin
2. Создайте нового администратора с другим паролем
3. Войдите под новым админом
4. Удалите старого admin
## Файлы данных
- `backend/users.json` - база пользователей (НЕ УДАЛЯЙТЕ!)
- `backend/servers/` - папки серверов
- `backend/servers/*/panel_config.json` - настройки каждого сервера
## Troubleshooting
### "Требуется авторизация"
Токен истек. Выйдите и войдите заново.
### Не могу войти
Удалите `backend/users.json` и перезапустите бэкенд. Создастся новый admin/admin.
### Пользователь не видит серверы
Проверьте что админ выдал ему доступ в разделе "Пользователи".
### Ошибка импорта passlib или jose
Установите зависимости:
```bash
pip install passlib[bcrypt] python-jose[cryptography]
```
## Доступ через сеть
Всё работает так же как раньше:
1. Узнайте ваш IP в Radmin VPN: `ipconfig`
2. Друг открывает: `http://ВАШ_IP:3000`
3. Друг регистрируется
4. Вы выдаете ему доступ к нужным серверам
## Готово!
Теперь у вас полноценная панель управления с:
- ✅ Авторизацией и регистрацией
- ✅ Ролями и правами доступа
- ✅ Управлением пользователями
- ✅ Контролем доступа к серверам
- ✅ Всеми предыдущими функциями
Подробнее см. `AUTH_SETUP.md`

View File

@@ -1,187 +0,0 @@
# ✅ Установка завершена!
## Что было создано
### Backend (FastAPI)
- ✅ Система авторизации с JWT токенами
- ✅ Управление пользователями и ролями
- ✅ Контроль доступа к серверам
- ✅ API для всех операций с серверами
- ✅ WebSocket для консоли в реальном времени
- ✅ Файловый менеджер с редактором
- ✅ Мониторинг ресурсов
### Frontend (React)
- ✅ Форма входа и регистрации
- ✅ Управление пользователями (для админов)
- ✅ Панель управления серверами
- ✅ Консоль с логами
- ✅ Файловый менеджер
- ✅ Статистика ресурсов
- ✅ Настройки серверов
## Финальные шаги
### 1. Переименуйте файлы
**Backend:**
```
backend/main_new.py → backend/main.py
```
**Frontend:**
```
frontend/src/App_final.jsx → frontend/src/App.jsx
```
### 2. Установите зависимости
```bash
cd backend
pip install -r requirements.txt
```
Новые зависимости:
- `passlib[bcrypt]` - хеширование паролей
- `python-jose[cryptography]` - JWT токены
### 3. Запустите панель
**Автоматически:**
```bash
START_PANEL.bat
```
**Вручную:**
```bash
# Терминал 1
cd backend
python main.py
# Терминал 2
cd frontend
npm run dev
```
### 4. Первый вход
1. Откройте http://localhost:3000
2. Войдите:
- Логин: `admin`
- Пароль: `admin`
## Основные функции
### Для администраторов
1. **Создание серверов** - кнопка "+" в боковой панели
2. **Управление пользователями** - кнопка "Пользователи" в шапке
3. **Выдача доступа** - нажимайте на названия серверов в карточке пользователя
4. **Изменение ролей** - кнопка "Сделать админом/пользователем"
5. **Настройки серверов** - вкладка "Настройки"
### Для пользователей
1. **Просмотр своих серверов** - только те, к которым есть доступ
2. **Запуск/остановка** - кнопки на карточке сервера
3. **Консоль** - просмотр логов и отправка команд
4. **Файлы** - управление файлами сервера
5. **Статистика** - мониторинг ресурсов
## Безопасность
### ⚠️ ВАЖНО: Смените секретный ключ!
Откройте `backend/main.py` и измените:
```python
SECRET_KEY = "your-secret-key-change-this-in-production-12345"
```
На случайную строку минимум 32 символа.
### Смените пароль администратора
1. Создайте нового администратора
2. Войдите под ним
3. Удалите старого admin
## Доступ через сеть (Radmin VPN)
### На вашем компьютере:
1. Узнайте IP: `ipconfig` (ищите Radmin VPN, обычно 26.x.x.x)
2. Запустите панель
3. Откройте: http://localhost:3000
### На компьютере друга:
1. Откройте: http://ВАШ_IP:3000
2. Зарегистрируйтесь
3. Попросите вас выдать доступ к серверам
### Откройте порты (если не работает):
```powershell
netsh advfirewall firewall add rule name="MC Panel Backend" dir=in action=allow protocol=TCP localport=8000
netsh advfirewall firewall add rule name="MC Panel Frontend" dir=in action=allow protocol=TCP localport=3000
```
## Структура файлов
```
mc-panel/
├── START_PANEL.bat # Автозапуск
├── FINAL_STEPS.md # Инструкция
├── AUTH_SETUP.md # Руководство по авторизации
├── backend/
│ ├── main_new.py # Новый бэкенд (переименуйте в main.py)
│ ├── requirements.txt # Зависимости
│ ├── users.json # База пользователей (создастся автоматически)
│ └── servers/ # Папки серверов
└── frontend/
├── src/
│ ├── App_final.jsx # Новый App (переименуйте в App.jsx)
│ └── components/
│ ├── Auth.jsx # Форма входа
│ ├── Users.jsx # Управление пользователями
│ ├── Console.jsx # Консоль (обновлен)
│ ├── FileManager.jsx # Файлы (обновлен)
│ ├── Stats.jsx # Статистика (обновлен)
│ └── ...
└── package.json
```
## Troubleshooting
### Ошибка импорта passlib или jose
```bash
pip install passlib[bcrypt] python-jose[cryptography]
```
### Не могу войти
Удалите `backend/users.json` и перезапустите бэкенд.
### Пользователь не видит серверы
Админ должен выдать доступ в разделе "Пользователи".
### Токен истек
Выйдите и войдите заново.
## Документация
- `FINAL_STEPS.md` - пошаговая инструкция
- `AUTH_SETUP.md` - полное руководство по авторизации
- `QUICK_START.md` - быстрый старт
- `DEBUG_GUIDE.md` - отладка проблем
- `NETWORK_SETUP.md` - настройка сети
## Готово! 🎉
Теперь у вас есть полноценная панель управления Minecraft серверами с:
- ✅ Авторизацией и регистрацией
- ✅ Ролями и правами доступа
- ✅ Управлением пользователями
- ✅ Контролем доступа к серверам
- ✅ Консолью в реальном времени
- ✅ Файловым менеджером с редактором
- ✅ Мониторингом ресурсов
- ✅ Поддержкой удаленного доступа
**Приятного использования!** 🚀

View File

@@ -0,0 +1,707 @@
{
"info": {
"name": "MC Panel API",
"description": "API коллекция для MC Panel - системы управления Minecraft серверами",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"version": "1.0.0"
},
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{token}}",
"type": "string"
}
]
},
"variable": [
{
"key": "baseUrl",
"value": "http://localhost:8000",
"type": "string"
},
{
"key": "token",
"value": "",
"type": "string"
},
{
"key": "serverName",
"value": "survival",
"type": "string"
}
],
"item": [
{
"name": "Authentication",
"item": [
{
"name": "Register",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const response = pm.response.json();",
" pm.collectionVariables.set('token', response.access_token);",
" pm.environment.set('token', response.access_token);",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"testuser\",\n \"password\": \"testpass123\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/auth/register",
"host": ["{{baseUrl}}"],
"path": ["api", "auth", "register"]
}
}
},
{
"name": "Login",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const response = pm.response.json();",
" pm.collectionVariables.set('token', response.access_token);",
" pm.environment.set('token', response.access_token);",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"username\": \"Sofa12345\",\n \"password\": \"arkonsad123\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/auth/login",
"host": ["{{baseUrl}}"],
"path": ["api", "auth", "login"]
}
}
},
{
"name": "Get Current User",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/auth/me",
"host": ["{{baseUrl}}"],
"path": ["api", "auth", "me"]
}
}
}
]
},
{
"name": "Users",
"item": [
{
"name": "Get All Users",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/users",
"host": ["{{baseUrl}}"],
"path": ["api", "users"]
}
}
},
{
"name": "Update User Role",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"role\": \"support\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/users/username/role",
"host": ["{{baseUrl}}"],
"path": ["api", "users", "username", "role"]
}
}
},
{
"name": "Update User Servers",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"servers\": [\"survival\", \"creative\"]\n}"
},
"url": {
"raw": "{{baseUrl}}/api/users/username/servers",
"host": ["{{baseUrl}}"],
"path": ["api", "users", "username", "servers"]
}
}
},
{
"name": "Delete User",
"request": {
"method": "DELETE",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/users/username",
"host": ["{{baseUrl}}"],
"path": ["api", "users", "username"]
}
}
}
]
},
{
"name": "Servers",
"item": [
{
"name": "Get Servers",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers",
"host": ["{{baseUrl}}"],
"path": ["api", "servers"]
}
}
},
{
"name": "Create Server",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"survival\",\n \"displayName\": \"Survival Server\",\n \"startCommand\": \"java -Xmx2G -Xms1G -jar server.jar nogui\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/create",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "create"]
}
}
},
{
"name": "Get Server Config",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/config",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "config"]
}
}
},
{
"name": "Update Server Config",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"survival\",\n \"displayName\": \"Updated Survival Server\",\n \"startCommand\": \"java -Xmx4G -Xms2G -jar server.jar nogui\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/config",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "config"]
}
}
},
{
"name": "Start Server",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/start",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "start"]
}
}
},
{
"name": "Stop Server",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/stop",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "stop"]
}
}
},
{
"name": "Send Command",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"command\": \"say Hello from API!\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/command",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "command"]
}
}
},
{
"name": "Get Server Stats",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/stats",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "stats"]
}
}
},
{
"name": "Delete Server",
"request": {
"method": "DELETE",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}"]
}
}
}
]
},
{
"name": "Files",
"item": [
{
"name": "List Files",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files?path=",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files"],
"query": [
{
"key": "path",
"value": ""
}
]
}
}
},
{
"name": "Create File",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"type\": \"file\",\n \"name\": \"test.txt\",\n \"path\": \"\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/create",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "create"]
}
}
},
{
"name": "Create Folder",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"type\": \"folder\",\n \"name\": \"backup\",\n \"path\": \"\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/create",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "create"]
}
}
},
{
"name": "Get File Content",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/content?path=server.properties",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "content"],
"query": [
{
"key": "path",
"value": "server.properties"
}
]
}
}
},
{
"name": "Update File Content",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"content\": \"server-port=25565\\nmax-players=20\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/content?path=server.properties",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "content"],
"query": [
{
"key": "path",
"value": "server.properties"
}
]
}
}
},
{
"name": "Rename File",
"request": {
"method": "PUT",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/rename?old_path=test.txt&new_name=test_renamed.txt",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "rename"],
"query": [
{
"key": "old_path",
"value": "test.txt"
},
{
"key": "new_name",
"value": "test_renamed.txt"
}
]
}
}
},
{
"name": "Move File",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"source\": \"test.txt\",\n \"destination\": \"backup\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/move",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "move"]
}
}
},
{
"name": "Delete File",
"request": {
"method": "DELETE",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files?path=test.txt",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files"],
"query": [
{
"key": "path",
"value": "test.txt"
}
]
}
}
},
{
"name": "Download File",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/servers/{{serverName}}/files/download?path=server.jar",
"host": ["{{baseUrl}}"],
"path": ["api", "servers", "{{serverName}}", "files", "download"],
"query": [
{
"key": "path",
"value": "server.jar"
}
]
}
}
}
]
},
{
"name": "Tickets",
"item": [
{
"name": "Get Tickets",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/tickets",
"host": ["{{baseUrl}}"],
"path": ["api", "tickets"]
}
}
},
{
"name": "Create Ticket",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"title\": \"Проблема с сервером\",\n \"description\": \"Сервер не запускается после обновления\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/tickets/create",
"host": ["{{baseUrl}}"],
"path": ["api", "tickets", "create"]
}
}
},
{
"name": "Get Ticket",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/tickets/1",
"host": ["{{baseUrl}}"],
"path": ["api", "tickets", "1"]
}
}
},
{
"name": "Add Message",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"text\": \"Я попробовал перезапустить, но проблема осталась\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/tickets/1/message",
"host": ["{{baseUrl}}"],
"path": ["api", "tickets", "1", "message"]
}
}
},
{
"name": "Update Status",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"status\": \"in_progress\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/tickets/1/status",
"host": ["{{baseUrl}}"],
"path": ["api", "tickets", "1", "status"]
}
}
}
]
},
{
"name": "Profile",
"item": [
{
"name": "Get Profile Stats",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/profile/stats",
"host": ["{{baseUrl}}"],
"path": ["api", "profile", "stats"]
}
}
},
{
"name": "Get User Profile Stats",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/profile/stats/username",
"host": ["{{baseUrl}}"],
"path": ["api", "profile", "stats", "username"]
}
}
},
{
"name": "Update Username",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"new_username\": \"newusername\",\n \"password\": \"currentpassword\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/profile/username",
"host": ["{{baseUrl}}"],
"path": ["api", "profile", "username"]
}
}
},
{
"name": "Update Password",
"request": {
"method": "PUT",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"old_password\": \"oldpass123\",\n \"new_password\": \"newpass456\"\n}"
},
"url": {
"raw": "{{baseUrl}}/api/profile/password",
"host": ["{{baseUrl}}"],
"path": ["api", "profile", "password"]
}
}
}
]
},
{
"name": "OpenID Connect",
"item": [
{
"name": "Get OIDC Providers",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/api/auth/oidc/providers",
"host": ["{{baseUrl}}"],
"path": ["api", "auth", "oidc", "providers"]
}
}
}
]
}
]
}

View File

@@ -1,114 +0,0 @@
# Настройка доступа через сеть
## Быстрый старт для Radmin VPN
### 1. Узнайте ваш IP адрес в Radmin VPN
Откройте командную строку и выполните:
```bash
ipconfig
```
Найдите адаптер Radmin VPN, IP обычно выглядит как `26.x.x.x`
### 2. Запустите бэкенд
```bash
cd backend
python main.py
```
Бэкенд автоматически слушает на всех сетевых интерфейсах (0.0.0.0:8000)
### 3. Запустите фронтенд
```bash
cd frontend
npm run dev
```
Теперь фронтенд запускается с флагом `--host` по умолчанию.
### 4. Откройте в браузере
**На вашем компьютере:**
- http://localhost:3000
**На компьютере друга:**
- http://ВАШ_RADMIN_IP:3000
- Например: http://26.123.45.67:3000
## Автоматическое определение API
Фронтенд автоматически определяет правильный API URL:
- Если открыто через `localhost` → подключится к `http://localhost:8000`
- Если открыто через IP → подключится к `http://ВАШ_IP:8000`
## Ручная настройка (если автоматика не работает)
Создайте файл `frontend/.env.local`:
```env
VITE_API_URL=http://26.123.45.67:8000
```
Замените `26.123.45.67` на ваш реальный IP в Radmin VPN.
Перезапустите фронтенд:
```bash
npm run dev
```
## Проверка подключения
1. Откройте консоль браузера (F12)
2. Проверьте, нет ли ошибок подключения
3. Убедитесь, что запросы идут на правильный IP адрес
## Возможные проблемы
### Серверы не загружаются
**Причина:** Фронтенд не может подключиться к бэкенду
**Решение:**
1. Убедитесь, что бэкенд запущен
2. Проверьте, что используется правильный IP
3. Проверьте брандмауэр Windows (порты 8000 и 3000 должны быть открыты)
### Ошибка при создании сервера
**Причина:** CORS или неправильный API URL
**Решение:**
1. Перезапустите бэкенд
2. Очистите кэш браузера (Ctrl+Shift+Delete)
3. Проверьте консоль браузера на ошибки
### WebSocket не подключается (консоль не работает)
**Причина:** WebSocket использует неправильный адрес
**Решение:**
1. Проверьте файл `frontend/src/config.js`
2. WebSocket должен использовать `ws://` вместо `http://`
3. Перезапустите фронтенд
## Открытие портов в брандмауэре Windows
```powershell
# Откройте PowerShell от имени администратора
# Порт для бэкенда
netsh advfirewall firewall add rule name="MC Panel Backend" dir=in action=allow protocol=TCP localport=8000
# Порт для фронтенда
netsh advfirewall firewall add rule name="MC Panel Frontend" dir=in action=allow protocol=TCP localport=3000
```
## Проверка работы
На компьютере друга откройте:
- http://ВАШ_IP:3000
Вы должны увидеть панель управления, и она должна показывать ваши серверы.

View File

@@ -1,150 +0,0 @@
# ✅ Изменения OpenID Connect - ZITADEL
## Что было сделано
### 1. Backend (Python/FastAPI)
#### `backend/main.py`
- ✅ Упрощена инициализация OAuth для работы только с ZITADEL
- ✅ Улучшена обработка callback с более детальным логированием
- ✅ Добавлена проверка наличия userinfo в токене
- ✅ Улучшена обработка ошибок OAuth
-**Исправлен импорт:** `starlette_client` вместо `fastapi_client` (FastAPI основан на Starlette)
#### `backend/oidc_config.py`
- ✅ Оставлен только ZITADEL провайдер
- ✅ Настройка через переменные окружения:
- `ZITADEL_ISSUER` - URL инстанса ZITADEL
- `ZITADEL_CLIENT_ID` - ID приложения
- `ZITADEL_CLIENT_SECRET` - Секретный ключ
#### `backend/.env.example`
- ✅ Обновлён с настройками ZITADEL
- ✅ Удалены старые провайдеры (Google, Microsoft, Discord, GitHub)
### 2. Frontend (React)
#### `frontend/src/components/Auth.jsx`
- ✅ Динамическая загрузка OIDC провайдеров
- ✅ Кнопка "Войти через ZITADEL" с иконкой 🔐
- ✅ Автоматическое скрытие если ZITADEL не настроен
#### `frontend/src/App.jsx`
- ✅ Обработка callback от ZITADEL в useEffect
- ✅ Автоматическое сохранение токена
- ✅ Очистка URL после входа
#### Удалено
-`frontend/src/components/AuthCallback.jsx` - не используется
### 3. Документация
#### `OPENID_CONNECT_SETUP.md`
- ✅ Полностью переписана для ZITADEL
- ✅ Пошаговая инструкция по настройке
- ✅ Примеры конфигурации
- ✅ Troubleshooting
#### `ZITADEL_QUICK_START.md` (новый)
- ✅ Краткая инструкция по быстрому старту
- ✅ 4 простых шага для настройки
- ✅ Решение типичных проблем
#### `OIDC_CHANGES.md` (этот файл)
- ✅ Резюме всех изменений
## Как использовать
### Для разработчика
1. Создайте приложение в ZITADEL
2. Скопируйте `backend/.env.example` в `backend/.env`
3. Заполните настройки ZITADEL
4. Запустите backend и frontend
5. Проверьте кнопку "Войти через ZITADEL"
### Для пользователя
1. Откройте страницу входа
2. Нажмите "Войти через ZITADEL"
3. Войдите в ZITADEL
4. Автоматически попадёте в панель
## Технические детали
### Поток аутентификации
```
1. Пользователь → Кнопка ZITADEL
2. Frontend → GET /api/auth/oidc/zitadel/login
3. Backend → Redirect на ZITADEL
4. ZITADEL → Пользователь вводит логин/пароль
5. ZITADEL → Redirect на /api/auth/oidc/zitadel/callback?code=...
6. Backend → Обмен code на access_token
7. Backend → Получение userinfo
8. Backend → Создание/обновление пользователя
9. Backend → Генерация JWT токена
10. Backend → Redirect на frontend?token=...&username=...
11. Frontend → Сохранение токена в localStorage
12. Frontend → Автоматический вход
```
### Структура пользователя OIDC
```json
{
"username": "john_doe",
"password": "",
"role": "user",
"servers": [],
"oidc_id": "zitadel:123456789012345678",
"email": "john@example.com",
"name": "John Doe",
"picture": "https://avatar.url",
"provider": "zitadel",
"created_at": "2026-01-15T12:00:00"
}
```
### API Endpoints
- `GET /api/auth/oidc/providers` - Список доступных провайдеров
- `GET /api/auth/oidc/zitadel/login` - Начало OAuth flow
- `GET /api/auth/oidc/zitadel/callback` - Обработка callback
## Зависимости
### Backend
- `authlib==1.3.0` - OAuth2/OIDC клиент
- `httpx==0.26.0` - HTTP клиент
### Frontend
- `axios` - HTTP запросы (уже установлен)
## Безопасность
- ✅ PKCE (Proof Key for Code Exchange)
- ✅ State parameter для CSRF защиты
- ✅ Проверка redirect_uri
- ✅ JWT токены с истечением
- ✅ Хранение токенов в localStorage
## Что дальше?
### Возможные улучшения
- [ ] Добавить refresh token
- [ ] Добавить logout через ZITADEL
- [ ] Добавить отображение аватара пользователя
- [ ] Добавить связывание OIDC аккаунта с существующим
- [ ] Добавить управление сессиями
### Для продакшена
- [ ] Использовать HTTPS
- [ ] Настроить CORS правильно
- [ ] Добавить rate limiting
- [ ] Настроить логирование
- [ ] Добавить мониторинг
## Готово! ✅
OpenID Connect с ZITADEL полностью настроен и готов к использованию!

View File

@@ -1,235 +0,0 @@
# 🔐 Настройка OpenID Connect
## Что добавлено
### Поддержка OpenID Connect провайдеров
MC Panel теперь поддерживает вход через ZITADEL:
- 🔐 **ZITADEL** - Современная платформа управления идентификацией и доступом
## 📋 Требования
### Установка зависимостей
```bash
cd backend
pip install authlib==1.3.0 httpx==0.26.0
```
Или установите все зависимости:
```bash
cd backend
pip install -r requirements.txt
```
Новые зависимости:
- `authlib==1.3.0` - OAuth2/OpenID Connect клиент
- `httpx==0.26.0` - HTTP клиент для API запросов
**Важно:** В authlib 1.3.0 используется `authlib.integrations.starlette_client` (FastAPI основан на Starlette)
## ⚙️ Настройка ZITADEL
### 1. Создание проекта в ZITADEL
#### Шаг 1: Регистрация в ZITADEL
1. Перейдите на [ZITADEL Cloud](https://zitadel.cloud/) или используйте свой self-hosted инстанс
2. Создайте новый проект или используйте существующий
3. Запомните URL вашего инстанса (например: `https://your-instance.zitadel.cloud`)
#### Шаг 2: Создание приложения
1. В проекте нажмите "New Application"
2. Выберите тип: **Web Application**
3. Выберите метод аутентификации: **Code (with PKCE)**
4. Укажите название: `MC Panel`
#### Шаг 3: Настройка Redirect URIs
Добавьте следующие redirect URIs:
- Для разработки: `http://localhost:8000/api/auth/oidc/zitadel/callback`
- Для продакшена: `https://your-domain.com/api/auth/oidc/zitadel/callback`
#### Шаг 4: Получение учётных данных
После создания приложения вы получите:
- **Client ID** - идентификатор приложения
- **Client Secret** - секретный ключ (сохраните его!)
- **Issuer URL** - URL вашего ZITADEL инстанса
### 2. Настройка в .env
Создайте или отредактируйте файл `backend/.env`:
```bash
# Базовые настройки
SECRET_KEY=your-secret-key-here-change-this-in-production
BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# ZITADEL Configuration
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=your-client-id@your-project
ZITADEL_CLIENT_SECRET=your-client-secret
```
### 3. Пример настройки
```bash
# Пример для ZITADEL Cloud
ZITADEL_ISSUER=https://mc-panel-abc123.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@mc-panel
ZITADEL_CLIENT_SECRET=abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGH
```
## 🚀 Запуск
### 1. Создайте .env файл
```bash
cd backend
cp .env.example .env
```
### 2. Настройте ZITADEL
Отредактируйте `.env` файл:
```bash
# Базовые настройки
SECRET_KEY=your-secret-key-here-change-this-in-production
BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# ZITADEL
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=your-client-id@your-project
ZITADEL_CLIENT_SECRET=your-client-secret
```
### 3. Запустите сервер
```bash
cd backend
python main.py
```
### 4. Запустите фронтенд
```bash
cd frontend
npm run dev
```
## 🎨 Интерфейс
### Страница входа
- Обычная форма входа (логин/пароль)
- Разделитель "Или войдите через"
- Кнопка ZITADEL с иконкой 🔐 и фиолетовым цветом
- Автоматическое скрытие кнопки если ZITADEL не настроен
## 🔧 Как это работает
### 1. Пользователь нажимает кнопку провайдера
```
GET /api/auth/oidc/{provider}/login
```
### 2. Перенаправление на провайдера
Пользователь перенаправляется на страницу авторизации провайдера
### 3. Callback от провайдера
```
GET /api/auth/oidc/{provider}/callback?code=...
```
### 4. Получение токена и данных пользователя
- Обмен code на access_token
- Получение информации о пользователе
- Создание или обновление пользователя в системе
### 5. Создание JWT токена
- Генерация JWT токена для пользователя
- Перенаправление на фронтенд с токеном
### 6. Автоматический вход
- Фронтенд получает токен из URL
- Сохраняет токен в localStorage
- Пользователь автоматически входит в систему
## 👥 Управление пользователями
### Автоматическое создание пользователей
При первом входе через OpenID Connect:
- Создаётся новый пользователь
- Роль: "user" (обычный пользователь)
- Username генерируется из email или имени
- Сохраняется связь с провайдером
### Данные пользователя
```json
{
"username": "john_doe",
"password": "",
"role": "user",
"servers": [],
"oidc_id": "zitadel:123456789012345678",
"email": "john@example.com",
"name": "John Doe",
"picture": "https://avatar.url",
"provider": "zitadel",
"created_at": "2026-01-15T12:00:00"
}
```
### Повторные входы
- Пользователь находится по `oidc_id`
- Обновляется email, имя и аватар
- Роль и серверы сохраняются
## 🔐 Безопасность
### Проверки
- Проверка state параметра (CSRF защита)
- Проверка redirect_uri
- Валидация токенов от провайдеров
- Проверка подписи JWT токенов
### Рекомендации
- Используйте HTTPS в продакшене
- Регулярно обновляйте client secrets
- Ограничьте redirect URIs
- Мониторьте подозрительную активность
## 🚨 Troubleshooting
### Ошибка "Provider not found"
- Проверьте настройки в .env файле
- Убедитесь что CLIENT_ID указан
- Перезапустите сервер
### Ошибка "Invalid redirect_uri"
- Проверьте redirect URI в настройках провайдера
- Должен точно совпадать с `BASE_URL/api/auth/oidc/{provider}/callback`
### Ошибка "Invalid client"
- Проверьте CLIENT_ID и CLIENT_SECRET
- Убедитесь что приложение активно у провайдера
### Пользователь не создаётся
- Проверьте логи сервера
- Убедитесь что провайдер возвращает email
- Проверьте права на запись в users.json
## ✅ Готово!
OpenID Connect с ZITADEL настроен и готов к использованию. Пользователи могут входить через:
- Обычную форму (логин/пароль)
- ZITADEL (OpenID Connect)
### Тестирование
1. Настройте ZITADEL в .env файле
2. Перезапустите сервер
3. Откройте страницу входа
4. Увидите кнопку "Войти через ZITADEL"
5. Нажмите на кнопку и войдите через ZITADEL
### Преимущества ZITADEL
- ✅ Полная поддержка OpenID Connect
- ✅ Современный интерфейс управления
- ✅ Поддержка многофакторной аутентификации
- ✅ Self-hosted или Cloud решение
- ✅ Бесплатный план для небольших проектов
**Удобного использования! 🔐**

View File

@@ -1,228 +0,0 @@
# 👤 Система личного кабинета
## Что добавлено
### ✅ Новые возможности
1. **Личный кабинет** - полноценная система управления профилем
2. **Три вкладки**:
- 📊 **Обзор** - статистика и информация о профиле
- 👤 **Имя пользователя** - изменение имени пользователя
- 🔒 **Пароль** - изменение пароля
3. **Кнопка "Личный кабинет"** в header рядом с кнопкой "Тикеты"
### 📊 Вкладка "Обзор"
#### Информация о пользователе
- Имя пользователя
- Роль (Администратор, Тех. поддержка, Пользователь)
- Цветной бейдж роли
#### Статистика
- **Серверы**:
- Всего серверов
- Мои серверы (владелец)
- Доступные серверы
- **Тикеты**:
- Всего тикетов
- На рассмотрении
- В работе
- Закрыто
- **Роль**:
- Название роли
- Описание прав
#### Список серверов
- Отображение всех серверов пользователя
- Название и ID сервера
- Красивые карточки
### 👤 Вкладка "Имя пользователя"
#### Возможности
- Просмотр текущего имени
- Ввод нового имени (минимум 3 символа)
- Подтверждение паролем
- Автоматический перелогин с новым именем
#### Безопасность
- Проверка уникальности имени
- Проверка пароля
- Обновление владельцев серверов
- Обновление доступов к серверам
- Новый JWT токен
### 🔒 Вкладка "Пароль"
#### Возможности
- Ввод текущего пароля
- Ввод нового пароля (минимум 6 символов)
- Подтверждение нового пароля
- Показ/скрытие паролей
#### Безопасность
- Проверка текущего пароля
- Проверка совпадения новых паролей
- Хеширование пароля (bcrypt)
## 🚀 Как использовать
### Открытие личного кабинета
1. Нажмите кнопку "Личный кабинет" в header
2. Откроется страница с тремя вкладками
### Просмотр статистики
1. Откройте вкладку "Обзор"
2. Посмотрите информацию о профиле
3. Посмотрите статистику по серверам и тикетам
4. Посмотрите список своих серверов
### Изменение имени пользователя
1. Откройте вкладку "Имя пользователя"
2. Введите новое имя (минимум 3 символа)
3. Введите текущий пароль для подтверждения
4. Нажмите "Изменить имя пользователя"
5. Вы будете автоматически перелогинены
⚠️ **Важно**: После изменения имени:
- Обновятся все серверы, где вы владелец
- Обновятся все доступы к серверам
- Вы получите новый токен авторизации
### Изменение пароля
1. Откройте вкладку "Пароль"
2. Введите текущий пароль
3. Введите новый пароль (минимум 6 символов)
4. Подтвердите новый пароль
5. Нажмите "Изменить пароль"
⚠️ **Важно**: После изменения пароля используйте новый пароль для входа.
## 📁 Новые файлы
### Backend
- Добавлены endpoints в `backend/main.py`:
- `PUT /api/profile/username` - изменить имя пользователя
- `PUT /api/profile/password` - изменить пароль
- `GET /api/profile/stats` - получить статистику профиля
### Frontend
- `frontend/src/components/Profile.jsx` - компонент личного кабинета
## 🎨 Интерфейс
### Вкладки
- Современный дизайн с вкладками
- Плавные переходы между вкладками
- Адаптивный дизайн
### Карточки статистики
- Цветные иконки
- Числовые показатели
- Детальная информация
### Формы
- Валидация полей
- Показ/скрытие паролей
- Предупреждения о последствиях
- Кнопки с индикацией загрузки
## 🔧 Технические детали
### API Endpoints
#### PUT /api/profile/username
Изменить имя пользователя
**Request:**
```json
{
"new_username": "NewUsername",
"password": "current_password"
}
```
**Response:**
```json
{
"message": "Имя пользователя изменено",
"access_token": "new_jwt_token",
"username": "NewUsername"
}
```
#### PUT /api/profile/password
Изменить пароль
**Request:**
```json
{
"old_password": "old_password",
"new_password": "new_password"
}
```
**Response:**
```json
{
"message": "Пароль изменён"
}
```
#### GET /api/profile/stats
Получить статистику профиля
**Response:**
```json
{
"username": "username",
"role": "user",
"owned_servers": [
{
"name": "server1",
"displayName": "My Server"
}
],
"accessible_servers": [],
"tickets": {
"total": 5,
"pending": 2,
"in_progress": 1,
"closed": 2
},
"total_servers": 1
}
```
### Безопасность
#### Изменение имени пользователя
1. Проверка пароля
2. Проверка уникальности нового имени
3. Обновление владельцев серверов
4. Обновление доступов к серверам
5. Создание нового JWT токена
#### Изменение пароля
1. Проверка текущего пароля
2. Проверка длины нового пароля (минимум 6 символов)
3. Хеширование нового пароля (bcrypt)
## ✅ Готово!
Система личного кабинета полностью интегрирована в MC Panel. Пользователи могут:
- Просматривать статистику своего профиля
- Изменять имя пользователя
- Изменять пароль
- Видеть свои серверы и тикеты
### Доступ к личному кабинету
Кнопка "Личный кабинет" доступна всем пользователям в header рядом с кнопкой "Тикеты".
### Учётные данные по умолчанию
- **Логин**: Sofa12345
- **Пароль**: arkonsad123
- **Роль**: admin

View File

@@ -1,201 +0,0 @@
# 🚀 Быстрый старт MC Panel
## Первый запуск
### Вариант 1: Автоматический запуск (Windows)
Просто запустите файл:
```
START_PANEL.bat
```
Откроются два окна:
- **MC Panel Backend** - бэкенд сервер
- **MC Panel Frontend** - фронтенд сервер
Подождите 10-15 секунд и откройте в браузере:
```
http://localhost:3000
```
### Вариант 2: Ручной запуск
**Терминал 1 - Бэкенд:**
```bash
cd backend
python main.py
```
**Терминал 2 - Фронтенд:**
```bash
cd frontend
npm run dev
```
## Создание первого сервера
1. Нажмите кнопку **"+"** в левой панели
2. Заполните форму:
- **Имя папки**: `my_server` (только латиница)
- **Отображаемое имя**: `Мой сервер`
- **Команда запуска**: `java -Xmx2G -Xms1G -jar server.jar nogui`
3. Нажмите **"Создать"**
## Загрузка файлов сервера
1. Выберите созданный сервер в списке
2. Перейдите на вкладку **"Файлы"**
3. Нажмите **"Загрузить"**
4. Выберите `server.jar` (скачайте с официального сайта Minecraft)
5. Если нужно, создайте файл `eula.txt`:
- Нажмите **"Загрузить"**
- Создайте текстовый файл с содержимым: `eula=true`
- Загрузите его
## Запуск сервера
1. Нажмите кнопку **"Старт"** на карточке сервера
2. Перейдите на вкладку **"Консоль"** чтобы видеть логи
3. Дождитесь сообщения `Done!` в консоли
4. Сервер готов к подключению!
## Управление сервером
### Консоль
- Просмотр логов в реальном времени
- Отправка команд серверу
- Примеры команд: `list`, `say Hello`, `stop`
### Файлы
- Просмотр и редактирование конфигов
- Загрузка плагинов/модов
- Скачивание файлов
- Переименование и удаление
### Статистика
- Использование CPU
- Потребление ОЗУ
- Размер на диске
- Статус сервера
### Настройки
- Изменение отображаемого имени
- Настройка команды запуска
- Удаление сервера
## Доступ через сеть (Radmin VPN)
### На вашем компьютере:
1. Узнайте ваш IP в Radmin VPN:
```bash
ipconfig
```
Ищите адаптер Radmin VPN (обычно `26.x.x.x`)
2. Запустите панель как обычно
3. Откройте в браузере:
```
http://localhost:3000
```
### На компьютере друга:
1. Откройте в браузере:
```
http://ВАШ_RADMIN_IP:3000
```
Например: `http://26.62.117.104:3000`
2. Панель автоматически подключится к вашему бэкенду
### Если не работает:
Откройте порты в брандмауэре Windows (от имени администратора):
```powershell
netsh advfirewall firewall add rule name="MC Panel Backend" dir=in action=allow protocol=TCP localport=8000
netsh advfirewall firewall add rule name="MC Panel Frontend" dir=in action=allow protocol=TCP localport=3000
```
## Типичные проблемы
### Java не найдена
**Ошибка:** `'java' is not recognized...`
**Решение:**
1. Установите Java 17+: https://adoptium.net/
2. Перезапустите терминал
3. Проверьте: `java -version`
### Сервер не запускается
**Причины:**
- Отсутствует `server.jar`
- Не принят EULA
- Неправильная команда запуска
**Решение:**
1. Проверьте наличие `server.jar` в файлах
2. Создайте `eula.txt` с содержимым `eula=true`
3. Проверьте команду запуска в настройках
### Порт уже занят
**Ошибка:** `Address already in use`
**Решение:**
```bash
# Найти процесс на порту 8000
netstat -ano | findstr :8000
# Убить процесс (замените PID)
taskkill /F /PID <PID>
```
### Не видно файлов/настроек
**Решение:**
1. Откройте консоль браузера (F12)
2. Проверьте вкладку Network на ошибки
3. Обновите страницу (F5)
4. Перезапустите бэкенд
## Полезные ссылки
- **Скачать Minecraft Server**: https://www.minecraft.net/en-us/download/server
- **Документация Minecraft**: https://minecraft.fandom.com/wiki/Server
- **Java Download**: https://adoptium.net/
- **Radmin VPN**: https://www.radmin-vpn.com/
## Команды Minecraft
Полезные команды для консоли:
```
list # Список игроков
say <message> # Сообщение всем
kick <player> # Кикнуть игрока
ban <player> # Забанить игрока
op <player> # Дать права оператора
deop <player> # Забрать права оператора
whitelist add <player> # Добавить в белый список
stop # Остановить сервер
```
## Конфигурационные файлы
Основные файлы для редактирования:
- **server.properties** - основные настройки сервера
- **eula.txt** - принятие лицензии
- **ops.json** - список операторов
- **whitelist.json** - белый список игроков
- **banned-players.json** - забаненные игроки
Редактируйте их через вкладку "Файлы" в панели!
---
**Готово!** Теперь у вас есть полноценная панель управления Minecraft серверами! 🎮

276
README.md
View File

@@ -1,128 +1,192 @@
# MC Panel - Панель управления Minecraft серверами
Панель управления для Minecraft серверов с FastAPI бэкендом и React фронтендом.
**Версия:** 1.0.0
**Дата:** 15 января 2026
## Возможности
---
- Создание новых серверов
- 🎮 Запуск и остановка серверов
- 💻 Консоль с отправкой команд в реальном времени
- 📁 Менеджер файлов:
- Загрузка и скачивание файлов
- Просмотр содержимого файлов
- Редактирование текстовых файлов
- Переименование файлов и папок
- Удаление файлов и папок
- 📊 Мониторинг ресурсов (CPU, ОЗУ, диск)
- ⚙️ Настройки сервера (название, команда запуска)
- 🗑️ Удаление серверов
- 🔄 Автообновление статистики
## 📚 Документация
## Установка
### 📖 [ДОКУМЕНТАЦИЯ.md](ДОКУМЕНТАЦИЯ.md)
**Полная документация проекта**
### Бэкенд
Содержит всю информацию о проекте:
- 🚀 Быстрый старт
- ⚙️ Установка и настройка
- 🎮 Функциональность
- 🔔 Система уведомлений
- 🎨 Дизайн и темы
- 📁 Файловый менеджер
- 🎫 Система тикетов
- 👤 Личный кабинет
- 🔐 OpenID Connect
- 👥 Роли пользователей
- 🔒 Безопасность
- 🔧 Troubleshooting
**Начните отсюда!** 👈
---
### 🌐 [API.md](API.md)
**Документация API**
Полное описание REST API:
- 📋 Все эндпоинты (37 шт.)
- 🔐 Аутентификация
- 👥 Управление пользователями
- 🖥️ Управление серверами
- 📁 Управление файлами
- 🎫 Тикеты
- 💡 Примеры интеграции (Python, JavaScript, cURL)
- 📦 Postman коллекция
**Для разработчиков!** 👨‍💻
---
### 📦 [MC_Panel_API.postman_collection.json](MC_Panel_API.postman_collection.json)
**Postman коллекция**
Готовая коллекция для тестирования API:
- 40+ готовых запросов
- Автоматическое сохранение токена
- Переменные окружения
- Примеры тел запросов
**Импортируйте в Postman!** 📮
---
## 🚀 Быстрый старт
### 1. Установка
**Backend:**
```bash
cd backend
pip install -r requirements.txt
python main.py
```
Сервер запустится на http://0.0.0.0:8000
### Фронтенд
**Frontend:**
```bash
cd frontend
npm install
npm run dev -- --host
```
Приложение откроется на http://localhost:3000
## Доступ через сеть (Radmin VPN, Hamachi и т.д.)
### Вариант 1: Автоматическое определение (рекомендуется)
Фронтенд автоматически определит IP адрес и подключится к бэкенду.
1. Запустите бэкенд (он слушает на всех интерфейсах)
2. Запустите фронтенд с флагом `--host`:
```bash
npm run dev -- --host
```
3. Откройте в браузере: `http://ВАШ_IP:3000`
- Например: `http://26.123.45.67:3000` (Radmin VPN IP)
### Вариант 2: Ручная настройка
Создайте файл `frontend/.env.local`:
```
VITE_API_URL=http://26.123.45.67:8000
```
Замените `26.123.45.67` на ваш IP адрес в Radmin VPN.
### Проверка IP адреса
Windows:
```bash
ipconfig
```
Ищите адрес адаптера Radmin VPN (обычно начинается с 26.x.x.x)
## Структура
```
backend/
main.py # FastAPI сервер
requirements.txt # Зависимости Python
servers/ # Папка с серверами Minecraft
frontend/
src/
components/
Console.jsx # Компонент консоли
FileManager.jsx # Менеджер файлов
Stats.jsx # Статистика
App.jsx # Главный компонент
main.jsx # Точка входа
package.json # Зависимости Node.js
```
## Использование
### Быстрый старт
**Windows:**
```bash
START_PANEL.bat
```
**Вручную:**
```bash
# Терминал 1 - Бэкенд
cd backend
python main.py
# Терминал 2 - Фронтенд
cd frontend
npm run dev
```
Откройте в браузере: http://localhost:3000
### 2. Первый вход
### Создание сервера
1. Откройте `http://localhost:3000`
2. Зарегистрируйтесь (первый пользователь = admin)
3. Создайте сервер
4. Загрузите `server.jar`
5. Запустите сервер!
1. Нажмите кнопку "+" для создания нового сервера
2. Укажите имя, отображаемое название и команду запуска
3. Загрузите файлы сервера (server.jar и т.д.) через менеджер файлов
4. Создайте файл `eula.txt` с содержимым `eula=true`
5. Запустите сервер и управляйте им через вкладки:
- **Консоль** - просмотр логов и отправка команд
- **Файлы** - управление файлами сервера
- **Статистика** - мониторинг ресурсов
- **Настройки** - изменение параметров сервера
**Учетные данные по умолчанию:**
- Логин: `Root`
- Пароль: `Admin`
Подробнее: см. `QUICK_START.md`
---
## ✨ Основные возможности
- 🖥️ **Управление серверами** - запуск, остановка, мониторинг
- 📁 **Файловый менеджер** - полное управление файлами
- 💬 **Консоль** - команды и логи в реальном времени
- 📊 **Статистика** - CPU, RAM, диск
- 🎫 **Тикеты** - система поддержки
- 👥 **Пользователи** - роли и права доступа
- 🔐 **OpenID Connect** - интеграция с ZITADEL
- 🎨 **6 тем** - включая современную темную
- 🔔 **Уведомления** - о всех событиях
- 👤 **Личный кабинет** - профиль и статистика
---
## 🛠️ Технологии
**Backend:**
- FastAPI (Python)
- JWT аутентификация
- WebSocket
- Authlib (OpenID Connect)
**Frontend:**
- React 18
- Tailwind CSS
- Axios
- Lucide React
---
## 📁 Структура проекта
```
mc-panel/
├── backend/
│ ├── main.py # FastAPI приложение
│ ├── oidc_config.py # OpenID Connect
│ ├── requirements.txt # Зависимости
│ └── servers/ # Папка серверов
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Главный компонент
│ │ ├── components/ # React компоненты
│ │ └── themes.js # Темы
│ └── package.json # npm зависимости
├── ДОКУМЕНТАЦИЯ.md # Документация проекта
├── API.md # API документация
├── MC_Panel_API.postman_collection.json # Postman
└── README.md # Этот файл
```
---
## 🔒 Безопасность
- JWT токены (7 дней)
- Bcrypt хеширование паролей
- Проверка прав доступа
- Защита файловой системы
- OpenID Connect поддержка
**Для production:**
1. Измените `SECRET_KEY` в `backend/main.py`
2. Используйте HTTPS
3. Настройте CORS
4. Используйте базу данных вместо JSON
---
## 📞 Поддержка
- **Документация:** [ДОКУМЕНТАЦИЯ.md](ДОКУМЕНТАЦИЯ.md)
- **API:** [API.md](API.md)
- **Тикеты:** Используйте систему тикетов в панели
- **GitHub:** [Ссылка на репозиторий]
---
## 📝 Лицензия
MIT License - свободное использование
---
## 🙏 Благодарности
Спасибо за использование MC Panel!
Если у вас есть вопросы или предложения:
1. Прочитайте документацию
2. Проверьте API документацию
3. Создайте тикет в системе
---
**Версия:** 1.0.0
**Дата:** 15 января 2026
**Приятного использования!** 🎮

View File

@@ -1,138 +0,0 @@
# MC Panel - Финальная версия с авторизацией
## ✅ Что готово
Полноценная панель управления Minecraft серверами с системой авторизации и управлением пользователями.
## 🚀 Быстрый старт
### 1. Переименуйте файлы
**ВАЖНО! Сделайте это вручную в проводнике Windows:**
1. `backend/main_new.py``backend/main.py`
2. `frontend/src/App_final.jsx``frontend/src/App.jsx`
### 2. Установите зависимости
```bash
cd backend
pip install -r requirements.txt
```
### 3. Запустите
```bash
START_PANEL.bat
```
Или вручную:
```bash
# Терминал 1
cd backend
python main.py
# Терминал 2
cd frontend
npm run dev
```
### 4. Войдите
Откройте http://localhost:3000
- Логин: `admin`
- Пароль: `admin`
## 📚 Документация
- **INSTALLATION_COMPLETE.md** - полная инструкция по установке
- **AUTH_SETUP.md** - руководство по авторизации
- **FINAL_STEPS.md** - пошаговые инструкции
- **QUICK_START.md** - быстрый старт для новичков
- **DEBUG_GUIDE.md** - решение проблем
- **NETWORK_SETUP.md** - настройка удаленного доступа
## 🎯 Основные функции
### Авторизация
- Вход и регистрация
- JWT токены
- Автоматический выход при истечении
### Роли
- **Администратор** - полный доступ
- **Пользователь** - доступ к назначенным серверам
### Управление пользователями
- Выдача/отзыв доступа к серверам
- Изменение ролей
- Удаление пользователей
### Управление серверами
- Создание/удаление серверов
- Запуск/остановка
- Консоль в реальном времени
- Файловый менеджер с редактором
- Мониторинг ресурсов
- Настройки
## ⚠️ Важно
### Смените секретный ключ!
Откройте `backend/main.py` и измените:
```python
SECRET_KEY = "your-secret-key-change-this-in-production-12345"
```
### Смените пароль admin
1. Создайте нового администратора
2. Войдите под ним
3. Удалите старого admin
## 🌐 Удаленный доступ
1. Узнайте IP: `ipconfig` (Radmin VPN обычно 26.x.x.x)
2. Друг открывает: `http://ВАШ_IP:3000`
3. Друг регистрируется
4. Вы выдаете ему доступ к серверам
## 📁 Структура
```
mc-panel/
├── backend/
│ ├── main_new.py → main.py # Переименуйте!
│ ├── requirements.txt
│ ├── users.json # Создастся автоматически
│ └── servers/
└── frontend/
├── src/
│ ├── App_final.jsx → App.jsx # Переименуйте!
│ └── components/
│ ├── Auth.jsx
│ ├── Users.jsx
│ └── ...
└── package.json
```
## 🆘 Помощь
### Не могу войти
Удалите `backend/users.json` и перезапустите бэкенд.
### Ошибка импорта
```bash
pip install passlib[bcrypt] python-jose[cryptography]
```
### Пользователь не видит серверы
Админ должен выдать доступ в разделе "Пользователи".
## 🎉 Готово!
Теперь у вас полноценная панель с авторизацией!
Подробнее см. **INSTALLATION_COMPLETE.md**

View File

@@ -1,228 +0,0 @@
# 🔐 OpenID Connect с ZITADEL - Документация
## 🎯 Статус: ✅ Готово к использованию
OpenID Connect с ZITADEL полностью интегрирован в MC Panel!
## 📚 Документация
### 🚀 Начало работы
Выберите подходящий файл в зависимости от ваших потребностей:
| Файл | Для кого | Время чтения |
|------|----------|--------------|
| **[СЛЕДУЮЩИЕАГИ.md](СЛЕДУЮЩИЕАГИ.md)** | Все | 2 минуты |
| **[РЕЗЮМЕ.md](РЕЗЮМЕ.md)** | Все | 1 минута |
| **[ZITADEL_QUICK_START.md](ZITADEL_QUICK_START.md)** | Пользователи | 3 минуты |
| **[OPENID_CONNECT_SETUP.md](OPENID_CONNECT_SETUP.md)** | Администраторы | 10 минут |
| **[OIDC_CHANGES.md](OIDC_CHANGES.md)** | Разработчики | 5 минут |
| **[СХЕМА_РАБОТЫ.md](СХЕМА_РАБОТЫ.md)** | Разработчики | 5 минут |
| **[ИТОГИ_РАБОТЫ.md](ИТОГИ_РАБОТЫ.md)** | Менеджеры | 5 минут |
| **[ГОТОВО_OIDC.md](ГОТОВО_OIDC.md)** | Все | 3 минуты |
## ⚡ Быстрый старт
### 0. Установите зависимости (если ещё не установлены)
```bash
cd backend
pip install authlib==1.3.0 httpx==0.26.0
```
### 1. Создайте приложение в ZITADEL
```
→ zitadel.cloud
→ New Application
→ Web Application
→ Code (with PKCE)
→ Redirect URI: http://localhost:8000/api/auth/oidc/zitadel/callback
```
### 2. Настройте .env
```bash
# backend/.env
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@your-project
ZITADEL_CLIENT_SECRET=your-secret-here
```
### 3. Запустите
```bash
# Backend
cd backend && python main.py
# Frontend
cd frontend && npm run dev
```
### 4. Проверьте
```
→ http://localhost:3000
→ Кнопка "Войти через ZITADEL" 🔐
```
## 📖 Подробная документация
### Для пользователей
#### [СЛЕДУЮЩИЕАГИ.md](СЛЕДУЮЩИЕАГИ.md)
**Что делать сейчас**
- Краткий план действий
- Проверка работы
- Визуальное представление
- Решение проблем
#### [ZITADEL_QUICK_START.md](ZITADEL_QUICK_START.md)
**Быстрый старт за 4 шага**
- Создание приложения в ZITADEL
- Настройка .env файла
- Запуск приложения
- Проверка работы
#### [ГОТОВО_OIDC.md](ГОТОВО_OIDC.md)
**Итоговая инструкция**
- Что сделано
- Как использовать
- Документация
- Возможности
### Для администраторов
#### [OPENID_CONNECT_SETUP.md](OPENID_CONNECT_SETUP.md)
**Полное руководство**
- Подробная настройка ZITADEL
- Конфигурация backend
- Конфигурация frontend
- Безопасность
- Troubleshooting
#### [РЕЗЮМЕ.md](РЕЗЮМЕ.md)
**Краткое резюме**
- Что выполнено
- Список файлов
- Инструкция по использованию
- Таблица документации
### Для разработчиков
#### [OIDC_CHANGES.md](OIDC_CHANGES.md)
**Технические детали**
- Изменения в коде
- Структура данных
- API endpoints
- Зависимости
- Безопасность
#### [СХЕМА_РАБОТЫ.md](СХЕМА_РАБОТЫ.md)
**Визуальная схема**
- Поток аутентификации
- Диаграммы
- Структура данных
- Безопасность
#### [ИТОГИ_РАБОТЫ.md](ИТОГИ_РАБОТЫ.md)
**Полный отчёт**
- Что было сделано
- Проверка качества
- Статистика
- Результаты
## 🔍 Структура проекта
```
MC Panel/
├── backend/
│ ├── main.py # OAuth инициализация и endpoints
│ ├── oidc_config.py # Конфигурация ZITADEL
│ ├── .env.example # Пример настроек
│ └── requirements.txt # Зависимости (authlib, httpx)
├── frontend/
│ └── src/
│ ├── App.jsx # Обработка callback
│ └── components/
│ └── Auth.jsx # Кнопка ZITADEL
└── docs/
├── СЛЕДУЮЩИЕАГИ.md # Что делать сейчас
├── РЕЗЮМЕ.md # Краткое резюме
├── ZITADEL_QUICK_START.md # Быстрый старт
├── OPENID_CONNECT_SETUP.md # Полная инструкция
├── OIDC_CHANGES.md # Технические детали
├── СХЕМА_РАБОТЫ.md # Визуальная схема
├── ИТОГИ_РАБОТЫ.md # Полный отчёт
├── ГОТОВО_OIDC.md # Итоговая инструкция
└── README_OIDC.md # Этот файл
```
## ✨ Возможности
### Функциональность
- ✅ Вход через ZITADEL
- ✅ Автоматическое создание пользователей
- ✅ Обновление данных при входе
- ✅ JWT токены для MC Panel
- ✅ Безопасность (PKCE, state, nonce)
### Интерфейс
- ✅ Кнопка "Войти через ZITADEL" 🔐
- ✅ Автоматический вход после OAuth
- ✅ Красивый дизайн
- ✅ Адаптивность
### Безопасность
- ✅ PKCE (Proof Key for Code Exchange)
- ✅ State parameter (CSRF защита)
- ✅ Nonce (replay защита)
- ✅ JWT с истечением
- ✅ Проверка redirect_uri
## 🎯 Рекомендации
### Для разработки
1. Используйте ZITADEL Cloud (бесплатно)
2. Тестируйте на localhost
3. Проверяйте логи backend
4. Используйте DevTools браузера
### Для продакшена
1. Используйте HTTPS
2. Настройте правильные redirect URIs
3. Регулярно обновляйте client_secret
4. Включите логирование
5. Добавьте мониторинг
## 🆘 Помощь
### Проблемы?
| Проблема | Решение |
|----------|---------|
| Кнопка не появляется | Проверьте `.env` и перезапустите backend |
| Ошибка redirect_uri | Проверьте настройки в ZITADEL |
| Ошибка invalid_client | Проверьте Client ID и Secret |
| Не создаётся пользователь | Проверьте логи backend |
**Подробнее:** [ZITADEL_QUICK_START.md](ZITADEL_QUICK_START.md) → Раздел "Проблемы?"
## 📊 Статистика
- **Файлов изменено:** 2
- **Файлов создано:** 8 (документация)
- **Файлов удалено:** 1
- **Строк кода:** ~50 изменено
- **Строк документации:** ~1500 добавлено
- **Время настройки:** ~7 минут
- **Сложность:** Низкая
## 🎉 Готово!
**OpenID Connect с ZITADEL полностью интегрирован и готов к использованию!**
### Следующий шаг
→ Откройте **[СЛЕДУЮЩИЕАГИ.md](СЛЕДУЮЩИЕАГИ.md)** и начните настройку!
---
**Вопросы?** Смотрите документацию выше или проверьте логи backend.
**Всё работает?** Отлично! Можете начинать использовать систему! 🚀

View File

@@ -1,61 +0,0 @@
# 🚀 MC Panel готова к использованию!
## Быстрый старт
### 1. Запустите бэкенд
```bash
cd backend
python main_new.py
```
### 2. Запустите фронтенд
```bash
cd frontend
npm run dev
```
### 3. Откройте панель
Откройте http://localhost:3000 в браузере
### 4. Войдите в систему
- **Логин**: admin
- **Пароль**: admin
## ✨ Возможности
### 🎨 Темы
- 5 тем на выбор: Тёмная, Светлая, Фиолетовая, Синяя, Зелёная
- Градиентный логотип "MC Panel" для каждой темы
- Автоматическое сохранение выбранной темы
### 🖥️ Управление серверами
- Создание и удаление серверов
- Запуск и остановка серверов
- Просмотр консоли в реальном времени
- Менеджер файлов с редактированием
- Мониторинг ресурсов (RAM, диск)
- Настройки сервера
### 👥 Пользователи
- Регистрация и авторизация
- Роли: Админ и Пользователь
- Управление доступом к серверам
- Владельцы серверов могут выдавать доступ другим пользователям
### 🌐 Сетевой доступ
- Работает через Radmin VPN
- Автоматическое определение API URL
- Поддержка локальной и сетевой работы
## 📱 Интерфейс
Современный дизайн в стиле TimeWeb Cloud:
- Карточки с тенями и анимациями
- Плавные переходы
- Адаптивный дизайн для мобильных
- Sticky header
- Анимированные индикаторы статуса
## 🎯 Готово!
Панель полностью настроена и готова к использованию. Наслаждайтесь! 🎉

View File

@@ -1,8 +0,0 @@
ВАЖНО: Переименуйте файл backend/main_new.py в backend/main.py
Удалите старый backend/main.py (если есть) и переименуйте backend/main_new.py в backend/main.py
Это можно сделать вручную в проводнике Windows или командой:
cd backend
del main.py
ren main_new.py main.py

View File

@@ -1,80 +0,0 @@
# Тестирование API
## Проверка работы API
Откройте браузер и проверьте следующие URL (замените IP на ваш):
### 1. Проверка списка серверов
```
http://26.123.45.67:8000/api/servers
```
Должен вернуть JSON с массивом серверов.
### 2. Проверка конфигурации сервера
```
http://26.123.45.67:8000/api/servers/ИМЯ_СЕРВЕРА/config
```
Должен вернуть JSON с настройками сервера.
### 3. Проверка файлов сервера
```
http://26.123.45.67:8000/api/servers/ИМЯ_СЕРВЕРА/files
```
Должен вернуть JSON с массивом файлов.
## Проверка в консоли браузера
Откройте консоль браузера (F12) и выполните:
```javascript
// Проверка API URL
console.log('API URL:', window.location.protocol + '//' + window.location.hostname + ':8000');
// Проверка серверов
fetch('http://' + window.location.hostname + ':8000/api/servers')
.then(r => r.json())
.then(data => console.log('Серверы:', data))
.catch(err => console.error('Ошибка:', err));
```
## Проверка логов бэкенда
В терминале где запущен бэкенд должны появляться сообщения:
- `Найдено серверов: X`
- `Загружена конфигурация для ...`
- `WebSocket подключен для сервера: ...`
Если сообщений нет, значит запросы не доходят до бэкенда.
## Возможные проблемы
### Проблема: Серверы показываются, но файлы/настройки не загружаются
**Причина:** Запросы идут на неправильный URL
**Решение:**
1. Откройте консоль браузера (F12)
2. Перейдите на вкладку Network
3. Попробуйте открыть файлы или настройки
4. Посмотрите на URL запросов - они должны начинаться с `http://ВАШ_IP:8000/api/`
### Проблема: CORS ошибки
**Причина:** Браузер блокирует запросы
**Решение:**
1. Перезапустите бэкенд
2. Убедитесь, что в логах бэкенда нет ошибок
3. Очистите кэш браузера
### Проблема: WebSocket не подключается
**Причина:** WebSocket использует неправильный протокол
**Решение:**
1. Проверьте файл `frontend/src/config.js`
2. WebSocket URL должен быть `ws://ВАШ_IP:8000`
3. Перезапустите фронтенд

View File

@@ -1,47 +0,0 @@
# ✅ Тема успешно применена!
## Что было сделано
### 🎨 Система тем
- ✅ Создано 5 тем: Тёмная, Светлая, Фиолетовая, Синяя, Зелёная
- ✅ Каждая тема имеет уникальный градиент для логотипа "MC Panel"
- ✅ Селектор тем добавлен в header
- ✅ Выбранная тема сохраняется в localStorage
### 🎯 Градиенты для "MC Panel"
- **Тёмная**: синий → фиолетовый (from-blue-400 to-purple-600)
- **Светлая**: синий → фиолетовый (from-blue-600 to-purple-600)
- **Фиолетовая**: фиолетовый → розовый (from-purple-400 to-pink-600)
- **Синяя**: голубой → синий (from-cyan-400 to-blue-600)
- **Зелёная**: изумрудный → зелёный (from-emerald-400 to-green-600)
### 🎨 Современный интерфейс
- ✅ Дизайн в стиле TimeWeb Cloud
- ✅ Карточки с тенями и анимациями
- ✅ Плавные переходы между темами
- ✅ Адаптивный дизайн для мобильных устройств
- ✅ Sticky header с информацией о пользователе
- ✅ Анимированные индикаторы статуса серверов
### 📱 Адаптивность
- ✅ Скрываемая боковая панель на мобильных
- ✅ Адаптивные кнопки (текст скрывается на маленьких экранах)
- ✅ Горизонтальная прокрутка вкладок
## Как использовать
### Смена темы
1. Нажмите на селектор тем в правом верхнем углу
2. Выберите нужную тему из списка
3. Тема применится мгновенно и сохранится автоматически
### Файлы с темами
- `frontend/src/themes.js` - конфигурация всех тем
- `frontend/src/App.jsx` - главный компонент с темами
- `frontend/src/components/Auth.jsx` - страница входа с темами
- `frontend/src/components/ThemeSelector.jsx` - селектор тем
## Готово! 🎉
Панель теперь имеет современный интерфейс с 5 темами и градиентным логотипом "MC Panel".
Все компоненты автоматически используют цвета выбранной темы.

View File

@@ -1,88 +0,0 @@
# ✅ Система тем полностью готова!
## Что было исправлено и улучшено
### 🎨 Градиентный логотип "MC Panel"
- ✅ Добавлен градиент в Auth.jsx (страница входа)
- ✅ Добавлен градиент в App.jsx (главная панель)
- ✅ Каждая тема имеет свой уникальный градиент:
- **Тёмная**: синий → фиолетовый
- **Светлая**: синий → фиолетовый
- **Фиолетовая**: фиолетовый → розовый
- **Синяя**: голубой → синий
- **Зелёная**: изумрудный → зелёный
### 🎯 Обновлённые компоненты
1. **ThemeSelector.jsx** - теперь использует динамические цвета из текущей темы
2. **CreateServerModal.jsx** - обновлён для использования тем с современным дизайном
3. **App.jsx** - добавлен градиент для логотипа
4. **Auth.jsx** - добавлен градиент для логотипа
### 📁 Структура файлов
```
frontend/src/
├── themes.js # Конфигурация всех тем
├── App.jsx # Главный компонент с темами ✅
├── components/
│ ├── Auth.jsx # Страница входа с темами ✅
│ ├── ThemeSelector.jsx # Селектор тем ✅
│ ├── CreateServerModal.jsx # Модальное окно создания сервера ✅
│ ├── Console.jsx # Получает theme prop
│ ├── FileManager.jsx # Получает theme prop
│ ├── Stats.jsx # Получает theme prop
│ ├── ServerSettings.jsx # Получает theme prop
│ └── Users.jsx # Получает theme prop
```
## 🚀 Как использовать
### Запуск панели
```bash
# Терминал 1 - Бэкенд
cd backend
python main_new.py
# Терминал 2 - Фронтенд
cd frontend
npm run dev
```
### Смена темы
1. Откройте панель в браузере
2. Нажмите на иконку палитры (🎨) в правом верхнем углу
3. Выберите нужную тему из выпадающего меню
4. Тема применится мгновенно и сохранится автоматически
### Доступные темы
- 🌑 **Тёмная** - классическая тёмная тема (по умолчанию)
- ☀️ **Светлая** - светлая тема для дневного использования
- 💜 **Фиолетовая** - стильная фиолетовая палитра
- 💙 **Синяя** - холодная синяя тема
- 💚 **Зелёная** - природная зелёная тема
## ✨ Особенности
### Градиентный логотип
Логотип "MC Panel" теперь использует градиент, который меняется в зависимости от выбранной темы:
```jsx
<h1 className={`text-xl font-bold bg-gradient-to-r ${currentTheme.gradient} bg-clip-text text-transparent`}>
MC Panel
</h1>
```
### Автоматическое сохранение
Выбранная тема автоматически сохраняется в `localStorage` и применяется при следующем входе.
### Плавные переходы
Все элементы интерфейса имеют плавные переходы при смене темы благодаря классу `transition-colors duration-300`.
### Адаптивный дизайн
- Скрываемая боковая панель на мобильных устройствах
- Адаптивные кнопки (текст скрывается на маленьких экранах)
- Горизонтальная прокрутка вкладок на мобильных
## 🎉 Готово!
Панель MC Panel теперь имеет полноценную систему тем с градиентным логотипом и современным интерфейсом в стиле TimeWeb Cloud. Все компоненты используют цвета из выбранной темы, обеспечивая единообразный и красивый дизайн.
Наслаждайтесь использованием! 🚀

View File

@@ -1,125 +0,0 @@
# Обновление: Система тем и современный интерфейс
## Что добавлено
### 🎨 Система тем
- **5 тем на выбор:**
- Тёмная (по умолчанию)
- Светлая
- Фиолетовая
- Синяя
- Зелёная
### 🎯 Современный интерфейс в стиле TimeWeb Cloud
- Чистый и минималистичный дизайн
- Плавные переходы и анимации
- Адаптивная вёрстка
- Улучшенная типографика
- Современные карточки и кнопки
- Sticky header
- Анимированные индикаторы статуса
## Установка
### 1. Замените App.jsx
Переименуйте файлы:
```
frontend/src/App.jsx → frontend/src/App_old.jsx (бэкап)
frontend/src/App_modern.jsx → frontend/src/App.jsx
```
### 2. Перезапустите фронтенд
```bash
cd frontend
npm run dev
```
## Использование
### Смена темы
1. Нажмите на иконку палитры (🎨) в правом верхнем углу
2. Выберите нужную тему из выпадающего меню
3. Тема сохраняется автоматически
### Особенности интерфейса
**Header:**
- Sticky (прилипает к верху при прокрутке)
- Показывает статус подключения
- Отображает текущего пользователя и роль
- Кнопки быстрого доступа
**Sidebar:**
- Список серверов с карточками
- Кнопки запуска/остановки на каждой карточке
- Анимированный индикатор статуса (пульсирует когда запущен)
- Скрывается на мобильных устройствах
**Вкладки:**
- Современный дизайн с подчёркиванием
- Иконки для каждой вкладки
- Плавные переходы
**Карточки серверов:**
- Закруглённые углы
- Тени при наведении
- Цветовая индикация выбранного сервера
- Плавные анимации
## Темы
### Тёмная (Dark)
- Основной: Серый 900
- Вторичный: Серый 800
- Акцент: Синий 600
### Светлая (Light)
- Основной: Серый 50
- Вторичный: Белый
- Акцент: Синий 600
### Фиолетовая (Purple)
- Основной: Фиолетовый 950
- Вторичный: Фиолетовый 900
- Акцент: Фиолетовый 600
### Синяя (Blue)
- Основной: Синий 950
- Вторичный: Синий 900
- Акцент: Синий 500
### Зелёная (Green)
- Основной: Зелёный 950
- Вторичный: Зелёный 900
- Акцент: Зелёный 600
## Файлы
- `frontend/src/App_modern.jsx` - новый App с темами
- `frontend/src/themes.js` - конфигурация тем
- `frontend/src/components/ThemeSelector.jsx` - селектор тем
## Что дальше
Компоненты (Console, FileManager, Stats, ServerSettings) нужно обновить для поддержки тем.
Они получают `theme` prop с текущей темой.
Пример использования в компоненте:
```jsx
export default function MyComponent({ theme }) {
return (
<div className={`${theme.primary} ${theme.text}`}>
<button className={`${theme.accent} ${theme.accentHover}`}>
Кнопка
</button>
</div>
);
}
```
## Готово! 🎉
Теперь у вас современный интерфейс с системой тем!

View File

@@ -1,143 +0,0 @@
# 🎫 Система тикетов
## Что добавлено
### ✅ Новые возможности
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. Назначьте ему роль "Тех. поддержка"

View File

@@ -1,187 +0,0 @@
# 👁️ Просмотр профилей пользователей
## Что добавлено
### Возможность просмотра профилей для админов и тех. поддержки
Администраторы и сотрудники технической поддержки теперь могут просматривать личные кабинеты других пользователей, нажав на их логин в списке пользователей.
## 🎯 Как использовать
### Просмотр профиля пользователя
1. Войдите как администратор или тех. поддержка
2. Нажмите кнопку "Пользователи" в header
3. Найдите нужного пользователя в списке
4. **Нажмите на логин пользователя** (он теперь кликабельный и подсвечивается при наведении)
5. Откроется личный кабинет этого пользователя
### Что можно увидеть
- ✅ Имя пользователя
- ✅ Роль пользователя
- ✅ Статистику по серверам (всего, мои, доступные)
- ✅ Список серверов пользователя
- ✅ Статистику по тикетам (всего, по статусам)
### Что нельзя сделать
- ❌ Изменить имя пользователя (вкладка скрыта)
- ❌ Изменить пароль пользователя (вкладка скрыта)
- ❌ Редактировать профиль другого пользователя
## 🎨 Визуальные изменения
### В списке пользователей (Users.jsx)
- **Логин пользователя** теперь кликабельный
- При наведении логин подсвечивается синим цветом
- Курсор меняется на pointer (указатель)
- Подсказка "Просмотреть профиль" при наведении
### В личном кабинете (Profile.jsx)
- **Заголовок**: "Профиль пользователя: [username]" (вместо "Личный кабинет")
- **Подзаголовок**: "Просмотр профиля другого пользователя"
- **Вкладки**: скрыты вкладки "Имя пользователя" и "Пароль"
- **Только вкладка "Обзор"**: показывается статистика пользователя
## 📋 Технические детали
### Backend (main.py)
#### Новый endpoint
```python
@app.get("/api/profile/stats/{username}")
async def get_user_profile_stats(username: str, user: dict = Depends(get_current_user)):
"""Получить статистику профиля другого пользователя"""
# Проверка прав доступа
if user["role"] not in ["admin", "support"]:
raise HTTPException(403, "Недостаточно прав")
# Возвращает статистику указанного пользователя
```
#### Проверка прав
- Только администраторы и тех. поддержка могут просматривать чужие профили
- Обычные пользователи получат ошибку 403
### Frontend
#### App.jsx
```javascript
const [viewingUsername, setViewingUsername] = useState(null);
const handleViewProfile = (username) => {
setViewingUsername(username);
setShowProfile(true);
setShowUsers(false);
};
```
#### Users.jsx
```javascript
<button
onClick={() => onViewProfile && onViewProfile(user.username)}
className="text-lg font-semibold hover:text-blue-400 transition cursor-pointer"
title="Просмотреть профиль"
>
{user.username}
</button>
```
#### Profile.jsx
```javascript
const isViewingOther = viewingUsername && viewingUsername !== user?.username;
const loadStats = async () => {
const endpoint = isViewingOther
? `${API_URL}/api/profile/stats/${viewingUsername}`
: `${API_URL}/api/profile/stats`;
// ...
};
```
## 🔐 Безопасность
### Проверка прав на уровне API
- Endpoint `/api/profile/stats/{username}` проверяет роль пользователя
- Только `admin` и `support` могут получить доступ
- Обычные пользователи получат ошибку 403
### Защита на уровне UI
- Вкладки изменения имени и пароля скрыты при просмотре чужого профиля
- Невозможно редактировать данные другого пользователя
- Только просмотр статистики
## 📊 Доступные роли
### Кто может просматривать чужие профили
1. **Администратор** (admin) - ✅ Может просматривать все профили
2. **Тех. поддержка** (support) - ✅ Может просматривать все профили
3. **Пользователь** (user) - ❌ Не может просматривать чужие профили
4. **Забанен** (banned) - ❌ Не имеет доступа к панели
## ✅ Примеры использования
### Сценарий 1: Проверка активности пользователя
1. Админ хочет проверить, сколько серверов у пользователя
2. Открывает "Пользователи"
3. Нажимает на логин пользователя
4. Видит статистику: 3 сервера, 5 тикетов
### Сценарий 2: Помощь пользователю
1. Тех. поддержка получила тикет от пользователя
2. Хочет посмотреть его серверы для диагностики
3. Открывает "Пользователи"
4. Нажимает на логин пользователя
5. Видит список серверов и их названия
### Сценарий 3: Модерация
1. Админ хочет проверить активность пользователя перед баном
2. Открывает профиль пользователя
3. Видит статистику по тикетам и серверам
4. Принимает решение о блокировке
## 🎯 Возврат к списку пользователей
### Из профиля пользователя
1. Нажмите кнопку "Серверы" в header
2. Вы вернётесь к главной странице
3. Снова откройте "Пользователи" для просмотра других профилей
### Или откройте свой профиль
1. Нажмите кнопку "Личный кабинет" в header
2. Откроется ваш собственный профиль
3. Будут доступны все вкладки (Обзор, Имя пользователя, Пароль)
## ⚠️ Важные замечания
### Ограничения
- Нельзя редактировать чужие профили
- Нельзя изменить имя или пароль другого пользователя
- Только просмотр статистики
### Рекомендации
- Используйте эту функцию для помощи пользователям
- Не злоупотребляйте просмотром чужих профилей
- Соблюдайте конфиденциальность данных пользователей
## ✅ Готово!
Функция просмотра профилей пользователей полностью интегрирована в MC Panel. Администраторы и тех. поддержка могут легко просматривать информацию о пользователях для помощи и модерации.
### Тестирование
1. **Войдите как администратор**
- Логин: none
- Пароль: none
2. **Создайте тестового пользователя**
- Зарегистрируйте нового пользователя
- Создайте несколько серверов от его имени
3. **Просмотрите его профиль**
- Откройте "Пользователи"
- Нажмите на логин тестового пользователя
- Увидите его статистику
4. **Вернитесь к своему профилю**
- Нажмите "Личный кабинет"
- Откроется ваш профиль со всеми вкладками
**Удобного использования! 👁️**

View File

@@ -1,76 +0,0 @@
# 🚀 Быстрый старт с ZITADEL
## Что нужно сделать
### 1⃣ Создать приложение в ZITADEL
1. Зайдите на [zitadel.cloud](https://zitadel.cloud) или используйте свой инстанс
2. Создайте новый проект или выберите существующий
3. Нажмите **"New Application"**
4. Выберите **"Web Application"**
5. Выберите **"Code (with PKCE)"**
6. Добавьте Redirect URI: `http://localhost:8000/api/auth/oidc/zitadel/callback`
7. Сохраните **Client ID** и **Client Secret**
### 2⃣ Настроить .env файл
Откройте `backend/.env` и добавьте:
```bash
# ZITADEL Configuration
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@your-project
ZITADEL_CLIENT_SECRET=your-secret-key-here
# URLs
BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
```
### 3⃣ Запустить приложение
```bash
# Backend
cd backend
python main.py
# Frontend (в другом терминале)
cd frontend
npm run dev
```
### 4⃣ Проверить
1. Откройте http://localhost:3000
2. Увидите кнопку **"Войти через ZITADEL"** 🔐
3. Нажмите и войдите через ZITADEL
4. Готово! ✅
## Что происходит?
1. **Пользователь нажимает кнопку** → Перенаправление на ZITADEL
2. **Вход в ZITADEL** → Пользователь вводит логин/пароль
3. **Callback** → ZITADEL возвращает код авторизации
4. **Обмен кода на токен** → Backend получает данные пользователя
5. **Создание пользователя** → Автоматическое создание в системе
6. **JWT токен** → Пользователь получает токен для доступа
7. **Автоматический вход** → Перенаправление в панель
## Проблемы?
### Кнопка ZITADEL не появляется
- Проверьте `.env` файл
- Убедитесь что `ZITADEL_CLIENT_ID` и `ZITADEL_ISSUER` заполнены
- Перезапустите backend
### Ошибка "Invalid redirect_uri"
- Проверьте Redirect URI в настройках ZITADEL
- Должен быть: `http://localhost:8000/api/auth/oidc/zitadel/callback`
### Ошибка "Invalid client"
- Проверьте `ZITADEL_CLIENT_ID` и `ZITADEL_CLIENT_SECRET`
- Убедитесь что приложение активно в ZITADEL
## Готово! 🎉
Теперь пользователи могут входить через ZITADEL!

View File

@@ -73,8 +73,8 @@ IS_WINDOWS = sys.platform == 'win32'
def init_users():
if not USERS_FILE.exists():
admin_user = {
"username": "Sofa12345",
"password": pwd_context.hash("arkonsad123"),
"username": "Root",
"password": pwd_context.hash("Admin"),
"role": "admin",
"servers": []
}
@@ -1005,23 +1005,88 @@ async def download_file(server_name: str, path: str, user: dict = Depends(get_cu
@app.post("/api/servers/{server_name}/files/upload")
async def upload_file(server_name: str, path: str, file: UploadFile = File(...), user: dict = Depends(get_current_user)):
print(f"Upload request: server={server_name}, path='{path}', filename='{file.filename}'")
if not check_server_access(user, server_name):
raise HTTPException(403, "Нет доступа к этому серверу")
server_path = SERVERS_DIR / server_name
target_path = server_path / path / file.filename
print(f"Target path: {target_path}")
print(f"Server path: {server_path}")
print(f"Path starts with server_path: {str(target_path).startswith(str(server_path))}")
if not str(target_path).startswith(str(server_path)):
raise HTTPException(400, "Недопустимый путь")
target_path.parent.mkdir(parents=True, exist_ok=True)
try:
target_path.parent.mkdir(parents=True, exist_ok=True)
print(f"Created directory: {target_path.parent}")
except Exception as e:
print(f"Error creating directory: {e}")
raise HTTPException(500, f"Ошибка создания директории: {str(e)}")
with open(target_path, "wb") as f:
content = await file.read()
f.write(content)
try:
with open(target_path, "wb") as f:
content = await file.read()
f.write(content)
print(f"File written successfully: {target_path}")
except Exception as e:
print(f"Error writing file: {e}")
raise HTTPException(500, f"Ошибка записи файла: {str(e)}")
return {"message": "Файл загружен"}
@app.post("/api/servers/{server_name}/files/create")
async def create_file_or_folder(server_name: str, data: dict, user: dict = Depends(get_current_user)):
"""Создать новый файл или папку"""
if not check_server_access(user, server_name):
raise HTTPException(403, "Нет доступа к этому серверу")
item_type = data.get("type") # "file" or "folder"
name = data.get("name", "").strip()
path = data.get("path", "") # Текущая папка
if not name:
raise HTTPException(400, "Имя не может быть пустым")
if item_type not in ["file", "folder"]:
raise HTTPException(400, "Тип должен быть 'file' или 'folder'")
server_path = SERVERS_DIR / server_name
# Формируем полный путь
if path:
full_path = server_path / path / name
else:
full_path = server_path / name
print(f"Creating {item_type}: {full_path}")
# Проверка безопасности
if not str(full_path).startswith(str(server_path)):
raise HTTPException(400, "Недопустимый путь")
try:
if item_type == "folder":
# Создаем папку
full_path.mkdir(parents=True, exist_ok=True)
# Создаем .gitkeep чтобы папка не была пустой
gitkeep = full_path / ".gitkeep"
gitkeep.touch()
print(f"Folder created: {full_path}")
else:
# Создаем файл
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.touch()
print(f"File created: {full_path}")
return {"message": f"{'Папка' if item_type == 'folder' else 'Файл'} создан(а)", "path": str(full_path)}
except Exception as e:
print(f"Error creating {item_type}: {e}")
raise HTTPException(500, f"Ошибка создания: {str(e)}")
@app.delete("/api/servers/{server_name}/files")
async def delete_file(server_name: str, path: str, user: dict = Depends(get_current_user)):
if not check_server_access(user, server_name):
@@ -1098,6 +1163,82 @@ async def rename_file(server_name: str, old_path: str, new_name: str, user: dict
old_file_path.rename(new_file_path)
return {"message": "Файл переименован"}
@app.post("/api/servers/{server_name}/files/move")
async def move_file(server_name: str, data: dict, user: dict = Depends(get_current_user)):
"""Переместить файл или папку"""
if not check_server_access(user, server_name):
raise HTTPException(403, "Нет доступа к этому серверу")
source_path = data.get("source", "").strip()
destination_path = data.get("destination", "").strip()
if not source_path:
raise HTTPException(400, "Не указан исходный путь")
server_path = SERVERS_DIR / server_name
source_full = server_path / source_path
# Формируем путь назначения
if destination_path:
# Извлекаем имя файла из source_path
file_name = source_full.name
dest_full = server_path / destination_path / file_name
else:
# Перемещение в корень
file_name = source_full.name
dest_full = server_path / file_name
print(f"Moving: {source_full} -> {dest_full}")
# Проверки безопасности
if not source_full.exists():
raise HTTPException(404, "Исходный файл не найден")
if not str(source_full).startswith(str(server_path)):
raise HTTPException(400, "Недопустимый исходный путь")
if not str(dest_full).startswith(str(server_path)):
raise HTTPException(400, "Недопустимый путь назначения")
if dest_full.exists():
raise HTTPException(400, "Файл с таким именем уже существует в папке назначения")
try:
# Создаем папку назначения если не существует
dest_full.parent.mkdir(parents=True, exist_ok=True)
# Перемещаем файл/папку
import shutil
shutil.move(str(source_full), str(dest_full))
print(f"Moved successfully: {dest_full}")
return {"message": "Файл перемещен", "new_path": str(dest_full)}
except Exception as e:
print(f"Error moving file: {e}")
raise HTTPException(500, f"Ошибка перемещения: {str(e)}")
@app.put("/api/servers/{server_name}/files/rename")
async def rename_file(server_name: str, old_path: str, new_name: str, user: dict = Depends(get_current_user)):
if not check_server_access(user, server_name):
raise HTTPException(403, "Нет доступа к этому серверу")
server_path = SERVERS_DIR / server_name
old_file_path = server_path / old_path
if not old_file_path.exists() or not str(old_file_path).startswith(str(server_path)):
raise HTTPException(404, "Файл не найден")
new_file_path = old_file_path.parent / new_name
if new_file_path.exists():
raise HTTPException(400, "Файл с таким именем уже существует")
if not str(new_file_path).startswith(str(server_path)):
raise HTTPException(400, "Недопустимое имя файла")
old_file_path.rename(new_file_path)
return {"message": "Файл переименован"}
# API для тикетов
@app.get("/api/tickets")
async def get_tickets(user: dict = Depends(get_current_user)):

View File

@@ -1,129 +1 @@
{
"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"
}
]
},
"2": {
"id": "2",
"title": "Разраб даун",
"description": "помогите разраб минды даун, а киро вообще маньяк на коммиты в гитею",
"author": "MihailPrud",
"status": "closed",
"created_at": "2026-01-15T03:25:33.660528",
"updated_at": "2026-01-15T03:27:41.117949",
"messages": [
{
"author": "MihailPrud",
"text": "помогите разраб минды даун, а киро вообще маньяк на коммиты в гитею",
"timestamp": "2026-01-15T03:25:33.660528"
},
{
"author": "system",
"text": "Статус изменён на: В работе",
"timestamp": "2026-01-15T03:25:56.445796"
},
{
"author": "Sofa12345",
"text": "Дааааа, туда этого бота",
"timestamp": "2026-01-15T03:25:58.592839"
},
{
"author": "MihailPrud",
"text": "памагете",
"timestamp": "2026-01-15T03:26:20.740325"
},
{
"author": "Sofa12345",
"text": "чим",
"timestamp": "2026-01-15T03:26:29.038071"
},
{
"author": "MihailPrud",
"text": "у миня -30 и минет в школу надоть",
"timestamp": "2026-01-15T03:26:37.692369"
},
{
"author": "Sofa12345",
"text": "пиздец нахуй блять",
"timestamp": "2026-01-15T03:26:48.846565"
},
{
"author": "MihailPrud",
"text": "согласен",
"timestamp": "2026-01-15T03:26:56.324587"
},
{
"author": "Sofa12345",
"text": "Nahyi eto school nyxna",
"timestamp": "2026-01-15T03:27:15.968192"
},
{
"author": "Sofa12345",
"text": "pizdets",
"timestamp": "2026-01-15T03:27:21.810953"
},
{
"author": "MihailPrud",
"text": "не нужна",
"timestamp": "2026-01-15T03:27:24.548623"
},
{
"author": "MihailPrud",
"text": "но ходить надоть",
"timestamp": "2026-01-15T03:27:31.625634"
},
{
"author": "system",
"text": "Статус изменён на: Закрыт",
"timestamp": "2026-01-15T03:27:38.480740"
},
{
"author": "MihailPrud",
"text": "для баланса вселеннной",
"timestamp": "2026-01-15T03:27:41.117949"
}
]
}
}
{}

View File

@@ -11,15 +11,10 @@
"arkonsad": {
"username": "arkonsad",
"password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392",
"role": "banned",
"role": "user",
"servers": [
"123"
"123",
"sdfsdf"
]
},
"Sofa12345": {
"username": "Sofa12345",
"password": "$2b$12$Fph20p2mwgOAqoT77wSA3.n1S7NiHLa28aiNOwWcz3PfNhgC5pp5.",
"role": "admin",
"servers": []
}
}

View File

@@ -11,6 +11,7 @@ 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';
@@ -27,7 +28,7 @@ function App() {
const [showProfile, setShowProfile] = useState(false);
const [viewingUsername, setViewingUsername] = useState(null);
const [connectionError, setConnectionError] = useState(false);
const [theme, setTheme] = useState(localStorage.getItem('theme') || 'dark');
const [theme, setTheme] = useState(localStorage.getItem('theme') || 'modern');
const [sidebarOpen, setSidebarOpen] = useState(true);
const currentTheme = getTheme(theme);
@@ -149,11 +150,13 @@ function App() {
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log('Сервер запущен:', response.data);
notify('success', 'Сервер запущен', `Сервер "${serverName}" успешно запущен`);
setTimeout(() => {
loadServers();
}, 1000);
} catch (error) {
console.error('Ошибка запуска сервера:', error);
notify('error', 'Ошибка запуска', error.response?.data?.detail || 'Не удалось запустить сервер');
alert(error.response?.data?.detail || 'Ошибка запуска сервера');
}
};
@@ -166,11 +169,13 @@ function App() {
{ 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 || 'Ошибка остановки сервера');
}
};
@@ -334,6 +339,7 @@ function App() {
return (
<div className={`min-h-screen ${currentTheme.primary} ${currentTheme.text} transition-colors duration-300`}>
<NotificationSystem theme={currentTheme} />
{/* Header */}
<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">
@@ -486,6 +492,42 @@ function App() {
<main className="flex-1 flex flex-col overflow-hidden">
{selectedServer ? (
<>
{/* Server Header with Controls */}
<div className={`${currentTheme.secondary} ${currentTheme.border} border-b px-6 py-3 flex items-center justify-between`}>
<div className="flex items-center gap-3">
<Server className="w-5 h-5" />
<span className="font-semibold text-lg">
{servers.find(s => s.name === selectedServer)?.displayName || selectedServer}
</span>
<span className={`px-2 py-1 rounded text-xs font-medium ${
servers.find(s => s.name === selectedServer)?.status === 'running'
? 'bg-green-600 text-white'
: 'bg-gray-600 text-white'
}`}>
{servers.find(s => s.name === selectedServer)?.status === 'running' ? 'Запущен' : 'Остановлен'}
</span>
</div>
<div className="flex items-center gap-2">
{servers.find(s => s.name === selectedServer)?.status === 'stopped' ? (
<button
onClick={() => startServer(selectedServer)}
className="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg text-sm font-medium flex items-center gap-2 text-white transition shadow-lg"
>
<Play className="w-4 h-4" />
Запустить
</button>
) : (
<button
onClick={() => stopServer(selectedServer)}
className="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg text-sm font-medium flex items-center gap-2 text-white transition shadow-lg"
>
<Square className="w-4 h-4" />
Сброс
</button>
)}
</div>
</div>
{/* Tabs */}
<div className={`${currentTheme.secondary} ${currentTheme.border} border-b flex overflow-x-auto`}>
{[

View File

@@ -11,7 +11,7 @@ export default function Auth({ onLogin }) {
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [theme] = useState(localStorage.getItem('theme') || 'dark');
const [theme] = useState(localStorage.getItem('theme') || 'modern');
const [oidcProviders, setOidcProviders] = useState({});
const currentTheme = getTheme(theme);
@@ -182,7 +182,7 @@ export default function Auth({ onLogin }) {
{isLogin && (
<div className={`mt-6 text-center text-sm ${currentTheme.textSecondary}`}>
<p>Учётные данные по умолчанию:</p>
<p className={`${currentTheme.text} font-mono mt-1`}>Sofa12345 / arkonsad123</p>
<p className={`${currentTheme.text} font-mono mt-1`}>none / none</p>
</div>
)}
</div>

View File

@@ -54,32 +54,63 @@ export default function Console({ serverName, token, theme }) {
}
};
// Функция для раскраски логов
const colorizeLog = (log) => {
// INFO - зеленый
if (log.includes('[INFO]') || log.includes('Done (')) {
return <span className="text-green-400">{log}</span>;
}
// WARN - желтый
if (log.includes('[WARN]') || log.includes('WARNING')) {
return <span className="text-yellow-400">{log}</span>;
}
// ERROR - красный
if (log.includes('[ERROR]') || log.includes('Exception')) {
return <span className="text-red-400">{log}</span>;
}
// Время - серый
if (log.match(/^\[\d{2}:\d{2}:\d{2}\]/)) {
const time = log.match(/^\[\d{2}:\d{2}:\d{2}\]/)[0];
const rest = log.substring(time.length);
return (
<>
<span className="text-gray-500">{time}</span>
<span className="text-gray-300">{rest}</span>
</>
);
}
// Обычный текст
return <span className="text-gray-300">{log}</span>;
};
return (
<div className={`flex flex-col h-full ${theme.primary}`}>
<div className={`flex-1 overflow-y-auto p-4 font-mono text-sm ${theme.secondary}`}>
{/* Консоль */}
<div className={`flex-1 overflow-y-auto p-4 font-mono text-sm ${theme.console || theme.secondary}`}>
{logs.length === 0 ? (
<div className={theme.textSecondary}>Консоль пуста. Запустите сервер для просмотра логов.</div>
) : (
logs.map((log, index) => (
<div key={index} className={`${theme.text} whitespace-pre-wrap leading-relaxed`}>
{log}
<div key={index} className="whitespace-pre-wrap leading-relaxed">
{colorizeLog(log)}
</div>
))
)}
<div ref={logsEndRef} />
</div>
{/* Поле ввода команды */}
<form onSubmit={sendCommand} className={`${theme.border} border-t p-4 flex gap-2`}>
<input
type="text"
value={command}
onChange={(e) => setCommand(e.target.value)}
placeholder="Введите команду..."
className={`flex-1 ${theme.input} ${theme.border} border rounded-xl px-4 py-2 ${theme.text} focus:outline-none focus:ring-2 focus:ring-blue-500 transition`}
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`}
/>
<button
type="submit"
className={`${theme.accent} ${theme.accentHover} px-6 py-2 rounded-xl flex items-center gap-2 text-white transition`}
className={`${theme.success} ${theme.successHover} px-6 py-2.5 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg`}
>
<Send className="w-4 h-4" />
Отправить

View File

@@ -2,6 +2,7 @@ import { useState } from 'react';
import { X } from 'lucide-react';
import axios from 'axios';
import { API_URL } from '../config';
import { notify } from './NotificationSystem';
export default function CreateServerModal({ token, theme, onClose, onCreated }) {
const [formData, setFormData] = useState({
@@ -21,9 +22,11 @@ export default function CreateServerModal({ token, theme, onClose, onCreated })
formData,
{ headers: { Authorization: `Bearer ${token}` } }
);
notify('success', 'Сервер создан', `Сервер "${formData.displayName}" успешно создан`);
onCreated();
onClose();
} catch (error) {
notify('error', 'Ошибка создания', error.response?.data?.detail || 'Не удалось создать сервер');
alert(error.response?.data?.detail || 'Ошибка создания сервера');
} finally {
setLoading(false);

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { X, Save } from 'lucide-react';
export default function FileEditorModal({ file, onClose, onSave }) {
export default function FileEditorModal({ file, onClose, onSave, theme }) {
const [content, setContent] = useState(file.content);
const [saving, setSaving] = useState(false);
@@ -25,37 +25,37 @@ export default function FileEditorModal({ file, onClose, onSave }) {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-gray-800 rounded-lg w-full max-w-4xl h-[80vh] flex flex-col">
<div className="flex items-center justify-between p-4 border-b border-gray-700">
<h2 className="text-xl font-bold">Редактирование: {file.name}</h2>
<div className={`${theme.secondary} rounded-lg w-full max-w-4xl h-[80vh] flex flex-col ${theme.border} border`}>
<div className={`flex items-center justify-between p-4 ${theme.border} border-b`}>
<h2 className={`text-xl font-bold ${theme.text}`}>Редактирование: {file.name}</h2>
<div className="flex gap-2">
<button
onClick={handleSave}
disabled={saving}
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded flex items-center gap-2 disabled:opacity-50"
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded flex items-center gap-2 disabled:opacity-50 text-white transition"
>
<Save className="w-4 h-4" />
{saving ? 'Сохранение...' : 'Сохранить'}
</button>
<button
onClick={onClose}
className="text-gray-400 hover:text-white"
className={`${theme.textSecondary} hover:${theme.text} transition`}
>
<X className="w-6 h-6" />
</button>
</div>
</div>
<div className="flex-1 overflow-hidden p-4 bg-gray-900">
<div className={`flex-1 overflow-hidden p-4 ${theme.console || theme.primary}`}>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
className="w-full h-full bg-black text-gray-300 font-mono text-sm p-4 rounded border border-gray-700 focus:outline-none focus:border-blue-500 resize-none"
className={`w-full h-full ${theme.console || theme.primary} ${theme.consoleText || theme.text} font-mono text-sm p-4 rounded ${theme.border} border focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none`}
spellCheck={false}
/>
</div>
<div className="p-4 border-t border-gray-700 text-sm text-gray-400">
<div className={`p-4 ${theme.border} border-t text-sm ${theme.textSecondary}`}>
Используйте Ctrl+S для быстрого сохранения
</div>
</div>

View File

@@ -1,22 +1,42 @@
import { useState, useEffect } from 'react';
import { Folder, File, Download, Trash2, Upload, Edit, Eye } from 'lucide-react';
import { Folder, File, Download, Trash2, Upload, Edit, Eye, Search } from 'lucide-react';
import axios from 'axios';
import FileEditorModal from './FileEditorModal';
import FileViewerModal from './FileViewerModal';
import { API_URL } from '../config';
import { notify } from './NotificationSystem';
export default function FileManager({ serverName, token }) {
export default function FileManager({ serverName, token, theme }) {
const [files, setFiles] = useState([]);
const [currentPath, setCurrentPath] = useState('');
const [editingFile, setEditingFile] = useState(null);
const [viewingFile, setViewingFile] = useState(null);
const [renamingFile, setRenamingFile] = useState(null);
const [newFileName, setNewFileName] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [selectedFiles, setSelectedFiles] = useState([]);
const [selectAll, setSelectAll] = useState(false);
const [showNewMenu, setShowNewMenu] = useState(false);
const [creatingNew, setCreatingNew] = useState(null); // 'file' or 'folder'
const [newItemName, setNewItemName] = useState('');
const [cutFiles, setCutFiles] = useState([]); // Файлы для перемещения
useEffect(() => {
loadFiles();
}, [serverName, currentPath]);
// Закрытие меню "Новый" при клике вне его
useEffect(() => {
const handleClickOutside = (e) => {
if (showNewMenu && !e.target.closest('.new-menu-container')) {
setShowNewMenu(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [showNewMenu]);
const loadFiles = async () => {
try {
const { data } = await axios.get(`${API_URL}/api/servers/${serverName}/files`, {
@@ -53,8 +73,10 @@ export default function FileManager({ serverName, token }) {
params: { path: filePath },
headers: { Authorization: `Bearer ${token}` }
});
notify('success', 'Файл удален', `"${fileName}" успешно удален`);
loadFiles();
} catch (error) {
notify('error', 'Ошибка удаления', 'Не удалось удалить файл');
alert('Ошибка удаления файла');
}
};
@@ -72,8 +94,10 @@ export default function FileManager({ serverName, token }) {
formData,
{ headers: { Authorization: `Bearer ${token}` } }
);
notify('success', 'Файл загружен', `"${file.name}" успешно загружен`);
loadFiles();
} catch (error) {
notify('error', 'Ошибка загрузки', 'Не удалось загрузить файл');
alert('Ошибка загрузки файла');
}
};
@@ -115,8 +139,10 @@ export default function FileManager({ serverName, token }) {
}
);
setEditingFile(null);
notify('success', 'Файл сохранен', 'Изменения успешно сохранены');
alert('Файл сохранен');
} catch (error) {
notify('error', 'Ошибка сохранения', error.response?.data?.detail || 'Не удалось сохранить файл');
alert(error.response?.data?.detail || 'Ошибка сохранения файла');
}
};
@@ -144,8 +170,10 @@ export default function FileManager({ serverName, token }) {
}
);
setRenamingFile(null);
notify('success', 'Файл переименован', `"${oldName}" → "${newFileName}"`);
loadFiles();
} catch (error) {
notify('error', 'Ошибка переименования', error.response?.data?.detail || 'Не удалось переименовать файл');
alert(error.response?.data?.detail || 'Ошибка переименования файла');
}
};
@@ -158,124 +186,493 @@ export default function FileManager({ serverName, token }) {
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
};
const filteredFiles = files.filter(file =>
file.name.toLowerCase().includes(searchQuery.toLowerCase())
);
// Выбор всех файлов
const handleSelectAll = () => {
if (selectAll) {
setSelectedFiles([]);
} else {
setSelectedFiles(filteredFiles.map(f => f.name));
}
setSelectAll(!selectAll);
};
// Выбор отдельного файла
const handleSelectFile = (fileName) => {
if (selectedFiles.includes(fileName)) {
setSelectedFiles(selectedFiles.filter(f => f !== fileName));
} else {
setSelectedFiles([...selectedFiles, fileName]);
}
};
// Создание нового файла
const createNewFile = async () => {
if (!newItemName.trim()) {
alert('Введите имя файла');
return;
}
try {
console.log('Creating file:', {
type: 'file',
name: newItemName,
path: currentPath
});
const response = await axios.post(
`${API_URL}/api/servers/${serverName}/files/create`,
{
type: 'file',
name: newItemName,
path: currentPath
},
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log('File created successfully:', response.data);
notify('success', 'Файл создан', `"${newItemName}" успешно создан`);
setCreatingNew(null);
setNewItemName('');
loadFiles();
} catch (error) {
console.error('Ошибка создания файла:', error);
console.error('Error details:', error.response?.data);
notify('error', 'Ошибка создания', error.response?.data?.detail || 'Не удалось создать файл');
alert(error.response?.data?.detail || 'Ошибка создания файла');
}
};
// Создание новой папки
const createNewFolder = async () => {
if (!newItemName.trim()) {
alert('Введите имя папки');
return;
}
try {
console.log('Creating folder:', {
type: 'folder',
name: newItemName,
path: currentPath
});
const response = await axios.post(
`${API_URL}/api/servers/${serverName}/files/create`,
{
type: 'folder',
name: newItemName,
path: currentPath
},
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log('Folder created successfully:', response.data);
notify('success', 'Папка создана', `"${newItemName}" успешно создана`);
setCreatingNew(null);
setNewItemName('');
loadFiles();
} catch (error) {
console.error('Ошибка создания папки:', error);
console.error('Error details:', error.response?.data);
notify('error', 'Ошибка создания', error.response?.data?.detail || 'Не удалось создать папку');
alert(error.response?.data?.detail || 'Ошибка создания папки');
}
};
// Перемещение файла
const moveFile = async (sourcePath, destinationPath) => {
try {
console.log('Moving file:', { sourcePath, destinationPath });
const response = await axios.post(
`${API_URL}/api/servers/${serverName}/files/move`,
{
source: sourcePath,
destination: destinationPath
},
{ headers: { Authorization: `Bearer ${token}` } }
);
console.log('File moved successfully:', response.data);
const fileName = sourcePath.split('/').pop();
notify('success', 'Файл перемещен', `"${fileName}" успешно перемещен`);
loadFiles();
} catch (error) {
console.error('Ошибка перемещения файла:', error);
notify('error', 'Ошибка перемещения', error.response?.data?.detail || 'Не удалось переместить файл');
alert(error.response?.data?.detail || 'Ошибка перемещения файла');
}
};
// Вырезать файлы
const handleCut = () => {
if (selectedFiles.length === 0) {
alert('Выберите файлы для перемещения');
return;
}
const filesToCut = selectedFiles.map(fileName => {
const filePath = currentPath ? `${currentPath}/${fileName}` : fileName;
return { name: fileName, path: filePath };
});
setCutFiles(filesToCut);
console.log('Files cut:', filesToCut);
};
// Вставить файлы
const handlePaste = async () => {
if (cutFiles.length === 0) {
alert('Нет файлов для вставки');
return;
}
try {
// Перемещаем каждый файл
for (const file of cutFiles) {
await moveFile(file.path, currentPath);
}
notify('success', 'Файлы перемещены', `Перемещено файлов: ${cutFiles.length}`);
// Очищаем список вырезанных файлов
setCutFiles([]);
setSelectedFiles([]);
setSelectAll(false);
} catch (error) {
console.error('Ошибка вставки файлов:', error);
}
};
// Отмена вырезания
const handleCancelCut = () => {
setCutFiles([]);
};
return (
<div className="h-full flex flex-col bg-gray-900">
<div className="border-b border-gray-700 p-4 flex items-center justify-between">
<div className={`h-full flex flex-col ${theme.primary}`}>
{/* Header */}
<div className={`${theme.border} border-b p-4`}>
<h2 className={`text-xl font-semibold mb-4 ${theme.text}`}>Управление файлами</h2>
<div className="flex items-center gap-3">
{/* Search */}
<div className="flex-1 relative">
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 ${theme.textSecondary}`} />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Поиск по названию ф..."
className={`w-full ${theme.input} ${theme.border} border rounded-lg pl-10 pr-4 py-2 ${theme.text} placeholder:text-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500`}
/>
</div>
{/* Buttons */}
<label className={`${theme.success} ${theme.successHover} px-4 py-2 rounded-lg cursor-pointer flex items-center gap-2 text-white font-medium transition shadow-lg`}>
<Download className="w-4 h-4" />
Загрузить
<input type="file" onChange={uploadFile} className="hidden" />
</label>
<button
onClick={loadFiles}
className={`${theme.danger} ${theme.dangerHover} px-4 py-2 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg`}
>
Обновить
</button>
{/* Кнопки вырезать/вставить */}
<button
onClick={handleCut}
disabled={selectedFiles.length === 0}
className={`bg-orange-600 hover:bg-orange-700 disabled:opacity-50 disabled:cursor-not-allowed px-4 py-2 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg`}
title="Вырезать выбранные файлы"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z" />
</svg>
Вырезать {selectedFiles.length > 0 && `(${selectedFiles.length})`}
</button>
<button
onClick={handlePaste}
disabled={cutFiles.length === 0}
className={`bg-purple-600 hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed px-4 py-2 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg`}
title="Вставить файлы"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
Вставить {cutFiles.length > 0 && `(${cutFiles.length})`}
</button>
{cutFiles.length > 0 && (
<button
onClick={handleCancelCut}
className="bg-gray-600 hover:bg-gray-700 px-4 py-2 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg"
title="Отменить вырезание"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
Отмена
</button>
)}
<div className="relative new-menu-container">
<button
onClick={() => setShowNewMenu(!showNewMenu)}
className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg flex items-center gap-2 text-white font-medium transition shadow-lg"
>
Новый
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{showNewMenu && (
<div className={`absolute right-0 mt-2 w-48 ${theme.secondary} rounded-lg shadow-xl ${theme.border} border z-10`}>
<button
onClick={() => {
setCreatingNew('file');
setShowNewMenu(false);
setNewItemName('');
}}
className={`w-full text-left px-4 py-2 ${theme.hover} ${theme.text} transition rounded-t-lg`}
>
📄 Создать файл
</button>
<button
onClick={() => {
setCreatingNew('folder');
setShowNewMenu(false);
setNewItemName('');
}}
className={`w-full text-left px-4 py-2 ${theme.hover} ${theme.text} transition rounded-b-lg`}
>
📁 Создать папку
</button>
</div>
)}
</div>
</div>
</div>
{/* Path */}
<div className={`${theme.secondary} px-4 py-3 ${theme.border} border-b`}>
<div className="flex items-center gap-2">
{currentPath && (
<button
onClick={goBack}
className="bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded"
className={`${theme.hover} px-3 py-1 rounded text-sm ${theme.text} transition`}
>
Назад
</button>
)}
<span className="text-gray-400">/{currentPath || 'root'}</span>
<span className={`${theme.textSecondary} font-mono text-sm`}>
/{currentPath || ''}
</span>
{/* Индикатор вырезанных файлов */}
{cutFiles.length > 0 && (
<span className="ml-auto text-sm text-orange-400 flex items-center gap-2">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.121 14.121L19 19m-7-7l7-7m-7 7l-2.879 2.879M12 12L9.121 9.121m0 5.758a3 3 0 10-4.243 4.243 3 3 0 004.243-4.243zm0-5.758a3 3 0 10-4.243-4.243 3 3 0 004.243 4.243z" />
</svg>
Вырезано файлов: {cutFiles.length}
</span>
)}
</div>
<label className="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded cursor-pointer flex items-center gap-2">
<Upload className="w-4 h-4" />
Загрузить
<input type="file" onChange={uploadFile} className="hidden" />
</label>
</div>
{/* Table */}
<div className="flex-1 overflow-y-auto">
<table className="w-full">
<thead className="bg-gray-800 sticky top-0">
<thead className={`${theme.secondary} sticky top-0 ${theme.border} border-b`}>
<tr>
<th className="text-left p-4">Имя</th>
<th className="text-left p-4">Размер</th>
<th className="text-right p-4">Действия</th>
<th className={`text-left p-4 ${theme.textSecondary} font-medium text-sm`}>
<input
type="checkbox"
className="mr-3 cursor-pointer"
checked={selectAll}
onChange={handleSelectAll}
/>
Имя
</th>
<th className={`text-left p-4 ${theme.textSecondary} font-medium text-sm`}>Тип</th>
<th className={`text-left p-4 ${theme.textSecondary} font-medium text-sm`}>Размер</th>
<th className={`text-left p-4 ${theme.textSecondary} font-medium text-sm`}>Последнее изменение</th>
<th className={`text-left p-4 ${theme.textSecondary} font-medium text-sm`}>Разрешение</th>
<th className={`text-right p-4 ${theme.textSecondary} font-medium text-sm`}>Действия</th>
</tr>
</thead>
<tbody>
{files.map((file) => (
<tr
key={file.name}
className="border-b border-gray-800 hover:bg-gray-800"
>
<td className="p-4">
{renamingFile === file.name ? (
<div className="flex items-center gap-2">
{file.type === 'directory' ? (
<Folder className="w-5 h-5 text-blue-400" />
) : (
<File className="w-5 h-5 text-gray-400" />
)}
<input
type="text"
value={newFileName}
onChange={(e) => setNewFileName(e.target.value)}
onBlur={() => renameFile(file.name)}
onKeyDown={(e) => {
if (e.key === 'Enter') renameFile(file.name);
if (e.key === 'Escape') setRenamingFile(null);
}}
autoFocus
className="bg-gray-700 border border-gray-600 rounded px-2 py-1 text-sm focus:outline-none focus:border-blue-500"
/>
</div>
) : (
<div
className="flex items-center gap-2 cursor-pointer"
onClick={() => file.type === 'directory' && openFolder(file.name)}
onDoubleClick={() => file.type === 'file' && viewFile(file.name)}
>
{file.type === 'directory' ? (
<Folder className="w-5 h-5 text-blue-400" />
) : (
<File className="w-5 h-5 text-gray-400" />
)}
<span>{file.name}</span>
</div>
)}
</td>
<td className="p-4 text-gray-400">{formatSize(file.size)}</td>
<td className="p-4">
<div className="flex gap-2 justify-end">
{file.type === 'file' && (
<>
<button
onClick={() => viewFile(file.name)}
className="bg-blue-600 hover:bg-blue-700 p-2 rounded"
title="Просмотр"
>
<Eye className="w-4 h-4" />
</button>
<button
onClick={() => editFile(file.name)}
className="bg-purple-600 hover:bg-purple-700 p-2 rounded"
title="Редактировать"
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => downloadFile(file.name)}
className="bg-green-600 hover:bg-green-700 p-2 rounded"
title="Скачать"
>
<Download className="w-4 h-4" />
</button>
</>
{/* Форма создания нового файла/папки */}
{creatingNew && (
<tr className={`${theme.border} border-b bg-blue-900 bg-opacity-20`}>
<td className="p-4" colSpan="6">
<div className="flex items-center gap-3">
{creatingNew === 'file' ? (
<File className="w-5 h-5 text-gray-400" />
) : (
<Folder className="w-5 h-5 text-blue-400" />
)}
<input
type="text"
value={newItemName}
onChange={(e) => setNewItemName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
creatingNew === 'file' ? createNewFile() : createNewFolder();
}
if (e.key === 'Escape') {
setCreatingNew(null);
setNewItemName('');
}
}}
placeholder={creatingNew === 'file' ? 'Имя файла...' : 'Имя папки...'}
autoFocus
className={`flex-1 ${theme.input} ${theme.border} border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`}
/>
<button
onClick={() => startRename(file.name)}
className="bg-yellow-600 hover:bg-yellow-700 p-2 rounded"
title="Переименовать"
onClick={creatingNew === 'file' ? createNewFile : createNewFolder}
className="bg-green-600 hover:bg-green-700 px-4 py-2 rounded text-sm text-white transition"
>
<Edit className="w-4 h-4" />
Создать
</button>
<button
onClick={() => deleteFile(file.name)}
className="bg-red-600 hover:bg-red-700 p-2 rounded"
title="Удалить"
onClick={() => {
setCreatingNew(null);
setNewItemName('');
}}
className={`${theme.danger} ${theme.dangerHover} px-4 py-2 rounded text-sm text-white transition`}
>
<Trash2 className="w-4 h-4" />
Отмена
</button>
</div>
</td>
</tr>
))}
)}
{filteredFiles.length === 0 ? (
<tr>
<td colSpan="6" className="text-center py-12">
<div className={theme.textSecondary}>
<Folder className="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>No data</p>
</div>
</td>
</tr>
) : (
filteredFiles.map((file) => {
const isCut = cutFiles.some(f => f.name === file.name);
return (
<tr
key={file.name}
className={`${theme.border} border-b ${theme.hover} transition ${
isCut ? 'opacity-50 bg-orange-900 bg-opacity-20' : ''
}`}
>
<td className="p-4">
{renamingFile === file.name ? (
<div className="flex items-center gap-2">
<input
type="checkbox"
checked={selectedFiles.includes(file.name)}
onChange={() => handleSelectFile(file.name)}
onClick={(e) => e.stopPropagation()}
/>
{file.type === 'directory' ? (
<Folder className="w-5 h-5 text-blue-400" />
) : (
<File className="w-5 h-5 text-gray-400" />
)}
<input
type="text"
value={newFileName}
onChange={(e) => setNewFileName(e.target.value)}
onBlur={() => renameFile(file.name)}
onKeyDown={(e) => {
if (e.key === 'Enter') renameFile(file.name);
if (e.key === 'Escape') setRenamingFile(null);
}}
autoFocus
className={`${theme.input} ${theme.border} border rounded px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500`}
/>
</div>
) : (
<div
className="flex items-center gap-2 cursor-pointer"
onClick={() => file.type === 'directory' && openFolder(file.name)}
onDoubleClick={() => file.type === 'file' && viewFile(file.name)}
>
<input
type="checkbox"
checked={selectedFiles.includes(file.name)}
onChange={() => handleSelectFile(file.name)}
onClick={(e) => e.stopPropagation()}
/>
{file.type === 'directory' ? (
<Folder className="w-5 h-5 text-blue-400" />
) : (
<File className="w-5 h-5 text-gray-400" />
)}
<span className={theme.text}>{file.name}</span>
</div>
)}
</td>
<td className={`p-4 ${theme.textSecondary} text-sm`}>
{file.type === 'directory' ? 'Папка' : 'Файл'}
</td>
<td className={`p-4 ${theme.textSecondary} text-sm`}>{formatSize(file.size)}</td>
<td className={`p-4 ${theme.textSecondary} text-sm`}>-</td>
<td className={`p-4 ${theme.textSecondary} text-sm`}>-</td>
<td className="p-4">
<div className="flex gap-2 justify-end">
{file.type === 'file' && (
<>
<button
onClick={() => viewFile(file.name)}
className={`${theme.card} ${theme.hover} p-2 rounded transition`}
title="Просмотр"
>
<Eye className="w-4 h-4" />
</button>
<button
onClick={() => editFile(file.name)}
className={`${theme.card} ${theme.hover} p-2 rounded transition`}
title="Редактировать"
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => downloadFile(file.name)}
className={`${theme.card} ${theme.hover} p-2 rounded transition`}
title="Скачать"
>
<Download className="w-4 h-4" />
</button>
</>
)}
<button
onClick={() => deleteFile(file.name)}
className={`${theme.card} ${theme.hover} p-2 rounded text-red-400 transition`}
title="Удалить"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</td>
</tr>
);
})
)}
</tbody>
</table>
</div>
@@ -288,6 +685,7 @@ export default function FileManager({ serverName, token }) {
setEditingFile(viewingFile);
setViewingFile(null);
}}
theme={theme}
/>
)}
@@ -296,6 +694,7 @@ export default function FileManager({ serverName, token }) {
file={editingFile}
onClose={() => setEditingFile(null)}
onSave={saveFile}
theme={theme}
/>
)}
</div>

View File

@@ -1,30 +1,30 @@
import { X, Edit } from 'lucide-react';
export default function FileViewerModal({ file, onClose, onEdit }) {
export default function FileViewerModal({ file, onClose, onEdit, theme }) {
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-gray-800 rounded-lg w-full max-w-4xl h-[80vh] flex flex-col">
<div className="flex items-center justify-between p-4 border-b border-gray-700">
<h2 className="text-xl font-bold">{file.name}</h2>
<div className={`${theme.secondary} rounded-lg w-full max-w-4xl h-[80vh] flex flex-col ${theme.border} border`}>
<div className={`flex items-center justify-between p-4 ${theme.border} border-b`}>
<h2 className={`text-xl font-bold ${theme.text}`}>{file.name}</h2>
<div className="flex gap-2">
<button
onClick={onEdit}
className="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded flex items-center gap-2"
className="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded flex items-center gap-2 text-white transition"
>
<Edit className="w-4 h-4" />
Редактировать
</button>
<button
onClick={onClose}
className="text-gray-400 hover:text-white"
className={`${theme.textSecondary} hover:${theme.text} transition`}
>
<X className="w-6 h-6" />
</button>
</div>
</div>
<div className="flex-1 overflow-auto p-4 bg-gray-900">
<pre className="text-sm text-gray-300 font-mono whitespace-pre-wrap">
<div className={`flex-1 overflow-auto p-4 ${theme.console || theme.primary}`}>
<pre className={`text-sm ${theme.consoleText || theme.text} font-mono whitespace-pre-wrap`}>
{file.content}
</pre>
</div>

View File

@@ -0,0 +1,95 @@
import { useState, useEffect } from 'react';
import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from 'lucide-react';
export default function NotificationSystem({ theme }) {
const [notifications, setNotifications] = useState([]);
useEffect(() => {
// Слушаем события уведомлений
const handleNotification = (event) => {
const { type, title, message } = event.detail;
addNotification(type, title, message);
};
window.addEventListener('notification', handleNotification);
return () => window.removeEventListener('notification', handleNotification);
}, []);
const addNotification = (type, title, message) => {
const id = Date.now();
const notification = { id, type, title, message };
setNotifications(prev => [...prev, notification]);
// Автоматически удаляем через 5 секунд
setTimeout(() => {
removeNotification(id);
}, 5000);
};
const removeNotification = (id) => {
setNotifications(prev => prev.filter(n => n.id !== id));
};
const getIcon = (type) => {
switch (type) {
case 'success':
return <CheckCircle className="w-5 h-5" />;
case 'error':
return <AlertCircle className="w-5 h-5" />;
case 'warning':
return <AlertTriangle className="w-5 h-5" />;
case 'info':
default:
return <Info className="w-5 h-5" />;
}
};
const getColors = (type) => {
switch (type) {
case 'success':
return 'bg-green-600 border-green-500';
case 'error':
return 'bg-red-600 border-red-500';
case 'warning':
return 'bg-yellow-600 border-yellow-500';
case 'info':
default:
return 'bg-blue-600 border-blue-500';
}
};
return (
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm">
{notifications.map((notification) => (
<div
key={notification.id}
className={`${getColors(notification.type)} border-l-4 rounded-lg shadow-2xl p-4 text-white animate-slide-in-right`}
>
<div className="flex items-start gap-3">
<div className="flex-shrink-0 mt-0.5">
{getIcon(notification.type)}
</div>
<div className="flex-1 min-w-0">
<h4 className="font-semibold text-sm mb-1">{notification.title}</h4>
<p className="text-sm opacity-90">{notification.message}</p>
</div>
<button
onClick={() => removeNotification(notification.id)}
className="flex-shrink-0 hover:bg-white hover:bg-opacity-20 rounded p-1 transition"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
))}
</div>
);
}
// Вспомогательная функция для отправки уведомлений
export const notify = (type, title, message) => {
window.dispatchEvent(new CustomEvent('notification', {
detail: { type, title, message }
}));
};

View File

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { User, Lock, Server, MessageSquare, Shield, TrendingUp, Eye, EyeOff } from 'lucide-react';
import axios from 'axios';
import { API_URL } from '../config';
import { notify } from './NotificationSystem';
export default function Profile({ token, user, theme, onUsernameChange, viewingUsername }) {
const [stats, setStats] = useState(null);
@@ -66,6 +67,7 @@ export default function Profile({ token, user, theme, onUsernameChange, viewingU
// Обновляем токен
localStorage.setItem('token', data.access_token);
notify('success', 'Имя изменено', `Ваше новое имя: ${data.username}`);
alert('Имя пользователя успешно изменено!');
setUsernameForm({ new_username: '', password: '' });
@@ -76,6 +78,7 @@ export default function Profile({ token, user, theme, onUsernameChange, viewingU
loadStats();
} catch (error) {
notify('error', 'Ошибка изменения', error.response?.data?.detail || 'Не удалось изменить имя');
alert(error.response?.data?.detail || 'Ошибка изменения имени пользователя');
} finally {
setUsernameLoading(false);
@@ -111,9 +114,11 @@ export default function Profile({ token, user, theme, onUsernameChange, viewingU
{ headers: { Authorization: `Bearer ${token}` } }
);
notify('success', 'Пароль изменён', 'Ваш пароль успешно обновлен');
alert('Пароль успешно изменён!');
setPasswordForm({ old_password: '', new_password: '', confirm_password: '' });
} catch (error) {
notify('error', 'Ошибка изменения', error.response?.data?.detail || 'Не удалось изменить пароль');
alert(error.response?.data?.detail || 'Ошибка изменения пароля');
} finally {
setPasswordLoading(false);

View File

@@ -3,7 +3,7 @@ import { Cpu, HardDrive, Activity } from 'lucide-react';
import axios from 'axios';
import { API_URL } from '../config';
export default function Stats({ serverName, token }) {
export default function Stats({ serverName, token, theme }) {
const [stats, setStats] = useState({
status: 'stopped',
cpu: 0,
@@ -29,17 +29,17 @@ export default function Stats({ serverName, token }) {
};
return (
<div className="p-8 bg-gray-900">
<h2 className="text-2xl font-bold mb-6">Статистика сервера</h2>
<div className={`p-8 ${theme.primary}`}>
<h2 className={`text-2xl font-bold mb-6 ${theme.text}`}>Статистика сервера</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-gray-800 rounded-lg p-6 border border-gray-700">
<div className={`${theme.card} rounded-lg p-6 ${theme.border} border`}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold">CPU</h3>
<h3 className={`text-lg font-semibold ${theme.text}`}>CPU</h3>
<Cpu className="w-6 h-6 text-blue-400" />
</div>
<div className="text-3xl font-bold mb-2">{stats.cpu}%</div>
<div className="w-full bg-gray-700 rounded-full h-2">
<div className={`text-3xl font-bold mb-2 ${theme.text}`}>{stats.cpu}%</div>
<div className={`w-full ${theme.tertiary} rounded-full h-2`}>
<div
className="bg-blue-500 h-2 rounded-full transition-all"
style={{ width: `${Math.min(stats.cpu, 100)}%` }}
@@ -47,13 +47,13 @@ export default function Stats({ serverName, token }) {
</div>
</div>
<div className="bg-gray-800 rounded-lg p-6 border border-gray-700">
<div className={`${theme.card} rounded-lg p-6 ${theme.border} border`}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold">ОЗУ</h3>
<h3 className={`text-lg font-semibold ${theme.text}`}>ОЗУ</h3>
<Activity className="w-6 h-6 text-green-400" />
</div>
<div className="text-3xl font-bold mb-2">{stats.memory} МБ</div>
<div className="w-full bg-gray-700 rounded-full h-2">
<div className={`text-3xl font-bold mb-2 ${theme.text}`}>{stats.memory} МБ</div>
<div className={`w-full ${theme.tertiary} rounded-full h-2`}>
<div
className="bg-green-500 h-2 rounded-full transition-all"
style={{ width: `${Math.min((stats.memory / 2048) * 100, 100)}%` }}
@@ -61,27 +61,27 @@ export default function Stats({ serverName, token }) {
</div>
</div>
<div className="bg-gray-800 rounded-lg p-6 border border-gray-700">
<div className={`${theme.card} rounded-lg p-6 ${theme.border} border`}>
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold">Диск</h3>
<h3 className={`text-lg font-semibold ${theme.text}`}>Диск</h3>
<HardDrive className="w-6 h-6 text-purple-400" />
</div>
<div className="text-3xl font-bold mb-2">{stats.disk} МБ</div>
<div className="text-sm text-gray-400 mt-2">
<div className={`text-3xl font-bold mb-2 ${theme.text}`}>{stats.disk} МБ</div>
<div className={`text-sm ${theme.textSecondary} mt-2`}>
Использовано на диске
</div>
</div>
</div>
<div className="mt-8 bg-gray-800 rounded-lg p-6 border border-gray-700">
<h3 className="text-lg font-semibold mb-4">Статус</h3>
<div className={`mt-8 ${theme.card} rounded-lg p-6 ${theme.border} border`}>
<h3 className={`text-lg font-semibold mb-4 ${theme.text}`}>Статус</h3>
<div className="flex items-center gap-3">
<div
className={`w-4 h-4 rounded-full ${
stats.status === 'running' ? 'bg-green-500' : 'bg-red-500'
}`}
/>
<span className="text-xl">
<span className={`text-xl ${theme.text}`}>
{stats.status === 'running' ? 'Запущен' : 'Остановлен'}
</span>
</div>

View File

@@ -5,6 +5,7 @@ export default function ThemeSelector({ currentTheme, onThemeChange }) {
const theme = getTheme(currentTheme);
const themeColors = {
modern: 'bg-gradient-to-r from-green-600 to-emerald-600',
dark: 'bg-gray-800',
light: 'bg-gray-100',
purple: 'bg-purple-600',

View File

@@ -2,12 +2,14 @@ import { useState, useEffect, useRef } from 'react';
import { ArrowLeft, Send, Clock, AlertCircle, CheckCircle } from 'lucide-react';
import axios from 'axios';
import { API_URL } from '../config';
import { notify } from './NotificationSystem';
export default function TicketChat({ ticket, token, user, theme, onBack }) {
const [messages, setMessages] = useState(ticket.messages || []);
const [newMessage, setNewMessage] = useState('');
const [currentTicket, setCurrentTicket] = useState(ticket);
const [loading, setLoading] = useState(false);
const [previousMessagesCount, setPreviousMessagesCount] = useState(ticket.messages?.length || 0);
const messagesEndRef = useRef(null);
useEffect(() => {
@@ -29,6 +31,30 @@ export default function TicketChat({ ticket, token, user, theme, onBack }) {
const { data } = await axios.get(`${API_URL}/api/tickets/${ticket.id}`, {
headers: { Authorization: `Bearer ${token}` }
});
// Проверяем новые сообщения
if (data.messages.length > previousMessagesCount) {
const newMessagesCount = data.messages.length - previousMessagesCount;
const lastMessage = data.messages[data.messages.length - 1];
// Уведомляем только если сообщение не от текущего пользователя
if (lastMessage.author !== user.username && lastMessage.author !== 'system') {
notify('info', 'Новое сообщение', `${lastMessage.author}: ${lastMessage.text.substring(0, 50)}${lastMessage.text.length > 50 ? '...' : ''}`);
}
setPreviousMessagesCount(data.messages.length);
}
// Проверяем изменение статуса
if (data.status !== currentTicket.status) {
const statusNames = {
'pending': 'На рассмотрении',
'in_progress': 'В работе',
'closed': 'Закрыт'
};
notify('info', 'Статус изменён', `Тикет #${ticket.id}: ${statusNames[data.status]}`);
}
setCurrentTicket(data);
setMessages(data.messages || []);
} catch (error) {
@@ -49,9 +75,12 @@ export default function TicketChat({ ticket, token, user, theme, onBack }) {
);
setMessages(data.ticket.messages);
setCurrentTicket(data.ticket);
setPreviousMessagesCount(data.ticket.messages.length);
setNewMessage('');
notify('success', 'Сообщение отправлено', 'Ваше сообщение успешно отправлено');
} catch (error) {
console.error('Ошибка отправки сообщения:', error);
notify('error', 'Ошибка отправки', error.response?.data?.detail || 'Не удалось отправить сообщение');
alert(error.response?.data?.detail || 'Ошибка отправки сообщения');
} finally {
setLoading(false);
@@ -59,6 +88,12 @@ export default function TicketChat({ ticket, token, user, theme, onBack }) {
};
const changeStatus = async (newStatus) => {
const statusNames = {
'pending': 'На рассмотрении',
'in_progress': 'В работе',
'closed': 'Закрыт'
};
try {
const { data } = await axios.put(
`${API_URL}/api/tickets/${ticket.id}/status`,
@@ -67,8 +102,11 @@ export default function TicketChat({ ticket, token, user, theme, onBack }) {
);
setCurrentTicket(data.ticket);
setMessages(data.ticket.messages);
setPreviousMessagesCount(data.ticket.messages.length);
notify('success', 'Статус изменён', `Тикет #${ticket.id} теперь: ${statusNames[newStatus]}`);
} catch (error) {
console.error('Ошибка изменения статуса:', error);
notify('error', 'Ошибка изменения статуса', error.response?.data?.detail || 'Не удалось изменить статус');
alert(error.response?.data?.detail || 'Ошибка изменения статуса');
}
};

View File

@@ -4,12 +4,14 @@ import axios from 'axios';
import { API_URL } from '../config';
import TicketChat from './TicketChat';
import CreateTicketModal from './CreateTicketModal';
import { notify } from './NotificationSystem';
export default function Tickets({ token, user, theme }) {
const [tickets, setTickets] = useState([]);
const [selectedTicket, setSelectedTicket] = useState(null);
const [showCreateModal, setShowCreateModal] = useState(false);
const [loading, setLoading] = useState(true);
const [previousTickets, setPreviousTickets] = useState([]);
useEffect(() => {
loadTickets();
@@ -22,6 +24,22 @@ export default function Tickets({ token, user, theme }) {
const { data } = await axios.get(`${API_URL}/api/tickets`, {
headers: { Authorization: `Bearer ${token}` }
});
// Проверяем новые сообщения в тикетах
if (previousTickets.length > 0) {
data.forEach(ticket => {
const prevTicket = previousTickets.find(t => t.id === ticket.id);
if (prevTicket && ticket.messages.length > prevTicket.messages.length) {
const newMessage = ticket.messages[ticket.messages.length - 1];
// Уведомляем только если сообщение не от текущего пользователя
if (newMessage.author !== user.username) {
notify('info', 'Новое сообщение', `Тикет #${ticket.id}: ${newMessage.author} ответил`);
}
}
});
}
setPreviousTickets(data);
setTickets(data);
setLoading(false);
} catch (error) {
@@ -32,6 +50,7 @@ export default function Tickets({ token, user, theme }) {
const handleTicketCreated = () => {
setShowCreateModal(false);
notify('success', 'Тикет создан', 'Ваш тикет успешно создан');
loadTickets();
};

View File

@@ -20,3 +20,19 @@ body {
width: 100%;
height: 100vh;
}
/* Анимация для уведомлений */
@keyframes slide-in-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-slide-in-right {
animation: slide-in-right 0.3s ease-out;
}

View File

@@ -1,4 +1,27 @@
export const themes = {
modern: {
name: 'Современная',
gradient: 'from-green-400 to-emerald-600',
primary: 'bg-[#0f1115]',
secondary: 'bg-[#1a1d24]',
tertiary: 'bg-[#23262e]',
accent: 'bg-green-600',
accentHover: 'hover:bg-green-700',
text: 'text-gray-100',
textSecondary: 'text-gray-400',
border: 'border-gray-800',
hover: 'hover:bg-[#23262e]',
input: 'bg-[#0f1115] border-gray-700',
card: 'bg-[#1a1d24]',
cardHover: 'hover:bg-[#23262e]',
success: 'bg-green-600',
successHover: 'hover:bg-green-700',
danger: 'bg-gray-700',
dangerHover: 'hover:bg-gray-600',
warning: 'bg-yellow-600',
console: 'bg-[#0f1115]',
consoleText: 'text-gray-300',
},
dark: {
name: 'Тёмная',
gradient: 'from-blue-400 to-purple-600',

View File

@@ -1,46 +0,0 @@
# ⚡ Быстрое исправление ошибки
## Проблема решена! ✅
Ошибка `ModuleNotFoundError: No module named 'authlib.integrations.fastapi_client'` исправлена.
## Что нужно сделать:
### 1. Установите зависимости (30 секунд)
```bash
cd backend
pip install authlib==1.3.0 httpx==0.26.0
```
### 2. Запустите сервер (10 секунд)
```bash
python main.py
```
## ✅ Должно работать!
Вы увидите:
```
⚠ ZITADEL провайдер не настроен. Проверьте .env файл.
INFO: Uvicorn running on http://0.0.0.0:8000
```
Это нормально! Предупреждение исчезнет после настройки ZITADEL.
## 🚀 Что дальше?
1. **Настройте ZITADEL**`ZITADEL_QUICK_START.md`
2. **Обновите .env** → Добавьте настройки ZITADEL
3. **Перезапустите**`python main.py`
4. **Проверьте** → http://localhost:3000
## 📚 Подробнее
- **Полная инструкция:** СПРАВЛЕНИЕ_ОШИБКИ.md`
- **Документация:** `README_OIDC.md`
---
**Всё работает?** Отлично! Переходите к настройке ZITADEL! 🎉

View File

@@ -1,88 +0,0 @@
# 🚀 Быстрый старт MC Panel
## Запуск панели
### 1⃣ Запустите бэкенд
Откройте первый терминал:
```bash
cd backend
python main_new.py
```
### 2⃣ Запустите фронтенд
Откройте второй терминал:
```bash
cd frontend
npm run dev
```
### 3⃣ Откройте в браузере
```
http://localhost:3000
```
### 4⃣ Войдите в систему
- **Логин**: `Sofa12345`
- **Пароль**: `arkonsad123`
## 🎨 Смена темы
1. Нажмите на иконку палитры (🎨) в правом верхнем углу
2. Выберите тему:
- 🌑 Тёмная
- ☀️ Светлая
- 💜 Фиолетовая
- 💙 Синяя
- 💚 Зелёная
## 📋 Основные функции
### Создание сервера
1. Нажмите кнопку "+" в левой панели
2. Заполните форму:
- Имя папки (латиница, цифры, _ и -)
- Отображаемое имя
- Команда запуска
3. Нажмите "Создать"
### Управление сервером
- **Запуск**: кнопка "Запустить" на карточке сервера
- **Остановка**: кнопка "Остановить" на карточке сервера
- **Консоль**: вкладка "Консоль" для просмотра логов
- **Файлы**: вкладка "Файлы" для управления файлами
- **Статистика**: вкладка "Статистика" для мониторинга ресурсов
- **Настройки**: вкладка "Настройки" для изменения параметров
### Управление пользователями (только для админов)
1. Нажмите кнопку "Пользователи" в header
2. Создайте новых пользователей
3. Выдайте доступ к серверам
4. Назначьте роли (Пользователь, Тех. поддержка, Администратор)
### Система тикетов 🎫
1. Нажмите кнопку "Тикеты" в header
2. Создайте тикет с описанием проблемы
3. Общайтесь в чате тикета
4. Тех. поддержка и админы могут менять статусы:
- 🟡 На рассмотрении
- 🔵 В работе
- 🟢 Закрыт
### Выдача доступа к серверу
1. Выберите сервер
2. Перейдите в "Настройки"
3. В разделе "Управление доступом" выберите пользователя
4. Нажмите "Выдать доступ"
## 🌐 Доступ через сеть
Панель автоматически определяет ваш IP-адрес и работает через:
- Локальную сеть
- Radmin VPN
- Другие VPN-сети
Друзья могут подключиться по вашему IP-адресу на порту 3000.
## ✅ Готово!
Панель готова к использованию. Создавайте серверы, управляйте ими и наслаждайтесь современным интерфейсом! 🎉

View File

@@ -1,181 +1,225 @@
# ✅ MC Panel готова к использованию!
# ✅ Документация объединена!
## 🎉 Что сделано
## Что было сделано
### 1. Изменён логин администратора
- **Старый**: admin / admin
- **Новый**: Sofa12345 / arkonsad123
### 📚 Создано 3 основных файла:
### 2. Добавлена система тикетов 🎫
- Кнопка "Тикеты" в header
- Создание тикетов с темой и описанием
- Чат для общения в тикете
- Три статуса:
- 🟡 На рассмотрении
- 🔵 В работе
- 🟢 Закрыт
#### 1. **README.md** - Главная страница
Навигация по всей документации с кратким описанием проекта.
### 3. Добавлена роль "Тех. поддержка" 👨‍💻
- Доступ ко всем тикетам
- Возможность менять статусы
- Возможность отвечать на тикеты
- Отдельный бейдж в интерфейсе
**Содержит:**
- Ссылки на всю документацию
- Быстрый старт
- Основные возможности
- Структура проекта
- Информация о поддержке
### 4. Добавлен личный кабинет 👤
- Кнопка "Личный кабинет" в header рядом с "Тикеты"
- Три вкладки:
- 📊 Обзор - статистика профиля
- 👤 Имя пользователя - изменение имени
- 🔒 Пароль - изменение пароля
- Статистика по серверам и тикетам
- Список своих серверов
**Начните отсюда!** 👈
### 5. Улучшено управление пользователями
- Выпадающий список для выбора роли
- Три роли: Пользователь, Тех. поддержка, Администратор
- Цветные индикаторы ролей
---
## 🚀 Запуск панели
#### 2. **ДОКУМЕНТАЦИЯ.md** - Полная документация проекта
Вся документация проекта в одном файле (кроме API).
### Шаг 1: Запустите бэкенд
```bash
cd backend
python main.py
```
**Разделы:**
1. О проекте
2. Быстрый старт
3. Установка и настройка
4. Функциональность
5. Система уведомлений
6. Дизайн и темы
7. Файловый менеджер
8. Система тикетов
9. Личный кабинет
10. OpenID Connect
11. Роли пользователей
12. Безопасность
13. Troubleshooting
14. Дополнительная информация
15. Changelog
### Шаг 2: Запустите фронтенд
```bash
cd frontend
npm run dev
```
**Объем:** ~500 строк
### Шаг 3: Откройте в браузере
```
http://localhost:3000
```
---
### Шаг 4: Войдите как администратор
- **Логин**: none
- **Пароль**: none
#### 3. **API.md** - API документация
Вся API документация в одном файле.
## 📋 Быстрый старт
**Разделы:**
1. Базовая информация
2. Быстрый старт
3. Аутентификация (3 эндпоинта)
4. Управление пользователями (4 эндпоинта)
5. Личный кабинет (4 эндпоинта)
6. Управление серверами (10 эндпоинтов)
7. Управление файлами (9 эндпоинтов)
8. Тикеты (5 эндпоинтов)
9. OpenID Connect (3 эндпоинта)
10. Коды ошибок
11. Примеры интеграции (Python, JavaScript, cURL)
12. Postman коллекция
### Создание пользователя тех. поддержки
1. Зарегистрируйте нового пользователя
2. Войдите как админ (Sofa12345)
3. Нажмите "Пользователи"
4. Найдите нового пользователя
5. В выпадающем списке выберите "Тех. поддержка"
**Всего эндпоинтов:** 37
**Объем:** ~300 строк
### Создание тикета
1. Войдите как обычный пользователь
2. Нажмите кнопку "Тикеты" в header
3. Нажмите "Создать тикет"
4. Заполните тему и описание
5. Нажмите "Создать"
---
### Работа с тикетом (тех. поддержка)
1. Войдите как пользователь с ролью "Тех. поддержка"
2. Нажмите "Тикеты"
3. Выберите тикет из списка
4. Отвечайте на сообщения
5. Меняйте статус тикета кнопками вверху
### 🗑️ Удалено 8 старых файлов:
## 🎨 Возможности
- ❌ API_README.md
- ❌ API_QUICK_REFERENCE.md
- ❌ API_ДОКУМЕНТАЦИЯ.md
- ❌ API_DOCUMENTATION.md
- ❌ ДОКУМЕНТАЦИЯ_ГОТОВА.md
-СИСТЕМА_УВЕДОМЛЕНИЙ.md
-УВЕДОМЛЕНИЯ_ТИКЕТОВ.md
-ОБНОВЛЕНИЕ_УВЕДОМЛЕНИЙ.md
### Для всех пользователей
- ✅ Создание серверов
- ✅ Управление своими серверами
- ✅ Создание тикетов
- ✅ Общение в своих тикетах
- ✅ Смена темы интерфейса
- ✅ Личный кабинет с статистикой
- ✅ Изменение имени пользователя
- ✅ Изменение пароля
---
### Для тех. поддержки
-Все возможности пользователя
- ✅ Просмотр всех тикетов
- ✅ Ответы на любые тикеты
- ✅ Изменение статусов тикетов
### 📦 Сохранено:
### Для администраторов
-Все возможности тех. поддержки
- ✅ Управление пользователями
- ✅ Назначение ролей
- ✅ Удаление пользователей
- ✅ Управление доступом к серверам
-**MC_Panel_API.postman_collection.json** - Postman коллекция
-Все остальные технические .md файлы (история разработки)
## 📁 Структура проекта
---
## 📊 Статистика
### Было:
- 📄 12+ разрозненных .md файлов
- 🔀 Дублирование информации
- 😕 Сложная навигация
### Стало:
- 📄 3 основных файла
- ✨ Вся информация структурирована
- 🎯 Простая навигация
- 📖 Легко найти нужное
---
## 🎯 Структура документации
```
MC Panel/
├── backend/
│ ├── main.py # Основной файл бэкенда
│ ├── users.json # База пользователей
── tickets.json # База тикетов (создаётся автоматически)
│ └── servers/ # Папка с серверами
├── README.md # 👈 Начните здесь!
│ ├── Навигация
│ ├── Быстрый старт
── Ссылки на документацию
├── frontend/
── src/
├── App.jsx # Главный компонент
├── themes.js # Конфигурация тем
└── components/
├── Auth.jsx # Авторизация
├── Profile.jsx # Личный кабинет
├── Tickets.jsx # Список тикетов
├── TicketChat.jsx # Чат тикета
├── CreateTicketModal.jsx # Создание тикета
├── Users.jsx # Управление пользователями
├── Console.jsx # Консоль сервера
│ ├── FileManager.jsx # Менеджер файлов
│ ├── Stats.jsx # Статистика
│ └── ServerSettings.jsx # Настройки сервера
├── ДОКУМЕНТАЦИЯ.md # Полная документация
── О проекте
├── Установка
├── Функциональность
├── Система уведомлений
├── Файловый менеджер
├── Тикеты
├── Личный кабинет
├── OpenID Connect
├── Роли
├── Безопасность
└── Troubleshooting
── Документация/
├── ГОТОВО.md # Этот файл
├── PROFILE_SYSTEM.md # Документация личного кабинета
├── TICKETS_SYSTEM.md # Документация системы тикетов
── CHANGELOG.md # История изменений
└── БЫСТРЫЙ_СТАРТ.md # Быстрый старт
── API.md # API документация
├── Все эндпоинты (37)
├── Примеры запросов
├── Коды ошибок
── Интеграция (Python, JS, cURL)
└── MC_Panel_API.postman_collection.json # Postman коллекция
```
## 🎯 Что дальше?
---
### Тестирование
1. Создайте несколько пользователей
2. Назначьте одному роль "Тех. поддержка"
3. Создайте тикет от имени обычного пользователя
4. Ответьте на тикет от имени тех. поддержки
5. Измените статус тикета
## 🚀 Как использовать
### Настройка
1. Измените темы под свой вкус в `frontend/src/themes.js`
2. Настройте порты в конфигурации
3. Добавьте свои серверы
### Для пользователей:
1. Откройте **README.md** для обзора
2. Читайте **ДОКУМЕНТАЦИЯ.md** для изучения функций
3. Используйте **Troubleshooting** при проблемах
### Развёртывание
1. Настройте production сборку фронтенда
2. Настройте HTTPS для безопасности
3. Настройте базу данных вместо JSON файлов
4. Настройте резервное копирование
### Для разработчиков:
1. Откройте **README.md** для обзора
2. Читайте **API.md** для интеграции
3. Импортируйте **Postman коллекцию** для тестирования
## 📞 Поддержка
### Для администраторов:
1. Читайте **ДОКУМЕНТАЦИЯ.md** → Установка и настройка
2. Изучите раздел **Безопасность**
3. Настройте **OpenID Connect**
Если возникли вопросы:
1. Прочитайте `TICKETS_SYSTEM.md`
2. Прочитайте `CHANGELOG.md`
3. Создайте тикет в системе
---
## ✨ Готово!
## ✨ Преимущества новой структуры
Панель MC Panel полностью готова к использованию со всеми функциями:
- ✅ Управление серверами
- ✅ Система пользователей
- ✅ Система тикетов
- ✅ Роль тех. поддержки
### 1. Простота
- Всего 3 файла вместо 12+
- Легко найти нужную информацию
- Понятная навигация
### 2. Полнота
- Вся информация в одном месте
- Нет дублирования
- Актуальные данные
### 3. Удобство
- README с навигацией
- Разделение проект/API
- Быстрый поиск (Ctrl+F)
### 4. Поддержка
- Легко обновлять
- Легко добавлять новое
- Легко переводить
---
## 📝 Что включено
### ДОКУМЕНТАЦИЯ.md содержит:
- ✅ Быстрый старт
- ✅ Полная установка
-Все функции проекта
- ✅ Система уведомлений (полное описание)
- ✅ Дизайн и темы (6 тем)
- ✅ Файловый менеджер (все операции)
- ✅ Система тикетов (с уведомлениями)
- ✅ Личный кабинет
-5 тем оформления
-Современный интерфейс
-OpenID Connect (ZITADEL)
-Роли пользователей (4 роли)
- ✅ Безопасность (рекомендации)
- ✅ Troubleshooting (решение проблем)
- ✅ Структура БД
- ✅ Горячие клавиши
- ✅ Советы и трюки
- ✅ Roadmap
- ✅ Changelog
**Наслаждайтесь использованием! 🚀**
### API.md содержит:
-Все 37 эндпоинтов
- ✅ Примеры запросов/ответов
- ✅ Коды ошибок
- ✅ Примеры интеграции:
- Python (класс MCPanelAPI)
- JavaScript (класс MCPanelAPI)
- cURL (готовые команды)
- ✅ Postman коллекция (описание)
- ✅ Безопасность API
- ✅ Лимиты и ограничения
- ✅ Changelog
---
## 🎉 Готово к использованию!
Вся документация объединена и структурирована.
**Начните с файла README.md** для навигации по документации.
---
**Дата создания:** 15 января 2026
**Версия:** 1.0.0
**Приятного использования MC Panel!** 🎮

View File

@@ -1,139 +0,0 @@
# ✅ OpenID Connect с ZITADEL - Готово!
## 🎉 Что сделано
Интеграция OpenID Connect с ZITADEL полностью завершена!
### Основные изменения
1. **Backend**
- ✅ Настроена интеграция с ZITADEL
- ✅ Автоматическое создание пользователей при первом входе
- ✅ Обработка OAuth callback
- ✅ Генерация JWT токенов
2. **Frontend**
- ✅ Кнопка "Войти через ZITADEL" 🔐
- ✅ Автоматический вход после OAuth
- ✅ Обработка токенов
3. **Документация**
- ✅ Полная инструкция по настройке
- ✅ Быстрый старт
- ✅ Решение проблем
## 🚀 Как начать использовать
### Шаг 1: Настройте ZITADEL
1. Зайдите на [zitadel.cloud](https://zitadel.cloud)
2. Создайте приложение (Web Application, Code with PKCE)
3. Добавьте Redirect URI: `http://localhost:8000/api/auth/oidc/zitadel/callback`
4. Сохраните Client ID и Client Secret
### Шаг 2: Настройте .env
Откройте `backend/.env` и добавьте:
```bash
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@your-project
ZITADEL_CLIENT_SECRET=your-secret-here
```
### Шаг 3: Запустите
```bash
# Backend
cd backend
python main.py
# Frontend
cd frontend
npm run dev
```
### Шаг 4: Проверьте
Откройте http://localhost:3000 и увидите кнопку "Войти через ZITADEL"!
## 📚 Документация
- **`ZITADEL_QUICK_START.md`** - Быстрый старт (4 шага)
- **`OPENID_CONNECT_SETUP.md`** - Полная инструкция
- **`OIDC_CHANGES.md`** - Технические детали изменений
## 🔧 Файлы
### Изменённые
- `backend/main.py` - OAuth инициализация и endpoints
- `backend/oidc_config.py` - Конфигурация ZITADEL
- `backend/.env.example` - Пример настроек
- `frontend/src/components/Auth.jsx` - Кнопка ZITADEL
- `frontend/src/App.jsx` - Обработка callback
- `OPENID_CONNECT_SETUP.md` - Обновлённая документация
### Новые
- `ZITADEL_QUICK_START.md` - Быстрый старт
- `OIDC_CHANGES.md` - Резюме изменений
- ОТОВО_OIDC.md` - Этот файл
### Удалённые
- `frontend/src/components/AuthCallback.jsx` - Не используется
## ✨ Возможности
### Для пользователей
- 🔐 Вход через ZITADEL
- 👤 Автоматическое создание аккаунта
- 🔄 Обновление данных при каждом входе
- 🎨 Красивая кнопка входа
### Для администраторов
- 📊 Просмотр OIDC пользователей
- 🔧 Управление ролями
- 📝 История входов в логах
## 🎯 Что дальше?
Система готова к использованию! Можете:
1. **Протестировать** - Создайте тестового пользователя в ZITADEL
2. **Настроить продакшен** - Используйте HTTPS и настоящий домен
3. **Добавить функции** - Аватары, logout через ZITADEL, и т.д.
## 💡 Советы
### Для разработки
- Используйте ZITADEL Cloud (бесплатно)
- Тестируйте на localhost
- Проверяйте логи backend
### Для продакшена
- Используйте HTTPS
- Настройте правильные redirect URIs
- Регулярно обновляйте client secret
- Включите логирование
## 🆘 Проблемы?
### Кнопка не появляется
→ Проверьте `.env` файл и перезапустите backend
### Ошибка redirect_uri
→ Проверьте настройки в ZITADEL
### Ошибка invalid_client
→ Проверьте Client ID и Secret
**Подробнее в `ZITADEL_QUICK_START.md`**
## 🎊 Готово!
OpenID Connect с ZITADEL полностью настроен и работает!
Теперь пользователи могут входить через:
- ✅ Обычную форму (логин/пароль)
- ✅ ZITADEL (OpenID Connect)
**Приятного использования! 🚀**

998
ДОКУМЕНТАЦИЯ.md Normal file
View File

@@ -0,0 +1,998 @@
# MC Panel - Полная документация проекта
**Версия:** 1.0.0
**Дата:** 15 января 2026
---
## 📋 Содержание
1. [О проекте](#о-проекте)
2. [Быстрый старт](#быстрый-старт)
3. [Установка и настройка](#установка-и-настройка)
4. [Функциональность](#функциональность)
5. [Система уведомлений](#система-уведомлений)
6. [Дизайн и темы](#дизайн-и-темы)
7. [Файловый менеджер](#файловый-менеджер)
8. [Система тикетов](#система-тикетов)
9. [Личный кабинет](#личный-кабинет)
10. [OpenID Connect](#openid-connect)
11. [Роли пользователей](#роли-пользователей)
12. [Безопасность](#безопасность)
13. [Troubleshooting](#troubleshooting)
---
## О проекте
MC Panel - это современная веб-панель для управления Minecraft серверами с полным набором функций.
### Основные возможности
- 🖥️ **Управление серверами** - запуск, остановка, мониторинг
- 📁 **Файловый менеджер** - полное управление файлами сервера
- 💬 **Консоль** - отправка команд и просмотр логов в реальном времени
- 📊 **Статистика** - мониторинг CPU, RAM, диска
- 🎫 **Система тикетов** - поддержка пользователей
- 👥 **Управление пользователями** - роли и права доступа
- 🔐 **OpenID Connect** - интеграция с ZITADEL
- 🎨 **6 тем оформления** - включая современную темную тему
- 🔔 **Система уведомлений** - информирование о всех событиях
- 👤 **Личный кабинет** - управление профилем и статистика
### Технологии
**Backend:**
- FastAPI (Python)
- JWT аутентификация
- WebSocket для консоли
- Authlib для OpenID Connect
**Frontend:**
- React 18
- Tailwind CSS
- Axios для API запросов
- Lucide React для иконок
---
## Быстрый старт
### Шаг 1: Установка зависимостей
**Backend:**
```bash
cd backend
pip install -r requirements.txt
```
**Frontend:**
```bash
cd frontend
npm install
```
### Шаг 2: Настройка окружения
Создайте файл `.env` в корне проекта:
```env
# ZITADEL OpenID Connect
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=your_client_id
ZITADEL_CLIENT_SECRET=your_client_secret
# URLs
BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
```
### Шаг 3: Запуск
**Backend:**
```bash
cd backend
python main.py
```
Сервер запустится на `http://localhost:8000`
**Frontend:**
```bash
cd frontend
npm run dev
```
Интерфейс будет доступен на `http://localhost:3000`
### Шаг 4: Первый вход
1. Откройте `http://localhost:3000`
2. Зарегистрируйтесь (первый пользователь получит роль admin)
3. Создайте свой первый сервер
4. Загрузите server.jar в папку сервера
5. Запустите сервер!
**Учетные данные по умолчанию:**
- Логин: `Root`
- Пароль: `Admin`
---
## Установка и настройка
### Требования
- Python 3.8+
- Node.js 16+
- npm или yarn
- Git
### Полная установка
#### 1. Клонирование репозитория
```bash
git clone <repository-url>
cd mc-panel
```
#### 2. Установка Backend
```bash
cd backend
pip install -r requirements.txt
```
**Зависимости:**
- fastapi - веб-фреймворк
- uvicorn - ASGI сервер
- python-jose - JWT токены
- passlib - хеширование паролей
- python-multipart - загрузка файлов
- psutil - мониторинг системы
- authlib - OpenID Connect
- httpx - HTTP клиент
- python-dotenv - переменные окружения
#### 3. Установка Frontend
```bash
cd frontend
npm install
```
**Зависимости:**
- react - UI библиотека
- react-dom - рендеринг React
- axios - HTTP клиент
- lucide-react - иконки
- tailwindcss - CSS фреймворк
#### 4. Настройка ZITADEL (опционально)
Если хотите использовать OpenID Connect:
1. Создайте аккаунт на [ZITADEL](https://zitadel.com)
2. Создайте новое приложение (Application)
3. Выберите тип "Web"
4. Настройте Redirect URIs:
- `http://localhost:8000/api/auth/oidc/zitadel/callback`
5. Скопируйте Client ID и Client Secret
6. Добавьте в `.env` файл
#### 5. Структура проекта
```
mc-panel/
├── backend/
│ ├── main.py # Главный файл FastAPI
│ ├── oidc_config.py # Конфигурация OpenID Connect
│ ├── requirements.txt # Python зависимости
│ ├── users.json # База пользователей
│ ├── tickets.json # База тикетов
│ └── servers/ # Папка с серверами
├── frontend/
│ ├── src/
│ │ ├── App.jsx # Главный компонент
│ │ ├── components/ # React компоненты
│ │ ├── themes.js # Темы оформления
│ │ └── config.js # Конфигурация
│ ├── package.json # npm зависимости
│ └── vite.config.js # Конфигурация Vite
├── .env # Переменные окружения
└── ДОКУМЕНТАЦИЯ.md # Этот файл
```
---
## Функциональность
### Управление серверами
#### Создание сервера
1. Нажмите кнопку "+" в боковой панели
2. Заполните форму:
- **Имя папки** - только латиница, цифры, _ и -
- **Отображаемое имя** - любое название
- **Команда запуска** - команда для запуска сервера
3. Нажмите "Создать"
#### Запуск и остановка
- **Запустить** - зеленая кнопка "Запустить"
- **Остановить** - серая кнопка "Сброс"
- Статус отображается цветным индикатором
#### Консоль
- Просмотр логов в реальном времени
- Отправка команд серверу
- Цветная подсветка:
- 🟢 INFO - зеленый
- 🟡 WARN - желтый
- 🔴 ERROR - красный
- ⚪ Время - серый
#### Статистика
- **CPU** - использование процессора (%)
- **RAM** - использование памяти (МБ)
- **Disk** - размер файлов сервера (МБ)
- Обновление каждые 5 секунд
#### Настройки
- Изменение отображаемого имени
- Изменение команды запуска
- Удаление сервера (только админ)
---
## Система уведомлений
### Описание
Полноценная система уведомлений с автоматическим исчезновением через 5 секунд.
### Типы уведомлений
- 🟢 **Success** - успешные операции
- 🔴 **Error** - ошибки
- 🟡 **Warning** - предупреждения
- 🔵 **Info** - информационные сообщения
### Где используются
#### Управление серверами
- ✅ Сервер запущен
- Сервер остановлен
- ❌ Ошибки запуска/остановки
#### Файловый менеджер
- ✅ Файл/папка создан(а)
- ✅ Файл загружен/удален/сохранен
- ✅ Файл переименован/перемещен
- ❌ Ошибки операций
#### Тикеты
- ✅ Тикет создан
- ✅ Сообщение отправлено
- Новое сообщение (от других, каждые 3 сек)
- ✅ Статус изменён (действие)
- Статус изменён (просмотр)
- ❌ Ошибки
**Особенности тикетов:**
- Автообновление каждые 3 секунды
- Превью сообщений (50 символов)
- Не показываются для собственных действий
#### Личный кабинет
- ✅ Имя изменено
- ✅ Пароль изменён
- ❌ Ошибки
#### Создание сервера
- ✅ Сервер создан
- ❌ Ошибка создания
### Технические детали
**Компонент:** `frontend/src/components/NotificationSystem.jsx`
**Использование:**
```javascript
import { notify } from './components/NotificationSystem';
notify('success', 'Заголовок', 'Сообщение');
notify('error', 'Ошибка', 'Описание');
notify('warning', 'Внимание', 'Предупреждение');
notify('info', 'Информация', 'Сообщение');
```
**Анимация:** slide-in-right (0.3 сек)
---
## Дизайн и темы
### Доступные темы
1. **Modern (Современная)** - по умолчанию
- Цвета: #0f1115, #1a1d24, #23262e
- Акцент: зеленый
- Градиент: зеленый → изумрудный
2. **Dark (Тёмная)**
- Цвета: черный, темно-серый
- Акцент: синий
- Градиент: синий → фиолетовый
3. **Light (Светлая)**
- Цвета: белый, светло-серый
- Акцент: синий
- Градиент: синий → фиолетовый
4. **Purple (Фиолетовая)**
- Цвета: темный с фиолетовым оттенком
- Акцент: фиолетовый
- Градиент: фиолетовый → розовый
5. **Blue (Синяя)**
- Цвета: темный с синим оттенком
- Акцент: синий
- Градиент: голубой → синий
6. **Green (Зелёная)**
- Цвета: темный с зеленым оттенком
- Акцент: зеленый
- Градиент: изумрудный → зеленый
### Переключение темы
Кнопка в правом верхнем углу → выбор темы из списка
### Особенности дизайна
- Цветная консоль (INFO, WARN, ERROR)
- Кнопки с тенями и hover эффектами
- Плавные переходы и анимации
- Адаптивный дизайн
- Современные иконки (Lucide React)
---
## Файловый менеджер
### Возможности
#### Просмотр файлов
- Список файлов и папок
- 6 колонок: Имя, Тип, Размер, Изменение, Разрешение, Действия
- Поиск по названию
- Навигация по папкам
#### Создание
- **Файл** - кнопка "Новый" → "Создать файл"
- **Папка** - кнопка "Новый" → "Создать папку"
- Ввод имени и Enter
#### Загрузка и скачивание
- **Загрузить** - зеленая кнопка, выбор файла
- **Скачать** - иконка скачивания у файла
#### Редактирование
- **Просмотр** - иконка глаза
- **Редактирование** - иконка карандаша
- Сохранение изменений
#### Переименование
- Двойной клик по имени файла
- Ввод нового имени
- Enter для сохранения
#### Перемещение файлов
**Cut/Paste (Вырезать/Вставить):**
1. Выберите файлы чекбоксами
2. Нажмите "Вырезать" (оранжевая кнопка)
3. Перейдите в нужную папку
4. Нажмите "Вставить" (фиолетовая кнопка)
**Особенности:**
- Файлы подсвечиваются оранжевым
- Счетчик вырезанных файлов
- Кнопка "Отмена" для отмены операции
- Drag & Drop отключен
#### Удаление
- Иконка корзины
- Подтверждение удаления
- Удаление файлов и папок
#### Выбор файлов
- Чекбокс в заголовке - выбрать все
- Чекбоксы у файлов - выбор отдельных
- Кнопка "Обновить" - обновить список
### Интерфейс
- Поиск с иконкой
- Кнопки: Загрузить (зеленая), Обновить (серая), Новый (синяя)
- Кнопки перемещения: Вырезать (оранжевая), Вставить (фиолетовая), Отмена (серая)
- Таблица с hover эффектами
- "No data" при пустой папке
---
## Система тикетов
### Создание тикета
1. Кнопка "Тикеты" в шапке
2. Кнопка "+" для создания
3. Заполните:
- **Заголовок** - краткое описание
- **Описание** - подробности проблемы
4. Нажмите "Создать"
### Статусы тикетов
- 🟡 **На рассмотрении** (pending) - новый тикет
- 🔵 **В работе** (in_progress) - тикет взят в работу
- 🟢 **Закрыт** (closed) - проблема решена
### Работа с тикетами
#### Просмотр
- Список всех тикетов
- Фильтр по статусу (цветные индикаторы)
- Информация: автор, дата, статус
#### Чат
- Отправка сообщений
- Просмотр истории
- Автообновление каждые 3 секунды
- Уведомления о новых сообщениях
#### Изменение статуса (админ/поддержка)
- Кнопки статусов в шапке тикета
- Автоматическое системное сообщение
- Уведомление всем участникам
### Уведомления в тикетах
**При отправке сообщения:**
- ✅ "Сообщение отправлено"
**При получении сообщения:**
- "Новое сообщение: {автор}: {превью}..."
- Только от других пользователей
- Превью 50 символов
**При изменении статуса:**
- ✅ "Статус изменён: Тикет #X теперь: {статус}" (действие)
- "Статус изменён: Тикет #X: {статус}" (просмотр)
**При ошибках:**
- ❌ "Ошибка отправки" / "Ошибка изменения статуса"
### Права доступа
- **Пользователи** - видят только свои тикеты
- **Админы** - видят все тикеты, могут менять статус
- **Поддержка** - видят все тикеты, могут менять статус
---
## Личный кабинет
### Доступ
Кнопка "Личный кабинет" в правом верхнем углу
### Разделы
#### Обзор
- Имя пользователя и роль
- Статистика:
- Мои серверы (созданные)
- Доступные серверы (общие)
- Тикеты (всего, по статусам)
- Общее количество серверов
#### Безопасность
**Изменение имени пользователя:**
1. Введите новое имя (минимум 3 символа)
2. Введите текущий пароль
3. Нажмите "Изменить имя"
4. Получите новый токен
**Изменение пароля:**
1. Введите старый пароль
2. Введите новый пароль (минимум 6 символов)
3. Подтвердите новый пароль
4. Нажмите "Изменить пароль"
**Показ/скрытие паролей:**
- Иконка глаза для переключения видимости
### Просмотр чужих профилей (админ/поддержка)
Админы и техподдержка могут просматривать профили других пользователей:
- Статистика пользователя
- Список серверов
- Тикеты пользователя
- Индикатор "Просмотр профиля: {username}"
**Доступ:**
1. Раздел "Пользователи"
2. Кнопка "Профиль" у пользователя
---
## OpenID Connect
### Поддерживаемые провайдеры
- **ZITADEL** - основной провайдер
### Настройка ZITADEL
#### 1. Создание приложения
1. Зарегистрируйтесь на [ZITADEL](https://zitadel.com)
2. Создайте новый проект
3. Добавьте приложение (Application)
4. Выберите тип "Web"
5. Настройте Redirect URIs:
```
http://localhost:8000/api/auth/oidc/zitadel/callback
https://your-domain.com/api/auth/oidc/zitadel/callback
```
#### 2. Получение учетных данных
1. Скопируйте **Client ID**
2. Создайте и скопируйте **Client Secret**
3. Скопируйте **Issuer URL** (например: `https://your-instance.zitadel.cloud`)
#### 3. Настройка .env
```env
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=your_client_id_here
ZITADEL_CLIENT_SECRET=your_client_secret_here
BASE_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
```
### Использование
#### Вход через ZITADEL
1. На странице входа нажмите кнопку "ZITADEL"
2. Авторизуйтесь на странице ZITADEL
3. Разрешите доступ приложению
4. Автоматическое перенаправление в панель
#### Создание пользователя
- При первом входе автоматически создается пользователь
- Имя пользователя берется из email (до @)
- Роль: `user` (обычный пользователь)
- Пароль не требуется (используется OIDC)
#### Связывание аккаунтов
- Каждый OIDC аккаунт уникален
- Повторный вход использует существующего пользователя
- ID хранится в формате: `zitadel:{sub}`
### Безопасность
- Токены обновляются автоматически
- Используется PKCE для защиты
- Все данные передаются через HTTPS (в production)
---
## Роли пользователей
### Admin (Администратор)
**Полный доступ ко всем функциям:**
- ✅ Все серверы (создание, удаление, управление)
- ✅ Все тикеты (просмотр, изменение статуса)
- ✅ Управление пользователями (роли, доступ, удаление)
- ✅ Просмотр всех профилей
- ✅ Изменение настроек серверов
**Получение роли:**
- Первый зарегистрированный пользователь
- Назначение другим админом
### User (Пользователь)
**Стандартные права:**
- ✅ Свои серверы (создание, управление)
- ✅ Серверы с предоставленным доступом
- ✅ Свои тикеты (создание, просмотр)
- ✅ Свой профиль
- ❌ Управление другими пользователями
- ❌ Удаление серверов
- ❌ Просмотр чужих тикетов
### Support (Техподдержка)
**Права поддержки:**
- ✅ Все тикеты (просмотр, ответы, изменение статуса)
- ✅ Просмотр профилей пользователей
- ✅ Свои серверы
- ❌ Управление пользователями
- ❌ Удаление серверов
- ❌ Изменение ролей
### Banned (Заблокирован)
**Нет доступа:**
- ❌ Вход в систему запрещен
- ❌ API запросы отклоняются
- ❌ Все функции недоступны
### Изменение ролей
**Только админы могут:**
1. Раздел "Пользователи"
2. Выбрать пользователя
3. Кнопка "Изменить роль"
4. Выбрать новую роль
5. Подтвердить
**Ограничения:**
- Нельзя изменить свою роль
- Нельзя удалить себя
---
## Безопасность
### JWT Токены
- **Алгоритм:** HS256
- **Срок действия:** 7 дней
- **Хранение:** localStorage (фронтенд)
- **Передача:** Bearer Token в заголовке Authorization
### Пароли
- **Хеширование:** bcrypt
- **Минимальная длина:** 6 символов
- **Проверка:** при каждом входе
- **Изменение:** требует старый пароль
### Файловая безопасность
- Все пути проверяются на выход за пределы папки сервера
- Запрещены операции с файлами вне `servers/`
- Проверка прав доступа к серверу
### API безопасность
- Все эндпоинты требуют авторизацию (кроме login/register)
- Проверка роли для админских функций
- Валидация входных данных
- Защита от SQL injection (используется JSON)
### Рекомендации для production
#### 1. Измените SECRET_KEY
В `backend/main.py`:
```python
SECRET_KEY = "your-very-long-random-secret-key-here"
```
#### 2. Используйте HTTPS
```env
BASE_URL=https://your-domain.com
FRONTEND_URL=https://your-domain.com
```
#### 3. Настройте CORS
В `backend/main.py`:
```python
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-domain.com"], # Конкретный домен
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
```
#### 4. Используйте базу данных
Замените `users.json` и `tickets.json` на PostgreSQL/MySQL
#### 5. Настройте firewall
- Ограничьте доступ к портам
- Разрешите только необходимые IP
#### 6. Регулярные обновления
```bash
pip install --upgrade -r requirements.txt
npm update
```
#### 7. Логирование
Включите подробное логирование для отслеживания действий
#### 8. Backup
Регулярно создавайте резервные копии:
- `users.json`
- `tickets.json`
- Папка `servers/`
---
## Troubleshooting
### Проблемы с запуском
#### Backend не запускается
```bash
# Проверьте Python версию
python --version # Должно быть 3.8+
# Переустановите зависимости
pip install --upgrade -r requirements.txt
# Проверьте порт 8000
netstat -ano | findstr :8000
```
#### Frontend не запускается
```bash
# Проверьте Node.js версию
node --version # Должно быть 16+
# Очистите кэш и переустановите
rm -rf node_modules package-lock.json
npm install
# Проверьте порт 3000
netstat -ano | findstr :3000
```
### Проблемы с аутентификацией
#### "Неверный токен"
- Токен истек (7 дней)
- Измененный SECRET_KEY
- **Решение:** Выйдите и войдите снова
#### "Требуется авторизация"
- Токен не передан
- Неверный формат токена
- **Решение:** Проверьте заголовок Authorization
#### OpenID Connect не работает
```bash
# Проверьте .env файл
cat .env
# Проверьте переменные
echo $ZITADEL_ISSUER
echo $ZITADEL_CLIENT_ID
# Проверьте Redirect URI в ZITADEL
# Должен быть: http://localhost:8000/api/auth/oidc/zitadel/callback
```
### Проблемы с серверами
#### Сервер не запускается
- Проверьте наличие `server.jar`
- Проверьте команду запуска
- Проверьте логи в консоли
- Проверьте права доступа к файлам
#### Консоль пустая
- Сервер еще не запущен
- WebSocket не подключен
- **Решение:** Перезапустите сервер
#### Статистика показывает 0
- Сервер остановлен
- Процесс завершился
- **Решение:** Запустите сервер
### Проблемы с файлами
#### "Файл не найден"
- Неверный путь
- Файл удален
- Нет прав доступа
- **Решение:** Проверьте путь и права
#### Не удается загрузить файл
- Файл слишком большой
- Нет места на диске
- **Решение:** Освободите место
#### Не удается переместить файл
- Файл открыт процессом
- Нет прав доступа
- **Решение:** Остановите сервер
### Проблемы с тикетами
#### Не приходят уведомления
- Проверьте интервал обновления (3 сек)
- Откройте консоль браузера (F12)
- Проверьте ошибки JavaScript
#### Сообщения не отправляются
- Проверьте подключение к интернету
- Проверьте токен авторизации
- Проверьте логи backend
### Проблемы с производительностью
#### Медленная работа
```bash
# Проверьте использование ресурсов
# Windows:
tasklist | findstr python
tasklist | findstr node
# Проверьте количество серверов
# Каждый сервер потребляет ресурсы
```
#### Высокое использование CPU
- Много запущенных серверов
- Частые обновления статистики
- **Решение:** Остановите неиспользуемые серверы
### Логи и отладка
#### Включить подробные логи (Backend)
В `backend/main.py`:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
#### Просмотр логов (Frontend)
Откройте консоль браузера (F12) → Console
#### Проверка API
```bash
# Проверьте доступность API
curl http://localhost:8000/api/auth/oidc/providers
# Проверьте токен
curl http://localhost:8000/api/auth/me \
-H "Authorization: Bearer YOUR_TOKEN"
```
### Получение помощи
1. **Проверьте документацию** - возможно, ответ уже есть
2. **Проверьте логи** - backend и frontend
3. **Создайте тикет** - опишите проблему подробно
4. **GitHub Issues** - для багов и предложений
---
## Дополнительная информация
### Структура базы данных
#### users.json
```json
{
"username": {
"username": "string",
"password": "hashed_password",
"role": "admin|user|support|banned",
"servers": ["server1", "server2"],
"oidc_id": "provider:sub",
"email": "user@example.com",
"name": "User Name",
"picture": "https://...",
"provider": "zitadel",
"created_at": "2026-01-15T10:00:00"
}
}
```
#### tickets.json
```json
{
"1": {
"id": "1",
"title": "Проблема",
"description": "Описание",
"author": "username",
"status": "pending|in_progress|closed",
"created_at": "2026-01-15T10:00:00",
"updated_at": "2026-01-15T11:00:00",
"messages": [
{
"author": "username",
"text": "Сообщение",
"timestamp": "2026-01-15T10:00:00"
}
]
}
}
```
#### panel_config.json (в папке сервера)
```json
{
"name": "server1",
"displayName": "My Server",
"startCommand": "java -Xmx2G -jar server.jar nogui",
"owner": "username"
}
```
### Горячие клавиши
- **Ctrl + K** - Поиск (в файловом менеджере)
- **Enter** - Отправить сообщение (в тикете)
- **Esc** - Закрыть модальное окно
- **F5** - Обновить страницу
### Советы и трюки
#### Быстрое создание сервера
1. Создайте сервер через панель
2. Загрузите `server.jar` через файловый менеджер
3. Создайте `eula.txt` с содержимым `eula=true`
4. Запустите сервер
#### Массовое перемещение файлов
1. Выберите все файлы (чекбокс в заголовке)
2. Нажмите "Вырезать"
3. Перейдите в папку назначения
4. Нажмите "Вставить"
#### Мониторинг нескольких серверов
Откройте панель в нескольких вкладках браузера для одновременного мониторинга
#### Быстрый доступ к консоли
Добавьте панель в закладки браузера для быстрого доступа
### Ограничения
- **Максимальный размер файла:** зависит от настроек сервера
- **Количество серверов:** не ограничено (зависит от ресурсов)
- **Количество пользователей:** не ограничено
- **Длина сообщения в тикете:** не ограничена
- **Срок хранения логов:** 1000 последних строк
### Roadmap (Планы развития)
- [ ] Поддержка нескольких OIDC провайдеров
- [ ] Расписание запуска/остановки серверов
- [ ] Автоматические бэкапы
- [ ] Графики статистики
- [ ] Плагин-менеджер
- [ ] Мобильное приложение
- [ ] Push-уведомления
- [ ] Двухфакторная аутентификация
- [ ] Темная тема для консоли
- [ ] Экспорт логов
---
## Changelog
### Версия 1.0.0 (15.01.2026)
- ✨ Первый релиз
- ✅ Управление серверами
- ✅ Файловый менеджер
- ✅ Система тикетов
- ✅ Личный кабинет
- ✅ OpenID Connect (ZITADEL)
- ✅ 6 тем оформления
- ✅ Система уведомлений
- ✅ Управление пользователями
- ✅ Роли и права доступа
- ✅ WebSocket консоль
- ✅ Мониторинг ресурсов
---
## Лицензия
MIT License - свободное использование
---
## Контакты и поддержка
- **Документация:** Этот файл
- **API Документация:** API.md
- **Тикеты:** Используйте систему тикетов в панели
- **GitHub:** [Ссылка на репозиторий]
---
**Спасибо за использование MC Panel!** 🎮
**Версия документации:** 1.0.0
**Дата обновления:** 15 января 2026

View File

@@ -1,104 +0,0 @@
# ✅ Исправление ошибки ModuleNotFoundError
## ❌ Ошибка
```
ModuleNotFoundError: No module named 'authlib.integrations.fastapi_client'
```
## ✅ Решение
### Шаг 1: Установите зависимости
```bash
cd backend
pip install authlib==1.3.0 httpx==0.26.0
```
Или установите все зависимости сразу:
```bash
cd backend
pip install -r requirements.txt
```
### Шаг 2: Проверьте установку
```bash
python -c "from authlib.integrations.starlette_client import OAuth; print('✓ OK')"
```
Должно вывести: `✓ OK`
### Шаг 3: Запустите сервер
```bash
cd backend
python main.py
```
Должно появиться:
```
⚠ ZITADEL провайдер не настроен. Проверьте .env файл.
INFO: Started server process [12345]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000
```
## 📝 Что было исправлено
### В файле `backend/main.py`
**Было:**
```python
from authlib.integrations.fastapi_client import OAuth
```
**Стало:**
```python
from authlib.integrations.starlette_client import OAuth
```
**Причина:** FastAPI основан на Starlette, поэтому в authlib 1.3.0 используется `starlette_client`, а не `fastapi_client`.
## 🔍 Проверка
### 1. Проверьте версию authlib
```bash
pip show authlib
```
Должно быть: `Version: 1.3.0`
### 2. Проверьте импорт
```bash
python -c "from authlib.integrations.starlette_client import OAuth; print('✓ Импорт работает')"
```
### 3. Проверьте main.py
```bash
python -c "import py_compile; py_compile.compile('backend/main.py', doraise=True); print('✓ Синтаксис правильный')"
```
## ✅ Готово!
Ошибка исправлена. Теперь можете запускать сервер:
```bash
cd backend
python main.py
```
## 🚀 Следующие шаги
1. Настройте ZITADEL (см. `ZITADEL_QUICK_START.md`)
2. Обновите `.env` файл
3. Перезапустите сервер
4. Проверьте кнопку "Войти через ZITADEL"
## 📚 Дополнительная информация
- **Документация authlib:** https://docs.authlib.org/
- **FastAPI и Starlette:** https://fastapi.tiangolo.com/
- **Наша документация:** `README_OIDC.md`

View File

@@ -1,209 +0,0 @@
# 📊 Итоги работы - OpenID Connect с ZITADEL
## ✅ Задача выполнена
**Задача:** Добавить провайдера OpenID Connect ZITADEL и удалить провайдеров Google, Microsoft, Discord, GitHub
**Статус:** ✅ Полностью выполнено
## 📝 Что было сделано
### 1. Backend изменения
#### `backend/main.py`
**Изменено:**
- Упрощена инициализация OAuth (строки 28-42)
- Удалён цикл по провайдерам
- Оставлена только регистрация ZITADEL
- Добавлено логирование статуса регистрации
- Улучшена обработка callback (строки 200-230)
- Добавлена проверка наличия userinfo
- Улучшена обработка id_token
- Добавлено детальное логирование ошибок
**Результат:** Код стал проще и понятнее, работает только с ZITADEL
#### `backend/oidc_config.py`
**Изменено:**
- Удалены все провайдеры кроме ZITADEL
- Оставлена только конфигурация ZITADEL:
- `ZITADEL_ISSUER` - URL инстанса
- `ZITADEL_CLIENT_ID` - ID приложения
- `ZITADEL_CLIENT_SECRET` - Секретный ключ
**Результат:** Чистая конфигурация только для ZITADEL
#### `backend/.env.example`
**Статус:** Уже был обновлён ранее
- Содержит настройки ZITADEL
- Удалены старые провайдеры
### 2. Frontend изменения
#### `frontend/src/components/Auth.jsx`
**Статус:** Уже работает корректно
- Динамически загружает провайдеров
- Показывает кнопку ZITADEL если настроен
- Скрывает если не настроен
#### `frontend/src/App.jsx`
**Статус:** Уже работает корректно
- Обрабатывает callback от ZITADEL
- Сохраняет токен
- Очищает URL
#### `frontend/src/components/AuthCallback.jsx`
**Удалено:** ❌
- Компонент не использовался
- Логика перенесена в App.jsx
### 3. Документация
#### Обновлено
- **`OPENID_CONNECT_SETUP.md`** - Полностью переписана для ZITADEL
- Удалены инструкции для Google, Microsoft, Discord, GitHub
- Добавлена подробная инструкция для ZITADEL
- Обновлены примеры конфигурации
#### Создано
- **`ZITADEL_QUICK_START.md`** - Быстрый старт (4 шага)
- **`OIDC_CHANGES.md`** - Технические детали изменений
- **ОТОВО_OIDC.md`** - Итоговая инструкция
- **`СЛЕДУЮЩИЕАГИ.md`** - Что делать дальше
- **ТОГИ_РАБОТЫ.md`** - Этот файл
## 🔍 Проверка качества
### Синтаксис Python
```bash
✓ backend/main.py - OK
✓ backend/oidc_config.py - OK
```
### Импорты
```bash
✓ oidc_config импортируется корректно
Все зависимости на месте
```
### Структура файлов
```
backend/
├── main.py ✓
├── oidc_config.py ✓
├── .env.example ✓
└── requirements.txt ✓
frontend/
└── src/
├── App.jsx ✓
└── components/
└── Auth.jsx ✓
docs/
├── OPENID_CONNECT_SETUP.md ✓
├── ZITADEL_QUICK_START.md ✓
├── OIDC_CHANGES.md ✓
├── ГОТОВО_OIDC.md ✓
└── СЛЕДУЮЩИЕАГИ.md ✓
```
## 📊 Статистика
### Файлы
- **Изменено:** 2 файла (main.py, OPENID_CONNECT_SETUP.md)
- **Создано:** 5 файлов документации
- **Удалено:** 1 файл (AuthCallback.jsx)
### Код
- **Строк изменено:** ~50 строк
- **Строк добавлено:** ~200 строк (документация)
- **Упрощено:** OAuth инициализация (с 20 строк до 10)
### Провайдеры
- **Было:** Google, Microsoft, Discord, GitHub (4)
- **Стало:** ZITADEL (1)
- **Упрощение:** 75%
## 🎯 Результаты
### Функциональность
✅ Вход через ZITADEL работает
✅ Автоматическое создание пользователей
✅ Обработка callback
✅ Генерация JWT токенов
✅ Обновление данных пользователя
### Код
✅ Чище и проще
✅ Меньше зависимостей
✅ Лучше читаемость
✅ Подробное логирование
✅ Обработка ошибок
### Документация
✅ Полная инструкция по ZITADEL
✅ Быстрый старт
✅ Решение проблем
✅ Технические детали
✅ Примеры конфигурации
## 🚀 Готово к использованию
Система полностью готова к работе!
### Что нужно сделать пользователю:
1. Создать приложение в ZITADEL (5 минут)
2. Настроить .env файл (1 минута)
3. Запустить backend и frontend (30 секунд)
4. Протестировать вход (10 секунд)
**Общее время настройки: ~7 минут**
## 📚 Документация для пользователя
### Начало работы
**`СЛЕДУЮЩИЕАГИ.md`** - Что делать сейчас
### Быстрая настройка
**`ZITADEL_QUICK_START.md`** - 4 простых шага
### Подробная инструкция
**`OPENID_CONNECT_SETUP.md`** - Полное руководство
### Для разработчиков
**`OIDC_CHANGES.md`** - Технические детали
## ✨ Преимущества решения
### Для пользователей
- 🔐 Безопасный вход через ZITADEL
- 👤 Автоматическое создание аккаунта
- 🔄 Синхронизация данных
- 🎨 Красивый интерфейс
### Для администраторов
- 📊 Централизованное управление
- 🔧 Простая настройка
- 📝 Подробные логи
- 🛡️ Высокая безопасность
### Для разработчиков
- 💻 Чистый код
- 📚 Хорошая документация
- 🔍 Легко отлаживать
- 🚀 Легко расширять
## 🎉 Итог
**OpenID Connect с ZITADEL полностью интегрирован и готов к использованию!**
Все задачи выполнены:
- ✅ Добавлен ZITADEL провайдер
- ✅ Удалены Google, Microsoft, Discord, GitHub
- ✅ Упрощён код
- ✅ Создана документация
- ✅ Протестирована работа
**Система готова к продакшену!** 🚀

View File

@@ -1,183 +0,0 @@
# ✅ Личный кабинет готов!
## 🎉 Что добавлено
### Кнопка "Личный кабинет"
Расположена в header рядом с кнопкой "Тикеты". Доступна всем пользователям.
### Три вкладки
#### 1. 📊 Обзор
**Информация о пользователе:**
- Имя пользователя
- Роль с цветным бейджем
- Аватар
**Статистика:**
- 🖥️ **Серверы**: всего, мои, доступные
- 🎫 **Тикеты**: всего, на рассмотрении, в работе, закрыто
- 🛡️ **Роль**: название и описание прав
**Список серверов:**
- Все серверы пользователя
- Название и ID
- Красивые карточки
#### 2. 👤 Имя пользователя
**Возможности:**
- Просмотр текущего имени
- Ввод нового имени (минимум 3 символа)
- Подтверждение паролем
- Автоматический перелогин
**Что обновляется:**
- Владельцы серверов
- Доступы к серверам
- JWT токен
#### 3. 🔒 Пароль
**Возможности:**
- Ввод текущего пароля
- Ввод нового пароля (минимум 6 символов)
- Подтверждение нового пароля
- Показ/скрытие паролей (иконка глаза)
**Безопасность:**
- Проверка текущего пароля
- Проверка совпадения новых паролей
- Хеширование (bcrypt)
## 🚀 Как использовать
### Открыть личный кабинет
1. Войдите в панель
2. Нажмите кнопку "Личный кабинет" в header
3. Откроется страница с вкладками
### Посмотреть статистику
1. Откройте вкладку "Обзор"
2. Посмотрите информацию о профиле
3. Посмотрите статистику
4. Посмотрите список серверов
### Изменить имя пользователя
1. Откройте вкладку "Имя пользователя"
2. Введите новое имя (минимум 3 символа)
3. Введите текущий пароль
4. Нажмите "Изменить имя пользователя"
5. Вы будете автоматически перелогинены
⚠️ **Важно**: После изменения имени обновятся все серверы и доступы.
### Изменить пароль
1. Откройте вкладку "Пароль"
2. Введите текущий пароль
3. Введите новый пароль (минимум 6 символов)
4. Подтвердите новый пароль
5. Нажмите "Изменить пароль"
⚠️ **Важно**: После изменения пароля используйте новый пароль для входа.
## 🎨 Дизайн
### Современный интерфейс
- Вкладки с плавными переходами
- Цветные карточки статистики
- Иконки для каждого раздела
- Адаптивный дизайн
### Формы
- Валидация полей
- Показ/скрытие паролей
- Предупреждения
- Индикация загрузки
### Цветовые схемы
- Работает со всеми 5 темами
- Градиентный логотип
- Цветные бейджи ролей
## 📋 API Endpoints
### PUT /api/profile/username
Изменить имя пользователя
```bash
curl -X PUT http://localhost:8000/api/profile/username \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"new_username": "NewUsername",
"password": "current_password"
}'
```
### PUT /api/profile/password
Изменить пароль
```bash
curl -X PUT http://localhost:8000/api/profile/password \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"old_password": "old_password",
"new_password": "new_password"
}'
```
### GET /api/profile/stats
Получить статистику профиля
```bash
curl -X GET http://localhost:8000/api/profile/stats \
-H "Authorization: Bearer YOUR_TOKEN"
```
## 🔐 Безопасность
### Изменение имени
1. ✅ Проверка пароля
2. ✅ Проверка уникальности
3. ✅ Обновление владельцев серверов
4. ✅ Обновление доступов
5. ✅ Новый JWT токен
### Изменение пароля
1. ✅ Проверка текущего пароля
2. ✅ Проверка длины (минимум 6 символов)
3. ✅ Хеширование (bcrypt)
## ✅ Готово!
Личный кабинет полностью интегрирован в MC Panel. Все пользователи могут:
- 📊 Просматривать статистику
- 👤 Изменять имя пользователя
- 🔒 Изменять пароль
- 🖥️ Видеть свои серверы
- 🎫 Видеть статистику по тикетам
### Тестирование
1. **Войдите в панель**
- Логин: Sofa12345
- Пароль: arkonsad123
2. **Откройте личный кабинет**
- Нажмите кнопку "Личный кабинет"
3. **Посмотрите статистику**
- Вкладка "Обзор"
4. **Измените имя (опционально)**
- Вкладка "Имя пользователя"
- Введите новое имя и пароль
5. **Измените пароль (опционально)**
- Вкладка "Пароль"
- Введите старый и новый пароли
## 🎯 Что дальше?
Система личного кабинета готова к использованию. Теперь пользователи могут полностью управлять своим профилем!
**Наслаждайтесь! 🚀**

View File

@@ -1,123 +0,0 @@
# ✅ РЕЗЮМЕ - OpenID Connect готов!
## 🎯 Задача
Добавить провайдера OpenID Connect **ZITADEL** и удалить провайдеров Google, Microsoft, Discord, GitHub.
## ✅ Выполнено
Задача **полностью выполнена**! Система готова к использованию.
## 📋 Что сделано
### Backend
- ✅ Упрощена инициализация OAuth (только ZITADEL)
- ✅ Улучшена обработка callback
- ✅ Добавлено логирование
- ✅ Обработка ошибок
### Frontend
- ✅ Кнопка "Войти через ZITADEL" 🔐
- ✅ Автоматический вход после OAuth
- ✅ Обработка токенов
### Документация
- ✅ Быстрый старт (4 шага)
- ✅ Подробная инструкция
- ✅ Технические детали
- ✅ Схема работы
- ✅ Решение проблем
## 📁 Файлы
### Изменено
- `backend/main.py` - OAuth инициализация
- `OPENID_CONNECT_SETUP.md` - Обновлена для ZITADEL
### Создано
- `ZITADEL_QUICK_START.md` - Быстрый старт
- `OIDC_CHANGES.md` - Технические детали
- ОТОВО_OIDC.md` - Итоговая инструкция
- `СЛЕДУЮЩИЕАГИ.md` - Что делать дальше
- ТОГИ_РАБОТЫ.md` - Полный отчёт
- `СХЕМА_РАБОТЫ.md` - Визуальная схема
- `РЕЗЮМЕ.md` - Этот файл
### Удалено
- `frontend/src/components/AuthCallback.jsx` - Не используется
## 🚀 Как использовать
### 1. Настройте ZITADEL (5 минут)
```
1. Зайти на zitadel.cloud
2. Создать приложение (Web, Code with PKCE)
3. Добавить Redirect URI
4. Скопировать Client ID и Secret
```
### 2. Обновите .env (1 минута)
```bash
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@your-project
ZITADEL_CLIENT_SECRET=your-secret-here
```
### 3. Запустите (30 секунд)
```bash
# Backend
cd backend
python main.py
# Frontend
cd frontend
npm run dev
```
### 4. Проверьте (10 секунд)
Откройте http://localhost:3000 → Кнопка "Войти через ZITADEL" 🔐
## 📚 Документация
| Файл | Описание |
|------|----------|
| `СЛЕДУЮЩИЕАГИ.md` | Что делать сейчас |
| `ZITADEL_QUICK_START.md` | Быстрый старт (4 шага) |
| `OPENID_CONNECT_SETUP.md` | Подробная инструкция |
| `OIDC_CHANGES.md` | Технические детали |
| `СХЕМА_РАБОТЫ.md` | Визуальная схема |
| ТОГИ_РАБОТЫ.md` | Полный отчёт |
## ✨ Результат
### Функциональность
- ✅ Вход через ZITADEL
- ✅ Автоматическое создание пользователей
- ✅ Обновление данных при входе
- ✅ JWT токены
- ✅ Безопасность (PKCE, state, nonce)
### Код
- ✅ Чище и проще
- ✅ Меньше зависимостей
- ✅ Лучше читаемость
- ✅ Подробное логирование
### Документация
- ✅ 7 файлов документации
- ✅ Быстрый старт
- ✅ Подробные инструкции
- ✅ Визуальные схемы
- ✅ Решение проблем
## 🎉 Готово!
**OpenID Connect с ZITADEL полностью интегрирован!**
Система готова к использованию. Следуйте инструкциям в `СЛЕДУЮЩИЕАГИ.md`.
---
**Время настройки:** ~7 минут
**Сложность:** Низкая
**Статус:** ✅ Готово к продакшену

View File

@@ -1,146 +0,0 @@
# 📋 Следующие шаги - OpenID Connect готов!
## ✅ Что сделано
Интеграция OpenID Connect с ZITADEL **полностью завершена**!
## 🎯 Что нужно сделать сейчас
### 1. Настроить ZITADEL (5 минут)
```
1. Зайти на zitadel.cloud
2. Создать приложение (Web, Code with PKCE)
3. Добавить Redirect URI: http://localhost:8000/api/auth/oidc/zitadel/callback
4. Скопировать Client ID и Secret
```
### 2. Обновить .env файл (1 минута)
Откройте `backend/.env` и добавьте:
```bash
ZITADEL_ISSUER=https://your-instance.zitadel.cloud
ZITADEL_CLIENT_ID=123456789012345678@your-project
ZITADEL_CLIENT_SECRET=your-secret-here
```
### 3. Запустить (30 секунд)
```bash
# Backend
cd backend
python main.py
# Frontend (новый терминал)
cd frontend
npm run dev
```
### 4. Проверить (10 секунд)
Откройте http://localhost:3000 → Увидите кнопку "Войти через ZITADEL" 🔐
## 📚 Документация
### Быстрый старт
**`ZITADEL_QUICK_START.md`** - 4 простых шага
### Подробная инструкция
**`OPENID_CONNECT_SETUP.md`** - Полное руководство
### Технические детали
**`OIDC_CHANGES.md`** - Что изменилось в коде
## 🔍 Проверка работы
### Backend
```bash
cd backend
python main.py
```
Должно появиться:
```
✓ ZITADEL провайдер зарегистрирован: https://your-instance.zitadel.cloud
```
Если видите:
```
⚠ ZITADEL провайдер не настроен. Проверьте .env файл.
```
→ Проверьте настройки в `.env`
### Frontend
Откройте http://localhost:3000
Должна быть кнопка:
```
🔐 Войти через ZITADEL
```
Если кнопки нет:
- Проверьте что backend запущен
- Проверьте консоль браузера (F12)
- Проверьте `.env` файл
## 🎨 Как выглядит
### Страница входа
```
┌─────────────────────────────────┐
│ MC Panel Logo │
│ │
│ ┌───────────────────────────┐ │
│ │ Имя пользователя │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────────────────────┐ │
│ │ Пароль │ │
│ └───────────────────────────┘ │
│ │
│ ┌───────────────────────────┐ │
│ │ Войти │ │
│ └───────────────────────────┘ │
│ │
│ ─── Или войдите через ─── │
│ │
│ ┌───────────────────────────┐ │
│ │ 🔐 Войти через ZITADEL │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
```
## 🔄 Процесс входа
1. Пользователь нажимает "Войти через ZITADEL"
2. Перенаправление на ZITADEL
3. Пользователь вводит логин/пароль в ZITADEL
4. ZITADEL возвращает на панель
5. Автоматическое создание пользователя
6. Автоматический вход в систему
**Всё происходит автоматически!**
## 🎁 Бонусы
### Для пользователей
-Не нужно запоминать ещё один пароль
- ✅ Безопасный вход через ZITADEL
- ✅ Можно включить 2FA в ZITADEL
### Для администраторов
- ✅ Централизованное управление пользователями
- ✅ Автоматическое создание аккаунтов
- ✅ Логи входов
## 🚀 Готово к использованию!
Система полностью настроена и готова к работе.
**Следующий шаг:** Настройте ZITADEL и протестируйте вход!
---
**Нужна помощь?** Смотрите `ZITADEL_QUICK_START.md`

View File

@@ -1,282 +0,0 @@
# 🔄 Схема работы OpenID Connect с ZITADEL
## Визуальная схема
```
┌─────────────┐
│ Пользователь│
└──────┬──────┘
│ 1. Открывает страницу входа
┌─────────────────────────────────────┐
│ Frontend (React) │
│ http://localhost:3000 │
│ │
│ ┌───────────────────────────────┐ │
│ │ Форма входа │ │
│ │ - Логин/Пароль │ │
│ │ - Кнопка ZITADEL 🔐 │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 2. Нажимает "Войти через ZITADEL"
│ GET /api/auth/oidc/zitadel/login
┌─────────────────────────────────────┐
│ Backend (FastAPI) │
│ http://localhost:8000 │
│ │
│ ┌───────────────────────────────┐ │
│ │ OAuth Client │ │
│ │ - Создаёт authorize URL │ │
│ │ - Добавляет state, nonce │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 3. Redirect на ZITADEL
┌─────────────────────────────────────┐
│ ZITADEL │
│ https://your-instance.zitadel.cloud│
│ │
│ ┌───────────────────────────────┐ │
│ │ Страница входа │ │
│ │ - Email/Username │ │
│ │ - Password │ │
│ │ - 2FA (опционально) │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 4. Пользователь вводит данные
│ 5. ZITADEL проверяет
│ 6. Redirect с code
┌─────────────────────────────────────┐
│ Backend (FastAPI) │
│ /api/auth/oidc/zitadel/callback │
│ │
│ ┌───────────────────────────────┐ │
│ │ 7. Обмен code на token │ │
│ │ POST /oauth/token │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ 8. Получение userinfo │ │
│ │ - email │ │
│ │ - name │ │
│ │ - sub (user ID) │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ 9. Создание/обновление │ │
│ │ пользователя в users.json │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ 10. Генерация JWT токена │ │
│ │ для MC Panel │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 11. Redirect на frontend
│ ?token=xxx&username=yyy
┌─────────────────────────────────────┐
│ Frontend (React) │
│ │
│ ┌───────────────────────────────┐ │
│ │ 12. Обработка callback │ │
│ │ - Извлечение token │ │
│ │ - Сохранение в localStorage│ │
│ │ - Очистка URL │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ 13. Автоматический вход │ │
│ │ - Загрузка данных │ │
│ │ - Показ панели │ │
│ └───────────────────────────────┘ │
└──────────────┬──────────────────────┘
│ 14. Пользователь в системе!
┌─────────────────────────────────────┐
│ MC Panel Dashboard │
│ - Серверы │
│ - Тикеты │
│ - Личный кабинет │
└─────────────────────────────────────┘
```
## Детальный поток данных
### Шаг 1-2: Инициация входа
```
Пользователь → Frontend
Frontend → Backend: GET /api/auth/oidc/zitadel/login
Backend создаёт OAuth URL:
- client_id
- redirect_uri
- scope: openid email profile
- state (CSRF защита)
- nonce (replay защита)
```
### Шаг 3-6: Аутентификация в ZITADEL
```
Backend → ZITADEL: Redirect на /oauth/authorize
ZITADEL показывает форму входа
Пользователь вводит данные
ZITADEL проверяет учётные данные
ZITADEL → Backend: Redirect с code
URL: /callback?code=xxx&state=yyy
```
### Шаг 7-8: Получение данных
```
Backend → ZITADEL: POST /oauth/token
Параметры:
- code
- client_id
- client_secret
- redirect_uri
ZITADEL → Backend: access_token + id_token
Backend извлекает userinfo:
{
"sub": "123456789012345678",
"email": "user@example.com",
"name": "John Doe",
"picture": "https://..."
}
```
### Шаг 9-10: Создание пользователя
```
Backend проверяет users.json:
- Ищет по oidc_id = "zitadel:123456789012345678"
Если найден:
- Обновляет email, name, picture
Если не найден:
- Создаёт нового пользователя
- Генерирует username из email
- Роль: "user"
- Пустой пароль (OIDC пользователь)
Backend создаёт JWT токен:
{
"sub": "john_doe",
"role": "user",
"exp": 1234567890
}
```
### Шаг 11-14: Возврат в приложение
```
Backend → Frontend: Redirect
URL: http://localhost:3000/?token=xxx&username=yyy
Frontend (useEffect):
- Извлекает token и username из URL
- Сохраняет в localStorage
- Очищает URL (history.replaceState)
- Обновляет состояние (setToken, setUser)
Frontend загружает данные:
- GET /api/auth/me (проверка токена)
- GET /api/servers (список серверов)
Пользователь видит панель управления
```
## Структура данных
### ZITADEL userinfo
```json
{
"sub": "123456789012345678",
"email": "user@example.com",
"email_verified": true,
"name": "John Doe",
"given_name": "John",
"family_name": "Doe",
"picture": "https://avatar.url",
"locale": "en"
}
```
### MC Panel user (users.json)
```json
{
"john_doe": {
"username": "john_doe",
"password": "",
"role": "user",
"servers": [],
"oidc_id": "zitadel:123456789012345678",
"email": "user@example.com",
"name": "John Doe",
"picture": "https://avatar.url",
"provider": "zitadel",
"created_at": "2026-01-15T12:00:00"
}
}
```
### JWT токен (MC Panel)
```json
{
"sub": "john_doe",
"role": "user",
"exp": 1737820800,
"iat": 1737216000
}
```
## Безопасность
### Защита от атак
1. **CSRF (Cross-Site Request Forgery)**
- State parameter проверяется
- Случайное значение для каждого запроса
2. **Replay атаки**
- Nonce в id_token
- Одноразовое использование code
3. **Man-in-the-Middle**
- HTTPS обязателен в продакшене
- Проверка redirect_uri
4. **Token theft**
- JWT с истечением (7 дней)
- Хранение в localStorage (XSS защита нужна)
### Рекомендации для продакшена
```
✓ Использовать HTTPS
✓ Настроить CORS правильно
✓ Добавить rate limiting
✓ Логировать все входы
✓ Мониторить подозрительную активность
✓ Регулярно обновлять client_secret
✓ Использовать refresh tokens
```
## Готово! 🎉
Схема показывает полный цикл аутентификации через ZITADEL.
**Всё работает автоматически и безопасно!** 🔐