@rebasepro/cli 0.11.0 → 0.11.1-canary.g8caabf3

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.
Files changed (33) hide show
  1. package/bin/rebase.js +21 -0
  2. package/dist/bundle.d.ts +15 -5
  3. package/dist/commands/cloud/resources.d.ts +20 -0
  4. package/dist/commands/eject.d.ts +1 -0
  5. package/dist/commands/init.d.ts +19 -15
  6. package/dist/fold-static.d.ts +41 -15
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.es.js +766 -241
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/manifest.d.ts +26 -7
  11. package/package.json +7 -7
  12. package/runtime/dev-server.mjs +0 -1
  13. package/templates/{template/backend → eject}/Dockerfile +16 -4
  14. package/templates/{template → eject}/backend/src/env.ts +0 -1
  15. package/templates/eject/docker-compose.custom.yml +71 -0
  16. package/templates/overlays/baas/backend/package.json +1 -4
  17. package/templates/overlays/baas/backend/tsconfig.json +8 -2
  18. package/templates/overlays/baas/config/index.ts +15 -0
  19. package/templates/overlays/baas/config/package.json +28 -0
  20. package/templates/overlays/baas/package.json +2 -1
  21. package/templates/overlays/baas/pnpm-workspace.yaml +1 -0
  22. package/templates/overlays/baas/rebase.json +2 -6
  23. package/templates/template/README.md +34 -16
  24. package/templates/template/backend/package.json +1 -4
  25. package/templates/template/docker-compose.yml +62 -38
  26. package/templates/template/frontend/src/main.tsx +8 -1
  27. package/templates/template/frontend/vite.config.ts +5 -0
  28. package/templates/template/rebase.json +5 -8
  29. package/templates/overlays/baas/backend/src/index.ts +0 -216
  30. package/templates/template/frontend/Dockerfile +0 -52
  31. package/templates/template/frontend/nginx.conf +0 -40
  32. /package/templates/{template → eject}/backend/src/index.ts +0 -0
  33. /package/templates/overlays/baas/{backend/src → config}/storage.ts +0 -0
@@ -36,10 +36,12 @@ export declare function validateManifest(raw: unknown): {
36
36
  * the manifest a no-op for existing projects: the synthesized result is what
37
37
  * they would have written by hand.
38
38
  *
39
- * An ejected backend one with its own `src/index.ts` entrypoint is reported
40
- * as a `custom` app rather than a `backend` app. That is not a downgrade; it is
41
- * an accurate description, and it is what keeps such a project deploying exactly
42
- * as it does today.
39
+ * The backend's `runtime` is inferred from whether the repository declares a
40
+ * **Dockerfile** the thing that actually builds an image and from nothing
41
+ * else. It used to be inferred from the presence of `backend/src/index.ts`,
42
+ * which every scaffolded project had whether or not it wanted its own server, so
43
+ * projects predating the manifest silently landed on the custom runtime and paid
44
+ * for it (see `docs/cloud-deploy-workspace-vendoring.md`).
43
45
  */
44
46
  export declare function synthesizeManifest(projectRoot: string): RebaseProjectManifest;
45
47
  export declare function manifestPath(projectRoot: string): string;
@@ -72,12 +74,29 @@ export declare function buildableApps(manifest: RebaseProjectManifest): {
72
74
  * verdict.
73
75
  */
74
76
  export declare function assessManagedCompatibility(manifest: RebaseProjectManifest): ManagedCompatibility;
75
- /** Resolve a backend app's directories against the conventions it omits. */
76
- export declare function resolveBackendPaths(app: RebaseBackendAppConfig): {
77
+ /**
78
+ * Resolve a backend app's directories against the conventions it omits.
79
+ *
80
+ * `hasCollections` replaces the old `mode: "cms" | "baas"` field. Where the
81
+ * collections come from was never an independent choice: either they are
82
+ * declared in code and the bundle ships them, or they are not and the runtime
83
+ * introspects the live database at boot. Declaring it separately only created
84
+ * the contradictory state — code-first declared, no collections anywhere.
85
+ *
86
+ * `hasConfig` is deliberately a **separate** question. A headless project has no
87
+ * `config/collections`, but it may still ship a config package — that is where
88
+ * the `storageAuthorize` hook lives, and storage is not under row-level
89
+ * security, so without one the server refuses to boot with storage enabled.
90
+ * Collapsing the two would leave a headless project nowhere to put it.
91
+ */
92
+ export declare function resolveBackendPaths(app: RebaseBackendAppConfig, projectRoot: string): {
77
93
  config: string;
78
94
  functions: string;
79
95
  crons: string;
80
96
  schema: string;
81
97
  usersCollection: string;
82
- mode: "cms" | "baas";
98
+ /** Whether a config package exists — hooks, storage authorization. */
99
+ hasConfig: boolean;
100
+ /** Whether collections are declared in code, under `<config>/collections`. */
101
+ hasCollections: boolean;
83
102
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/cli",
3
- "version": "0.11.0",
3
+ "version": "0.11.1-canary.g8caabf3",
4
4
  "description": "Developer tools for Rebase projects",
5
5
  "main": "./dist/index.es.js",
6
6
  "module": "./dist/index.es.js",
@@ -31,12 +31,12 @@
31
31
  "execa": "^9.6.1",
32
32
  "inquirer": "14.0.2",
33
33
  "jiti": "^2.7.0",
34
- "@rebasepro/agent-skills": "0.11.0",
35
- "@rebasepro/client": "0.11.0",
36
- "@rebasepro/codegen": "0.11.0",
37
- "@rebasepro/server-postgres": "0.11.0",
38
- "@rebasepro/types": "0.11.0",
39
- "@rebasepro/server": "0.11.0"
34
+ "@rebasepro/agent-skills": "0.11.1-canary.g8caabf3",
35
+ "@rebasepro/client": "0.11.1-canary.g8caabf3",
36
+ "@rebasepro/server": "0.11.1-canary.g8caabf3",
37
+ "@rebasepro/codegen": "0.11.1-canary.g8caabf3",
38
+ "@rebasepro/server-postgres": "0.11.1-canary.g8caabf3",
39
+ "@rebasepro/types": "0.11.1-canary.g8caabf3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^25.9.3",
@@ -31,7 +31,6 @@ const bundle = createSourceBundle({
31
31
  functions: optional(process.env.REBASE_DEV_FUNCTIONS),
32
32
  crons: optional(process.env.REBASE_DEV_CRONS),
33
33
  schema: optional(process.env.REBASE_DEV_SCHEMA),
34
- mode: process.env.REBASE_DEV_MODE === "baas" ? "baas" : "cms",
35
34
  app: optional(process.env.REBASE_DEV_APP)
36
35
  });
37
36
 
@@ -1,10 +1,22 @@
1
- # ─── Multi-stage production Dockerfile for the Rebase backend ─────────
2
- # Produces a minimal image (~150MB) with only the runtime needed.
1
+ # ─── Your image ───────────────────────────────────────────────────────
2
+ #
3
+ # `rebase eject` wrote this file, and it is yours now — nothing regenerates or
4
+ # updates it. The platform runtime is no longer involved: this image compiles
5
+ # and runs backend/src/index.ts, so CORS, auth wiring, storage and shutdown are
6
+ # configured there rather than by the runtime, and Rebase runtime upgrades do
7
+ # not reach this project.
8
+ #
9
+ # Assumes pnpm, which is what `rebase init` scaffolds a workspace for. On npm,
10
+ # swap the three pnpm lines below for `npm ci` and `npm run build --workspace`.
3
11
  #
4
12
  # Build context: the project root (where pnpm-workspace.yaml lives)
5
13
  # Usage:
6
- # docker build -t my-app-backend -f backend/Dockerfile .
7
- # docker run -p 3001:3001 --env-file .env my-app-backend
14
+ # docker build -t my-app .
15
+ # docker compose -f docker-compose.custom.yml up
16
+ #
17
+ # The managed alternative — no image to build, your project mounted into the
18
+ # published runtime — is docker-compose.yml, which is still here and still
19
+ # works if you change `runtime` back to "managed" in rebase.json.
8
20
 
9
21
  # ── Stage 1: Install + Build ─────────────────────────────────────────
10
22
  FROM node:22-alpine AS builder
@@ -19,7 +19,6 @@ const __dirname = path.dirname(__filename);
19
19
  */
20
20
  function findEnvFile(startDir: string): string | undefined {
21
21
  let dir = startDir;
22
- // eslint-disable-next-line no-constant-condition
23
22
  while (true) {
24
23
  const candidate = path.join(dir, ".env");
25
24
  if (fs.existsSync(candidate)) return candidate;
@@ -0,0 +1,71 @@
1
+ # ─── Self-hosting an ejected project ─────────────────────────────────
2
+ #
3
+ # `rebase eject` wrote this. It builds the image described by ./Dockerfile and
4
+ # runs YOUR backend/src/index.ts, rather than mounting a bundle into the
5
+ # published Rebase runtime.
6
+ #
7
+ # docker compose -f docker-compose.custom.yml up -d db
8
+ # rebase db push # once
9
+ # docker compose -f docker-compose.custom.yml up --build
10
+ #
11
+ # What you gave up by ejecting: the platform no longer upgrades the server
12
+ # underneath you, and CORS, auth wiring, storage and shutdown are configured in
13
+ # your entrypoint rather than by the runtime. What you gained: it is your
14
+ # process, and you can put anything you like in it.
15
+ #
16
+ # `docker-compose.yml` is still here and still runs the managed shape. Nothing
17
+ # stops you going back — set `runtime: "managed"` in rebase.json, `rebase build`,
18
+ # and use that file instead.
19
+ # ─────────────────────────────────────────────────────────────────────
20
+
21
+ name: {{PROJECT_NAME}}
22
+
23
+ services:
24
+ db:
25
+ image: postgres:18-alpine
26
+ restart: unless-stopped
27
+ environment:
28
+ POSTGRES_USER: rebase
29
+ POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-changeme}
30
+ POSTGRES_DB: rebase
31
+ ports:
32
+ - "5432:5432"
33
+ volumes:
34
+ - postgres_data:/var/lib/postgresql
35
+ healthcheck:
36
+ test: ["CMD-SHELL", "pg_isready -U rebase -d rebase"]
37
+ interval: 5s
38
+ timeout: 5s
39
+ retries: 10
40
+ start_period: 10s
41
+
42
+ backend:
43
+ build:
44
+ context: .
45
+ dockerfile: Dockerfile
46
+ restart: unless-stopped
47
+ ports:
48
+ - "${PORT:-3001}:3001"
49
+ env_file: .env
50
+ environment:
51
+ DATABASE_URL: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
52
+ ADMIN_CONNECTION_STRING: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
53
+ NODE_ENV: production
54
+ PORT: "3001"
55
+ # Your entrypoint serves the built frontend itself (see the `serveSPA`
56
+ # call in backend/src/index.ts), so this is one container, same origin.
57
+ CORS_ORIGINS: ${CORS_ORIGINS:?set CORS_ORIGINS to the origin you browse to}
58
+ # A durable named volume, which is the case this flag acknowledges.
59
+ STORAGE_PATH: /uploads
60
+ FORCE_LOCAL_STORAGE: "true"
61
+ depends_on:
62
+ db:
63
+ condition: service_healthy
64
+ volumes:
65
+ - uploads:/uploads
66
+
67
+ volumes:
68
+ postgres_data:
69
+ driver: local
70
+ uploads:
71
+ driver: local
@@ -2,12 +2,9 @@
2
2
  "name": "{{PROJECT_NAME}}-backend",
3
3
  "version": "1.0.0",
4
4
  "description": "Rebase BaaS — headless PostgreSQL API",
5
- "main": "src/index.ts",
6
5
  "type": "module",
7
6
  "scripts": {
8
- "dev": "tsx watch --include=\"./functions/**/*\" src/index.ts",
9
- "build": "tsc",
10
- "start": "node dist/index.js"
7
+ "build": "tsc"
11
8
  },
12
9
  "dependencies": {
13
10
  "@rebasepro/server": "workspace:*",
@@ -5,7 +5,6 @@
5
5
  "moduleResolution": "node",
6
6
  "lib": ["ES2022"],
7
7
  "outDir": "./dist",
8
- "rootDir": "./src",
9
8
  "strict": true,
10
9
  "esModuleInterop": true,
11
10
  "allowSyntheticDefaultImports": true,
@@ -15,5 +14,12 @@
15
14
  "declaration": true,
16
15
  "sourceMap": true
17
16
  },
18
- "include": ["src/**/*"]
17
+ // `functions/` as well as `src/`, because this flavour has nothing in src:
18
+ // it declares no collections, so there is no generated schema, and the server
19
+ // entrypoint lives behind `rebase eject`. An include matching only an empty
20
+ // directory is TS18003 — tsc reports "no inputs" as an error, not a no-op.
21
+ //
22
+ // No `rootDir`: it would have to be `.` for both, and letting tsc infer the
23
+ // common root gets there without a second place to keep in sync.
24
+ "include": ["src/**/*", "functions/**/*"]
19
25
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The config package for a headless project.
3
+ *
4
+ * There are no collections here: this flavour introspects them from the live
5
+ * database at boot, which is exactly what "no `config/collections` directory"
6
+ * means to `rebase build`.
7
+ *
8
+ * The package still exists because storage does not run under row-level
9
+ * security and its keys share one flat namespace — so without an access model,
10
+ * a deployment with file storage enabled serves every user's files to every
11
+ * signed-in user. The server refuses to boot in that state, and this is the
12
+ * export it looks for.
13
+ */
14
+
15
+ export { storageAuthorize } from "./storage.js";
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}-config",
3
+ "version": "1.0.0",
4
+ "description": "Storage access control for a headless Rebase project",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "private": true,
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "clean": "rm -rf dist"
13
+ },
14
+ "dependencies": {
15
+ "@rebasepro/types": "workspace:*"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.9.2"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ }
28
+ }
@@ -5,7 +5,8 @@
5
5
  "private": true,
6
6
  "type": "module",
7
7
  "workspaces": [
8
- "backend"
8
+ "backend",
9
+ "config"
9
10
  ],
10
11
  "scripts": {
11
12
  "dev": "rebase dev",
@@ -1,5 +1,6 @@
1
1
  packages:
2
2
  - "backend"
3
+ - "config"
3
4
  linkWorkspacePackages: true
4
5
  blockExoticSubdeps: false
5
6
  minimumReleaseAge: 0
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "$schema": "https://rebase.pro/schemas/rebase.json",
3
- "runtime": "^1",
3
+ "rebase": "^1",
4
4
  "apps": {
5
5
  "backend": {
6
6
  "type": "backend",
7
- "mode": "baas"
8
- },
9
- "admin": {
10
- "type": "admin",
11
- "mode": "hosted"
7
+ "runtime": "managed"
12
8
  }
13
9
  }
14
10
  }
@@ -60,20 +60,27 @@ server). Pin a port with `rebase dev --port 3001` if you need a stable one.
60
60
  ## Project Structure
61
61
 
62
62
  ```
63
- ├── frontend/ # React frontend (Vite)
64
- ├── Dockerfile # Production build → nginx
65
- │ └── nginx.conf # SPA routing + compression
66
- ├── backend/ # Hono backend with PostgreSQL
67
- │ ├── Dockerfile # Multi-stage production build
63
+ ├── frontend/ # Your admin panel (React + Vite)
64
+ ├── backend/ # Server code
68
65
  │ ├── functions/ # Custom API endpoints (auto-discovered)
69
- │ └── src/
66
+ │ └── src/ # Generated database schema
70
67
  ├── config/ # Shared collection definitions
71
68
  │ └── collections/ # Schema-as-Code TypeScript files
72
- ├── docker-compose.yml # Production stack (Postgres + Backend + Frontend)
69
+ ├── rebase.json # Which apps this repository contains
70
+ ├── docker-compose.yml # Self-hosting stack (Postgres + the Rebase runtime)
73
71
  ├── .env.example # Environment variable reference
74
72
  └── package.json # Root workspace config
75
73
  ```
76
74
 
75
+ There is no Dockerfile, and that is deliberate: `rebase build` produces a
76
+ **bundle** — your compiled collections, functions, crons and admin assets — and
77
+ the published `rebasepro/server` image boots it. The artifact you self-host is
78
+ the artifact Rebase Cloud runs, so moving between them changes nothing in this
79
+ repository.
80
+
81
+ If you would rather run your own server process, `rebase eject` writes the
82
+ entrypoint, a Dockerfile and a compose file that builds them.
83
+
77
84
  ### Custom Functions
78
85
 
79
86
  Drop a Hono app in `backend/functions/` and it's auto-mounted at `/api/functions/<name>`:
@@ -105,23 +112,28 @@ All configuration is managed through a single `.env` file in the project root. B
105
112
 
106
113
  `init` already generated your `.env`. See the comments in `.env.example` for details on each variable, and edit `.env` directly to change any of them.
107
114
 
108
- ## Production Deployment
115
+ ## Self-hosting
116
+
117
+ Two containers: PostgreSQL, and the Rebase runtime with your built project
118
+ mounted into it. There is no application image to build.
109
119
 
110
- The full stack PostgreSQL, backend, and frontend runs from
111
- `docker compose`. The backend image builds and boots, but it does **not**
112
- create your collection tables on its own, so push the schema once the
113
- database is up before (or right after) starting the rest of the stack.
120
+ The runtime creates its auth tables at boot but **not** your collection tables —
121
+ a container restart must not be able to change a schema as a side effect — so
122
+ push the schema once, while the database is up.
114
123
 
115
124
  ```bash
116
- # 1. Start the database and create the tables from your collections
125
+ # 1. Build your project into ./dist-bundle
126
+ pnpm run build # or: npm run build
127
+
128
+ # 2. Start the database and create the tables from your collections
117
129
  docker compose up -d db
118
130
  pnpm run db:push # or: npm run db:push
119
131
 
120
- # 2. Build and start the backend + frontend
121
- docker compose up -d --build
132
+ # 3. Start the runtime
133
+ docker compose up -d
122
134
 
123
135
  # View logs
124
- docker compose logs -f backend
136
+ docker compose logs -f api
125
137
 
126
138
  # Stop
127
139
  docker compose down
@@ -130,6 +142,12 @@ docker compose down
130
142
  docker compose down -v
131
143
  ```
132
144
 
145
+ One container now serves the API at `/api` and the admin at `/` — same origin,
146
+ so there is no CORS between them and no second web server to run.
147
+
148
+ To upgrade Rebase, set `REBASE_VERSION` in `.env` and restart. Your bundle is
149
+ untouched.
150
+
133
151
  > `docker compose` reads the generated `.env` as-is. Set production values
134
152
  > (a strong `DATABASE_PASSWORD`, `JWT_SECRET`, storage credentials, …) by
135
153
  > editing `.env` directly — see `.env.example` for the full list. Do not
@@ -2,12 +2,9 @@
2
2
  "name": "{{PROJECT_NAME}}-backend",
3
3
  "version": "1.0.0",
4
4
  "description": "Rebase backend with PostgreSQL",
5
- "main": "src/index.ts",
6
5
  "type": "module",
7
6
  "scripts": {
8
- "dev": "tsx watch --include=\"../config/**/*\" --include=\"./functions/**/*\" src/index.ts",
9
- "build": "rebase schema generate --collections ../config/collections && tsc",
10
- "start": "node dist/backend/src/index.js"
7
+ "build": "rebase schema generate --collections ../config/collections && tsc"
11
8
  },
12
9
  "dependencies": {
13
10
  "{{PROJECT_NAME}}-config": "*",
@@ -1,17 +1,32 @@
1
- # ─── Rebase Production Docker Compose ──────────────────────────────
1
+ # ─── Self-hosting this project ───────────────────────────────────────
2
2
  #
3
- # Quick start:
4
- # cp .env.example .env # Edit with your secrets
5
- # docker compose up -d # Start everything
3
+ # Two containers: PostgreSQL, and the Rebase runtime with your built project
4
+ # mounted into it. There is no application image to build here — the runtime is
5
+ # the same published image every Rebase deployment runs, and your project
6
+ # travels as a bundle.
6
7
  #
7
- # This runs:
8
- # 1. PostgreSQL 18 with persistent data
9
- # 2. Backend (Node.js / Hono) with health checks
10
- # 3. Frontend (nginx) serving the Vite build
8
+ # That is the point: the artifact you self-host is the artifact Rebase Cloud
9
+ # runs. Nothing about this repository changes when you move between them; only
10
+ # the destination does, and that lives in `.rebase/cloud.json`, which is not
11
+ # committed.
12
+ #
13
+ # rebase build # produces ./dist-bundle
14
+ # docker compose up -d db
15
+ # rebase db push # create the collection tables, once
16
+ # docker compose up
17
+ #
18
+ # One container then serves the API at /api and the admin at / — same origin, so
19
+ # there is no CORS to configure between them and no second web server.
11
20
  #
12
21
  # For development, use `rebase dev` instead.
22
+ #
23
+ # To upgrade Rebase, change REBASE_VERSION and restart. Your bundle is untouched.
24
+ # To run your OWN server code instead, `rebase eject` — it writes the entrypoint,
25
+ # a Dockerfile and a compose file that builds them.
13
26
  # ─────────────────────────────────────────────────────────────────────
14
27
 
28
+ name: {{PROJECT_NAME}}
29
+
15
30
  services:
16
31
  # ── PostgreSQL ───────────────────────────────────────────────────────
17
32
  db:
@@ -21,20 +36,25 @@ services:
21
36
  POSTGRES_USER: rebase
22
37
  POSTGRES_PASSWORD: ${DATABASE_PASSWORD:-changeme}
23
38
  POSTGRES_DB: rebase
39
+ # Published so `rebase db push` can reach it from the host. Remove this
40
+ # mapping once the schema is in place if the database should not be
41
+ # reachable from outside the compose network.
24
42
  ports:
25
43
  - "5432:5432"
26
44
  volumes:
27
45
  - postgres_data:/var/lib/postgresql
28
46
  healthcheck:
47
+ # The runtime must not start before the database can answer, or its first
48
+ # boot fails on a connection refused and the container restarts for no
49
+ # reason a reader would understand.
29
50
  test: ["CMD-SHELL", "pg_isready -U rebase -d rebase"]
30
51
  interval: 5s
31
52
  timeout: 5s
32
53
  retries: 10
33
54
  start_period: 10s
34
- # Production tuning (adjust for your workload)
35
55
  command:
36
56
  - "postgres"
37
- - "-c"
57
+ - "-c"
38
58
  - "shared_buffers=256MB"
39
59
  - "-c"
40
60
  - "max_connections=100"
@@ -45,44 +65,48 @@ services:
45
65
  - "-c"
46
66
  - "log_min_duration_statement=1000"
47
67
 
48
- # ── Backend ──────────────────────────────────────────────────────────
49
- backend:
50
- build:
51
- context: .
52
- dockerfile: backend/Dockerfile
68
+ # ── The Rebase runtime, booting your bundle ──────────────────────────
69
+ api:
70
+ image: rebasepro/server:${REBASE_VERSION:-latest}
53
71
  restart: unless-stopped
72
+ depends_on:
73
+ db:
74
+ condition: service_healthy
54
75
  ports:
55
76
  - "${PORT:-3001}:3001"
56
- env_file: .env
57
77
  environment:
58
- # Override DATABASE_URL to point to the Docker network service
59
78
  DATABASE_URL: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
60
79
  ADMIN_CONNECTION_STRING: postgresql://rebase:${DATABASE_PASSWORD:-changeme}@db:5432/rebase?options=-c%20search_path=public
80
+ JWT_SECRET: ${JWT_SECRET:?set JWT_SECRET in .env — `rebase init` generates one}
81
+ REBASE_SERVICE_KEY: ${REBASE_SERVICE_KEY:?set REBASE_SERVICE_KEY in .env — `rebase init` generates one}
61
82
  NODE_ENV: production
62
83
  PORT: "3001"
63
- # The server refuses to boot with local storage under NODE_ENV=production,
64
- # because the container filesystem is destroyed on the next restart and
65
- # uploads go with it. That does not apply here: the `uploads` volume below
66
- # is a durable named volume mounted at the storage path, which is exactly
67
- # the case the check tells you to acknowledge with this flag. Switch to
68
- # STORAGE_TYPE=s3 or gcs and drop this line if you move storage off-box.
84
+
85
+ # The origins a browser will load the admin from. With the admin folded
86
+ # into the bundle it is served from this very container, so this is just
87
+ # your own address but it is still required, because an API that guesses
88
+ # its allowed origins is one that eventually allows the wrong one.
89
+ CORS_ORIGINS: ${CORS_ORIGINS:?set CORS_ORIGINS to the origin you browse to, e.g. http://localhost:3001}
90
+
91
+ # Auth tables are created at boot. Collection tables are not: run
92
+ # `rebase db push` once, against the database above. A container restart
93
+ # must not be able to change a schema as a side effect.
94
+ REBASE_MIGRATE_ON_BOOT: ${REBASE_MIGRATE_ON_BOOT:-ensure}
95
+
96
+ # Uploads land on the named volume below, which survives restarts. That is
97
+ # the case FORCE_LOCAL_STORAGE exists to acknowledge — without a durable
98
+ # mount the server refuses local storage in production, because the
99
+ # container filesystem is destroyed on the next deploy and every uploaded
100
+ # file goes with it. Switch to STORAGE_TYPE=s3 or gcs and drop both lines
101
+ # if you move storage off-box.
102
+ STORAGE_PATH: /uploads
69
103
  FORCE_LOCAL_STORAGE: "true"
70
- depends_on:
71
- db:
72
- condition: service_healthy
73
104
  volumes:
74
- - uploads:/app/backend/uploads
75
-
76
- # ── Frontend ─────────────────────────────────────────────────────────
77
- frontend:
78
- build:
79
- context: .
80
- dockerfile: frontend/Dockerfile
81
- restart: unless-stopped
82
- ports:
83
- - "80:80"
84
- depends_on:
85
- - backend
105
+ # Your built project. Writable, because the runtime installs the bundle's
106
+ # declared dependencies into it on first start — `rebase build` emits a
107
+ # package.json but not a node_modules.
108
+ - ./dist-bundle:/bundle
109
+ - uploads:/uploads
86
110
 
87
111
  volumes:
88
112
  postgres_data:
@@ -8,12 +8,19 @@ window.addEventListener("unhandledrejection", (event: PromiseRejectionEvent) =>
8
8
  console.error("[Rebase] Unhandled promise rejection:", event.reason);
9
9
  });
10
10
 
11
+ // Where this app is mounted, from the `path` declared in rebase.json.
12
+ //
13
+ // `rebase build` passes it to Vite as `base` (REBASE_APP_BASE), and Vite exposes
14
+ // it back as BASE_URL — so the assets, the router and the server all agree on
15
+ // one value without it being written down three times. At "/" this is "".
16
+ const basename = import.meta.env.BASE_URL.replace(/\/$/, "");
17
+
11
18
  const router = createBrowserRouter([
12
19
  {
13
20
  path: "/*",
14
21
  element: <App/>
15
22
  }
16
- ]);
23
+ ], { basename });
17
24
 
18
25
  ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
19
26
  <React.StrictMode>
@@ -7,6 +7,11 @@ import { rebaseCollectionsPlugin } from "@rebasepro/app/vitePlugin";
7
7
 
8
8
  export default defineConfig({
9
9
  envDir: path.resolve(__dirname, ".."),
10
+ // The public path this app is served under, from its `path` in rebase.json.
11
+ // `rebase build` sets REBASE_APP_BASE; without this line an app declared at
12
+ // "/admin" would emit assets rooted at "/" and render a blank page. The
13
+ // build refuses to ship that — see docs/apps-and-runtimes.md §4.2.
14
+ base: process.env.REBASE_APP_BASE ?? "/",
10
15
  // Force a single copy of React and React Router across the app and all
11
16
  // @rebasepro/* packages. Without this, a locally `link:`ed Rebase checkout
12
17
  // resolves its own copies of react-router, producing "multiple copies of
@@ -1,20 +1,17 @@
1
1
  {
2
2
  "$schema": "https://rebase.pro/schemas/rebase.json",
3
- "runtime": "^1",
3
+ "rebase": "^1",
4
4
  "apps": {
5
5
  "backend": {
6
- "type": "backend"
6
+ "type": "backend",
7
+ "runtime": "managed"
7
8
  },
8
- "web": {
9
+ "admin": {
9
10
  "type": "static",
10
11
  "root": "frontend",
11
12
  "build": "npm run build --workspace frontend",
12
13
  "output": "frontend/dist",
13
- "spa": true
14
- },
15
- "admin": {
16
- "type": "admin",
17
- "mode": "hosted"
14
+ "path": "/"
18
15
  }
19
16
  }
20
17
  }