@rebasepro/cli 0.24.0 → 0.24.1-canary.g19596ef
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/bin/rebase.js +3 -0
- package/dist/agent-setup-B31F00DC.js +324 -0
- package/dist/agent-setup-B31F00DC.js.map +1 -0
- package/dist/api-keys-Bl1I9q7M.js +525 -0
- package/dist/api-keys-Bl1I9q7M.js.map +1 -0
- package/dist/apps-CNgQJjg5.js +340 -0
- package/dist/apps-CNgQJjg5.js.map +1 -0
- package/dist/args-BnmsEXvu.js +109 -0
- package/dist/args-BnmsEXvu.js.map +1 -0
- package/dist/atlas-dev-database-D_LP_Woq.js +62 -0
- package/dist/atlas-dev-database-D_LP_Woq.js.map +1 -0
- package/dist/auth-aySHEF45.js +414 -0
- package/dist/auth-aySHEF45.js.map +1 -0
- package/dist/branch-pointer-DvSHBjjz.js +2 -0
- package/dist/build-DIHWIqO4.js +336 -0
- package/dist/build-DIHWIqO4.js.map +1 -0
- package/dist/bundle-CqURVN-K.js +1621 -0
- package/dist/bundle-CqURVN-K.js.map +1 -0
- package/dist/bundle.d.ts +6 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cloud-DjByQlkW.js +9667 -0
- package/dist/cloud-DjByQlkW.js.map +1 -0
- package/dist/collection-drift-BKj_BoMq.js +106 -0
- package/dist/collection-drift-BKj_BoMq.js.map +1 -0
- package/dist/commands/api-keys.d.ts +2 -0
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/db.d.ts +64 -24
- package/dist/commands/dev.d.ts +103 -6
- package/dist/commands/generate_sdk.d.ts +26 -0
- package/dist/commands/init.d.ts +58 -0
- package/dist/commands/skills.d.ts +22 -7
- package/dist/commands/start.d.ts +15 -0
- package/dist/commands/storage.d.ts +46 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/consent-ClRHmcxf.js +76 -0
- package/dist/consent-ClRHmcxf.js.map +1 -0
- package/dist/console-D6SJf8k_.js +102 -0
- package/dist/console-D6SJf8k_.js.map +1 -0
- package/dist/context-BqhRydBg.js +1234 -0
- package/dist/context-BqhRydBg.js.map +1 -0
- package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
- package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
- package/dist/daemon-pzsQwDHa.js +2 -0
- package/dist/db-B7RuNuaQ.js +1312 -0
- package/dist/db-B7RuNuaQ.js.map +1 -0
- package/dist/derive-Dl0z2OWi.js +484 -0
- package/dist/derive-Dl0z2OWi.js.map +1 -0
- package/dist/dev-CYr_WXan.js +1599 -0
- package/dist/dev-CYr_WXan.js.map +1 -0
- package/dist/dev-db/atlas-dev-database.d.ts +32 -0
- package/dist/dev-db/daemon-entry.d.ts +13 -0
- package/dist/dev-db/last-dev-database.d.ts +41 -0
- package/dist/dev-db/prepare.d.ts +19 -3
- package/dist/dev-db/pull.d.ts +90 -5
- package/dist/dev-db/resolve.d.ts +26 -2
- package/dist/dev-preflight-B-LYyBf1.js +2 -0
- package/dist/dev-server-DG8Tt-uf.js +96 -0
- package/dist/dev-server-DG8Tt-uf.js.map +1 -0
- package/dist/doctor-BP8cZ-7F.js +771 -0
- package/dist/doctor-BP8cZ-7F.js.map +1 -0
- package/dist/eject-DizdoecV.js +335 -0
- package/dist/eject-DizdoecV.js.map +1 -0
- package/dist/fold-static-BC9CYicw.js +201 -0
- package/dist/fold-static-BC9CYicw.js.map +1 -0
- package/dist/function-portability-BIvlvR9q.js +265 -0
- package/dist/function-portability-BIvlvR9q.js.map +1 -0
- package/dist/generate_sdk-BPqdJc3Q.js +489 -0
- package/dist/generate_sdk-BPqdJc3Q.js.map +1 -0
- package/dist/index.es.js +151 -24674
- package/dist/index.es.js.map +1 -1
- package/dist/init-Utstvh2Q.js +1149 -0
- package/dist/init-Utstvh2Q.js.map +1 -0
- package/dist/last-dev-database-B58g89uA.js +2 -0
- package/dist/manifest-DRp0UVTj.js +880 -0
- package/dist/manifest-DRp0UVTj.js.map +1 -0
- package/dist/manifest.d.ts +15 -3
- package/dist/normalize-imports-X0Tgze9y.js +107 -0
- package/dist/normalize-imports-X0Tgze9y.js.map +1 -0
- package/dist/{package-manager-CXQevsE1.js → package-manager-DSEOwTqM.js} +2 -2
- package/dist/{package-manager-CXQevsE1.js.map → package-manager-DSEOwTqM.js.map} +1 -1
- package/dist/path-flags-BFgG9V6y.js +91 -0
- package/dist/path-flags-BFgG9V6y.js.map +1 -0
- package/dist/precompress.d.ts +17 -0
- package/dist/prepare-CDN6lSpz.js +1236 -0
- package/dist/prepare-CDN6lSpz.js.map +1 -0
- package/dist/{project-DmaGLmH2.js → project-Wp2zoyeN.js} +33 -4
- package/dist/project-Wp2zoyeN.js.map +1 -0
- package/dist/pull-CrGYMEsy.js +458 -0
- package/dist/pull-CrGYMEsy.js.map +1 -0
- package/dist/resolve-B2O-wKKk.js +2 -0
- package/dist/resources-FD7AfZx8.js +183 -0
- package/dist/resources-FD7AfZx8.js.map +1 -0
- package/dist/schema-DMdr431S.js +125 -0
- package/dist/schema-DMdr431S.js.map +1 -0
- package/dist/skills-DTFibT1m.js +642 -0
- package/dist/skills-DTFibT1m.js.map +1 -0
- package/dist/start-DEa-hOBh.js +267 -0
- package/dist/start-DEa-hOBh.js.map +1 -0
- package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
- package/dist/state-Do7ZJYkF.js.map +1 -0
- package/dist/status-DxnJTekv.js +352 -0
- package/dist/status-DxnJTekv.js.map +1 -0
- package/dist/storage-DyVq-f_F.js +303 -0
- package/dist/storage-DyVq-f_F.js.map +1 -0
- package/dist/telemetry/identity.d.ts +10 -1
- package/dist/telemetry-C8Uq8phb.js +129 -0
- package/dist/telemetry-C8Uq8phb.js.map +1 -0
- package/dist/telemetry-EMFRVRQi.js +351 -0
- package/dist/telemetry-EMFRVRQi.js.map +1 -0
- package/dist/unknown-command-DNwDcX3w.js +98 -0
- package/dist/unknown-command-DNwDcX3w.js.map +1 -0
- package/dist/upgrade-CtZtRkTE.js +1332 -0
- package/dist/upgrade-CtZtRkTE.js.map +1 -0
- package/dist/upgrade.d.ts +24 -3
- package/dist/utils/dev-preflight.d.ts +47 -5
- package/dist/utils/dev-server.d.ts +30 -0
- package/dist/utils/path-flags.d.ts +15 -2
- package/dist/utils/project.d.ts +16 -0
- package/dist/utils/temp-archive.d.ts +9 -0
- package/dist/{version-DsgBmP22.js → version-Buyyo1ut.js} +2 -2
- package/dist/{version-DsgBmP22.js.map → version-Buyyo1ut.js.map} +1 -1
- package/package.json +7 -7
- package/templates/eject/Dockerfile +4 -2
- package/templates/eject/backend/src/env.ts +1 -1
- package/templates/eject/backend/src/index.ts +29 -4
- package/templates/eject/docker-compose.custom.yml +8 -4
- package/templates/generated-schemas/blank.ts +40 -0
- package/templates/generated-schemas/blog.ts +127 -0
- package/templates/generated-schemas/ecommerce.ts +93 -0
- package/templates/overlays/baas/config/package.json +1 -0
- package/templates/overlays/baas/package.json +1 -0
- package/templates/template/.env.example +10 -4
- package/templates/template/README.md +11 -4
- package/templates/template/ai-instructions.md +3 -2
- package/templates/template/config/resources.ts +7 -6
- package/templates/template/docker-compose.yml +9 -3
- package/templates/template/frontend/vite.config.ts +13 -3
- package/templates/template/package.json +1 -0
- package/templates/template/scripts/example.ts +2 -1
- package/templates/template/tsconfig.json +25 -0
- package/dist/branch-pointer-kk9F5FBO.js +0 -111
- package/dist/branch-pointer-kk9F5FBO.js.map +0 -1
- package/dist/constraints-DKGbNfUW.js +0 -48
- package/dist/constraints-DKGbNfUW.js.map +0 -1
- package/dist/daemon-C8p32i-n.js +0 -357
- package/dist/daemon-C8p32i-n.js.map +0 -1
- package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
- package/dist/dev-preflight-CNLl4rdo.js +0 -367
- package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
- package/dist/project-DmaGLmH2.js.map +0 -1
- package/dist/pull-Dywm5FWP.js +0 -232
- package/dist/pull-Dywm5FWP.js.map +0 -1
- package/dist/resolve-BAeRUBhg.js +0 -74
- package/dist/resolve-BAeRUBhg.js.map +0 -1
- package/dist/state-C59Elrnt.js.map +0 -1
- package/templates/template/backend/src/schema.generated.ts +0 -13
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
|
|
2
|
+
|
|
3
|
+
import { boolean, jsonb, pgPolicy, pgSchema, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { relations as drizzleRelations, sql } from 'drizzle-orm';
|
|
5
|
+
|
|
6
|
+
export const rebaseSchema = pgSchema("rebase");
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export const users = rebaseSchema.table("users", {
|
|
10
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
11
|
+
email: text("email").unique().notNull(),
|
|
12
|
+
displayName: text("display_name"),
|
|
13
|
+
photoURL: text("photo_url"),
|
|
14
|
+
roles: text("roles").array(),
|
|
15
|
+
passwordHash: text("password_hash"),
|
|
16
|
+
emailVerified: boolean("email_verified").default(sql`FALSE`),
|
|
17
|
+
emailVerificationToken: text("email_verification_token"),
|
|
18
|
+
emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
|
|
19
|
+
metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
|
|
20
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
|
|
21
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
|
|
22
|
+
}, (table) => ([
|
|
23
|
+
pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
24
|
+
pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
25
|
+
pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
26
|
+
pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
27
|
+
pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
28
|
+
pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
29
|
+
pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
30
|
+
pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
31
|
+
pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
|
|
32
|
+
pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
33
|
+
pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
34
|
+
pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
35
|
+
])).enableRLS();
|
|
36
|
+
|
|
37
|
+
export const tables = { users };
|
|
38
|
+
export const enums = { };
|
|
39
|
+
export const relations = { };
|
|
40
|
+
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
|
|
2
|
+
|
|
3
|
+
import { type AnyPgColumn, boolean, index, integer, jsonb, pgEnum, pgPolicy, pgSchema, pgTable, primaryKey, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { relations as drizzleRelations, sql } from 'drizzle-orm';
|
|
5
|
+
|
|
6
|
+
export const rebaseSchema = pgSchema("rebase");
|
|
7
|
+
|
|
8
|
+
export const postsStatus = pgEnum("posts_status", ["draft", "review", "published"]);
|
|
9
|
+
|
|
10
|
+
export const authors = pgTable("authors", {
|
|
11
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
12
|
+
name: text("name").notNull(),
|
|
13
|
+
email: text("email").notNull(),
|
|
14
|
+
picture: text("picture")
|
|
15
|
+
}, (table) => ([
|
|
16
|
+
pgPolicy("authors_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
|
|
17
|
+
pgPolicy("authors_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
18
|
+
pgPolicy("authors_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
19
|
+
pgPolicy("authors_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
20
|
+
pgPolicy("authors_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
21
|
+
pgPolicy("authors_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
22
|
+
pgPolicy("authors_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
23
|
+
pgPolicy("authors_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
24
|
+
])).enableRLS();
|
|
25
|
+
|
|
26
|
+
export const posts = pgTable("posts", {
|
|
27
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
28
|
+
title: text("title").notNull(),
|
|
29
|
+
content: text("content"),
|
|
30
|
+
status: postsStatus("status"),
|
|
31
|
+
authorId: integer("author_id").references((): AnyPgColumn => authors.id, { onDelete: "set null" })
|
|
32
|
+
}, (table) => ([
|
|
33
|
+
index("posts_author_id_ix_00ad191").on(table.authorId.asc().nullsLast()),
|
|
34
|
+
pgPolicy("posts_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
|
|
35
|
+
pgPolicy("posts_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
36
|
+
pgPolicy("posts_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
37
|
+
pgPolicy("posts_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
38
|
+
pgPolicy("posts_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
39
|
+
pgPolicy("posts_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
40
|
+
pgPolicy("posts_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
41
|
+
pgPolicy("posts_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
42
|
+
])).enableRLS();
|
|
43
|
+
|
|
44
|
+
export const postsTags = pgTable("posts_tags", {
|
|
45
|
+
post_id: integer("post_id").notNull().references((): AnyPgColumn => posts.id, { onDelete: "cascade" }),
|
|
46
|
+
tag_id: integer("tag_id").notNull().references((): AnyPgColumn => tags.id, { onDelete: "cascade" }),
|
|
47
|
+
}, (table) => ([
|
|
48
|
+
primaryKey({ columns: [table.post_id, table.tag_id] }),
|
|
49
|
+
index("posts_tags_tag_id_ix_d4456f1").on(table.tag_id.asc().nullsLast()),
|
|
50
|
+
pgPolicy("posts_tags_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
51
|
+
pgPolicy("posts_tags_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
52
|
+
pgPolicy("posts_tags_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
53
|
+
pgPolicy("posts_tags_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
54
|
+
pgPolicy("posts_tags_default_edge_read", { as: "permissive", for: "select", to: ["public"], using: sql`(EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE "_ex0".id = "public"."posts_tags".post_id)) AND (EXISTS (SELECT 1 FROM "public"."tags" "_ex1" WHERE "_ex1".id = "public"."posts_tags".tag_id))` }),
|
|
55
|
+
pgPolicy("posts_tags_default_edge_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
|
|
56
|
+
pgPolicy("posts_tags_default_edge_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))`, withCheck: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
|
|
57
|
+
pgPolicy("posts_tags_default_edge_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`EXISTS (SELECT 1 FROM "public"."posts" "_ex0" WHERE ("_ex0".id = "public"."posts_tags".post_id) AND ((SELECT (string_to_array(rebase.roles(), ',') && ARRAY['admin']))))` }),
|
|
58
|
+
])).enableRLS();
|
|
59
|
+
|
|
60
|
+
export const tags = pgTable("tags", {
|
|
61
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
62
|
+
name: text("name").notNull()
|
|
63
|
+
}, (table) => ([
|
|
64
|
+
pgPolicy("tags_select_841c287", { as: "permissive", for: "select", to: ["public"], using: sql`true` }),
|
|
65
|
+
pgPolicy("tags_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
66
|
+
pgPolicy("tags_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
67
|
+
pgPolicy("tags_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
68
|
+
pgPolicy("tags_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
69
|
+
pgPolicy("tags_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
70
|
+
pgPolicy("tags_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
71
|
+
pgPolicy("tags_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
72
|
+
])).enableRLS();
|
|
73
|
+
|
|
74
|
+
export const users = rebaseSchema.table("users", {
|
|
75
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
76
|
+
email: text("email").unique().notNull(),
|
|
77
|
+
displayName: text("display_name"),
|
|
78
|
+
photoURL: text("photo_url"),
|
|
79
|
+
roles: text("roles").array(),
|
|
80
|
+
passwordHash: text("password_hash"),
|
|
81
|
+
emailVerified: boolean("email_verified").default(sql`FALSE`),
|
|
82
|
+
emailVerificationToken: text("email_verification_token"),
|
|
83
|
+
emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
|
|
84
|
+
metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
|
|
85
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
|
|
86
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
|
|
87
|
+
}, (table) => ([
|
|
88
|
+
pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
89
|
+
pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
90
|
+
pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
91
|
+
pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
92
|
+
pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
93
|
+
pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
94
|
+
pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
95
|
+
pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
96
|
+
pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
|
|
97
|
+
pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
98
|
+
pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
99
|
+
pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
100
|
+
])).enableRLS();
|
|
101
|
+
|
|
102
|
+
export const postsRelations = drizzleRelations(posts, ({ one, many }) => ({
|
|
103
|
+
"author": one(authors, {
|
|
104
|
+
fields: [posts.authorId],
|
|
105
|
+
references: [authors.id],
|
|
106
|
+
relationName: "posts_authorId"
|
|
107
|
+
}),
|
|
108
|
+
"tags": many(postsTags, { relationName: "tags" })
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
export const postsTagsRelations = drizzleRelations(postsTags, ({ one, many }) => ({
|
|
112
|
+
"post_id": one(posts, {
|
|
113
|
+
fields: [postsTags.post_id],
|
|
114
|
+
references: [posts.id],
|
|
115
|
+
relationName: "tags"
|
|
116
|
+
}),
|
|
117
|
+
"tag_id": one(tags, {
|
|
118
|
+
fields: [postsTags.tag_id],
|
|
119
|
+
references: [tags.id],
|
|
120
|
+
relationName: "posts_tags_tag_id"
|
|
121
|
+
})
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
export const tables = { authors, posts, postsTags, tags, users };
|
|
125
|
+
export const enums = { postsStatus };
|
|
126
|
+
export const relations = { postsRelations, postsTagsRelations };
|
|
127
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// This file is auto-generated by the Rebase Drizzle generator. Do not edit manually.
|
|
2
|
+
|
|
3
|
+
import { type AnyPgColumn, boolean, index, integer, jsonb, numeric, pgEnum, pgPolicy, pgSchema, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
4
|
+
import { relations as drizzleRelations, sql } from 'drizzle-orm';
|
|
5
|
+
|
|
6
|
+
export const rebaseSchema = pgSchema("rebase");
|
|
7
|
+
|
|
8
|
+
export const ordersStatus = pgEnum("orders_status", ["pending", "processing", "shipped", "delivered", "cancelled"]);
|
|
9
|
+
export const productsStatus = pgEnum("products_status", ["draft", "active", "archived"]);
|
|
10
|
+
|
|
11
|
+
export const categories = pgTable("categories", {
|
|
12
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
13
|
+
name: text("name").notNull(),
|
|
14
|
+
slug: text("slug").notNull(),
|
|
15
|
+
description: text("description"),
|
|
16
|
+
icon: text("icon")
|
|
17
|
+
}, (table) => ([
|
|
18
|
+
pgPolicy("categories_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
19
|
+
pgPolicy("categories_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
20
|
+
pgPolicy("categories_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
21
|
+
pgPolicy("categories_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
22
|
+
])).enableRLS();
|
|
23
|
+
|
|
24
|
+
export const orders = pgTable("orders", {
|
|
25
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
26
|
+
customerEmail: text("customer_email").notNull(),
|
|
27
|
+
status: ordersStatus("status"),
|
|
28
|
+
total: numeric("total").notNull(),
|
|
29
|
+
notes: text("notes"),
|
|
30
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
|
|
31
|
+
}, (table) => ([
|
|
32
|
+
pgPolicy("orders_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
33
|
+
pgPolicy("orders_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
34
|
+
pgPolicy("orders_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
35
|
+
pgPolicy("orders_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
36
|
+
])).enableRLS();
|
|
37
|
+
|
|
38
|
+
export const products = pgTable("products", {
|
|
39
|
+
id: integer("id").generatedByDefaultAsIdentity().primaryKey(),
|
|
40
|
+
name: text("name").notNull(),
|
|
41
|
+
description: text("description"),
|
|
42
|
+
price: numeric("price").notNull(),
|
|
43
|
+
image: text("image"),
|
|
44
|
+
status: productsStatus("status"),
|
|
45
|
+
categoryId: integer("category_id").references((): AnyPgColumn => categories.id, { onDelete: "set null" })
|
|
46
|
+
}, (table) => ([
|
|
47
|
+
index("products_category_id_ix_066548f").on(table.categoryId.asc().nullsLast()),
|
|
48
|
+
pgPolicy("products_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
49
|
+
pgPolicy("products_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
50
|
+
pgPolicy("products_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
51
|
+
pgPolicy("products_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
52
|
+
])).enableRLS();
|
|
53
|
+
|
|
54
|
+
export const users = rebaseSchema.table("users", {
|
|
55
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
56
|
+
email: text("email").unique().notNull(),
|
|
57
|
+
displayName: text("display_name"),
|
|
58
|
+
photoURL: text("photo_url"),
|
|
59
|
+
roles: text("roles").array(),
|
|
60
|
+
passwordHash: text("password_hash"),
|
|
61
|
+
emailVerified: boolean("email_verified").default(sql`FALSE`),
|
|
62
|
+
emailVerificationToken: text("email_verification_token"),
|
|
63
|
+
emailVerificationSentAt: timestamp("email_verification_sent_at", { withTimezone: true, mode: 'string' }),
|
|
64
|
+
metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
|
|
65
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`now()`),
|
|
66
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`now()`)
|
|
67
|
+
}, (table) => ([
|
|
68
|
+
pgPolicy("users_select_5f32f06", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
69
|
+
pgPolicy("users_insert_3561e70_0", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
70
|
+
pgPolicy("users_update_3561e70_1", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])`, withCheck: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
71
|
+
pgPolicy("users_delete_3561e70_2", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT string_to_array(rebase.roles(), ',') && ARRAY['admin'])` }),
|
|
72
|
+
pgPolicy("users_default_admin_read", { as: "permissive", for: "select", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
73
|
+
pgPolicy("users_default_admin_write_insert", { as: "permissive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
74
|
+
pgPolicy("users_default_admin_write_update", { as: "permissive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
75
|
+
pgPolicy("users_default_admin_write_delete", { as: "permissive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
76
|
+
pgPolicy("users_default_self_read", { as: "permissive", for: "select", to: ["public"], using: sql`(id)::text = (SELECT rebase.uid())` }),
|
|
77
|
+
pgPolicy("users_require_admin_write_insert", { as: "restrictive", for: "insert", to: ["public"], withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
78
|
+
pgPolicy("users_require_admin_write_update", { as: "restrictive", for: "update", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))`, withCheck: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
79
|
+
pgPolicy("users_require_admin_write_delete", { as: "restrictive", for: "delete", to: ["public"], using: sql`(SELECT (rebase.uid() IS NULL) OR (string_to_array(rebase.roles(), ',') && ARRAY['admin']))` }),
|
|
80
|
+
])).enableRLS();
|
|
81
|
+
|
|
82
|
+
export const productsRelations = drizzleRelations(products, ({ one, many }) => ({
|
|
83
|
+
"category": one(categories, {
|
|
84
|
+
fields: [products.categoryId],
|
|
85
|
+
references: [categories.id],
|
|
86
|
+
relationName: "products_categoryId"
|
|
87
|
+
})
|
|
88
|
+
}));
|
|
89
|
+
|
|
90
|
+
export const tables = { categories, orders, products, users };
|
|
91
|
+
export const enums = { ordersStatus, productsStatus };
|
|
92
|
+
export const relations = { productsRelations };
|
|
93
|
+
|
|
@@ -161,14 +161,20 @@ VITE_API_URL=
|
|
|
161
161
|
# In production, "local" means NO file storage: uploads answer
|
|
162
162
|
# 501 STORAGE_NOT_CONFIGURED instead of landing on a container filesystem that
|
|
163
163
|
# is erased on the next redeploy. Configure a bucket below to switch it on, or
|
|
164
|
-
# set FORCE_LOCAL_STORAGE=true if a durable volume really
|
|
165
|
-
#
|
|
164
|
+
# set FORCE_LOCAL_STORAGE=true if a durable volume really holds STORAGE_PATH
|
|
165
|
+
# (the generated docker-compose.yml does exactly that). Mount it on the parent
|
|
166
|
+
# of STORAGE_PATH: a named local bucket keeps its files beside it, in
|
|
167
|
+
# <STORAGE_PATH>__<key>, and those belong on the volume too.
|
|
166
168
|
#
|
|
167
169
|
# STORAGE_TYPE=local
|
|
168
170
|
|
|
169
171
|
# --- Local filesystem (default — great for dev and single-server deployments) --
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
+
# Unset, files land in <project>/uploads, under `rebase dev` and in the server
|
|
173
|
+
# `rebase eject` writes alike. A relative path is read from the directory the
|
|
174
|
+
# server runs in: the project root under `rebase dev`, backend/ after
|
|
175
|
+
# `rebase eject`. So ./uploads names two places, and an absolute path one.
|
|
176
|
+
# STORAGE_PATH=/absolute/path/to/uploads
|
|
177
|
+
# FORCE_LOCAL_STORAGE=true # Only with a durable volume holding STORAGE_PATH
|
|
172
178
|
|
|
173
179
|
# --- S3-compatible (AWS S3, Cloudflare R2, MinIO, Hetzner Object Storage, Backblaze B2) ---
|
|
174
180
|
# STORAGE_TYPE=s3
|
|
@@ -40,7 +40,7 @@ every collection declared there, and serves the API and the admin panel.
|
|
|
40
40
|
`rebase dev` prints the two URLs it actually bound. The first account you
|
|
41
41
|
register becomes the admin. `REBASE_ADMIN_EMAIL` and `REBASE_ADMIN_PASSWORD` in
|
|
42
42
|
`.env` are ignored here and read by the production boot below, where that window
|
|
43
|
-
is shut — see [Self-hosting](#self-hosting
|
|
43
|
+
is shut — see [Self-hosting](#self-hosting).
|
|
44
44
|
|
|
45
45
|
Ports are **derived from this project's path**, not fixed at 3001/5173, so
|
|
46
46
|
several Rebase projects can run at once without colliding. That is why the URL
|
|
@@ -53,9 +53,10 @@ built bundle). Pin a port with `rebase dev --port 3001` if you need a stable one
|
|
|
53
53
|
Uncomment `DATABASE_URL` in `.env` and run `pnpm dev` again. Nothing else
|
|
54
54
|
changes. A `DATABASE_URL` that is already set is never touched.
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
database
|
|
56
|
+
The migration commands work on either database. They plan changes with Atlas,
|
|
57
|
+
which needs a second empty database to compare against: beside your own
|
|
58
|
+
database Rebase makes one, and on the managed one, which serves exactly one,
|
|
59
|
+
the command starts a throwaway in-memory PGlite:
|
|
59
60
|
|
|
60
61
|
```bash
|
|
61
62
|
pnpm run db:push
|
|
@@ -81,12 +82,18 @@ docker compose up -d db
|
|
|
81
82
|
│ └── src/ # Generated database schema
|
|
82
83
|
├── config/ # Shared collection definitions
|
|
83
84
|
│ └── collections/ # Schema-as-Code TypeScript files
|
|
85
|
+
├── scripts/ # Scripts that call the API, e.g. example.ts
|
|
84
86
|
├── rebase.json # Which apps this repository contains
|
|
85
87
|
├── docker-compose.yml # Self-hosting stack (Postgres + the Rebase runtime)
|
|
86
88
|
├── .env.example # Environment variable reference
|
|
89
|
+
├── tsconfig.json # Typechecks scripts/ only
|
|
87
90
|
└── package.json # Root workspace config
|
|
88
91
|
```
|
|
89
92
|
|
|
93
|
+
Each workspace typechecks on its own, with its own `tsconfig.json`:
|
|
94
|
+
`cd backend && npx tsc --noEmit`, and the same in `config/` and `frontend/`.
|
|
95
|
+
`tsc --noEmit` at the root checks `scripts/`.
|
|
96
|
+
|
|
90
97
|
There is no Dockerfile, and that is deliberate: `rebase build` produces a
|
|
91
98
|
**bundle** — your compiled collections, functions, crons and admin assets — and
|
|
92
99
|
the published `rebasepro/server` image boots it. The artifact you self-host is
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
| Command | What it does |
|
|
8
8
|
|---|---|
|
|
9
9
|
| `pnpm dev` | Backend, frontend and the managed development database, together |
|
|
10
|
-
| `pnpm schema:generate` then `pnpm
|
|
10
|
+
| `pnpm schema:generate` then `pnpm generate:sdk` | The same loop with `pnpm dev` not running: collections → Drizzle schema and SDK types. The next boot creates what is missing |
|
|
11
|
+
| `pnpm db:push` | The changes boot leaves alone (a rename, a narrowed type, a removed field), on the managed development database as on your own PostgreSQL |
|
|
11
12
|
| `pnpm db:generate` then `pnpm db:migrate` | The production path — write the migration file, then apply it |
|
|
12
13
|
| `pnpm schema:introspect` | The other direction: existing tables → collection definitions |
|
|
13
14
|
| `pnpm generate:sdk` | Regenerate the typed client in `generated/sdk/` from the collections. `pnpm dev` already does this on start and on every save; the script is for CI and for when `pnpm dev` is not running |
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
## Core rules
|
|
31
32
|
|
|
32
33
|
1. **Schema-as-code**: define and edit collections in `config/collections/` with `defineCollection` from `@rebasepro/cms-types`, one collection per file, default-exported. Every file in that directory is loaded — by the backend, the admin panel and the SDK generator — whether or not `index.ts` lists it: the barrel is a sort order for the admin navigation (and holds `defaultSecurityRules`), not a registry. To remove a collection, delete its file. Never hand-edit generated schemas or Postgres tables.
|
|
33
|
-
2. **Applying a collection change**: while `pnpm dev` is running, saving a file in `config/collections/` is the whole step — it regenerates `backend/src/schema.generated.ts` and the SDK types in `generated/sdk/`, restarts the backend, and boot creates the tables and columns that are missing. With `pnpm dev` stopped, that is `pnpm schema:generate` and `pnpm generate:sdk`. `pnpm db:push` is only for what boot deliberately leaves alone — a renamed column, a narrowed type, a removed field —
|
|
34
|
+
2. **Applying a collection change**: while `pnpm dev` is running, saving a file in `config/collections/` is the whole step — it regenerates `backend/src/schema.generated.ts` and the SDK types in `generated/sdk/`, restarts the backend, and boot creates the tables and columns that are missing. With `pnpm dev` stopped, that is `pnpm schema:generate` and `pnpm generate:sdk`. `pnpm db:push` is only for what boot deliberately leaves alone — a renamed column, a narrowed type, a removed field — on the managed development database as on your own PostgreSQL. In production the pair is `pnpm db:generate` then `pnpm db:migrate`.
|
|
34
35
|
3. **Use the SDK**: server-side that is `rebase.dataAsAdmin.<slug>` for work done as the service identity, or `getDriver(c)` inside a function when the read should run as the caller. The server client has no plain `data` accessor — it is omitted precisely so that the choice of identity is written down.
|
|
35
36
|
4. **Guard every custom route**: routes in `backend/functions/` are mounted **without** an auth requirement — webhook receivers need that — so each one is public until you guard it. Import `requireAuth` / `requireAdmin` from `@rebasepro/server/functions` and pass them in the route's own middleware slot (`app.post("/", requireAuth, handler)`), not via `app.use()`, which only covers routes declared below it. Reading `getUser(c)` is not a guard: an anonymous caller gets `undefined` and the handler still runs. See `backend/functions/hello.ts` for all three tiers.
|
|
36
37
|
5. **In `backend/functions/`, always import from `@rebasepro/server/functions`** — never from `@rebasepro/server`. Both work today; the subpath is the portable one, and it also gives you the typed context accessors (`getUser`, `getDriver`, `requireDriver`) instead of casting `c.get("user")`. The package root is for a server entrypoint, not for route handlers.
|
|
@@ -43,12 +43,13 @@ export const main = database();
|
|
|
43
43
|
* export const files = bucket({ engine: "s3" });
|
|
44
44
|
* export const media = bucket("media", { engine: "s3" });
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
46
|
+
* An upload whose property names no `storageSource` goes to the default
|
|
47
|
+
* bucket, which keeps its own variables — `STORAGE_PATH` (else `./uploads`),
|
|
48
|
+
* `S3_BUCKET` / `GCS_BUCKET` — whatever you declare here, so adding
|
|
49
|
+
* `bucket("media")` never moves the files already there. To send those
|
|
50
|
+
* uploads to a named bucket instead, mark it:
|
|
51
|
+
* `bucket("media", { engine: "s3", default: true })`. Two buckets may not
|
|
52
|
+
* share a directory or a bucket; boot refuses if they would.
|
|
52
53
|
*
|
|
53
54
|
* Point a property at it by handle — the same name, spelled once:
|
|
54
55
|
*
|
|
@@ -147,7 +147,13 @@ services:
|
|
|
147
147
|
# container filesystem is destroyed on the next deploy and every uploaded
|
|
148
148
|
# file goes with it. Switch to STORAGE_TYPE=s3 or gcs and drop both lines
|
|
149
149
|
# if you move storage off-box.
|
|
150
|
-
|
|
150
|
+
#
|
|
151
|
+
# The volume is mounted at /storage, one level above STORAGE_PATH, because
|
|
152
|
+
# every bucket you declare in config/resources.ts gets a directory beside
|
|
153
|
+
# this one: bucket("media") is /storage/uploads__media. Mounted at
|
|
154
|
+
# /storage/uploads itself, the volume would hold the default bucket only,
|
|
155
|
+
# and every other bucket's files would go with the container.
|
|
156
|
+
STORAGE_PATH: /storage/uploads
|
|
151
157
|
FORCE_LOCAL_STORAGE: "true"
|
|
152
158
|
volumes:
|
|
153
159
|
# Your built project. `rebase build` installs its declared dependencies
|
|
@@ -156,10 +162,10 @@ services:
|
|
|
156
162
|
# that runs after it. Drop the `:ro` if you build with `--no-vendor`: the
|
|
157
163
|
# runtime installs at every start instead, and needs to write here.
|
|
158
164
|
- ./dist-bundle:/bundle:ro
|
|
159
|
-
-
|
|
165
|
+
- storage:/storage
|
|
160
166
|
|
|
161
167
|
volumes:
|
|
162
168
|
postgres_data:
|
|
163
169
|
driver: local
|
|
164
|
-
|
|
170
|
+
storage:
|
|
165
171
|
driver: local
|
|
@@ -30,9 +30,9 @@ export default defineConfig({
|
|
|
30
30
|
"@remix-run/router"
|
|
31
31
|
]
|
|
32
32
|
},
|
|
33
|
-
esbuild:
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
// No `esbuild` block: Vite 8 transforms with oxc and ignores it, and
|
|
34
|
+
// having one made every `rebase dev` and build print "Both esbuild and oxc
|
|
35
|
+
// options were set".
|
|
36
36
|
build: {
|
|
37
37
|
minify: true,
|
|
38
38
|
outDir: "./dist",
|
|
@@ -42,6 +42,8 @@ export default defineConfig({
|
|
|
42
42
|
// successful JS transform, so the build fails at the very end.
|
|
43
43
|
target: "es2022",
|
|
44
44
|
sourcemap: true,
|
|
45
|
+
// The admin's own chunks are already code-split and the warning names only them, never this project's code.
|
|
46
|
+
chunkSizeWarningLimit: Infinity,
|
|
45
47
|
rollupOptions: {
|
|
46
48
|
output: {
|
|
47
49
|
// These rules are copied in rather than taken from
|
|
@@ -75,6 +77,14 @@ export default defineConfig({
|
|
|
75
77
|
// anchor a heavy one to the critical path.
|
|
76
78
|
if (id.includes("commonjsHelpers") || id.includes("commonjs-dynamic-modules")) return "vendor-commonjs-helpers";
|
|
77
79
|
|
|
80
|
+
// Small libraries the entry needs, named before the heavy groups. A name
|
|
81
|
+
// takes the unassigned dependencies of its modules with it, so
|
|
82
|
+
// `prism-react-renderer` took `clsx` — which the kit imports statically — and
|
|
83
|
+
// the entry imported 85 kB of syntax highlighter for a 633-byte helper;
|
|
84
|
+
// React core landed in `vendor-dnd` the same way.
|
|
85
|
+
if (/[\\/]node_modules[\\/](react|scheduler|use-sync-external-store)[\\/]/.test(id)) return "vendor-react";
|
|
86
|
+
if (/[\\/]node_modules[\\/]clsx[\\/]/.test(id)) return "vendor-clsx";
|
|
87
|
+
|
|
78
88
|
if (id.includes("exceljs")) return "vendor-exceljs";
|
|
79
89
|
if (id.includes("prosemirror")) return "vendor-prosemirror";
|
|
80
90
|
if (id.includes("monaco-editor") || id.includes("@monaco-editor")) return "vendor-monaco";
|
|
@@ -30,7 +30,8 @@ import { createRebaseClient } from "@rebasepro/client";
|
|
|
30
30
|
// import { collectionsDictionary, type Database } from "../generated/sdk/database.types";
|
|
31
31
|
|
|
32
32
|
// Load .env from project root (same file the backend uses)
|
|
33
|
-
dotenv
|
|
33
|
+
// `quiet`: dotenv 17 otherwise prints its own advertisement banner.
|
|
34
|
+
dotenv.config({ path: path.resolve(process.cwd(), ".env"), quiet: true });
|
|
34
35
|
|
|
35
36
|
// ─── Resolve Backend URL ─────────────────────────────────────────────
|
|
36
37
|
let baseUrl = process.env.REBASE_URL;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
// The project root's own program: `scripts/`, which no workspace compiles.
|
|
3
|
+
//
|
|
4
|
+
// frontend/, backend/ and config/ each typecheck on their own, with the
|
|
5
|
+
// tsconfig in that folder — `cd backend && tsc --noEmit` — and this file
|
|
6
|
+
// does not reach into them: it would compile their code under options that
|
|
7
|
+
// are not theirs. Without it, `tsc --noEmit` here printed tsc's help and
|
|
8
|
+
// exited 0, which reads as a passing check of nothing.
|
|
9
|
+
"compilerOptions": {
|
|
10
|
+
"target": "ES2022",
|
|
11
|
+
"module": "ESNext",
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"forceConsistentCasingInFileNames": true,
|
|
19
|
+
"resolveJsonModule": true,
|
|
20
|
+
// Pinned, as every workspace's tsconfig is: unset, tsc sweeps every
|
|
21
|
+
// `node_modules/@types` it can reach, the pnpm virtual store included.
|
|
22
|
+
"types": ["node"]
|
|
23
|
+
},
|
|
24
|
+
"include": ["scripts/**/*"]
|
|
25
|
+
}
|