mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-11 20:00:23 +01:00
4.7 KiB
4.7 KiB
Production Setup (Cloudflare Workers)
This app deploys two Workers:
@linsa/web: SSR app + API routes + Electric proxy@linsa/worker: standalone API bound in@linsa/webasWORKER_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
wranglerlogged 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
- Sign up at Electric Cloud or point to your own Electric instance.
- Create a source connected to your Postgres.
- Note:
ELECTRIC_URL– Electric endpoint (shape API)ELECTRIC_SOURCE_IDandELECTRIC_SOURCE_SECRET– only if Electric Cloud auth is enabled
3) Cloudflare Worker configuration
- Optional: rename the
namefields inpackages/worker/wrangler.jsoncandpackages/web/wrangler.jsonc. If you rename the worker, also updateservices[0].serviceinpackages/web/wrangler.jsoncso theWORKER_RPCbinding still points to the right script. - Set secrets for
@linsa/web(run insidepackages/web):
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
wrangler secret put JAZZ_SPOTIFY_STATE_ID # optional, for Spotify now playing
- Set non-secret vars:
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
pnpmwrappers if you want to stay in the monorepo context:
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:
pnpm deploy:worker # deploy @linsa/worker
pnpm deploy:web # build + deploy @linsa/web
# or
pnpm deploy # deploy both
# Flow shortcut
f deploy
5) Verify
- Open your production URL and confirm auth flows (sign up / sign in).
- Create a chat thread/message; check Electric sync across two tabs.
- Hit
/api/chat/aito confirm OpenRouter responses (or expect the demo reply when no key is set). - 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) |
JAZZ_SPOTIFY_STATE_ID |
No | Jazz Spotify state id (from x/server) for now playing proxy |
Troubleshooting
- Auth:
APP_BASE_URLmust match your deployed origin; rotateBETTER_AUTH_SECRETonly 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
wherefilters in logs if shapes look empty. - AI chat: set
OPENROUTER_API_KEY; without it you’ll see the demo reply instead of model output.