@rebasepro/cli 0.8.0 → 0.9.1-canary.09aaf62
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 +32 -3
- package/dist/commands/cloud/auth.d.ts +3 -0
- package/dist/commands/cloud/context.d.ts +157 -0
- package/dist/commands/cloud/databases.d.ts +1 -0
- package/dist/commands/cloud/deploy.d.ts +2 -0
- package/dist/commands/cloud/deployments.d.ts +39 -0
- package/dist/commands/cloud/domains.d.ts +1 -0
- package/dist/commands/cloud/env.d.ts +6 -0
- package/dist/commands/cloud/extensions.d.ts +3 -0
- package/dist/commands/cloud/index.d.ts +1 -0
- package/dist/commands/cloud/link.d.ts +5 -0
- package/dist/commands/cloud/orgs.d.ts +1 -0
- package/dist/commands/cloud/power.d.ts +4 -0
- package/dist/commands/cloud/projects.d.ts +13 -0
- package/dist/commands/cloud/resources.d.ts +6 -0
- package/dist/commands/cloud/settings.d.ts +8 -0
- package/dist/commands/init.d.ts +23 -0
- package/dist/commands/skills.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +5347 -1804
- package/dist/index.es.js.map +1 -1
- package/dist/utils/package-manager.d.ts +19 -5
- package/dist/utils/project.d.ts +1 -1
- package/package.json +8 -7
- package/templates/overlays/baas/README.md +55 -0
- package/templates/overlays/baas/backend/package.json +31 -0
- package/templates/overlays/baas/backend/src/index.ts +192 -0
- package/templates/overlays/baas/backend/tsconfig.json +19 -0
- package/templates/overlays/baas/package.json +42 -0
- package/templates/overlays/baas/pnpm-workspace.yaml +9 -0
- package/templates/template/.env.example +27 -1
- package/templates/template/backend/functions/hello.ts +11 -7
- package/templates/template/backend/package.json +7 -7
- package/templates/template/backend/src/env.ts +32 -2
- package/templates/template/backend/src/index.ts +33 -27
- package/templates/template/config/collections/authors.ts +2 -2
- package/templates/template/config/collections/index.ts +20 -0
- package/templates/template/config/collections/posts.ts +2 -2
- package/templates/template/config/collections/presets/blank/index.ts +3 -1
- package/templates/template/config/collections/presets/ecommerce/categories.ts +2 -2
- package/templates/template/config/collections/presets/ecommerce/index.ts +3 -1
- package/templates/template/config/collections/presets/ecommerce/orders.ts +2 -2
- package/templates/template/config/collections/presets/ecommerce/products.ts +2 -2
- package/templates/template/config/collections/tags.ts +2 -2
- package/templates/template/config/collections/users.ts +4 -2
- package/templates/template/config/package.json +1 -2
- package/templates/template/frontend/package.json +2 -3
- package/templates/template/frontend/src/App.tsx +7 -5
- package/templates/template/frontend/vite.config.ts +15 -1
- package/templates/template/gitignore +31 -0
- package/templates/template/npmrc +10 -0
|
@@ -7,14 +7,14 @@ import path from "path";
|
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import {
|
|
9
9
|
initializeRebaseBackend,
|
|
10
|
+
installShutdownHandlers,
|
|
10
11
|
serveSPA,
|
|
11
12
|
HonoEnv,
|
|
12
13
|
listenWithPortRetry,
|
|
13
14
|
cleanupDevPortFile,
|
|
14
15
|
logger
|
|
15
|
-
} from "@rebasepro/server
|
|
16
|
-
import
|
|
17
|
-
import { createPostgresDatabaseConnection, createPostgresAdapter } from "@rebasepro/server-postgresql";
|
|
16
|
+
} from "@rebasepro/server";
|
|
17
|
+
import { createPostgresDatabaseConnection, createPostgresAdapter } from "@rebasepro/server-postgres";
|
|
18
18
|
import { enums, relations, tables } from "./schema.generated.js";
|
|
19
19
|
import { env } from "./env.js";
|
|
20
20
|
import usersCollection from "../../config/collections/users.js";
|
|
@@ -39,10 +39,26 @@ const allowedOrigins = isProduction
|
|
|
39
39
|
})()
|
|
40
40
|
: [];
|
|
41
41
|
|
|
42
|
+
// In dev we still restrict which origins are reflected. Because `credentials`
|
|
43
|
+
// is enabled, reflecting an arbitrary Origin would let any website the
|
|
44
|
+
// developer happens to visit make credentialed cross-origin requests to this
|
|
45
|
+
// dev server (and read the responses) using the developer's session. So dev
|
|
46
|
+
// reflects only localhost origins; requests with no Origin (curl, same-origin)
|
|
47
|
+
// are unaffected.
|
|
48
|
+
const isLocalhostOrigin = (origin: string): boolean => {
|
|
49
|
+
try {
|
|
50
|
+
const { hostname } = new URL(origin);
|
|
51
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
42
57
|
app.use("/*", cors({
|
|
43
58
|
origin: (origin) => {
|
|
44
|
-
if (
|
|
45
|
-
|
|
59
|
+
if (isProduction) return allowedOrigins.includes(origin) ? origin : null;
|
|
60
|
+
if (!origin) return "*";
|
|
61
|
+
return isLocalhostOrigin(origin) ? origin : null;
|
|
46
62
|
},
|
|
47
63
|
credentials: true
|
|
48
64
|
}));
|
|
@@ -60,15 +76,6 @@ async function startServer() {
|
|
|
60
76
|
const PORT = env.PORT;
|
|
61
77
|
const server = createServer(getRequestListener(app.fetch));
|
|
62
78
|
|
|
63
|
-
// Default security rules for collections that don't define their own.
|
|
64
|
-
// Authenticated users can read all rows; only admins can write.
|
|
65
|
-
const defaultSecurityRules: SecurityRule[] = [
|
|
66
|
-
{ operation: "select",
|
|
67
|
-
access: "public" },
|
|
68
|
-
{ operations: ["insert", "update", "delete"],
|
|
69
|
-
roles: ["admin"] }
|
|
70
|
-
];
|
|
71
|
-
|
|
72
79
|
const backend = await initializeRebaseBackend({
|
|
73
80
|
collectionsDir: path.resolve(__dirname, "../../config/collections"),
|
|
74
81
|
functionsDir: path.resolve(__dirname, "../functions"),
|
|
@@ -88,6 +95,12 @@ relations },
|
|
|
88
95
|
accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN,
|
|
89
96
|
refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN,
|
|
90
97
|
serviceKey: env.REBASE_SERVICE_KEY,
|
|
98
|
+
// Cookie-based auth: the refresh token is stored in an httpOnly
|
|
99
|
+
// cookie (not readable by JS) instead of localStorage, so it is
|
|
100
|
+
// not exposed to XSS. The frontend opts in via
|
|
101
|
+
// `authFlowMode: "cookie"` on createRebaseClient. Requires CORS
|
|
102
|
+
// `credentials: true` (set above).
|
|
103
|
+
cookieAuth: { sameSite: "Lax" },
|
|
91
104
|
google: env.GOOGLE_CLIENT_ID
|
|
92
105
|
? { clientId: env.GOOGLE_CLIENT_ID }
|
|
93
106
|
: undefined,
|
|
@@ -124,8 +137,7 @@ pass: env.SMTP_PASS! }
|
|
|
124
137
|
type: "local",
|
|
125
138
|
basePath: env.STORAGE_PATH || path.resolve(__dirname, "../../uploads")
|
|
126
139
|
},
|
|
127
|
-
history: true
|
|
128
|
-
defaultSecurityRules
|
|
140
|
+
history: true
|
|
129
141
|
});
|
|
130
142
|
|
|
131
143
|
// ─── Health check ─────────────────────────────────────────────
|
|
@@ -163,17 +175,11 @@ pass: env.SMTP_PASS! }
|
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
// ─── Graceful Shutdown ───────────────────────────────────────────────
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
await pool.end();
|
|
172
|
-
logger.info("Database pool closed");
|
|
173
|
-
process.exit(0);
|
|
174
|
-
};
|
|
175
|
-
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
176
|
-
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
178
|
+
// Drains HTTP, stops crons, tears down realtime, then closes the pool.
|
|
179
|
+
// Guards against double signals and force-exits if shutdown hangs.
|
|
180
|
+
installShutdownHandlers(backend, {
|
|
181
|
+
onCleanup: () => pool.end()
|
|
182
|
+
});
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
startServer().catch(err => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
|
|
3
|
-
const authorsCollection:
|
|
3
|
+
const authorsCollection: CollectionConfig = {
|
|
4
4
|
name: "Authors",
|
|
5
5
|
singularName: "Author",
|
|
6
6
|
slug: "authors",
|
|
@@ -2,5 +2,25 @@ import postsCollection from "./posts.js";
|
|
|
2
2
|
import authorsCollection from "./authors.js";
|
|
3
3
|
import tagsCollection from "./tags.js";
|
|
4
4
|
import usersCollection from "./users.js";
|
|
5
|
+
import type { SecurityRule } from "@rebasepro/types";
|
|
5
6
|
|
|
6
7
|
export const collections = [postsCollection, authorsCollection, tagsCollection, usersCollection];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Applied to any collection in this directory that declares no
|
|
11
|
+
* `securityRules` of its own: anyone can read, only admins can write.
|
|
12
|
+
*
|
|
13
|
+
* These live here, next to the collections, because `rebase db push`
|
|
14
|
+
* generates the Postgres policies from these files — that is what actually
|
|
15
|
+
* enforces access. A default declared on the server could never reach the
|
|
16
|
+
* database.
|
|
17
|
+
*
|
|
18
|
+
* A collection that declares neither its own rules nor inherits these is
|
|
19
|
+
* locked by default: admin-only.
|
|
20
|
+
*/
|
|
21
|
+
export const defaultSecurityRules: SecurityRule[] = [
|
|
22
|
+
{ operation: "select",
|
|
23
|
+
access: "public" },
|
|
24
|
+
{ operations: ["insert", "update", "delete"],
|
|
25
|
+
roles: ["admin"] }
|
|
26
|
+
];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
import authorsCollection from "./authors.js";
|
|
3
3
|
import tagsCollection from "./tags.js";
|
|
4
4
|
|
|
5
|
-
const postsCollection:
|
|
5
|
+
const postsCollection: CollectionConfig = {
|
|
6
6
|
name: "Posts",
|
|
7
7
|
singularName: "Post",
|
|
8
8
|
slug: "posts",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Resolves after `rebase init` copies this file into config/collections/,
|
|
2
|
+
// next to the shared users.ts — not from inside presets/, which never runs.
|
|
3
|
+
import usersCollection from "./users.js";
|
|
2
4
|
|
|
3
5
|
export const collections = [usersCollection];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
|
|
3
|
-
const categoriesCollection:
|
|
3
|
+
const categoriesCollection: CollectionConfig = {
|
|
4
4
|
name: "Categories",
|
|
5
5
|
singularName: "Category",
|
|
6
6
|
slug: "categories",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import productsCollection from "./products.js";
|
|
2
2
|
import categoriesCollection from "./categories.js";
|
|
3
3
|
import ordersCollection from "./orders.js";
|
|
4
|
-
|
|
4
|
+
// Resolves after `rebase init` copies this file into config/collections/,
|
|
5
|
+
// next to the shared users.ts — not from inside presets/, which never runs.
|
|
6
|
+
import usersCollection from "./users.js";
|
|
5
7
|
|
|
6
8
|
export const collections = [productsCollection, categoriesCollection, ordersCollection, usersCollection];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
import categoriesCollection from "./categories.js";
|
|
3
3
|
|
|
4
|
-
const productsCollection:
|
|
4
|
+
const productsCollection: CollectionConfig = {
|
|
5
5
|
name: "Products",
|
|
6
6
|
singularName: "Product",
|
|
7
7
|
slug: "products",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
|
|
3
|
-
const usersCollection:
|
|
3
|
+
const usersCollection: CollectionConfig = {
|
|
4
4
|
name: "Users",
|
|
5
5
|
singularName: "User",
|
|
6
6
|
slug: "users",
|
|
@@ -66,6 +66,7 @@ roles: ["admin"] }
|
|
|
66
66
|
name: "Password Hash",
|
|
67
67
|
type: "string",
|
|
68
68
|
columnName: "password_hash",
|
|
69
|
+
excludeFromApi: true,
|
|
69
70
|
ui: {
|
|
70
71
|
hideFromCollection: true,
|
|
71
72
|
disabled: { hidden: true }
|
|
@@ -85,6 +86,7 @@ roles: ["admin"] }
|
|
|
85
86
|
name: "Email Verification Token",
|
|
86
87
|
type: "string",
|
|
87
88
|
columnName: "email_verification_token",
|
|
89
|
+
excludeFromApi: true,
|
|
88
90
|
ui: {
|
|
89
91
|
hideFromCollection: true,
|
|
90
92
|
disabled: { hidden: true }
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@rebasepro/
|
|
8
|
-
"@rebasepro/
|
|
9
|
-
"@rebasepro/plugin-data-enhancement": "workspace:*",
|
|
7
|
+
"@rebasepro/app": "workspace:*",
|
|
8
|
+
"@rebasepro/plugin-ai": "workspace:*",
|
|
10
9
|
"@rebasepro/client": "workspace:*",
|
|
11
10
|
"@rebasepro/admin": "workspace:*",
|
|
12
11
|
"@rebasepro/studio": "workspace:*",
|
|
@@ -3,9 +3,8 @@ import React from "react";
|
|
|
3
3
|
import "@fontsource/jetbrains-mono";
|
|
4
4
|
import "@fontsource/rubik";
|
|
5
5
|
|
|
6
|
-
import { useRebaseAuthController } from "@rebasepro/
|
|
7
|
-
import {
|
|
8
|
-
import { RebaseCMS, RebaseShell } from "@rebasepro/admin";
|
|
6
|
+
import { Rebase, RebaseAuth, useRebaseAuthController } from "@rebasepro/app";
|
|
7
|
+
import { RebaseAdmin, RebaseShell } from "@rebasepro/admin";
|
|
9
8
|
import { ErrorBoundary } from "@rebasepro/ui";
|
|
10
9
|
import { RebaseStudio } from "@rebasepro/studio";
|
|
11
10
|
import { createRebaseClient } from "@rebasepro/client";
|
|
@@ -17,7 +16,10 @@ const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
|
|
17
16
|
|
|
18
17
|
export function App() {
|
|
19
18
|
const rebaseClient = React.useMemo(() => createRebaseClient({
|
|
20
|
-
baseUrl: API_URL
|
|
19
|
+
baseUrl: API_URL,
|
|
20
|
+
// Store the refresh token in an httpOnly cookie (XSS-safe) rather than
|
|
21
|
+
// localStorage. The backend issues it via `auth.cookieAuth`.
|
|
22
|
+
auth: { authFlowMode: "cookie" }
|
|
21
23
|
}), []);
|
|
22
24
|
|
|
23
25
|
const authController = useRebaseAuthController({
|
|
@@ -32,7 +34,7 @@ export function App() {
|
|
|
32
34
|
authController={authController}
|
|
33
35
|
>
|
|
34
36
|
<RebaseAuth />
|
|
35
|
-
<
|
|
37
|
+
<RebaseAdmin
|
|
36
38
|
collections={collections}
|
|
37
39
|
/>
|
|
38
40
|
<RebaseStudio/>
|
|
@@ -3,10 +3,24 @@ import { defineConfig } from "vite";
|
|
|
3
3
|
import react from "@vitejs/plugin-react";
|
|
4
4
|
import svgr from "vite-plugin-svgr";
|
|
5
5
|
import tailwindcss from "@tailwindcss/vite";
|
|
6
|
-
import { rebaseCollectionsPlugin } from "@rebasepro/
|
|
6
|
+
import { rebaseCollectionsPlugin } from "@rebasepro/app/vitePlugin";
|
|
7
7
|
|
|
8
8
|
export default defineConfig({
|
|
9
9
|
envDir: path.resolve(__dirname, ".."),
|
|
10
|
+
// Force a single copy of React and React Router across the app and all
|
|
11
|
+
// @rebasepro/* packages. Without this, a locally `link:`ed Rebase checkout
|
|
12
|
+
// resolves its own copies of react-router, producing "multiple copies of
|
|
13
|
+
// React" and "useBlocker must be used within a data router" errors in the
|
|
14
|
+
// admin. Safe to keep for npm-installed setups too.
|
|
15
|
+
resolve: {
|
|
16
|
+
dedupe: [
|
|
17
|
+
"react",
|
|
18
|
+
"react-dom",
|
|
19
|
+
"react-router",
|
|
20
|
+
"react-router-dom",
|
|
21
|
+
"@remix-run/router"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
10
24
|
esbuild: {
|
|
11
25
|
logOverride: { "this-is-undefined-in-esm": "silent" }
|
|
12
26
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
|
|
4
|
+
# Build output
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
|
|
8
|
+
# Environment (secrets — .env.example is tracked)
|
|
9
|
+
.env
|
|
10
|
+
.env.local
|
|
11
|
+
!.env.example
|
|
12
|
+
|
|
13
|
+
# IDE
|
|
14
|
+
.idea/
|
|
15
|
+
.vscode/
|
|
16
|
+
*.swp
|
|
17
|
+
*.swo
|
|
18
|
+
|
|
19
|
+
# OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
|
|
23
|
+
# Drizzle
|
|
24
|
+
drizzle/meta/
|
|
25
|
+
|
|
26
|
+
# Uploads
|
|
27
|
+
uploads/
|
|
28
|
+
|
|
29
|
+
# Rebase dev
|
|
30
|
+
.rebase-dev-url
|
|
31
|
+
.rebase-dev-port
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Ensure pnpm links local workspace packages when version specifiers
|
|
2
|
+
# like "*" are used (instead of the pnpm-specific "workspace:*" protocol).
|
|
3
|
+
# This makes the project compatible with both pnpm and npm workspaces.
|
|
4
|
+
link-workspace-packages=true
|
|
5
|
+
|
|
6
|
+
# Disable automatic dependency check before running scripts to prevent background install failures in read-only environments
|
|
7
|
+
verify-deps-before-run=false
|
|
8
|
+
|
|
9
|
+
# Prevent pnpm from prompting for confirmation when purging modules in non-TTY environments (e.g. Docker, CI)
|
|
10
|
+
confirm-modules-purge=false
|