Files
2026-07-18 01:34:52 +05:30

106 lines
5.5 KiB
Markdown

# Deploying for free (Vercel + Supabase free tier)
This app runs 100% on free tiers: **Vercel Hobby** (Next.js hosting) + **Supabase Free**
(Postgres for metadata + Storage for the media files). No paid services are required.
The in-browser AI (object detection, faces, OCR) runs on the user's device for free; only the
optional Gemini generative edit needs a key (and can be left off).
> TL;DR — run the SQL once, push to GitHub, import into Vercel with **Root Directory = `apps/web`**,
> set the two `NEXT_PUBLIC_SUPABASE_*` env vars, deploy. Everything (photos, videos, tags, versions,
> comments, albums, edited copies) then persists to your Supabase project.
---
## 1. Supabase (database + file storage)
1. Create a project at <https://supabase.com> (free tier).
2. Open **SQL Editor → New query**, paste all of [`docs/supabase-setup.sql`](./supabase-setup.sql),
and **Run**. This creates the metadata tables, the demo RLS policies, **and the public `media`
storage bucket** (the bucket is what makes uploaded/edited/captured files survive a reload —
without it the app silently falls back to non-durable URLs).
3. Confirm **Storage** now lists a bucket named `media` marked **Public**.
4. **Project Settings → API** → copy:
- **Project URL** → `NEXT_PUBLIC_SUPABASE_URL`
- **anon public** key → `NEXT_PUBLIC_SUPABASE_ANON_KEY`
(Never use the `service_role` key in the client — it stays server-side only, if used at all.)
> Free-tier note: a Supabase project **pauses after ~7 days idle**. Open the dashboard to unpause
> before a demo. Storage free tier is 1 GB, Postgres 500 MB — plenty for a demo library.
## 2. Push the repo to GitHub
Commit everything **including `pnpm-lock.yaml`** at the repo root (Vercel uses it to detect pnpm and
install the whole workspace). Do not commit `.env.local` (it's gitignored).
## 3. Vercel
1. <https://vercel.com> → **Add New → Project** → import your GitHub repo.
2. **Configure the project:**
| Setting | Value |
|---|---|
| **Root Directory** | `apps/web` |
| **Framework Preset** | Next.js (auto-detected) |
| **Install Command** | `pnpm install` (default) |
| **Build Command** | `next build` (default) |
| **Output** | `.next` (default) |
| **Node.js Version** | 20.x |
> No separate SDK build step is needed: `@photo-gallery/sdk` is consumed from TypeScript source
> via Next's `transpilePackages`, so `pnpm install` (which links the workspace) is enough.
3. **Environment Variables** (Project → Settings → Environment Variables), for Production + Preview:
**Client (safe to expose — required for persistence):**
| Name | Value |
|---|---|
| `NEXT_PUBLIC_SUPABASE_URL` | your Project URL |
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | your anon public key |
| `NEXT_PUBLIC_SUPABASE_BUCKET` | `media` *(optional; default is `media`)* |
**Server-only (optional — do NOT prefix with `NEXT_PUBLIC`):**
| Name | Value |
|---|---|
| `GEMINI_API_KEY` | a Gemini key *(only for the AI generative-edit button)* |
| `GEMINI_IMAGE_MODEL` | `gemini-2.5-flash-image` *(optional)* |
Without the Supabase vars the app still runs, but on browser-local storage only (data won't sync
across devices). Without `GEMINI_API_KEY` the generative-edit route returns 503 while all the free
in-browser AI keeps working.
Optional theming/feature flags (`NEXT_PUBLIC_APG_*`) are documented in [`docs/ENV.md`](./ENV.md).
4. **Deploy.**
## 4. Verify after the first deploy
- Response headers include a per-request `content-security-policy` with a `nonce-…` (confirms the
nonce CSP middleware works on Vercel — expected; the app renders dynamically, not as a static export).
- Import a photo → its `src` becomes a `https://<project>.supabase.co/...` URL → **hard-reload**: it
still shows. Same for a camera capture and an edited copy.
- Edit a photo twice → the Info panel shows **Version 3**; reload → history + comments persist.
- The **Objects** sidebar section fills in as on-device detection tags photos.
## What persists to the database
Everything is stored as JSONB per item (`gallery_media.data`) + binaries in Storage, so a reload
restores it all: **tags, detected objects/labels, faces, versions[] (full edit history + audit log),
comments[], albums, favorites, edits, and Save-as-Copy items** (each copy is a new row with its own
2-entry history). System/smart albums (incl. the auto **object albums**) are regenerated on load, so
they're intentionally not stored.
## Gotchas / limits (free tier)
- **Gemini AI edit + Vercel body limit:** Vercel Hobby caps request bodies at ~4.5 MB. The AI-edit
route is capped accordingly and large images are downscaled client-side first; very large images
may still be rejected. The free in-browser AI is unaffected.
- **Single-user demo:** the Supabase adapter does a full-state sync (no auth). For multi-user, add
Supabase Auth and scope rows per user before going to production (the RLS policies are open for the
demo — tighten them with `auth.uid()`).
- **In-browser video export** (trim/overlays/watermark/etc.) requires the source video to be readable
for canvas capture. Same-origin (Supabase Storage with CORS, which is on by default) works; a
cross-origin host without CORS headers can't be exported in-browser (the editor now surfaces a clear
error instead of failing silently).
- **Heavy AI libs** (TensorFlow.js / face-api / tesseract / transformers / imgly) are dynamically
imported on the client only — they don't bloat the server bundle, but first analysis downloads model
weights (cached afterwards).