Files
ReadaBook/README.md

215 lines
8.0 KiB
Markdown
Raw Permalink 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.
## Dépôt distant
Le dépôt est hébergé sur un Gitea self-hosted et peut être cloné via :
```bash
git clone https://gitea.anthonybouteiller.ovh/blomios/ReadaBook.git
```
Le déploiement git suit un git-flow simplifié : `main` (releases), `develop` (intégration), `feature/*` / `fix/*` (travail en cours).
## 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 depuis images publiees
docker-compose.build.yaml Override de build local des images 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
```
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`.
Images par défaut :
- `${READABOOK_REGISTRY:-gitea.anthonybouteiller.ovh/blomios/readabook}/api:${READABOOK_TAG:-latest}`
- `${READABOOK_REGISTRY:-gitea.anthonybouteiller.ovh/blomios/readabook}/web:${READABOOK_TAG:-latest}`
Pour construire localement au lieu de tirer les images publiées :
```bash
READABOOK_TAG=local docker compose -f docker-compose.yaml -f docker-compose.build.yaml up --build
```
Pour publier les images vers le registry Gitea :
```bash
docker login gitea.anthonybouteiller.ovh
./scripts/publish.sh
```
## 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`.
- `${READABOOK_LIBRARY_HOST_PATH:-./data/library}:/library:ro` : bibliothèque locale scannée en lecture seule.
Variables principales :
- `READABOOK_REGISTRY` : préfixe registry/repository des images API et web.
- `READABOOK_TAG` : tag dimage utilisé par compose et par `scripts/publish.sh`.
- `JWT_SECRET` : secret JWT, à changer hors développement.
- `OPEN_LIBRARY_ENABLED=true|false` : active/désactive lenrichissement distant.
- `READABOOK_LIBRARY_HOST_PATH` : dossier hôte monté en lecture seule sur `/library`.
- `READABOOK_LIBRARY_ALIAS_FROM` : chemin alternatif accepté par lAPI et traduit vers `/library`.
- `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
```
Pour tester le corpus réel local `Books/` sans le versionner, crée un `.env` local :
```bash
READABOOK_LIBRARY_HOST_PATH=./Books
READABOOK_LIBRARY_ALIAS_FROM=/chemin/absolu/vers/ReadaBook/Books
```
QA peut ensuite créer la bibliothèque avec le chemin absolu saisi dans `READABOOK_LIBRARY_ALIAS_FROM`;
lAPI le traduit vers `/library`, puis le scan manuel teste lextraction ISBN/métadonnées/jaquettes sur ce corpus.
## 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.