Files
ReadaBook/README.md

172 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ReadaBook
ReadaBook est une application locale-first pour cataloguer, rechercher et lire une bibliothèque personnelle de livres EPUB/PDF stockés sur disque. Le MVP livré vise un usage domestique : un administrateur déclare un dossier local, lance un scan, puis les livres deviennent accessibles via un catalogue web et une API locale.
## Fonctionnalités MVP présentes
- Backend NestJS/Fastify exécutable avec healthcheck `GET /healthz`.
- Base SQLite locale avec Drizzle, WAL, migrations idempotentes au démarrage et index FTS5.
- Auth locale : bootstrap du premier admin, login/logout, cookie JWT httpOnly, rôles `admin`/`user`.
- Administration : CRUD utilisateurs, CRUD bibliothèques, déclenchement de scan, consultation des jobs.
- Scan récursif de bibliothèques EPUB/PDF.
- Extraction locale pragmatique : métadonnées EPUB, métadonnées PDF simples, jaquettes EPUB quand présentes.
- Enrichissement opportuniste Open Library, désactivable.
- Catalogue : liste, recherche FTS, fiche livre, stream fichier, stream couverture.
- Progression utilisateur : sauvegarde dun locator/percent et étagère “continuer”.
- Frontend PWA Vite/React dans `apps/web`, installable en mode standalone et servi par Nginx en compose.
- Expérience web “cabinet de curiosités numérique” : login, bootstrap admin, accueil, bibliothèques, recherche, fiches livre, lecteur, profil et administration.
- Lecteur web : rendu PDF via `pdfjs-dist`, adaptateur EPUB `foliate-js`, reprise de progression via `/progress`.
## Stack actuelle
- Monorepo `pnpm`.
- `apps/api` : NestJS 11, Fastify, SQLite `better-sqlite3`, Drizzle ORM, FTS5, Argon2, JOSE JWT.
- `apps/web` : Vite, React, TypeScript, `lucide-react`, `pdfjs-dist`, `foliate-js`, service worker et manifeste PWA, Nginx runtime.
- `packages/shared` : contrats Zod partagés.
- Docker multi-stage Node 22 pour build/runtime API, Nginx pour web.
## Structure repo
```text
apps/
api/ Backend NestJS/Fastify
web/ Frontend Vite/React
packages/
shared/ Types et schémas Zod partagés
data/
library/ Dossier local à scanner, monté en lecture seule dans compose
storage/ Cache backend, notamment couvertures extraites
Dockerfile Image API
docker-compose.yaml Services API + web
pnpm-workspace.yaml Workspace monorepo
```
## Développement local
```bash
corepack enable
pnpm install
pnpm dev
```
Scripts utiles :
- `pnpm dev` : lance API + web en parallèle.
- `pnpm dev:api` : backend seul.
- `pnpm dev:web` : frontend seul.
- `pnpm build` : build récursif.
- `pnpm test` : tests récursifs.
Note sandbox constatée : sur Node 24 local, `better-sqlite3` peut nécessiter un build natif. Le chemin Docker utilise Node 22 et a été validé.
## Docker Compose
```bash
JWT_SECRET="replace-me" docker compose up --build
```
Services :
- `api` : API interne sur `api:3000`, non publiée directement sur lhôte.
- `web` : Nginx + frontend, publié sur `http://localhost:3000`.
Ports :
- Hôte `3000` -> conteneur `web:80`.
- LAPI est accessible depuis le navigateur via les routes proxifiées par Nginx : `/auth`, `/admin`, `/books`, `/progress`, `/healthz`.
## Frontend PWA
Le frontend est accessible sur `http://localhost:3000` en compose. Il expose les routes MVP :
- `/login` : connexion locale.
- `/setup/*` : bootstrap du premier administrateur.
- `/home` : accueil, reprise de lecture, bibliothèques et catalogue.
- `/library/:libraryId` : ouvrages dune bibliothèque.
- `/book/:bookId` : fiche livre.
- `/reader/:bookId` : lecture EPUB/PDF avec sauvegarde de progression.
- `/search` : recherche catalogue.
- `/me` : profil/session.
- `/admin/*` : bibliothèques, scans, jobs et comptes.
La PWA fournit `manifest.webmanifest`, `sw.js`, une icône maskable SVG et `display: standalone`. Les appels API passent par le même origin en compose via Nginx, ce qui conserve les cookies httpOnly de session.
Volumes :
- `./data:/data` : base SQLite `/data/readabook.sqlite` et cache `/data/storage`.
- `./data/library:/library:ro` : bibliothèque locale scannée en lecture seule.
Variables principales :
- `JWT_SECRET` : secret JWT, à changer hors développement.
- `OPEN_LIBRARY_ENABLED=true|false` : active/désactive lenrichissement distant.
- `DATABASE_PATH=/data/readabook.sqlite`
- `STORAGE_DIR=/data/storage`
## Bootstrap admin
Après démarrage compose :
```bash
curl -c cookies.txt \
-H "content-type: application/json" \
-d '{"email":"admin@example.com","password":"password123","name":"Admin"}' \
http://localhost:3000/auth/bootstrap
```
Connexion :
```bash
curl -c cookies.txt -b cookies.txt \
-H "content-type: application/json" \
-d '{"email":"admin@example.com","password":"password123"}' \
http://localhost:3000/auth/login
```
## Ajouter une bibliothèque `/library`
Place les fichiers EPUB/PDF dans `data/library` côté hôte, puis déclare le volume monté dans le conteneur :
```bash
curl -b cookies.txt \
-H "content-type: application/json" \
-d '{"name":"Bibliothèque locale","path":"/library","enabled":true}' \
http://localhost:3000/admin/libraries
```
## Lancer un scan
```bash
curl -b cookies.txt -X POST http://localhost:3000/admin/libraries/1/scan
curl -b cookies.txt http://localhost:3000/admin/jobs
curl -b cookies.txt http://localhost:3000/books
```
## Endpoints principaux
- `GET /healthz`
- `POST /auth/bootstrap`
- `POST /auth/login`
- `POST /auth/logout`
- `GET /auth/me`
- `GET|POST /admin/users`
- `GET|POST|PATCH|DELETE /admin/libraries`
- `POST /admin/libraries/:id/scan`
- `GET /admin/jobs`
- `GET /books`
- `GET /books/search?q=term`
- `GET /books/:id`
- `GET /books/:id/file`
- `GET /books/:id/cover`
- `GET|PUT /progress/:bookId`
- `GET /progress/continue`
## Limites connues
- Extraction PDF limitée aux champs metadata simples, sans OCR ni parsing complet.
- Extraction EPUB correcte pour les cas standards, pas exhaustive sur tous les EPUB malformés.
- Open Library est opportuniste : échec silencieux en cas derreur réseau ou de rate limit.
- Jobs in-process : pas de worker externe, pas de reprise fine dun scan interrompu.
- Pas encore de migrations versionnées Drizzle Kit ; migrations SQL idempotentes exécutées au démarrage.
- LAPI nest pas exposée directement en compose ; elle passe par le reverse proxy Nginx du service web.