Skip to content
oiibarPublic

About

A full-stack flashcard and quiz platform featuring interactive study cards, automatically generated multiple-choice tests, deck management, public sharing, and secure authentication. Built with Nuxt, Vue, TypeScript, NestJS, PostgreSQL, and Docker.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

Flashcards that stick: build decks, study them with flip cards, and test yourself with multiple-choice quizzes generated from your own material.

QuizDeck landing page

Why

Memorising terms, definitions or dates works best with active recall, but spreadsheets and paper cards make it slow to build material and impossible to test yourself properly. QuizDeck turns a list of term and definition pairs into two study modes: flip cards with self-grading, and a scored multiple-choice test whose wrong answers come from the deck itself, so the options are always plausible. Decks can stay private or be shared publicly for anyone to study or copy.

Live demo: not deployed. The repository contains a production-ready deployment setup (docs/deployment.md), but no public instance is running.

Features

  • Decks: create, edit, reorder cards by drag-and-drop or keyboard, pin favourites, keep private or publish; unsaved-changes guard.
  • Study mode: 3D flip cards, keyboard shortcuts, shuffle, "got it" / "still learning" grading, review only what you missed.
  • Test mode: auto-generated multiple-choice questions, back and forward navigation, warning before submitting with gaps, scored review, retry missed.
  • Explore: public catalogue with search, sort and pagination, usable without an account; copy any public deck into your library.
  • Accounts: sign up, e-mail verification, password reset, change password, log out everywhere, delete account.
  • Quality: responsive and keyboard accessible (axe-checked), server-side rendered with real 404s, SEO metadata, sitemap.
Library Study Test
Library Study Test
Deck Editor Mobile
Deck Editor Explore on mobile

Tech stack

Area Choice Why
Web Nuxt 3 (Vue 3, TypeScript), Pinia, Tailwind CSS SSR for fast first paint, real status codes and indexable public decks; kept from the original project
API NestJS 11 (TypeScript), REST + OpenAPI Modules, DI and guards give clear boundaries in a modular monolith
Database PostgreSQL 17, TypeORM 0.3 with versioned migrations Relational integrity (FKs, unique and check constraints) plus jsonb for ordered cards
Auth HttpOnly cookie sessions: 15-min JWT + rotating refresh tokens, Argon2id XSS cannot read the session; sessions are revocable (ADR 0002)
Edge Caddy 2 Automatic HTTPS and renewal, one origin for web and API
Ops Docker, Compose, GitHub Actions, GHCR Reproducible builds, one-command local stack, gated releases
Observability pino JSON logs, Prometheus metrics, Terminus health checks, optional Sentry Diagnosable without a heavy monitoring stack
Tests Jest, Supertest, Vitest, @nuxt/test-utils, Playwright, axe, k6 Unit to end-to-end coverage of the critical paths

Deliberately not used: microservices, Kubernetes, Redis, message queues. At this scale they add operational cost without benefit (ADR 0001, docs/performance.md lists when that would change).

Architecture

flowchart LR
  Browser -- HTTPS --> Caddy
  Caddy -- "/api/*" --> API["NestJS API<br/>auth · users · decks"]
  Caddy -- "pages" --> Web["Nuxt SSR"]
  Web -- "SSR fetch (visitor cookies)" --> API
  API --> DB[(PostgreSQL)]
  API --> SMTP[SMTP]
Loading

More: architecture · ER diagram · deployment · auth sequences · ADRs.

Getting started

Requirements: Docker with Compose v2. Node.js 24 only for running tests or developing outside containers.

git clone https://github.com/oiibar/QuizDeck.git
cd QuizDeck
cp .env.example .env
# set JWT_SECRET in .env, e.g.:
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

docker compose up -d --build --wait   # db, mailpit, api, web
docker compose run --rm migrate       # create the schema
docker compose run --rm seed          # optional demo data
URL What
http://localhost:3000 Web app
http://localhost:4000/api/docs Swagger UI (OpenAPI)
http://localhost:8025 Mailpit: verification and reset e-mails land here

Demo login after seeding: demo@quizdeck.dev / Demo-password-1.

Stop with docker compose down (add -v to also delete the database volume).

Developing with hot reload

docker compose up -d db mailpit
cd server && cp .env.example .env && npm ci     # set JWT_SECRET in server/.env
npm run migration:run && npm run seed && npm run start:dev
cd ../client && npm ci && npm run dev           # proxies /api to localhost:4000

Environment variables

Compose reads the root .env.example. The API's full list, with defaults, is in server/.env.example and is validated at startup: the API refuses to boot and names the problem if a value is missing or invalid. The web client's variables are in client/.env.example.

Variable Required Purpose
JWT_SECRET yes Signs access tokens; 32+ characters
DATABASE_URL yes (set by Compose) PostgreSQL connection
APP_URL, CORS_ORIGINS defaults to localhost Public origin for links, CSRF and CORS checks
MAIL_TRANSPORT, SMTP_*, MAIL_FROM for e-mail smtp (Mailpit locally) or log
COOKIE_SECURE, TRUST_PROXY production Secure cookies; trust Caddy's forwarded IP
SENTRY_DSN no Error tracking

Database

Migrations live in server/src/database/migrations and run as an explicit step (docker compose run --rm migrate). The seed is idempotent and uses fictional accounts only. Backup and restore:

./scripts/backup-db.sh
./scripts/restore-db.sh backups/<file>.dump            # rehearse into a scratch DB
./scripts/restore-db.sh backups/<file>.dump --replace  # real restore

Details: docs/database.md, docs/disaster-recovery.md.

Tests and quality checks

# server/  (API tests need the Compose db and a quizdeck_test database, see docs/testing.md)
npm run lint && npm run typecheck && npm test && npm run test:e2e

# client/
npm run lint && npm run typecheck && npm test
npm run test:e2e        # Playwright against the running stack

Latest local run (2026-11-10): 23 server unit, 48 API/integration, 37 client unit and component, and 12 Playwright tests, all passing. Combined server coverage is 95% of lines and 67% of branches. Under a 50-user k6 load test the API served 164 requests per second with a p95 of 48 ms and no errors, on a laptop with seed data. Conditions and query-plan analysis are in docs/performance.md.

CI (GitHub Actions) runs format, lint, typecheck, all test suites, builds, npm audit, Trivy image scans, a Gitleaks secret scan and Playwright against the Compose stack on every push and pull request.

Deployment

One Linux VM with Docker Compose and Caddy (automatic Let's Encrypt). Tagging vX.Y.Z builds and publishes images to GHCR. After approval on the production environment, CI deploys over SSH: backup, migrate, roll out, smoke test. Rollback is redeploying the previous tag. Full guide: docs/deployment.md. Operations: docs/operations-runbook.md.

Security

Argon2id passwords; HttpOnly SameSite cookies with rotating refresh tokens and reuse detection; ownership checks on every deck operation (private decks of others return 404); strict input whitelisting; per-IP rate limits with a stricter limit on credential endpoints; CSRF origin checks; Helmet and CSP headers; secrets only from the environment; credentials and tokens redacted from logs. See docs/security.md and SECURITY.md.

The pre-1.0 code committed database credentials. They were removed but remain in Git history and must be rotated at the provider.

Known limitations and next steps

  • Not deployed publicly yet; production backups and monitoring are documented but not exercised on a real host.
  • Web CSP allows inline scripts (Nuxt hydration); nonces are the next step.
  • Rate limits are per process; a multi-replica setup needs Redis-backed throttling.
  • No spaced-repetition scheduling, images or audio on cards, tags or folders, social login or MFA.
  • Search uses ILIKE; a pg_trgm index is needed for a large catalogue.
  • No licence has been chosen yet, so all rights are reserved by the author.

Documentation

Product requirements · Architecture · API · Database · Security · Testing · Performance · Deployment · Runbook · Disaster recovery · Audit of the original code · Contributing · Changelog

Author

Built by oiibar. Contact: contact-me-neon.vercel.app.

About

A full-stack flashcard and quiz platform featuring interactive study cards, automatically generated multiple-choice tests, deck management, public sharing, and secure authentication. Built with Nuxt, Vue, TypeScript, NestJS, PostgreSQL, and Docker.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages