Fix WebSocket URL handling and simplify .env.local setup

This commit is contained in:
igor_nadein 2026-02-26 20:18:37 +03:00
parent bc20d7e61e
commit 8a7810431d
10 changed files with 656 additions and 931 deletions

View File

@ -9,6 +9,5 @@ class ApiConfig(AppConfig):
def ready(self):
"""Инициализация при загрузке приложения."""
# Применяем патч для django-scheduler (исправление бага с byweekday)
print("🔧 ApiConfig.ready() - применение патча django-scheduler")
import schedule_patch
schedule_patch.apply_patch()

File diff suppressed because it is too large Load Diff

View File

@ -70,8 +70,18 @@ def create_patched_method():
return patched_event_params
# Флаг для предотвращения повторного применения патча
_patch_applied = False
def apply_patch():
"""Применяет патч к django-scheduler Event модели."""
global _patch_applied
# Если патч уже применён, пропускаем
if _patch_applied:
return True
try:
# Ленивый импорт - только при вызове apply_patch()
from schedule.models import Event
@ -86,6 +96,7 @@ def apply_patch():
print(f" Патч применён: Event._event_params = {patched_method.__name__}")
print("=" * 80)
_patch_applied = True
return True
except Exception as e:
print("=" * 80)

View 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)

View 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)

View File

@ -1,6 +1,12 @@
# API Configuration
VITE_API_URL=http://localhost:8000/api/v2
VITE_WS_URL=ws://localhost:8000/ws
# Next.js Environment Variables Example
# Copy this file to .env.local for local development
# Copy this file to .env for production
# Development server port (optional, Vite uses 5173 by default)
# PORT=5173
# Backend API URL
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

View File

@ -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 и можно работать!

View File

@ -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

View File

@ -17,6 +17,18 @@ cd backend
.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`:
```python
@ -52,7 +64,15 @@ npm install
### 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
cd backend
.venv/Scripts/python manage.py runserver 8000
@ -68,6 +88,16 @@ npm run dev
Откройте в браузере: **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: ваш_пароль
```
После входа токен автоматически сохранится в `localStorage` и будет использован для WebSocket аутентификации.
## 📁 Структура страниц
Сейчас доступно:
@ -124,10 +156,40 @@ touch src/pages/documents/index.ts
- Проверьте что пользователь существует в БД
- Проверьте 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
- Откройте DevTools → Network
- Проверьте `.env.local` файл в frontend
- Откройте DevTools → Network и посмотрите статусы запросов
## 📚 Документация

View File

@ -23,18 +23,18 @@ interface UseWebSocketOptions {
maxReconnectAttempts?: number;
}
export function useWebSocket({
chatId,
export function useWebSocket({
chatId,
autoConnect = true,
reconnectInterval = 3000,
maxReconnectAttempts = 5
maxReconnectAttempts = 5
}: UseWebSocketOptions) {
const [isConnected, setIsConnected] = useState(false);
const [reconnectAttempts, setReconnectAttempts] = useState(0);
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const isConnectingRef = useRef(false);
const handlers = useRef<WebSocketHandlers>({
onMessage: () => {},
onTyping: () => {},
@ -51,15 +51,22 @@ export function useWebSocket({
isConnectingRef.current = true;
try {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const host = process.env.NEXT_PUBLIC_WS_URL || 'localhost:9000';
// Получаем JWT токен для аутентификации WebSocket
// Простую логику построения URL
let wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:9000/ws/';
// // Если нет протокола - добавляем
// 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 wsUrl = token
? `${protocol}//${host}/ws/?token=${token}`
: `${protocol}//${host}/ws/`;
if (token) {
wsUrl += `${wsUrl.includes('?') ? '&' : '?'}token=${token}`;
}
console.log('🔄 WebSocket URL:', wsUrl);
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
@ -67,7 +74,7 @@ export function useWebSocket({
setIsConnected(true);
setReconnectAttempts(0);
isConnectingRef.current = false;
// Открываем чат на сервере
ws.send(JSON.stringify({
action: 'open_chat',
@ -136,7 +143,7 @@ export function useWebSocket({
if (wsRef.current) {
const ws = wsRef.current;
if (ws.readyState === WebSocket.OPEN && chatId) {
ws.send(JSON.stringify({
action: 'close_chat',
@ -155,8 +162,8 @@ export function useWebSocket({
const sendTyping = useCallback(() => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({
action: 'typing'
wsRef.current.send(JSON.stringify({
action: 'typing'
}));
}
}, []);