@rebasepro/cli 0.10.0 → 0.10.1-canary.14e53ae
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 +1 -1
- package/dist/bundle.d.ts +111 -0
- package/dist/commands/apps.d.ts +1 -0
- package/dist/commands/build.d.ts +1 -1
- package/dist/commands/cloud/bundle-deploy.d.ts +28 -0
- package/dist/commands/cloud/context.d.ts +28 -0
- package/dist/commands/cloud/deployments.d.ts +16 -0
- package/dist/commands/cloud/env.d.ts +2 -0
- package/dist/commands/cloud/resources.d.ts +38 -0
- package/dist/commands/generate_sdk.d.ts +12 -0
- package/dist/commands/start.d.ts +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.es.js +2380 -87
- package/dist/index.es.js.map +1 -1
- package/dist/manifest.d.ts +83 -0
- package/dist/utils/package-manager.d.ts +26 -2
- package/dist/utils/project.d.ts +11 -3
- package/package.json +8 -7
- package/runtime/dev-server.mjs +43 -0
- package/templates/overlays/baas/backend/src/index.ts +21 -4
- package/templates/overlays/baas/rebase.json +14 -0
- package/templates/template/.env.example +16 -3
- package/templates/template/README.md +39 -29
- package/templates/template/backend/src/index.ts +21 -4
- package/templates/template/gitignore +4 -0
- package/templates/template/rebase.json +20 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ManagedCompatibility, RebaseAppConfig, RebaseBackendAppConfig, RebaseProjectManifest } from "@rebasepro/types";
|
|
2
|
+
/** Runtime range written into new manifests. */
|
|
3
|
+
export declare const CURRENT_RUNTIME_RANGE = "^1";
|
|
4
|
+
/** Conventional locations, matching what `rebase init` scaffolds. */
|
|
5
|
+
export declare const DEFAULT_CONFIG_DIR = "config";
|
|
6
|
+
export declare const DEFAULT_FUNCTIONS_DIR = "backend/functions";
|
|
7
|
+
export declare const DEFAULT_CRONS_DIR = "backend/crons";
|
|
8
|
+
export declare const DEFAULT_SCHEMA_FILE = "backend/src/schema.generated.ts";
|
|
9
|
+
export interface ManifestValidationIssue {
|
|
10
|
+
/** Dotted path to the offending value, e.g. `apps.web.output`. */
|
|
11
|
+
path: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
export interface LoadedManifest {
|
|
15
|
+
manifest: RebaseProjectManifest;
|
|
16
|
+
/** Where it came from — a real file, or inferred from the directory layout. */
|
|
17
|
+
source: "file" | "synthesized";
|
|
18
|
+
/** Absolute path to `rebase.json`, when one exists. */
|
|
19
|
+
filePath?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class ManifestError extends Error {
|
|
22
|
+
readonly issues: ManifestValidationIssue[];
|
|
23
|
+
constructor(message: string, issues?: ManifestValidationIssue[]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate a parsed manifest, collecting every problem.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateManifest(raw: unknown): {
|
|
29
|
+
manifest?: RebaseProjectManifest;
|
|
30
|
+
issues: ManifestValidationIssue[];
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Infer a manifest from a directory that does not have one.
|
|
34
|
+
*
|
|
35
|
+
* This mirrors exactly what the template scaffolds, which is what makes adopting
|
|
36
|
+
* the manifest a no-op for existing projects: the synthesized result is what
|
|
37
|
+
* they would have written by hand.
|
|
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.
|
|
43
|
+
*/
|
|
44
|
+
export declare function synthesizeManifest(projectRoot: string): RebaseProjectManifest;
|
|
45
|
+
export declare function manifestPath(projectRoot: string): string;
|
|
46
|
+
export declare function manifestExists(projectRoot: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Read the manifest, falling back to a synthesized one.
|
|
49
|
+
*
|
|
50
|
+
* A malformed manifest throws — unlike a missing one. Silently ignoring a file
|
|
51
|
+
* the developer wrote, and building something else instead, is the worst
|
|
52
|
+
* available behaviour.
|
|
53
|
+
*/
|
|
54
|
+
export declare function loadManifest(projectRoot: string): LoadedManifest;
|
|
55
|
+
/** Write a manifest, with a trailing newline so it plays well with other tools. */
|
|
56
|
+
export declare function writeManifest(projectRoot: string, manifest: RebaseProjectManifest): string;
|
|
57
|
+
/** Find the single backend app, if this repository declares one. */
|
|
58
|
+
export declare function findBackendApp(manifest: RebaseProjectManifest): {
|
|
59
|
+
name: string;
|
|
60
|
+
app: RebaseBackendAppConfig;
|
|
61
|
+
} | undefined;
|
|
62
|
+
/** Apps that produce build output, in the order they should be built. */
|
|
63
|
+
export declare function buildableApps(manifest: RebaseProjectManifest): {
|
|
64
|
+
name: string;
|
|
65
|
+
app: RebaseAppConfig;
|
|
66
|
+
}[];
|
|
67
|
+
/**
|
|
68
|
+
* Decide whether a project can run on the managed runtime, and say why not.
|
|
69
|
+
*
|
|
70
|
+
* "Not eligible" is never a dead end — it selects the custom-runtime path, which
|
|
71
|
+
* still deploys. The reasons exist so the answer is actionable rather than a
|
|
72
|
+
* verdict.
|
|
73
|
+
*/
|
|
74
|
+
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
|
+
config: string;
|
|
78
|
+
functions: string;
|
|
79
|
+
crons: string;
|
|
80
|
+
schema: string;
|
|
81
|
+
usersCollection: string;
|
|
82
|
+
mode: "cms" | "baas";
|
|
83
|
+
};
|
|
@@ -19,14 +19,38 @@ export interface PMCommands {
|
|
|
19
19
|
/** The workspace dependency protocol: `"workspace:*"` for pnpm, `"*"` for npm. */
|
|
20
20
|
workspaceProtocol: string;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Decide availability from a `spawnSync` outcome.
|
|
24
|
+
*
|
|
25
|
+
* Split out from the spawn itself so the decision is testable without starting
|
|
26
|
+
* a process — which is what made the old test load-sensitive and occasionally
|
|
27
|
+
* red for reasons that had nothing to do with the code under test.
|
|
28
|
+
*
|
|
29
|
+
* The three outcomes are distinguishable, and the old code conflated two of
|
|
30
|
+
* them by asking only `status === 0`:
|
|
31
|
+
*
|
|
32
|
+
* not installed status null, signal null, error.code ENOENT
|
|
33
|
+
* timed out status null, signal SIGTERM, error.code ETIMEDOUT
|
|
34
|
+
* broken install status non-zero, no error
|
|
35
|
+
*/
|
|
36
|
+
export declare function pnpmAvailabilityFromProbe(res: {
|
|
37
|
+
status: number | null;
|
|
38
|
+
signal?: NodeJS.Signals | null;
|
|
39
|
+
error?: {
|
|
40
|
+
code?: string;
|
|
41
|
+
} | Error;
|
|
42
|
+
}): boolean;
|
|
22
43
|
/**
|
|
23
44
|
* Whether pnpm is runnable on this machine.
|
|
24
45
|
*
|
|
25
46
|
* Used to decide whether a fresh project can be scaffolded with pnpm. Kept
|
|
26
|
-
* cheap and non-interactive (
|
|
27
|
-
* hangs detection if a corepack shim misbehaves
|
|
47
|
+
* cheap and non-interactive (bounded timeout, output discarded) so it never
|
|
48
|
+
* hangs detection if a corepack shim misbehaves, and memoised so that repeated
|
|
49
|
+
* detection in one CLI run costs one process rather than one per call.
|
|
28
50
|
*/
|
|
29
51
|
export declare function isPnpmAvailable(): boolean;
|
|
52
|
+
/** Forget the memoised probe. For tests; nothing in a CLI run needs it. */
|
|
53
|
+
export declare function resetPnpmAvailabilityCache(): void;
|
|
30
54
|
/**
|
|
31
55
|
* Detect the package manager for a Rebase project.
|
|
32
56
|
*
|
package/dist/utils/project.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
/** The authored project manifest. Its presence alone marks a project root. */
|
|
2
|
+
export declare const MANIFEST_FILENAME = "rebase.json";
|
|
1
3
|
/**
|
|
2
4
|
* Walk up from `startDir` to find the Rebase project root.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* A directory is the root when it holds a `rebase.json`, or when it holds a
|
|
7
|
+
* `package.json` that either lists `backend` as a workspace or sits beside both
|
|
8
|
+
* `backend/` and `config/`.
|
|
9
|
+
*
|
|
10
|
+
* `rebase.json` is checked first and needs no `package.json` beside it, because
|
|
11
|
+
* the conventions below all describe a repository that *contains the backend*.
|
|
12
|
+
* A repository holding only a frontend — the normal shape once a project's apps
|
|
13
|
+
* live in separate repositories — matches none of them, so without this the
|
|
14
|
+
* tooling could not run there at all.
|
|
7
15
|
*/
|
|
8
16
|
export declare function findProjectRoot(startDir?: string): string | null;
|
|
9
17
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1-canary.14e53ae",
|
|
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.10.
|
|
35
|
-
"@rebasepro/
|
|
36
|
-
"@rebasepro/
|
|
37
|
-
"@rebasepro/server
|
|
38
|
-
"@rebasepro/
|
|
39
|
-
"@rebasepro/
|
|
34
|
+
"@rebasepro/agent-skills": "0.10.1-canary.14e53ae",
|
|
35
|
+
"@rebasepro/client": "0.10.1-canary.14e53ae",
|
|
36
|
+
"@rebasepro/codegen": "0.10.1-canary.14e53ae",
|
|
37
|
+
"@rebasepro/server": "0.10.1-canary.14e53ae",
|
|
38
|
+
"@rebasepro/server-postgres": "0.10.1-canary.14e53ae",
|
|
39
|
+
"@rebasepro/types": "0.10.1-canary.14e53ae"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^25.9.3",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
50
50
|
"bin/",
|
|
51
|
+
"runtime/",
|
|
51
52
|
"dist/",
|
|
52
53
|
"templates/"
|
|
53
54
|
],
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The development entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* Run under `tsx` by `rebase dev` for projects that have no hand-written
|
|
5
|
+
* `backend/src/index.ts`. It boots the same runtime a deployment does, over the
|
|
6
|
+
* project's TypeScript **source** rather than a compiled bundle — so hot reload
|
|
7
|
+
* still works, and development still predicts production because both go through
|
|
8
|
+
* one boot path.
|
|
9
|
+
*
|
|
10
|
+
* Paths arrive in the environment rather than as arguments because `tsx watch`
|
|
11
|
+
* owns the argument list.
|
|
12
|
+
*/
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import dotenv from "dotenv";
|
|
16
|
+
|
|
17
|
+
const projectRoot = process.env.REBASE_DEV_PROJECT_ROOT || process.cwd();
|
|
18
|
+
|
|
19
|
+
const envFile = process.env.DOTENV_CONFIG_PATH;
|
|
20
|
+
if (envFile && fs.existsSync(envFile)) {
|
|
21
|
+
dotenv.config({ path: envFile });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { createSourceBundle, runFromBundle } = await import("@rebasepro/server");
|
|
25
|
+
|
|
26
|
+
const optional = (value) => (value && value.length > 0 ? value : undefined);
|
|
27
|
+
|
|
28
|
+
const bundle = createSourceBundle({
|
|
29
|
+
projectRoot,
|
|
30
|
+
config: optional(process.env.REBASE_DEV_CONFIG),
|
|
31
|
+
functions: optional(process.env.REBASE_DEV_FUNCTIONS),
|
|
32
|
+
crons: optional(process.env.REBASE_DEV_CRONS),
|
|
33
|
+
schema: optional(process.env.REBASE_DEV_SCHEMA),
|
|
34
|
+
mode: process.env.REBASE_DEV_MODE === "baas" ? "baas" : "cms",
|
|
35
|
+
app: optional(process.env.REBASE_DEV_APP)
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// The dev port file and the graceful-shutdown handlers both key off the project
|
|
39
|
+
// root, so make sure the process agrees with the CLI about where that is.
|
|
40
|
+
process.chdir(projectRoot);
|
|
41
|
+
void path;
|
|
42
|
+
|
|
43
|
+
await runFromBundle({ bundle });
|
|
@@ -122,6 +122,12 @@ pass: env.SMTP_PASS! }
|
|
|
122
122
|
}
|
|
123
123
|
: undefined
|
|
124
124
|
},
|
|
125
|
+
// File storage is opt-in. With no bucket configured, storage is OFF in
|
|
126
|
+
// production — the upload routes answer 501 STORAGE_NOT_CONFIGURED —
|
|
127
|
+
// rather than writing to the container filesystem, which is erased on
|
|
128
|
+
// every restart and redeploy. Uploads that fail loudly are recoverable;
|
|
129
|
+
// uploads that succeed into a disk about to be wiped are not.
|
|
130
|
+
// Local disk stays the default in development, where it is what you want.
|
|
125
131
|
storage: env.STORAGE_TYPE === "s3"
|
|
126
132
|
? {
|
|
127
133
|
type: "s3",
|
|
@@ -132,10 +138,21 @@ pass: env.SMTP_PASS! }
|
|
|
132
138
|
endpoint: env.S3_ENDPOINT,
|
|
133
139
|
forcePathStyle: env.S3_FORCE_PATH_STYLE
|
|
134
140
|
}
|
|
135
|
-
:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
: env.STORAGE_TYPE === "gcs"
|
|
142
|
+
? {
|
|
143
|
+
type: "gcs",
|
|
144
|
+
bucket: env.GCS_BUCKET!,
|
|
145
|
+
projectId: env.GCS_PROJECT_ID,
|
|
146
|
+
keyFilename: env.GCS_KEY_FILENAME
|
|
147
|
+
}
|
|
148
|
+
// Set FORCE_LOCAL_STORAGE=true only if this deployment really
|
|
149
|
+
// does have a durable volume mounted at STORAGE_PATH.
|
|
150
|
+
: isProduction && !env.FORCE_LOCAL_STORAGE
|
|
151
|
+
? undefined
|
|
152
|
+
: {
|
|
153
|
+
type: "local",
|
|
154
|
+
basePath: env.STORAGE_PATH || path.resolve(__dirname, "../../uploads")
|
|
155
|
+
},
|
|
139
156
|
history: true,
|
|
140
157
|
enableSwagger: true
|
|
141
158
|
});
|
|
@@ -80,12 +80,19 @@ VITE_API_URL=http://localhost:3001
|
|
|
80
80
|
# ── Storage ───────────────────────────────────────────────────────────────────
|
|
81
81
|
# Set STORAGE_TYPE to choose a storage backend. Default is "local" (filesystem).
|
|
82
82
|
#
|
|
83
|
-
# Supported values: local, s3
|
|
83
|
+
# Supported values: local, s3, gcs
|
|
84
|
+
#
|
|
85
|
+
# In production, "local" means NO file storage: uploads answer
|
|
86
|
+
# 501 STORAGE_NOT_CONFIGURED instead of landing on a container filesystem that
|
|
87
|
+
# is erased on the next redeploy. Configure a bucket below to switch it on, or
|
|
88
|
+
# set FORCE_LOCAL_STORAGE=true if a durable volume really is mounted at
|
|
89
|
+
# STORAGE_PATH (the generated docker-compose.yml does exactly that).
|
|
84
90
|
#
|
|
85
91
|
# STORAGE_TYPE=local
|
|
86
92
|
|
|
87
93
|
# --- Local filesystem (default — great for dev and single-server deployments) --
|
|
88
94
|
# STORAGE_PATH=./uploads
|
|
95
|
+
# FORCE_LOCAL_STORAGE=true # Only with a durable volume mounted at STORAGE_PATH
|
|
89
96
|
|
|
90
97
|
# --- S3-compatible (AWS S3, Cloudflare R2, MinIO, Hetzner Object Storage, Backblaze B2) ---
|
|
91
98
|
# STORAGE_TYPE=s3
|
|
@@ -103,9 +110,15 @@ VITE_API_URL=http://localhost:3001
|
|
|
103
110
|
# S3_ENDPOINT=https://storage.googleapis.com
|
|
104
111
|
# S3_ACCESS_KEY_ID=GOOG... # HMAC access key
|
|
105
112
|
# S3_SECRET_ACCESS_KEY=... # HMAC secret
|
|
113
|
+
|
|
114
|
+
# --- Native GCS (Google Cloud Storage, Firebase Storage) ---
|
|
115
|
+
# STORAGE_TYPE=gcs
|
|
116
|
+
# GCS_BUCKET=my-gcs-bucket
|
|
117
|
+
# GCS_PROJECT_ID= # Optional — auto-detected from credentials
|
|
118
|
+
# GCS_KEY_FILENAME= # Omit on GCP: Workload Identity/ADC supplies credentials
|
|
106
119
|
#
|
|
107
|
-
# For
|
|
108
|
-
#
|
|
120
|
+
# For other providers (Azure Blob, etc.), implement the StorageController
|
|
121
|
+
# interface and pass it directly in your backend config.
|
|
109
122
|
|
|
110
123
|
# ── Backups (optional) ────────────────────────────────────────────────────────
|
|
111
124
|
# Manual backups: `rebase db backup --out ./backups` (or an s3://… URL).
|
|
@@ -4,57 +4,53 @@ A [Rebase](https://rebase.pro) project with a PostgreSQL backend.
|
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`rebase init` already generated a `.env` for you with a real `JWT_SECRET`, a
|
|
8
|
+
database password, and a free local database port. **Don't run
|
|
9
|
+
`cp .env.example .env`** — that would overwrite those generated values.
|
|
10
|
+
`.env.example` is a reference for the variables you can set, not a starting
|
|
11
|
+
point for this project.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
cp .env.example .env
|
|
11
|
-
# Edit .env — set JWT_SECRET and DATABASE_URL (see comments for generators)
|
|
12
|
-
|
|
13
|
-
docker compose up -d
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
That's it. Your app is running:
|
|
17
|
-
- **Frontend**: http://localhost (port 80)
|
|
18
|
-
- **Backend API**: http://localhost:3001
|
|
19
|
-
- **PostgreSQL**: localhost:5432
|
|
20
|
-
|
|
21
|
-
### Option 2: Local Development
|
|
22
|
-
|
|
23
|
-
#### Prerequisites
|
|
13
|
+
### Prerequisites
|
|
24
14
|
|
|
25
15
|
- [Node.js](https://nodejs.org) >= 18
|
|
26
16
|
- [pnpm](https://pnpm.io) or [npm](https://www.npmjs.com) (v7+)
|
|
27
|
-
-
|
|
17
|
+
- [Docker](https://www.docker.com) (to run the included PostgreSQL container),
|
|
18
|
+
or your own PostgreSQL database
|
|
28
19
|
|
|
29
|
-
|
|
20
|
+
### Run it
|
|
30
21
|
|
|
31
|
-
1. Install dependencies:
|
|
22
|
+
1. Install dependencies (skip if `init` already did this):
|
|
32
23
|
|
|
33
24
|
```bash
|
|
34
25
|
pnpm install # or: npm install
|
|
35
26
|
```
|
|
36
27
|
|
|
37
|
-
2.
|
|
28
|
+
2. Start the PostgreSQL database container:
|
|
38
29
|
|
|
39
30
|
```bash
|
|
40
|
-
|
|
41
|
-
# Edit .env — set DATABASE_URL, JWT_SECRET
|
|
31
|
+
docker compose up -d db
|
|
42
32
|
```
|
|
43
33
|
|
|
44
|
-
3.
|
|
34
|
+
3. Create the database tables from your collections:
|
|
45
35
|
|
|
46
36
|
```bash
|
|
47
|
-
pnpm run
|
|
48
|
-
pnpm run db:push # or: npm run db:push
|
|
37
|
+
pnpm run db:push # or: npm run db:push
|
|
49
38
|
```
|
|
50
39
|
|
|
51
|
-
4. Start the dev
|
|
40
|
+
4. Start the dev servers:
|
|
52
41
|
|
|
53
42
|
```bash
|
|
54
43
|
pnpm dev # or: npm run dev
|
|
55
44
|
```
|
|
56
45
|
|
|
57
|
-
|
|
46
|
+
Open **http://localhost:5173** — the admin panel. The first account you
|
|
47
|
+
register becomes the admin. The backend API (Hono + PostgreSQL) runs on
|
|
48
|
+
port 3001; the frontend (Vite + React) on port 5173.
|
|
49
|
+
|
|
50
|
+
> The `db:push` step is what creates the tables for the example `posts`,
|
|
51
|
+
> `authors`, and `tags` collections. Skip it and the admin panel still opens,
|
|
52
|
+
> but those collections will be empty and their API calls will fail until the
|
|
53
|
+
> tables exist.
|
|
58
54
|
|
|
59
55
|
## Project Structure
|
|
60
56
|
|
|
@@ -102,12 +98,21 @@ All configuration is managed through a single `.env` file in the project root. B
|
|
|
102
98
|
- **Frontend**: Vite reads `VITE_*` variables via `envDir` pointing to the project root
|
|
103
99
|
- **Scripts**: load via `dotenv` from the project root
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
`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.
|
|
106
102
|
|
|
107
103
|
## Production Deployment
|
|
108
104
|
|
|
105
|
+
The full stack — PostgreSQL, backend, and frontend — runs from
|
|
106
|
+
`docker compose`. The backend image builds and boots, but it does **not**
|
|
107
|
+
create your collection tables on its own, so push the schema once the
|
|
108
|
+
database is up before (or right after) starting the rest of the stack.
|
|
109
|
+
|
|
109
110
|
```bash
|
|
110
|
-
#
|
|
111
|
+
# 1. Start the database and create the tables from your collections
|
|
112
|
+
docker compose up -d db
|
|
113
|
+
pnpm run db:push # or: npm run db:push
|
|
114
|
+
|
|
115
|
+
# 2. Build and start the backend + frontend
|
|
111
116
|
docker compose up -d --build
|
|
112
117
|
|
|
113
118
|
# View logs
|
|
@@ -120,6 +125,11 @@ docker compose down
|
|
|
120
125
|
docker compose down -v
|
|
121
126
|
```
|
|
122
127
|
|
|
128
|
+
> `docker compose` reads the generated `.env` as-is. Set production values
|
|
129
|
+
> (a strong `DATABASE_PASSWORD`, `JWT_SECRET`, storage credentials, …) by
|
|
130
|
+
> editing `.env` directly — see `.env.example` for the full list. Do not
|
|
131
|
+
> `cp .env.example .env`; that discards the values `init` generated.
|
|
132
|
+
|
|
123
133
|
## Documentation
|
|
124
134
|
|
|
125
135
|
- [Rebase Docs](https://rebase.pro/docs)
|
|
@@ -123,6 +123,12 @@ pass: env.SMTP_PASS! }
|
|
|
123
123
|
}
|
|
124
124
|
: undefined
|
|
125
125
|
},
|
|
126
|
+
// File storage is opt-in. With no bucket configured, storage is OFF in
|
|
127
|
+
// production — the upload routes answer 501 STORAGE_NOT_CONFIGURED —
|
|
128
|
+
// rather than writing to the container filesystem, which is erased on
|
|
129
|
+
// every restart and redeploy. Uploads that fail loudly are recoverable;
|
|
130
|
+
// uploads that succeed into a disk about to be wiped are not.
|
|
131
|
+
// Local disk stays the default in development, where it is what you want.
|
|
126
132
|
storage: env.STORAGE_TYPE === "s3"
|
|
127
133
|
? {
|
|
128
134
|
type: "s3",
|
|
@@ -133,10 +139,21 @@ pass: env.SMTP_PASS! }
|
|
|
133
139
|
endpoint: env.S3_ENDPOINT,
|
|
134
140
|
forcePathStyle: env.S3_FORCE_PATH_STYLE
|
|
135
141
|
}
|
|
136
|
-
:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
: env.STORAGE_TYPE === "gcs"
|
|
143
|
+
? {
|
|
144
|
+
type: "gcs",
|
|
145
|
+
bucket: env.GCS_BUCKET!,
|
|
146
|
+
projectId: env.GCS_PROJECT_ID,
|
|
147
|
+
keyFilename: env.GCS_KEY_FILENAME
|
|
148
|
+
}
|
|
149
|
+
// Set FORCE_LOCAL_STORAGE=true only if this deployment really
|
|
150
|
+
// does have a durable volume mounted at STORAGE_PATH.
|
|
151
|
+
: isProduction && !env.FORCE_LOCAL_STORAGE
|
|
152
|
+
? undefined
|
|
153
|
+
: {
|
|
154
|
+
type: "local",
|
|
155
|
+
basePath: env.STORAGE_PATH || path.resolve(__dirname, "../../uploads")
|
|
156
|
+
},
|
|
140
157
|
history: true
|
|
141
158
|
});
|
|
142
159
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://rebase.pro/schemas/rebase.json",
|
|
3
|
+
"runtime": "^1",
|
|
4
|
+
"apps": {
|
|
5
|
+
"backend": {
|
|
6
|
+
"type": "backend"
|
|
7
|
+
},
|
|
8
|
+
"web": {
|
|
9
|
+
"type": "static",
|
|
10
|
+
"root": "frontend",
|
|
11
|
+
"build": "npm run build --workspace frontend",
|
|
12
|
+
"output": "frontend/dist",
|
|
13
|
+
"spa": true
|
|
14
|
+
},
|
|
15
|
+
"admin": {
|
|
16
|
+
"type": "admin",
|
|
17
|
+
"mode": "hosted"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|