diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-02-13 08:45:49 +0100 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-02-13 08:45:49 +0100 |
| commit | 17be2fcb106ddc9c36628e9f5dece28369affc30 (patch) | |
| tree | 4f3cb1d2fade5467f6c79a9a1abe1da0852a61d0 /DESIGN.md | |
Extract gallery app from perso/vps into standalone project
Extracted all gallery-related files from ~/perso/vps:
- gallery.go, gallery_test.go (Go source + tests)
- templates/ (index, login, gallery HTML)
- static/style.css
- go.mod/go.sum (renamed module to github.com/kagamino/kagapics)
- Caddyfile.example (gallery-only Caddy config)
- DESIGN.md (from galery.md)
Diffstat (limited to 'DESIGN.md')
| -rw-r--r-- | DESIGN.md | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..39f7db4 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,277 @@ +# Photo Gallery — Design Document + +## 0. Solution + +**Go app (Option B)** — a single static binary reverse-proxied by Caddy. + +- Caddy handles TLS and serves the app on a secret URL path (e.g., `https://photos.example.com/trip-crete-2026-a8f3b2c1/`). +- The Go binary serves an HTML page with multi-file upload (drag-and-drop), a file listing, and a "download all as zip" button. +- Photos land in a configurable directory (one per trip). When the trip is done, stop the binary, move the folder to cold storage. +- No database, no runtime deps, no Docker. ~200-400 lines of Go, ~10 MB binary. + +**Why this over pure Caddy (Option A):** At this scale (10 friends, 200 photos, ~4 GB max), both work. But the Go app gives a noticeably better mobile upload experience (multi-file, progress feedback) and zip download for free — worth the cost of one extra process. + +--- + +## 1. Context & Scope + +After a holiday with friends, everyone has photos scattered across their phones. The goal is a self-hosted, lightweight web application where: + +- Friends open a link in their browser, upload their photos. +- Friends can browse and download photos others have uploaded. +- Once everyone has contributed, all photos live on the server — a single consolidated archive. +- The host (you) keeps everything on their own infrastructure. No third-party cloud involved. + +**Current landscape:** Caddy is already used as the primary web server/reverse proxy. The solution should integrate naturally with it. The server is personal infrastructure — simplicity and low resource usage matter more than scaling to thousands of users. + +--- + +## 2. Goals & Non-Goals + +### Goals + +- **Browser-only UX** — no app install, no client software, no account creation. +- **Upload & download** — friends can push photos and pull the full set. +- **Lightweight** — minimal dependencies, low memory/CPU footprint, easy to deploy and tear down. +- **Self-hosted** — all data stays on your server, full ownership. +- **Simple ops** — ideally a single binary or a Caddyfile tweak, no database. +- **Caddy-friendly** — works behind or with Caddy with minimal config. + +### Non-Goals + +- User accounts or authentication beyond a shared secret/link. +- Image editing, resizing, or transcoding. +- Long-lived public gallery (this is ephemeral, per-trip). +- Mobile app. +- Handling thousands of concurrent users. +- Video support (nice-to-have at best, not a driver). + +--- + +## 3. Design + +The system is intentionally shallow. One server, one directory of files, one web interface. + +```mermaid +graph LR + subgraph Friends + A[Browser] + B[Browser] + C[Browser] + end + + subgraph Server + D[Caddy] + E[Application / File Backend] + F[/photos directory/] + end + + A -- "upload/download via HTTPS" --> D + B -- "upload/download via HTTPS" --> D + C -- "upload/download via HTTPS" --> D + D -- "reverse proxy or direct serve" --> E + E -- "read/write" --> F +``` + +### Features + +#### Friends (end users) + +| Feature | Detail | +|---|---| +| **Batch upload** | Select multiple photos at once (file picker or drag-and-drop) and upload them in a single action. Progress feedback per file. | +| **Browse gallery** | View all uploaded photos directly in the browser — no download required. Images are served as compressed/resized previews to keep page load fast. | +| **Download as zip** | Download the entire directory as a single `.zip` file with one click. | +| **Password access** | Enter a directory-specific password to access the gallery. No account creation. | + +#### Operator (you) + +| Feature | Detail | +|---|---| +| **Create directory + password** | Create a new photo directory (one per trip/event) and assign it a password. This is the only admin action needed before sharing the link with friends. | +| **Tear down** | When collection is done, stop the service or remove the route. Archive the photos directory to cold storage. | + +### Key design points + +- **One directory per event** — each trip/event gets its own directory and its own password. No shared state between directories. +- **Caddy terminates TLS** — every approach sits behind Caddy for HTTPS + domain. +- **Access control** — password per directory, set by the operator at creation time. Friends enter the password once to access upload/browse/download. +- **Ephemeral lifecycle** — spin up before sharing the link, tear down after collection is done. The photos directory persists. +- **Compressed previews** — the gallery view serves resized/compressed versions of photos for fast browsing. Original full-resolution files are preserved on disk and served in the zip download. + +--- + +## 4. Alternatives + +### A. Caddy `file_server` + `upload` module (pure Caddy) + +Use Caddy's built-in `file_server` for browsing/downloading, combined with a community Caddy upload handler plugin ([caddyserver/caddy-upload](https://github.com/caddyserver/caddy-upload) or similar). + +| Aspect | Detail | +|---|---| +| **How it works** | Caddy serves the directory with `browse`. Uploads go through a Caddy handler that accepts `PUT`/`POST` and writes to disk. A minimal HTML page with a `<form>` or JS fetch provides the upload UI. | +| **Dependencies** | Caddy + one plugin (or a custom build via `xcaddy`). | +| **Upload UX** | Requires a small static HTML page with a drag-and-drop or file picker form. No server-side code beyond Caddy. | +| **Download UX** | Caddy's built-in browse UI lists files; click to download individually. Bulk download would need an external script or zip-on-the-fly. | + +**Pros:** +- Zero extra processes — it's just Caddy. +- Already in your stack; nothing new to learn operationally. +- TLS, basic auth, rate-limiting all native. + +**Cons:** +- Upload plugin maturity varies; may need `xcaddy build` to include it. +- Browse UI is functional but bare — no thumbnails, no gallery feel. +- No bulk zip download out of the box. +- Multi-file upload from a phone browser can be clunky without a custom HTML page. + +--- + +### B. Minimal Go application (custom binary) + +A small, purpose-built Go HTTP server: one binary, no dependencies, serves an HTML page with upload form + file listing. + +| Aspect | Detail | +|---|---| +| **How it works** | Go binary listens on a local port. Caddy reverse-proxies to it. Handles `POST /upload` (multipart form), serves `GET /` with a gallery page listing files, and `GET /photos/*` to serve files. | +| **Dependencies** | Go binary (~5-10 MB). No runtime deps, no database. | +| **Upload UX** | Full control — can build a drag-and-drop multi-file UI, show progress bars, rename duplicates. | +| **Download UX** | Can serve individual files and generate a zip of all photos on-the-fly for bulk download. Thumbnails possible via Go's image stdlib. | + +**Pros:** +- Total control over UX — can be exactly what you want. +- Single static binary, trivially deployable. Copy & run. +- Zip download, thumbnail generation, progress feedback — all easy in Go. +- No plugin ecosystem dependency; you own the code. + +**Cons:** +- You have to write and maintain it (even if small, ~200-400 lines). +- Another process to manage alongside Caddy. +- Feature creep risk — temptation to add more and more. + +--- + +### C. WebDAV via Caddy + Caddy browse + +Caddy has a `webdav` plugin. WebDAV is HTTP-native file access — upload, download, directory listing — and modern browsers/OS file managers support it. + +| Aspect | Detail | +|---|---| +| **How it works** | Caddy serves a WebDAV endpoint for the photos directory. A static HTML page provides a browser-based upload form. File listing served via `file_server browse` on a separate route. | +| **Dependencies** | Caddy + `webdav` plugin (`xcaddy build`). | +| **Upload UX** | Friends can use the HTML form in browser, or mount the WebDAV share on their OS and drag files in. | +| **Download UX** | Browse + click to download. OS-level mount allows bulk copy. | + +**Pros:** +- Standard protocol — works with native OS file managers (macOS Finder, Windows Explorer, Linux Nautilus). +- Upload and download in one protocol; no custom code. +- Caddy ecosystem, single process. + +**Cons:** +- WebDAV in browsers is inconsistent — you still need a static HTML upload form for reliable mobile UX. +- Plugin needed (`xcaddy build`). +- The "mount a network drive" step is too technical for most friends. +- No gallery view, no thumbnails. + +--- + +### D. FTP server with web frontend + +Run a lightweight FTP server (e.g., `vsftpd`, or a Go-based one like `ftpgrab/ftpserver`) and pair it with a web file manager frontend. + +| Aspect | Detail | +|---|---| +| **How it works** | FTP server exposes the photos directory. A web-based FTP client (e.g., Monsta FTP, or a minimal custom page) served by Caddy provides the browser UI. | +| **Dependencies** | FTP daemon + web frontend + Caddy. | +| **Upload UX** | Through the web frontend's upload widget. | +| **Download UX** | Through the web frontend's file listing. | + +**Pros:** +- FTP is battle-tested for file transfer. +- Some web FTP clients provide decent UIs. + +**Cons:** +- **Heaviest approach** — three components (FTP server, web UI, Caddy). +- FTP is a legacy protocol with known security quirks (passive ports, plaintext by default). FTPS adds complexity. +- Web FTP clients are often clunky, unmaintained, or PHP-based. +- Overkill for the problem. Friends don't need FTP; they need a web page. +- Firewall/NAT unfriendly compared to pure HTTP. + +--- + +### E. Existing lightweight gallery software + +Deploy an existing open-source photo gallery (e.g., **PiGallery2**, **Photoview**, **Lychee**, **Photoprism**). + +| Aspect | Detail | +|---|---| +| **How it works** | Docker container or binary, reverse-proxied by Caddy. Provides web UI for upload, gallery view with thumbnails, download. | +| **Dependencies** | Varies — some need a database (SQLite at minimum), Node.js, or Docker. | +| **Upload UX** | Usually polished with drag-and-drop. | +| **Download UX** | Gallery browsing with thumbnails, album zip downloads. | + +**Pros:** +- Best UX out of the box — thumbnail grids, slideshows, EXIF data. +- Actively maintained projects with communities. +- Handles edge cases (duplicate detection, format support). + +**Cons:** +- **Not lightweight** — most pull in a database, runtime, or Docker image. +- Opinionated features you don't need (user management, albums, face detection). +- Heavier operational footprint — updates, config, database migrations. +- Misaligned: these are permanent gallery tools, not ephemeral upload-and-collect tools. + +--- + +### Comparison Summary + +| Criterion | A. Pure Caddy | B. Go App | C. WebDAV | D. FTP + Web | E. Existing Gallery | +|---|:---:|:---:|:---:|:---:|:---:| +| Lightweight | ★★★★★ | ★★★★ | ★★★★ | ★★ | ★★ | +| Browser UX | ★★ | ★★★★★ | ★★★ | ★★ | ★★★★★ | +| Upload ease (mobile) | ★★★ | ★★★★★ | ★★★ | ★★ | ★★★★ | +| Bulk download | ★★ | ★★★★★ | ★★★ | ★★★ | ★★★★ | +| Ops simplicity | ★★★★★ | ★★★★ | ★★★★ | ★★ | ★★ | +| Caddy integration | ★★★★★ | ★★★★ | ★★★★ | ★★★ | ★★★ | +| Nothing to write | ★★★★ | ★★ | ★★★★ | ★★★★ | ★★★★★ | +| Time to working MVP | ★★★★ | ★★★ | ★★★ | ★★ | ★★★★ | + +--- + +## 5. Open Questions (Resolved) + +1. **Access control model** — Secret/unguessable URL path or basic auth. No accounts. +2. **Photo volume** — ~10 people, ~200 photos total. Worst case ~4 GB (200 × 20 MB). Zip-on-the-fly is feasible at this scale. +3. **Retention** — Ephemeral. Spin up for the collection window, then archive photos to personal storage and tear down. +4. **Duplicate handling** — None. Keep everything as-is. +5. **Upload size limits** — 20 MB per file as a safety cap. +6. **Multi-trip reuse** — One directory per trip. Either clean the folder between trips or configure a new folder path. The Go binary takes the photo directory as a flag (e.g., `--dir ./crete-2026`). + +--- + +## 6. Parties Involved + +| Role | Who | +|---|---| +| **Author** | You (server owner/operator) | +| **Users** | Friends (non-technical, browser-only) | +| **Operator** | You — deployment, maintenance, teardown | + +--- + +## 7. Cross-Concerns + +### Security +- **TLS mandatory** — Caddy handles this automatically with HTTPS. +- **Access control** — at minimum, an unguessable URL path (e.g., `/holiday-crete-2026-a8f3b2c1/`). Optionally, Caddy `basicauth` for a shared password. +- **Upload validation** — accept only image MIME types; reject executables. Enforce a max file size. +- **Path traversal** — the application or Caddy handler must sanitize filenames to prevent `../../etc/passwd` style writes. + +### Privacy +- Photos are personal. No analytics, no third-party scripts, no CDN. +- Server-side: photos stored as flat files with no metadata indexing unless explicitly built. +- After collection, the upload endpoint should be disabled (remove the route or stop the service). + +### Observability +- Caddy access logs are sufficient. No need for metrics, APM, or dashboards for this scale. +- Optional: log upload events (filename, size, timestamp) to a simple text file for your own tracking. |
