diff --git a/SPECIFICATION.md b/spec/SPECIFICATION-aurora.md similarity index 54% rename from SPECIFICATION.md rename to spec/SPECIFICATION-aurora.md index 603c15b..61f52ff 100644 --- a/SPECIFICATION.md +++ b/spec/SPECIFICATION-aurora.md @@ -1,7 +1,8 @@ -# Team Board — Полная Спецификация +# Team Board — Полная Спецификация (Aurora Edition) -Версия: 1.0 +Версия: 1.1 Дата: 2026-03-13 +Автор: Аврора Статус: В разработке --- @@ -21,6 +22,13 @@ 11. [Инфраструктура](#11-инфраструктура) 12. [Функциональность](#12-функциональность) 13. [Дорожная карта](#13-дорожная-карта) +14. [Разработка и деплой](#14-разработка-и-деплой) +15. [Тестирование](#15-тестирование) +16. [Производительность](#16-производительность) +17. [Безопасность](#17-безопасность) +18. [Мониторинг и логирование](#18-мониторинг-и-логирование) +19. [Troubleshooting](#19-troubleshooting) +20. [Приложения](#20-приложения) --- @@ -799,4 +807,730 @@ MIT --- +## 14. Разработка и деплой + +### 14.1 Локальная разработка + +**Backend (Tracker):** +```bash +cd /root/projects/team-board/tracker +python -m venv venv +source venv/bin/activate +pip install -r requirements.txt +alembic upgrade head +uvicorn app.main:app --reload --port 8100 +``` + +**Frontend:** +```bash +cd /root/projects/team-board/web-client-vite +npm install +npm run dev +``` + +**Agent (Picogent):** +```bash +cd /root/projects/team-board/picogent +npm install +npm run build +npm run start --config agent.json +``` + +### 14.2 Docker деплой + +**Сборка:** +```bash +docker-compose build +``` + +**Запуск:** +```bash +docker-compose up -d +``` + +**Логи:** +```bash +docker-compose logs -f tracker +``` + +**Остановка:** +```bash +docker-compose down +``` + +### 14.3 Миграции БД + +**Создание миграции:** +```bash +alembic revision --autogenerate -m "Add new table" +alembic upgrade head +``` + +**Откат:** +```bash +alembic downgrade -1 +``` + +**История:** +```bash +alembic history +``` + +### 14.4 Переменные окружения + +**Tracker (.env):** +```bash +DATABASE_URL=postgresql://user:pass@localhost:5432/teamboard +JWT_SECRET=your-secret-key +CORS_ORIGINS=https://dev.team.uix.su,https://team.uix.su +UPLOAD_DIR=/var/www/team-board/uploads +``` + +**Picogent (.env):** +```bash +TRACKER_URL=http://localhost:8100 +AGENT_TOKEN=tb-agent-xxx +MODEL=claude-sonnet-4 +``` + +--- + +## 15. Тестирование +### 15.1 Backend тесты + +**Стек:** pytest, pytest-asyncio + +**Структура:** +``` +tests/ +├── conftest.py +├── test_auth.py +├── test_tasks.py +├── test_projects.py +└── test_agents.py +``` + +**Пример:** +```python +import pytest +from httpx import AsyncClient + +@pytest.mark.asyncio +async def test_create_task(client: AsyncClient, auth_headers): + response = await client.post( + "/api/projects/test-project/tasks", + json={"title": "Test task"}, + headers=auth_headers + ) + assert response.status_code == 201 + data = response.json() + assert data["title"] == "Test task" +``` + +**Запуск:** +```bash +pytest tests/ -v +pytest tests/ --cov=app +``` + +### 15.2 Frontend тесты + +**Стек:** Vitest, React Testing Library + +**Пример:** +```typescript +import { render, screen } from '@testing-library/react' +import { KanbanBoard } from './KanbanBoard' + +test('renders kanban board', () => { + render() + expect(screen.getByText('To Do')).toBeInTheDocument() +}) +``` + +**Запуск:** +```bash +npm run test +npm run test:coverage +``` + +### 15.3 E2E тесты + +**Стек:** Playwright + +**Пример:** +```typescript +test('user can create task', async ({ page }) => { + await page.goto('/projects/test-project') + await page.click('[data-testid="add-task"]') + await page.fill('[data-testid="task-title"]', 'New task') + await page.click('[data-testid="save-task"]') + await expect(page.locator('.task-card')).toBeVisible() +}) +``` + +### 15.4 CI/CD + +**GitHub Actions:** +```yaml +name: Tests +on: [push, pull_request] + +jobs: + backend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + - run: pip install -r requirements.txt + - run: pytest --cov=app + + frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '20' + - run: npm ci + - run: npm run test +``` + +--- + +## 16. Производительность + +### 16.1 Оптимизации БД + +**Индексы:** +```sql +CREATE INDEX idx_tasks_project_status ON tasks(project_id, status); +CREATE INDEX idx_tasks_assignee ON tasks(assignee_id); +CREATE INDEX idx_messages_project_created ON messages(project_id, created_at DESC); +CREATE INDEX idx_members_slug ON members(slug); +``` + +**Connection pooling:** +```python +from sqlalchemy.ext.asyncio import create_async_engine + +engine = create_async_engine( + DATABASE_URL, + pool_size=20, + max_overflow=10, + pool_pre_ping=True, + pool_recycle=3600 +) +``` + +### 16.2 Кэширование + +**Redis (будущее):** +```python +import aioredis + +redis = await aioredis.create_redis_pool(REDIS_URL) + +# Cache project tasks +async def get_cached_tasks(project_id: UUID): + cached = await redis.get(f"tasks:{project_id}") + if cached: + return json.loads(cached) + + tasks = await fetch_tasks_from_db(project_id) + await redis.setex(f"tasks:{project_id}", 300, json.dumps(tasks)) + return tasks +``` + +### 16.3 WebSocket масштабирование + +**Текущее:** In-memory connections dict + +**Будущее:** Redis Pub/Sub +```python +# Horizontal scaling with Redis +await redis.publish("ws:broadcast", json.dumps({ + "project_id": str(project_id), + "event": event_type, + "data": data +})) + +# Each worker subscribes +async def redis_subscriber(): + pubsub = redis.pubsub() + await pubsub.subscribe("ws:broadcast") + async for message in pubsub.listen(): + event = json.loads(message["data"]) + await broadcast_to_local_connections(event) +``` + +### 16.4 Фронтенд оптимизации + +**Code splitting:** +```typescript +const KanbanBoard = lazy(() => import('./KanbanBoard')) +const Chat = lazy(() => import('./Chat')) +``` + +**Bundle analysis:** +```bash +npm run build -- --mode analyze +``` + +**Asset optimization:** +- Image compression (WebP) +- Lazy loading images +- Service Worker для offline + +--- + +## 17. Безопасность + +### 17.1 JWT Security + +**Token rotation:** +```python +# Short-lived access token (15 min) +access_token = create_access_token(user_id, expires_delta=timedelta(minutes=15)) + +# Long-lived refresh token (7 days) +refresh_token = create_refresh_token(user_id, expires_delta=timedelta(days=7)) +``` + +**Token validation:** +```python +async def get_current_user(token: str = Depends(oauth2_scheme)): + try: + payload = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) + if payload["exp"] < time.time(): + raise HTTPException(401, "Token expired") + return await get_user(payload["sub"]) + except jwt.InvalidTokenError: + raise HTTPException(401, "Invalid token") +``` + +### 17.2 Input Validation + +**Pydantic schemas:** +```python +from pydantic import BaseModel, validator + +class TaskCreate(BaseModel): + title: str + description: str | None + priority: Priority = Priority.medium + + @validator('title') + def validate_title(cls, v): + if len(v) < 3: + raise ValueError('Title must be at least 3 characters') + if len(v) > 200: + raise ValueError('Title too long') + return v +``` + +### 17.3 SQL Injection Protection + +**SQLAlchemy параметризованные запросы:** +```python +# ✅ Безопасно +tasks = await session.execute( + select(Task).where(Task.project_id == :project_id), + {"project_id": project_id} +) + +# ❌ НЕБЕЗОПАСНО (никогда не использовать) +query = f"SELECT * FROM tasks WHERE project_id = '{project_id}'" +``` + +### 17.4 XSS Protection + +**Frontend sanitization:** +```typescript +import DOMPurify from 'dompurify' + +const SafeHTML = ({ content }) => ( +
+) +``` + +### 17.5 Rate Limiting + +**Slowapi:** +```python +from slowapi import Limiter +from slowapi.util import get_remote_address + +limiter = Limiter(key_func=get_remote_address) + +@app.post("/api/tasks") +@limiter.limit("10/minute") +async def create_task(request: Request, task: TaskCreate): + ... +``` + +### 17.6 CORS + +**Настройка:** +```python +from fastapi.middleware.cors import CORSMiddleware + +app.add_middleware( + CORSMiddleware, + allow_origins=[ + "https://dev.team.uix.su", + "https://team.uix.su" + ], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +``` + +--- + +## 18. Мониторинг и логирование +### 18.1 Структура логов + +**Уровни:** DEBUG, INFO, WARNING, ERROR, CRITICAL + +**Формат:** +```python +import logging +import json + +class JSONFormatter(logging.Formatter): + def format(self, record): + log_obj = { + "timestamp": record.created, + "level": record.levelname, + "logger": record.name, + "message": record.getMessage(), + "module": record.module, + "function": record.funcName, + } + if record.exc_info: + log_obj["exception"] = self.formatException(record.exc_info) + return json.dumps(log_obj) + +logging.setFormatter(JSONFormatter()) +``` + +### 18.2 Prometheus метрики + +**Custom metrics:** +```python +from prometheus_client import Counter, Histogram + +# Counters +tasks_created = Counter('tasks_created_total', 'Tasks created') +messages_sent = Counter('messages_sent_total', 'Messages sent') +agents_active = Counter('agents_active', 'Active agents') + +# Histograms +request_duration = Histogram( + 'http_request_duration_seconds', + 'HTTP request duration', + ['method', 'endpoint'] +) + +@app.middleware("http") +async def metrics_middleware(request: Request, call_next): + start = time.time() + response = await call_next(request) + duration = time.time() - start + + request_duration.labels(request.method, request.url.path).observe(duration) + + return response +``` + +### 18.3 Health Checks + +**Endpoint:** +```python +@app.get("/api/health") +async def health_check(): + checks = { + "database": await check_database(), + "redis": await check_redis() if REDIS_URL else "disabled", + "storage": check_storage(), + } + + all_healthy = all(v == "ok" for v in checks.values()) + status_code = 200 if all_healthy else 503 + + return JSONResponse( + content={"status": "healthy" if all_healthy else "unhealthy", "checks": checks}, + status_code=status_code + ) +``` + +### 18.4 Error Tracking + +**Sentry интеграция:** +```python +import sentry_sdk +from sentry_sdk.integrations.fastapi import FastApiIntegration + +sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=[FastApiIntegration()], + traces_sample_rate=0.1, + environment=ENVIRONMENT, +) +``` + +### 18.5 Log Aggregation + +**ELK Stack (будущее):** +- Filebeat → Logstash → Elasticsearch → Kibana +- Или: Loki + Grafana + +--- + +## 19. Troubleshooting +### 19.1 Частые проблемы + +**Проблема:** WebSocket отключается +``` +Решение: +- Проверить nginx конфигурацию (proxy_read_timeout) +- Проверить keepalive настройки +- Клиент: reconnect logic +``` + +**Проблема:** Агент не видит задачи +``` +Решение: +- Проверить agent token +- Проверить project membership +- Проверить listen mode (all vs mentions) +``` + +**Проблема:** Медленные запросы к БД +``` +Решение: +- Проверить индексы +- Проверить connection pool +- Включить slow query log +- EXPLAIN ANALYZE запроса +``` + +### 19.2 Диагностика + +**Проверка здоровья системы:** +```bash +# Backend +curl http://localhost:8100/api/health + +# Database +psql -c "SELECT 1" + +# Redis (если есть) +redis-cli ping + +# Disk space +df -h /var/www/team-board +``` + +**Логи:** +```bash +# Tracker logs +docker-compose logs tracker | grep ERROR + +# Nginx logs +tail -f /var/log/nginx/error.log + +# System logs +journalctl -u picogent -f +``` + +**Database queries:** +```sql +-- Активные подключения +SELECT count(*) FROM pg_stat_activity; + +-- Медленные запросы +SELECT query, calls, total_time +FROM pg_stat_statements +ORDER BY total_time DESC +LIMIT 10; + +-- Размер таблиц +SELECT schemaname, tablename, + pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size +FROM pg_tables +WHERE schemaname = 'public' +ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC; +``` + +### 19.3 Recovery процедуры + +**Восстановление БД:** +```bash +# Backup +pg_dump teamboard > backup_$(date +%Y%m%d).sql + +# Restore +psql teamboard < backup_20260313.sql +``` + +**Откат миграции:** +```bash +alembic downgrade -1 +``` + +**Перезапуск сервисов:** +```bash +docker-compose restart tracker +sudo systemctl restart picogent +sudo systemctl reload nginx +``` + +--- + +## 20. Приложения +### D. Глоссарий + +| Термин | Определение | +|--------|------------| +| **Tracker** | Единый backend-сервис | +| **Picogent** | AI-агент фреймворк | +| **Member** | Участник (человек или агент) | +| **Project** | Проект с задачами и чатом | +| **Task** | Задача на канбан-доске | +| **MCP** | Model Context Protocol | +| **BFF** | Backend for Frontend (не используется) | + +### E. Changelog + +**2026-03-13 (v1.1):** +- Добавлены разделы 14-19 (Deployment, Testing, Performance, Security, Monitoring, Troubleshooting) +- Расширена документация +- Aurora Edition + +**2026-03-13 (v1.0):** +- Initial specification +- Все основные разделы + +### F. Roadmap Details + +**Q1 2026:** +- RBAC implementation +- Advanced permissions +- Audit logging + +**Q2 2026:** +- GitHub/GitLab integration +- Webhooks +- External API + +**Q3 2026:** +- Multi-tenant architecture +- SaaS preparation +- Enterprise features + +**Q4 2026:** +- Mobile application (React Native) +- Offline support +- Push notifications + +### G. API Response Examples + +**GET /api/projects:** +```json +{ + "projects": [ + { + "id": "uuid", + "slug": "my-project", + "name": "My Project", + "description": "Description", + "color": "#3B82F6", + "archived": false, + "created_at": "2026-03-13T10:00:00Z", + "members_count": 5, + "tasks_count": 23 + } + ], + "total": 10, + "page": 1, + "per_page": 20 +} +``` + +**POST /api/projects/my-project/tasks:** +```json +{ + "id": "uuid", + "slug": "TASK-123", + "title": "Implement feature X", + "description": "Detailed description", + "status": "todo", + "priority": "high", + "assignee": null, + "labels": [ + {"id": "uuid", "name": "feature", "color": "#10B981"} + ], + "created_at": "2026-03-13T10:00:00Z", + "actor": { + "id": "uuid", + "slug": "coder", + "name": "Кодер" + } +} +``` + +### H. Database Schema (ER Diagram) + +``` +┌─────────────┐ +│ Member │ +├─────────────┤ +│ id (PK) │ +│ slug │ +│ name │ +│ type │ +└──────┬──────┘ + │ + │ 1:N + │ +┌──────┴──────────┐ +│ ProjectMember │ +├────────────────┤ +│ project_id (FK)│ +│ member_id (FK) │ +│ role │ +└──────┬──────────┘ + │ + │ N:1 + │ +┌──────┴──────────┐ +│ Project │ +├────────────────┤ +│ id (PK) │ +│ slug │ +│ name │ +└──────┬─────────┘ + │ + │ 1:N + │ +┌──────┴──────────┐ +│ Task │ +├────────────────┤ +│ id (PK) │ +│ project_id (FK)│ +│ assignee_id(FK)│ +│ parent_id (FK) │ +└────────────────┘ +``` + +--- + *Документ поддерживается Авророй. Последнее обновление: 2026-03-13* +*Версия: Aurora Edition 1.1*