4.4 KiB
4.4 KiB
Self-Hosted Photo Manager — TODO
Architecture
A self-built alternative to Immich: React/Vite frontend + Express/SQLite backend, files stay on disk, DB indexes them, thumbnails are the only derived asset.
┌───────────────────┐ watches ┌──────────────────────┐
│ Photo folder(s) │ ─────────────────────▶ │ Ingestion Worker │
│ (disk / NAS) │ │ (chokidar file watch) │
└───────────────────┘ └───────────┬────────────┘
│ extract EXIF,
│ generate thumbnail,
│ compute pHash
▼
┌──────────────────────┐
│ SQLite metadata DB │
│ (photos, tags, │
│ albums, hashes) │
└───────────┬────────────┘
│
▼
┌──────────────────────┐
│ Express API server │
└───────────┬────────────┘
│ REST
▼
┌──────────────────────┐
│ React/Vite frontend │
│ (timeline, albums, │
│ search, map) │
└──────────────────────┘
Build Order
- 1. Ingestion worker + SQLite schema — watch folder, extract EXIF, generate thumbnails, populate DB
- 2. Basic API + timeline UI — paginated grid, chronological browsing (usable on its own)
- 3. Detail view + manual tagging/albums
- 4. Dedupe detection + review UI — pHash comparison, side-by-side review, keep/delete
- 5. Search + filters — date range, camera model, tag
- 6. Map view — Leaflet/MapLibre pins from GPS EXIF
- 7. Face grouping — only if wanted after v1–v5 (biggest scope jump, treat as v2+)
Stack
| Layer | Tech |
|---|---|
| File watching | chokidar (persistent) or cron scan |
| EXIF extraction | exifr or exiftool wrapper |
| Thumbnails | sharp (1–2 sizes: grid thumb + preview) |
| Dedupe hashing | pHash via sharp + hashing lib, or blockhash-core |
| Database | SQLite via better-sqlite3 |
| API | Express |
| Frontend | React + Vite |
| Map | Leaflet or MapLibre |
| Deployment | Docker container, Nginx Proxy Manager, behind Authelia |
Database Schema
photos (
id, filepath, thumbnail_path, date_taken, date_imported,
camera_model, gps_lat, gps_lon, phash, width, height, filesize
)
albums (id, name, created_at)
photo_albums (photo_id, album_id)
tags (id, name)
photo_tags (photo_id, tag_id)
API Endpoints (planned)
GET /photos paginated timeline (filter by date, tag, album)
GET /photos/:id full detail + EXIF
GET /photos/duplicates grouped by pHash similarity
GET /photos/map GPS-tagged photos for map view
POST /albums create album
POST /photos/:id/tags tag a photo
Deployment Notes
- Mount photo folder(s) read-only (worker doesn't need write access to originals)
- Writable volume for thumbnails + DB
- Route:
photos.necloud.usvia Nginx Proxy Manager - Same homelab pattern as other services