Files
LiveServer-M1/CLAUDE.md
joylessorchid f9f8d22f8d docs: update CLAUDE.md and README.md for v0.0.1
- CLAUDE.md: add dev:https command, UI Architecture section (design
  system, room layout, screen share, sidebar), new Known Issues (HTTPS,
  chat duplication, self-ban), https:// in trustedOrigins note
- README.md: add dev:https command, features list for v0.0.1, screen
  share mention, HTTPS note for LAN, self-ban protection in API table
2026-03-24 12:39:52 +03:00

251 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**LiveServer-M1** — образовательная видеоконференц-платформа на базе LiveKit.
Ключевые фичи:
- AI-ассистент: авто-подключение к комнатам, real-time транскрипция (Deepgram), суммаризация лекций (OpenAI GPT)
- Защита от Zoom-bombing: lobby/waiting room, PIN-коды, kick/ban по session fingerprint, panic button
- Пост-лекционная панель: чат, файлы, транскрипт, AI-саммари, экспорт в PDF/Google Drive
Полная спецификация: **`PROMPT.md`**
## Tech Stack
| Слой | Технологии |
|------|-----------|
| Frontend | Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS v4 |
| Video | LiveKit Cloud → self-hosted, `@livekit/components-react` |
| Backend | Next.js Route Handlers, `livekit-server-sdk` |
| Auth | `better-auth` + Prisma adapter |
| AI Agent | Python, `livekit-agents`, `livekit-plugins-deepgram` (STT), `livekit-plugins-openai` (LLM) |
| DB | PostgreSQL + Prisma 7 ORM + PgBouncer (connection pooling) |
| Cache/PubSub | Redis 7 (rate limiting, lobby pub/sub, chat real-time, caching) |
| Storage | MinIO (S3-compatible) |
| Proxy | Traefik v3 + Let's Encrypt (prod only) |
## Architecture
```
├── src/
│ ├── app/
│ │ ├── api/ # 15 API route handlers
│ │ │ ├── auth/[...all]/ # better-auth catch-all
│ │ │ ├── rooms/ # CRUD, by-code lookup
│ │ │ ├── rooms/[roomId]/ # join, lobby, lobby/stream (SSE), chat, files, moderate, hand-raise, start, end
│ │ │ └── livekit/ # token generation, webhook receiver
│ │ ├── (dashboard)/ # Host/Admin pages
│ │ ├── join/[code]/ # Guest entry
│ │ ├── room/[code]/ # Video room (LiveKit)
│ │ ├── login/ & register/ # Auth pages
│ │ └── page.tsx # Landing
│ ├── components/
│ │ ├── room/ # ChatPanel, ModerationPanel (with self-ban protection)
│ │ └── lobby/ # WaitingRoom, LobbyManager (scrollable)
│ ├── lib/ # prisma, auth, auth-helpers, livekit, redis, rate-limit, lobby-pubsub, chat-pubsub
│ ├── middleware.ts # Dev protection (DEV_ACCESS_KEY, ALLOWED_IPS)
│ └── types/
├── ai-agent/ # Python LiveKit Agent
├── prisma/schema.prisma # 9 models, 3 enums
├── docker-compose.yml # Base: postgres, minio, redis, pgbouncer, ai-agent, app
├── docker-compose.override.yml # Local dev: direct ports, no SSL
├── docker-compose.prod.yml # Traefik + Let's Encrypt
└── Dockerfile # Multi-stage Next.js standalone build
```
### Key Data Flows
1. **Guest join (with lobby):**
Guest → POST `/api/rooms/[id]/join` → LobbyEntry(PENDING) → SSE via Redis pub/sub → Host approves → LiveKit token → connect
2. **Real-time chat:**
POST message → DB + Redis PUBLISH → SSE `/api/rooms/[id]/chat/stream` → instant delivery to all participants
3. **Real-time transcription:**
Audio tracks → AI Agent (Deepgram STT) → DataChannel → Live captions
4. **Post-lecture (`room_finished` webhook):**
Transcript → OpenAI GPT → LectureArtifact → `/lectures/[id]`
### User Roles
- **ADMIN** — global panel, all rooms monitoring
- **HOST** — room creation, moderation, security
- **GUEST** — join by link, no registration required
### Security Layers
- `sessionFingerprint` (required) for bans — IP is secondary only
- PIN hashed with bcrypt, rate-limited (5 attempts/min per IP)
- Chat/files auth: verified via ParticipantHistory
- SSE lobby: verified against existing LobbyEntry
- Token generation: only room owner can get LiveKit token
- `DEV_ACCESS_KEY` middleware: protects local dev from network access
- LiveKit webhook: signature verification via `WebhookReceiver`
## Performance Architecture
```
Clients → Traefik (LB) → Next.js (x2 replicas) → PgBouncer (pool 25, max 500) → PostgreSQL
→ Redis (pub/sub, cache, rate limit)
→ LiveKit (video/audio SFU)
```
- **Redis pub/sub** replaces DB polling for lobby SSE and chat — instant delivery (<10ms)
- **PgBouncer** multiplexes 500 client connections into 25 real PostgreSQL connections
- **Chat SSE** via Redis pub/sub + optimistic UI on client — messages appear before server confirms
- **Rate limiting** via Redis INCR+EXPIRE — works across multiple app replicas
- **Chat cache** in Redis (10s TTL) — reduces DB reads during active rooms
- **Prisma select** — only fetch needed columns, not full rows
## Commands
```bash
# Dev
npm run dev # Next.js dev server (localhost:3000)
npm run dev:https # Dev server with self-signed HTTPS (media devices on LAN)
docker compose up -d postgres minio redis pgbouncer # DB + Storage + Redis + PgBouncer
npm run lint # TypeScript type-check (tsc --noEmit)
# Database
npx prisma migrate dev # Create + apply migration
npx prisma db push # Quick schema sync (no migration)
npx prisma studio # DB GUI
npx prisma generate # Regenerate Prisma Client
# Build
npm run build -- --webpack # Production build (Webpack, not Turbopack — WASM limitation on Windows)
# Docker (full stack)
docker compose up -d --build # Local
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build # Prod (with Traefik)
# AI Agent
cd ai-agent && python main.py start
# Setup script (universal installer/doctor)
./setup.sh # Interactive menu
./setup.sh dev # Kill old + pull + deps + containers + wait healthy + prisma + run
./setup.sh install # Full first-time setup (auto-generates passwords)
./setup.sh update # git pull + npm install + prisma + rebuild
./setup.sh doctor # Diagnose & auto-fix common issues
./setup.sh status # Health check all services
./setup.sh restart # Restart all containers
./setup.sh logs [service] # Tail service logs (default: app)
./setup.sh admin # Promote user to ADMIN role
./setup.sh reset # Full teardown (with confirmation)
```
## Environment Variables
See `.env.example` for all variables. Key ones:
```env
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/liveserver
LIVEKIT_URL=wss://...
LIVEKIT_API_KEY=...
LIVEKIT_API_SECRET=...
BETTER_AUTH_SECRET=...
# Auth — trusted origins for non-localhost access (comma-separated)
BETTER_AUTH_TRUSTED_ORIGINS=http://192.168.1.78:3000,http://localhost:3000
# LAN IP for auto trustedOrigins in dev (auto-detected by ./setup.sh dev)
# LAN_HOST=192.168.1.78
# Local dev protection (when DOMAIN is not set)
DEV_ACCESS_KEY=mySecretKey123
# ALLOWED_IPS=192.168.1.10,192.168.1.11
# Production
# DOMAIN=live.example.com
# ACME_EMAIL=admin@example.com
```
## Deployment Modes
| Mode | Command | Description |
|------|---------|-------------|
| **Local dev** | `docker compose up -d` | Direct ports (3000, 5432, 9000). `docker-compose.override.yml` auto-applied |
| **Production** | `docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d` | Traefik reverse proxy, auto SSL via Let's Encrypt |
## Prisma 7 Notes
- `prisma.config.ts` in project root — `migrate.url()` provides DATABASE_URL for CLI commands (`prisma db push`, `prisma migrate`)
- **No `url` in schema datasource** — Prisma 7 forbids it, only `provider` allowed
- `PrismaClient` uses `@prisma/adapter-pg` (driver adapter) — `new PrismaClient({ adapter })` with `PrismaPg({ connectionString })`
- `new PrismaClient()` without adapter **will crash** in Prisma 7 (no datasourceUrl/datasources support)
- Use `-- --webpack` flag for `next build` on Windows (Turbopack WASM issue)
## Auth Notes
- `better-auth` handles registration/login via `/api/auth/[...all]` catch-all route
- **Client:** `auth-client.ts` uses `createAuthClient()` without `baseURL` — auto-detects current origin (works from any IP/domain)
- **Server:** `auth.ts` uses `BETTER_AUTH_URL` for `baseURL` and `BETTER_AUTH_TRUSTED_ORIGINS` (comma-separated) for CSRF origin validation. Without `BETTER_AUTH_TRUSTED_ORIGINS`, auto-detects all machine IPs via `os.networkInterfaces()` + localhost on ports 30003010, both `http://` and `https://` protocols
- First admin: first registered user automatically becomes ADMIN (via `databaseHooks.user.create.before` in `auth.ts`)
- **No `"type"` field in `package.json`** — removed to fix Turbopack ESM/CJS conflict in dev mode. Next.js handles ESM in `.ts/.tsx` automatically
## Agent Orchestration
| Task | Agent |
|------|-------|
| API routes, backend logic | `backend-architect` |
| React components, LiveKit UI | `frontend-developer` |
| DB schema, indexes, queries | `database-optimizer` |
| Python AI Agent, Deepgram/OpenAI | `ai-engineer` |
| System architecture | `software-architect` |
| UI design, components | `ui-designer` |
| UX flows, accessibility | `ux-architect` |
| Code review before commit | `code-reviewer` |
**Rule:** for tasks spanning 2+ layers — use `agents-orchestrator`.
## Post-Change Checklist (ОБЯЗАТЕЛЬНО)
После **каждого** изменения кода или конфигурации Claude **обязан** выполнить:
1. **Обновить `CLAUDE.md`** — если изменились архитектура, endpoints, модели, команды, стек или conventions
2. **Проверить `setup.sh`** — все новые сервисы/зависимости должны быть в скрипте (readiness checks, .env переменные, docker compose команды)
3. **Проверить `README.md`** — инструкции, API таблицы, структура проекта должны отражать текущее состояние
4. **Проверить `PROMPT.md`** — спецификация должна соответствовать реализации
5. **Проверить `.env.example`** — все новые переменные окружения должны быть задокументированы
Не пропускать этот чеклист. Если изменение не затрагивает файл — просто подтвердить что файл актуален.
## Known Issues & Fixes
| Issue | Cause | Fix |
|-------|-------|-----|
| White screen in dev (ESM/CJS conflict) | `"type": "module"` or `"type": "commonjs"` in package.json | Remove the `"type"` field entirely — Next.js doesn't need it |
| Auth silently fails from LAN | `auth-client.ts` had hardcoded `baseURL` → requests went to localhost from browser | Removed `baseURL`, added `trustedOrigins` config |
| `setup.sh doctor` crashes on .env check | Comments in `.env.example` parsed as variable names by `set -euo pipefail` | Fixed parsing logic |
| `setup.sh update` runs git pull after stash declined | Missing `else` branch after stash prompt | Fixed control flow |
| Auth form resets on non-localhost (no error shown) | `trustedOrigins` fallback only had `localhost:3000`, CSRF rejected other origins silently | `auth.ts` auto-allows ports 30003010 + `LAN_HOST`; `setup.sh dev` auto-detects LAN IP |
| Media devices blocked on LAN (not HTTPS) | Browsers require secure context for getUserMedia | `npm run dev:https` (Next.js `--experimental-https`), `setup.sh dev` auto-uses HTTPS |
| Chat messages duplicated | Race: SSE delivers msg before POST response, seenIds doesn't have real id yet | Check if SSE already delivered the message before replacing optimistic |
| Ban self crashes room | No server-side check for self-ban/kick | API rejects `targetSessionId === session.user.id`, UI hides buttons for self |
## UI Architecture
- **Design system:** CSS custom properties in `globals.css` via Tailwind v4 `@theme` — surface levels (03), accent (indigo), status colors, border tokens
- **Room layout (Google Meet style):**
- Minimal top bar (room name + code)
- Video area: `GridLayout` (cameras) or `FocusLayout` (screen share active — carousel + focused view)
- Bottom control bar: mic, camera, screen share | chat, lobby, moderation, hand raise | leave/end
- **Screen share:** auto-detected via `useTracks(Track.Source.ScreenShare)`, switches to focus layout with `CarouselLayout` for camera feeds
- **Sidebar:** 320px right panel for chat, lobby manager, moderation — stacked, each with own scroll
- **Components use LiveKit hooks:** `useLocalParticipant`, `useParticipants`, `useRoomContext`, `useTracks`
## Conventions
- Communication language: Russian
- Guests don't register, but `user_id` reserved for future LMS
- `sessionId` (UUID) is the cross-cutting key linking LobbyEntry, ParticipantHistory, ChatMessage, SharedFile for guests
- `Room.code` is the user-facing invite code (not the DB id)
- Files store `fileKey` (S3 object key), not full URLs
- `LectureArtifact` is 1:1 with Room
- All cascade deletes: removing Room removes all related data
- MinIO stays as S3 storage — GitHub repo archived (Feb 2026) but Docker image still maintained, no viable lightweight alternative