HOME EXPERIENCE PROJECTS & AI PHOTOGRAPHY ABOUT
← PROJECTS & AI

CLIENT DELIVERY PORTAL

Password-protected photo delivery for photography clients. Photos are stored in Cloudflare R2 and served via short-lived presigned URLs. Galleries expire automatically; a nightly function cleans up the files.

CLOUDFLARE R2 NETLIFY FUNCTIONS NODE.JS JWT AUTH RESEND API CLIENT EXPERIENCE

Overview

Built as a branded alternative to WeTransfer or Google Drive for delivering finished photo galleries. A single CLI command uploads an edited folder to Cloudflare R2, generates a password-protected gallery URL with a configurable expiry, and prints the shareable link ready to send to the client.

Everything runs on dvedee.com via Netlify Functions — no separate server, no third-party gallery platform, no per-download fees.

Uploading a Gallery

One command uploads the folder, generates the gallery, and prints the shareable link:

node scripts/upload-gallery.js /path/to/photos/folder

The script prompts for client name, password, expiry duration, and which folder to show first. When it finishes:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Gallery ready!

  Client:   Sarah & John
  URL:      https://dvedee.com/client-gallery?id=abc123...
  Password: bluemoon42
  Expires:  Wed May 22 2026
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

What Clients Experience

  1. Unlock

    Client opens the link and enters their password. The gallery unlocks and their session stays active for 4 hours without needing to re-enter it.

  2. Browse

    Photos are organised into tabs (Social, High Res, etc.) based on the folder structure. Clients can browse full-screen and switch between tabs.

  3. Download

    Individual photos, per-tab zip files, or a full-gallery zip are all available. Downloads are served via short-lived presigned R2 URLs — never routed through Netlify.

  4. Notification

    A Resend email is sent when a client downloads, with a timezone-localised timestamp. The gallery then expires automatically on the set date and files are cleaned up from R2 overnight.

Technical Details

  • Storage: photos, per-folder zips, a master zip, and a meta.json manifest are stored in R2 under a random gallery ID
  • Auth: password is checked against a SHA-256 hash in meta.json; a short-lived HMAC session token (4 hours) is returned on success
  • Rate limiting: 5 failed password attempts per IP per hour before lockout
  • Downloads: presigned R2 URLs are generated on demand and valid for 1 hour — actual files never pass through Netlify
  • Auto-cleanup: a Netlify scheduled function runs at 3am UTC daily and deletes R2 files for any expired gallery
  • Analytics: a private dashboard shows each gallery, when it was opened, and when downloads happened