diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-11-30 05:46:10 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-11-30 05:46:10 +0900 |
| commit | 785a98ebb0c65cc400d35897fab1460ad3f6959c (patch) | |
| tree | bc62bc65b155157d96412f0b2d34b1f0b53ccbd4 /docs/dev/architecture.md | |
| parent | e753941a8918929e05077e88cd47b1a34bb15ca3 (diff) | |
| download | kioku-785a98ebb0c65cc400d35897fab1460ad3f6959c.tar.gz kioku-785a98ebb0c65cc400d35897fab1460ad3f6959c.tar.zst kioku-785a98ebb0c65cc400d35897fab1460ad3f6959c.zip | |
prepare docs
Diffstat (limited to 'docs/dev/architecture.md')
| -rw-r--r-- | docs/dev/architecture.md | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md new file mode 100644 index 0000000..9c3749b --- /dev/null +++ b/docs/dev/architecture.md @@ -0,0 +1,256 @@ +# Kioku Architecture + +## Overview + +Kioku is a spaced repetition learning application (Anki clone) with PWA offline support and cloud sync. + +## Tech Stack + +| Layer | Technology | +|-------|------------| +| Frontend | SvelteKit | +| Backend | Hono + TypeScript | +| Database | PostgreSQL | +| ORM | Drizzle | +| Client DB | Dexie.js (IndexedDB) | +| PWA | @vite-pwa/sveltekit | +| Algorithm | FSRS (ts-fsrs) | +| Auth | username/password + JWT | +| Test | Vitest | +| Monorepo | pnpm workspace | +| Deploy | Docker + VPS | + +## Architecture Diagram + +``` ++--------------------------------------------------+ +| Client (PWA) | +| +-------------+ +------------+ +------------+ | +| | SvelteKit | | Dexie.js | | Service | | +| | UI |<>| (IndexedDB)|<>| Worker | | +| +-------------+ +------------+ +------------+ | +| | | | +| +-------+-------+ | +| | | +| +------v------+ | +| | Sync Engine | | +| +-------------+ | ++--------------------------------------------------+ + | + v HTTPS (REST API) ++--------------------------------------------------+ +| Server | +| +----------------------------------------------+| +| | Hono (TypeScript) || +| | +--------+ +--------+ +--------+ +--------+ || +| | | Auth | | FSRS | | Sync | | Import | || +| | +--------+ +--------+ +--------+ +--------+ || +| +----------------------------------------------+| +| | | +| v | +| +----------------------------------------------+| +| | PostgreSQL (Drizzle) || +| +----------------------------------------------+| ++--------------------------------------------------+ +``` + +## Project Structure + +``` +kioku/ +├── package.json # Workspace root +├── pnpm-workspace.yaml +├── docker-compose.yml +├── apps/ +│ ├── web/ # SvelteKit frontend +│ │ ├── src/ +│ │ │ ├── lib/ +│ │ │ │ ├── components/ +│ │ │ │ ├── stores/ +│ │ │ │ ├── db/ # Dexie IndexedDB +│ │ │ │ ├── sync/ # Sync engine +│ │ │ │ └── api/ +│ │ │ └── routes/ +│ │ └── static/ +│ └── server/ # Hono backend +│ └── src/ +│ ├── routes/ +│ ├── services/ +│ ├── db/ # Drizzle schema +│ ├── middleware/ +│ └── lib/ +│ └── apkg/ # Anki import +└── packages/ + └── shared/ # Shared types + └── src/ + ├── types/ + └── schemas/ # Zod validation +``` + +## Data Models + +### User + +```typescript +interface User { + id: string; // UUID + username: string; // unique + password_hash: string; + created_at: Date; + updated_at: Date; +} +``` + +### Deck + +```typescript +interface Deck { + id: string; + user_id: string; + name: string; + description: string | null; + new_cards_per_day: number; + created_at: Date; + updated_at: Date; + deleted_at: Date | null; // Soft delete + sync_version: number; +} +``` + +### Card (FSRS) + +```typescript +enum CardState { + New = 0, + Learning = 1, + Review = 2, + Relearning = 3, +} + +interface Card { + id: string; + deck_id: string; + front: string; // Plain text + back: string; // Plain text + + // FSRS fields + state: CardState; + due: Date; + stability: number; + difficulty: number; + elapsed_days: number; + scheduled_days: number; + reps: number; + lapses: number; + last_review: Date | null; + + created_at: Date; + updated_at: Date; + deleted_at: Date | null; + sync_version: number; +} +``` + +### ReviewLog + +```typescript +enum Rating { + Again = 1, + Hard = 2, + Good = 3, + Easy = 4, +} + +interface ReviewLog { + id: string; + card_id: string; + user_id: string; + rating: Rating; + state: CardState; + scheduled_days: number; + elapsed_days: number; + reviewed_at: Date; + duration_ms: number | null; + sync_version: number; +} +``` + +## API Design + +### Authentication + +``` +POST /api/auth/register - User registration +POST /api/auth/login - Login (returns JWT) +POST /api/auth/refresh - Refresh token +POST /api/auth/logout - Logout +``` + +### Decks + +``` +GET /api/decks - List decks +POST /api/decks - Create deck +GET /api/decks/:id - Get deck +PUT /api/decks/:id - Update deck +DELETE /api/decks/:id - Delete deck (soft) +``` + +### Cards + +``` +GET /api/decks/:deckId/cards - List cards +POST /api/decks/:deckId/cards - Create card +PUT /api/decks/:deckId/cards/:id - Update card +DELETE /api/decks/:deckId/cards/:id - Delete card +``` + +### Study + +``` +GET /api/decks/:deckId/study - Get cards to study +POST /api/decks/:deckId/study/:cardId - Submit review +``` + +### Sync + +``` +POST /api/sync/push - Push local changes to server +GET /api/sync/pull - Pull server changes +``` + +### Import + +``` +POST /api/import/apkg - Import Anki deck +``` + +## Offline Sync Strategy + +### Approach + +- **Method**: Last-Write-Wins with timestamps +- **Client**: Store in IndexedDB with `_synced` flag +- **Conflict Resolution**: Compare `updated_at`, newer wins +- **ReviewLog**: Append-only (no conflicts) + +### Sync Flow + +1. Local changes saved with `_synced = false` +2. On sync, push pending changes to server +3. Server resolves conflicts by timestamp +4. Client pulls server changes +5. Mark synced items with `_synced = true` + +## Authentication + +- **Hash**: Argon2 for password hashing +- **Access Token**: JWT, 15min expiry +- **Refresh Token**: JWT, 7 days, stored in DB + +## References + +- [ts-fsrs](https://github.com/open-spaced-repetition/ts-fsrs) +- [Anki APKG Format](https://eikowagenknecht.de/posts/understanding-the-anki-apkg-format/) +- [Vite PWA for SvelteKit](https://vite-pwa-org.netlify.app/frameworks/sveltekit.html) +- [Dexie.js](https://dexie.org/) |
