@rebasepro/cli 0.2.5 → 0.4.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/package.json +5 -5
- package/templates/template/.env.example +1 -0
- package/templates/template/backend/src/env.ts +13 -2
- package/templates/template/backend/src/index.ts +39 -3
- package/templates/template/config/collections/users.ts +6 -0
- package/templates/template/config/collections/roles.ts +0 -62
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Developer tools for Rebase projects",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"execa": "^9.6.1",
|
|
32
32
|
"inquirer": "12.11.1",
|
|
33
33
|
"jiti": "^2.7.0",
|
|
34
|
-
"@rebasepro/sdk-generator": "0.
|
|
35
|
-
"@rebasepro/
|
|
36
|
-
"@rebasepro/
|
|
37
|
-
"@rebasepro/server-postgresql": "0.
|
|
34
|
+
"@rebasepro/sdk-generator": "0.4.0",
|
|
35
|
+
"@rebasepro/types": "0.4.0",
|
|
36
|
+
"@rebasepro/server-core": "0.4.0",
|
|
37
|
+
"@rebasepro/server-postgresql": "0.4.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^20.19.41",
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import * as dotenv from "dotenv";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
-
import { loadEnv } from "@rebasepro/server-core";
|
|
4
|
+
import { loadEnv, z } from "@rebasepro/server-core";
|
|
5
5
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
|
|
9
9
|
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
|
10
10
|
|
|
11
|
-
export const env = loadEnv(
|
|
11
|
+
export const env = loadEnv({
|
|
12
|
+
extend: z.object({
|
|
13
|
+
SMTP_HOST: z.string().optional(),
|
|
14
|
+
SMTP_PORT: z.string().default("587").transform(Number),
|
|
15
|
+
SMTP_SECURE: z.enum(["true", "false", ""]).default("false").transform(v => v === "true"),
|
|
16
|
+
SMTP_USER: z.string().optional(),
|
|
17
|
+
SMTP_PASS: z.string().optional(),
|
|
18
|
+
SMTP_FROM: z.string().optional(),
|
|
19
|
+
SMTP_NAME: z.string().optional(),
|
|
20
|
+
APP_NAME: z.string().default("Rebase"),
|
|
21
|
+
})
|
|
22
|
+
});
|
|
@@ -13,9 +13,11 @@ import {
|
|
|
13
13
|
cleanupDevPortFile,
|
|
14
14
|
logger
|
|
15
15
|
} from "@rebasepro/server-core";
|
|
16
|
+
import type { SecurityRule } from "@rebasepro/types";
|
|
16
17
|
import { createPostgresDatabaseConnection, createPostgresAdapter } from "@rebasepro/server-postgresql";
|
|
17
18
|
import { enums, relations, tables } from "./schema.generated.js";
|
|
18
19
|
import { env } from "./env.js";
|
|
20
|
+
import usersCollection from "../../config/collections/users.js";
|
|
19
21
|
|
|
20
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
23
|
const __dirname = path.dirname(__filename);
|
|
@@ -25,7 +27,16 @@ const app: Hono<HonoEnv> = new Hono<HonoEnv>();
|
|
|
25
27
|
|
|
26
28
|
const isProduction = env.NODE_ENV === "production";
|
|
27
29
|
const allowedOrigins = isProduction
|
|
28
|
-
? (
|
|
30
|
+
? (() => {
|
|
31
|
+
const origins = env.CORS_ORIGINS || env.FRONTEND_URL;
|
|
32
|
+
if (!origins) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"CORS_ORIGINS or FRONTEND_URL must be set in production. " +
|
|
35
|
+
"Example: CORS_ORIGINS=https://yourdomain.com"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return origins.split(",").map(s => s.trim());
|
|
39
|
+
})()
|
|
29
40
|
: [];
|
|
30
41
|
|
|
31
42
|
app.use("/*", cors({
|
|
@@ -49,6 +60,13 @@ async function startServer() {
|
|
|
49
60
|
const PORT = env.PORT;
|
|
50
61
|
const server = createServer(getRequestListener(app.fetch));
|
|
51
62
|
|
|
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", access: "public" },
|
|
67
|
+
{ operations: ["insert", "update", "delete"], roles: ["admin"] }
|
|
68
|
+
];
|
|
69
|
+
|
|
52
70
|
const backend = await initializeRebaseBackend({
|
|
53
71
|
collectionsDir: path.resolve(__dirname, "../../config/collections"),
|
|
54
72
|
functionsDir: path.resolve(__dirname, "../functions"),
|
|
@@ -63,6 +81,7 @@ relations },
|
|
|
63
81
|
connectionString
|
|
64
82
|
}),
|
|
65
83
|
auth: {
|
|
84
|
+
collection: usersCollection,
|
|
66
85
|
jwtSecret,
|
|
67
86
|
accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN,
|
|
68
87
|
refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN,
|
|
@@ -71,7 +90,23 @@ relations },
|
|
|
71
90
|
? { clientId: env.GOOGLE_CLIENT_ID }
|
|
72
91
|
: undefined,
|
|
73
92
|
seedDefaultRoles: true,
|
|
74
|
-
allowRegistration: env.ALLOW_REGISTRATION
|
|
93
|
+
allowRegistration: env.ALLOW_REGISTRATION,
|
|
94
|
+
email: env.SMTP_HOST
|
|
95
|
+
? {
|
|
96
|
+
from: env.SMTP_FROM || `${env.APP_NAME} <noreply@rebase.pro>`,
|
|
97
|
+
smtp: {
|
|
98
|
+
host: env.SMTP_HOST,
|
|
99
|
+
port: env.SMTP_PORT,
|
|
100
|
+
secure: env.SMTP_SECURE,
|
|
101
|
+
auth: env.SMTP_USER
|
|
102
|
+
? { user: env.SMTP_USER, pass: env.SMTP_PASS! }
|
|
103
|
+
: undefined,
|
|
104
|
+
name: env.SMTP_NAME,
|
|
105
|
+
},
|
|
106
|
+
appName: env.APP_NAME,
|
|
107
|
+
resetPasswordUrl: env.FRONTEND_URL,
|
|
108
|
+
}
|
|
109
|
+
: undefined,
|
|
75
110
|
},
|
|
76
111
|
storage: env.STORAGE_TYPE === "s3"
|
|
77
112
|
? {
|
|
@@ -87,7 +122,8 @@ relations },
|
|
|
87
122
|
type: "local",
|
|
88
123
|
basePath: env.STORAGE_PATH || path.resolve(__dirname, "../../uploads")
|
|
89
124
|
},
|
|
90
|
-
history: true
|
|
125
|
+
history: true,
|
|
126
|
+
defaultSecurityRules
|
|
91
127
|
});
|
|
92
128
|
|
|
93
129
|
// ─── Health check ─────────────────────────────────────────────
|
|
@@ -11,6 +11,10 @@ const usersCollection: EntityCollection = {
|
|
|
11
11
|
group: "Settings",
|
|
12
12
|
openEntityMode: "dialog",
|
|
13
13
|
disableDefaultActions: ["copy"],
|
|
14
|
+
securityRules: [
|
|
15
|
+
{ operation: "select", roles: ["admin"] },
|
|
16
|
+
{ operations: ["insert", "update", "delete"], roles: ["admin"] }
|
|
17
|
+
],
|
|
14
18
|
entityActions: [
|
|
15
19
|
resetPasswordAction,
|
|
16
20
|
{
|
|
@@ -53,6 +57,7 @@ const usersCollection: EntityCollection = {
|
|
|
53
57
|
roles: {
|
|
54
58
|
name: "Roles",
|
|
55
59
|
type: "array",
|
|
60
|
+
columnType: "text[]",
|
|
56
61
|
of: {
|
|
57
62
|
name: "Role",
|
|
58
63
|
type: "string",
|
|
@@ -113,6 +118,7 @@ const usersCollection: EntityCollection = {
|
|
|
113
118
|
name: "Created At",
|
|
114
119
|
type: "date",
|
|
115
120
|
columnName: "created_at",
|
|
121
|
+
autoValue: "on_create",
|
|
116
122
|
ui: {
|
|
117
123
|
readOnly: true
|
|
118
124
|
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { PostgresCollection, EntityCollection } from "@rebasepro/types";
|
|
2
|
-
|
|
3
|
-
const rolesCollection: PostgresCollection = {
|
|
4
|
-
name: "Roles",
|
|
5
|
-
singularName: "Role",
|
|
6
|
-
slug: "roles",
|
|
7
|
-
table: "roles",
|
|
8
|
-
schema: "rebase",
|
|
9
|
-
icon: "Shield",
|
|
10
|
-
group: "Settings",
|
|
11
|
-
properties: {
|
|
12
|
-
id: {
|
|
13
|
-
name: "ID",
|
|
14
|
-
type: "string",
|
|
15
|
-
isId: "manual",
|
|
16
|
-
validation: {
|
|
17
|
-
required: true
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
name: {
|
|
21
|
-
name: "Name",
|
|
22
|
-
type: "string",
|
|
23
|
-
validation: {
|
|
24
|
-
required: true
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
isAdmin: {
|
|
28
|
-
name: "Is Admin",
|
|
29
|
-
type: "boolean",
|
|
30
|
-
columnName: "is_admin",
|
|
31
|
-
defaultValue: false
|
|
32
|
-
},
|
|
33
|
-
defaultPermissions: {
|
|
34
|
-
name: "Default Permissions",
|
|
35
|
-
type: "map",
|
|
36
|
-
columnName: "default_permissions",
|
|
37
|
-
properties: {
|
|
38
|
-
read: { name: "Read", type: "boolean" },
|
|
39
|
-
create: { name: "Create", type: "boolean" },
|
|
40
|
-
edit: { name: "Edit", type: "boolean" },
|
|
41
|
-
delete: { name: "Delete", type: "boolean" }
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
collectionPermissions: {
|
|
45
|
-
name: "Collection Permissions",
|
|
46
|
-
type: "map",
|
|
47
|
-
columnName: "collection_permissions"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
rolesCollection.securityRules = [
|
|
53
|
-
{
|
|
54
|
-
name: "roles_public_access",
|
|
55
|
-
mode: "permissive",
|
|
56
|
-
operation: "all",
|
|
57
|
-
pgRoles: ["public"],
|
|
58
|
-
using: "true"
|
|
59
|
-
}
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
export default rolesCollection;
|