camera

Frame-It-Now (Camera)

Product: Frame-It-Now — mobile-first selfie PWA for events and public engagement.
Repository / UI strings: The codebase and some pages still label the app “Camera”; operationally this is the same product.

Version: 2.9.0 (canonical: package.json"version")
Last Updated: 2026-04-09
Status: Production-ready

A Next.js application: users capture photos, apply branding frames, upload to a CDN, store metadata in MongoDB, share via public links, and display submissions on event slideshows with fair rotation and aspect-aware layouts.


Quick Start

npm install
npm run dev          # http://localhost:3000
npm run build
npm start

Technology Stack

Area Choice
Framework Next.js (App Router)
Language TypeScript (strict)
Database MongoDB
Auth SSO (OAuth2/OIDC + PKCE), encrypted session cookie
Image hosting imgbb.com
Styling Tailwind CSS
Transactional email Not wired (SSO does not send app mail; see docs/AUTHORIZATION.md)

1. System Model

End-to-end flow

  1. Participant opens /capture/[eventId] (event) or /capture (legacy global capture).
  2. Optional custom pages: who-are-you, accept, CTA — then frame selection (if multiple frames), camera (getUserMedia) or file upload (legacy page).
  3. Client-side compositing: Canvas draws the photo (cropped to frame aspect ratio on the event path), then the frame asset; output is a JPEG (quality ~0.85, longest side capped around 2048px).
  4. POST /api/submissions: sends base64 image data + event UUID + optional userInfo / consents.
  5. Server uploads to imgbb, inserts a submission document in MongoDB.
  6. Share: /share/[id] uses the submission’s MongoDB _id and imageUrl for OG tags and display.
  7. Slideshow: /slideshow/[slideshowId] fetches playlist JSON, renders 16:9 single or mosaic slides, POSTs play counts so least-played items surface more often.

Major components

Layer Responsibility
Browser Camera, canvas compositing, share/download UX, slideshow player (FIFO queue + preload, see docs/SLIDESHOW_LOGIC.md).
Next.js API routes Events, frames, logos, submissions, slideshows, auth, admin CRUD.
MongoDB Partners, events, frames, logos, submissions, slideshows; optional user cache; SSO DB reads for inactive-user filtering in playlists.
imgbb Stores composed and admin-uploaded rasters; returns public URLs and delete URLs.

Data flow (camera → slideshow)

Video → canvas snapshot (JPEG, frame aspect) → second canvas (photo + frame bitmap) → JSON POSTimgbbMongoDB insertslideshow aggregate (match by event UUID, sort by playCount / createdAt) → generatePlaylist → client display → /played increments counts.


2. Feature Breakdown

Camera capture (components/camera/CameraCapture.tsx)

Overlay / frame during live preview

Image compositing (app/capture/[eventId]/page.tsx, legacy app/capture/page.tsx)

Upload pipeline (lib/imgbb/upload.ts, POST /api/submissions)

Database storage

Slideshow (lib/slideshow/playlist.ts, app/slideshow/[slideshowId]/page.tsx, APIs under /api/slideshows/...)


3. User Flows

First-time participant (typical event)

  1. Load event → optional loading-capture logo (no mandatory loading copy).
  2. If custom pages exist before take-photo → onboarding (who-are-you, accept, CTA).
  3. Frame selection if multiple frames; otherwise skip to camera.
  4. Capture → compositing overlay → preview → save → share URL / social / copy → NEXT → thank-you pages or flow restart.

Returning / SSO resume

Slideshow viewer

Admin (frames / events)

Edge cases


4. Data Model

What the system relies on (submissions)

Field / pattern Role
imageUrl Slideshow, share page, Open Graph — required for display.
eventId (UUID) or eventIds[] Playlist filter — must match the event’s eventId UUID (slideshow API resolves slideshow → event document).
metadata.finalWidth / finalHeight Aspect detection; missing values fall back to 1920×1080 in playlist code and can mis-classify aspect ratio.
playCount, createdAt Fair rotation (least played, then oldest).
isArchived, hiddenFromEvents Excluded from slideshow when set.
userEmail / userId Slideshow may filter out inactive SSO users; anonymous path must remain valid.
Optional: userInfo, consents, deleteUrl, slideshowPlays, partner fields GDPR, analytics, per-slideshow play stats.

Schema documentation vs runtime

lib/db/schemas.ts describes a rich Submission shape (e.g. submissionId, originalImageUrl, finalImageUrl, eventIds). POST /api/submissions currently persists a different document shape (imageUrl, singular eventId, userName, etc.). Slideshow and playlist code often accept imageUrl || finalImageUrl. Treat schema drift as operational risk: new code should align types, persistence, and consumers or keep explicit compatibility shims.


5. Performance & Constraints


6. Failure Modes (summary)

Area Detection Mitigation / UX
Camera API errors, black-frame check Messages, retry, switch camera
Compositing Thrown errors User alert
imgbb / network Axios errors, timeouts Retries where configured; user retry save
MongoDB Route errors withErrorHandler → 5xx
Empty slideshow Empty playlist “No submissions yet” UI
Play count API Non-OK response Logged; playback continues
Inactive-user filter (getInactiveUserEmails) DB/SSO failure Playlist route may 500 — see code paths

7. Scalability

Scale Notes
Low tens Typical single-instance + Mongo + imgbb is fine.
~100 concurrent imgbb quotas, Mongo write rate, playlist aggregate cost, CDN egress become visible.
1k+ Unbounded aggregation per playlist request is a memory/CPU hotspot; frequent playlist?limit=1 prefetch adds read load.
10k+ imgbb as single vendor, write amplification on /played, lack of edge caching on API reads — likely need object storage + CDN, bounded queries, Redis (limits, sessions, queues), and observability.

8. Code & Architecture Quality


9. Security & Privacy


10. Roadmap (suggested)

Short-term: Reconcile submission schema vs DB writes; reduce production console noise where still noisy; document event id semantics (Mongo _id on slideshow document vs UUID on submissions).

Mid-term: Cap or paginate playlist sourcing; optional original + final image storage; unify legacy vs event capture behavior where product allows; moderation / hold queue before slideshow.

Long-term: First-party object storage + CDN + signed URLs; multi-tenant quotas; real-time slideshow channel if required; formal privacy tooling.


Project Structure

├── app/
│   ├── api/                 # REST handlers
│   ├── admin/               # Admin UI
│   ├── capture/             # /capture + /capture/[eventId]
│   ├── slideshow/[slideshowId]/   # Fullscreen player (wraps SlideshowPlayerCore)
│   ├── slideshow-layout/[layoutId]/  # Composite layout player
│   ├── share/[id]/          # Public share + metadata
│   └── users/[name]/        # Public user profile (admin actions when permitted)
├── middleware.ts            # Edge: /admin/* gate (cookie + appRole + appAccess)
├── components/
│   ├── camera/              # CameraCapture, FileUpload
│   ├── capture/             # Custom page steps
│   ├── shared/
│   └── admin/
├── lib/
│   ├── admin/               # Shared admin helpers (e.g. user-management props)
│   ├── api/                 # withErrorHandler, responses, rateLimiter
│   ├── auth/
│   ├── db/                  # mongodb, schemas, sso helpers
│   ├── imgbb/
│   ├── security/
│   └── slideshow/           # playlist generation, viewport-scale
├── ARCHITECTURE.md          # Deeper architecture (repo root)
├── TECH_STACK.md
├── NAMING_GUIDE.md
└── docs/                    # SLIDESHOW_LOGIC.md, MONGODB_CONVENTIONS.md, …

Documentation Index

Doc Purpose
README.md This file — product + system model + ops
ARCHITECTURE.md Deeper architecture
TECH_STACK.md Technology decisions
NAMING_GUIDE.md Conventions
docs/SLIDESHOW_LOGIC.md Slideshow behavior detail
docs/DOCUMENTATION.md How to keep docs aligned with code
docs/MONGODB_CONVENTIONS.md DB patterns
docs/MONGODB_ATLAS.md Atlas setup, npm run db:verify-uri, npm run db:ensure-indexes
RELEASE_NOTES.md Changelog
TASKLIST.md / ROADMAP.md Planning

API Overview

Auth: GET /api/auth/login?provider=google|facebook (optional), GET /api/auth/callback, GET or POST /api/auth/logout, GET /api/auth/session

Core: partners, events, frames, logos, submissions (GET authenticated list; POST create), slideshows (.../playlist, .../played, .../background-image, …), slideshow-layouts (GET public by layoutId; admin CRUD with auth)

See ARCHITECTURE.md and route files under app/api/ for the full set.


Development Guidelines

Version protocol (from team practice)


Environment Variables

Copy .env.example to .env / .env.local and fill in values. Check DNS with npm run db:verify-uri; npm run env:verify exercises Mongo, SSO discovery, ImgBB, and Upstash Redis when configured.

MONGODB_URI=mongodb+srv://...
MONGODB_DB=camera

SSO_BASE_URL=https://...
SSO_CLIENT_ID=...

IMGBB_API_KEY=...

# Public base URL for this deployment (emails, links — set per host in Vercel if needed).
NEXT_PUBLIC_APP_URL=https://camera.doneisbetter.com

Production web entrypoints include https://camera.doneisbetter.com and https://camera.messmass.com. OAuth redirect_uri is derived from the browser’s host (…/api/auth/callback), so in SSO (e.g. sso.doneisbetter.com) your Camera OAuth client must allowlist every callback you use (localhost, both camera hosts, preview URLs if applicable) and you should remove retired hosts (for example an old fancam-style URL) from that client’s redirect list so tokens are not issued back to stale domains.

API rate limits use Upstash when both variables are set; otherwise limits are per serverless instance only.

  1. In Upstash Console, create a Redis database (global region is fine).
  2. Open the database → REST API → copy UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN.
  3. In Vercel → your project → SettingsEnvironment Variables → add both for Production (and Preview if you want the same behavior there).
  4. Redeploy the project (Deployments → ⋮ → Redeploy).
  5. Locally: add the same keys to .env.local, then run npm run env:verify — you should see ✓ Upstash Redis: PING ok.

Install/use the CLI with npx vercel@latest. Log in once: vercel login.

Shorthand scripts: npm run vercel:link, npm run vercel:env-pull.


License

Proprietary — all rights reserved.


Support

SSO · MongoDB Atlas · imgbb