From ddebd50d3fd3750f97f13a07afb38447601e3889 Mon Sep 17 00:00:00 2001 From: Luca <102960244+Luca-Timo@users.noreply.github.com> Date: Mon, 10 Aug 2026 09:16:13 +0300 Subject: [PATCH] docs: slim README to a lean router, stage deep content for docs-site migration (#1001) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 of the README slim / docs-migration plan in #1000. README goes from 577 to ~191 lines: hero, one Quick Start, a Documentation index, comparison table, tech stack and a table of contents. The deep inline prose moves into a temporary docs/_to-migrate/ staging folder (webhooks, storage backends, first-run setup, system requirements, roadmap) so README links keep resolving until the docs-site pages are live. Existing docs/*.md referenced by app code are deliberately left in place โ€” crm-disclaimers.md (frontend TSX, i18n, a backend route and migration), fonts.md (server.js), accounting-inbound-invoices.md (Dockerfile) and migration-to-org.md (UpdateNotification.tsx, MigrationBanner.tsx). Moving them is a separate, code-touching change. Verified before merge: merges cleanly against main with no conflicts; all 14 in-repo links resolve in the merged tree; no docs file is deleted or renamed; and the registry-move notice from #995 survives the rewrite in condensed form, keeping 'still responds but its tags are frozen at 2026-05-27' plus the migration-to-org.md link. The fuller symptom explanation remains in that doc, which the README links to. Follow-up per #1000: port docs/_to-migrate/* into docs.picpeak.app, then flip the README links and delete the staging folder. Co-authored-by: Luca-Timo --- README.md | 566 +++++------------------- docs/_to-migrate/README.md | 22 + docs/_to-migrate/first-run-setup.md | 26 ++ docs/_to-migrate/roadmap.md | 28 ++ docs/_to-migrate/storage-backends.md | 22 + docs/_to-migrate/system-requirements.md | 63 +++ docs/_to-migrate/webhooks.md | 77 ++++ 7 files changed, 339 insertions(+), 465 deletions(-) create mode 100644 docs/_to-migrate/README.md create mode 100644 docs/_to-migrate/first-run-setup.md create mode 100644 docs/_to-migrate/roadmap.md create mode 100644 docs/_to-migrate/storage-backends.md create mode 100644 docs/_to-migrate/system-requirements.md create mode 100644 docs/_to-migrate/webhooks.md diff --git a/README.md b/README.md index 91a29557..76aae2e6 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,48 @@ -# ๐Ÿ“ธ PicPeak - Open Source Photo Sharing for Events - -> [!IMPORTANT] -> **PicPeak has moved to its own GitHub organization.** -> -> - **Docker images** are now published at `ghcr.io/picpeak/picpeak/{backend,frontend}`. Update your `docker-compose.yml`. -> - โš ๏ธ The old path (`ghcr.io/the-luap/picpeak/...`) **still responds, but its tags are frozen** at 2026-05-27. `docker compose pull` succeeds and hands back the same build every time, so an out-of-date install looks like a broken download rather than a dead path. If PicPeak keeps reporting an update that never arrives, check your image path first. -> - **Branches**: active development is now on `main` (was `beta`); the curated stable channel is now `stable` (was `main`). Existing PRs and clones auto-redirect via GitHub. -> -> See **[`docs/migration-to-org.md`](docs/migration-to-org.md)** for the one-line `docker-compose.yml` edit and full details. -
PicPeak Logo - + + # ๐Ÿ“ธ PicPeak + + **Open-source, self-hosted photo sharing for events.** + [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=flat&logo=docker&logoColor=white)](https://www.docker.com/) - [![Node.js](https://img.shields.io/badge/node.js-6DA55F?style=flat&logo=node.js&logoColor=white)](https://nodejs.org/) - [![React](https://img.shields.io/badge/react-%2320232a.svg?style=flat&logo=react&logoColor=%2361DAFB)](https://reactjs.org/) [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-theluap-FFDD00?logo=buymeacoffee&logoColor=black)](https://buymeacoffee.com/theluap) - [Homepage](https://www.picpeak.app) ยท [Live Demo](https://demo.picpeak.app) ยท [Documentation](https://docs.picpeak.app) ยท [Support the project โ˜•](https://buymeacoffee.com/theluap) + [Homepage](https://www.picpeak.app) ยท [Live Demo](https://demo.picpeak.app) ยท [Documentation](https://docs.picpeak.app) ยท [Support โ˜•](https://buymeacoffee.com/theluap)
-**PicPeak** is a powerful, self-hosted open-source alternative to commercial photo-sharing platforms like PicDrop.com and Scrapbook.de. Designed specifically for photographers and event organizers, PicPeak makes it simple to share beautiful, time-limited photo galleries with clients while maintaining full control over your data and branding. +--- + +**PicPeak** is a powerful, self-hosted open-source alternative to commercial photo-sharing platforms like PicDrop.com and Scrapbook.de. Built for photographers and event organizers, it makes it simple to share beautiful, time-limited photo galleries with clients while keeping full control over your data and branding. ![PicPeak Gallery Preview](docs/screenshot-gallery.png) +> [!IMPORTANT] +> **PicPeak has moved to its own GitHub organization.** Docker images are now at `ghcr.io/picpeak/picpeak/{backend,frontend}` and active development is on `main`. The old `ghcr.io/the-luap/...` path still responds but its tags are **frozen** at 2026-05-27 โ€” if updates never arrive, check your image path first. See **[`docs/migration-to-org.md`](docs/migration-to-org.md)** for the one-line `docker-compose.yml` edit. + +## Contents + +- [Live Demo](#-live-demo) +- [Quick Start](#-quick-start) +- [Why PicPeak?](#-why-picpeak) +- [Features](#-features) +- [Documentation](#-documentation) +- [Comparison](#-comparison-with-alternatives) +- [Tech Stack](#๏ธ-tech-stack) +- [Contributing & Support](#-contributing) +- [License](#-license) + ## ๐ŸŽฎ Live Demo -Try PicPeak without installing anything: +Try PicPeak without installing anything โ€” [demo.picpeak.app](https://demo.picpeak.app) ยท [admin panel](https://demo.picpeak.app/admin) -| | | +| Email | Password | |---|---| -| **Demo URL** | [demo.picpeak.app](https://demo.picpeak.app) | -| **Admin Panel** | [demo.picpeak.app/admin](https://demo.picpeak.app/admin) | -| **Email** | `demo@picpeak.app` | -| **Password** | `Demo2026!` | +| `demo@picpeak.app` | `Demo2026!` | > The demo resets periodically. Uploaded content may be removed without notice. -## ๐ŸŒŸ Why Choose PicPeak? - -Unlike expensive SaaS solutions, PicPeak gives you: - -- **๐Ÿ’ฐ No Monthly Fees** - One-time setup, unlimited galleries -- **๐Ÿ”’ Complete Data Control** - Your photos stay on your server -- **๐ŸŽจ White-Label Ready** - Full branding customization -- **๐Ÿ“ฑ Mobile-First Design** - Beautiful on all devices -- **๐Ÿš€ Lightning Fast** - Optimized performance and caching -- **๐ŸŒ Multi-Language** - Built-in i18n support (EN, DE) - -## โœจ Key Features - -### For Photographers -- ๐Ÿ“ **Drag & Drop Upload** - Simply drop photos into folders -- ๐Ÿ”— **External Media (Reference Mode)** - Browse and import from a readโ€‘only external folder library without copying originals -- โฐ **Auto-Expiring Galleries** - Set expiration dates (default: 30 days) -- ๐Ÿ” **Password Protection** - Secure client galleries -- ๐Ÿ“ง **Automated Emails** - Creation confirmations and expiration warnings -- ๐Ÿ“Š **Analytics Dashboard** - Track views, downloads, and engagement -- ๐Ÿ“ฝ๏ธ **Live Slideshow** - A separate fullscreen "Diashow" link per event for projectors at live events โ€” auto-picks-up new uploads while it runs, with transitions, a logo watermark, and image-fit/colour options ([guide](docs/live-slideshow.md)) -- ๐ŸŽจ **Custom Themes** - Match your brand perfectly -- ๐ŸŒ **Public Landing Page** - Publish a curated marketing page when guests visit your root URL - -### For Clients -- ๐Ÿ–ผ๏ธ **Beautiful Galleries** - Clean, modern interface -- ๐Ÿ“ฑ **Mobile Optimized** - Swipe through photos on any device -- โฌ‡๏ธ **Bulk Downloads** - Download all photos with one click -- ๐Ÿ” **Smart Search** - Find photos quickly -- ๐Ÿ“ค **Guest Uploads** - Optional client photo uploads -- ๐Ÿ›ก๏ธ **Download Protection** - Advanced image protection with watermarking and right-click prevention - -### Technical Excellence -- ๐Ÿณ **Docker Ready** - Deploy in minutes -- ๐Ÿ”„ **Auto-Processing** - Automatic thumbnail generation -- ๐Ÿ—‚๏ธ **Reference Library Support** - Point PicPeak at `EXTERNAL_MEDIA_ROOT` to reference existing originals, index quickly, and generate thumbnails on demand -- ๐Ÿ’พ **Smart Storage** - Automatic archiving of expired galleries -- ๐Ÿ›ก๏ธ **Security First** - JWT auth, rate limiting, CORS protection -- ๐Ÿ“ˆ **Scalable** - From small studios to large agencies - -### For Studios โ€” CRM & Accounting (Beta ยท off by default) -- ๐Ÿ“ **Quotes โ†’ Contracts โ†’ Invoices** - One deal lineage; cancel-and-reissue (Storno) keeps issued invoices immutable -- โฑ๏ธ **Hours Logging & Calendar** - Per-customer time tracking; admin calendar of events, logged hours, and pending quotes/contracts -- ๐Ÿงพ **Inbound Supplier Invoices & Expenses** - Capture received invoices (upload/camera, rasterised server-side), categorise, and re-bill costs to clients -- ๐Ÿ“Š **Tax Report & Accountant Export** - Period-scoped income/cost report with VAT breakdown; PDF/CSV plus a Treuhรคnder/Banana (Swiss/LI) journal export, scopable to income-only or cost-only -- ๐ŸŒ **VAT & Multi-currency** - Single VAT-code registry snapshotted onto each document; data-driven per-country rates -- โš ๏ธ **Verify locally** - Feature-flagged off by default. Seeded contracts, QR/IBAN and tax defaults are **examples only** โ€” review your own legal **and tax** regulations first (see disclaimers below) - ## ๐Ÿš€ Quick Start Get PicPeak running in under 5 minutes: @@ -97,8 +54,8 @@ cd picpeak # Copy the environment template โ€” the defaults work out of the box. # Machine secrets (JWT, DB, Redis) are auto-generated on first run, and the -# admin account is created in the browser (see below). Edit .env only to -# customise (domain, SMTP, storage paths, โ€ฆ) โ€” nothing is required. +# admin account is created in the browser. Edit .env only to customise +# (domain, SMTP, storage paths, โ€ฆ) โ€” nothing is required. cp .env.example .env # Start with Docker Compose @@ -107,293 +64,61 @@ docker compose up -d # Access at http://localhost:3000 ``` -### First run โ€” create your admin account +On first start, open **http://localhost:3000/admin** and follow the in-browser setup to create your admin account. Full details โ€” the one-time setup token, Docker file permissions, and ARM64 notes โ€” are in **[First-run setup](docs/_to-migrate/first-run-setup.md)**. -On first start with no `ADMIN_PASSWORD` set, PicPeak has **no admin account yet** and greets you with an in-browser setup screen โ€” no credentials in `.env`: +> **Updating / release channels:** set `PICPEAK_CHANNEL` (`stable` default, or `beta`) in `.env`, then `docker compose pull && docker compose up -d`. See [RELEASING.md](RELEASING.md) for the promotion cadence. -1. Open **http://localhost:3000/admin** โ€” you'll be redirected to `/setup`. -2. Read the **one-time setup token** from the 0600 file the backend writes it to - (it is deliberately *not* printed to the logs โ€” that would leave a live - bootstrap credential in `docker logs`): - ```bash - docker compose exec backend cat /app/data/SETUP_TOKEN - ``` - It is bind-mounted, so `sudo cat data/SETUP_TOKEN` on the host works too. Only - if that file could not be written does the backend fall back to logging the - token (`docker compose logs backend | grep -i "setup token"`). -3. Paste the token, set your admin **email + password**, and you're in. The token is single-use, and the setup screen closes permanently once an admin exists. +## ๐ŸŒŸ Why PicPeak? -> Prefer the old behaviour? Set `ADMIN_PASSWORD` in `.env` and PicPeak auto-creates the admin on first boot instead (credentials written to `data/ADMIN_CREDENTIALS.txt`). +Unlike expensive SaaS solutions, PicPeak gives you: -Note on Docker file permissions -- The backend container starts as root, chowns bind-mounted host directories (`./storage`, `./data`, `./logs`) to UID 1001 (`nodejs`), then drops privileges via `su-exec` before running the app. No host-side setup needed for fresh installs. -- If you pin `user:` in a compose override (e.g. to map a specific host UID), the self-chown is skipped and you must pre-chown the host directories to that UID โ€” see [docs.picpeak.app/deployment/docker#permissions](https://docs.picpeak.app/deployment/docker#permissions). +- **๐Ÿ’ฐ No Monthly Fees** โ€” one-time setup, unlimited galleries +- **๐Ÿ”’ Complete Data Control** โ€” your photos stay on your server +- **๐ŸŽจ White-Label Ready** โ€” full branding customization +- **๐Ÿ“ฑ Mobile-First Design** โ€” beautiful on all devices +- **๐ŸŒ Multi-Language** โ€” built-in i18n (EN, DE) -**ARM64 (aarch64) systems:** Pre-built images include native `linux/arm64`, no platform flags or emulation needed. If you're on an older image tag that's still amd64-only, see [docker-compose.amd64.override.yml](docker-compose.amd64.override.yml) for a transitional fallback. +## โœจ Features -## ๐Ÿ”„ Release Channels +**For photographers** โ€” drag & drop upload, auto-expiring & password-protected galleries, automated emails, an analytics dashboard, custom themes, a public landing page, and a [Live Slideshow](docs/live-slideshow.md) projector view that auto-picks-up new uploads during live events. -PicPeak offers two release channels for different needs. Stable promotions are cut from a known-good beta point every 4โ€“6 weeks โ€” see [RELEASING.md](RELEASING.md) for the maintainer's promotion criteria and cadence policy. +**For clients** โ€” clean mobile-optimized galleries, one-click bulk downloads, smart search, optional guest uploads, and download protection (watermarking + right-click prevention). -### Stable Channel (Recommended) -- Production-ready releases -- Thoroughly tested before release -- Docker tags: `stable`, `latest`, or specific version like `v2.3.0` +**Technical** โ€” Docker-ready, automatic thumbnail generation, external media reference mode, smart archiving of expired galleries, S3-compatible [storage backends](docs/_to-migrate/storage-backends.md), [webhooks](docs/_to-migrate/webhooks.md), and security-first defaults (JWT, rate limiting, CORS). -### Beta Channel -- Early access to new features -- May contain bugs or incomplete functionality -- Docker tags: `beta` or specific version like `v2.3.0-beta.1` +
+๐Ÿงพ For studios โ€” CRM & Accounting (Beta, off by default) -### Switching Channels +- ๐Ÿ“ **Quotes โ†’ Contracts โ†’ Invoices** โ€” one deal lineage; cancel-and-reissue (Storno) keeps issued invoices immutable +- โฑ๏ธ **Hours Logging & Calendar** โ€” per-customer time tracking; admin calendar of events, logged hours, and pending quotes/contracts +- ๐Ÿงพ **Inbound Supplier Invoices & Expenses** โ€” capture received invoices (upload/camera, rasterised server-side), categorise, and re-bill costs to clients +- ๐Ÿ“Š **Tax Report & Accountant Export** โ€” period-scoped income/cost report with VAT breakdown; PDF/CSV plus a Treuhรคnder/Banana (Swiss/LI) journal export +- ๐ŸŒ **VAT & Multi-currency** โ€” single VAT-code registry snapshotted onto each document -Set the `PICPEAK_CHANNEL` environment variable in your `.env` file: +
-```bash -# For stable releases (default) -PICPEAK_CHANNEL=stable - -# For beta releases -PICPEAK_CHANNEL=beta - -# For a specific version -PICPEAK_CHANNEL=v2.3.0 -``` - -Then update your containers: - -```bash -docker compose -f docker-compose.production.yml pull -docker compose -f docker-compose.production.yml up -d -``` - -### Update Notifications - -The admin dashboard automatically notifies you when updates are available for your channel. To disable update checks, set: - -```bash -UPDATE_CHECK_ENABLED=false -``` +> [!WARNING] +> **CRM & Accounting โ€” examples only, verify locally.** Feature-flagged off by default. Seeded contract blocks are written by the maintainer, **not a lawyer**; QR-bills/SEPA payloads and every tax, VAT and Treuhรคnder/Banana figure are computed from your input and defaults and are **jurisdiction-specific guidance only**. Have your lawyer review contracts, scan a test QR with your bank's app, and verify all numbers with your accountant / Treuhรคnder / tax authority before customer-facing use. Read **[docs/crm-disclaimers.md](docs/crm-disclaimers.md)** first. ## ๐Ÿ“– Documentation -Full documentation lives at **[docs.picpeak.app](https://docs.picpeak.app)** โ€” deployment, admin settings reference, API docs, webhooks, archive lifecycle, branding, and everything else. Some quick links: +Full documentation lives at **[docs.picpeak.app](https://docs.picpeak.app)** โ€” deployment, admin settings, API, branding, and more. -- ๐Ÿš€ [**Deployment**](https://docs.picpeak.app/deployment) - Docker, environment variables, reverse proxy, SSL -- โš™๏ธ [**Admin Settings**](https://docs.picpeak.app/guides/admin-settings) - Every tab in the Settings panel -- ๐ŸŽฏ [**Creating Events**](https://docs.picpeak.app/guides/creating-events) - Full event field reference -- ๐Ÿ“ฝ๏ธ [**Live Slideshow**](https://docs.picpeak.app/features/live-slideshow) - Fullscreen projector view that auto-updates during live events -- ๐Ÿ’พ [**Backup & Restore**](https://docs.picpeak.app/guides/backup-restore) - Backup configuration, restore wizard, full disaster recovery -- ๐Ÿ”Œ [**API Reference**](https://docs.picpeak.app/api) - REST endpoints, OpenAPI spec, webhooks -- ๐Ÿช [**Webhooks**](https://docs.picpeak.app/features/webhooks) - Event payloads, signing, filters, templates - -Project meta: - -- ๐Ÿค [**Contributing**](CONTRIBUTING.md) - How to contribute -- ๐Ÿ“œ [**License**](LICENSE) - MIT License -- ๐Ÿ”’ [**Security**](SECURITY.md) - Security policies -- ๐Ÿ“‹ [**Code of Conduct**](CODE_OF_CONDUCT.md) - Community guidelines - -## ๐ŸŒ Public Landing Page - -Spotlight your studio with a customizable marketing page at `/`: - -- Head to **Admin โ†’ CMS Pages** to enable the public landing page toggle. -- Edit the provided HTML template (rich sections, hero, testimonials) and optional CSS overrides. -- The preview renders in a sandboxed iframe so you can iterate safely before publishing. -- PicPeak sanitizes stored HTML and CSS server-sideโ€”scripts, iframes, and unsafe attributes are stripped automatically. -- Use **Reset to default** anytime to restore the bundled template. -- The backend caches the rendered landing page for 60 seconds by default; override with `PUBLIC_SITE_CACHE_TTL_MS` if you need a different TTL. -- When the landing page is disabled PicPeak continues to serve the admin SPA/login exactly as before. - -## ๐ŸŽฏ Use Cases - -Perfect for: -- ๐Ÿ’’ **Wedding Photographers** - Share ceremony photos securely -- ๐ŸŽ‚ **Event Photography** - Birthday parties, corporate events -- ๐Ÿ“ธ **Portrait Studios** - Client galleries with download limits -- ๐Ÿข **Corporate Events** - Internal photo sharing with branding -- ๐ŸŽ“ **School Photography** - Secure parent access with expiration -- ๐Ÿ“ฝ๏ธ **Live Events** - Put a [Live Slideshow](docs/live-slideshow.md) on the venue projector that updates as you shoot - -## ๐Ÿ—๏ธ Tech Stack - -- **Backend**: Node.js, Express, SQLite/PostgreSQL -- **Frontend**: React, Tailwind CSS, Framer Motion -- **Storage**: Local filesystem (default) or S3-compatible object store (AWS S3, MinIO, R2, B2, Wasabi, Spaces) โ€” see [Storage Backends](#storage-backends) -- **Email**: SMTP with customizable templates -- **Analytics**: Privacy-focused with Umami integration - -## ๐Ÿ’พ Storage Backends - -PicPeak supports two storage backends for photos, thumbnails, hero images, watermarks, and archive zips. Both are configured via environment variables; no code change is required to switch. - -| Capability | `STORAGE_BACKEND=local` (default) | `STORAGE_BACKEND=s3` | -|---|---|---| -| Photo / thumbnail / hero storage | Local filesystem under `STORAGE_PATH` | Bucket on any S3-compatible service | -| Admin UI upload | โœ… | โœ… | -| Filesystem auto-import (chokidar watcher) | โœ… | โŒ โ€” disabled (use the upload API) | -| Watermarks, fingerprinting, fragmentation | โœ… | โœ… (materialized to a tmp file just-in-time) | -| Bulk download zips (cached + on-the-fly) | โœ… | โœ… | -| Backups | โœ… | โœ… | -| External media reference mode (`EXTERNAL_MEDIA_ROOT`) | โœ… (always local) | โœ… (still local โ€” not migrated) | - -### Switching to an S3-compatible backend - -1. Provision a bucket and credentials. The minimum IAM policy is documented in `.env.example`. -2. Set `STORAGE_BACKEND=s3` plus `STORAGE_S3_BUCKET`, `STORAGE_S3_REGION`, `STORAGE_S3_ACCESS_KEY`, `STORAGE_S3_SECRET_KEY`. For non-AWS providers (MinIO, R2, B2, โ€ฆ) also set `STORAGE_S3_ENDPOINT`. -3. If you have existing local content, copy it first: `node backend/scripts/migrate-storage.js --dry-run` then `node backend/scripts/migrate-storage.js`. The script is idempotent and writes a failures CSV. -4. Restart the backend. The startup check pings the bucket and refuses to boot on misconfig. - -Note: presigned-URL serving (zero-bandwidth direct downloads from S3) is intentionally **not** in v1 โ€” every request still streams through the backend so watermarks, devtools-detection, and access logging keep working. - -## ๐Ÿ”” Webhooks - -PicPeak POSTs event/photo lifecycle notifications to URLs you configure under **Settings โ†’ Webhooks**. Each delivery is signed `HMAC-SHA256` with a per-webhook secret in the `X-PicPeak-Signature` header so receivers can verify the request really came from your PicPeak instance. - -### Event types - -| Event | Fires when | +| Topic | Link | |---|---| -| `event.created` | Gallery created (admin or API) | -| `event.published` | Draft becomes live (`is_draft: true โ†’ false`) โ€” also fires when an event is created with `is_draft=false` | -| `event.archived` | Bulk-archive, manual archive, or auto-archive on expiry | -| `event.expired` | Expiration checker marks the gallery inactive (fires before `event.archived` in the cascade) | -| `photo.uploaded` | Admin upload, API upload, guest upload, or auto-import | -| `photo.deleted` | Single delete, bulk delete (NOT fired per-photo when an event is archived โ€” receivers infer from `event.archived` to avoid flooding) | +| ๐Ÿš€ Deployment (Docker, env, reverse proxy, SSL) | [docs.picpeak.app/deployment](https://docs.picpeak.app/deployment) | +| โš™๏ธ Admin settings reference | [docs.picpeak.app/guides/admin-settings](https://docs.picpeak.app/guides/admin-settings) | +| ๐ŸŽฏ Creating events | [docs.picpeak.app/guides/creating-events](https://docs.picpeak.app/guides/creating-events) | +| ๐Ÿ“ฝ๏ธ Live Slideshow | [docs/live-slideshow.md](docs/live-slideshow.md) | +| ๐Ÿ’พ Backup & Restore | [docs/backup-restore.md](docs/backup-restore.md) | +| ๐Ÿ”Œ API reference | [docs.picpeak.app/api](https://docs.picpeak.app/api) | +| ๐Ÿช Webhooks | [docs/_to-migrate/webhooks.md](docs/_to-migrate/webhooks.md) | +| ๐Ÿ’พ Storage backends (local / S3) | [docs/_to-migrate/storage-backends.md](docs/_to-migrate/storage-backends.md) | +| ๐Ÿ’ป System requirements & tuning | [docs/_to-migrate/system-requirements.md](docs/_to-migrate/system-requirements.md) | +| ๐Ÿงพ CRM & Accounting | [docs.picpeak.app/features/crm](https://docs.picpeak.app/features/crm) ยท [disclaimers](docs/crm-disclaimers.md) | +| ๐Ÿ—บ๏ธ Roadmap | [docs/_to-migrate/roadmap.md](docs/_to-migrate/roadmap.md) | -### Payload shape - -```json -{ - "id": "delivery-uuid", - "type": "event.published", - "created_at": "2026-04-28T05:25:00.000Z", - "data": { - "event": { "id": 123, "slug": "wedding-smith", "share_url": "https://..." } - } -} -``` - -Also sent on every request: -- `X-PicPeak-Signature` โ€” `HMAC-SHA256(secret, raw_body)` as hex -- `X-PicPeak-Event` โ€” the event type (handy for routing without parsing the body) -- `X-PicPeak-Delivery` โ€” UUID for idempotency on the receiver side -- `User-Agent: PicPeak-Webhooks/1.0` - -### Verifying signatures - -**Node.js** -```js -const crypto = require('crypto'); -function verify(secret, rawBody, signature) { - const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); - const a = Buffer.from(expected, 'hex'); - const b = Buffer.from(signature, 'hex'); - if (a.length !== b.length) return false; - return crypto.timingSafeEqual(a, b); -} -``` - -**Python** -```python -import hmac, hashlib -def verify(secret: str, raw_body: bytes, signature: str) -> bool: - expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() - return hmac.compare_digest(expected, signature) -``` - -**curl + openssl** (one-liner for a quick replay) -```sh -SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}') -[ "$SIG" = "$RECEIVED_SIG" ] && echo OK || echo MISMATCH -``` - -### Retries + observability - -- `2xx` โ†’ success, recorded with latency -- Non-`2xx` or network error โ†’ exponential backoff: `1m โ†’ 5m โ†’ 30m โ†’ 2h โ†’ 12h`, max 5 attempts -- After max attempts: status `failed`, surfaces in **Settings โ†’ Webhooks โ†’ Deliveries** with a "Replay" button -- Up to 5 deliveries in flight at once; one slow consumer can't block others (configurable via `WEBHOOK_DELIVERY_CONCURRENCY`) -- Response body truncated to 1KB before storage so chatty receivers don't bloat the audit log - -The deliveries page (`/admin/webhooks/:id/deliveries`) shows every attempt with timestamp, status, HTTP code, latency, payload sent, signature, and response. Click "Send test event" to fire a synthetic delivery for any event type. - -### SSRF protection - -Webhook URLs are validated against the same private-IP blocklist used elsewhere in the app โ€” loopback, private RFC1918 ranges, link-local, `.local`/`.internal` hostnames, cloud metadata endpoints. The check runs both at create time and per-delivery (DNS-rebinding mitigation). - -For local development with a receiver on the same machine or docker network, set `WEBHOOK_ALLOW_PRIVATE_URLS=true`. Production deployments must leave this OFF. - -## ๐Ÿ’ป System Requirements - -### Minimum Requirements -- **CPU**: 2 CPU cores -- **RAM**: **4 GB minimum** for a normal photo-upload workload โ€” sharp/libvips - decodes the full uncompressed frame before resize, and the default two - worker loops at sharp-concurrency 2 can push peak RSS past 1.5 GB on a - batch of 20-MP+ photos. On a 2 GB VPS that's enough to OOM-kill the - backend mid-batch (surfaces as 503s on thumbnails โ€” see [Low-memory - hosts](#low-memory-hosts) below for the recipe to run on 2 GB). -- **Storage**: 20GB minimum (plus photo storage needs) -- **OS**: Linux (Ubuntu 20.04+), macOS, or Windows with WSL2 -- **Node.js**: v18.0.0 or higher -- **Database**: SQLite (included) or PostgreSQL 12+ - -### Docker Requirements (Recommended) -- **Docker**: v20.10.0+ -- **Docker Compose**: v2.0.0+ - -### Low-memory hosts - -Running on 2 GB RAM (e.g. an entry-level VPS) is workable but requires -tuning the upload-processor concurrency down. The backend auto-detects -total RAM at startup via `os.totalmem()` โ€” on a host that reports < 3 GB, -it defaults `UPLOAD_PROCESSOR_CONCURRENCY` to **1** instead of 2 and logs -a one-shot warning. You can pin the value explicitly in `.env`: - -```env -# Single worker loop โ€” slower batch processing, lower peak RSS -UPLOAD_PROCESSOR_CONCURRENCY=1 -``` - -The trade-off is throughput: a single worker processes one photo at a -time, so a 100-photo batch takes ~2ร— as long but won't OOM. **Health-check -note**: if the backend dies under memory pressure, the gallery serves -`503 Service Unavailable` on thumbnails until Docker's -`restart: unless-stopped` brings the container back. Persistent 503s -during/after an upload batch on a low-memory host are almost always this. - -### Video Support Requirements -When enabling video uploads, consider these additional resources: - -| Resource | Recommendation | Notes | -|----------|----------------|-------| -| **RAM** | 4GB+ recommended | FFmpeg processing requires more memory | -| **Storage** | Plan for 10-100x more | Videos are significantly larger than images | -| **CPU** | Additional cores help | Video thumbnail extraction is CPU-intensive | -| **Bandwidth** | Higher throughput | Video streaming requires more bandwidth | - -**Technical Notes:** -- FFmpeg is bundled via npm (`@ffmpeg-installer/ffmpeg`) - no system installation required -- Maximum upload size: **10GB per video file** -- Chunked upload support for files >100MB (resumable uploads) -- Supported formats: MP4, WebM, MOV, AVI -- Video thumbnails are automatically generated from the first few seconds - -**For Nginx/Reverse Proxy:** -If using Nginx, increase the client max body size: -```nginx -client_max_body_size 10G; -proxy_read_timeout 3600; -proxy_send_timeout 3600; -``` - -## ๐Ÿค Contributing - -We love contributions! PicPeak is built by photographers, for photographers. Whether you're fixing bugs, adding features, or improving documentation, your help is welcome. - -See our [Contributing Guide](CONTRIBUTING.md) for details. +**Project meta:** [Contributing](CONTRIBUTING.md) ยท [License](LICENSE) ยท [Security](SECURITY.md) ยท [Code of Conduct](CODE_OF_CONDUCT.md) ## ๐Ÿ“Š Comparison with Alternatives @@ -410,168 +135,79 @@ See our [Contributing Guide](CONTRIBUTING.md) for details. | Quotes / Contracts / Invoices | ๐Ÿงช Beta | โŒ | โŒ | โœ… | | Incoming Invoices & Accounting | ๐Ÿงช Beta | โŒ | โŒ | โŒ | -*You still bring your own server (own hardware or a VPS) and, if you want one, a domain. -**Limited only by your server storage. -***Pixieset's "unlimited" is photos only; video is capped by plan (roughly 0โ€“10 h depending on tier). -๐Ÿงช Beta = built but feature-flagged off by default (see [Beta Features](#-beta-features-use-at-your-own-risk)). +*You bring your own server and, optionally, a domain. **Limited only by your server storage. ***Pixieset's "unlimited" is photos only; video is capped by plan. ๐Ÿงช Beta = built but feature-flagged off by default. -## ๐Ÿ›ก๏ธ Security +## ๐Ÿ—๏ธ Tech Stack -PicPeak takes security seriously: -- ๐Ÿ” Password hashing with bcrypt -- ๐ŸŽซ JWT-based authentication -- ๐Ÿšฆ Rate limiting on all endpoints -- ๐Ÿ›ก๏ธ CORS protection -- ๐Ÿ“ Activity logging -- ๐Ÿ”’ Secure file access - -Found a security issue? Please open a [security issue](https://github.com/PicPeak/picpeak/issues/new?labels=security) on GitHub +- **Backend**: Node.js, Express, SQLite/PostgreSQL +- **Frontend**: React, Tailwind CSS, Framer Motion +- **Storage**: Local filesystem (default) or S3-compatible object store (AWS S3, MinIO, R2, B2, Wasabi, Spaces) โ€” see [Storage Backends](docs/_to-migrate/storage-backends.md) +- **Email**: SMTP with customizable templates +- **Analytics**: Privacy-focused with Umami integration +- **External media**: point PicPeak at `EXTERNAL_MEDIA_ROOT` to reference existing originals read-only, index quickly, and generate thumbnails on demand ## ๐Ÿ“ธ Screenshots -### ๐ŸŽ›๏ธ **Admin Dashboard** -Get a complete overview of your photo galleries, analytics, and system status. +
+Click to see the admin dashboard, analytics, and event management +### ๐ŸŽ›๏ธ Admin Dashboard PicPeak Admin Dashboard -### ๐Ÿ“Š **Analytics & Insights** -Track gallery performance, view statistics, and monitor user engagement. - +### ๐Ÿ“Š Analytics & Insights PicPeak Analytics Dashboard -### ๐Ÿ“ **Event Management** -Organize and manage your photo galleries with intuitive event management tools. - +### ๐Ÿ“ Event Management PicPeak Events Management -### โœจ **Key Interface Highlights** - -
-๐Ÿ‘† Click to see more interface details - -#### What makes PicPeak's interface special: - -- **๐ŸŽจ Clean Design**: Modern, photographer-friendly interface -- **๐Ÿ“ฑ Responsive**: Perfect on desktop, tablet, and mobile -- **โšก Fast Loading**: Optimized for quick photo browsing -- **๐Ÿ”’ Secure Access**: Password-protected galleries with expiration -- **๐Ÿ“ค Easy Uploads**: Drag & drop functionality for effortless photo management -- **๐ŸŽฏ Client-Focused**: Intuitive gallery experience for your clients -
-## ๐Ÿ—บ๏ธ Roadmap +## ๐Ÿค Contributing -We're constantly improving PicPeak and welcome contributions from our community! If you have ideas for new features or want to help implement existing ones, please open an issue or submit a pull request. Your contributions help make PicPeak better for everyone. +We love contributions! PicPeak is built by photographers, for photographers โ€” whether you're fixing bugs, adding features, or improving docs. See the [Contributing Guide](CONTRIBUTING.md) to get started. -### ๐Ÿšง Beta Features (Use at your own risk) - -These features are currently in beta testing and may have limited functionality or stability: - -| Feature | Description | Status | -|---------|-------------|--------| -| **CRM & Accounting Module** | Quotes, contracts, invoices (+ Storno), hours logging, calendar, and tax report โ€” plus inbound supplier-invoice capture, internal expenses, and a Treuhรคnder/Banana (Swiss/LI) accountant-journal export. Feature-flagged off by default. Seeded contract blocks, payment terms, IBAN / QR-bill and tax defaults are **examples only** and need legal / financial / **tax** review before customer-facing use. See [docs.picpeak.app/features/crm](https://docs.picpeak.app/features/crm). | ๐Ÿงช Beta | -| **Simple Deployment Script** | One-click deployment script for quick server setup with automated configuration and dependency installation | ๐Ÿงช Beta | - -### ๐Ÿ“‹ Future Enhancements - -| Feature | Description | Priority | Status | -|---------|-------------|----------|---------| -| **Backup & Restore** | Comprehensive backup system with S3/MinIO support, automated scheduling, and safe restore functionality | High | โœ… Implemented | -| **External Media Library (Reference Mode)** | Use an external folder library as a readโ€‘only source with import and onโ€‘demand thumbnail generation | High | โœ… Implemented | -| **Download Protection** | Advanced image protection system with canvas rendering, invisible watermarking, right-click prevention, and DevTools detection to protect photos from unauthorized downloads | High | โœ… Implemented | -| **Gallery Templates** | Multiple gallery layouts (grid, masonry, carousel, timeline, hero, mosaic) with custom CSS styling support. Includes starter templates like Apple Liquid Glass for complete visual customization | Medium | โœ… Implemented | -| **Face Recognition** | AI-powered face detection to help guests find their photos and create automatic person-based albums | Low | ๐Ÿ”„ Open | -| **Gallery Feedback** | Allow guests to like, rate, and comment on photos with admin notifications and moderation | Medium | โœ… Implemented | -| **Video Support** | Upload and display videos alongside photos in galleries with streaming support | Low | โœ… Implemented | -| **Multiple Administrators** | Support for multiple admin accounts with role-based permissions and activity tracking | Low | โœ… Implemented | -| **Filtering & Export Options** | Filter photos by likes, ratings, comments, or favorites. Search by filename. Sort by date, name, size, or rating. Export filtered selections as ZIP or generate Capture One/Lightroom-compatible file lists for professional workflows | Medium | โœ… Implemented | - -**Status Legend:** โœ… Implemented | ๐Ÿšง In Progress | ๐Ÿ”„ Open | ๐Ÿ“‹ Planned +Found a security issue? Please open a [security issue](https://github.com/PicPeak/picpeak/issues/new?labels=security). See [SECURITY.md](SECURITY.md) for the policy. ## โ˜• Support the Project -PicPeak is free, open source, and self-hostable forever. If it saves you time or replaces a paid subscription, consider buying me a coffee โ€” it directly funds the time spent on new features, bug fixes, and keeping the demo + docs running. - -

- - Buy Me A Coffee - -

- -Other ways to support without spending anything: โญ star the repo, share it with photographer friends, file good bug reports, or open a PR. +PicPeak is free, open source, and self-hostable forever. If it saves you time or replaces a paid subscription, consider [buying me a coffee](https://buymeacoffee.com/theluap) โ€” it directly funds new features, bug fixes, and keeping the demo + docs running. You can also โญ star the repo, share it, file good bug reports, or open a PR. ## ๐Ÿ™ Acknowledgments -PicPeak is inspired by the best features of commercial platforms while remaining completely open source. Special thanks to all contributors who make this project possible. +PicPeak is inspired by the best features of commercial platforms while remaining completely open source. It's developed with AI assistance, but human-tested end-to-end, security-audited, and human-reviewed for quality. ### ๐Ÿ‘ฅ Contributors A huge thank you to the people whose code, reports, and feedback have shaped PicPeak: -- [**@the-luap**](https://github.com/the-luap) โ€” creator and lead maintainer. Started the project and built PicPeak's foundation and the entire gallery experience (events, galleries, uploads, sharing, download protection, templates), plus backup & restore, analytics, system health, branding/theming, and WhatsApp notifications โ€” and the architecture every later feature builds on. -- [**@Luca-Timo**](https://github.com/Luca-Timo) โ€” native Apple Silicon multi-arch images, external-URL toggle for legal CMS pages, the lazy-loaded folder tree picker, the admin-email picker on event creation, the data-driven self-hosted webfont system, the gallery header/banner decoupling, several typed-API refactors, and the CRM + accounting suite (quotes/contracts/invoices, hours logging, calendar, tax report, inbound supplier-invoice capture, expenses, and the Treuhรคnder/Banana export). Consistently raises the bar with thoughtful PRs. -- [**@Rekoo-PS**](https://github.com/Rekoo-PS) โ€” sharp-eyed bug reporter and product feedback. Filed the issues that drove the login-loop fix, the gallery-loading skeleton work, the redirection cleanup, the mobile-lightbox overhaul, the admin-events search-counter fix, the photo-count column, and the bulk-delete workflow. Also a [BuyMeACoffee](https://buymeacoffee.com/theluap) supporter โ€” the kind of feedback loop that keeps the project useful for real deployments. +**[@the-luap](https://github.com/the-luap)** โ€” creator and lead maintainer +- Gallery foundation (events, uploads, sharing, download protection, templates) +- Backup & restore, analytics, branding/theming +- The architecture every later feature builds on + +**[@Luca-Timo](https://github.com/Luca-Timo)** +- Native Apple Silicon multi-arch images +- CRM & accounting suite (quotes/contracts/invoices) +- Hours logging & Treuhรคnder/Banana tax export +- Gallery header/banner decoupling + +**[@Rekoo-PS](https://github.com/Rekoo-PS)** โ€” bug reports & product feedback +- Login-loop fix, mobile-lightbox overhaul, bulk-delete workflow +- Also a [BuyMeACoffee](https://buymeacoffee.com/theluap) supporter If you've contributed and aren't listed here, please open a PR โ€” this list is meant to grow. -### ๐Ÿค– AI-Assisted Development - -This project was generated with the assistance of AI technology, but has been: -- โœ… **Fully tested end-to-end** by human developers -- ๐Ÿ”’ **Security audited** with comprehensive security checks -- ๐Ÿ‘จโ€๐Ÿ’ป **Human-reviewed** for code quality and best practices -- ๐Ÿงช **Production-tested** in real-world scenarios - -We believe in transparent development practices and the responsible use of AI as a tool to accelerate development while maintaining high standards of quality and security. - -## โš ๏ธ CRM & Accounting disclaimers โ€” examples only, verify locally - -The CRM & accounting modules (contracts, invoices, QR-bills, the tax -report and the accountant exports) ship seeded content and computed -figures that are intended as a **starting point only**: - -- **Contract blocks** (image rights, NDA, model release, cancellation, - jurisdiction, โ€ฆ) are written by the maintainer, **not by a lawyer**. - Every operator must have their lawyer review and adapt them before - sending any contract to a customer. -- **QR-bills and SEPA EPC payloads** are rendered from the data you - typed. Picpeak is open source โ€” please scan a test invoice with your - bank's app to check the QR actually works. We are not responsible for - any mistakes that come from sending an invoice with bad data on it. -- **Tax, VAT & accounting figures** (the tax report, VAT-payable, the - per-rate breakdown, the Treuhรคnder / Banana export, etc.) are computed - from the data you enter and the defaults you configure. They are - **guidance only and jurisdiction-specific** โ€” tax rules, VAT rates, - deduction schemes (e.g. the Liechtenstein 20 % Gewinnungskosten flat - rate) and filing duties differ by country and change over time. **Every - operator must check their own tax / VAT regulations and verify the - numbers with their accountant / Treuhรคnder / tax authority before - relying on any figure or export.** Picpeak makes no warranty that the - output is correct for your jurisdiction or situation. - -Read [`docs/crm-disclaimers.md`](docs/crm-disclaimers.md) before -enabling the Contracts, Invoices or Accounting features. - ## ๐Ÿ“„ License PicPeak is released under the [MIT License](LICENSE). Use it freely for personal or commercial projects. -## ๐Ÿš€ Ready to Get Started? - -1. โญ **Star this repository** to show your support -2. ๐Ÿ“– Read the [docs at docs.picpeak.app](https://docs.picpeak.app) -3. ๐Ÿ› Report issues or request features -4. ๐Ÿค Join our community and contribute! - ---

Made with โค๏ธ by photographers, for photographers
- Homepage โ€ข - Live Demo โ€ข - GitHub โ€ข - Documentation โ€ข + Homepage ยท + Live Demo ยท + Documentation ยท Support

diff --git a/docs/_to-migrate/README.md b/docs/_to-migrate/README.md new file mode 100644 index 00000000..d63301d4 --- /dev/null +++ b/docs/_to-migrate/README.md @@ -0,0 +1,22 @@ +# `_to-migrate` โ€” staging area, **not canonical** + +> [!WARNING] +> These pages were cut out of the root `README.md` to slim it down. They are +> **being ported to the PicPeak docs site** (its own repo โ†’ docs.picpeak.app). +> +> - **Do not edit** these files expecting the change to be authoritative โ€” edit +> the docs-site copy once it exists. +> - **Do not add new links** to this folder from code or other docs. +> - This whole folder is **deleted** once the docs-site pages are live (see the +> 3-phase plan in PicPeak/picpeak#1000). + +The root README links here only as a temporary bridge so no link 404s while the +docs-site pages are being written. + +| File | Ported to docs.picpeak.app path | +|---|---| +| `webhooks.md` | `/features/webhooks` | +| `storage-backends.md` | `/deployment` (storage section) | +| `first-run-setup.md` | `/deployment` (first run / permissions) | +| `system-requirements.md` | `/deployment` (system requirements) | +| `roadmap.md` | project roadmap / GitHub Projects (TBD) | diff --git a/docs/_to-migrate/first-run-setup.md b/docs/_to-migrate/first-run-setup.md new file mode 100644 index 00000000..14fd38ac --- /dev/null +++ b/docs/_to-migrate/first-run-setup.md @@ -0,0 +1,26 @@ +# First run โ€” create your admin account + +On first start with no `ADMIN_PASSWORD` set, PicPeak has **no admin account yet** and greets you with an in-browser setup screen โ€” no credentials in `.env`: + +1. Open **http://localhost:3000/admin** โ€” you'll be redirected to `/setup`. +2. Read the **one-time setup token** from the 0600 file the backend writes it to + (it is deliberately *not* printed to the logs โ€” that would leave a live + bootstrap credential in `docker logs`): + ```bash + docker compose exec backend cat /app/data/SETUP_TOKEN + ``` + It is bind-mounted, so `sudo cat data/SETUP_TOKEN` on the host works too. Only + if that file could not be written does the backend fall back to logging the + token (`docker compose logs backend | grep -i "setup token"`). +3. Paste the token, set your admin **email + password**, and you're in. The token is single-use, and the setup screen closes permanently once an admin exists. + +> Prefer the old behaviour? Set `ADMIN_PASSWORD` in `.env` and PicPeak auto-creates the admin on first boot instead (credentials written to `data/ADMIN_CREDENTIALS.txt`). + +## Docker file permissions + +- The backend container starts as root, chowns bind-mounted host directories (`./storage`, `./data`, `./logs`) to UID 1001 (`nodejs`), then drops privileges via `su-exec` before running the app. No host-side setup needed for fresh installs. +- If you pin `user:` in a compose override (e.g. to map a specific host UID), the self-chown is skipped and you must pre-chown the host directories to that UID โ€” see [docs.picpeak.app/deployment/docker#permissions](https://docs.picpeak.app/deployment/docker#permissions). + +## ARM64 (aarch64) systems + +Pre-built images include native `linux/arm64`, no platform flags or emulation needed. If you're on an older image tag that's still amd64-only, see [docker-compose.amd64.override.yml](../../docker-compose.amd64.override.yml) for a transitional fallback. diff --git a/docs/_to-migrate/roadmap.md b/docs/_to-migrate/roadmap.md new file mode 100644 index 00000000..6002839b --- /dev/null +++ b/docs/_to-migrate/roadmap.md @@ -0,0 +1,28 @@ +# Roadmap + +We're constantly improving PicPeak and welcome contributions from our community! If you have ideas for new features or want to help implement existing ones, please open an issue or submit a pull request. Your contributions help make PicPeak better for everyone. + +## ๐Ÿšง Beta Features (Use at your own risk) + +These features are currently in beta testing and may have limited functionality or stability: + +| Feature | Description | Status | +|---------|-------------|--------| +| **CRM & Accounting Module** | Quotes, contracts, invoices (+ Storno), hours logging, calendar, and tax report โ€” plus inbound supplier-invoice capture, internal expenses, and a Treuhรคnder/Banana (Swiss/LI) accountant-journal export. Feature-flagged off by default. Seeded contract blocks, payment terms, IBAN / QR-bill and tax defaults are **examples only** and need legal / financial / **tax** review before customer-facing use. See [docs.picpeak.app/features/crm](https://docs.picpeak.app/features/crm). | ๐Ÿงช Beta | +| **Simple Deployment Script** | One-click deployment script for quick server setup with automated configuration and dependency installation | ๐Ÿงช Beta | + +## ๐Ÿ“‹ Future Enhancements + +| Feature | Description | Priority | Status | +|---------|-------------|----------|---------| +| **Backup & Restore** | Comprehensive backup system with S3/MinIO support, automated scheduling, and safe restore functionality | High | โœ… Implemented | +| **External Media Library (Reference Mode)** | Use an external folder library as a readโ€‘only source with import and onโ€‘demand thumbnail generation | High | โœ… Implemented | +| **Download Protection** | Advanced image protection system with canvas rendering, invisible watermarking, right-click prevention, and DevTools detection to protect photos from unauthorized downloads | High | โœ… Implemented | +| **Gallery Templates** | Multiple gallery layouts (grid, masonry, carousel, timeline, hero, mosaic) with custom CSS styling support. Includes starter templates like Apple Liquid Glass for complete visual customization | Medium | โœ… Implemented | +| **Face Recognition** | AI-powered face detection to help guests find their photos and create automatic person-based albums | Low | ๐Ÿ”„ Open | +| **Gallery Feedback** | Allow guests to like, rate, and comment on photos with admin notifications and moderation | Medium | โœ… Implemented | +| **Video Support** | Upload and display videos alongside photos in galleries with streaming support | Low | โœ… Implemented | +| **Multiple Administrators** | Support for multiple admin accounts with role-based permissions and activity tracking | Low | โœ… Implemented | +| **Filtering & Export Options** | Filter photos by likes, ratings, comments, or favorites. Search by filename. Sort by date, name, size, or rating. Export filtered selections as ZIP or generate Capture One/Lightroom-compatible file lists for professional workflows | Medium | โœ… Implemented | + +**Status Legend:** โœ… Implemented | ๐Ÿšง In Progress | ๐Ÿ”„ Open | ๐Ÿ“‹ Planned diff --git a/docs/_to-migrate/storage-backends.md b/docs/_to-migrate/storage-backends.md new file mode 100644 index 00000000..d6afc191 --- /dev/null +++ b/docs/_to-migrate/storage-backends.md @@ -0,0 +1,22 @@ +# Storage Backends + +PicPeak supports two storage backends for photos, thumbnails, hero images, watermarks, and archive zips. Both are configured via environment variables; no code change is required to switch. + +| Capability | `STORAGE_BACKEND=local` (default) | `STORAGE_BACKEND=s3` | +|---|---|---| +| Photo / thumbnail / hero storage | Local filesystem under `STORAGE_PATH` | Bucket on any S3-compatible service | +| Admin UI upload | โœ… | โœ… | +| Filesystem auto-import (chokidar watcher) | โœ… | โŒ โ€” disabled (use the upload API) | +| Watermarks, fingerprinting, fragmentation | โœ… | โœ… (materialized to a tmp file just-in-time) | +| Bulk download zips (cached + on-the-fly) | โœ… | โœ… | +| Backups | โœ… | โœ… | +| External media reference mode (`EXTERNAL_MEDIA_ROOT`) | โœ… (always local) | โœ… (still local โ€” not migrated) | + +## Switching to an S3-compatible backend + +1. Provision a bucket and credentials. The minimum IAM policy is documented in `.env.example`. +2. Set `STORAGE_BACKEND=s3` plus `STORAGE_S3_BUCKET`, `STORAGE_S3_REGION`, `STORAGE_S3_ACCESS_KEY`, `STORAGE_S3_SECRET_KEY`. For non-AWS providers (MinIO, R2, B2, โ€ฆ) also set `STORAGE_S3_ENDPOINT`. +3. If you have existing local content, copy it first: `node backend/scripts/migrate-storage.js --dry-run` then `node backend/scripts/migrate-storage.js`. The script is idempotent and writes a failures CSV. +4. Restart the backend. The startup check pings the bucket and refuses to boot on misconfig. + +Note: presigned-URL serving (zero-bandwidth direct downloads from S3) is intentionally **not** in v1 โ€” every request still streams through the backend so watermarks, devtools-detection, and access logging keep working. diff --git a/docs/_to-migrate/system-requirements.md b/docs/_to-migrate/system-requirements.md new file mode 100644 index 00000000..33984cfa --- /dev/null +++ b/docs/_to-migrate/system-requirements.md @@ -0,0 +1,63 @@ +# System Requirements + +## Minimum Requirements +- **CPU**: 2 CPU cores +- **RAM**: **4 GB minimum** for a normal photo-upload workload โ€” sharp/libvips + decodes the full uncompressed frame before resize, and the default two + worker loops at sharp-concurrency 2 can push peak RSS past 1.5 GB on a + batch of 20-MP+ photos. On a 2 GB VPS that's enough to OOM-kill the + backend mid-batch (surfaces as 503s on thumbnails โ€” see [Low-memory + hosts](#low-memory-hosts) below for the recipe to run on 2 GB). +- **Storage**: 20GB minimum (plus photo storage needs) +- **OS**: Linux (Ubuntu 20.04+), macOS, or Windows with WSL2 +- **Node.js**: v18.0.0 or higher +- **Database**: SQLite (included) or PostgreSQL 12+ + +## Docker Requirements (Recommended) +- **Docker**: v20.10.0+ +- **Docker Compose**: v2.0.0+ + +## Low-memory hosts + +Running on 2 GB RAM (e.g. an entry-level VPS) is workable but requires +tuning the upload-processor concurrency down. The backend auto-detects +total RAM at startup via `os.totalmem()` โ€” on a host that reports < 3 GB, +it defaults `UPLOAD_PROCESSOR_CONCURRENCY` to **1** instead of 2 and logs +a one-shot warning. You can pin the value explicitly in `.env`: + +```env +# Single worker loop โ€” slower batch processing, lower peak RSS +UPLOAD_PROCESSOR_CONCURRENCY=1 +``` + +The trade-off is throughput: a single worker processes one photo at a +time, so a 100-photo batch takes ~2ร— as long but won't OOM. **Health-check +note**: if the backend dies under memory pressure, the gallery serves +`503 Service Unavailable` on thumbnails until Docker's +`restart: unless-stopped` brings the container back. Persistent 503s +during/after an upload batch on a low-memory host are almost always this. + +## Video Support Requirements +When enabling video uploads, consider these additional resources: + +| Resource | Recommendation | Notes | +|----------|----------------|-------| +| **RAM** | 4GB+ recommended | FFmpeg processing requires more memory | +| **Storage** | Plan for 10-100x more | Videos are significantly larger than images | +| **CPU** | Additional cores help | Video thumbnail extraction is CPU-intensive | +| **Bandwidth** | Higher throughput | Video streaming requires more bandwidth | + +**Technical Notes:** +- FFmpeg is bundled via npm (`@ffmpeg-installer/ffmpeg`) - no system installation required +- Maximum upload size: **10GB per video file** +- Chunked upload support for files >100MB (resumable uploads) +- Supported formats: MP4, WebM, MOV, AVI +- Video thumbnails are automatically generated from the first few seconds + +**For Nginx/Reverse Proxy:** +If using Nginx, increase the client max body size: +```nginx +client_max_body_size 10G; +proxy_read_timeout 3600; +proxy_send_timeout 3600; +``` diff --git a/docs/_to-migrate/webhooks.md b/docs/_to-migrate/webhooks.md new file mode 100644 index 00000000..bb497018 --- /dev/null +++ b/docs/_to-migrate/webhooks.md @@ -0,0 +1,77 @@ +# Webhooks + +PicPeak POSTs event/photo lifecycle notifications to URLs you configure under **Settings โ†’ Webhooks**. Each delivery is signed `HMAC-SHA256` with a per-webhook secret in the `X-PicPeak-Signature` header so receivers can verify the request really came from your PicPeak instance. + +## Event types + +| Event | Fires when | +|---|---| +| `event.created` | Gallery created (admin or API) | +| `event.published` | Draft becomes live (`is_draft: true โ†’ false`) โ€” also fires when an event is created with `is_draft=false` | +| `event.archived` | Bulk-archive, manual archive, or auto-archive on expiry | +| `event.expired` | Expiration checker marks the gallery inactive (fires before `event.archived` in the cascade) | +| `photo.uploaded` | Admin upload, API upload, guest upload, or auto-import | +| `photo.deleted` | Single delete, bulk delete (NOT fired per-photo when an event is archived โ€” receivers infer from `event.archived` to avoid flooding) | + +## Payload shape + +```json +{ + "id": "delivery-uuid", + "type": "event.published", + "created_at": "2026-04-28T05:25:00.000Z", + "data": { + "event": { "id": 123, "slug": "wedding-smith", "share_url": "https://..." } + } +} +``` + +Also sent on every request: +- `X-PicPeak-Signature` โ€” `HMAC-SHA256(secret, raw_body)` as hex +- `X-PicPeak-Event` โ€” the event type (handy for routing without parsing the body) +- `X-PicPeak-Delivery` โ€” UUID for idempotency on the receiver side +- `User-Agent: PicPeak-Webhooks/1.0` + +## Verifying signatures + +**Node.js** +```js +const crypto = require('crypto'); +function verify(secret, rawBody, signature) { + const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex'); + const a = Buffer.from(expected, 'hex'); + const b = Buffer.from(signature, 'hex'); + if (a.length !== b.length) return false; + return crypto.timingSafeEqual(a, b); +} +``` + +**Python** +```python +import hmac, hashlib +def verify(secret: str, raw_body: bytes, signature: str) -> bool: + expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() + return hmac.compare_digest(expected, signature) +``` + +**curl + openssl** (one-liner for a quick replay) +```sh +SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}') +[ "$SIG" = "$RECEIVED_SIG" ] && echo OK || echo MISMATCH +``` + +## Retries + observability + +- `2xx` โ†’ success, recorded with latency +- Non-`2xx` or network error โ†’ exponential backoff: `1m โ†’ 5m โ†’ 30m โ†’ 2h โ†’ 12h`, max 5 attempts +- After max attempts: status `failed`, surfaces in **Settings โ†’ Webhooks โ†’ Deliveries** with a "Replay" button +- Up to 5 deliveries in flight at once; one slow consumer can't block others (configurable via `WEBHOOK_DELIVERY_CONCURRENCY`) +- Response body truncated to 1KB before storage so chatty receivers don't bloat the audit log + +The deliveries page (`/admin/webhooks/:id/deliveries`) shows every attempt with timestamp, status, HTTP code, latency, payload sent, signature, and response. Click "Send test event" to fire a synthetic delivery for any event type. + +## SSRF protection + +Webhook URLs are validated against the same private-IP blocklist used elsewhere in the app โ€” loopback, private RFC1918 ranges, link-local, `.local`/`.internal` hostnames, cloud metadata endpoints. The check runs both at create time and per-delivery (DNS-rebinding mitigation). + +For local development with a receiver on the same machine or docker network, set `WEBHOOK_ALLOW_PRIVATE_URLS=true`. Production deployments must leave this OFF.