A full-stack language-learning web app inspired by LingQ: read lessons with clickable words, track LingQs (words you are learning), known vocabulary, phrases, courses, streaks, and daily goals. The UI is a React (Vite) SPA; the API is Express with SQLite (Drizzle ORM).
- Account & profile — Sign up, JWT login, target language, daily goal tier, streaks and daily stats (listening, words read, LingQs created/learned).
- Library — Own courses, guided course catalog, lesson feed, “continue studying”, bookmarks, completion progress.
- Reader — Tokenized lessons with word states (new / learning / known), phrase LingQs, audio, pagination, keyboard shortcuts, coins for stage progression.
- Vocabulary & phrases — Searchable lists, tags, hints (optional LingQ API + cache), batch operations, Markov-style learning insights on the profile.
- Import — Create lessons from raw text; optional one-time import of recommended content from LingQ (per language) using a user API key.
- Uploads — Authenticated image/audio uploads for lesson metadata.
lingq-clone/
├── .env # The root enviro used for production-sync secrets, especially when using Docker.
├── src/ # React app/Front end (assets, components, views, features, stores, types, utils, API client)
├── backend/
│ ├── .env # Use this enviro for offline local development.
│ ├── sqlite.db # The SQLite database file (if not using Turso)
│ ├── drizzle-config.ts # Drizzle configuration
│ ├── src/
│ │ ├── constants/ # For saving constant value that is reusable
│ │ ├── routes/ # Express routers (auth, library, reader, vocab, phrases, upload)
│ │ ├── db/ # Drizzle schema, migrations, seed
│ │ ├── middleware/ # JWT authentication
│ │ ├── services/ # LingQ import, analytics, vocab history
│ │ └── utils/ # Lesson parsing, stats engine, and timezone
├── docs/
│ ├── openapi.yaml # OpenAPI 3 specification (Swagger)
│ ├── API.md # API overview and links
│ ├── BACKEND.md # Backend module notes
│ ├── FRONTEND.md # Frontend module notes
│ └── CONTRIBUTING.md # Contribution guidelines
├── Dockerfile # Multi-stage: Vite build + API + static SPA in production
├── docker-compose.yml # Single service with SQLite on a volume
├── vercel.json
└── package.json # Root scripts and frontend dependencies
- Spec file:
docs/openapi.yaml— use in Swagger Editor, Redoc, or codegen tools. - Interactive UI: With the backend running, open http://localhost:3000/api-docs (Swagger UI). The server loads the spec from
docs/openapi.yaml(local dev) oropenapi.yamlnext to the compiled output (Docker image).
See docs/API.md for a short endpoint map and authentication notes.
- Node.js 20+ (matches CI)
- npm
Run the API and the Vite dev server in two terminals. The frontend is configured to call http://localhost:3000/api/v1 (see src/api/client.ts).
-
Backend
cd backend cp .env.example .env # Edit .env — set JWT_SECRET (required for auth) npm install npm run db:migrate # if your clone expects migrations applied npm run dev
SQLite file path defaults to
DATABASE_URLorsqlite.dbin the current working directory (usuallybackend/when you runnpm run dev). -
Frontend (repository root)
npm install npm run dev
Open the URL Vite prints (typically http://localhost:5173).
-
Optional seed data
cd backend && npm run seed
npm run install-all
npm run build
npm run build-backendCopy the SPA into the folder the server expects (../public relative to backend/dist):
rm -rf backend/public && mkdir -p backend/public && cp -r dist/* backend/public/
cd backend && NODE_ENV=production node dist/server.jsThe Docker image performs the equivalent layout automatically; for day-to-day work, local dev with two terminals is simpler.
docker compose build
docker compose up- Port:
3000(API + SPA in production mode inside the container). - Environment: Set a strong
JWT_SECRETindocker-compose.yml(or override via env).DATABASE_URL=/app/data/sqlite.dbkeeps the database on the mounted./datavolume. - After dependency changes, run
npm installinbackend/sopackage-lock.jsonstays in sync (required for reproducible Docker builds).
| Variable | Purpose |
|---|---|
PORT |
HTTP port (default 3000) |
JWT_SECRET |
Signing key for JWTs (required for login) |
DATABASE_URL |
SQLite file path (default sqlite.db) |
LINGQ_TOKEN |
Optional; server-side hints and LingQ import helpers |
NODE_ENV |
production enables static SPA + stricter deployment assumptions |
Copy backend/.env.example to backend/.env, so does the root .env.example to /.env.
See docs/CONTRIBUTING.md for branch hygiene, CI, and code-style expectations.
docs/BACKEND.md— database, routes, uploads, migrations.docs/FRONTEND.md— routing, state, feature folders.
See backend/package.json (ISC) and root package.json for package metadata.