diff --git a/.env.staging.example b/.env.staging.example new file mode 100644 index 00000000..39de6783 --- /dev/null +++ b/.env.staging.example @@ -0,0 +1,65 @@ +# Staging environment variable overrides +# ---------------------------------------- +# These are the values that should DIFFER between staging and production. +# All other variables from .env.example apply unchanged. +# +# In Vercel: set these under Settings → Environment Variables, scoped to "Preview" +# (optionally restricted to the "staging" branch for branch-specific isolation). +# +# The staging deployment URL is: https://staging.reactnativevibecode.com + +# ── App URLs ──────────────────────────────────────────────────────────────────── +# Tell the app its own public URL so OAuth callbacks and auth work correctly. +NEXT_PUBLIC_APP_URL=https://staging.reactnativevibecode.com +NEXT_PUBLIC_BASE_URL=https://staging.reactnativevibecode.com +NEXT_PUBLIC_SITE_URL=https://staging.reactnativevibecode.com +# Leave NEXT_PUBLIC_PROD_URL pointing to production (used only when VERCEL_ENV=production) +NEXT_PUBLIC_PROD_URL=https://www.reactnativevibecode.com + +# ── Database ───────────────────────────────────────────────────────────────────── +# Use a SEPARATE staging database — never share the production database. +DATABASE_URL=postgresql://USER:PASSWORD@HOST/staging_db + +# ── Authentication ──────────────────────────────────────────────────────────────── +# Generate a distinct secret for staging (min 32 chars, never share with production). +BETTER_AUTH_SECRET= + +# Google OAuth — create separate OAuth credentials for staging in Google Cloud Console, +# with Authorized Redirect URIs pointing to staging.reactnativevibecode.com +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# ── Billing (Polar) ──────────────────────────────────────────────────────────────── +# Use Polar's sandbox server for staging so no real payments are processed. +POLAR_SERVER=sandbox +POLAR_ACCESS_TOKEN= +POLAR_WEBHOOK_SECRET= +# Sandbox product IDs from your Polar sandbox dashboard +NEXT_PUBLIC_POLAR_START_PRODUCT_ID= +NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID= +NEXT_PUBLIC_POLAR_SENIOR_PRODUCT_ID= + +# ── Real-time (Pusher) ───────────────────────────────────────────────────────────── +# Optionally use a separate Pusher app for staging to isolate events. +NEXT_PUBLIC_PUSHER_APP_KEY= +PUSHER_APP_ID= +PUSHER_APP_SECRET= +NEXT_PUBLIC_PUSHER_CLUSTER= + +# ── E2B Sandboxes ───────────────────────────────────────────────────────────────── +# You can share the same E2B key as production, or use a separate one for staging. +E2B_API_KEY= + +# ── Inngest ─────────────────────────────────────────────────────────────────────── +# Use Inngest's staging/dev branch or a separate app for background jobs. +INNGEST_EVENT_KEY= +INNGEST_SIGNING_KEY= + +# ── Additional trusted origins ───────────────────────────────────────────────────── +# Add your Vercel preview URL pattern here if needed (e.g. Vercel bot previews). +# The regex .*\.reactnativevibecode\.com already covers staging.reactnativevibecode.com. +ADDITIONAL_TRUSTED_ORIGINS=https://staging.reactnativevibecode.com + +# ── Anthropic ───────────────────────────────────────────────────────────────────── +# You can share the production key or use a rate-limited staging key. +ANTHROPIC_API_KEY= diff --git a/apps/web/lib/auth/config.ts b/apps/web/lib/auth/config.ts index 93d5ff1a..d5801205 100644 --- a/apps/web/lib/auth/config.ts +++ b/apps/web/lib/auth/config.ts @@ -94,7 +94,11 @@ async function ensureUserSubscription(userId: string, customerId?: string) { } export const auth = betterAuth({ - baseURL: process.env.NODE_ENV === 'production' ? process.env.NEXT_PUBLIC_PROD_URL || 'https://www.reactnativevibecode.com' : getBaseURL(), + // Use VERCEL_ENV (not NODE_ENV) to distinguish production from preview/staging deployments. + // Vercel sets NODE_ENV='production' for ALL deployed environments (production AND preview/staging), + // so NODE_ENV alone cannot distinguish between them. VERCEL_ENV is 'production' only for + // the production branch, and 'preview' for staging/feature branches. + baseURL: process.env.VERCEL_ENV === 'production' ? process.env.NEXT_PUBLIC_PROD_URL || 'https://www.reactnativevibecode.com' : getBaseURL(), database: drizzleAdapter(db, { provider: 'pg', schema: { diff --git a/docs/STAGING_SETUP.md b/docs/STAGING_SETUP.md new file mode 100644 index 00000000..07540570 --- /dev/null +++ b/docs/STAGING_SETUP.md @@ -0,0 +1,186 @@ +# Staging Environment Setup + +This guide explains how to configure a persistent staging environment at +`staging.reactnativevibecode.com` using Vercel. + +## How It Works + +Vercel automatically deploys every Git branch as a **Preview** deployment. The +strategy is: + +| Branch | Vercel environment | URL | +|-----------|--------------------|----------------------------------------| +| `main` | Production | `reactnativevibecode.com` | +| `staging` | Preview | `staging.reactnativevibecode.com` | +| `feat/*` | Preview | `.vercel.app` | + +The `staging` branch gets a permanent custom subdomain instead of the ephemeral +Vercel preview URL. Everything else (feature branches, PRs) continues to use +auto-generated preview URLs. + +--- + +## Step 1 — Create the `staging` Branch + +```bash +git checkout main +git pull +git checkout -b staging +git push origin staging +``` + +Vercel will immediately build and deploy it as a Preview deployment. + +--- + +## Step 2 — Add the Custom Domain in Vercel + +1. Open the [Vercel dashboard](https://vercel.com/dashboard) and select the project. +2. Go to **Settings → Domains**. +3. Click **Add**. +4. Enter `staging.reactnativevibecode.com`. +5. When prompted for **Git Branch**, enter `staging`. + +This tells Vercel to always route that domain to the latest deployment from the +`staging` branch. + +--- + +## Step 3 — Configure DNS + +Add a CNAME record with your DNS provider (wherever `reactnativevibecode.com` +is managed): + +| Name | Type | Value | TTL | +|-----------|-------|-------------------------|------| +| `staging` | CNAME | `cname.vercel-dns.com` | 3600 | + +Vercel will provision a TLS certificate automatically once DNS propagates +(usually within a few minutes). + +--- + +## Step 4 — Set Environment Variables for Staging + +In Vercel: **Settings → Environment Variables**. + +For each variable, choose **scope = Preview** and optionally restrict it to the +`staging` branch. See `.env.staging.example` at the repo root for the full list +of values that should differ from production. + +### Critical variables + +| Variable | Staging value | +|---|---| +| `NEXT_PUBLIC_APP_URL` | `https://staging.reactnativevibecode.com` | +| `NEXT_PUBLIC_BASE_URL` | `https://staging.reactnativevibecode.com` | +| `NEXT_PUBLIC_SITE_URL` | `https://staging.reactnativevibecode.com` | +| `DATABASE_URL` | A **separate** staging PostgreSQL database | +| `BETTER_AUTH_SECRET` | A **different** secret from production | +| `POLAR_SERVER` | `sandbox` (uses Polar's test environment) | +| `GOOGLE_CLIENT_ID/SECRET` | Separate OAuth credentials (see below) | + +### Why a separate database? + +The staging database must be isolated from production so that schema migrations, +test data, and destructive operations cannot affect real users. Provision a +Neon branch (Neon supports instant database branching) or a separate Neon +project for staging. + +--- + +## Step 5 — Configure Google OAuth for Staging + +Google OAuth credentials require explicit Redirect URIs. Attempting to use +production credentials from staging will fail. + +1. Open [Google Cloud Console](https://console.cloud.google.com) → **APIs & Services → Credentials**. +2. Create a new **OAuth 2.0 Client ID** (or edit the existing one and add staging URIs). +3. Add the following **Authorized Redirect URIs**: + ``` + https://staging.reactnativevibecode.com/api/auth/callback/google + ``` +4. Set `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in Vercel (Preview scope, + staging branch) to these staging credentials. + +--- + +## Step 6 — Configure Polar Billing (Sandbox) + +Polar provides a full sandbox environment for testing subscriptions without +real payments. + +1. Log in to [Polar](https://polar.sh) and switch to **Sandbox mode** (top-right toggle). +2. Create products in the sandbox that mirror your production products. +3. Copy the sandbox access token and product IDs. +4. Set the following in Vercel (Preview scope): + - `POLAR_SERVER=sandbox` + - `POLAR_ACCESS_TOKEN=` + - `NEXT_PUBLIC_POLAR_START_PRODUCT_ID=` + - `NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID=` + - `NEXT_PUBLIC_POLAR_SENIOR_PRODUCT_ID=` + +--- + +## Step 7 — Turbo Remote Cache (already enabled) + +Vercel has native Turborepo remote cache support. No additional configuration +is required — Turbo remote caching is enabled automatically for all Vercel +deployments, including staging. The `vercel.json` build command already uses +`turbo run build --filter=@react-native-vibe-code/web`. + +--- + +## Deploying to Staging + +```bash +# Merge a feature branch into staging for QA +git checkout staging +git merge feat/my-feature +git push origin staging +``` + +Vercel will automatically build and deploy the updated `staging` branch to +`https://staging.reactnativevibecode.com`. + +To promote staging to production, open a PR from `staging → main` and merge it. + +--- + +## How `VERCEL_ENV` is Used + +The app uses `VERCEL_ENV` (set automatically by Vercel) to distinguish +environments: + +| Context | `VERCEL_ENV` | `NODE_ENV` | +|---|---|---| +| Vercel Production | `production` | `production` | +| Vercel Preview / Staging | `preview` | `production` | +| Local `vercel dev` | `development` | `development` | +| Local `next dev` | `undefined` | `development` | + +`NODE_ENV` is always `production` on Vercel, so the auth config uses +`VERCEL_ENV === 'production'` to gate the hardcoded production URL. Preview +deployments (including staging) fall through to `getBaseURL()`, which reads +`NEXT_PUBLIC_APP_URL` — set to `https://staging.reactnativevibecode.com` in the +staging Preview env vars. + +--- + +## Troubleshooting + +**Auth redirects go to the production domain from staging** +- Make sure `NEXT_PUBLIC_APP_URL` is set to `https://staging.reactnativevibecode.com` + in the Vercel Preview env vars (scoped to the `staging` branch). + +**Google OAuth callback fails** +- Verify the staging redirect URI is added in Google Cloud Console. +- Verify `GOOGLE_CLIENT_ID/SECRET` in Vercel are the staging credentials. + +**Polar webhooks don't arrive on staging** +- In Polar Sandbox dashboard, add a webhook pointing to + `https://staging.reactnativevibecode.com/api/webhooks/polar`. + +**Domain shows "Invalid Configuration" in Vercel** +- Check that the CNAME record for `staging` is live and pointing to + `cname.vercel-dns.com`. Use `dig staging.reactnativevibecode.com` to verify.