-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
dripips edited this page Jun 24, 2026
·
2 revisions
-
Frontend: Vite + React 19 + TypeScript + Tailwind v4. PWA via
vite-plugin-pwa(injectManifest, custom service worker for push). Single bundle served by the API. -
Backend: Fastify 5 on Node 24, using the built-in
node:sqlite— no external database, no ORM. - Auth: signed JWT in an httpOnly cookie; 10-year sessions (survive deploys). Role read live from the DB on every request, so promotions/demotions apply without re-login.
All learning material is plain files under content/, parsed at server startup (and hot-reloaded on change):
-
lessons/*.md— gray-matter front-matter (id, order, level, phase, exercises, reading, …) + Markdown theory. A lesson withkind: readingis a reading text (excluded from the grammar list, plan, and practice queue); alisteningtag marks dictation drills. -
grammar/*.md— reference cards. -
vocab/*.json— themed word sets. Wordid= slug, so the same word across sets de-duplicates on load. -
books/*.json— picture books:{ id, title, level, character, style, pages:[{en, ru, scene}] }. Page art lives inweb/public/books/<id>/<n>.webp.
Adding content needs no code and no frontend rebuild — just drop a file and restart the server (image/code changes do need npm run build:web).
-
users(role, xp, longest_streak) ·settings(per-user key/value) -
lesson_attempts(per exercise, upserted) ·activity(daily reviews/exercises → streaks) -
srs_cards(SM-2: ease, interval, reps, due, state) ·custom_words(saved from texts) -
error_log·progress(topic mastery) ·push_subscriptions·translations_cache
Classic SM-2 (server/src/srs.js): again/hard/good/easy ratings adjust ease & interval; new-cards-per-day cap is configurable per user.
-
Translation & writing/speaking feedback: any OpenAI-compatible chat API (
LLM_TRANSLATE_*). Feedback is forced to structured JSON and calibrated to the learner's level. -
TTS:
POST /api/ttsproxies ElevenLabs, caches mp3 toserver/data/tts-cache/keyed bymodel|voice|speed|text; concurrent identical requests are coalesced. Client falls back to the browserspeechSynthesisvoice. - Story art: generated out-of-band with Gemini ("Nano Banana") — see Content Authoring.
- Back/forward navigation restores scroll position (custom in
Layout), forward nav resets to top. -
useApiauto-retries transient failures (e.g. a deploy restart) so the UI self-heals. - Empty-body POSTs never send a JSON content-type (Fastify would 400) — fixed in the fetch wrapper.