
Internal Video & Photo Library with Direct-to-CDN Uploads





A small internal team needed one private place to keep every marketing video and photo — searchable, taggable, and organised into folders — without paying for a heavyweight DAM and without their own server ever having to store or transcode a single video file.
I built a two-part system: a Spring Boot API that owns auth, metadata, and orchestration, and a Next.js app that uploads videos straight from the browser to Bunny Stream over a resumable protocol. The app's own server only ever holds a Postgres row — title, tags, dimensions, uploader — never the media itself.
The backend creates a video entry in Bunny's library and hands the browser signed TUS credentials; the file then streams directly to Bunny via tus-js-client, resumable across network drops, with no proxy server in the middle.
When Bunny finishes transcoding it calls a secret webhook URL. Rather than trusting the payload, the backend re-fetches the video's real status from Bunny's API before flipping the row to READY or FAILED and filling in duration and thumbnail.
Folders and subfolders, favorites, a recoverable Trash with a daily auto-purge job, multi-select bulk actions, expiring public share links with QR codes, server-side photo thumbnailing, and EXIF extraction (camera, date, GPS) at upload time.
MEMBER / ADMIN roles with a self-healing admin bootstrap, an admin storage dashboard with real per-uploader byte counts, in-app user management, and an optional Cloudflare Turnstile gate for anonymous visitors and the login form.
A Spring Boot API and a Next.js frontend deployed side by side on a self-managed VPS — the backend as a systemd service, the frontend under PM2 — with all video storage, transcoding, and delivery offloaded to Bunny and only metadata kept in Postgres.