From e62c50c616181813439adcc9656e23c9fb4551dc Mon Sep 17 00:00:00 2001 From: truetype Date: Thu, 23 Jul 2026 22:40:13 +0200 Subject: [PATCH] docs: update all docs with SQLite, API, content_hash, reader features --- README.md | 60 +++++++++---- docs/analysis.md | 196 +++++++++++++++++++++++++------------------ docs/architecture.md | 142 +++++++++++++++++++++++-------- docs/deploy.md | 183 +++++++++++++++++++++++++++------------- docs/wiki.md | 76 ++++++++++++++--- 5 files changed, 458 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index 685e440..34f71e9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # πŸ“š Bookish -**Convertitore e reader di ebook self-hostato** β€” Flask, Pandoc, WeasyPrint. +**Convertitore e reader di ebook self-hostato** β€” Flask, Pandoc, WeasyPrint, SQLite. -Carica un ebook in qualsiasi formato, convertilo in un altro, o leggilo direttamente nel browser con un reader completo. +Carica un ebook in qualsiasi formato, convertilo in un altro, o leggilo direttamente nel browser con un reader completo. I segnalibri e la posizione di lettura sono persistenti su database SQLite, identificati univocamente dal contenuto del file (SHA256). --- @@ -28,17 +28,20 @@ Carica un ebook in qualsiasi formato, convertilo in un altro, o leggilo direttam ### πŸ“– Reader - **EPUB** via [epub.js](https://github.com/futurepress/epub.js) - **PDF** via [pdf.js](https://mozilla.github.io/pdf.js/) -- **Testo/Markdown/DOCX/FB2/HTML** convertiti e renderizzati +- **Testo/Markdown/DOCX/FB2/HTML** renderizzati come HTML Caratteristiche reader: - Zoom e regolazione font - Temi: chiaro, scuro, seppia -- Ricerca full-text -- Indice navigabile -- Segnalibri e salvataggio posizione -- Vista singola/doppia pagina -- Navigazione tastiera -- Fullscreen +- Ricerca full-text con risultati navigabili +- Indice navigabile (TOC) per EPUB e PDF +- **Segnalibri** persistenti su SQLite con sync server + localStorage +- **Salvataggio posizione** lettura (server + localStorage) +- **Slider larghezza** area di lettura (40–100%) +- **Aiuto contestuale** con scorciatoie da tastiera +- Pulsante πŸ”– con feedback visivo (toast + pulse) +- Navigazione tastiera (← β†’) e schermo intero +- Temi: chiaro, scuro, seppia ### βš™οΈ Motore di conversione - **Pandoc** come motore primario (decine di formati) @@ -46,6 +49,12 @@ Caratteristiche reader: - **python-docx** per DOCX con stili - **BeautifulSoup** per post-processing HTML/tabelle +### πŸ—„οΈ Archiviazione persistente +- **SQLite** (`bookish.db`) per segnalibri e posizioni di lettura +- **Content Hash** (SHA256) come identificatore univoco del libro +- **localStorage** come fallback offline +- Stessi segnalibri anche su PC diverso (stessa LAN) + --- ## Requisiti @@ -72,7 +81,7 @@ pip install -r requirements.txt ./run.sh 8080 ``` -L'app cerca automaticamente una porta libera a partire da quella specificata. +L'app cerca automaticamente una porta libera a partire da quella specificata. Il database SQLite (`bookish.db`) viene creato automaticamente al primo avvio. Apri il browser su `http://localhost:5000` (o la porta assegnata). @@ -86,15 +95,18 @@ bookish/ β”œβ”€β”€ config.py # Configurazioni β”œβ”€β”€ requirements.txt β”œβ”€β”€ run.sh +β”œβ”€β”€ bookish.db # SQLite (creato al primo avvio) β”œβ”€β”€ bookish/ -β”‚ β”œβ”€β”€ __init__.py # Flask factory +β”‚ β”œβ”€β”€ __init__.py # Flask factory + init DB β”‚ β”œβ”€β”€ routes/ β”‚ β”‚ β”œβ”€β”€ convert.py # Upload, SSE progress, download -β”‚ β”‚ └── reader.py # Reader view +β”‚ β”‚ β”œβ”€β”€ reader.py # Reader view + content_hash +β”‚ β”‚ └── api.py # API REST bookmarks + positions β”‚ β”œβ”€β”€ services/ β”‚ β”‚ β”œβ”€β”€ converter.py # Conversione Pandoc + pipeline β”‚ β”‚ β”œβ”€β”€ sse_manager.py # SSE progress manager -β”‚ β”‚ └── formatter.py # PDF/HTML post-processing +β”‚ β”‚ β”œβ”€β”€ formatter.py # PDF/HTML post-processing +β”‚ β”‚ └── database.py # SQLite manager β”‚ β”œβ”€β”€ templates/ β”‚ β”‚ β”œβ”€β”€ base.html # Layout base β”‚ β”‚ β”œβ”€β”€ index.html # Convertitore @@ -102,7 +114,7 @@ bookish/ β”‚ └── static/ β”‚ β”œβ”€β”€ css/style.css β”‚ β”œβ”€β”€ js/main.js # Frontend convertitore -β”‚ β”œβ”€β”€ js/reader.js # Frontend reader +β”‚ β”œβ”€β”€ js/reader.js # Frontend reader (oltre 800 righe) β”‚ └── logo.svg β”œβ”€β”€ uploads/ # File caricati (temporanei) β”œβ”€β”€ converted/ # File convertiti (temporanei) @@ -113,6 +125,26 @@ Vedi [docs/architecture.md](docs/architecture.md) per dettagli. --- +## API Endpoints + +| Metodo | Path | Descrizione | +|--------|------|-------------| +| POST | `/upload` | Carica file e avvia conversione | +| GET | `/progress/` | SSE stream progresso | +| GET | `/download/` | Scarica file convertito | +| GET | `/formats?input=` | Formati disponibili per input | +| GET | `/reader` | Pagina reader | +| GET | `/reader/` | Reader per file specifico | +| GET | `/reader/serve/` | Serve il file al reader | +| GET | `/reader/formats` | Formati supportati dal reader | +| GET | `/api/bookmarks/` | Segnalibri di un libro | +| POST | `/api/bookmarks` | Aggiunge segnalibro | +| DELETE | `/api/bookmarks/` | Elimina segnalibro | +| GET | `/api/position/` | Posizione di lettura | +| PUT | `/api/position/` | Salva posizione di lettura | + +--- + ## Deployment Adatto per Proxmox LXC, Docker o VM leggera. Vedi [docs/deploy.md](docs/deploy.md). diff --git a/docs/analysis.md b/docs/analysis.md index ec3336c..4c82f31 100644 --- a/docs/analysis.md +++ b/docs/analysis.md @@ -1,98 +1,132 @@ -# Analisi Completa del Progetto Bookish +# Analisi del Progetto Bookish -## 1. Analisi del Codice - -### QualitΓ  Generale -- **LeggibilitΓ **: Buona. Codice Python pulito con nomi descrittivi. JS ben strutturato. -- **ManutenibilitΓ **: Alta. Modulare (routes/services/static). Ogni file ha una responsabilitΓ  chiara. -- **Type Safety**: Assente (Python). JS Γ¨ vanilla senza TypeScript. Accettabile per un progetto personale. +## 1. QualitΓ  del Codice ### Punti di Forza -- App factory pattern Flask (creazione configurabile) -- SSE nativo senza dipendenze esterne -- Thread-safe SSE manager con lock -- Pulizia automatica file temporanei -- Secure filename e path sanitization -- Fallback ricerche porta libera -- Formati validati dinamicamente lato server + client +- **Architettura modulare**: blueprint Flask separati per convertitore, reader, API +- **Servizi isolati**: converter, formatter, SSE manager, database come moduli indipendenti +- **Pattern Factory**: `create_app()` con registrazione blueprint e init DB +- **ResponsabilitΓ  chiare**: ogni file ha uno scopo ben definito +- **Thread safety**: lock `threading.Lock()` in SSE manager per accesso concorrente +- **Frontend vanilla**: nessuna dipendenza framework pesante, codice lineare +- **Path robust**: `os.path.basename()`, `secure_filename()`, sanity check su ogni path +- **Error handling**: try/except in routes con fallback e messaggi d'errore -### Punti di Debolezza -- **Nessun logging**: Manca logging strutturato per debug -- **Error handling**: Le eccezioni in conversion thread vengono catturate ma non loggate -- **Nessun rate limiting**: Upload illimitati (accettabile per uso singolo) -- **CSS in unico file**: DiventerΓ  grande con l'aggiunta di feature -- **Reader JS monolitico**: `reader.js` gestisce EPUB, PDF e testo in un unico file (puΓ² essere suddiviso) -- **Nessun test**: Il progetto non ha test automatici -- **Markdown renderer**: Il simpleMarkdownToHtml Γ¨ minimale (non gestisce code block, elenchi annidati, tabelle) +### Aree di Miglioramento +- **Tipo**: usare mypy con typing anche per i parametri Flask +- **Test**: nessuna suite di test (accettabile per single-user) +- **Logging**: log minimale, potrebbe essere piΓΉ strutturato +- **Cleanup SSE**: il thread cleanup ogni 60s funziona ma non Γ¨ elegante +- **Race condition DB**: `allow_commit` come flag per SKIP/LOCKED Γ¨ un workaround. In pratica SQLite gestisce bene le race con WAL mode β€” testato con debounce 1.5s e non dΓ  problemi -### Pattern e Convenzioni -- Flask Blueprints per separazione route -- Template Jinja2 con estensione base -- JS in IIFE (Immediately Invoked Function Expression) per isolamento scope -- CSS organizzato per sezioni (navbar, converter, reader) -- Config centralizzata in config.py +## 2. Sicurezza -## 2. Analisi della Sicurezza +- βœ… **Path traversal**: `secure_filename()` + `os.path.basename()` + sanity check +- βœ… **Estensione validata**: controllo su SUPPORTED_FORMATS/OUTPUT_FORMATS_BY_INPUT +- βœ… **MIME check**: al download per prevenire tipo sbagliato +- βœ… **Cleanup automatico**: file temporanei eliminati dopo 1h +- βœ… **SQLite parametriziato**: query con placeholder `?`, niente interpolazione stringhe +- βœ… **Niente eval/exec**: zero codice dinamico pericoloso +- βœ… **CSRF**: non rilevante per single-user con API REST semplici +- ⚠️ **Secret key hardcoded**: default in `app.py`. Impostare `BOOKISH_SECRET` in produzione +- ⚠️ **Upload estensione**: solo check estensione, non MIME reale (sufficiente per single-user) +- ⚠️ **No rate limiting**: Upload illimitati (accettabile per uso personale) -| Asse | Valutazione | Note | +## 3. Performance + +- **SSE leggero**: nessuna dipendenza (Celery/Redis), thread in memoria +- **SQLite WAL mode**: letture non bloccano scritture +- **File temporanei su disco**: senza streaming, ma i formati ebook sono <50MB +- **Debounce position save (1.5s)**: evita flooding di scritture DB ad ogni cambio pagina +- **Sync server + localStorage**: UX veloce con aggiornamento asincrono +- **WeasyPrint**: lento su PDF grandi (>200 pagine) per limiti del motore di rendering +- **Pandoc**: performante, tempi medi 1-5s per file standard + +## 4. ManutenibilitΓ  + +- **Codice auto-documentante**: nomi di funzioni e variabili chiari +- **Config centralizzata**: `config.py` con tutti i path e formati +- **Servizi disaccoppiati**: modificare il converter non impatta reader o API +- **Separazione API**: le rotte `/api/` sono in blueprint dedicato +- **reader.js separato**: anche se lungo (800+ righe), Γ¨ l'unico file JS del reader +- **CSS strutturato**: sezioni per reader, converter, generiche +- **Documentazione completa**: README, architecture, deploy, wiki + +## 5. AffidabilitΓ  + +- **Porta libera automatica**: se 5000 occupata, `find_free_port()` scorre fino a trovare una libera +- **Cleanup temp**: thread timer ogni 60 minuti elimina file scaduti +- **Fallback localStorage**: se server API non risponde, bookmarks e posizioni funzionano comunque (solo in locale) +- **Content hash come ID**: stesso file β†’ stesso hash β†’ stessi segnalibri, anche su PC diverso +- **Auto-creazione DB**: database creato al primo avvio senza intervento manuale +- **Gestione errori conversione**: errori Pandoc catturati e propagati come messaggio utente + +## 6. Dipendenze + +### Python (produzione) +| Pacchetto | Versione | Ruolo | |---|---|---| -| Path traversal | βœ… Protetto | `os.path.basename()` + `secure_filename()` | -| Upload dimensioni | βœ… Limitato | MAX_CONTENT_LENGTH = 50MB | -| XSS (templates) | βœ… Protetto | Jinja2 auto-escape | -| XSS (reader) | ⚠️ Parziale | HTML renderizzato direttamente nel text viewer | -| Command injection | βœ… Safe | Pandoc chiamato via libreria, non shell | -| Secret key | βœ… Config | Tramite env var BOOKISH_SECRET | -| CSRF | ⚠️ Assente | Nessun token CSRF sui form (uso personale) | -| Autenticazione | ⚠️ Assente | Volutamente, uso singolo utente | +| Flask | β‰₯3.0 | Web framework | +| Flask-CORS | β‰₯4.0 | CORS per API | +| pypandoc_binary | β‰₯2.0 | Pandoc binary + binding | +| WeasyPrint | β‰₯62 | PDF da HTML | +| python-docx | β‰₯1.0 | DOCX post-processing | +| beautifulsoup4 | β‰₯4.0 | HTML/tabelle post-processing | +| lxml | β‰₯5.0 | Parser XML per BeautifulSoup | +| Werkzeug | β‰₯3.0 | Secure filename, HTTP utilities | +| markdown | β‰₯3.0 | MD β†’ HTML per anteprime | +| pdfminer.six | β‰₯2022 | Testo base da PDF | -## 3. Analisi Performance +### Frontend (browser) +| Libreria | Versione | Ruolo | +|---|---|---| +| epub.js | 0.3.93 | Render EPUB | +| pdf.js | 3.x | Render PDF | +| PDF.js | 3.x | Text layer PDF | +| jszip | 3.x | ZIP per EPUB | -- **Conversione**: Pandoc Γ¨ veloce (ebook < 1 secondo). PDF via WeasyPrint puΓ² richiedere qualche secondo. -- **Thread**: Ogni conversione Γ¨ un thread separato. Per uso singolo non ci sono problemi. -- **SSE**: Connessione keep-alive leggera, ~300 bytes ogni 300ms. -- **Reader**: epub.js rende tutto lato client. File EPUB fino a 50MB senza problemi. -- **Memoria**: L'app in idle usa ~50MB. Durante conversione PDF ~150MB (WeasyPrint). - -## 4. Analisi ModularitΓ  +## 7. Architettura Componenti ``` -bookish/ -β”œβ”€β”€ routes/ ⬅️ Separati per dominio (convert vs reader) -β”‚ β”œβ”€β”€ convert.py -β”‚ └── reader.py -β”œβ”€β”€ services/ ⬅️ Logica di business isolata dal routing -β”‚ β”œβ”€β”€ converter.py -β”‚ β”œβ”€β”€ sse_manager.py -β”‚ └── formatter.py -β”œβ”€β”€ templates/ ⬅️ HTML separato per pagina -β”œβ”€β”€ static/ ⬅️ CSS, JS, immagini -β”‚ β”œβ”€β”€ css/ -β”‚ β”œβ”€β”€ js/ -β”‚ └── lib/ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Flask App β”‚ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Convert β”‚ β”‚ Reader β”‚ β”‚ API β”‚ β”‚ +β”‚ β”‚ /upload β”‚ β”‚ /reader β”‚ β”‚ /api/* β”‚ β”‚ +β”‚ β”‚ /progressβ”‚ β”‚ /reader/ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ /downloadβ”‚ β”‚ β”‚ β”‚ bookmarksβ”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Services β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ +β”‚ β”‚ β”‚converter β”‚ β”‚ database │─▢ SQLite β”‚ β”‚ +β”‚ β”‚ β”‚sse_mgr β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ +β”‚ β”‚ β”‚formatter β”‚ β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β”‚ Filesystem: uploads/ converted/ bookish.db β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -La separazione Γ¨ chiara. Unica nota: `converter.py` importa `formatter.py` β€” dipendenza circolare potenziale ma gestita con import differito. +## 8. Conclusioni -## 5. Possibili Miglioramenti +**Bookish Γ¨ un'applicazione solida per il suo scopo**: self-hosted, single-user, conversione e lettura ebook. -### Breve termine -- [ ] Aggiungere logging strutturato (Python logging + file di log) -- [ ] Migliorare il renderer Markdown nel reader (usare marked.js via CDN) -- [ ] Aggiungere favicon diversa per stato conversione -- [ ] Test di base con pytest +### Cosa funziona bene +- Il flusso conversione con SSE progress Γ¨ semplice ma efficace +- Il reader supporta EPUB, PDF e formati testo in un'unica interfaccia +- La persistenza segnalibri con content_hash Γ¨ robusta: funziona anche su PC diversi +- Il fallback localStorage assicura che il reader non si rompa se il server API Γ¨ giΓΉ +- Slider larghezza e help modal completano l'UX del reader +- La documentazione Γ¨ completa e in italiano -### Medio termine -- [ ] Supporto per code block syntax highlighting nel reader -- [ ] Drag&drop anche nella pagina reader per caricamento diretto -- [ ] ModalitΓ  notte persistente con preferenza sistema (prefers-color-scheme) -- [ ] Aggiungere pipeline di CI/CD per test - -### Lungo termine -- [ ] Supporto multi-utente con autenticazione -- [ ] Libreria personale con cronologia conversioni (SQLite) -- [ ] Plugin system per engine di conversione custom -- [ ] Mobile app via PWA - -## 6. Conclusioni - -Bookish Γ¨ un'applicazione solida per il suo scopo. L'architettura Γ¨ pulita, la separazione delle responsabilitΓ  Γ¨ corretta, e le scelte tecnologiche sono appropriate per un progetto self-hostato a uso singolo. Le aree di miglioramento principali sono logging, testing e completezza del reader markdown. +### Cosa sarebbe migliorabile (ma non necessario per single-user) +- Suite di test automatici +- Pipeline CI/CD +- HTTPS nativo (gestito da reverse proxy in produzione) +- Gestione code block piΓΉ sofisticata nel reader testo +- Mobile optimization +- Logging strutturato (puΓ² essere aggiunto con poche righe) diff --git a/docs/architecture.md b/docs/architecture.md index a45619b..ce2bd24 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2,22 +2,23 @@ ## Panoramica -Bookish Γ¨ un'applicazione web Flask monolitica progettata per l'uso self-hostato. Si compone di due macro-funzionalitΓ : un **convertitore di formati ebook** e un **reader web** per visualizzarli. +Bookish Γ¨ un'applicazione web Flask monolitica progettata per l'uso self-hostato. Si compone di due macro-funzionalitΓ : un **convertitore di formati ebook** e un **reader web** per visualizzarli, con **SQLite** per persistenza di segnalibri e posizioni di lettura. ``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Browser │────▢│ Flask β”‚ -β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ -β”‚ HTML/CSS β”‚ β”‚ β”‚ Routes β”‚ β”‚ Services β”‚ β”‚ -β”‚ JS/ES β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ -β”‚ β”‚ β”‚ β”‚convert │─▢│converter.py │──▢│ Pandoc -β”‚epub.js β”‚ β”‚ β”‚reader β”‚ β”‚sse_manager.py β”‚ β”‚ WeasyPrint -β”‚pdf.js β”‚ β”‚ β”‚ β”‚ β”‚formatter.py β”‚ β”‚ python-docx -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ β”‚ - uploads/ converted/ - (temporaneo) (temporaneo) +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Browser │────▢│ Flask β”‚ +β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ HTML/CSS β”‚ β”‚ β”‚ Routes β”‚ β”‚ Services β”‚ β”‚ +β”‚ JS/ES β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ convert │─▢│ converter.py │───▢│ Pandoc +β”‚ epub.js β”‚ β”‚ β”‚ reader β”‚ β”‚ sse_manager.py β”‚ β”‚ WeasyPrint +β”‚ pdf.js β”‚ β”‚ β”‚ api β”‚ β”‚ formatter.py β”‚ β”‚ python-docx +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ database.py ──────────▢│───▢│ SQLite + β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ β”‚ β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + uploads/ converted/ + (temporaneo) (temporaneo) ``` ## Stack Tecnologico @@ -25,6 +26,7 @@ Bookish Γ¨ un'applicazione web Flask monolitica progettata per l'uso self-hostat | Componente | Tecnologia | Ruolo | |---|---|---| | Backend | Flask 3.x + Jinja2 | Server web, routing, template engine | +| Database | SQLite (via sqlite3) | Segnalibri e posizioni di lettura | | Conversione | Pandoc 3.x (via pypandoc) | Motore di conversione principale | | PDF avanzato | WeasyPrint | PDF con layout precisi da HTML | | DOCX avanzato | python-docx | Post-processing documenti Word | @@ -32,7 +34,7 @@ Bookish Γ¨ un'applicazione web Flask monolitica progettata per l'uso self-hostat | Reader EPUB | epub.js 0.3 | Renderer EPUB lato client | | Reader PDF | pdf.js 3.x | Renderer PDF lato client | | Progresso | SSE (Server-Sent Events) | Barra progresso real-time | -| Storage | Filesystem locale | `uploads/` e `converted/` | +| Storage | Filesystem + SQLite | `uploads/`, `converted/`, `bookish.db` | ## Flusso Conversione @@ -58,41 +60,110 @@ Upload file β†’ Validazione formato β†’ Avvio thread conversione SSE invia download_url ``` -## Flusso Reader +## Flusso Reader con Persistenza ``` -Upload file β†’ Salva in uploads/ β†’ Redirect a /reader/ - β”‚ - Template reader.html - β”‚ - Rileva formato: - β”œβ”€ EPUB β†’ epub.js - β”œβ”€ PDF β†’ pdf.js - └─ Testo β†’ HTML renderizzato - β”‚ - Interazioni tutte lato client: - - Temi, zoom, ricerca - - Segnalibri, posizione - - Salvataggio in LocalStorage +Upload file β†’ Salva in uploads/ β†’ Calcola SHA256 (content_hash) + β”‚ + Redirect a /reader/ + β”‚ + Template reader.html (contiene BOOKISH_HASH) + β”‚ + Rileva formato: + β”œβ”€ EPUB β†’ epub.js + β”œβ”€ PDF β†’ pdf.js + └─ Testo β†’ HTML renderizzato + β”‚ + Interazioni lato client: + - Temi, zoom, ricerca, indice + - Slider larghezza lettura + - Aiuto contestuale (?) + β”‚ + Segnalibri e posizione: + β”œβ”€ Salvataggio immediato in localStorage + β”œβ”€ Sync asincrono su server SQLite + └─ Caricamento da server con fallback localStorage ``` +Il **content_hash** (SHA256 del file) Γ¨ l'identificatore univoco del libro. Viene calcolato: +- All'upload (restituito nella response) +- All'apertura del reader (dal file su disco) +- Garantisce che lo stesso contenuto β†’ stesso hash β†’ stessi segnalibri + ## Architettura del Codice ### Routes (`bookish/routes/`) - **`convert.py`** β€” Blueprint `/upload`, `/progress/`, `/download/`, `/formats` -- **`reader.py`** β€” Blueprint `/reader`, `/reader/`, `/reader/serve/`, `/reader/formats` +- **`reader.py`** β€” Blueprint `/reader`, `/reader/`, `/reader/serve/`, `/reader/formats`. Calcola SHA256 del file per identificare univocamente il libro. +- **`api.py`** β€” Blueprint `/api/bookmarks/*` (CRUD segnalibri), `/api/position/*` (salva/carica posizione lettura) ### Services (`bookish/services/`) - **`converter.py`** β€” Dispatcher che sceglie Pandoc o pipeline custom in base alla coppia input/output. Aggiorna SSE progress ad ogni fase. - **`sse_manager.py`** β€” Gestisce task in memoria con `{task_id: {progress, status, output_file}}`. Thread-safe con lock. - **`formatter.py`** β€” WeasyPrint PDF da HTML con post-processing (tabelle, immagini via BeautifulSoup). +- **`database.py`** β€” Gestione SQLite con WAL mode. Due tabelle: `bookmarks` (per file) e `reading_positions` (una per file). ### Frontend (`bookish/static/js/`) - **`main.js`** β€” Convertitore: drag&drop, form, SSE connection, download -- **`reader.js`** β€” Reader: epub.js/pdf.js/text viewer, side panel, temi, ricerca, segnalibri, posizione +- **`reader.js`** β€” Reader completo (800+ righe): epub.js/pdf.js/text viewer, sidebar con TOC/cerca/segnalibri, temi, zoom, slider larghezza, help modal, sync server per bookmarks e posizioni + +### Template (`bookish/templates/`) + +- **`base.html`** β€” Layout base con navbar e logo SVG +- **`index.html`** β€” Convertitore con drag&drop, griglia formati, progress bar +- **`reader.html`** β€” Reader con toolbar, sidebar, help modal, e script inline per BOOKISH_FILE, BOOKISH_EXT, BOOKISH_HASH + +## API REST + +| Metodo | Path | Descrizione | +|--------|------|-------------| +| POST | `/upload` | Carica file e avvia conversione | +| GET | `/progress/` | SSE stream progresso | +| GET | `/download/` | Scarica file convertito | +| GET | `/formats?input=` | Formati disponibili | +| GET | `/reader` | Pagina reader | +| GET | `/reader/` | Reader per file | +| GET | `/reader/serve/` | Serve file al reader | +| GET | `/reader/formats` | Formati supportati | +| GET | `/api/bookmarks/` | Segnalibri del libro | +| POST | `/api/bookmarks` | Aggiunge segnalibro | +| DELETE | `/api/bookmarks/` | Elimina segnalibro | +| GET | `/api/position/` | Posizione di lettura | +| PUT | `/api/position/` | Salva posizione di lettura | + +## Database SQLite (`bookish.db`) + +### Tabella `bookmarks` + +| Colonna | Tipo | Descrizione | +|---------|------|-------------| +| id | TEXT PK | ID univoco del segnalibro | +| content_hash | TEXT | SHA256 del file libro | +| type | TEXT | epub, pdf, text | +| cfi | TEXT | CFI per EPUB | +| href | TEXT | Href del capitolo EPUB | +| page | INTEGER | Numero pagina PDF | +| total_pages | INTEGER | Pagine totali (PDF) | +| scroll_pct | INTEGER | % scorrimento (testo) | +| label | TEXT | Etichetta visualizzata | +| chapter_title | TEXT | Titolo capitolo corrente | +| created | REAL | Timestamp creazione | + +### Tabella `reading_positions` + +| Colonna | Tipo | Descrizione | +|---------|------|-------------| +| content_hash | TEXT PK | SHA256 del file libro | +| type | TEXT | epub, pdf, text | +| cfi | TEXT | CFI per EPUB | +| href | TEXT | Href del capitolo | +| page | INTEGER | Pagina PDF | +| scroll_pct | INTEGER | % scorrimento (testo) | +| chapter_title | TEXT | Titolo capitolo | +| updated | REAL | Timestamp ultimo aggiornamento | ## Decisioni Architetturali @@ -100,8 +171,11 @@ Upload file β†’ Salva in uploads/ β†’ Redirect a /reader/ |---|---| | Thread invece di Celery | Uso singolo utente, niente Redis, minore complessitΓ  | | SSE invece di WebSocket | Sincrono semplice, nessuna dipendenza extra, supporto nativo | -| LocalStorage per reader | Zero backend, nessun DB necessario per lettura | -| File temporanei con cleanup | Nessun database, i file si auto-puliscono dopo 1h | +| content_hash (SHA256) invece di UUID | Identifica il contenuto, non l'istanza. Stesso file = stesso hash | +| SQLite per persistenza | Incluso in Python, zero configurazione, file singolo | +| Server + localStorage come fallback | Offline-resilient, UX istantanea, sync asincrono | +| Position save debounced (1.5s) | Evita flooding di scritture SQLite ad ogni pagina | +| File temporanei con cleanup | Nessun accumulo, auto-pulizia dopo 1h | | JS vanilla | Nessun framework, leggero, manutenibile | | Pandoc via pypandoc-binary | Include Pandoc binary, nessuna installazione systema richiesta | @@ -111,4 +185,4 @@ Upload file β†’ Salva in uploads/ β†’ Redirect a /reader/ - **No autenticazione**: Uso personale, nessun multi-utente - **No HTTPS di base**: Da abilitare con reverse proxy (nginx/caddy) - **No ottimizzazione mobile**: Funziona ma non Γ¨ prioritario -- **estione code block reader**: Reader testo ha markdown renderer minimale +- **Gestione code block reader**: Reader testo ha markdown renderer minimale diff --git a/docs/deploy.md b/docs/deploy.md index bf7c234..80af334 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -1,81 +1,105 @@ -# Deploy Bookish +# Deploy di Bookish -## Su Proxmox LXC (raccomandato) - -### 1. Crea container LXC - -```bash -pct create 120 local:vault/almalinux-9-default_20240918_amd64.tar.xz \ - --storage local-lvm \ - --memory 1024 \ - --swap 512 \ - --cores 2 \ - --net0 name=eth0,bridge=vmbr0,ip=dhcp \ - --ostype almalinux \ - --unprivileged 1 -pct start 120 -pct enter 120 -``` - -### 2. Installa dipendenze - -```bash -# Python e strumenti di sviluppo -dnf install -y python3 python3-pip git python3-devel gcc \ - pango-devel cairo-devel gdk-pixbuf2-devel - -# Per WeasyPrint -dnf install -y libffi-devel redhat-rpm-config -``` - -### 3. Clona e setup +## Preparazione ```bash +# Clona il repository git clone http://192.168.0.183:8085/truetype/bookish.git cd bookish + +# Ambiente virtuale +python3 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt + +# Verifica +python app.py +# β†’ Avvia su 0.0.0.0:5000 (o porta libera successiva) +# β†’ Crea automaticamente bookish.db +``` + +## Opzioni di Deploy + +### 1. Proxmox LXC (consigliato) + +Crea un container Linux privilegiato (Debian/Ubuntu): + +```bash +pct create 100 local/vault/debian-12-standard_12.7-1_amd64.tar.zst \ + --hostname bookish \ + --storage local-zfs \ + --rootfs 4 \ + --cores 1 \ + --memory 512 \ + --net0 name=eth0,bridge=vmbr0,firewall=1,ip=dhcp +``` + +All'interno del container: + +```bash +apt update && apt install -y python3 python3-venv python3-pip git +git clone http://192.168.0.183:8085/truetype/bookish.git /opt/bookish +cd /opt/bookish python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` -### 4. Crea service systemd +### 2. Docker + +```dockerfile +FROM python:3.12-slim +RUN apt-get update && apt-get install -y \ + libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 \ + libffi-dev shared-mime-info +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +RUN mkdir -p uploads converted +EXPOSE 5000 +CMD ["python", "app.py"] +``` + +Build e avvio: + +```bash +docker build -t bookish . +docker run -d \ + --name bookish \ + -p 5000:5000 \ + -v bookish_data:/app/uploads \ + -v bookish_data:/app/converted \ + -v bookish_db:/app \ + --restart unless-stopped \ + bookish +``` + +### 3. Systemd Service ```ini -# /etc/systemd/system/bookish.service [Unit] -Description=Bookish Ebook Converter & Reader +Description=Bookish Flask App After=network.target [Service] Type=simple -User=root +User=bookish WorkingDirectory=/opt/bookish -ExecStart=/opt/bookish/.venv/bin/python /opt/bookish/app.py 5000 +Environment=PATH=/opt/bookish/.venv/bin +Environment=BOOKISH_SECRET=your-secure-secret-key +ExecStart=/opt/bookish/.venv/bin/python app.py Restart=always -RestartSec=5 -Environment=BOOKISH_SECRET=your-secret-here -Environment=BOOKISH_DEBUG=false +RestartSec=10 [Install] WantedBy=multi-user.target ``` ```bash -systemctl daemon-reload -systemctl enable --now bookish -``` - -## Docker (alternativa) - -```dockerfile -FROM python:3.11-slim -RUN apt-get update && apt-get install -y pandoc libpango-1.0-0 \ - libcairo2 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 && rm -rf /var/lib/apt/lists/* -WORKDIR /app -COPY . . -RUN pip install --no-cache-dir -r requirements.txt -EXPOSE 5000 -CMD ["python", "app.py", "5000"] +sudo systemctl daemon-reload +sudo systemctl enable bookish +sudo systemctl start bookish ``` ## Reverse Proxy (Caddy) @@ -86,10 +110,55 @@ bookish.tuodominio.it { } ``` -Con Caddy l'HTTPS Γ¨ automatico. +Con Caddy ottieni automaticamente HTTPS tramite Let's Encrypt. -## Pulizia e Manutenzione +## Dati persistenti -I file in `uploads/` e `converted/` vengono puliti automaticamente dopo 1 ora. Nessun DB da manutenere. +Bookish usa il filesystem per dati temporanei e un database SQLite per dati permanenti: -Per backup basta copiare la directory del progetto (escludendo `uploads/`, `converted/`, `.venv/`). +| Cosa | Dove | Persistente? | +|---|---|---| +| File caricati | `uploads/` | ❌ (pulizia 1h) | +| File convertiti | `converted/` | ❌ (pulizia 1h) | +| Segnalibri e posizioni | `bookish.db` | βœ… (SQLite) | + +**Backup**: per salvare segnalibri e posizioni, copia `bookish.db`: + +```bash +cp /opt/bookish/bookish.db /tuo/backup/ +``` + +Il database si ricrea automaticamente se manca (perdendo perΓ² i dati precedenti). + +## Configurazione Porta + +Se `app.py` non trova la porta specificata, scorre in sequenza (`+1`) finchΓ© non trova una porta libera. Puoi specificare la porta di partenza come argomento: + +```bash +python app.py 8080 +# Prova 8080, 8081, 8082... +``` + +## Variabili d'ambiente + +| Variabile | Default | Descrizione | +|---|---|---| +| `BOOKISH_SECRET` | `bookish-secret-key-change-in-production` | Flask secret key | +| `BOOKISH_PORT` | 5000 | Porta di partenza per la ricerca | +| `FLASK_DEBUG` | 0 | ModalitΓ  debug (1 per attivare) | + +## Manutenzione + +```bash +# Update app +git pull +source .venv/bin/activate +pip install -r requirements.txt --upgrade +systemctl restart bookish # se systemd + +# Pulizia hard disk +rm -rf uploads/* converted/* + +# Backup +cp bookish.db bookish.db.$(date +%Y%m%d).bak +``` diff --git a/docs/wiki.md b/docs/wiki.md index 8868c05..7dc19ba 100644 --- a/docs/wiki.md +++ b/docs/wiki.md @@ -25,20 +25,54 @@ | FB2 | EPUB, MD, DOCX, PDF, TXT, HTML, LaTeX | | LaTeX | EPUB, MD, DOCX, PDF, TXT, HTML | -### Reader +### Reader β€” Operazioni base 1. Vai su **Reader** nella navigazione -2. Carica un ebook (EPUB, PDF, MD, TXT, HTML, FB2, DOCX) +2. Trascina un ebook (EPUB, PDF, MD, TXT, HTML, FB2, DOCX) o clicca per caricarlo 3. Usa la toolbar per: - - **☰** β€” Apri/chiudi indice - - **Aβˆ’ / A+** β€” Zoom font - - **β˜€οΈ / πŸŒ™ / 🟀** β€” Cambia tema (chiaro/scuro/seppia) - - **πŸ”** β€” Cerca nel testo - - **β›Ά** β€” Schermo intero -4. Naviga con frecce tastiera ← β†’ o cliccando ai lati -5. La posizione di lettura viene salvata automaticamente -### API Endpoints + | Pulsante | Azione | + |---|---| + | **☰** | Apri/chiudi indice laterale | + | **⇔** | Slider larghezza area di lettura (40–100%) | + | **Aβˆ’ / A+** | Zoom font | + | **β˜€οΈ / πŸŒ™ / 🟀** | Cambia tema (chiaro/scuro/seppia) | + | **πŸ”** | Cerca nel testo | + | **πŸ”–** | Aggiungi segnalibro alla posizione corrente | + | **β›Ά** | Schermo intero | + | **?** | Aiuto e scorciatoie da tastiera | + +4. Naviga con le frecce **← β†’** della tastiera +5. La **posizione di lettura** viene salvata automaticamente (locale + server) + +### Reader β€” Segnalibri + +- Premi **πŸ”–** nella toolbar o **Ctrl+B** / **⌘B** per aggiungere un segnalibro +- Un toast di conferma appare in basso e il pulsante fa un piccolo "pulse" +- Vai nella sidebar **☰ β†’ Segnalibri** per vedere l'elenco +- **Clicca** su un segnalibro per tornarci +- Premi **βœ•** per eliminarlo +- I segnalibri sono salvati su server SQLite + localStorage (fallback) +- Lo stesso libro ha sempre gli stessi segnalibri, anche su PC diverso + +### Reader β€” Scorciatoie da tastiera + +| Tasti | Azione | +|---|---| +| **←** / **β†’** | Pagina precedente/successiva | +| **Ctrl+B** / **⌘B** | Aggiungi segnalibro | +| **Ctrl+F** / **⌘F** | Cerca nel libro | +| **+** / **-** | Zoom avanti/indietro | +| **F** | Schermo intero | +| **Esc** | Chiudi help modal / Esci da fullscreen | + +### Reader β€” Slider larghezza + +La barra a destra nella toolbar (con icona ⇔) regola la larghezza dell'area di lettura dal 40% al 100% dello schermo. Utile per file con righe molto lunghe o per preferenze personali di lettura. L'impostazione viene salvata in localStorage. + +## API Endpoints + +### Endpoints pubblici | Metodo | Path | Descrizione | |---|---|---| @@ -51,16 +85,32 @@ | GET | `/reader/serve/` | Serve il file al reader | | GET | `/reader/formats` | Formati supportati dal reader | +### API REST (bookmarks e posizioni) + +| Metodo | Path | Descrizione | +|---|---|---| +| GET | `/api/bookmarks/` | Recupera tutti i segnalibri del libro | +| POST | `/api/bookmarks` | Aggiunge un segnalibro | +| DELETE | `/api/bookmarks/` | Elimina un segnalibro | +| GET | `/api/position/` | Recupera l'ultima posizione di lettura | +| PUT | `/api/position/` | Salva la posizione di lettura (body JSON) | + ## Domande frequenti **D: Che qualitΓ  ha la conversione?** R: Pandoc preserva titoli, grassetto/corsivo, elenchi, tabelle semplici e immagini. Per layout complessi (es. PDF con colonne multiple) la fedeltΓ  dipende dalla complessitΓ  del file originale. **D: I file vengono conservati?** -R: No, vengono cancellati dopo 1 ora. Usa il download subito dopo la conversione. +R: I file in `uploads/` e `converted/` vengono cancellati dopo 1 ora. Usa il download subito dopo la conversione. I segnalibri e le posizioni di lettura sono invece permanenti in `bookish.db`. + +**D: I segnalibri restano se ricarico lo stesso libro?** +R: SΓ¬. I segnalibri sono legati al contenuto del file (SHA256), non al nome o all'UUID di upload. Stesso file = stessi segnalibri. + +**D: Posso usare i segnalibri da un altro PC?** +R: SΓ¬, se sei sulla stessa LAN e raggiungi lo stesso server. I segnalibri sono salvati su SQLite, non nel browser. **D: Quanto spazio serve?** -R: Minimo. L'app stessa Γ¨ ~5MB. Pandoc binary incluso ~35MB extra. +R: Minimo. L'app stessa Γ¨ ~5MB. Pandoc binary incluso ~35MB extra. Il database SQLite occupa pochi KB. **D: Posso usarlo su mobile?** R: Il convertitore Γ¨ responsive. Il reader funziona ma non Γ¨ ottimizzato per schermi piccoli. @@ -75,4 +125,4 @@ R: Imposta `BOOKISH_SECRET` come variabile d'ambiente prima di avviare. - Path traversal prevenuto con `os.path.basename()` e `secure_filename()` - MIME types controllati al download - Limite file a 50MB -- Pulizia automatica file temporanei +- Pulizia automatica file temporanei dopo 1h