photo_server_json_con_aves22/public/README.md
2026-04-18 20:14:42 +02:00

696 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

Certo Fabio — ora che tutto il backend e il frontend sono finalmente coerenti, ti preparo un README tecnico completo, scritto come si deve, chiaro, professionale e utile sia per te che per chiunque metterà mano al progetto in futuro.
È pensato per essere messo direttamente in README.md alla radice del progetto.
---
📸 PhotoGallery — README Tecnico Completo
Una piattaforma completa per la gestione di foto multiutente, con:
- autenticazione JWT
- scansione automatica delle cartelle
- thumbnails generate lato server
- sincronizzazione incrementale
- WebSocket realtime
- frontend modulare e reattivo
- mappa globale stile Google Photos
- pannello info EXIF + geolocalizzazione
- bottom sheet multifoto
- modal fullscreen con navigazione
---
🚀 1. Architettura generale
`
backend/
server.js
api_v1/
scanner/
config.js
...
db/
knex.js
routes/
photos.js
public/
index.html
login.html
css/
js/
auth.js
login.js
logout.js
config.js
api.js
state.js
sync.js
gallery.js
modal.js
infoPanel.js
mapGlobal.js
bottomSheet.js
optionsSheet.js
`
---
🔐 2. Autenticazione
Login
POST /auth/login
Richiede:
`json
{ "email": "...", "password": "..." }
`
Risponde:
`json
{ "token": "JWT...", "name": "Fabio" }
`
Il token contiene:
`json
{
"id": 1,
"email": "fabio@example.com",
"name": "Fabio",
"exp": 1234567890
}
`
Logout
POST /auth/logout
Il token viene inserito in denylist.
Middleware
Tutte le rotte (eccetto /auth/*) richiedono:
`
Authorization: Bearer <token>
`
Il middleware aggiunge automaticamente:
`
req.query.user = [req.user.name, "Common"]
`
Quindi il frontend non deve mai passare user=.
---
🗂️ 3. Configurazione
GET /config restituisce:
`json
{
"baseUrl": "https://…",
"pathFull": false,
"galleryRefreshSeconds": 30
}
`
Il frontend usa:
- baseUrl per costruire URL assoluti
- pathFull per capire se i path sono già completi
- galleryRefreshSeconds per il polling
---
🖼️ 4. Foto: API principali
Tutte le foto
GET /photos?user[]=Fabio&user[]=Common
(aggiunto automaticamente dal middleware)
Foto per ID
GET /photos/byIds?id=123
Cambiamenti incrementali
GET /photos/changes?since=<ISO>
Restituisce:
`json
{
"changes": [
{ "photoid": 123, "changetype": "added", "timestamp": "..." },
{ "photoid": 456, "changetype": "removed", "timestamp": "..." }
]
}
`
---
🔄 5. Sincronizzazione
Il frontend implementa:
Full load
- chiamato al primo avvio
- scarica tutte le foto
- salva in localStorage
- aggiorna lastSync
Incremental sync
- usa /photos/changes
- applica solo differenze
- aggiorna la gallery
- aggiorna lastSync
WebSocket realtime
Il server invia:
`
{ type: "added", id }
{ type: "removed", id }
{ type: "add_dir", folder }
{ type: "del_dir", folder }
`
Il frontend aggiorna:
- stato locale
- gallery
- mappa
---
🗺️ 6. Mappa globale
Basata su Leaflet + MarkerCluster.
- clustering automatico
- collage thumbnails nei cluster
- click cluster → bottom sheet
- click foto → modal
- refresh automatico quando cambia la gallery
---
🪟 7. Modal foto/video
Funzionalità:
- navigazione ← →
- click ai bordi
- tastiera
- preload ±3 foto
- supporto video (mp4/webm/mov)
- pannello info integrato
---
8. Info Panel
Mostra:
- nome
- data
- dimensioni
- peso
- MIME
- cartella
- EXIF GPS
- reverse geocoding (se presente)
- mappa dedicata
Autorefresh quando cambi foto nel modal.
---
📚 9. Bottom Sheet
Usato per:
- mostrare gruppi di foto (cluster mappa)
- navigazione rapida
- apertura modal
---
⚙️ 10. Options Sheet
Gestisce:
- ordinamento (asc/desc)
- raggruppamento (auto/day/month/year)
- filtri (folder/location/type)
Aggiorna la gallery in tempo reale.
---
🧠 11. Stato locale
Gestito in state.js:
- localPhotos[]
- lastSync
- cache in localStorage
- funzioni: add/remove/update
---
🧩 12. Costruzione URL media
Tutto passa da:
`js
mediaUrlsFromPhoto(photo)
`
Restituisce:
`js
{ original, preview }
`
Supporta:
- path relativi
- path assoluti
- baseUrl dinamico
- cartella utente
---
🧪 13. Test endtoend consigliato
1. Vai su /login
2. Inserisci email/password
3. Redirect a /
4. Full load → gallery appare
5. Apri una foto → modal
6. Apri info → mappa + EXIF
7. Apri mappa globale → cluster
8. Click cluster → bottom sheet
9. Click foto → modal
10. Cancella una foto dal filesystem → WS → frontend aggiorna
11. Logout → redirect a login
---
🛠️ 14. Requisiti
- Node.js 18+
- SQLite3
- ffmpeg (per thumbnails video)
- ImageMagick (per thumbnails foto)
---
▶️ 15. Avvio
`bash
npm install
node server.js
`
Server su:
`
http://localhost:4000
`
---
🎯 16. TODO / miglioramenti futuri
- virtualizzazione gallery (migliaia di foto)
- ricerca per testo
- timeline orizzontale
- upload drag&drop
- editing EXIF
- preferiti / album
---
💬 17. Contatti
Progetto sviluppato da Fabio
Backend + frontend completamente integrati.
---
Perfetto Fabio — ti preparo il diagramma architetturale completo, chiaro, leggibile e fedele al tuo backend e al tuo frontend attuale.
Niente fronzoli, niente astrazioni inutili: solo ciò che serve davvero per capire come gira tutto.
---
🧩 DIAGRAMMA ARCHITETTURALE — PhotoGallery System
1) Vista generale (HighLevel)
`
┌──────────────────────────┐
│ FRONTEND │
│ (public/, JS modules) │
└─────────────┬────────────┘
│ HTTP + WS
┌─────────────▼────────────┐
│ BACKEND │
│ (Node + Express) │
└─────────────┬────────────┘
│ DB queries
┌─────────────▼────────────┐
│ SQLite DB │
│ (tabella photos) │
└───────────────────────────┘
`
---
2) Dettaglio Frontend (moduli JS)
`
┌──────────────────────────────────────────────────────────────┐
│ FRONTEND │
│ │
│ index.html / login.html │
│ │ │
│ ▼ │
│ auth.js ←→ login.js ←→ logout.js │
│ │ │
│ ▼ │
│ config.js → api.js → state.js → sync.js │
│ │ │ │ │
│ │ │ └── WebSocket │
│ ▼ ▼ │
│ gallery.js ←→ modal.js ←→ infoPanel.js │
│ │ │ │
│ ▼ ▼ │
│ mapGlobal.js ←→ bottomSheet.js ←→ optionsSheet.js │
└──────────────────────────────────────────────────────────────┘
`
Flusso principale:
1. auth.js controlla token → se valido, mostra app
2. config.js carica /config
3. sync.js fa full load → incremental sync → WebSocket
4. state.js mantiene foto locali
5. gallery.js renderizza
6. modal.js apre foto/video
7. infoPanel.js mostra EXIF + mappa
8. mapGlobal.js mostra mappa globale
9. bottomSheet.js mostra strip foto
10. optionsSheet.js gestisce filtri/ordinamento
---
3) Dettaglio Backend (server.js)
`
┌──────────────────────────────────────────────────────────────┐
│ BACKEND │
│ │
│ server.js │
│ │ │
│ ├── STATIC: serve public/ │
│ ├── /config │
│ ├── /auth/login │
│ ├── /auth/logout │
│ │ │
│ ├── JWT middleware │
│ │ ├── verify token │
│ │ ├── denylist │
│ │ └── req.user = { id, email, name } │
│ │ │
│ ├── GET middleware (user filtering) │
│ │ └── req.query.user = [req.user.name, "Common"] │
│ │ │
│ ├── /scan (Admin → tutti, User → solo se stesso) │
│ ├── /apiv1/autoscan (ADD, DEL, ADDDIR, DELDIR) │
│ │ └── WebSocket broadcast │
│ │ │
│ ├── /photos (router SQLite) │
│ │ ├── /photos │
│ │ ├── /photos/byIds │
│ │ └── /photos/changes │
│ │ │
│ ├── /files (serve file statici sicuri) │
│ ├── /initDB /initDBuser │
│ │ │
│ └── ws-server.js (WebSocket) │
└──────────────────────────────────────────────────────────────┘
`
---
4) Flusso completo di una foto (endtoend)
`
[1] File aggiunto nel filesystem
[2] scan_auto → type="ADD"
├── scanFile()
├── scanPhotoSingle()
├── INSERT in DB
├── genera thumbnails
└── WS: { type:"added", id }
[3] Frontend riceve WS
├── getPhotoById(id)
├── addPhotoLocal()
├── refreshGallery()
└── redrawPhotoMarkers()
`
---
5) Flusso login → gallery
`
login.html
login.js → AppAuth.login(email, password)
POST /auth/login
token JWT salvato
redirect → /
index.html
auth.js.isLoggedIn() → OK
config.js → GET /config
sync.js.fullLoad()
GET /photos
state.js.setLocalPhotos()
gallery.js.renderGallery()
mapGlobal.js.redrawPhotoMarkers()
`
---
6) Diagramma WebSocket
`
┌──────────────┐ WS ┌──────────────┐
│ Backend │ ───────────→ │ Frontend │
└──────────────┘ └──────────────┘
│ │
│ added → { id } │
│────────────────────────────────▶│ addPhotoLocal()
│ │ refreshGallery()
│ │ redrawPhotoMarkers()
│ │
│ removed → { id } │
│────────────────────────────────▶│ removePhotoLocal()
│ │ refreshGallery()
│ │ redrawPhotoMarkers()
│ │
│ adddir / deldir │
│────────────────────────────────▶│ incrementalSync()
`
---
7) Diagramma Database
`
┌──────────────────────────────┐
│ photos │
├──────────────────────────────┤
│ id (PK) │
│ user │
│ name │
│ path │
│ cartella │
│ mime_type │
│ width │
│ height │
│ size_bytes │
│ taken_at │
│ gps_lat │
│ gps_lng │
│ gps_alt │
│ location_json │
│ created_at │
└──────────────────────────────┘
`
---
8) Diagramma URL media
`
BASE_URL/photos/<user>/<type>/<cartella>/<file>
Esempi:
original:
https://server/photos/Fabio/original/2024/IMG_001.jpg
thumbs:
https://server/photos/Fabio/thumbs/2024/IMG001thub2.jpg
`
---
9) Diagramma dei moduli JS (dipendenze)
`
auth.js
login.js → logout.js
index.html
config.js → api.js → state.js → sync.js → WebSocket
↓ ↓ ↓
gallery.js ← modal.js ← infoPanel.js
mapGlobal.js ← bottomSheet.js ← optionsSheet.js
`
---
Utente Browser/Frontend Backend (Express) DB (SQLite)
│ │ │ │
│ 1. Apre /login │ │ │
├──────────────────────────▶│ │ │
│ │ │ │
│ │ 2. Inserisce email/password │ │
│ ├────────────────────────────────▶│ POST /auth/login │
│ │ │ │
│ │ │ 3. Verifica utente │
│ │ │ bcrypt.compare() │
│ │ ├────────────────────────▶│
│ │ │ │
│ │ │ 4. Genera JWT │
│ │ │ createToken() │
│ │ │ │
│ │ 5. Riceve token │ │
│ ◀─────────────────────────────────┤ │
│ │ │ │
│ │ 6. Salva token (localStorage) │ │
│ │ 7. Redirect → / │ │
├──────────────────────────▶│ │ │
│ │ │ │
│ │ 8. GET /config │ │
│ ├────────────────────────────────▶│ │
│ │ │ │
│ │ 9. Riceve config │ │
│ ◀─────────────────────────────────┤ │
│ │ │ │
│ │ 10. Full Sync │ │
│ │ GET /photos │ │
│ ├────────────────────────────────▶│ │
│ │ │ 11. Middleware JWT │
│ │ │ req.user = {name,...} │
│ │ │ │
│ │ │ 12. Filtra per utente │
│ │ │ req.query.user=[Fabio,Common]
│ │ │ │
│ │ ├────────────────────────▶│ SELECT * FROM photos WHERE user IN (...)
│ │ │ │
│ │ 13. Riceve lista foto │ │
│ ◀─────────────────────────────────┤ │
│ │ │ │
│ │ 14. state.setLocalPhotos() │ │
│ │ 15. gallery.render() │ │
│ │ 16. mapGlobal.redrawMarkers() │ │
│ │ │ │
│ │ 17. Apre WebSocket │ │
│ ├────────────────────────────────▶│ ws-server │
│ │ │ │
│ │ │ │
│ │ 18. Incremental Sync (polling) │ │
│ │ GET /photos/changes?since=... │ │
│ ├────────────────────────────────▶│ │
│ │ │ │
│ │ 19. Riceve changes │ │
│ ◀─────────────────────────────────┤ │
│ │ │ │
│ │ 20. Applica differenze │ │
│ │ state.add/remove/update │ │
│ │ gallery.refresh() │ │
│ │ mapGlobal.redrawMarkers() │ │
│ │ │ │
│ │ │ │
│ │ 21. Evento reale: file aggiunto │ │
│ │ │ scan_auto → ADD │
│ │ │ INSERT in DB │
│ │ ├────────────────────────▶│
│ │ │ │
│ │ │ 22. WS broadcast │
│ ◀─────────────────────────────────┤ {type:"added", id} │
│ │ │ │
│ │ 23. getPhotoById(id) │ │
│ ├────────────────────────────────▶│ /photos/byIds │
│ │ │ │
│ │ 24. Riceve foto │ │
│ ◀─────────────────────────────────┤ │
│ │ │ │
│ │ 25. state.addPhotoLocal() │ │
│ │ 26. gallery.refresh() │ │
│ │ 27. mapGlobal.redrawMarkers() │ │
│ │ │ │
│ │ │ │
│ 28. Clic su foto │ │ │
├──────────────────────────▶│ modal.open(photo) │ │
│ │ infoPanel.render(photo) │ │
│ │ │ │
│ │ │ │
│ 29. Logout │ │ │
├──────────────────────────▶│ AppAuth.logout() │ │
│ ├────────────────────────────────▶│ POST /auth/logout │
│ │ │ addToDenylist(token) │
│ │ │ │
│ │ 30. clearTokens() │ │
│ │ 31. redirect → /login │ │
└───────────────────────────┴─────────────────────────────────┴─────────────────────────┘