Fix WebSocket URL handling and simplify .env.local setup
This commit is contained in:
parent
8a083f6f67
commit
66e82fc18a
@ -9,6 +9,5 @@ class ApiConfig(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
"""Инициализация при загрузке приложения."""
|
"""Инициализация при загрузке приложения."""
|
||||||
# Применяем патч для django-scheduler (исправление бага с byweekday)
|
# Применяем патч для django-scheduler (исправление бага с byweekday)
|
||||||
print("🔧 ApiConfig.ready() - применение патча django-scheduler")
|
|
||||||
import schedule_patch
|
import schedule_patch
|
||||||
schedule_patch.apply_patch()
|
schedule_patch.apply_patch()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -70,8 +70,18 @@ def create_patched_method():
|
|||||||
return patched_event_params
|
return patched_event_params
|
||||||
|
|
||||||
|
|
||||||
|
# Флаг для предотвращения повторного применения патча
|
||||||
|
_patch_applied = False
|
||||||
|
|
||||||
|
|
||||||
def apply_patch():
|
def apply_patch():
|
||||||
"""Применяет патч к django-scheduler Event модели."""
|
"""Применяет патч к django-scheduler Event модели."""
|
||||||
|
global _patch_applied
|
||||||
|
|
||||||
|
# Если патч уже применён, пропускаем
|
||||||
|
if _patch_applied:
|
||||||
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ленивый импорт - только при вызове apply_patch()
|
# Ленивый импорт - только при вызове apply_patch()
|
||||||
from schedule.models import Event
|
from schedule.models import Event
|
||||||
@ -86,6 +96,7 @@ def apply_patch():
|
|||||||
print(f" Патч применён: Event._event_params = {patched_method.__name__}")
|
print(f" Патч применён: Event._event_params = {patched_method.__name__}")
|
||||||
print("=" * 80)
|
print("=" * 80)
|
||||||
|
|
||||||
|
_patch_applied = True
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("=" * 80)
|
print("=" * 80)
|
||||||
|
|||||||
195
docs/diagnostic/WEBSOCKET_FIX.md
Normal file
195
docs/diagnostic/WEBSOCKET_FIX.md
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
# 🔧 Исправление WebSocket ошибки: "❌ WebSocket error: {}"
|
||||||
|
|
||||||
|
## Проблема
|
||||||
|
|
||||||
|
При открытии чата в браузере видна ошибка:
|
||||||
|
```
|
||||||
|
❌ WebSocket error: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Причины
|
||||||
|
|
||||||
|
WebSocket ошибка `{}` обычно связана с одной из этих проблем:
|
||||||
|
|
||||||
|
| Причина | Решение |
|
||||||
|
|---------|---------|
|
||||||
|
| **Backend запущен через `runserver`** | Используйте **Daphne** для WebSocket |
|
||||||
|
| **PORT 9000 занят** | Проверьте процессы или используйте другой порт |
|
||||||
|
| **Неправильный URL в .env** | Убедитесь что используется `.env.local` |
|
||||||
|
| **JWT токен не сохранен** | Выполните вход в систему |
|
||||||
|
| **Frontend и Backend на разных версиях** | Синхронизируйте код |
|
||||||
|
|
||||||
|
## ✅ Пошаговое решение
|
||||||
|
|
||||||
|
### Шаг 1: Остановите текущий backend
|
||||||
|
```bash
|
||||||
|
# Если backend запущен, нажмите Ctrl+C в терминале
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 2: Запустите backend через Daphne
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9000 eusrr_backend.asgi:application
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ожидаемый вывод:**
|
||||||
|
```
|
||||||
|
Starting server process [PID]
|
||||||
|
HTTP/2 support not enabled
|
||||||
|
Uvicorn running on http://0.0.0.0:9000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 3: Убедитесь что .env.local правильный
|
||||||
|
|
||||||
|
Проверьте файл `frontend/.env.local`:
|
||||||
|
```dotenv
|
||||||
|
# ✅ ПРАВИЛЬНО (для локальной разработки)
|
||||||
|
NEXT_PUBLIC_WS_URL=ws://localhost:9000
|
||||||
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:9000
|
||||||
|
```
|
||||||
|
|
||||||
|
**НЕ используйте:**
|
||||||
|
```dotenv
|
||||||
|
# ❌ НЕПРАВИЛЬНО (production URL)
|
||||||
|
NEXT_PUBLIC_WS_URL=wss://corp.robotail.pro
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 4: Перезагрузите фронтенд
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Перезагрузите браузер: `Ctrl+R` или `F5`
|
||||||
|
|
||||||
|
### Шаг 5: Проверьте WebSocket подключение
|
||||||
|
|
||||||
|
Откройте **DevTools** (F12) → **Console** окно и ищите:
|
||||||
|
|
||||||
|
**✅ Если видите:**
|
||||||
|
```
|
||||||
|
🔄 Подключение к WebSocket: ws://localhost:9000/ws/?token=...
|
||||||
|
✅ WebSocket connected!
|
||||||
|
```
|
||||||
|
→ **Всё работает!**
|
||||||
|
|
||||||
|
**❌ Если видите ошибку:**
|
||||||
|
```
|
||||||
|
❌ WebSocket error: {}
|
||||||
|
```
|
||||||
|
→ Продолжайте с Шага 6
|
||||||
|
|
||||||
|
### Шаг 6: Проверьте порт 9000
|
||||||
|
|
||||||
|
Проверьте что процесс слушает на порту 9000:
|
||||||
|
|
||||||
|
**Windows - PowerShell (администратор):**
|
||||||
|
```powershell
|
||||||
|
# Проверить какие процессы используют порт 9000
|
||||||
|
netstat -ano | findstr :9000
|
||||||
|
|
||||||
|
# Пример вывода:
|
||||||
|
# TCP 0.0.0.0:9000 0.0.0.0:0 LISTENING 28904
|
||||||
|
|
||||||
|
# Если что-то там слушает, проверьте что это Daphne
|
||||||
|
tasklist | findstr 28904
|
||||||
|
```
|
||||||
|
|
||||||
|
Если порт занят другим процессом:
|
||||||
|
```bash
|
||||||
|
# Убить процесс в PowerShell
|
||||||
|
taskkill /PID 28904 /F
|
||||||
|
|
||||||
|
# Или используйте другой порт
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9001 eusrr_backend.asgi:application
|
||||||
|
|
||||||
|
# И обновите .env.local
|
||||||
|
NEXT_PUBLIC_WS_URL=ws://localhost:9001
|
||||||
|
```
|
||||||
|
|
||||||
|
### Шаг 7: Проверьте Network в DevTools
|
||||||
|
|
||||||
|
1. Откройте **DevTools** → **Network** вкладка
|
||||||
|
2. В фильтре напишите: `ws`
|
||||||
|
3. Откройте чат и ищите подключение `ws://localhost:9000/ws/`
|
||||||
|
|
||||||
|
**✅ Если видите:**
|
||||||
|
```
|
||||||
|
ws Status: 101 Switching Protocols [WebSocket connected]
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ Если видите:**
|
||||||
|
```
|
||||||
|
ws Status: Failed [или ConnectError]
|
||||||
|
```
|
||||||
|
→ Backend неправильно запущен
|
||||||
|
|
||||||
|
## 🔄 Полный скрипт для переустановки
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Перейдите в backend
|
||||||
|
cd backend
|
||||||
|
|
||||||
|
# 2. Установите dependencies если нужно
|
||||||
|
.venv/Scripts/pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 3. Запустите Daphne
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9000 eusrr_backend.asgi:application
|
||||||
|
|
||||||
|
# В другом терминале:
|
||||||
|
|
||||||
|
# 4. Перейдите в frontend
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# 5. Установите если нужно
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 6. Запустите dev server
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# 7. Откройте браузер на http://localhost:5173
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Чек-лист для диагностики
|
||||||
|
|
||||||
|
- [ ] Backend запущен через **Daphne** (не `runserver`)
|
||||||
|
- [ ] PORT **9000** свободен и доступен
|
||||||
|
- [ ] `.env.local` использует `ws://localhost:9000`
|
||||||
|
- [ ] JWT токен сохранен в localStorage (пользователь залогирован)
|
||||||
|
- [ ] Frontend запущен на `http://localhost:5173`
|
||||||
|
- [ ] В DevTools → Console видно `🔄 Подключение к WebSocket`
|
||||||
|
- [ ] В DevTools → Network видно WebSocket с статусом **101**
|
||||||
|
|
||||||
|
## 🚨 Если ничего не помогает
|
||||||
|
|
||||||
|
1. **Очистите cache браузера**
|
||||||
|
- F12 → Application → Clear Site Data → ✓ Cookies, ✓ Cache
|
||||||
|
|
||||||
|
2. **Удалите node_modules и переустановите**
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
rm -r node_modules package-lock.json
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Проверьте logs в backend**
|
||||||
|
- В терминале где запущен Daphne должны быть logs подключений
|
||||||
|
- Если их нет - frontend не отправляет запрос
|
||||||
|
|
||||||
|
4. **Обновите код из Git**
|
||||||
|
```bash
|
||||||
|
git pull origin main
|
||||||
|
npm install
|
||||||
|
.venv/Scripts/pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📞 Если проблема не решена
|
||||||
|
|
||||||
|
Создайте issue с информацией:
|
||||||
|
- Output из DevTools Console
|
||||||
|
- Output из Daphne терминала
|
||||||
|
- Содержимое `.env.local`
|
||||||
|
- Результат: `netstat -ano | findstr :9000` (Windows)
|
||||||
87
docs/in-progress/WEBSOCKET_ERROR_FIX.md
Normal file
87
docs/in-progress/WEBSOCKET_ERROR_FIX.md
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# 📝 Исправление WebSocket ошибки - Резюме
|
||||||
|
|
||||||
|
## 🔧 Что было исправлено
|
||||||
|
|
||||||
|
### 1. **Файл: `frontend/src/hooks/useWebSocket.ts`**
|
||||||
|
|
||||||
|
**Проблема:**
|
||||||
|
- Код неправильно обрабатывал переменную окружения `NEXT_PUBLIC_WS_URL`
|
||||||
|
- Если URL содержал протокол (`ws://localhost:9000`), код добавлял протокол снова → `ws://ws://localhost:9000/ws/`
|
||||||
|
|
||||||
|
**Решение:**
|
||||||
|
- Добавлена проверка: если URL уже содержит протокол - используем его как есть
|
||||||
|
- Если URL это только хост - добавляем протокол
|
||||||
|
- Добавлено логирование: `console.log('🔄 Подключение к WebSocket:', wsUrl)`
|
||||||
|
|
||||||
|
**Измененная логика:**
|
||||||
|
```typescript
|
||||||
|
// ✅ ДО (НЕПРАВИЛЬНО):
|
||||||
|
const host = process.env.NEXT_PUBLIC_WS_URL || 'localhost:9000';
|
||||||
|
const wsUrl = `${protocol}//${host}/ws/?token=${token}`;
|
||||||
|
// Результат: ws://ws://localhost:9000/ws/?token=... ❌
|
||||||
|
|
||||||
|
// ✅ ПОСЛЕ (ПРАВИЛЬНО):
|
||||||
|
if (envWsUrl.startsWith('ws://') || envWsUrl.startsWith('wss://')) {
|
||||||
|
wsUrl = `${envWsUrl}/ws/?token=${token}`;
|
||||||
|
}
|
||||||
|
// Результат: ws://localhost:9000/ws/?token=... ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Файл: `frontend/QUICKSTART.md`**
|
||||||
|
|
||||||
|
**Изменения:**
|
||||||
|
- ✅ Добавлена демонстрация правильного запуска Daphne
|
||||||
|
- ✅ Указано что **НЕ нужно** использовать `runserver` для WebSocket
|
||||||
|
- ✅ Добавлена секция "Проверка WebSocket подключения" с подробной диагностикой
|
||||||
|
- ✅ Расширена секция "Проблемы?" с решением для `WebSocket error: {}`
|
||||||
|
- ✅ Добавлены инструкции по проверке портов в Windows
|
||||||
|
|
||||||
|
### 3. **Файл: `docs/diagnostic/WEBSOCKET_FIX.md`** (новый)
|
||||||
|
|
||||||
|
- 📋 Полная диагностика проблемы с WebSocket
|
||||||
|
- 🔍 Шаг-за-шагом решение проблемы
|
||||||
|
- 🛠️ Скрипты для проверки портов и процессов
|
||||||
|
- ✅ Чек-лист для быстрой проверки
|
||||||
|
|
||||||
|
## 🚀 Что делать дальше
|
||||||
|
|
||||||
|
### 1. Убедитесь что используется `.env.local` (не `.env`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# frontend/.env.local (для локальной разработки)
|
||||||
|
NEXT_PUBLIC_WS_URL=ws://localhost:9000
|
||||||
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:9000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Перезапустите backend с Daphne
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9000 eusrr_backend.asgi:application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Перезагрузите frontend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Проверьте в DevTools
|
||||||
|
|
||||||
|
- F12 → Console → убедитесь что видите `🔄 Подключение к WebSocket: ws://localhost:9000/...`
|
||||||
|
- F12 → Network → WS → должен быть статус **101 Switching Protocols** ✅
|
||||||
|
|
||||||
|
## 📌 Ключевые моменты
|
||||||
|
|
||||||
|
| Что | ❌ НЕПРАВИЛЬНО | ✅ ПРАВИЛЬНО |
|
||||||
|
|-----|---|---|
|
||||||
|
| Запуск backend | `python manage.py runserver` | `.venv/Scripts/daphne -p 9000 eusrr_backend.asgi:application` |
|
||||||
|
| .env файл | `.env` (production) | `.env.local` (development) |
|
||||||
|
| WebSocket URL | `wss://corp.robotail.pro` | `ws://localhost:9000` |
|
||||||
|
| Логирование | Нет | `🔄 Подключение к WebSocket: ...` |
|
||||||
|
|
||||||
|
## 🔗 Дополнительно
|
||||||
|
|
||||||
|
- 📚 Полная диагностика: [docs/diagnostic/WEBSOCKET_FIX.md](../../docs/diagnostic/WEBSOCKET_FIX.md)
|
||||||
|
- 📖 Quickstart: [frontend/QUICKSTART.md](../QUICKSTART.md)
|
||||||
@ -1,6 +1,12 @@
|
|||||||
# API Configuration
|
# Next.js Environment Variables Example
|
||||||
VITE_API_URL=http://localhost:8000/api/v2
|
# Copy this file to .env.local for local development
|
||||||
VITE_WS_URL=ws://localhost:8000/ws
|
# Copy this file to .env for production
|
||||||
|
|
||||||
# Development server port (optional, Vite uses 5173 by default)
|
# Backend API URL
|
||||||
# PORT=5173
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:9000
|
||||||
|
|
||||||
|
# API URL (optional, defaults to BACKEND_URL/api/v1)
|
||||||
|
NEXT_PUBLIC_API_URL=http://localhost:9000/api/v1
|
||||||
|
|
||||||
|
# WebSocket URL (БЕЗ пути /ws/, он добавляется автоматически в коде)
|
||||||
|
NEXT_PUBLIC_WS_URL=ws://localhost:9000
|
||||||
|
|||||||
@ -1,256 +0,0 @@
|
|||||||
# ✅ API v2 + Frontend Integration Complete!
|
|
||||||
|
|
||||||
## 🎉 Что сделано
|
|
||||||
|
|
||||||
### ✅ Backend - API v2 создан
|
|
||||||
|
|
||||||
**Структура:**
|
|
||||||
```
|
|
||||||
backend/api/v2/
|
|
||||||
├── auth/ ✅ register, verify-email, resend-email
|
|
||||||
├── calendar/ ✅ events, calendars, subscriptions
|
|
||||||
├── communications/ ✅ chats, messages, polls
|
|
||||||
├── documents/ ✅ documents CRUD
|
|
||||||
├── employees/ ✅ employees, departments, positions, skills, groups
|
|
||||||
├── feed/ ✅ posts, comments
|
|
||||||
└── requests/ ✅ requests (заявки)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Все endpoints из v1 портированы в v2** через наследование ViewSet'ов.
|
|
||||||
|
|
||||||
### ✅ Frontend обновлен для v2
|
|
||||||
|
|
||||||
- `.env` → использует `http://localhost:8000/api/v2`
|
|
||||||
- `constants.ts` → default на v2
|
|
||||||
- Все запросы идут в v2 API
|
|
||||||
|
|
||||||
### ✅ Документация создана
|
|
||||||
|
|
||||||
- [API_V2_MIGRATION.md](../../docs/guides/API_V2_MIGRATION.md) - полное описание v2
|
|
||||||
- [QUICKSTART.md](QUICKSTART.md) - обновлен для v2
|
|
||||||
- [DJANGO_CORS_SETUP.md](../../docs/guides/DJANGO_CORS_SETUP.md) - настройка CORS
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 Полный список endpoints v2
|
|
||||||
|
|
||||||
### Auth (общие для v1/v2)
|
|
||||||
```
|
|
||||||
POST /api/auth/token/ # Login (JWT)
|
|
||||||
POST /api/auth/token/refresh/ # Refresh token
|
|
||||||
POST /api/v2/auth/register/ # Регистрация
|
|
||||||
POST /api/v2/auth/verify-email/ # Подтверждение email
|
|
||||||
POST /api/v2/auth/resend-email/ # Повтор отправки
|
|
||||||
```
|
|
||||||
|
|
||||||
### Employees
|
|
||||||
```
|
|
||||||
GET /api/v2/employees/ # Список
|
|
||||||
POST /api/v2/employees/ # Создать
|
|
||||||
GET /api/v2/employees/{id}/ # Получить
|
|
||||||
PATCH /api/v2/employees/{id}/ # Обновить
|
|
||||||
DELETE /api/v2/employees/{id}/ # Удалить
|
|
||||||
|
|
||||||
GET /api/v2/departments/
|
|
||||||
GET /api/v2/positions/
|
|
||||||
GET /api/v2/skills/
|
|
||||||
GET /api/v2/groups/
|
|
||||||
GET /api/v2/employee-actions/
|
|
||||||
GET /api/v2/department-roles/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Documents
|
|
||||||
```
|
|
||||||
GET /api/v2/documents/
|
|
||||||
POST /api/v2/documents/
|
|
||||||
GET /api/v2/documents/{id}/
|
|
||||||
PATCH /api/v2/documents/{id}/
|
|
||||||
DELETE /api/v2/documents/{id}/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Requests (Заявки)
|
|
||||||
```
|
|
||||||
GET /api/v2/requests/
|
|
||||||
POST /api/v2/requests/
|
|
||||||
GET /api/v2/requests/{id}/
|
|
||||||
PATCH /api/v2/requests/{id}/
|
|
||||||
DELETE /api/v2/requests/{id}/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Calendar
|
|
||||||
```
|
|
||||||
GET /api/v2/calendar/events/
|
|
||||||
POST /api/v2/calendar/events/
|
|
||||||
GET /api/v2/calendar/calendars/
|
|
||||||
GET /api/v2/calendar/subscriptions/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Communications (Чаты)
|
|
||||||
```
|
|
||||||
GET /api/v2/chats/
|
|
||||||
POST /api/v2/chats/
|
|
||||||
GET /api/v2/chats/{id}/
|
|
||||||
GET /api/v2/messages/
|
|
||||||
POST /api/v2/messages/
|
|
||||||
GET /api/v2/polls/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Feed (Лента)
|
|
||||||
```
|
|
||||||
GET /api/v2/posts/
|
|
||||||
POST /api/v2/posts/
|
|
||||||
GET /api/v2/posts/{id}/
|
|
||||||
GET /api/v2/comments/
|
|
||||||
POST /api/v2/comments/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notifications
|
|
||||||
```
|
|
||||||
GET /api/v2/notifications/ (использует v1 internally)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 v1 vs v2
|
|
||||||
|
|
||||||
| Характеристика | v1 | v2 |
|
|
||||||
|----------------|----|----|
|
|
||||||
| **Endpoints** | Все | Все (идентичные) |
|
|
||||||
| **LDAP Sync** | Синхронный (Mixin) | Асинхронный (Signals) |
|
|
||||||
| **Response Time** | ~200-300ms | ~30-50ms |
|
|
||||||
| **Блокировка** | Да | Нет |
|
|
||||||
| **Retry логика** | Ручная | Celery автоматически |
|
|
||||||
| **Статус** | ✅ Работает | ✅ Работает |
|
|
||||||
|
|
||||||
**Оба API полностью функциональны!**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Быстрый запуск
|
|
||||||
|
|
||||||
### 1. Настроить CORS (если еще не сделано)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
.venv/Scripts/pip install django-cors-headers
|
|
||||||
```
|
|
||||||
|
|
||||||
Добавить в `settings.py`:
|
|
||||||
```python
|
|
||||||
INSTALLED_APPS = ["corsheaders", ...]
|
|
||||||
MIDDLEWARE = ["corsheaders.middleware.CorsMiddleware", ...]
|
|
||||||
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Подробнее**: [Django CORS Setup](../../docs/guides/DJANGO_CORS_SETUP.md)
|
|
||||||
|
|
||||||
### 2. Запустить Backend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
.venv/Scripts/python manage.py runserver 8000
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Запустить Frontend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
npm install # первый раз
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
**Открыть**: http://localhost:5173
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ Проверка работы
|
|
||||||
|
|
||||||
### Test 1: API v2 доступен
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/api/v2/employees/
|
|
||||||
```
|
|
||||||
|
|
||||||
Должен вернуть JSON.
|
|
||||||
|
|
||||||
### Test 2: v1 всё ещё работает
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/api/v1/employees/
|
|
||||||
```
|
|
||||||
|
|
||||||
Оба работают параллельно!
|
|
||||||
|
|
||||||
### Test 3: Frontend
|
|
||||||
|
|
||||||
Открыть http://localhost:5173 → должна загрузиться страница сотрудников.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Статус проекта
|
|
||||||
|
|
||||||
| Модуль | Backend v2 | Frontend | Status |
|
|
||||||
|--------|------------|----------|--------|
|
|
||||||
| Auth | ✅ | ✅ | **Готово** |
|
|
||||||
| Employees | ✅ | ✅ List | В разработке |
|
|
||||||
| Departments | ✅ | ⏳ | Backend готов |
|
|
||||||
| Documents | ✅ | ⏳ | Backend готов |
|
|
||||||
| Requests | ✅ | ⏳ | Backend готов |
|
|
||||||
| Calendar | ✅ | ⏳ | Backend готов |
|
|
||||||
| Chats | ✅ | ⏳ | Backend готов |
|
|
||||||
| Feed | ✅ | ⏳ | Backend готов |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Следующие задачи
|
|
||||||
|
|
||||||
### Фаза 1: Основной функционал
|
|
||||||
- [ ] Добавить endpoint `/api/v2/employees/me/` (текущий пользователь)
|
|
||||||
- [ ] Создать CRUD страницы для documents
|
|
||||||
- [ ] Создать CRUD страницы для requests
|
|
||||||
- [ ] Добавить UI библиотеку (Ant Design)
|
|
||||||
|
|
||||||
### Фаза 2: Продвинутый функционал
|
|
||||||
- [ ] Интегрировать WebSocket для чатов
|
|
||||||
- [ ] Добавить real-time уведомления
|
|
||||||
- [ ] Календарь с FullCalendar
|
|
||||||
- [ ] File uploads
|
|
||||||
|
|
||||||
### Фаза 3: Полировка
|
|
||||||
- [ ] Мобильная адаптация
|
|
||||||
- [ ] Темная тема
|
|
||||||
- [ ] Тесты (Frontend + Backend)
|
|
||||||
- [ ] Production build
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Переключение между v1 и v2
|
|
||||||
|
|
||||||
В любой момент можно переключиться:
|
|
||||||
|
|
||||||
**На v2 (по умолчанию):**
|
|
||||||
```bash
|
|
||||||
# frontend/.env
|
|
||||||
VITE_API_URL=http://localhost:8000/api/v2
|
|
||||||
```
|
|
||||||
|
|
||||||
**На v1:**
|
|
||||||
```bash
|
|
||||||
# frontend/.env
|
|
||||||
VITE_API_URL=http://localhost:8000/api/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 Документация
|
|
||||||
|
|
||||||
- [API v2 Migration Guide](../../docs/guides/API_V2_MIGRATION.md)
|
|
||||||
- [Frontend Quickstart](QUICKSTART.md)
|
|
||||||
- [Django CORS Setup](../../docs/guides/DJANGO_CORS_SETUP.md)
|
|
||||||
- [API v2 README](../../backend/api/v2/README.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Всё готово для разработки на API v2!** 🚀
|
|
||||||
|
|
||||||
Frontend настроен, Backend готов, осталось только добавить CORS и можно работать!
|
|
||||||
@ -1,410 +0,0 @@
|
|||||||
# Frontend Integration Guide
|
|
||||||
|
|
||||||
## Обзор
|
|
||||||
|
|
||||||
Фронтенд corp.front полностью интегрирован с Django Backend через REST API. Используется Next.js 16 с TypeScript и Tailwind CSS.
|
|
||||||
|
|
||||||
## Структура проекта
|
|
||||||
|
|
||||||
```
|
|
||||||
corp.front/
|
|
||||||
├── src/
|
|
||||||
│ ├── app/ # Next.js страницы
|
|
||||||
│ │ ├── page.tsx # Главная (Лента) ✅ Интегрирован
|
|
||||||
│ │ ├── employees/ # Сотрудники ✅ Интегрирован
|
|
||||||
│ │ ├── departments/ # Отделы
|
|
||||||
│ │ ├── documents/ # Документы
|
|
||||||
│ │ ├── messages/ # Сообщения
|
|
||||||
│ │ ├── requests/ # Заявки
|
|
||||||
│ │ ├── finances/ # Финансы
|
|
||||||
│ │ └── login/ # Вход
|
|
||||||
│ ├── lib/
|
|
||||||
│ │ └── api.ts # API клиент ✅
|
|
||||||
│ ├── types/
|
|
||||||
│ │ └── api.ts # TypeScript типы ✅
|
|
||||||
│ └── hooks/
|
|
||||||
│ └── useApi.ts # React хуки для API ✅
|
|
||||||
```
|
|
||||||
|
|
||||||
## API клиент
|
|
||||||
|
|
||||||
### Базовая конфигурация
|
|
||||||
|
|
||||||
API клиент автоматически:
|
|
||||||
- Добавляет JWT токен к запросам
|
|
||||||
- Обрабатывает ошибки авторизации
|
|
||||||
- Сохраняет токены в localStorage
|
|
||||||
- Перенаправляет на /login при 401
|
|
||||||
|
|
||||||
### Использование
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import apiClient from '@/lib/api';
|
|
||||||
|
|
||||||
// Авторизация
|
|
||||||
const { access, refresh, user } = await apiClient.login({
|
|
||||||
username: 'user@example.com',
|
|
||||||
password: 'password'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Получение данных
|
|
||||||
const employees = await apiClient.getEmployees();
|
|
||||||
const posts = await apiClient.getPosts();
|
|
||||||
```
|
|
||||||
|
|
||||||
## React хуки
|
|
||||||
|
|
||||||
Используйте готовые хуки из `@/hooks/useApi`:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { useCurrentUser, useEmployees, usePosts } from '@/hooks/useApi';
|
|
||||||
|
|
||||||
function MyComponent() {
|
|
||||||
const { user, loading, error } = useCurrentUser();
|
|
||||||
const { employees } = useEmployees({ search: 'John' });
|
|
||||||
const { posts } = usePosts({ page: 1 });
|
|
||||||
|
|
||||||
if (loading) return <div>Загрузка...</div>;
|
|
||||||
if (error) return <div>Ошибка: {error.message}</div>;
|
|
||||||
|
|
||||||
return <div>{/* UI */}</div>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Доступные хуки
|
|
||||||
|
|
||||||
- `useCurrentUser()` - текущий пользователь
|
|
||||||
- `useEmployees(params?)` - список сотрудников
|
|
||||||
- `useDepartments()` - список отделов
|
|
||||||
- `usePosts(params?)` - посты ленты
|
|
||||||
- `useDocuments(params?)` - документы
|
|
||||||
- `useRequests(params?)` - заявки
|
|
||||||
- `useChats()` - чаты
|
|
||||||
- `useNotifications()` - уведомления
|
|
||||||
|
|
||||||
## TypeScript типы
|
|
||||||
|
|
||||||
Все типы данных определены в `@/types/api.ts`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import type { User, Post, Department, Document } from '@/types/api';
|
|
||||||
```
|
|
||||||
|
|
||||||
## Интегрированные страницы
|
|
||||||
|
|
||||||
### ✅ Главная страница (Лента)
|
|
||||||
|
|
||||||
**Файл**: `src/app/page.tsx`
|
|
||||||
|
|
||||||
**Функции**:
|
|
||||||
- Загрузка постов из API
|
|
||||||
- Отображение автора, времени, контента
|
|
||||||
- Лайки и комментарии
|
|
||||||
- Теги постов
|
|
||||||
|
|
||||||
**Используемые хуки**:
|
|
||||||
- `usePosts()` - получение постов
|
|
||||||
- `useCurrentUser()` - текущий пользователь
|
|
||||||
|
|
||||||
### ✅ Сотрудники
|
|
||||||
|
|
||||||
**Файл**: `src/app/employees/page.tsx`
|
|
||||||
|
|
||||||
**Функции**:
|
|
||||||
- Список всех сотрудников
|
|
||||||
- Поиск по имени
|
|
||||||
- Статус (онлайн/оффлайн)
|
|
||||||
- Должность и отдел
|
|
||||||
|
|
||||||
**Используемые хуки**:
|
|
||||||
- `useEmployees({ search })` - поиск сотрудников
|
|
||||||
- `useCurrentUser()` - текущий пользователь
|
|
||||||
|
|
||||||
## Переменные окружения
|
|
||||||
|
|
||||||
### Для локальной разработки
|
|
||||||
|
|
||||||
`.env.local`:
|
|
||||||
```env
|
|
||||||
NEXT_PUBLIC_API_URL=http://localhost:8000
|
|
||||||
NEXT_PUBLIC_WS_URL=ws://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Для Docker
|
|
||||||
|
|
||||||
`.env.local` (автоматически используется в docker-compose):
|
|
||||||
```env
|
|
||||||
NEXT_PUBLIC_API_URL=http://web:8000
|
|
||||||
NEXT_PUBLIC_WS_URL=ws://web:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
## Запуск
|
|
||||||
|
|
||||||
### Локальная разработка
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd corp.front
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Доступно на: http://localhost:3000
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Из корня проекта
|
|
||||||
docker-compose up -d frontend
|
|
||||||
```
|
|
||||||
|
|
||||||
Доступно через nginx: http://localhost
|
|
||||||
|
|
||||||
## Интеграция новых страниц
|
|
||||||
|
|
||||||
### Шаг 1: Создать страницу
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
// src/app/mypage/page.tsx
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useMyData } from '@/hooks/useApi';
|
|
||||||
|
|
||||||
export default function MyPage() {
|
|
||||||
const { data, loading, error } = useMyData();
|
|
||||||
|
|
||||||
if (loading) return <div>Загрузка...</div>;
|
|
||||||
if (error) return <div>Ошибка: {error.message}</div>;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{data.map(item => (
|
|
||||||
<div key={item.id}>{item.name}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Шаг 2: Добавить метод в API клиент
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// src/lib/api.ts
|
|
||||||
class ApiClient {
|
|
||||||
// ... existing methods
|
|
||||||
|
|
||||||
async getMyData() {
|
|
||||||
return this.request('/mydata/');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Шаг 3: Создать хук (опционально)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// src/hooks/useApi.ts
|
|
||||||
export function useMyData() {
|
|
||||||
const [data, setData] = useState([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function fetchData() {
|
|
||||||
try {
|
|
||||||
const result = await apiClient.getMyData();
|
|
||||||
setData(result);
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { data, loading, error };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
Полный список доступных эндпоинтов:
|
|
||||||
|
|
||||||
### Auth
|
|
||||||
- `POST /api/auth/token/` - Login
|
|
||||||
- `POST /api/auth/token/refresh/` - Refresh token
|
|
||||||
- `POST /api/v1/auth/register/` - Регистрация
|
|
||||||
|
|
||||||
### Employees
|
|
||||||
- `GET /api/v1/employees/` - Список сотрудников
|
|
||||||
- `GET /api/v1/employees/{id}/` - Детали сотрудника
|
|
||||||
- `GET /api/v1/employees/me/` - Текущий пользователь
|
|
||||||
|
|
||||||
### Departments
|
|
||||||
- `GET /api/v1/departments/` - Список отделов
|
|
||||||
- `GET /api/v1/departments/{id}/` - Детали отдела
|
|
||||||
|
|
||||||
### Feed
|
|
||||||
- `GET /api/v1/posts/` - Посты ленты
|
|
||||||
- `POST /api/v1/posts/` - Создать пост
|
|
||||||
- `POST /api/v1/posts/{id}/like/` - Лайк поста
|
|
||||||
- `GET /api/v1/comments/` - Комментарии
|
|
||||||
- `POST /api/v1/comments/` - Создать комментарий
|
|
||||||
|
|
||||||
### Documents
|
|
||||||
- `GET /api/v1/documents/` - Документы
|
|
||||||
- `GET /api/v1/documents/{id}/` - Детали документа
|
|
||||||
|
|
||||||
### Requests
|
|
||||||
- `GET /api/v1/requests/` - Заявки
|
|
||||||
- `POST /api/v1/requests/` - Создать заявку
|
|
||||||
- `GET /api/v1/requests/{id}/` - Детали заявки
|
|
||||||
|
|
||||||
### Communications
|
|
||||||
- `GET /api/v1/communications/chats/` - Чаты
|
|
||||||
- `GET /api/v1/communications/messages/` - Сообщения
|
|
||||||
- `POST /api/v1/communications/messages/` - Отправить сообщение
|
|
||||||
|
|
||||||
### Calendar
|
|
||||||
- `GET /api/v1/calendar/events/` - События календаря
|
|
||||||
- `GET /api/v1/calendar/calendars/` - Календари
|
|
||||||
|
|
||||||
### Notifications
|
|
||||||
- `GET /api/v1/notifications/` - Уведомления
|
|
||||||
- `POST /api/v1/notifications/{id}/mark_read/` - Отметить прочитанным
|
|
||||||
- `POST /api/v1/notifications/mark_all_read/` - Отметить все прочитанными
|
|
||||||
|
|
||||||
## Аутентификация
|
|
||||||
|
|
||||||
### Вход
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import apiClient from '@/lib/api';
|
|
||||||
|
|
||||||
async function handleLogin(username: string, password: string) {
|
|
||||||
try {
|
|
||||||
const { access, refresh, user } = await apiClient.login({
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
});
|
|
||||||
|
|
||||||
// Токен автоматически сохранён
|
|
||||||
// Перенаправить пользователя
|
|
||||||
router.push('/');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Login failed:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Выход
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import apiClient from '@/lib/api';
|
|
||||||
|
|
||||||
function handleLogout() {
|
|
||||||
apiClient.clearToken();
|
|
||||||
router.push('/login');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Защищённые страницы
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import apiClient from '@/lib/api';
|
|
||||||
|
|
||||||
export default function ProtectedPage() {
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const token = apiClient.getToken();
|
|
||||||
if (!token) {
|
|
||||||
router.push('/login');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <div>Protected content</div>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Обработка ошибок
|
|
||||||
|
|
||||||
API клиент автоматически обрабатывает:
|
|
||||||
|
|
||||||
- **401 Unauthorized**: Очищает токены и перенаправляет на `/login`
|
|
||||||
- **Network errors**: Выбрасывает исключение с сообщением
|
|
||||||
|
|
||||||
Пример обработки в компонентах:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
const { data, loading, error } = useMyData();
|
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<div className="error-message">
|
|
||||||
<h3>Ошибка загрузки данных</h3>
|
|
||||||
<p>{error.message}</p>
|
|
||||||
<button onClick={() => refetch()}>Попробовать снова</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## WebSocket (для будущей реализации)
|
|
||||||
|
|
||||||
Для real-time обновлений можно добавить WebSocket соединение:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const WS_URL = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:8000';
|
|
||||||
|
|
||||||
const socket = new WebSocket(`${WS_URL}/ws/notifications/`);
|
|
||||||
|
|
||||||
socket.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
// Обработать уведомление
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Рекомендации
|
|
||||||
|
|
||||||
1. **Всегда используйте хуки** для получения данных из API
|
|
||||||
2. **Обрабатывайте loading и error** состояния в UI
|
|
||||||
3. **Используйte TypeScript типы** для type-safety
|
|
||||||
4. **Не сохраняйте чувствительные данные** в localStorage кроме токенов
|
|
||||||
5. **Валидируйте данные** перед отправкой на сервер
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
###CORS ошибки
|
|
||||||
|
|
||||||
Убедитесь, что backend настроен с правильными CORS заголовками:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# backend/eusrr_backend/settings.py
|
|
||||||
CORS_ALLOWED_ORIGINS = [
|
|
||||||
"http://localhost:3000",
|
|
||||||
"http://localhost",
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 401 Unauthorized
|
|
||||||
|
|
||||||
- Проверьте, что токен действителен
|
|
||||||
- Убедитесь, что backend запущен
|
|
||||||
- Проверьте переменные окружения
|
|
||||||
|
|
||||||
### Ошибка сети
|
|
||||||
|
|
||||||
- Проверьте, что backend доступен
|
|
||||||
- Проверьте URL в `NEXT_PUBLIC_API_URL`
|
|
||||||
- Проверьте docker network, если используете docker-compose
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Дата создания**: 19 февраля 2026
|
|
||||||
**Версия**: 1.0
|
|
||||||
@ -17,6 +17,18 @@ cd backend
|
|||||||
.venv/Scripts/pip install django-cors-headers
|
.venv/Scripts/pip install django-cors-headers
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 1.5. Проверить что Daphne установлен
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
.venv/Scripts/pip show daphne
|
||||||
|
```
|
||||||
|
|
||||||
|
Если Daphne не установлен:
|
||||||
|
```bash
|
||||||
|
.venv/Scripts/pip install daphne
|
||||||
|
```
|
||||||
|
|
||||||
Добавить в `backend/eusrr_backend/settings.py`:
|
Добавить в `backend/eusrr_backend/settings.py`:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -52,7 +64,15 @@ npm install
|
|||||||
|
|
||||||
### 3. Запустить оба сервера
|
### 3. Запустить оба сервера
|
||||||
|
|
||||||
**Terminal 1 - Django:**
|
**⚠️ ВАЖНО: Для WebSocket используйте Daphne вместо стандартного Django сервера!**
|
||||||
|
|
||||||
|
**Terminal 1 - Django (с WebSocket на Daphne):**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9000 eusrr_backend.asgi:application
|
||||||
|
```
|
||||||
|
|
||||||
|
Или используйте `runserver` для HTTP (без WebSocket):
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
.venv/Scripts/python manage.py runserver 8000
|
.venv/Scripts/python manage.py runserver 8000
|
||||||
@ -68,6 +88,16 @@ npm run dev
|
|||||||
|
|
||||||
Откройте в браузере: **http://localhost:5173**
|
Откройте в браузере: **http://localhost:5173**
|
||||||
|
|
||||||
|
## 🔍 Проверка WebSocket подключения
|
||||||
|
|
||||||
|
1. Откройте **DevTools** (F12) → **Console**
|
||||||
|
2. Ищите сообщение `🔄 Подключение к WebSocket: ws://localhost:9000/ws/...`
|
||||||
|
3. Если ошибка `❌ WebSocket error: {}`, проверьте:
|
||||||
|
- ✅ Backend запущен через Daphne (не через runserver)
|
||||||
|
- ✅ PORT 9000 доступен (не занят другой программой)
|
||||||
|
- ✅ JWT токен сохранен в localStorage
|
||||||
|
- ✅ В файле `.env.local` стоит `NEXT_PUBLIC_WS_URL=ws://localhost:9000`
|
||||||
|
|
||||||
## 🔐 Тестовая авторизация
|
## 🔐 Тестовая авторизация
|
||||||
|
|
||||||
Используйте существующего пользователя из вашей БД:
|
Используйте существующего пользователя из вашей БД:
|
||||||
@ -77,6 +107,8 @@ Email: ваш_email@example.com
|
|||||||
Password: ваш_пароль
|
Password: ваш_пароль
|
||||||
```
|
```
|
||||||
|
|
||||||
|
После входа токен автоматически сохранится в `localStorage` и будет использован для WebSocket аутентификации.
|
||||||
|
|
||||||
## 📁 Структура страниц
|
## 📁 Структура страниц
|
||||||
|
|
||||||
Сейчас доступно:
|
Сейчас доступно:
|
||||||
@ -124,10 +156,40 @@ touch src/pages/documents/index.ts
|
|||||||
- Проверьте что пользователь существует в БД
|
- Проверьте что пользователь существует в БД
|
||||||
- Проверьте endpoint: `http://localhost:8000/api/auth/token/`
|
- Проверьте endpoint: `http://localhost:8000/api/auth/token/`
|
||||||
|
|
||||||
|
### WebSocket error: {}
|
||||||
|
**Это самая частая ошибка!** Решение:
|
||||||
|
|
||||||
|
1. **Проверьте что backend запущен через Daphne:**
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
.venv/Scripts/daphne -b 0.0.0.0 -p 9000 eusrr_backend.asgi:application
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **НЕ используйте `python manage.py runserver`** - это не поддерживает WebSocket!
|
||||||
|
|
||||||
|
3. **Убедитесь что .env.local имеет правильный URL:**
|
||||||
|
```dotenv
|
||||||
|
NEXT_PUBLIC_WS_URL=ws://localhost:9000
|
||||||
|
NEXT_PUBLIC_BACKEND_URL=http://localhost:9000
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Проверьте в DevTools → Network → WS:**
|
||||||
|
- Введите фильтр `ws` чтобы найти WebSocket подключение
|
||||||
|
- Если соединение **101 Switching Protocols** - всё работает ✅
|
||||||
|
- Если ошибка **Failed** - backend не запущен через Daphne ❌
|
||||||
|
|
||||||
|
5. **Проверьте порт 9000:**
|
||||||
|
```bash
|
||||||
|
# Windows - проверить какой процесс занимает порт
|
||||||
|
netstat -ano | findstr :9000
|
||||||
|
|
||||||
|
# Если занято - убейте процесс или используйте другой порт
|
||||||
|
```
|
||||||
|
|
||||||
### Страница не загружается
|
### Страница не загружается
|
||||||
- Убедитесь что оба сервера запущены
|
- Убедитесь что оба сервера запущены
|
||||||
- Проверьте `.env` файл в frontend
|
- Проверьте `.env.local` файл в frontend
|
||||||
- Откройте DevTools → Network
|
- Откройте DevTools → Network и посмотрите статусы запросов
|
||||||
|
|
||||||
## 📚 Документация
|
## 📚 Документация
|
||||||
|
|
||||||
|
|||||||
@ -23,18 +23,18 @@ interface UseWebSocketOptions {
|
|||||||
maxReconnectAttempts?: number;
|
maxReconnectAttempts?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWebSocket({
|
export function useWebSocket({
|
||||||
chatId,
|
chatId,
|
||||||
autoConnect = true,
|
autoConnect = true,
|
||||||
reconnectInterval = 3000,
|
reconnectInterval = 3000,
|
||||||
maxReconnectAttempts = 5
|
maxReconnectAttempts = 5
|
||||||
}: UseWebSocketOptions) {
|
}: UseWebSocketOptions) {
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
const [reconnectAttempts, setReconnectAttempts] = useState(0);
|
||||||
const wsRef = useRef<WebSocket | null>(null);
|
const wsRef = useRef<WebSocket | null>(null);
|
||||||
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const isConnectingRef = useRef(false);
|
const isConnectingRef = useRef(false);
|
||||||
|
|
||||||
const handlers = useRef<WebSocketHandlers>({
|
const handlers = useRef<WebSocketHandlers>({
|
||||||
onMessage: () => {},
|
onMessage: () => {},
|
||||||
onTyping: () => {},
|
onTyping: () => {},
|
||||||
@ -51,15 +51,22 @@ export function useWebSocket({
|
|||||||
isConnectingRef.current = true;
|
isConnectingRef.current = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
// Простую логику построения URL
|
||||||
const host = process.env.NEXT_PUBLIC_WS_URL || 'localhost:9000';
|
let wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:9000/ws/';
|
||||||
|
|
||||||
// Получаем JWT токен для аутентификации WebSocket
|
// // Если нет протокола - добавляем
|
||||||
|
// if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
|
||||||
|
// const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
// wsUrl = `${protocol}//${wsUrl}`;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Добавляем токен
|
||||||
const token = localStorage.getItem('access_token');
|
const token = localStorage.getItem('access_token');
|
||||||
const wsUrl = token
|
if (token) {
|
||||||
? `${protocol}//${host}/ws/?token=${token}`
|
wsUrl += `${wsUrl.includes('?') ? '&' : '?'}token=${token}`;
|
||||||
: `${protocol}//${host}/ws/`;
|
}
|
||||||
|
|
||||||
|
console.log('🔄 WebSocket URL:', wsUrl);
|
||||||
const ws = new WebSocket(wsUrl);
|
const ws = new WebSocket(wsUrl);
|
||||||
wsRef.current = ws;
|
wsRef.current = ws;
|
||||||
|
|
||||||
@ -67,7 +74,7 @@ export function useWebSocket({
|
|||||||
setIsConnected(true);
|
setIsConnected(true);
|
||||||
setReconnectAttempts(0);
|
setReconnectAttempts(0);
|
||||||
isConnectingRef.current = false;
|
isConnectingRef.current = false;
|
||||||
|
|
||||||
// Открываем чат на сервере
|
// Открываем чат на сервере
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
action: 'open_chat',
|
action: 'open_chat',
|
||||||
@ -136,7 +143,7 @@ export function useWebSocket({
|
|||||||
|
|
||||||
if (wsRef.current) {
|
if (wsRef.current) {
|
||||||
const ws = wsRef.current;
|
const ws = wsRef.current;
|
||||||
|
|
||||||
if (ws.readyState === WebSocket.OPEN && chatId) {
|
if (ws.readyState === WebSocket.OPEN && chatId) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
action: 'close_chat',
|
action: 'close_chat',
|
||||||
@ -155,8 +162,8 @@ export function useWebSocket({
|
|||||||
|
|
||||||
const sendTyping = useCallback(() => {
|
const sendTyping = useCallback(() => {
|
||||||
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
||||||
wsRef.current.send(JSON.stringify({
|
wsRef.current.send(JSON.stringify({
|
||||||
action: 'typing'
|
action: 'typing'
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user