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

4.8 KiB
Raw Blame History

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.


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

# 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

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