This commit is contained in:
Nikita
2025-12-21 13:37:19 -08:00
commit 8cd4b943a5
173 changed files with 44266 additions and 0 deletions

87
docs/production-setup.md Normal file
View File

@@ -0,0 +1,87 @@
# Production Setup (Cloudflare Workers)
This app deploys two Workers:
- `@linsa/web`: SSR app + API routes + Electric proxy
- `@linsa/worker`: standalone API bound in `@linsa/web` as `WORKER_RPC`
The stack expects Postgres over HTTP (Neon or a Postgres behind a Neon HTTP proxy) and ElectricSQL for sync.
## Prerequisites
- Cloudflare account with Workers enabled and `wrangler` logged in
- Production Postgres reachable over HTTP (Neon recommended, or a Postgres behind a Neon HTTP proxy)
- Electric Cloud account (or self-hosted Electric instance)
- Domain for cookies (`APP_BASE_URL`)
- OpenRouter API key (optional, for AI responses)
## 1) Database (Postgres)
- Create a Neon database (recommended) and copy the `postgresql://...neon.tech/...` connection string.
- Ensure logical replication (`wal_level=logical`) is enabled. Neon enables it by default; for other Postgres, enable it and allow replication access.
- Electric needs replication on these tables: `users`, `sessions`, `accounts`, `verifications`, `chat_threads`, `chat_messages`.
- If not using Neon, expose your Postgres through a Neon HTTP proxy; Cloudflare Workers cannot talk to raw TCP Postgres.
## 2) Electric Cloud / Self-hosted Electric
1. Sign up at [Electric Cloud](https://electric-sql.com/product/cloud) or point to your own Electric instance.
2. Create a source connected to your Postgres.
3. Note:
- `ELECTRIC_URL` Electric endpoint (shape API)
- `ELECTRIC_SOURCE_ID` and `ELECTRIC_SOURCE_SECRET` only if Electric Cloud auth is enabled
## 3) Cloudflare Worker configuration
- Optional: rename the `name` fields in `packages/worker/wrangler.jsonc` and `packages/web/wrangler.jsonc`. If you rename the worker, also update `services[0].service` in `packages/web/wrangler.jsonc` so the `WORKER_RPC` binding still points to the right script.
- Set secrets for `@linsa/web` (run inside `packages/web`):
```bash
cd packages/web
wrangler secret put DATABASE_URL # Neon/Postgres HTTP URL
wrangler secret put BETTER_AUTH_SECRET # generate with: openssl rand -hex 32
wrangler secret put ELECTRIC_URL # e.g., https://your-electric-host/v1/shape
wrangler secret put ELECTRIC_SOURCE_ID # only if Electric Cloud auth is on
wrangler secret put ELECTRIC_SOURCE_SECRET # only if Electric Cloud auth is on
wrangler secret put OPENROUTER_API_KEY # optional, for real AI replies
```
- Set non-secret vars:
```bash
wrangler vars set APP_BASE_URL https://your-domain.com # exact origin for cookies
wrangler vars set OPENROUTER_MODEL anthropic/claude-sonnet-4 # optional override
```
- Prefer `pnpm` wrappers if you want to stay in the monorepo context:
```bash
pnpm --filter @linsa/web exec wrangler whoami
```
You can also run `f deploy-setup` from the repo root for an interactive secret setup.
## 4) Deploy
From the repo root:
```bash
pnpm deploy:worker # deploy @linsa/worker
pnpm deploy:web # build + deploy @linsa/web
# or
pnpm deploy # deploy both
# Flow shortcut
f deploy
```
## 5) Verify
1. Open your production URL and confirm auth flows (sign up / sign in).
2. Create a chat thread/message; check Electric sync across two tabs.
3. Hit `/api/chat/ai` to confirm OpenRouter responses (or expect the demo reply when no key is set).
4. Tail logs if needed: `pnpm --filter @linsa/web exec wrangler tail`.
## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `DATABASE_URL` | Yes | Postgres URL reachable over HTTP (Neon or Postgres behind Neon proxy) |
| `BETTER_AUTH_SECRET` | Yes | Secret for auth/session signing (32+ chars) |
| `ELECTRIC_URL` | Yes | Electric Cloud/self-host URL (shape endpoint) |
| `ELECTRIC_SOURCE_ID` | Conditional | Needed when Electric Cloud auth is enabled |
| `ELECTRIC_SOURCE_SECRET` | Conditional | Needed with `ELECTRIC_SOURCE_ID` |
| `APP_BASE_URL` | Yes | Production origin for cookies/CORS (e.g., https://app.example.com) |
| `OPENROUTER_API_KEY` | No | Enables real AI responses |
| `OPENROUTER_MODEL` | No | AI model id (default: `anthropic/claude-sonnet-4`) |
## Troubleshooting
- Auth: `APP_BASE_URL` must match your deployed origin; rotate `BETTER_AUTH_SECRET` only when you intend to invalidate sessions.
- Database: use an HTTP-capable connection string; ensure logical replication is on and tables exist; allow Cloudflare egress to the DB host.
- Electric: confirm the source is healthy and credentials are set; verify `where` filters in logs if shapes look empty.
- AI chat: set `OPENROUTER_API_KEY`; without it youll see the demo reply instead of model output.