Migrate env variables to Cloudron

This commit is contained in:
adilallo
2026-05-22 15:50:33 -06:00
parent c663e051da
commit 8137593aa0
16 changed files with 165 additions and 42 deletions
+5 -5
View File
@@ -24,7 +24,7 @@ Use this if you **do not** have SSH or hosting access yet. Most engineering tick
### You do **not** need the server admin for
- **Tickets 18, 10:** Everything runs on your machine: `docker compose up -d postgres mailhog`, `.env`, `npm run dev`, `npx prisma migrate dev`. **Magic-link** sign-in email can use Mailhog or **dev server logs** (verify URL) when `SMTP_URL` is unset—no real SMTP required locally.
- **Tickets 18, 10:** Everything runs on your machine: `docker compose up -d postgres mailhog`, `.env`, `npm run dev`, `npx prisma migrate dev`. **Magic-link** sign-in email can use Mailhog or **dev server logs** (verify URL) when `CLOUDRON_MAIL_SMTP_*` is unset—no real SMTP required locally.
- **Verifying APIs:** Use `localhost` and the same Docker Postgres—no production host.
### The **first** time you need someone with hosting access
@@ -35,10 +35,10 @@ Ask the admin to provide (or do for you) the items below—**Ticket 12** turns t
| What | Why you need it |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Postgres** | Managed instance or container; a **`DATABASE_URL`** you can plug into the deployed app. |
| **Postgres** | Managed instance or container; a **`CLOUDRON_POSTGRESQL_URL`** you can plug into the deployed app. |
| **Run migrations** | Someone runs **`npx prisma migrate deploy`** against that database **before** the new app version serves traffic (or gives you a secure way to run it in CI/CD). |
| **`SESSION_SECRET`** | Long random string in production env (sessions **+ hashed magic-link tokens**). |
| **SMTP** | **`SMTP_URL`** + **`SMTP_FROM`** for real **sign-in link** email; not required on laptop if you use logs/Mailhog. |
| **SMTP** | **`CLOUDRON_MAIL_SMTP_*`** + **`SMTP_FROM`** for real **sign-in link** email; not required on laptop if you use logs/Mailhog. |
| **DNS for mail** | Often **SPF/DKIM** so **magic-link** messages are not spam—admin or whoever owns DNS. |
| **TLS + hostname** | HTTPS URL for the site; reverse proxy (nginx, Caddy, etc.) in front of Node. |
| **Health check** | Load balancer or platform should probe **`GET /api/health`** (or your chosen path). |
@@ -137,7 +137,7 @@ Optional: **Docker image deploy** using the repo [Dockerfile](Dockerfile)—admi
2. Flow: email → “Send link” → user opens link (email, Mailhog, or dev log) → `GET /api/auth/magic-link/verify?token=...` sets session and redirects; optional `next` for post-login path.
3. Surface API errors: invalid email, 429 `retryAfterMs`, expired/invalid token, network failure (accessible copy).
4. Ensure `fetch` calls use `credentials: "include"` where needed (see [lib/create/api.ts](lib/create/api.ts)).
5. **Dev:** without `SMTP_URL`, verify URL is logged; with Mailhog, use [docker-compose.yml](docker-compose.yml) and `SMTP_URL=smtp://localhost:1025`.
5. **Dev:** without `CLOUDRON_MAIL_SMTP_*`, verify URL is logged; with Mailhog, use [docker-compose.yml](docker-compose.yml) and `CLOUDRON_MAIL_SMTP_SERVER=localhost` + `CLOUDRON_MAIL_SMTP_PORT=1025`.
6. **Marketing header:** When signed in (`fetchAuthSession`), **Log in** becomes **Profile** linking to [`/profile`](app/(app)/profile/page.tsx) (placeholder until Ticket 15 / CR-86). Implemented in [TopWithPathname.tsx](app/components/navigation/Top/TopWithPathname.tsx) + [Top.container.tsx](app/components/navigation/Top/Top.container.tsx).
**Acceptance criteria:**
@@ -678,7 +678,7 @@ All six are titled `[Backend] …`, assigned to Vinod, in the **community-rule**
**Per-ticket detail:**
1. **Bridge `CLOUDRON_*` env vars to canonical names.** Cloudron injects `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_SERVER/PORT/USERNAME/PASSWORD`; the app reads `DATABASE_URL` / `SMTP_URL`. Recommended approach: read both names in [`lib/server/env.ts`](../../lib/server/env.ts) and assemble `SMTP_URL` from the four parts in [`lib/server/mail.ts`](../../lib/server/mail.ts) when only the Cloudron names are present. Alternative: a `start.sh` shim in the image. Acceptance: with only `CLOUDRON_*` set, app connects to DB and sends mail; with only canonical names set (current behavior), unchanged; unit tests cover both.
1. **Cloudron-native env vars (CR-96).** App reads `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only (no `DATABASE_URL` / `SMTP_URL` shim). Local dev uses the same names in `.env`. SMTP URL assembled in [`lib/server/env.ts`](../../lib/server/env.ts); mail senders use `getSmtpUrl()`. Acceptance: with only `CLOUDRON_*` set, app connects to DB and sends mail; unit tests in `tests/unit/env.test.ts`.
2. **Container image registry: choose, build, push.** Acceptance: `docker pull <registry>/communityrule:<tag>` works from a Cloudron-reachable network. CI builds and pushes on merge to `main` (stretch).
3. **Cloudron staging install + smoke.** Acceptance: `curl https://<staging>/api/health` returns `{"ok":true,"database":"connected"}`; magic-link request → click link → `GET /api/auth/session` returns a user; publishing a rule succeeds.
4. **Cloudron production install + DNS cutover.** Acceptance: production subdomain resolves to the new app; old subdomain still works during overlap; sign-in + publish succeed against production; backups confirmed.
+4 -4
View File
@@ -76,7 +76,7 @@ Full table: [CONTRIBUTING.md](../CONTRIBUTING.md) **API routes**.
**Step 2.** Use **Prisma**`schema.prisma`, `npx prisma migrate dev` / `migrate deploy`.
**Step 3.** Add **SMTP** (or Mailhog locally) for **magic-link** sign-in email in deployed environments; when `SMTP_URL` is unset in dev, the app can log the **verify URL** to the console (same pattern as [`lib/server/mail.ts`](lib/server/mail.ts)).
**Step 3.** Add **SMTP** (or Mailhog locally) for **magic-link** sign-in email in deployed environments; when `CLOUDRON_MAIL_SMTP_*` is unset in dev, the app can log the **verify URL** to the console (same pattern as [`lib/server/mail.ts`](lib/server/mail.ts)).
**Step 4.** **Redis / queues / Kubernetes** — not required for v1. **Exception:** before running **multiple app instances**, plan a **shared rate-limit store** (often Redis) for **passwordless email (magic-link request)**; the current limiter is in-memory per process ([`lib/server/rateLimit.ts`](lib/server/rateLimit.ts)).
@@ -157,7 +157,7 @@ Match the current API behavior; tighten as product evolves:
---
**Step 1.** Copy `.env.example` to `.env`. Set `DATABASE_URL` and secrets (see file comments).
**Step 1.** Copy `.env.example` to `.env`. Set `CLOUDRON_POSTGRESQL_URL` and secrets (see file comments).
**Step 2.** Start Postgres locally:
@@ -183,7 +183,7 @@ npm run dev
**Step 6.** **Magic-link sign-in** (happy path):
1. `POST /api/auth/magic-link/request` with `{ "email": "you@example.com" }` (optional `"next"` for redirect after verify).
2. Open the link from email, Mailhog, or **server logs** when `SMTP_URL` is unset (dev).
2. Open the link from email, Mailhog, or **server logs** when `CLOUDRON_MAIL_SMTP_*` is unset (dev).
3. Browser hits `GET /api/auth/magic-link/verify?token=...` (and optional `next=...`); response sets the session cookie and redirects.
4. `GET /api/auth/session` should show your user in the same browser.
@@ -221,7 +221,7 @@ npm run dev
**Optional QA:** Run automated tests against an **ephemeral** database in CI instead of maintaining a fourth long-lived server.
**Target platform:** **Cloudron at MEDLab** — same host as the legacy [`CommunityRule/CommunityRuleBackend`](https://git.medlab.host/CommunityRule/CommunityRuleBackend) (Express + MySQL). The new app is packaged as a proper Cloudron app (Docker image + `CloudronManifest.json`, **postgresql + sendmail + localstorage** addons). Cloudron's container supervisor replaces the legacy 30-min `run.sh` watchdog. Admin handoff (access, env vars, platform settings, open decisions): [`docs/guides/ops-backend-deploy.md`](ops-backend-deploy.md). Note: Cloudron injects `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*`; the app reads `DATABASE_URL` / `SMTP_URL`, so a small env-var bridge in [`lib/server/env.ts`](../../lib/server/env.ts) / [`lib/server/mail.ts`](../../lib/server/mail.ts) is needed (tracked in [**CR-96**](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names), filed under CR-83 — see [backend-linear-tickets.md](backend-linear-tickets.md) Ticket 12 follow-ups).
**Target platform:** **Cloudron at MEDLab** — same host as the legacy [`CommunityRule/CommunityRuleBackend`](https://git.medlab.host/CommunityRule/CommunityRuleBackend) (Express + MySQL). The new app is packaged as a proper Cloudron app (Docker image + `CloudronManifest.json`, **postgresql + sendmail + localstorage** addons). Cloudron's container supervisor replaces the legacy 30-min `run.sh` watchdog. Admin handoff (access, env vars, platform settings, open decisions): [`docs/guides/ops-backend-deploy.md`](ops-backend-deploy.md). The app reads Cloudron-injected `CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` via [`lib/server/env.ts`](../../lib/server/env.ts) (CR-96).
**Admin / infra (coordinate with whoever runs the server):**
+6 -6
View File
@@ -58,13 +58,13 @@ Cloudron addons are not "enabled" platform-wide; they are requested
per-app in the manifest and provisioned at install time.
- `CLOUDRON_POSTGRESQL_URL` — from the **postgresql** addon. The app
reads `DATABASE_URL`; bridging is a small in-app code change (see
§8 [CR-96](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names)).
reads this name directly (Prisma + [`lib/server/env.ts`](../../lib/server/env.ts)).
- `CLOUDRON_MAIL_SMTP_SERVER` / `_PORT` / `_USERNAME` / `_PASSWORD`
from the **sendmail** addon. The platform Mail server is configured
for `communityrule.info` with **Amazon SES relay** + "allow custom
from address" on, so `SMTP_FROM` of our choice will deliver. The
app reads `SMTP_URL`; bridged the same way.
from address" on, so `SMTP_FROM` of our choice will deliver. The app
assembles a Nodemailer transport URL from these four vars in
[`lib/server/env.ts`](../../lib/server/env.ts).
### I set manually via `cloudron configure --app <id> --set-env`
@@ -188,8 +188,8 @@ All filed in Linear, titled `[Backend] …`, assigned to me, in the
**Community-rule** team, **Backlog** state.
1. [**CR-96**](https://linear.app/community-rule/issue/CR-96/backend-bridge-cloudron-env-vars-to-canonical-names)
— `[Backend] Bridge CLOUDRON_* env vars to canonical names`. No
blockers; can land now.
— `[Backend] Cloudron-native env vars` (shipped: app reads
`CLOUDRON_POSTGRESQL_URL` and `CLOUDRON_MAIL_SMTP_*` only).
2. [**CR-97**](https://linear.app/community-rule/issue/CR-97/backend-container-image-registry-choose-build-push)
— `[Backend] Container image registry: choose, build, push`.
Blocked by registry decision (§6.3).
@@ -788,7 +788,7 @@ and return all rows.
score-0 templates still present at the end in curated order.
- [x] No-facets `GET /api/templates` matches today's curated ordering
(no regression for the existing marketing/templates surfaces).
- [x] DB-down smoke: with `DATABASE_URL` unset, the four wizard
- [x] DB-down smoke: with `CLOUDRON_POSTGRESQL_URL` unset, the four wizard
card-deck steps still render the full deck from messages (no
5xx, no broken cards).
- [x] Editing a `data/create/customRule/<section>.json` entry and
+1 -1
View File
@@ -26,7 +26,7 @@ Starts a throwaway Postgres on `127.0.0.1:5433`, runs `prisma migrate
deploy`, checks the connection, then removes the container. Port **5433**
avoids clashing with `docker compose` on **5432**. If you already use
Compose on 5432: `docker compose up -d postgres` then
`DATABASE_URL=postgresql://communityrule:communityrule@127.0.0.1:5432/communityrule npm run db:deploy`.
`CLOUDRON_POSTGRESQL_URL=postgresql://communityrule:communityrule@127.0.0.1:5432/communityrule npm run db:deploy`.
Do not rewrite migrations already applied to shared DBs — see
[CONTRIBUTING.md](../CONTRIBUTING.md) and