75 lines
2.9 KiB
Markdown
75 lines
2.9 KiB
Markdown
# Architecture
|
|
|
|
## Overview
|
|
|
|
```text
|
|
Browser
|
|
│ HTTPS (same origin)
|
|
▼
|
|
Traefik ──► frontend (Next.js :3000) paths: UI
|
|
└──► backend (Go :8080) paths: /api/*, /media/*, /c/* (via FE + API)
|
|
│
|
|
├── Postgres (stateful: users, progress, chats, certs, …)
|
|
├── Redis (rate limit, captcha, refresh metadata, pub/sub WS)
|
|
└── Object storage (RustFS/S3) videos, avatars, chat attachments
|
|
```
|
|
|
|
- **One public origin:** the browser talks only to the public domain; Traefik splits paths.
|
|
- **Backend** owns business logic, auth, media signing, and WebSocket.
|
|
- **Frontend** is an App Router SPA; data comes from React Query with `credentials: "include"`.
|
|
|
|
Edge path details: [deployment.md](deployment.md#routing)
|
|
|
|
## Backend packages
|
|
|
|
| Package | Role |
|
|
|---------|------|
|
|
| `internal/api` | HTTP handlers, middleware, captcha, rate limit, cookies |
|
|
| `internal/store` | Postgres access |
|
|
| `internal/config` | Env loading and `Validate()` |
|
|
| `internal/storage` | Local / S3; `EnsureSampleMP4` at startup |
|
|
| `internal/seed` | Admin bootstrap + optional curriculum |
|
|
| `internal/db/migrations` | Ordered SQL |
|
|
| `cmd/server` | Process entrypoint |
|
|
|
|
## Learning flow (core)
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
login[Login_CAPTCHA]
|
|
tree[Skill_tree_cap]
|
|
watch[Verified_watch]
|
|
quiz[Quiz_hearts]
|
|
unlock[Staff_unlock]
|
|
cert[Digital_cert]
|
|
login --> tree --> watch --> quiz
|
|
quiz -->|fail_hearts| lockout[Lockout]
|
|
quiz -->|pass| unlock
|
|
unlock --> watch
|
|
unlock -->|curriculum_done| cert
|
|
```
|
|
|
|
1. Watch accumulates verified time via anti-cheat heartbeats until the completion threshold.
|
|
2. Quizzes spend hearts; zero → `/lockout` until staff reopens the account.
|
|
3. Unlocking the next lesson is **staff-only**; the student requests it and follows up in chat.
|
|
4. Completing the curriculum → unlisted digital certificate; physical copy is optional and offline.
|
|
|
|
## Sessions and realtime
|
|
|
|
- Browser session: HttpOnly cookies `pargar_access` / `pargar_refresh` (+ JSON tokens for non-browser clients). Details: [security.md](security.md).
|
|
- WebSocket: `GET /api/ws` — Bearer, `Sec-WebSocket-Protocol: bearer,<jwt>`, or same-origin access cookie.
|
|
- Hub coordinates across replicas via Redis pub/sub (online presence is per-process).
|
|
|
|
## Media storage
|
|
|
|
- Lesson keys (e.g. `sample.mp4`) live in object storage; browser URLs are short-lived HMAC-signed.
|
|
- At startup, if the sample key is missing, `backend/media/sample.mp4` is uploaded.
|
|
- Chat / avatar / admin video uploads use content sniffing (not extension alone).
|
|
|
|
## Errors and observability
|
|
|
|
- Error body: JSON `{ error, code, requestId }` plus `X-Request-ID`.
|
|
- Frontend: `toUserError`, toasts for mutations, empty vs error states in lists.
|
|
- Prometheus metrics on `/metrics` (requires `METRICS_TOKEN` outside DevMode).
|
|
- Optional OTLP via `OTEL_*`.
|