@rebasepro/cli 0.3.0 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/cli",
3
- "version": "0.3.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/server-core": "0.3.0",
35
- "@rebasepro/sdk-generator": "0.3.0",
36
- "@rebasepro/server-postgresql": "0.3.0",
37
- "@rebasepro/types": "0.3.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",
@@ -46,6 +46,7 @@ ALLOW_REGISTRATION=true
46
46
  # SMTP_USER=
47
47
  # SMTP_PASS=
48
48
  # SMTP_FROM=noreply@yourapp.com
49
+ # SMTP_NAME=
49
50
  # APP_NAME=Your App Name
50
51
 
51
52
  # ── URLs ──────────────────────────────────────────────────────────────────────
@@ -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
- ? (env.CORS_ORIGINS || env.FRONTEND_URL || "https://yourdomain.com").split(",").map(s => s.trim())
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 ─────────────────────────────────────────────
@@ -2,6 +2,5 @@ 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 rolesCollection from "./roles.js";
6
5
 
7
- export const collections = [postsCollection, authorsCollection, tagsCollection, usersCollection, rolesCollection];
6
+ export const collections = [postsCollection, authorsCollection, tagsCollection, usersCollection];
@@ -1,6 +1,5 @@
1
- import { EntityCollection } from "@rebasepro/types";
2
- import { resetPasswordAction, deleteEntityAction, RolesFilterSelect, UserRolesSelectField } from "@rebasepro/admin";
3
- import rolesCollection from "./roles.js";
1
+ import type { EntityCollection } from "@rebasepro/types";
2
+ import { resetPasswordAction, deleteEntityAction } from "@rebasepro/admin";
4
3
 
5
4
  const usersCollection: EntityCollection = {
6
5
  name: "Users",
@@ -12,7 +11,10 @@ const usersCollection: EntityCollection = {
12
11
  group: "Settings",
13
12
  openEntityMode: "dialog",
14
13
  disableDefaultActions: ["copy"],
15
- Actions: [RolesFilterSelect],
14
+ securityRules: [
15
+ { operation: "select", roles: ["admin"] },
16
+ { operations: ["insert", "update", "delete"], roles: ["admin"] }
17
+ ],
16
18
  entityActions: [
17
19
  resetPasswordAction,
18
20
  {
@@ -41,6 +43,7 @@ const usersCollection: EntityCollection = {
41
43
  displayName: {
42
44
  name: "Name",
43
45
  type: "string",
46
+ columnName: "display_name",
44
47
  validation: {
45
48
  required: true
46
49
  }
@@ -48,21 +51,21 @@ const usersCollection: EntityCollection = {
48
51
  photoURL: {
49
52
  name: "Photo URL",
50
53
  type: "string",
54
+ columnName: "photo_url",
51
55
  url: "image"
52
56
  },
53
57
  roles: {
54
58
  name: "Roles",
55
- type: "relation",
56
- target: () => rolesCollection,
57
- cardinality: "many",
58
- direction: "owning",
59
- through: {
60
- table: "user_roles",
61
- sourceColumn: "user_id",
62
- targetColumn: "role_id"
63
- },
64
- ui: {
65
- Field: UserRolesSelectField
59
+ type: "array",
60
+ columnType: "text[]",
61
+ of: {
62
+ name: "Role",
63
+ type: "string",
64
+ enum: {
65
+ admin: "Admin",
66
+ editor: "Editor",
67
+ viewer: "Viewer"
68
+ }
66
69
  }
67
70
  },
68
71
  passwordHash: {
@@ -114,6 +117,8 @@ const usersCollection: EntityCollection = {
114
117
  createdAt: {
115
118
  name: "Created At",
116
119
  type: "date",
120
+ columnName: "created_at",
121
+ autoValue: "on_create",
117
122
  ui: {
118
123
  readOnly: true
119
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;