Skip to content

Commit f06ffa3

Browse files
committed
Draft a Deplpyment.md
1 parent e65ac3c commit f06ffa3

File tree

2 files changed

+149
-60
lines changed

2 files changed

+149
-60
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,4 @@ See: `docs/GOOGLE_MAPS_APPROX_LOCATION.md`
137137
- Platform organization: `docs/PLATFORM_ORGANIZATION.md`
138138
- Google maps “approx street”: `docs/GOOGLE_MAPS_APPROX_LOCATION.md`
139139
- OpenRouter LLM extraction: `docs/OPENROUTER_LLM_EXTRACTION.md`
140-
- Deployment (Vercel + Cloud Run + Cloud SQL): `docs/DEPLOYMENT.md`
140+
- Deployment runbook (GCP managed + self-hosted): `docs/DEPLOYMENT.md`

docs/DEPLOYMENT.md

Lines changed: 148 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,206 @@
1-
# Deployment (Vercel + Cloud Run + Cloud SQL Postgres)
1+
# Deployment (Engineering Runbook)
22

3-
This doc describes a simple production setup for EasyRelocate:
4-
- **Frontend**: Vercel (static hosting for the Vite app)
5-
- **Backend**: Google Cloud Run (FastAPI)
6-
- **Database**: Cloud SQL for PostgreSQL
7-
- **Auth**: **admin-created workspace tokens** (no user accounts)
3+
This doc describes two production-minded deployment options for EasyRelocate:
84

9-
## 1) Create Cloud SQL Postgres
5+
**Option A (Managed GCP)**
6+
- Frontend: **Vercel**
7+
- Backend: **Cloud Run**
8+
- Database: **Cloud SQL Postgres**
109

11-
Create a Cloud SQL Postgres instance and a database (example: `easyrelocate`), plus a DB user.
10+
**Option B (Self-hosted server)**
11+
- Frontend: **Vercel**
12+
- Backend + Database: **your miniPC / server** (Docker Compose)
13+
- Reverse proxy: **Caddy/Nginx** (HTTPS)
14+
- Optional exposure: **FRP**
1215

13-
You will need:
16+
Both options use the same app code. The only differences are environment variables (mainly `DATABASE_URL` and CORS).
17+
18+
## Architecture / trust boundaries
19+
20+
- The **browser** (web app + extension) talks to your backend via HTTP(S).
21+
- The backend is stateless (Cloud Run or container on a server).
22+
- All state lives in the database (SQLite for local dev, Postgres for production).
23+
- Auth is **workspace tokens**:
24+
- `Authorization: Bearer er_ws_...`
25+
- No user accounts.
26+
27+
## Configuration checklist
28+
29+
### Frontend (Vercel)
30+
- `VITE_API_BASE_URL` = your backend public URL (no trailing slash)
31+
- `VITE_GOOGLE_MAPS_API_KEY` = browser key (restrict by HTTP referrer to your domain)
32+
33+
### Backend (Cloud Run or server)
34+
- `DATABASE_URL` (Postgres in production; SQLite only for local dev)
35+
- `CORS_ALLOW_ORIGINS` = comma-separated list of allowed web origins (your Vercel domain(s))
36+
- Optional keys:
37+
- `GOOGLE_MAPS_API_KEY` (server-side geocoding)
38+
- `OPENROUTER_API_KEY` and `OPENROUTER_MODEL`
39+
- Optional self-serve onboarding:
40+
- `ENABLE_PUBLIC_WORKSPACE_ISSUE=1`
41+
- `PUBLIC_WORKSPACE_TTL_DAYS=30`
42+
43+
## Option A — Vercel + Cloud Run + Cloud SQL Postgres
44+
45+
### A1) Create Cloud SQL Postgres
46+
47+
Create:
48+
- a Cloud SQL instance (Postgres),
49+
- a database (e.g. `easyrelocate`),
50+
- a DB user (e.g. `easyrelocate_user`).
51+
52+
Record:
53+
- `DB_NAME`
1454
- `DB_USER`
1555
- `DB_PASSWORD`
16-
- `DB_NAME`
1756
- `INSTANCE_CONNECTION_NAME` (format: `project:region:instance`)
1857

19-
## 2) Deploy backend to Cloud Run
58+
### A2) Deploy backend to Cloud Run
2059

21-
### Backend env vars (Cloud Run)
60+
1) Containerize the backend (Docker image).
61+
2) Deploy the image to Cloud Run.
62+
3) Attach the Cloud SQL instance to the Cloud Run service (Cloud SQL connector).
63+
64+
#### A2.1) Backend env vars (Cloud Run)
2265

2366
Set these as Cloud Run environment variables:
24-
- `DATABASE_URL` (Postgres)
25-
- `CORS_ALLOW_ORIGINS` (comma-separated list, usually your Vercel domain(s))
26-
- `GOOGLE_MAPS_API_KEY` (optional, for server-side geocoding)
27-
- `OPENROUTER_API_KEY` (optional, for “Add selected post” extraction)
28-
- `OPENROUTER_MODEL` (optional; default `z-ai/glm-4.5-air:free`)
29-
- `ENABLE_PUBLIC_WORKSPACE_ISSUE` (optional; enable anonymous 30-day tokens)
30-
- `PUBLIC_WORKSPACE_TTL_DAYS` (optional; default `30`)
67+
- `DATABASE_URL`
68+
- `CORS_ALLOW_ORIGINS`
69+
- optional feature keys (`GOOGLE_MAPS_API_KEY`, `OPENROUTER_API_KEY`, etc.)
3170

32-
#### `DATABASE_URL` examples
71+
#### A2.2) `DATABASE_URL` examples
3372

34-
If you connect Cloud Run to Cloud SQL using the Cloud SQL connector (recommended), you can use a Unix socket host:
73+
Cloud SQL connector / Unix socket (recommended on Cloud Run):
3574
```text
3675
postgresql+psycopg://DB_USER:DB_PASSWORD@/DB_NAME?host=/cloudsql/INSTANCE_CONNECTION_NAME
3776
```
3877

39-
If you use a private IP / direct host:
78+
Private IP / direct host:
4079
```text
4180
postgresql+psycopg://DB_USER:DB_PASSWORD@DB_HOST:5432/DB_NAME
4281
```
4382

44-
### CORS (`CORS_ALLOW_ORIGINS`)
83+
#### A2.3) CORS allowlist (`CORS_ALLOW_ORIGINS`)
4584

4685
Example:
4786
```text
4887
https://your-vercel-app.vercel.app,https://easyrelocate.yourdomain.com
4988
```
5089

51-
Notes:
52-
- If you change your Vercel preview URLs often, add the stable custom domain (recommended).
53-
- Local dev still defaults to `http://127.0.0.1:5173` + `http://localhost:5173` when `CORS_ALLOW_ORIGINS` is not set.
90+
Operational note:
91+
- Prefer a stable custom domain. Vercel preview URLs change frequently.
5492

55-
## 3) Deploy frontend to Vercel
93+
### A3) Deploy frontend to Vercel
5694

57-
### Frontend env vars (Vercel)
95+
Set Vercel env vars:
96+
```text
97+
VITE_API_BASE_URL=https://YOUR_CLOUD_RUN_URL
98+
VITE_GOOGLE_MAPS_API_KEY=YOUR_BROWSER_KEY
99+
```
58100

59-
Set these in Vercel project settings:
60-
- `VITE_API_BASE_URL` (your Cloud Run URL, without trailing slash)
61-
- `VITE_GOOGLE_MAPS_API_KEY` (browser key; HTTP referrer-restricted to your Vercel domain)
101+
### A4) Create workspace tokens
62102

63-
Example:
103+
EasyRelocate does not ship a signup/login flow. You have two choices:
104+
105+
#### A4.1) Admin-created tokens (recommended)
106+
Run the script against the same Postgres `DATABASE_URL` the backend uses:
107+
```bash
108+
cd backend
109+
DATABASE_URL="postgresql+psycopg://..." python scripts/create_workspace.py --ttl-days 30
110+
```
111+
112+
Output:
64113
```text
65-
VITE_API_BASE_URL=https://easyrelocate-api-xxxxx-uc.a.run.app
114+
workspace_id=...
115+
workspace_token=er_ws_...
116+
expires_at=...
66117
```
67118

68-
## 4) Create admin workspace tokens
119+
Users paste the token into:
120+
- Web app: Compare page → Workspace panel → Save
121+
- Chrome extension: Options → Workspace token → Save (use the same token)
69122

70-
EasyRelocate does not ship a user signup/login flow. Instead, you (admin) create “workspace tokens”
71-
and distribute them to users. All reads/writes are scoped to a workspace.
123+
#### A4.2) Self-serve tokens (optional)
124+
Enable:
125+
```text
126+
ENABLE_PUBLIC_WORKSPACE_ISSUE=1
127+
PUBLIC_WORKSPACE_TTL_DAYS=30
128+
```
72129

73-
If you prefer a self-serve onboarding flow (no admin distribution), you can enable:
74-
- `ENABLE_PUBLIC_WORKSPACE_ISSUE=1`
75-
- `PUBLIC_WORKSPACE_TTL_DAYS=30`
130+
Then the web UI can call `POST /api/workspaces/issue` to mint tokens.
76131

77-
Then the web UI can call `POST /api/workspaces/issue` to generate a new 30-day token.
132+
Security note:
133+
- This endpoint is public. In production, add protection (rate limiting / bot protection) before
134+
sharing widely.
78135

79-
Production warning: this endpoint is public — protect it with rate limiting / bot protection.
136+
## Option B — Vercel + Self-hosted server (miniPC) + Postgres
80137

81-
### Create a token (recommended workflow)
138+
This is the lowest “monthly bill” option if you already have hardware, but you must handle:
139+
patching, backups, and uptime.
82140

83-
Run the script against the same `DATABASE_URL` your backend uses:
84-
```bash
85-
cd backend
86-
DATABASE_URL="postgresql+psycopg://..." python scripts/create_workspace.py
141+
### B1) Run Postgres + backend on the server
142+
143+
Recommended:
144+
- Use Docker Compose.
145+
- Keep Postgres private (not exposed to the internet).
146+
- Expose only the reverse proxy (80/443).
147+
148+
Minimum you need:
149+
- A public backend URL (e.g. `https://api.example.com`)
150+
- `DATABASE_URL` pointing at the Postgres container
151+
152+
Example `DATABASE_URL` (Compose network):
153+
```text
154+
postgresql+psycopg://easyrelocate_user:DB_PASSWORD@postgres:5432/easyrelocate
87155
```
88156

89-
Output:
157+
### B2) Reverse proxy + HTTPS
158+
159+
Use Caddy or Nginx to terminate HTTPS and proxy to the backend container.
160+
161+
If you use FRP:
162+
- Prefer mapping FRP to 443 with TLS termination at the server (Caddy/Nginx).
163+
- If FRP terminates TLS upstream, ensure the backend sees correct headers and set a strict CORS allowlist.
164+
165+
### B3) Vercel config
166+
167+
Set:
90168
```text
91-
workspace_id=...
92-
workspace_token=er_ws_...
169+
VITE_API_BASE_URL=https://api.example.com
93170
```
94171

95-
Treat `workspace_token` like a password.
172+
And on the backend:
173+
```text
174+
CORS_ALLOW_ORIGINS=https://your-vercel-domain.vercel.app,https://your-custom-domain.com
175+
```
96176

97-
### Where users paste the token
177+
### B4) Backups (required)
98178

99-
- **Web app**: Compare page → **Workspace** panel → paste token → Save.
100-
- **Chrome extension**: Extension options → **Workspace token** → Save (use the same token).
179+
At minimum:
180+
- daily `pg_dump`
181+
- keep 7–30 days of backups
182+
- periodically test restore
101183

102-
All API calls send `Authorization: Bearer <workspace_token>`.
184+
## Troubleshooting
103185

104-
## 5) Production notes
186+
### Frontend loads but API calls hang / abort
187+
- Confirm `VITE_API_BASE_URL` is correct and reachable from the browser.
188+
- Prefer `http://127.0.0.1:8000` for local dev (avoid IPv6-only `localhost` issues).
105189

106-
- Cloud Run instances are ephemeral; use Postgres in production (avoid relying on local SQLite files).
107-
- You’ll eventually want migrations (e.g., Alembic) instead of `create_all`, but `create_all` is fine
108-
for early versions.
190+
### 401 “Missing workspace token”
191+
- Ensure the workspace token is saved in:
192+
- Web app Compare page → Workspace panel → Save
193+
- Extension Options → Workspace token → Save
194+
195+
### CORS errors in browser console
196+
- Set backend `CORS_ALLOW_ORIGINS` to include your frontend origin(s).
109197

110198
## Local vs cloud DB (dev convenience)
199+
111200
If you want to keep both DB URLs in one `.env`, you can set:
112201
- `DATABASE_URL_LOCAL=...`
113202
- `DATABASE_URL_CLOUD=...`
114203
- `EASYRELOCATE_DB=local|cloud`
115204

116-
For local dev, you can also use the helper script:
205+
For local dev, you can also use:
117206
`./easyDeploy.sh --db local`.

0 commit comments

Comments
 (0)