Local-first web app for tracking Educative.io learning progress with offline-friendly SQLite storage.
- Backend: Node.js, Express, SQLite (
better-sqlite3), Zod - Frontend: React (Vite), TailwindCSS, Recharts
- Runtime: npm workspaces with one-command startup
- JWT auth with access/refresh tokens (
register,login,refresh) - Forgot/reset password flow with one-time reset tokens (dev reset link mode supported)
- Strong user isolation on courses, sessions, progress, dashboard, and analytics
- Course progress toggles including range-complete (
applyToPreviousfor lessons1..N) - Responsive UI with mobile card layouts for courses/sessions
- Centered, branded auth entry screen with logo badge and product title above the login form
- Accessibility-first improvements:
- focus-managed confirmation modal
- semantic progress indicators
- clearer form labels/help text/error states
- Frontend performance optimizations:
- route-level lazy loading (
React.lazy+Suspense) - isolated chart bundle (
rechartsmanual chunk)
- route-level lazy loading (
docs/ARCHITECTURE.md: architecture, schema, API contract, implementation phasesdocs/AUTH_ISOLATION_PLAN.md: auth and tenant-isolation hardening plan/statusbackend/: REST API + migrations + seedfrontend/: React UI
- Install dependencies:
npm install- Copy env file:
cp .env.example .env- Run migrations and seed:
npm run migrate
npm run seed- Start backend + frontend:
npm run devFrontend: http://localhost:5173
Backend API: http://localhost:4000/api
npm run dev: start backend + frontend concurrentlynpm run migrate: run backend migrationsnpm run seed: seed sample datanpm run test -w backend: backend integration testsnpm run test -w frontend: frontend integration testsnpm run build -w frontend: frontend production buildnpm run docker:up: build and start Docker servicesnpm run docker:up:prod: build and start Docker services with production overridenpm run docker:down: stop Docker servicesnpm run docker:down:prod: stop Docker services with production overridenpm run docker:logs: stream Docker logsnpm run docker:logs:prod: stream Docker logs with production overridenpm run docker:ps: list Docker containersnpm run docker:ps:prod: list Docker containers with production override
Production-style containerization is available with:
backend/Dockerfilefrontend/Dockerfilefrontend/nginx.conf(SPA +/apireverse proxy to backend)docker-compose.ymlscripts/docker.shhelper script
./scripts/docker.sh upor
npm run docker:up./scripts/docker.sh up-prodor
npm run docker:up:prod./scripts/docker.sh downProduction override:
./scripts/docker.sh down-prod./scripts/docker.sh logsProduction override:
./scripts/docker.sh logs-prod- Base compose:
- Frontend:
http://localhost:8080 - Backend health:
http://localhost:4000/api/health
- Frontend:
- Production override:
- Frontend:
http://localhost:8080 - Backend is internal-only (not published to host)
- Frontend:
- SQLite database is persisted in Docker named volume:
educative_db_data. - Backend runs migrations automatically on container startup.
- Docker Compose reads values from your root
.envfile automatically. - At minimum, set strong values for:
JWT_ACCESS_SECRETJWT_REFRESH_SECRET
- In production override (
docker-compose.prod.yml):- backend port is not published to host (
ports: [], internalexpose: 4000only) EXPOSE_RESET_TOKEN=false- tighter healthcheck settings
- backend port is not published to host (
GET /api/healthPOST /api/auth/registerPOST /api/auth/loginPOST /api/auth/refreshPOST /api/auth/forgot-passwordPOST /api/auth/reset-passwordGET/POST/PATCH/DELETE /api/coursesGET /api/courses/:id/progressPOST /api/courses/:id/progress/toggleGET/POST/PATCH/DELETE /api/sessionsGET /api/dashboard/summaryGET /api/dashboard/weeklyGET /api/analytics/heatmap?year=YYYYGET /api/analytics/insights
All endpoints except /api/health and /api/auth/* require:
Authorization: Bearer <access_token>
- SQLite file lives at
backend/data/educative_tracker.db(configurable viaDB_PATH). - App remains functional with local DB only.
- Seed inserts default user and sample data.
- App routes are lazy-loaded from
frontend/src/App.jsx. - Dashboard chart code is lazily loaded from
frontend/src/features/dashboard/WeeklyTrendChart.jsx. - Vite manual chunking isolates
rechartsinfrontend/vite.config.js.
- Keep REST contract stable; extract current service-layer SQL into a DB-adapter/repository abstraction.
- Replace SQLite datetime functions with DB-agnostic query builders/SQL dialect mapping.
- Migrate
INTEGER PRIMARY KEY AUTOINCREMENTto PostgreSQLBIGSERIAL/GENERATEDIDs. - Convert boolean-like integer fields (
is_completed,is_archived) to nativeBOOLEAN. - Move raw migrations to a migration tool (Prisma/Knex/Drizzle/Flyway) when cloud rollout begins.
- Keep auth/session contract stable while moving multi-tenant filters to PostgreSQL-safe query patterns.
See .env.example:
PORT: backend portDB_PATH: SQLite file pathFRONTEND_ORIGIN: CORS originJWT_ACCESS_SECRET: secret for access token signingJWT_REFRESH_SECRET: secret for refresh token signingJWT_ACCESS_EXPIRES_IN: access token TTL (default15m)JWT_REFRESH_EXPIRES_IN: refresh token TTL (default7d)JWT_ISSUER: JWT issuer claimJWT_AUDIENCE: JWT audience claimBCRYPT_SALT_ROUNDS: password hash costPASSWORD_RESET_TOKEN_TTL_MINUTES: reset token lifetime in minutes (default30)EXPOSE_RESET_TOKEN: whentrue, forgot-password response includes dev reset link/token (default true in non-production)
When shipping code changes, update docs in the same PR:
README.md:- Update API routes/auth requirements if endpoints or guards change.
- Update scripts/commands if workspace scripts or test commands change.
- Update env var list when config keys are added/removed/renamed.
- Update feature/performance notes when UX, lazy-loading, or bundling strategy changes.
docs/ARCHITECTURE.md:- Keep folder structure in sync with new modules/pages/components/tests.
- Keep schema/migration section aligned with files in
backend/migrations/. - Keep API contract and assumptions aligned with current auth/isolation behavior.
- Keep implementation phase status aligned with actual completed work.
docs/AUTH_ISOLATION_PLAN.md:- Mark completed phases/steps when implemented.
- Add new hardening/testing tasks when security scope changes.
- Verify docs examples by running:
npm run test -w backendnpm run test -w frontendnpm run build -w frontend
- Seeded default user credentials:
email=learner@example.com,password=changeme123 - Change seeded credentials for non-local environments.
- Lesson metadata is manually represented by lesson number due Educative API constraints.
- In production, set
EXPOSE_RESET_TOKEN=falseand wire forgot-password to a real email provider.