Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ SMTP_USER=
SMTP_PASS=
SMTP_FROM=

# ─────────────────────────────────────────────────────────────────────────────
# Admin dashboard
# Static bearer token for /admin/* routes. Must be a strong random string
# (e.g. `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`).
# Never share this value or commit a real one.
# ─────────────────────────────────────────────────────────────────────────────
ADMIN_SECRET=

# ─────────────────────────────────────────────────────────────────────────────
# Frontend build
# ─────────────────────────────────────────────────────────────────────────────
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ RUN npm ci --omit=dev \
&& npm cache clean --force

COPY server.js ./
COPY server ./server
COPY --from=builder /app/dist ./dist

# Runtime directories (uploads ephemeral; /data intended for SQLite volume mounts)
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ docker run --rm -p 3001:3001 \

Never commit real secrets to source control.

## Health & observability

- `GET /api/health` (public, no auth) returns `{ status, uptime, version, time }`. Useful for uptime monitors and Render health checks.
- `GET /sitemap.xml` and `GET /robots.txt` are served directly by the backend so they work without the SPA static build (e.g. during dev) and stay in sync with the canonical domain `https://spectracleanse.com`.

## Rate limiting

- All `/api/*` routes (except `/api/health` and the Stripe webhook) are gated by a 60-requests-per-minute-per-IP limiter.
- `/api/login`, `/api/register`, and the `/api/auth/*` routes get a tighter 10/min limiter on top to make brute-force harder.
- A throttled request returns `429` with `{ "error": "Too many requests" }`.

## Batch processing API

- `POST /api/process-batch` (authenticated): processes up to 20 uploaded files sequentially for paid plans (Creator/Studio). Free plan returns `403`.
Expand Down
98 changes: 98 additions & 0 deletions RENDER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Deploying SpectraCleanse AI on Render

This is the complete environment-variable reference and setup guide for running
SpectraCleanse AI on [Render](https://render.com). It explains why Stripe and
email verification may appear "not working" and exactly how to fix it.

## Why Stripe / email verification aren't working

The server changes behavior based on `NODE_ENV` and which secrets are present:

- **If `NODE_ENV` is NOT `production`** the server enables **mock checkout**
(Stripe is bypassed and returns a fake success URL — no real charge) and
**dev-fallback email** (verification/reset emails are only logged, never
sent). This is almost always the cause of "Stripe and email aren't working."
- **If `NODE_ENV` is `production` but Stripe vars are missing**, the server
exits on boot with `FATAL: Stripe is not fully configured in production.`
- **If SMTP vars are missing in production**, account creation still works but
verification/reset emails fail to send.

After deploying, open your Render service **Logs** and look for the
`[Config]` summary printed at startup — it tells you whether Stripe and Email
are actually live or running in mock/fallback mode.

## Required environment variables

Set these in **Render → your service → Environment**. (If you deploy via the
included `render.yaml` blueprint, the keys are pre-created and you just fill in
the secret values.)

### Core

| Variable | Required | Value / Notes |
|---|---|---|
| `NODE_ENV` | ✅ | `production` — **this is the single most important one.** Turns off mock checkout and dev-email fallback. |
| `JWT_SECRET` | ✅ | A long random string. Generate: `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"` |
| `FRONTEND_URL` | ✅ | Your public URL, e.g. `https://spectracleanseai.onrender.com`. Used for CORS, Stripe redirect URLs, and email links. |
| `DB_PATH` | ✅ | `/data/spectra.db` — must point at a **persistent disk** or data is wiped on every deploy. |
| `PORT` | Auto | Render injects this automatically; the server reads it. Do not hard-code. |
| `APP_BASE_URL` | Optional | Base URL for email links. Falls back to `FRONTEND_URL`, so usually unnecessary. |
| `ALLOWED_ORIGINS` | Optional | Comma-separated extra CORS origins. Only needed if the frontend is on a different domain than the API. |

### Stripe — all four required for live checkout

| Variable | Where to find it |
|---|---|
| `STRIPE_SECRET_KEY` | Stripe Dashboard → Developers → API keys → Secret key (`sk_live_…`) |
| `STRIPE_WEBHOOK_SECRET` | Stripe Dashboard → Developers → Webhooks → your endpoint → Signing secret (`whsec_…`) |
| `STRIPE_CREATOR_PRICE_ID` | Stripe → Products → Creator plan → Price ID (`price_…`) |
| `STRIPE_STUDIO_PRICE_ID` | Stripe → Products → Studio plan → Price ID (`price_…`) |

If **any** of these four is missing, the server treats Stripe as unconfigured.

### Email / SMTP — all five required to send mail

| Variable | Notes |
|---|---|
| `SMTP_HOST` | e.g. `smtp.sendgrid.net`, `smtp.resend.com`, `smtp.gmail.com` |
| `SMTP_PORT` | `587` (STARTTLS) or `465` (implicit TLS) |
| `SMTP_USER` | SMTP username (for SendGrid the literal string `apikey`) |
| `SMTP_PASS` | SMTP password / API key |
| `SMTP_FROM` | From address, e.g. `SpectraCleanse <no-reply@spectracleanse.com>` |

If **any** of these five is missing, verification/reset emails won't send in production.

### Optional

| Variable | Notes |
|---|---|
| `GEMINI_API_KEY` | Only needed for the AI SEO-generation feature. |
| `VITE_API_URL` | Build-time only. Leave **unset** for the default same-origin deployment. Set it only if you host the frontend separately from the API. |

## Setup steps

1. **Create the service.** Use the included `render.yaml` (New → Blueprint) or
create a Web Service with **Runtime: Docker** pointing at this repo's
`Dockerfile`.
2. **Add a persistent disk** mounted at `/data` (≥1 GB) so the SQLite database
survives deploys. Set `DB_PATH=/data/spectra.db`.
3. **Fill in all environment variables** from the tables above.
4. **Deploy**, then check `https://<your-service>.onrender.com/api/health` →
`{"status":"ok"}`.
5. **Configure the Stripe webhook.** In Stripe → Developers → Webhooks, add an
endpoint at `https://<your-service>.onrender.com/api/stripe-webhook` and
subscribe to `checkout.session.completed` and `customer.subscription.deleted`.
Copy its signing secret into `STRIPE_WEBHOOK_SECRET` and redeploy.
6. **Verify the logs.** The startup `[Config]` lines should show Stripe and
Email as `configured`.

## Quick checklist

- [ ] `NODE_ENV=production`
- [ ] `JWT_SECRET` set to a strong random value
- [ ] `FRONTEND_URL` = your Render URL
- [ ] Persistent disk at `/data` + `DB_PATH=/data/spectra.db`
- [ ] All 4 `STRIPE_*` vars set
- [ ] Stripe webhook endpoint created and `STRIPE_WEBHOOK_SECRET` set
- [ ] All 5 `SMTP_*` vars set
- [ ] `/api/health` returns ok and logs show Stripe + Email `configured`
Loading
Loading