docs: add logo, README, architecture, deploy, wiki, analysis

This commit is contained in:
truetype committed 2026-07-22 15:50:35 +02:00
1 parent 32245f76ac
commit 15eb4016de
9 files changed
+561 -1

No files matched your search

+1
View File
@@ -4,3 +4,4 @@ __pycache__/
uploads/
converted/
.venv.bak/
.sisyphus/
+124
View File
@@ -0,0 +1,124 @@
# 📚 Bookish
**Convertitore e reader di ebook self-hostato** — Flask, Pandoc, WeasyPrint.
Carica un ebook in qualsiasi formato, convertilo in un altro, o leggilo direttamente nel browser con un reader completo.
---
## Funzionalità
### 🔄 Convertitore
| Formato | Input | Output |
|---------|-------|--------|
| EPUB | ✅ | ✅ |
| PDF | ✅ | ✅ |
| DOCX | ✅ | ✅ |
| Markdown | ✅ | ✅ |
| Testo (TXT) | ✅ | ✅ |
| HTML | ✅ | ✅ |
| FB2 | ✅ | ✅ |
| MOBI | ✅ | ❌ |
| LaTeX | ✅ | ✅ |
- Drag & drop upload
- Barra progresso in tempo reale (SSE)
- Download automatico del convertito
### 📖 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
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
### ⚙️ Motore di conversione
- **Pandoc** come motore primario (decine di formati)
- **WeasyPrint** per PDF con layout avanzato
- **python-docx** per DOCX con stili
- **BeautifulSoup** per post-processing HTML/tabelle
---
## Requisiti
- Python 3.10+
- Pandoc (incluso via pypandoc-binary)
- Librerie di sistema per WeasyPrint (libpango, libcairo)
## Installazione
```bash
git clone http://192.168.0.183:8085/truetype/bookish.git
cd bookish
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
## Avvio
```bash
./run.sh
# oppure su porta specifica:
./run.sh 8080
```
L'app cerca automaticamente una porta libera a partire da quella specificata.
Apri il browser su `http://localhost:5000` (o la porta assegnata).
---
## Architettura
```
bookish/
├── app.py # Entry point
├── config.py # Configurazioni
├── requirements.txt
├── run.sh
├── bookish/
│ ├── __init__.py # Flask factory
│ ├── routes/
│ │ ├── convert.py # Upload, SSE progress, download
│ │ └── reader.py # Reader view
│ ├── services/
│ │ ├── converter.py # Conversione Pandoc + pipeline
│ │ ├── sse_manager.py # SSE progress manager
│ │ └── formatter.py # PDF/HTML post-processing
│ ├── templates/
│ │ ├── base.html # Layout base
│ │ ├── index.html # Convertitore
│ │ └── reader.html # Reader
│ └── static/
│ ├── css/style.css
│ ├── js/main.js # Frontend convertitore
│ ├── js/reader.js # Frontend reader
│ └── logo.svg
├── uploads/ # File caricati (temporanei)
├── converted/ # File convertiti (temporanei)
└── docs/ # Documentazione
```
Vedi [docs/architecture.md](docs/architecture.md) per dettagli.
---
## Deployment
Adatto per Proxmox LXC, Docker o VM leggera. Vedi [docs/deploy.md](docs/deploy.md).
---
## Licenza
MIT
+8
View File
@@ -44,6 +44,14 @@ body {
font-weight: 600;
text-decoration: none;
color: #1d1d1f;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-logo {
width: 28px;
height: 28px;
}
.nav-links {
+38
View File
@@ -0,0 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#007aff"/>
<stop offset="100%" stop-color="#34c759"/>
</linearGradient>
<linearGradient id="page" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#fff"/>
<stop offset="100%" stop-color="#f0f0f5"/>
</linearGradient>
</defs>
<!-- Cerchio sfondo -->
<circle cx="256" cy="256" r="240" fill="url(#bg)"/>
<!-- Libro aperto - pagina sinistra -->
<path d="M128 160 L128 352 L256 384 L256 192 Z" fill="url(#page)" stroke="#e0e0e5" stroke-width="2"/>
<!-- Libro aperto - pagina destra -->
<path d="M384 160 L384 352 L256 384 L256 192 Z" fill="url(#page)" stroke="#e0e0e5" stroke-width="2"/>
<!-- Copertina sinistra -->
<path d="M120 152 L120 344 L128 352 L128 160 Z" fill="#d0d0d5" stroke="#bbb" stroke-width="1.5"/>
<!-- Copertina destra -->
<path d="M392 152 L392 344 L384 352 L384 160 Z" fill="#d0d0d5" stroke="#bbb" stroke-width="1.5"/>
<!-- Dorso -->
<path d="M248 190 L248 382 L264 382 L264 190 Z" fill="#e0e0e5" stroke="#ccc" stroke-width="1"/>
<!-- Righe di testo sinistra -->
<line x1="156" y1="216" x2="236" y2="232" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="156" y1="240" x2="236" y2="256" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="156" y1="264" x2="236" y2="280" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="156" y1="288" x2="220" y2="300" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<!-- Righe di testo destra -->
<line x1="276" y1="232" x2="356" y2="216" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="276" y1="256" x2="356" y2="240" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="276" y1="280" x2="356" y2="264" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<line x1="292" y1="300" x2="356" y2="288" stroke="#999" stroke-width="3" stroke-linecap="round"/>
<!-- Icona freccia conversione in basso -->
<circle cx="256" cy="440" r="28" fill="#fff" opacity="0.9"/>
<path d="M244 436 L256 448 L268 436" stroke="#007aff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<path d="M256 424 L256 448" stroke="#007aff" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+5 -1
View File
@@ -4,13 +4,17 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Bookish{% endblock %}</title>
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='logo.svg') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block head %}{% endblock %}
</head>
<body>
<nav class="navbar">
<div class="nav-inner">
<a href="/" class="nav-brand">📚 Bookish</a>
<a href="/" class="nav-brand">
<img src="{{ url_for('static', filename='logo.svg') }}" alt="Bookish" class="nav-logo">
Bookish
</a>
<div class="nav-links">
<a href="/" class="nav-link {% if request.path == '/' %}active{% endif %}">Convertitore</a>
<a href="/reader" class="nav-link {% if request.path.startswith('/reader') %}active{% endif %}">Reader</a>
+98
View File
@@ -0,0 +1,98 @@
# Analisi Completa 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.
### 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
### 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)
### 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. Analisi della Sicurezza
| Asse | Valutazione | Note |
|---|---|---|
| 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 |
## 3. Analisi Performance
- **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à
```
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/
```
La separazione è chiara. Unica nota: `converter.py` importa `formatter.py` — dipendenza circolare potenziale ma gestita con import differito.
## 5. Possibili Miglioramenti
### 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
### 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.
+114
View File
@@ -0,0 +1,114 @@
# Architettura di Bookish
## 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.
```
┌──────────┐ ┌─────────────────────────────────────┐
│ 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)
```
## Stack Tecnologico
| Componente | Tecnologia | Ruolo |
|---|---|---|
| Backend | Flask 3.x + Jinja2 | Server web, routing, template engine |
| 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 |
| Frontend | JavaScript vanilla | Nessun framework pesante |
| 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/` |
## Flusso Conversione
```
Upload file → Validazione formato → Avvio thread conversione
│
┌──────────────────────┘
▼
SSE Manager crea task_id
│
Frontend apre connessione SSE
│
Converter dispatcher:
├─ Pandoc per formati standard
├─ WeasyPrint per PDF avanzati
├─ python-docx per DOCX
└─ BeautifulSoup per tabelle/HTML
│
Ogni fase → update SSE progress
│
▼
File salvato in converted/
SSE invia download_url
```
## Flusso Reader
```
Upload file → Salva in uploads/ → Redirect a /reader/<file_id>
│
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
```
## Architettura del Codice
### Routes (`bookish/routes/`)
- **`convert.py`** — Blueprint `/upload`, `/progress/<task_id>`, `/download/<filename>`, `/formats`
- **`reader.py`** — Blueprint `/reader`, `/reader/<filename>`, `/reader/serve/<filename>`, `/reader/formats`
### 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).
### 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
## Decisioni Architetturali
| Decisione | Motivazione |
|---|---|
| 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 |
| JS vanilla | Nessun framework, leggero, manutenibile |
| Pandoc via pypandoc-binary | Include Pandoc binary, nessuna installazione systema richiesta |
## Limiti Attuali
- **Thread in memoria**: I task di conversione muoiono se il processo Flask viene killato
- **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
+95
View File
@@ -0,0 +1,95 @@
# Deploy 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
```bash
git clone http://192.168.0.183:8085/truetype/bookish.git
cd bookish
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
### 4. Crea service systemd
```ini
# /etc/systemd/system/bookish.service
[Unit]
Description=Bookish Ebook Converter & Reader
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/bookish
ExecStart=/opt/bookish/.venv/bin/python /opt/bookish/app.py 5000
Restart=always
RestartSec=5
Environment=BOOKISH_SECRET=your-secret-here
Environment=BOOKISH_DEBUG=false
[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"]
```
## Reverse Proxy (Caddy)
```caddyfile
bookish.tuodominio.it {
reverse_proxy localhost:5000
}
```
Con Caddy l'HTTPS è automatico.
## Pulizia e Manutenzione
I file in `uploads/` e `converted/` vengono puliti automaticamente dopo 1 ora. Nessun DB da manutenere.
Per backup basta copiare la directory del progetto (escludendo `uploads/`, `converted/`, `.venv/`).
+78
View File
@@ -0,0 +1,78 @@
# Wiki di Bookish
## Guida all'uso
### Convertitore
1. Apri la homepage di Bookish
2. Trascina un file nell'area di upload, oppure clicca per selezionarlo
3. Scegli il formato di destinazione cliccando su un bottone
4. Clicca **Converti**
5. La barra di progresso mostra lo stato della conversione
6. A conversione completata, clicca **Scarica file**
7. Opzionale: clicca **Leggi nel reader** per visualizzare il file convertito
### Formati supportati
| Input | Output |
|---|---|
| EPUB | EPUB, MD, DOCX, PDF, TXT, HTML, FB2, LaTeX |
| PDF | EPUB, MD, DOCX, TXT, HTML, LaTeX |
| DOCX | EPUB, MD, PDF, TXT, HTML, LaTeX |
| Markdown | EPUB, DOCX, PDF, TXT, HTML, LaTeX |
| TXT | EPUB, MD, DOCX, PDF, HTML, LaTeX |
| HTML | EPUB, MD, DOCX, PDF, TXT, LaTeX |
| FB2 | EPUB, MD, DOCX, PDF, TXT, HTML, LaTeX |
| LaTeX | EPUB, MD, DOCX, PDF, TXT, HTML |
### Reader
1. Vai su **Reader** nella navigazione
2. Carica un ebook (EPUB, PDF, MD, TXT, HTML, FB2, DOCX)
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
| Metodo | Path | Descrizione |
|---|---|---|
| POST | `/upload` | Carica file e avvia conversione |
| GET | `/progress/<task_id>` | SSE stream progresso |
| GET | `/download/<filename>` | Scarica file convertito |
| GET | `/formats?input=<ext>` | Formati disponibili per input |
| GET | `/reader` | Pagina reader |
| GET | `/reader/<filename>` | Reader per file specifico |
| GET | `/reader/serve/<filename>` | Serve il file al reader |
| GET | `/reader/formats` | Formati supportati dal reader |
## 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.
**D: Quanto spazio serve?**
R: Minimo. L'app stessa è ~5MB. Pandoc binary incluso ~35MB extra.
**D: Posso usarlo su mobile?**
R: Il convertitore è responsive. Il reader funziona ma non è ottimizzato per schermi piccoli.
**D: Come cambio la secret key?**
R: Imposta `BOOKISH_SECRET` come variabile d'ambiente prima di avviare.
## Sicurezza
- Uso personale — nessuna autenticazione implementata
- File validati per estensione all'upload
- Path traversal prevenuto con `os.path.basename()` e `secure_filename()`
- MIME types controllati al download
- Limite file a 50MB
- Pulizia automatica file temporanei