@rebasepro/cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/commands/api-keys.d.ts +1 -0
- package/dist/commands/generate_sdk.d.ts +1 -1
- package/dist/commands/init.d.ts +13 -0
- package/dist/index.es.js +463 -105
- package/dist/index.es.js.map +1 -1
- package/package.json +22 -18
- package/templates/template/backend/package.json +0 -1
- package/templates/template/backend/src/index.ts +1 -1
- package/templates/template/config/collections/posts.ts +1 -3
- package/templates/template/config/collections/tags.ts +1 -3
- package/templates/template/config/collections/users.ts +1 -4
- package/templates/template/package.json +0 -1
- package/templates/template/scripts/example.ts +4 -1
- package/dist/index.cjs +0 -1948
- package/dist/index.cjs.map +0 -1
- package/skills/rebase-api/SKILL.md +0 -662
- package/skills/rebase-api/references/.gitkeep +0 -3
- package/skills/rebase-auth/SKILL.md +0 -1143
- package/skills/rebase-auth/references/.gitkeep +0 -3
- package/skills/rebase-backend-postgres/SKILL.md +0 -633
- package/skills/rebase-backend-postgres/references/.gitkeep +0 -3
- package/skills/rebase-basics/SKILL.md +0 -749
- package/skills/rebase-basics/references/.gitkeep +0 -3
- package/skills/rebase-collections/SKILL.md +0 -1328
- package/skills/rebase-collections/references/.gitkeep +0 -3
- package/skills/rebase-cron-jobs/SKILL.md +0 -699
- package/skills/rebase-cron-jobs/references/.gitkeep +0 -1
- package/skills/rebase-custom-functions/SKILL.md +0 -233
- package/skills/rebase-deployment/SKILL.md +0 -583
- package/skills/rebase-deployment/references/.gitkeep +0 -3
- package/skills/rebase-design-language/SKILL.md +0 -664
- package/skills/rebase-email/SKILL.md +0 -701
- package/skills/rebase-email/references/.gitkeep +0 -1
- package/skills/rebase-entity-history/SKILL.md +0 -485
- package/skills/rebase-entity-history/references/.gitkeep +0 -1
- package/skills/rebase-local-env-setup/SKILL.md +0 -189
- package/skills/rebase-local-env-setup/references/.gitkeep +0 -3
- package/skills/rebase-realtime/SKILL.md +0 -755
- package/skills/rebase-realtime/references/.gitkeep +0 -3
- package/skills/rebase-sdk/SKILL.md +0 -594
- package/skills/rebase-sdk/references/.gitkeep +0 -0
- package/skills/rebase-storage/SKILL.md +0 -765
- package/skills/rebase-storage/references/.gitkeep +0 -3
- package/skills/rebase-studio/SKILL.md +0 -746
- package/skills/rebase-studio/references/.gitkeep +0 -3
- package/skills/rebase-ui-components/SKILL.md +0 -1411
- package/skills/rebase-ui-components/references/.gitkeep +0 -3
- package/skills/rebase-webhooks/SKILL.md +0 -623
- package/skills/rebase-webhooks/references/.gitkeep +0 -1
- package/templates/template/backend/drizzle.config.ts +0 -51
|
@@ -1,583 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: rebase-deployment
|
|
3
|
-
description: Guide for deploying Rebase applications. Use this skill when the user needs to deploy to Rebase Cloud, set up Docker, configure Firebase Hosting, or self-host Rebase.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Rebase Deployment
|
|
7
|
-
|
|
8
|
-
Rebase supports multiple deployment strategies — from fully managed Rebase Cloud to self-hosted Docker deployments.
|
|
9
|
-
|
|
10
|
-
## Deployment Options
|
|
11
|
-
|
|
12
|
-
| Option | Best For | Complexity |
|
|
13
|
-
|--------|----------|------------|
|
|
14
|
-
| **Rebase Cloud** | Fastest setup, managed infrastructure | ⭐ Easy |
|
|
15
|
-
| **Docker** | Full control, self-hosted | ⭐⭐ Medium |
|
|
16
|
-
| **Firebase Hosting** | Static frontend + Cloud Functions backend | ⭐⭐ Medium |
|
|
17
|
-
| **Custom** | Any Node.js hosting (Railway, Render, Fly.io, etc.) | ⭐⭐⭐ Advanced |
|
|
18
|
-
|
|
19
|
-
## Rebase Cloud
|
|
20
|
-
|
|
21
|
-
The simplest deployment path. Sign up at [app.rebase.pro](https://app.rebase.pro).
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# 1. Authenticate
|
|
25
|
-
rebase login
|
|
26
|
-
|
|
27
|
-
# 2. Initialize (if new project)
|
|
28
|
-
rebase init
|
|
29
|
-
|
|
30
|
-
# 3. Deploy
|
|
31
|
-
rebase deploy
|
|
32
|
-
|
|
33
|
-
# Deploy to dev environment
|
|
34
|
-
rebase deploy --env dev
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Docker (Self-Hosted)
|
|
40
|
-
|
|
41
|
-
### Production Dockerfile (Multi-Stage)
|
|
42
|
-
|
|
43
|
-
The production Dockerfile is a **multi-stage build**. The build context is the **monorepo root** (where `pnpm-workspace.yaml` lives), not the app directory.
|
|
44
|
-
|
|
45
|
-
> **IMPORTANT FOR AGENTS:** The Dockerfile uses `node:22-alpine`, `corepack enable` (NOT `npm install -g pnpm`), and requires the monorepo root as the build context. Never generate a single-stage Dockerfile or use `node:20`.
|
|
46
|
-
|
|
47
|
-
```dockerfile
|
|
48
|
-
# ── Stage 1: Install + Build ─────────────────────────────────────────
|
|
49
|
-
FROM node:22-alpine AS builder
|
|
50
|
-
|
|
51
|
-
ENV PNPM_HOME="/pnpm"
|
|
52
|
-
ENV PATH="$PNPM_HOME:$PATH"
|
|
53
|
-
ENV CI=true
|
|
54
|
-
RUN corepack enable
|
|
55
|
-
|
|
56
|
-
RUN apk add --no-cache python3 make g++
|
|
57
|
-
|
|
58
|
-
WORKDIR /app
|
|
59
|
-
|
|
60
|
-
# Copy workspace root
|
|
61
|
-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
62
|
-
|
|
63
|
-
# Copy source packages and app
|
|
64
|
-
COPY packages ./packages
|
|
65
|
-
COPY app ./app
|
|
66
|
-
|
|
67
|
-
# Install all dependencies with flat node_modules for Docker compatibility
|
|
68
|
-
# (many packages rely on hoisted devDependencies like @vitejs/plugin-react)
|
|
69
|
-
RUN pnpm install --shamefully-hoist
|
|
70
|
-
|
|
71
|
-
# Build all packages using pnpm recursive with --no-bail.
|
|
72
|
-
# Some packages may fail tsc declarations — that's fine, we only need vite bundles + esbuild outputs.
|
|
73
|
-
RUN pnpm --filter './packages/*' -r --no-bail run build; exit 0
|
|
74
|
-
|
|
75
|
-
# Build the backend (TypeScript → JavaScript), then resolve ESM import extensions
|
|
76
|
-
# 1. tsc compiles TS→JS 2. tsc-alias resolves path aliases 3. sed adds .js to remaining relative imports
|
|
77
|
-
RUN cd app/backend && npx tsc -p tsconfig.docker.json && npx tsc-alias -p tsconfig.docker.json -f \
|
|
78
|
-
&& find dist -name '*.js' -exec sed -i 's/from "\(\.[^"]*\)"/from "\1.js"/g; s/\.js\.js/.js/g' {} +
|
|
79
|
-
|
|
80
|
-
# Build frontend (reads .env.production for VITE_API_URL etc.)
|
|
81
|
-
RUN cd app/frontend && npx vite build
|
|
82
|
-
|
|
83
|
-
# Prune devDependencies to reduce image size
|
|
84
|
-
RUN pnpm install --shamefully-hoist --prod
|
|
85
|
-
|
|
86
|
-
# ── Stage 2: Production Runtime ──────────────────────────────────────
|
|
87
|
-
FROM node:22-alpine AS runtime
|
|
88
|
-
|
|
89
|
-
ENV PNPM_HOME="/pnpm"
|
|
90
|
-
ENV PATH="$PNPM_HOME:$PATH"
|
|
91
|
-
ENV NODE_ENV=production
|
|
92
|
-
|
|
93
|
-
RUN corepack enable
|
|
94
|
-
|
|
95
|
-
# Security: run as non-root
|
|
96
|
-
RUN addgroup -g 1001 rebase && adduser -u 1001 -G rebase -s /bin/sh -D rebase
|
|
97
|
-
|
|
98
|
-
WORKDIR /app
|
|
99
|
-
|
|
100
|
-
# Copy only production artifacts (node_modules already pruned of devDependencies)
|
|
101
|
-
COPY --from=builder /app/package.json /app/pnpm-lock.yaml /app/pnpm-workspace.yaml ./
|
|
102
|
-
COPY --from=builder /app/node_modules ./node_modules
|
|
103
|
-
COPY --from=builder /app/packages ./packages
|
|
104
|
-
COPY --from=builder /app/app ./app
|
|
105
|
-
|
|
106
|
-
# Uploads directory with correct ownership
|
|
107
|
-
RUN mkdir -p /app/app/uploads && chown -R rebase:rebase /app
|
|
108
|
-
|
|
109
|
-
ENV STORAGE_PATH=/app/app/uploads
|
|
110
|
-
|
|
111
|
-
USER rebase
|
|
112
|
-
|
|
113
|
-
WORKDIR /app/app/backend
|
|
114
|
-
EXPOSE 3001
|
|
115
|
-
|
|
116
|
-
HEALTHCHECK --interval=30s --timeout=5s --retries=3 --start-period=10s \
|
|
117
|
-
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1
|
|
118
|
-
|
|
119
|
-
# Copy the entrypoint script and drizzle config for auto-migration
|
|
120
|
-
COPY --from=builder /app/app/backend/entrypoint.sh ./entrypoint.sh
|
|
121
|
-
COPY --from=builder /app/app/backend/drizzle.config.ts ./drizzle.config.ts
|
|
122
|
-
COPY --from=builder /app/app/backend/drizzle ./drizzle
|
|
123
|
-
|
|
124
|
-
# Auto-migrate then start the compiled JavaScript backend
|
|
125
|
-
CMD ["sh", "entrypoint.sh"]
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Build command** (run from monorepo root):
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
docker build -t rebase-backend -f app/backend/Dockerfile .
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Docker Entrypoint (Auto-Migration)
|
|
135
|
-
|
|
136
|
-
The Docker image uses an entrypoint script (`entrypoint.sh`) that **automatically runs database migrations** before starting the server. Migrations are non-fatal — if they fail (e.g. already applied), the server still starts.
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
#!/bin/sh
|
|
140
|
-
set -e
|
|
141
|
-
|
|
142
|
-
echo "🔄 Running database migrations..."
|
|
143
|
-
npx drizzle-kit migrate --config=drizzle.config.ts 2>&1 || echo "⚠️ Migrations skipped or failed (non-fatal)"
|
|
144
|
-
|
|
145
|
-
echo "🚀 Starting Rebase backend..."
|
|
146
|
-
exec node dist/app/backend/src/index.js
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
> **IMPORTANT FOR AGENTS:** The entrypoint runs `drizzle-kit migrate` automatically on container start. Never add manual migration steps to a Dockerfile CMD — they are handled by `entrypoint.sh`.
|
|
150
|
-
|
|
151
|
-
### Docker Compose (Monorepo)
|
|
152
|
-
|
|
153
|
-
This is the monorepo-internal Docker Compose (`app/backend/docker-compose.yml`). Note: the build context is `../..` (monorepo root).
|
|
154
|
-
|
|
155
|
-
```yaml
|
|
156
|
-
services:
|
|
157
|
-
db:
|
|
158
|
-
image: postgres:18-alpine
|
|
159
|
-
restart: unless-stopped
|
|
160
|
-
environment:
|
|
161
|
-
POSTGRES_USER: rebase
|
|
162
|
-
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-rebasepassword}
|
|
163
|
-
POSTGRES_DB: rebase
|
|
164
|
-
ports:
|
|
165
|
-
- "5432:5432"
|
|
166
|
-
volumes:
|
|
167
|
-
- postgres_data:/var/lib/postgresql/data
|
|
168
|
-
healthcheck:
|
|
169
|
-
test: ["CMD-SHELL", "pg_isready -U rebase -d rebase"]
|
|
170
|
-
interval: 5s
|
|
171
|
-
timeout: 5s
|
|
172
|
-
retries: 10
|
|
173
|
-
start_period: 10s
|
|
174
|
-
command:
|
|
175
|
-
- "postgres"
|
|
176
|
-
- "-c"
|
|
177
|
-
- "shared_buffers=256MB"
|
|
178
|
-
- "-c"
|
|
179
|
-
- "max_connections=100"
|
|
180
|
-
- "-c"
|
|
181
|
-
- "log_min_duration_statement=1000"
|
|
182
|
-
|
|
183
|
-
backend:
|
|
184
|
-
build:
|
|
185
|
-
context: ../..
|
|
186
|
-
dockerfile: app/backend/Dockerfile
|
|
187
|
-
restart: unless-stopped
|
|
188
|
-
ports:
|
|
189
|
-
- "${PORT:-3001}:3001"
|
|
190
|
-
environment:
|
|
191
|
-
DATABASE_URL: postgresql://rebase:${POSTGRES_PASSWORD:-rebasepassword}@db:5432/rebase
|
|
192
|
-
ADMIN_CONNECTION_STRING: postgresql://rebase:${POSTGRES_PASSWORD:-rebasepassword}@db:5432/rebase
|
|
193
|
-
JWT_SECRET: ${JWT_SECRET:-super-secret-jwt-key-change-in-production}
|
|
194
|
-
NODE_ENV: ${NODE_ENV:-development}
|
|
195
|
-
PORT: "3001"
|
|
196
|
-
ALLOW_REGISTRATION: ${ALLOW_REGISTRATION:-true}
|
|
197
|
-
depends_on:
|
|
198
|
-
db:
|
|
199
|
-
condition: service_healthy
|
|
200
|
-
volumes:
|
|
201
|
-
- uploads:/app/app/backend/uploads
|
|
202
|
-
|
|
203
|
-
volumes:
|
|
204
|
-
postgres_data:
|
|
205
|
-
driver: local
|
|
206
|
-
uploads:
|
|
207
|
-
driver: local
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
> **WARNING FOR AGENTS:** The build context is `../..` (monorepo root), NOT `.` or `app/backend`. The Dockerfile path is relative to the monorepo root: `app/backend/Dockerfile`. The `depends_on` uses `condition: service_healthy` so the backend waits for postgres to be ready. **Never use `version: '3.8'`** — modern Docker Compose doesn't use the `version` key.
|
|
211
|
-
|
|
212
|
-
### Docker Compose (User Template)
|
|
213
|
-
|
|
214
|
-
For user-generated projects (created via `rebase init`), the Docker Compose file is at the project root. It includes separate backend + frontend services:
|
|
215
|
-
|
|
216
|
-
```yaml
|
|
217
|
-
services:
|
|
218
|
-
db:
|
|
219
|
-
image: postgres:18-alpine
|
|
220
|
-
restart: unless-stopped
|
|
221
|
-
environment:
|
|
222
|
-
POSTGRES_USER: rebase
|
|
223
|
-
POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-changeme}
|
|
224
|
-
POSTGRES_DB: rebase
|
|
225
|
-
ports:
|
|
226
|
-
- "5432:5432"
|
|
227
|
-
volumes:
|
|
228
|
-
- postgres_data:/var/lib/postgresql/data
|
|
229
|
-
healthcheck:
|
|
230
|
-
test: ["CMD-SHELL", "pg_isready -U rebase -d rebase"]
|
|
231
|
-
interval: 5s
|
|
232
|
-
timeout: 5s
|
|
233
|
-
retries: 10
|
|
234
|
-
start_period: 10s
|
|
235
|
-
command:
|
|
236
|
-
- "postgres"
|
|
237
|
-
- "-c"
|
|
238
|
-
- "shared_buffers=256MB"
|
|
239
|
-
- "-c"
|
|
240
|
-
- "max_connections=100"
|
|
241
|
-
- "-c"
|
|
242
|
-
- "work_mem=4MB"
|
|
243
|
-
- "-c"
|
|
244
|
-
- "effective_cache_size=768MB"
|
|
245
|
-
- "-c"
|
|
246
|
-
- "log_min_duration_statement=1000"
|
|
247
|
-
|
|
248
|
-
backend:
|
|
249
|
-
build:
|
|
250
|
-
context: .
|
|
251
|
-
dockerfile: backend/Dockerfile
|
|
252
|
-
restart: unless-stopped
|
|
253
|
-
ports:
|
|
254
|
-
- "${PORT:-3001}:3001"
|
|
255
|
-
env_file: .env
|
|
256
|
-
environment:
|
|
257
|
-
DATABASE_URL: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
|
|
258
|
-
ADMIN_CONNECTION_STRING: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
|
|
259
|
-
NODE_ENV: production
|
|
260
|
-
PORT: "3001"
|
|
261
|
-
depends_on:
|
|
262
|
-
db:
|
|
263
|
-
condition: service_healthy
|
|
264
|
-
volumes:
|
|
265
|
-
- uploads:/app/backend/uploads
|
|
266
|
-
|
|
267
|
-
frontend:
|
|
268
|
-
build:
|
|
269
|
-
context: .
|
|
270
|
-
dockerfile: frontend/Dockerfile
|
|
271
|
-
restart: unless-stopped
|
|
272
|
-
ports:
|
|
273
|
-
- "80:80"
|
|
274
|
-
depends_on:
|
|
275
|
-
- backend
|
|
276
|
-
|
|
277
|
-
volumes:
|
|
278
|
-
postgres_data:
|
|
279
|
-
driver: local
|
|
280
|
-
uploads:
|
|
281
|
-
driver: local
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
Key differences from the monorepo compose:
|
|
285
|
-
- Build context is `.` (project root), dockerfile is `backend/Dockerfile`
|
|
286
|
-
- Uses `env_file: .env` to load all env vars
|
|
287
|
-
- Sets `NODE_ENV: production` explicitly
|
|
288
|
-
- Includes a separate `frontend` service (nginx)
|
|
289
|
-
- Adds Postgres tuning for `work_mem` and `effective_cache_size`
|
|
290
|
-
|
|
291
|
-
---
|
|
292
|
-
|
|
293
|
-
## Environment Variables
|
|
294
|
-
|
|
295
|
-
### `loadEnv()` — Validated Environment Loading
|
|
296
|
-
|
|
297
|
-
All environment variables are validated at startup via a Zod schema in `loadEnv()` from `@rebasepro/server-core`. The server **fails immediately** if required variables are missing or invalid.
|
|
298
|
-
|
|
299
|
-
```typescript
|
|
300
|
-
import dotenv from "dotenv";
|
|
301
|
-
import { loadEnv } from "@rebasepro/server-core";
|
|
302
|
-
|
|
303
|
-
dotenv.config({ path: "../../.env" });
|
|
304
|
-
|
|
305
|
-
// Basic — just Rebase env vars:
|
|
306
|
-
export const env = loadEnv();
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
> **IMPORTANT FOR AGENTS:** `loadEnv()` does NOT load `.env` files itself. You MUST call `dotenv.config()` (or use `--env-file`, container injection, etc.) **before** calling `loadEnv()`. This is a deployment concern, not a framework concern.
|
|
310
|
-
|
|
311
|
-
### Extending with Custom Variables
|
|
312
|
-
|
|
313
|
-
Use the `extend` option to add your own typed env variables on top of the base Rebase schema:
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
import dotenv from "dotenv";
|
|
317
|
-
import { loadEnv, z } from "@rebasepro/server-core";
|
|
318
|
-
|
|
319
|
-
dotenv.config({ path: "../../.env" });
|
|
320
|
-
|
|
321
|
-
export const env = loadEnv({
|
|
322
|
-
extend: z.object({
|
|
323
|
-
SMTP_HOST: z.string().optional(),
|
|
324
|
-
SMTP_PORT: z.string().default("587").transform(Number),
|
|
325
|
-
STRIPE_SECRET_KEY: z.string(),
|
|
326
|
-
})
|
|
327
|
-
});
|
|
328
|
-
// env.SMTP_HOST → string | undefined (fully typed)
|
|
329
|
-
// env.STRIPE_SECRET_KEY → string (validated, required)
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
The `extend` schema is merged with the base `rebaseEnvSchema` using Zod's `.merge()`. All base variables remain available alongside your custom ones.
|
|
333
|
-
|
|
334
|
-
### `loadEnv()` Function Signature
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
function loadEnv(): RebaseEnv;
|
|
338
|
-
function loadEnv<E extends z.AnyZodObject>(options: { extend: E }): RebaseEnv & z.infer<E>;
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### Complete Environment Variable Reference
|
|
342
|
-
|
|
343
|
-
All variables recognized by the base `rebaseEnvSchema`:
|
|
344
|
-
|
|
345
|
-
| Variable | Type | Required | Default | Description |
|
|
346
|
-
|----------|------|----------|---------|-------------|
|
|
347
|
-
| `DATABASE_URL` | `string` (URL) | ✅ Yes | — | PostgreSQL connection string |
|
|
348
|
-
| `ADMIN_CONNECTION_STRING` | `string` (URL) | No | — | Separate admin connection for migrations |
|
|
349
|
-
| `JWT_SECRET` | `string` (≥32 chars) | ✅ Yes | Auto-generated (dev only) | JWT signing secret |
|
|
350
|
-
| `JWT_ACCESS_EXPIRES_IN` | `string` | No | `"1h"` | JWT access token expiry (e.g. `"1h"`, `"30m"`) |
|
|
351
|
-
| `JWT_REFRESH_EXPIRES_IN` | `string` | No | `"30d"` | JWT refresh token expiry |
|
|
352
|
-
| `NODE_ENV` | `enum` | No | `"development"` | `"development"`, `"production"`, or `"test"` |
|
|
353
|
-
| `PORT` | `string` → `number` | No | `"3001"` → `3001` | Server port (string, transformed to number) |
|
|
354
|
-
| `CORS_ORIGINS` | `string` | ⚠️ Prod | — | Comma-separated allowed origins |
|
|
355
|
-
| `FRONTEND_URL` | `string` | ⚠️ Prod | — | Frontend URL (CORS fallback, email links) |
|
|
356
|
-
| `ALLOW_REGISTRATION` | `boolString` | No | `"false"` → `false` | Enable new user self-registration |
|
|
357
|
-
| `ALLOW_LOCALHOST_IN_PRODUCTION` | `optionalBoolString` | No | `undefined` → `false` | Allow localhost URLs in production |
|
|
358
|
-
| `GOOGLE_CLIENT_ID` | `string` | No | — | Google OAuth client ID |
|
|
359
|
-
| `GOOGLE_CLIENT_SECRET` | `string` | No | — | Google OAuth client secret |
|
|
360
|
-
| `REBASE_SERVICE_KEY` | `string` | No | Auto-generated (dev only) | Service-to-service auth key (≥32 chars) |
|
|
361
|
-
| `DB_POOL_MAX` | `string` → `number` | No | `"20"` → `20` | Max database pool connections |
|
|
362
|
-
| `DB_POOL_IDLE_TIMEOUT` | `string` → `number` | No | `"30000"` → `30000` | Idle connection timeout (ms) |
|
|
363
|
-
| `DB_POOL_CONNECT_TIMEOUT` | `string` → `number` | No | `"10000"` → `10000` | Connection timeout (ms) |
|
|
364
|
-
| `DATABASE_DIRECT_URL` | `string` (URL) | No | — | Direct database URL (bypasses pooler) |
|
|
365
|
-
| `DATABASE_READ_URL` | `string` (URL) | No | — | Read replica database URL |
|
|
366
|
-
| `STORAGE_TYPE` | `enum` | No | `"local"` | `"local"` or `"s3"` |
|
|
367
|
-
| `STORAGE_PATH` | `string` | No | — | Local file storage directory |
|
|
368
|
-
| `FORCE_LOCAL_STORAGE` | `optionalBoolString` | No | `undefined` → `false` | Suppress local-storage-in-production warning |
|
|
369
|
-
| `S3_BUCKET` | `string` | If S3 | — | S3 bucket name |
|
|
370
|
-
| `S3_REGION` | `string` | No | — | S3 region |
|
|
371
|
-
| `S3_ACCESS_KEY_ID` | `string` | If S3 | — | S3 access key |
|
|
372
|
-
| `S3_SECRET_ACCESS_KEY` | `string` | If S3 | — | S3 secret key |
|
|
373
|
-
| `S3_ENDPOINT` | `string` (URL) | No | — | Custom S3 endpoint (MinIO, R2) |
|
|
374
|
-
| `S3_FORCE_PATH_STYLE` | `optionalBoolString` | No | `undefined` → `false` | Use path-style S3 URLs (required for MinIO) |
|
|
375
|
-
|
|
376
|
-
### Zod Type Helpers
|
|
377
|
-
|
|
378
|
-
| Helper | Accepts | Result |
|
|
379
|
-
|--------|---------|--------|
|
|
380
|
-
| `boolString` | `"true"`, `"false"`, `""` | Defaults to `"false"`, transforms to `boolean` |
|
|
381
|
-
| `optionalBoolString` | `"true"`, `"false"`, `""`, `undefined` | Transforms `"true"` → `true`, everything else → `false` |
|
|
382
|
-
|
|
383
|
-
### Auto-Generated Secrets (Dev Only)
|
|
384
|
-
|
|
385
|
-
In non-production mode, `loadEnv()` auto-generates ephemeral secrets for:
|
|
386
|
-
- `JWT_SECRET` — if not set
|
|
387
|
-
- `REBASE_SERVICE_KEY` — if not set
|
|
388
|
-
|
|
389
|
-
These are random hex strings generated via `crypto.randomBytes(48)`. A warning is logged:
|
|
390
|
-
|
|
391
|
-
```
|
|
392
|
-
⚠️ Auto-generated secrets for: JWT_SECRET, REBASE_SERVICE_KEY. These are ephemeral — existing tokens will be invalidated on restart. Set them explicitly in .env for persistent sessions.
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
> **WARNING FOR AGENTS:** Auto-generated secrets are **blocked in production**. The server will crash with a Zod validation error if `JWT_SECRET` or `REBASE_SERVICE_KEY` are not explicitly set when `NODE_ENV=production`.
|
|
396
|
-
|
|
397
|
-
### Production Validations
|
|
398
|
-
|
|
399
|
-
When `NODE_ENV=production`, `loadEnv()` enforces these additional rules via Zod `.superRefine()`:
|
|
400
|
-
|
|
401
|
-
| Rule | Error if violated |
|
|
402
|
-
|------|-------------------|
|
|
403
|
-
| **CORS required** | At least one of `CORS_ORIGINS` or `FRONTEND_URL` must be set |
|
|
404
|
-
| **No auto-generated secrets** | `JWT_SECRET` and `REBASE_SERVICE_KEY` must be explicitly set |
|
|
405
|
-
| **No localhost URLs** | All URL-type env vars are scanned — any containing `localhost`, `127.0.0.1`, or `::1` will fail validation (unless `ALLOW_LOCALHOST_IN_PRODUCTION=true`) |
|
|
406
|
-
|
|
407
|
-
The localhost check applies to **all** string env vars (except `CORS_ORIGINS`) using a helper that parses URLs, custom protocols (e.g. `postgres://`), and plain `host:port` formats.
|
|
408
|
-
|
|
409
|
-
### `.env.example` Reference
|
|
410
|
-
|
|
411
|
-
Copy the `.env.example` file as a starting point:
|
|
412
|
-
|
|
413
|
-
```bash
|
|
414
|
-
cp .env.example .env
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
The `.env.example` includes all variables with inline documentation. See `app/.env.example` in the repository.
|
|
418
|
-
|
|
419
|
-
### `.env.production` (Frontend Build)
|
|
420
|
-
|
|
421
|
-
The `.env.production` file is read by Vite at build time to bake frontend config into the client bundle:
|
|
422
|
-
|
|
423
|
-
```bash
|
|
424
|
-
# API URL: empty string = same-origin (backend + frontend share the same URL)
|
|
425
|
-
VITE_API_URL=
|
|
426
|
-
|
|
427
|
-
# Google OAuth Client ID (public — embedded in client bundle)
|
|
428
|
-
VITE_GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
> **IMPORTANT FOR AGENTS:** `VITE_API_URL=` (empty) means same-origin — the frontend assumes the API is served from the same host. This works when using `serveSPA()` or a reverse proxy. Only set a full URL when the API is on a different domain.
|
|
432
|
-
|
|
433
|
-
---
|
|
434
|
-
|
|
435
|
-
## SPA Serving with `serveSPA()`
|
|
436
|
-
|
|
437
|
-
In production, the backend can serve the frontend SPA directly, eliminating the need for a separate web server or CDN.
|
|
438
|
-
|
|
439
|
-
### `serveSPA()` Function Signature
|
|
440
|
-
|
|
441
|
-
```typescript
|
|
442
|
-
import { serveSPA } from "@rebasepro/server-core";
|
|
443
|
-
|
|
444
|
-
function serveSPA<E extends Env>(app: Hono<E>, config: ServeSPAConfig): void;
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
### `ServeSPAConfig` Options
|
|
448
|
-
|
|
449
|
-
| Property | Type | Default | Description |
|
|
450
|
-
|----------|------|---------|-------------|
|
|
451
|
-
| `frontendPath` | `string` | — | **Required.** Absolute path to the frontend build directory |
|
|
452
|
-
| `apiBasePath` | `string` | `"/api"` | Base path for API routes (excluded from SPA handling) |
|
|
453
|
-
| `excludePaths` | `string[]` | `[]` | Additional paths to exclude from SPA handling (e.g. `["/health", "/ws", "/metrics"]`) |
|
|
454
|
-
| `indexFile` | `string` | `"index.html"` | Index file to serve for SPA routes |
|
|
455
|
-
|
|
456
|
-
### How It Works
|
|
457
|
-
|
|
458
|
-
1. Serves static files from `frontendPath` using `@hono/node-server/serve-static`
|
|
459
|
-
2. For any GET request not matching `apiBasePath` or `excludePaths`, returns `index.html` (SPA fallback)
|
|
460
|
-
3. If `frontendPath` doesn't exist, logs a warning and **disables SPA serving** (does not crash)
|
|
461
|
-
4. If `index.html` is missing, passes through to the next handler
|
|
462
|
-
|
|
463
|
-
### Usage Example
|
|
464
|
-
|
|
465
|
-
```typescript
|
|
466
|
-
import { serveSPA } from "@rebasepro/server-core";
|
|
467
|
-
import path from "path";
|
|
468
|
-
|
|
469
|
-
const isProduction = env.NODE_ENV === "production";
|
|
470
|
-
|
|
471
|
-
// ... after initializeRebaseBackend() ...
|
|
472
|
-
|
|
473
|
-
if (isProduction) {
|
|
474
|
-
serveSPA(app, {
|
|
475
|
-
frontendPath: path.resolve(process.cwd(), "../frontend/dist"),
|
|
476
|
-
// apiBasePath defaults to "/api" — matches the default Rebase basePath
|
|
477
|
-
// excludePaths: ["/health", "/ws"],
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
> **IMPORTANT FOR AGENTS:** Call `serveSPA()` **after** `initializeRebaseBackend()` and after mounting the `/health` endpoint. The SPA catch-all route (`*`) must be the last route registered to avoid intercepting API or health check requests.
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Firebase Hosting (Frontend)
|
|
487
|
-
|
|
488
|
-
Deploy the Studio frontend to Firebase Hosting:
|
|
489
|
-
|
|
490
|
-
```bash
|
|
491
|
-
# Build the frontend
|
|
492
|
-
cd frontend
|
|
493
|
-
pnpm run build
|
|
494
|
-
|
|
495
|
-
# Deploy to Firebase
|
|
496
|
-
npx firebase-tools@latest deploy --only hosting
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
---
|
|
500
|
-
|
|
501
|
-
## Health Check Endpoint
|
|
502
|
-
|
|
503
|
-
The Docker `HEALTHCHECK` instruction hits `/health`. Mount it in your backend entry:
|
|
504
|
-
|
|
505
|
-
```typescript
|
|
506
|
-
app.get("/health", async (c) => {
|
|
507
|
-
const result = await backend.healthCheck();
|
|
508
|
-
const status = result.healthy ? 200 : 503;
|
|
509
|
-
return c.json({
|
|
510
|
-
status: result.healthy ? "ok" : "degraded",
|
|
511
|
-
latencyMs: result.latencyMs,
|
|
512
|
-
...(result.details ? { details: result.details } : {})
|
|
513
|
-
}, status);
|
|
514
|
-
});
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
`healthCheck()` runs `SELECT 1` against the database and returns latency. Returns `{ healthy: boolean, latencyMs: number, details?: { error: string } }`.
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
## Graceful Shutdown
|
|
522
|
-
|
|
523
|
-
The `RebaseBackendInstance` exposes a `shutdown()` method for clean container termination:
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
const backend = await initializeRebaseBackend({ ... });
|
|
527
|
-
|
|
528
|
-
const gracefulShutdown = async (signal: string) => {
|
|
529
|
-
await backend.shutdown(); // Stops cron, realtime, drains HTTP (15s timeout)
|
|
530
|
-
await postgresResources.pool.end(); // Close database pool
|
|
531
|
-
process.exit(0);
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
535
|
-
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
536
|
-
```
|
|
537
|
-
|
|
538
|
-
`shutdown(timeoutMs?: number)`:
|
|
539
|
-
- Default timeout: `15000` ms
|
|
540
|
-
- Stops cron scheduler
|
|
541
|
-
- Destroys realtime services (LISTEN clients, debounce timers)
|
|
542
|
-
- Closes HTTP server and drains in-flight requests
|
|
543
|
-
- Pass `0` to skip the force-exit timer (useful in tests)
|
|
544
|
-
|
|
545
|
-
> **WARNING FOR AGENTS:** Do NOT call `server.close()` separately when using `backend.shutdown()` — it already closes the HTTP server internally. Calling `server.close()` twice will deadlock.
|
|
546
|
-
|
|
547
|
-
---
|
|
548
|
-
|
|
549
|
-
## ⛔ Agent Deployment Rules
|
|
550
|
-
|
|
551
|
-
**Agents should NEVER deploy or run deployment commands unless explicitly asked by the user in the current conversation.** This includes:
|
|
552
|
-
- `rebase deploy` (any variant)
|
|
553
|
-
- `firebase deploy` (any variant)
|
|
554
|
-
- `gcloud functions deploy`
|
|
555
|
-
- `gcloud run deploy`
|
|
556
|
-
- `terraform apply` (any variant that deploys resources)
|
|
557
|
-
- `docker compose up` in production
|
|
558
|
-
- Any command targeting staging or production environments
|
|
559
|
-
|
|
560
|
-
**What agents CAN do:**
|
|
561
|
-
- Edit source code
|
|
562
|
-
- Run builds (`pnpm run build`)
|
|
563
|
-
- Run tests (`pnpm test`)
|
|
564
|
-
- Run local dev server (`pnpm dev`)
|
|
565
|
-
- Check logs (read-only)
|
|
566
|
-
- Create or edit Dockerfiles and docker-compose files
|
|
567
|
-
- Create or edit `.env` files
|
|
568
|
-
- Run deployment commands *only* if the user explicitly asks you to deploy in the current conversation. Otherwise, provide the exact commands for the user to run.
|
|
569
|
-
|
|
570
|
-
---
|
|
571
|
-
|
|
572
|
-
## References
|
|
573
|
-
|
|
574
|
-
- **Documentation:** [rebase.pro/docs](https://rebase.pro/docs)
|
|
575
|
-
- **GitHub:** [github.com/rebasepro/rebase](https://github.com/rebasepro/rebase)
|
|
576
|
-
- **Dockerfile:** `app/backend/Dockerfile`
|
|
577
|
-
- **Docker Compose (monorepo):** `app/backend/docker-compose.yml`
|
|
578
|
-
- **Docker Compose (template):** `packages/cli/templates/template/docker-compose.yml`
|
|
579
|
-
- **Entrypoint:** `app/backend/entrypoint.sh`
|
|
580
|
-
- **Env Schema:** `packages/server-core/src/env.ts`
|
|
581
|
-
- **serveSPA:** `packages/server-core/src/serve-spa.ts`
|
|
582
|
-
- **Backend Entry:** `app/backend/src/index.ts`
|
|
583
|
-
- **.env.example:** `app/.env.example`
|