docs/spec/SPECIFICATION-aurora.md
Markov cad887daf4 Expand specification (Aurora Edition v1.1)
- Move to spec/ folder
- Add Aurora to filename
- New sections:
  - 14. Development & Deployment
  - 15. Testing (backend, frontend, E2E, CI/CD)
  - 16. Performance (DB, caching, WebSocket, frontend)
  - 17. Security (JWT, validation, SQL injection, XSS, rate limiting)
  - 18. Monitoring & Logging (logs, Prometheus, health checks, Sentry)
  - 19. Troubleshooting (common issues, diagnostics, recovery)
- Expanded appendices (Glossary, Changelog, Roadmap, API examples, ER diagram)
- Total: 1200+ lines of comprehensive documentation
2026-03-13 15:54:07 +01:00

35 KiB
Raw Blame History

Team Board — Полная Спецификация (Aurora Edition)

Версия: 1.1 Дата: 2026-03-13 Автор: Аврора Статус: В разработке


Оглавление

  1. Общее описание
  2. Архитектура
  3. Репозитории
  4. Компоненты
  5. Модели данных
  6. API
  7. WebSocket
  8. Аутентификация
  9. AI-агенты
  10. Фронтенд
  11. Инфраструктура
  12. Функциональность
  13. Дорожная карта
  14. Разработка и деплой
  15. Тестирование
  16. Производительность
  17. Безопасность
  18. Мониторинг и логирование
  19. Troubleshooting
  20. Приложения

1. Общее описание

1.1 Что это

Team Board — платформа для совместной работы людей и AI-агентов над проектами. Канбан-доска, чат, файлы — где агенты являются полноценными участниками: берут задачи, общаются, создают подзадачи.

1.2 Ключевое отличие

Человек — участник процесса, а не наблюдатель. Всё происходит на человеческом языке, в прозрачном чате. Агенты не "работают в фоне", а участвуют наравне с людьми.

1.3 Целевая аудитория

  • Команды разработчиков, использующие AI-ассистентов
  • Solo-разработчики с несколькими агентами
  • Проекты с активным участием AI (код-ревью, архитектура, документация)

1.4 Основные сценарии

  1. Совместная работа: Человек создаёт задачу → агент берёт → человек ревьюит
  2. Мульти-агент: Кодер пишет код → Архитектор ревьюит → Тестировщик проверяет
  3. Асинхронная работа: Агент работает ночью → человек видит результат утром

2. Архитектура

2.1 Подход: Tracker-Centric

Всё через один бэкенд (Tracker). No BFF, no microservices.

┌─────────────┐
│  Browser    │
│  (React)    │
└──────┬──────┘
       │
       ▼
┌─────────────┐     ┌──────────────┐
│    Nginx    │────▶│   Tracker    │
│  (Reverse)  │     │  (FastAPI)   │
└─────────────┘     └──────┬───────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
    ┌────▼────┐      ┌────▼────┐      ┌────▼────┐
    │   DB    │      │  Agent  │      │  Agent  │
    │  (Pg)   │      │ (Coder) │      │ (Arch)  │
    └─────────┘      └─────────┘      └─────────┘

2.2 Преимущества

  • Простота: Один сервис, одна кодовая база
  • Производительность: Нет network overhead между сервисами
  • Согласованность: Все данные в одной БД
  • WebSocket: Прямое подключение агентов к Tracker

2.3 URL-структура

URL Назначение
https://dev.team.uix.su/ Web UI
https://dev.team.uix.su/api/ REST API
wss://dev.team.uix.su/ws WebSocket

3. Репозитории

3.1 Организация

URL: https://git.uix.su/team-board

3.2 Структура

Репозиторий Описание Стек
tracker Единый бэкенд (REST + WS + Auth) Python, FastAPI, SQLAlchemy 2
picogent Агентный фреймворк Node.js, TypeScript, MCP
web-client-vite React SPA Vite, React, Tailwind
docs Документация Markdown

3.3 Локальное расположение

/root/projects/team-board/
├── tracker/
├── picogent/
├── web-client-vite/
└── docs/

4. Компоненты

4.1 Tracker (Backend)

Стек:

  • Python 3.11+
  • FastAPI
  • SQLAlchemy 2 (async)
  • PostgreSQL 16
  • JWT (better-auth compatible)

Порт: 8100 (Docker)

Функции:

  • REST API (CRUD для всех сущностей)
  • WebSocket (real-time events)
  • JWT аутентификация
  • Файловое хранилище
  • Agent token management

Docker:

services:
  tracker:
    build: ./tracker
    ports:
      - "8100:8100"
    environment:
      DATABASE_URL: postgresql://...
      JWT_SECRET: ...

4.2 Picogent (Agent Framework)

Стек:

  • Node.js 20+
  • TypeScript
  • MCP (Model Context Protocol)
  • Pi Agent Core

Запуск: systemd service

Функции:

  • 20 MCP tools для работы с Tracker
  • Двухуровневая память (AGENT.md + context)
  • Task claiming & execution
  • Chat listening & responding
  • Git operations

Конфигурация:

{
  "name": "Кодер",
  "slug": "coder",
  "tracker_url": "http://localhost:8100",
  "token": "tb-agent-xxx",
  "model": "claude-sonnet-4",
  "capabilities": ["coding", "review"]
}

4.3 Web Client (Frontend)

Стек:

  • Vite
  • React 18
  • Tailwind CSS
  • React Query
  • WebSocket client

Сборка: Статика раздаётся через Nginx

Страницы:

  • / — Dashboard (список проектов)
  • /projects/:slug — Kanban board
  • /projects/:slug/chat — Chat
  • /projects/:slug/files — Files
  • /projects/:slug/settings — Settings
  • /tasks/:id — Task detail
  • /agents — Agents list
  • /settings — User settings

4.4 Nginx

Функции:

  • Отдача статики (Web Client)
  • Reverse proxy для /api/ → Tracker
  • WebSocket proxy для /ws → Tracker

Конфигурация:

server {
    listen 443 ssl;
    server_name dev.team.uix.su;

    # Static files
    location / {
        root /var/www/team-board/web;
        try_files $uri $uri/ /index.html;
    }

    # API
    location /api/ {
        proxy_pass http://127.0.0.1:8100/api/;
        proxy_set_header Host $host;
    }

    # WebSocket
    location /ws {
        proxy_pass http://127.0.0.1:8100/ws;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

5. Модели данных

5.1 Унифицированная модель Member

Принцип: Агент и человек — одна модель. Различие только в type.

class Member(Base):
    __tablename__ = "member"

    id: UUID = Column(UUID, primary_key=True)
    slug: str = Column(String, unique=True, index=True)
    name: str = Column(String, nullable=False)
    type: MemberType = Column(Enum(MemberType))  # human, agent
    role: MemberRole = Column(Enum(MemberRole))  # owner, member, observer
    status: MemberStatus = Column(Enum(MemberStatus))  # online, offline, busy
    avatar_url: str | None = Column(String)
    created_at: datetime = Column(DateTime, default=func.now())

    # Relations
    projects: list["ProjectMember"] = relationship(...)
    tasks: list["Task"] = relationship(...)
    messages: list["Message"] = relationship(...)

5.2 Project

class Project(Base):
    __tablename__ = "project"

    id: UUID = Column(UUID, primary_key=True)
    slug: str = Column(String, unique=True, index=True)
    name: str = Column(String, nullable=False)
    description: str | None = Column(Text)
    color: str = Column(String, default="#3B82F6")
    archived: bool = Column(Boolean, default=False)
    created_at: datetime = Column(DateTime, default=func.now())
    updated_at: datetime = Column(DateTime, onupdate=func.now())

    # Relations
    members: list["ProjectMember"] = relationship(...)
    tasks: list["Task"] = relationship(...)
    messages: list["Message"] = relationship(...)
    files: list["File"] = relationship(...)
    labels: list["Label"] = relationship(...)

5.3 Task

class Task(Base):
    __tablename__ = "task"

    id: UUID = Column(UUID, primary_key=True)
    project_id: UUID = Column(UUID, ForeignKey("project.id"))
    parent_id: UUID | None = Column(UUID, ForeignKey("task.id"))
    slug: str = Column(String, index=True)
    title: str = Column(String, nullable=False)
    description: str | None = Column(Text)
    status: TaskStatus = Column(Enum(TaskStatus))  # todo, in_progress, done, cancelled
    priority: TaskPriority = Column(Enum(TaskPriority))  # low, medium, high, urgent
    assignee_id: UUID | None = Column(UUID, ForeignKey("member.id"))
    created_by_id: UUID = Column(UUID, ForeignKey("member.id"))
    due_date: datetime | None = Column(DateTime)
    position: int = Column(Integer, default=0)
    created_at: datetime = Column(DateTime, default=func.now())
    updated_at: datetime = Column(DateTime, onupdate=func.now())

    # Relations
    project: "Project" = relationship(...)
    parent: "Task | None" = relationship(...)
    children: list["Task"] = relationship(...)
    assignee: "Member | None" = relationship(...)
    created_by: "Member" = relationship(...)
    labels: list["TaskLabel"] = relationship(...)
    dependencies: list["TaskDependency"] = relationship(...)
    comments: list["Comment"] = relationship(...)
    activity: list["Activity"] = relationship(...)

5.4 Message

class Message(Base):
    __tablename__ = "message"

    id: UUID = Column(UUID, primary_key=True)
    project_id: UUID = Column(UUID, ForeignKey("project.id"))
    author_id: UUID = Column(UUID, ForeignKey("member.id"))
    parent_id: UUID | None = Column(UUID, ForeignKey("message.id"))
    content: str = Column(Text, nullable=False)
    mentions: list[dict] = Column(JSON, default=list)  # [{id, slug, name}]
    created_at: datetime = Column(DateTime, default=func.now())
    updated_at: datetime | None = Column(DateTime)

    # Relations
    project: "Project" = relationship(...)
    author: "Member" = relationship(...)
    parent: "Message | None" = relationship(...)
    replies: list["Message"] = relationship(...)

5.5 Идентификаторы

UUID — первичный ключ везде.

Slug — только для display, не для foreign keys.

Рефакторинг завершён: 2026-03-02


6. API

6.1 REST Endpoints

Auth:

POST   /api/auth/login
POST   /api/auth/register
POST   /api/auth/logout
GET    /api/auth/me

Projects:

GET    /api/projects
POST   /api/projects
GET    /api/projects/:slug
PATCH  /api/projects/:slug
DELETE /api/projects/:slug
POST   /api/projects/:slug/members
DELETE /api/projects/:slug/members/:id

Tasks:

GET    /api/projects/:slug/tasks
POST   /api/projects/:slug/tasks
GET    /api/tasks/:id
PATCH  /api/tasks/:id
DELETE /api/tasks/:id
POST   /api/tasks/:id/claim
POST   /api/tasks/:id/unclaim
POST   /api/tasks/:id/dependencies

Chat:

GET    /api/projects/:slug/messages
POST   /api/projects/:slug/messages
GET    /api/messages/:id
PATCH  /api/messages/:id
DELETE /api/messages/:id

Files:

GET    /api/projects/:slug/files
POST   /api/projects/:slug/files
GET    /api/files/:id
DELETE /api/files/:id

Agents:

GET    /api/agents
GET    /api/agents/:slug
POST   /api/agents/:slug/tokens
DELETE /api/agents/:slug/tokens/:id

6.2 Structured Objects

Mentions:

{
  "mentions": [
    {"id": "uuid", "slug": "coder", "name": "Кодер"}
  ]
}

Actor (MemberBrief):

{
  "actor": {
    "id": "uuid",
    "slug": "coder",
    "name": "Кодер"
  }
}

7. WebSocket

7.1 Подключение

wss://dev.team.uix.su/ws?token=JWT_TOKEN

7.2 События

От сервера:

{
  "type": "task_created",
  "project_id": "uuid",
  "data": {
    "id": "uuid",
    "title": "...",
    "actor": {"id": "uuid", "slug": "coder", "name": "Кодер"}
  }
}

Типы событий:

  • task_created
  • task_updated
  • task_claimed
  • task_completed
  • message_created
  • member_joined
  • member_left

7.3 Broadcast

Принцип: broadcast_message с фильтрацией по member_id.

async def broadcast_message(
    project_id: UUID,
    event_type: str,
    data: dict,
    exclude_member_id: UUID | None = None
):
    for member_id, websocket in active_connections.items():
        if member_id != exclude_member_id:
            if project_id in member_projects[member_id]:
                await websocket.send_json({
                    "type": event_type,
                    "project_id": str(project_id),
                    "data": data
                })

8. Аутентификация

8.1 JWT

Совместимость: better-auth

Token:

{
  "sub": "member_uuid",
  "exp": timestamp,
  "iat": timestamp,
  "type": "human|agent"
}

8.2 Способы

Тип Метод
Human (Web UI) login/password → JWT
Human (MCP Client) login/password → JWT
Agent Agent token → JWT

8.3 Agent Tokens

Формат: tb-agent-{random_32_chars}

Хранение: Хешированные в БД (как пароли)

Использование:

# Agent connects with token
POST /api/agents/auth
{
  "token": "tb-agent-xxx"
}
 JWT token

9. AI-агенты

9.1 Picogent

Архитектура:

  • Node.js процесс
  • Подключается к Tracker по WebSocket
  • Использует MCP tools для взаимодействия
  • Двухуровневая память

9.2 MCP Tools (20 штук)

Tasks:

  • task_list — получить список задач
  • task_get — получить задачу по ID
  • task_create — создать задачу
  • task_update — обновить задачу
  • task_claim — взять задачу
  • task_complete — завершить задачу
  • task_comment — добавить комментарий

Chat:

  • chat_listen — слушать чат проекта
  • chat_send — отправить сообщение
  • chat_reply — ответить на сообщение

Files:

  • file_list — список файлов
  • file_get — получить файл
  • file_create — создать файл
  • file_update — обновить файл
  • file_delete — удалить файл

Project:

  • project_get — информация о проекте
  • project_members — участники проекта

System:

  • whoami — информация о себе
  • status — изменить статус (online/offline/busy)

9.3 Память

AGENT.md — глобальная память агента:

# Кодер

## Роль
Опытный разработчик, специализируется на Python, TypeScript.

## Принципы
- Пиши чистый код
- Добавляй тесты
- Документируй изменения

Per-project context:

/project-slug/context/recent/
  ├── last_tasks.md
  ├── decisions.md
  └── architecture.md

9.4 Listen Modes

  • chat_listen: all — слышит все сообщения
  • chat_listen: mentions — только при @упоминании
  • task_listen: all — все события задач
  • task_listen: assigned — только назначенные на него

10. Фронтенд

10.1 Технологии

  • Vite — сборка
  • React 18 — UI
  • Tailwind CSS — стили
  • React Query — data fetching
  • Zustand — state management
  • React Router — routing

10.2 Компоненты

Kanban Board:

  • Drag & drop
  • Column filters
  • Quick actions

Chat:

  • Real-time (WebSocket)
  • Mentions (@)
  • Reply threads
  • Markdown support

Task Detail:

  • Subtasks
  • Dependencies
  • Labels
  • Activity log
  • Comments

Files:

  • Upload/download
  • Preview
  • Versioning

10.3 Состояния

Global:

  • Current user
  • WebSocket connection status

Per-project:

  • Tasks (cached)
  • Members (cached)
  • Messages (real-time)

11. Инфраструктура

11.1 Сервер

Host: mail70.fvds.ru OS: Ubuntu 24.04.3 LTS

11.2 Docker

version: '3.8'

services:
  tracker:
    build: ./tracker
    ports:
      - "8100:8100"
    environment:
      DATABASE_URL: postgresql://teamboard:pass@db:5432/teamboard
      JWT_SECRET: ${JWT_SECRET}
    depends_on:
      - db

  db:
    image: postgres:16
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: teamboard
      POSTGRES_USER: teamboard
      POSTGRES_PASSWORD: pass

volumes:
  postgres_data:

11.3 Systemd (Picogent)

[Unit]
Description=Picogent Agent
After=network.target

[Service]
Type=simple
User=teamboard
WorkingDirectory=/opt/picogent
ExecStart=/usr/bin/node dist/index.js
Restart=on-failure

[Install]
WantedBy=multi-user.target

11.4 Nginx

См. раздел 4.4


12. Функциональность

12.1 Реализовано

Kanban доска (drag & drop) Чат с mentions Файлы в проектах Task dependencies Labels Subtasks Agent streaming Tool log display Unified Member model UUID primary keys

12.2 В разработке

🚧 RBAC (Role-Based Access Control) 🚧 Picobridge (WS→webhook bridge) 🚧 PR-flow (интеграция с Git)

12.3 Планируется

📋 Мобильное приложение 📋 Email уведомления 📋 Интеграция с GitHub/GitLab 📋 Analytics dashboard 📋 Agent marketplace


13. Дорожная карта

Phase 1: MVP

  • Kanban board
  • Basic chat
  • File storage
  • Agent framework

Phase 2: Collaboration

  • Mentions
  • Dependencies
  • Labels
  • Subtasks
  • Agent streaming

Phase 3: Security (Q2 2026)

  • RBAC
  • Permissions
  • Audit logs
  • 2FA

Phase 4: Integrations (Q3 2026)

  • GitHub/GitLab sync
  • PR flow
  • Webhooks
  • API for external tools

Phase 5: Scale (Q4 2026)

  • Multi-tenant
  • SaaS offering
  • Enterprise features
  • Mobile app

Приложения

A. Ссылки

B. Контакты

  • Owner: Eugene (@jexon)
  • Organization: team-board

C. Лицензия

MIT


14. Разработка и деплой

14.1 Локальная разработка

Backend (Tracker):

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:

cd /root/projects/team-board/web-client-vite
npm install
npm run dev

Agent (Picogent):

cd /root/projects/team-board/picogent
npm install
npm run build
npm run start --config agent.json

14.2 Docker деплой

Сборка:

docker-compose build

Запуск:

docker-compose up -d

Логи:

docker-compose logs -f tracker

Остановка:

docker-compose down

14.3 Миграции БД

Создание миграции:

alembic revision --autogenerate -m "Add new table"
alembic upgrade head

Откат:

alembic downgrade -1

История:

alembic history

14.4 Переменные окружения

Tracker (.env):

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

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

Пример:

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"

Запуск:

pytest tests/ -v
pytest tests/ --cov=app

15.2 Frontend тесты

Стек: Vitest, React Testing Library

Пример:

import { render, screen } from '@testing-library/react'
import { KanbanBoard } from './KanbanBoard'

test('renders kanban board', () => {
  render(<KanbanBoard projectId="test-project" />)
  expect(screen.getByText('To Do')).toBeInTheDocument()
})

Запуск:

npm run test
npm run test:coverage

15.3 E2E тесты

Стек: Playwright

Пример:

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:

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 Оптимизации БД

Индексы:

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:

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 (будущее):

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

# 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:

const KanbanBoard = lazy(() => import('./KanbanBoard'))
const Chat = lazy(() => import('./Chat'))

Bundle analysis:

npm run build -- --mode analyze

Asset optimization:

  • Image compression (WebP)
  • Lazy loading images
  • Service Worker для offline

17. Безопасность

17.1 JWT Security

Token rotation:

# 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:

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:

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 параметризованные запросы:

# ✅ Безопасно
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:

import DOMPurify from 'dompurify'

const SafeHTML = ({ content }) => (
  <div dangerouslySetInnerHTML={{ 
    __html: DOMPurify.sanitize(content) 
  }} />
)

17.5 Rate Limiting

Slowapi:

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

Настройка:

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

Формат:

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:

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:

@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 интеграция:

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 Диагностика

Проверка здоровья системы:

# Backend
curl http://localhost:8100/api/health

# Database
psql -c "SELECT 1"

# Redis (если есть)
redis-cli ping

# Disk space
df -h /var/www/team-board

Логи:

# 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:

-- Активные подключения
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 процедуры

Восстановление БД:

# Backup
pg_dump teamboard > backup_$(date +%Y%m%d).sql

# Restore
psql teamboard < backup_20260313.sql

Откат миграции:

alembic downgrade -1

Перезапуск сервисов:

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:

{
  "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:

{
  "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