lila/README.md
2026-04-19 18:40:01 +02:00

170 lines
4.8 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.

# lila
**Learn words. Beat friends.**
lila is a vocabulary trainer built around a Duolingo-style quiz loop: a word appears in one language, you pick the correct translation from four choices. It supports singleplayer and real-time multiplayer, and is designed to work across multiple language pairs without schema changes.
Live at [lilastudy.com](https://lilastudy.com).
---
## Stack
| Layer | Technology |
|---|---|
| Monorepo | pnpm workspaces |
| Frontend | React 18, Vite, TypeScript |
| Routing | TanStack Router |
| Server state | TanStack Query |
| Styling | Tailwind CSS |
| Backend | Node.js, Express, TypeScript |
| Database | PostgreSQL + Drizzle ORM |
| Validation | Zod (shared schemas) |
| Auth | Better Auth (Google + GitHub) |
| Realtime | WebSockets (`ws` library) |
| Testing | Vitest, supertest |
| Deployment | Docker Compose, Caddy, Hetzner VPS |
| CI/CD | Forgejo Actions |
---
## Repository Structure
```
lila/
├── apps/
│ ├── api/ — Express backend
│ └── web/ — React frontend
├── packages/
│ ├── shared/ — Zod schemas and types shared between frontend and backend
│ └── db/ — Drizzle schema, migrations, models, seeding scripts
├── scripts/ — Python scripts for vocabulary data extraction
└── documentation/ — Project docs
```
`packages/shared` is the contract between frontend and backend. All request/response shapes are defined there as Zod schemas and never duplicated.
---
## Architecture
Requests flow through a strict layered architecture:
```
HTTP Request → Router → Controller → Service → Model → Database
```
Each layer only talks to the layer directly below it. Controllers handle HTTP only. Services contain business logic only. Models contain database queries only. All database code lives in `packages/db` — the API never imports Drizzle directly for queries.
---
## Data Model
Words are modelled as language-neutral concepts (`terms`) with per-language `translations`. Adding a new language requires no schema changes — only new rows. CEFR levels (A1C2) are stored per translation for difficulty filtering.
Core tables: `terms`, `translations`, `term_glosses`, `decks`, `deck_terms`
Auth tables (managed by Better Auth): `user`, `session`, `account`, `verification`
Vocabulary data is sourced from WordNet and the Open Multilingual Wordnet (OMW).
---
## API
```
POST /api/v1/game/start — start a quiz session (auth required)
POST /api/v1/game/answer — submit an answer (auth required)
GET /api/v1/health — health check (public)
ALL /api/auth/* — Better Auth handlers (public)
```
The correct answer is never sent to the frontend — all evaluation happens server-side.
---
## Multiplayer
Rooms are created via REST, then managed over WebSockets. Messages are typed via a Zod discriminated union. The host starts the game; all players answer simultaneously with a 15-second server-enforced timer. Room state is held in-memory (Valkey deferred).
---
## Infrastructure
```
Internet → Caddy (HTTPS)
├── lilastudy.com → web (nginx, static files)
├── api.lilastudy.com → api (Express)
└── git.lilastudy.com → Forgejo (git + registry)
```
Deployed on a Hetzner VPS (Debian 13, ARM64). Images are built cross-compiled for ARM64 and pushed to the Forgejo container registry. CI/CD runs via Forgejo Actions on push to `main`. Daily database backups are synced to the dev laptop via rsync.
See `documentation/deployment.md` for the full infrastructure setup.
---
## Local Development
### Prerequisites
- Node.js 20+
- pnpm 9+
- Docker + Docker Compose
### Setup
```bash
# Install dependencies
pnpm install
# Create your local env file (used by docker compose + the API)
cp .env.example .env
# Start local services (PostgreSQL, Valkey)
docker compose up -d
# Build shared packages
pnpm --filter @lila/shared build
pnpm --filter @lila/db build
# Run migrations and seed data
pnpm --filter @lila/db migrate
pnpm --filter @lila/db seed
# Start dev servers
pnpm dev
```
The API runs on `http://localhost:3000` and the frontend on `http://localhost:5173`.
---
## Testing
```bash
# All tests
pnpm test
# API only
pnpm --filter api test
# Frontend only
pnpm --filter web test
```
---
## Roadmap
| Phase | Description | Status |
|---|---|---|
| 0 | Foundation — monorepo, tooling, dev environment | ✅ |
| 1 | Vocabulary data pipeline + REST API | ✅ |
| 2 | Singleplayer quiz UI | ✅ |
| 3 | Auth (Google + GitHub) | ✅ |
| 4 | Multiplayer lobby (WebSockets) | ✅ |
| 5 | Multiplayer game (real-time, server timer) | ✅ |
| 6 | Production deployment + CI/CD | ✅ |
| 7 | Hardening (rate limiting, error boundaries, monitoring, accessibility) | 🔄 |
See `documentation/roadmap.md` for task-level detail.