Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .env.staging.example
Original file line number Diff line number Diff line change
@@ -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=<generate-a-new-32-char-secret-for-staging>

# Google OAuth — create separate OAuth credentials for staging in Google Cloud Console,
# with Authorized Redirect URIs pointing to staging.reactnativevibecode.com
GOOGLE_CLIENT_ID=<staging-google-client-id>
GOOGLE_CLIENT_SECRET=<staging-google-client-secret>

# ── Billing (Polar) ────────────────────────────────────────────────────────────────
# Use Polar's sandbox server for staging so no real payments are processed.
POLAR_SERVER=sandbox
POLAR_ACCESS_TOKEN=<your-polar-sandbox-access-token>
POLAR_WEBHOOK_SECRET=<your-polar-sandbox-webhook-secret>
# Sandbox product IDs from your Polar sandbox dashboard
NEXT_PUBLIC_POLAR_START_PRODUCT_ID=<sandbox-start-product-id>
NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID=<sandbox-pro-product-id>
NEXT_PUBLIC_POLAR_SENIOR_PRODUCT_ID=<sandbox-senior-product-id>

# ── Real-time (Pusher) ─────────────────────────────────────────────────────────────
# Optionally use a separate Pusher app for staging to isolate events.
NEXT_PUBLIC_PUSHER_APP_KEY=<staging-pusher-key>
PUSHER_APP_ID=<staging-pusher-app-id>
PUSHER_APP_SECRET=<staging-pusher-secret>
NEXT_PUBLIC_PUSHER_CLUSTER=<pusher-cluster>

# ── E2B Sandboxes ─────────────────────────────────────────────────────────────────
# You can share the same E2B key as production, or use a separate one for staging.
E2B_API_KEY=<e2b-api-key>

# ── Inngest ───────────────────────────────────────────────────────────────────────
# Use Inngest's staging/dev branch or a separate app for background jobs.
INNGEST_EVENT_KEY=<staging-inngest-event-key>
INNGEST_SIGNING_KEY=<staging-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=<anthropic-api-key>
6 changes: 5 additions & 1 deletion apps/web/lib/auth/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
186 changes: 186 additions & 0 deletions docs/STAGING_SETUP.md
Original file line number Diff line number Diff line change
@@ -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 | `<auto-generated>.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=<sandbox token>`
- `NEXT_PUBLIC_POLAR_START_PRODUCT_ID=<sandbox id>`
- `NEXT_PUBLIC_POLAR_PRO_PRODUCT_ID=<sandbox id>`
- `NEXT_PUBLIC_POLAR_SENIOR_PRODUCT_ID=<sandbox 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.