commit 309a6723d7fc8dfa16f23002b8f8ed519a7b629d Author: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Mon Aug 17 11:01:47 2026 -0400 Add architecture TODO and gitignore Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..27bdffb --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +.env +*.db +*.db-shm +*.db-wal +thumbnails/ diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..eff3b6c --- /dev/null +++ b/TODO.md @@ -0,0 +1,89 @@ +# 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 + +```sql +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.us` via Nginx Proxy Manager +- Same homelab pattern as other services