Adds Workers + static-assets configs for the app (app.diagrav.com) and a separate marketing/landing site (diagrav.com), plus the deployment runbook used to stand up Supabase Cloud, Google OAuth, Resend SMTP, and Cloudflare DNS/custom domains. Renames the app from "AV Planner" to "Diagrav" to match the new domain. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017DUU6CnxECCDeqDNYJgr5x
9.7 KiB
Deployment & CI/CD planning notes
Discussion notes only — nothing here has been implemented yet. Written so a different Claude Code conversation on this repo can pick up where this one left off without re-deriving it. Nothing below is committed to as final; it's a record of what was discussed and concluded, plus open decisions.
Hosting shape (discussed, not yet implemented)
The app is a static frontend (Vite build, no server of its own) + Supabase backend (Postgres, Auth, RLS, one Edge Function). organized-ideas.md already commits to this being provider-agnostic (Supabase Cloud, self-hosted Supabase Docker stack, or export the Postgres DB elsewhere — no app code changes needed either way).
To go live:
- Backend: create a Supabase Cloud project,
supabase link+supabase db pushto apply the 15 existing migrations,supabase functions deploy admin-user-action. Set real Google OAuth credentials for the production redirect URL — right nowsupabase/config.tomlandvite.config.tsare hardcoded to127.0.0.1withskip_nonce_check = truefor local dev; this needs a production config pass. Needs a real SMTP provider for auth emails (Resend suggested in organized-ideas.md as the default) since Inbucket (local fake mail catcher) doesn't exist in the cloud. - Frontend:
npm run build→ staticdist/, deploy to any static host (Vercel/Netlify/Cloudflare Pages, or self-hosted). SetVITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEYas build-time env vars pointing at the real Supabase project (Vite bakes these in at build time — not runtime-configurable without a refactor).
Marketing site (site/)
A separate, informational "what is this thing" page lives in site/ at the
repo root — plain hand-written HTML/CSS (index.html + style.css +
images/), no build step, not part of the Vite app or its src/ tree at
all. Screenshots under images/ are real captures from the app (a demo
DAWless rig wired up for the purpose), referenced with relative paths, so
the folder is fully self-contained.
Deploys the same way the app's frontend would, just simpler — no env vars to bake in, no build command, just serve the static files as-is. Same free-tier host options apply (Vercel/Netlify/Cloudflare Pages, or self-hosted).
Domain purchased: diagrav.com. The app was renamed from "AV Planner" to
Diagrav to match. The site's nav ("Log in" / "Sign up") and its closing
CTA ("Sign up free") now link to https://app.diagrav.com/ — so the
subdomain-split option below is the working assumption baked into the site
as of now, even though neither diagrav.com nor app.diagrav.com is
actually deployed anywhere yet. Those links will 404 until the app is
actually live at that subdomain; treat this as a leaning, not a final
decision — swapping to the path-based option only means changing the few
hrefs in site/index.html, nothing structural.
- Subdomain split (current assumption): bare
diagrav.comfor the marketing site,app.diagrav.comfor the actual tool — cleanest separation, two hosting targets instead of one. - Same domain, different path (marketing at
/, app at/appor similar) — one hosting target, but means the static host needs to route/tosite/and everything else to the Vitedist/output, which the simple "just deploydist/" flow above doesn't currently account for.
Whichever is chosen, this needs its own line in the CI/CD plan below (currently only the app's frontend/backend deploy steps are scoped) — it's a third deployable alongside "frontend" and "backend," not an afterthought folded into the frontend step.
Cost / free-tier findings
Provider: Supabase, since the app already codes directly against its
SDK/Auth/RLS model (src/data/Supabase*.ts) — switching providers would mean
rewriting those repositories.
Current Supabase Free plan limits (confirmed against supabase.com/pricing, not from training data — re-verify if this doc is read much later):
| Resource | Free limit |
|---|---|
| Database size | 500 MB |
| Egress | 10 GB/mo (5 GB cached + 5 GB uncached) |
| Monthly active users | 50,000 |
| Edge Function invocations | 500,000/mo |
| File storage | 1 GB |
| Realtime concurrent connections | 200 |
| Inactivity | paused after 1 week idle, max 2 active projects |
Conclusion: this app is storage-bound, not bandwidth-bound. File storage,
realtime, and edge function limits are all non-issues (no file uploads, no
supabase.channel(...) usage anywhere in src/, and the one edge function
is admin-only/rare). Egress is generous relative to what this app transfers
per session. The one limit likely to actually bite is database size
(500 MB) — driven less by user-count/traffic than by long-term accumulation
in diagrams + diagram_snapshots.
Specific risk found: SupabaseDiagramRepository.ts (maybeWriteSnapshot,
~line 70-93) writes a new version-history snapshot row every 5 minutes of
active editing, with no retention/expiry — they accumulate indefinitely.
A user who edits diagrams regularly for months will grow the DB more than a
burst of new signups would. Suggested follow-up (not yet done): add
snapshot retention — e.g. keep the last N snapshots per diagram, or prune
anything older than ~90 days. This is the highest-leverage lever for keeping
DB size (and therefore cost/free-tier headroom) under control.
Rough scale ballpark discussed: solo/small-team use won't come close to any limit for years. Somewhere in the ~300–1,500 regular-active-user range with several diagrams each is roughly where the 500MB DB cap would start to bite, well before the 10GB egress cap or 50k MAU cap would matter. Treat this as a rough estimate, not a measured number — worth checking real diagram JSON size in Studio once there's real usage.
Estimated cost: $0/mo to start (Supabase Free + Vercel/Netlify/Cloudflare Pages free tier + Resend free tier for auth email), $25/mo once you want Supabase Pro (no project auto-pause, higher limits across the board).
CI/CD plan: Gitea + Woodpecker, pushbutton staging/prod
Repo is self-hosted on Gitea (git.halfbinary.net/aarbit/av-planner) with
Woodpecker CI already in use elsewhere. No .woodpecker.yml exists yet in
this repo.
Desired flow: PR merges to main → pipeline runs lint/typecheck/build
and stops → a human clicks a "Deploy" button to push to staging, and
separately clicks it again to promote to production. Not fully automatic on
merge — deploys to both environments are manual/pushbutton.
Mechanism (confirmed against current Woodpecker docs): Woodpecker has a
built-in deploy event for exactly this. Enable "Allow deployments" in
the repo's Woodpecker project settings, and any successful pipeline run gets
a Deploy button in the UI. Clicking it re-runs that pipeline with
event: deploy and a deploy_to string you type (e.g. staging or
production), exposed to steps as $CI_PIPELINE_DEPLOY_TARGET. Steps gate
on it, e.g.:
steps:
- name: deploy-staging
when:
- event: deploy
evaluate: 'CI_PIPELINE_DEPLOY_TARGET == "staging"'
- name: deploy-prod
when:
- event: deploy
evaluate: 'CI_PIPELINE_DEPLOY_TARGET == "production"'
What needs to exist:
- Two Supabase projects — staging and prod. This exactly fills the free tier's "2 active projects" cap (see above) — no free-tier headroom left for a third project if one is ever wanted later.
- Two frontend hosting targets — two sites/deployments (SaaS host or self-hosted alongside Gitea/Woodpecker — not yet decided which).
- Known wrinkle: because Vite bakes
VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEYin at build time, you can't build once and promote the identical artifact to both environments — each deploy target needs its own build with its own env vars baked in. Not hard, just means "deploy" reruns the build rather than reusing one artifact. A non-issue at this app's scale. - Migrations run per-target via Supabase CLI, scripted per environment:
supabase link --project-ref $SUPABASE_PROJECT_REF supabase db push supabase functions deploy admin-user-action - Secrets needed in Woodpecker (scoped per target):
SUPABASE_ACCESS_TOKEN, staging/prodSUPABASE_PROJECT_REF, staging/prodVITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY, plus whatever the frontend host needs (API token, or SSH key if self-hosted). - Google OAuth: needs its own authorized redirect URI added in Google Cloud console for each of the staging and prod domains — one-time setup, easy to forget.
Difficulty estimate discussed: roughly a focused afternoon — nothing here
fights the tooling (no containers to orchestrate, no server process to run).
Breakdown: .woodpecker.yml (~1–2 hrs), second Supabase project + migration
check (~30 min), frontend hosting target wiring (~30–60 min), secrets +
"Allow deployments" toggle (~15 min), end-to-end test/debug (~1–2 hrs).
Open decisions (not yet made)
- Which frontend host: SaaS (Vercel/Netlify/Cloudflare Pages) vs self-hosted alongside the existing Gitea/Woodpecker infra.
- Where the marketing site (
site/, see above) lives relative to the app — leaning subdomain (diagrav.com+app.diagrav.com, per the links already insite/index.html) over same-domain/different-path, but not finalized — and whether it gets its own line in the eventual.woodpecker.ymlor piggybacks on the frontend deploy step. - Whether/when to add diagram-snapshot retention (the storage-risk item above) — not done yet, just identified.
- Actual
.woodpecker.ymlhas not been written yet — this doc captures the plan/shape only, per the user's request to keep this conversation discussion-only and not make repo changes here.