@rebasepro/cli 0.0.1-canary.f81da60 → 0.0.1-canary.fc811d7
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/LICENSE +17 -196
- package/README.md +57 -33
- package/dist/commands/api-keys.d.ts +1 -0
- package/dist/commands/build.d.ts +1 -0
- package/dist/commands/cloud/auth.d.ts +3 -0
- package/dist/commands/cloud/context.d.ts +61 -0
- package/dist/commands/cloud/databases.d.ts +1 -0
- package/dist/commands/cloud/deploy.d.ts +2 -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/projects.d.ts +13 -0
- package/dist/commands/cloud/resources.d.ts +6 -0
- package/dist/commands/generate_sdk.d.ts +1 -1
- package/dist/commands/init.d.ts +37 -1
- package/dist/commands/skills.d.ts +1 -0
- package/dist/commands/start.d.ts +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.es.js +3781 -1089
- package/dist/index.es.js.map +1 -1
- package/dist/utils/package-manager.d.ts +33 -0
- package/dist/utils/project.d.ts +16 -1
- package/package.json +26 -22
- package/templates/overlays/baas/README.md +37 -0
- package/templates/overlays/baas/backend/package.json +31 -0
- package/templates/overlays/baas/backend/src/index.ts +172 -0
- package/templates/overlays/baas/backend/tsconfig.json +18 -0
- package/templates/overlays/baas/package.json +42 -0
- package/templates/overlays/baas/pnpm-workspace.yaml +9 -0
- package/templates/template/.cursorrules +2 -0
- package/templates/template/.env.example +25 -1
- package/templates/template/.github/copilot-instructions.md +2 -0
- package/templates/template/.windsurfrules +2 -0
- package/templates/template/AGENTS.md +2 -0
- package/templates/template/CLAUDE.md +2 -0
- package/templates/template/README.md +6 -6
- package/templates/template/ai-instructions.md +17 -0
- package/templates/template/backend/Dockerfile +5 -4
- package/templates/template/backend/functions/hello.ts +36 -32
- package/templates/template/backend/package.json +9 -9
- package/templates/template/backend/src/env.ts +13 -42
- package/templates/template/backend/src/index.ts +54 -28
- package/templates/template/config/collections/authors.ts +2 -2
- package/templates/template/config/collections/index.ts +25 -4
- package/templates/template/config/collections/posts.ts +12 -32
- package/templates/template/config/collections/presets/blank/index.ts +3 -0
- package/templates/template/config/collections/presets/ecommerce/categories.ts +40 -0
- package/templates/template/config/collections/presets/ecommerce/index.ts +6 -0
- package/templates/template/config/collections/presets/ecommerce/orders.ts +66 -0
- package/templates/template/config/collections/presets/ecommerce/products.ts +64 -0
- package/templates/template/config/collections/tags.ts +3 -5
- package/templates/template/config/collections/users.ts +142 -0
- package/templates/template/config/index.ts +1 -1
- package/templates/template/docker-compose.yml +4 -4
- package/templates/template/frontend/Dockerfile +5 -3
- package/templates/template/frontend/package.json +8 -7
- package/templates/template/frontend/src/App.tsx +22 -22
- package/templates/template/frontend/src/main.tsx +4 -0
- package/templates/template/frontend/src/virtual.d.ts +6 -0
- package/templates/template/frontend/tsconfig.json +3 -2
- package/templates/template/frontend/vite.config.ts +16 -3
- package/templates/template/package.json +18 -5
- package/templates/template/pnpm-workspace.yaml +9 -0
- package/templates/template/scripts/example.ts +5 -2
- package/dist/auth.d.ts +0 -5
- package/dist/commands/cli.test.d.ts +0 -1
- package/dist/commands/dev.test.d.ts +0 -1
- package/dist/commands/init.test.d.ts +0 -1
- package/dist/index.cjs +0 -1306
- package/dist/index.cjs.map +0 -1
- package/dist/utils/project.test.d.ts +0 -1
- package/templates/template/backend/drizzle.config.ts +0 -42
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { HonoEnv } from "@rebasepro/server-core";
|
|
3
|
-
import { rebase } from "@rebasepro/server-core";
|
|
1
|
+
import { defineFunction } from "@rebasepro/server";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Example custom function route.
|
|
@@ -12,41 +10,47 @@ import { rebase } from "@rebasepro/server-core";
|
|
|
12
10
|
* Call from the client SDK:
|
|
13
11
|
* const result = await client.call("functions/hello", { name: "World" });
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Authored with `defineFunction`, which hands you a pre-typed Hono app
|
|
14
|
+
* (so `c.get("user")` / `c.get("driver")` are typed) and the `rebase`
|
|
15
|
+
* singleton via the injected context — use any Hono middleware, define any
|
|
16
|
+
* HTTP methods, access the request/response directly.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* `rebase.dataAsAdmin` gives you admin-level access to your data and
|
|
19
|
+
* **bypasses RLS** — use it only for trusted admin work. For request-scoped /
|
|
20
|
+
* RLS-scoped data access, use c.get("user") and c.get("driver"), which carry
|
|
21
|
+
* the caller's identity. (`rebase` also exposes auth, storage, email.)
|
|
21
22
|
*/
|
|
22
|
-
|
|
23
|
+
export default defineFunction((app, { rebase }) => {
|
|
24
|
+
void rebase; // available for dataAsAdmin/auth/storage/email — see commented usage below
|
|
23
25
|
|
|
24
|
-
app.post("/", async (c) => {
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
app.post("/", async (c) => {
|
|
27
|
+
const body = await c.req.json().catch(() => ({}));
|
|
28
|
+
const user = c.get("user");
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const userId = (user && typeof user === "object" && "userId" in user)
|
|
31
|
+
? user.userId
|
|
32
|
+
: "anonymous";
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
// Access any Rebase service via the injected `rebase`:
|
|
35
|
+
// await rebase.email.send({
|
|
36
|
+
// to: "admin@example.com",
|
|
37
|
+
// subject: "Function called",
|
|
38
|
+
// html: `<p>Hello from ${userId}!</p>`,
|
|
39
|
+
// });
|
|
40
|
+
//
|
|
41
|
+
// Admin-scoped data (bypasses RLS — trusted work only):
|
|
42
|
+
// const authors = await rebase.dataAsAdmin.authors.find({ limit: 5 });
|
|
43
|
+
// For user-scoped data (RLS applies), use the request-scoped driver
|
|
44
|
+
// (c.get("driver")), which carries the caller's identity.
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
return c.json({
|
|
47
|
+
message: `Hello, ${body.name || "World"}!`,
|
|
48
|
+
user: userId
|
|
49
|
+
});
|
|
44
50
|
});
|
|
45
|
-
});
|
|
46
51
|
|
|
47
|
-
app.get("/", (c) => {
|
|
48
|
-
|
|
49
|
-
endpoint: "hello" });
|
|
52
|
+
app.get("/", (c) => {
|
|
53
|
+
return c.json({ status: "ok",
|
|
54
|
+
endpoint: "hello" });
|
|
55
|
+
});
|
|
50
56
|
});
|
|
51
|
-
|
|
52
|
-
export default app;
|
|
@@ -6,26 +6,26 @@
|
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "tsx watch --watch=\"../config/**/*\" src/index.ts",
|
|
9
|
-
"build": "
|
|
10
|
-
"start": "
|
|
9
|
+
"build": "rebase schema generate --collections ../config/collections && tsc",
|
|
10
|
+
"start": "node dist/backend/src/index.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"{{PROJECT_NAME}}-config": "
|
|
14
|
-
"@rebasepro/server
|
|
15
|
-
"@rebasepro/server-
|
|
16
|
-
"
|
|
13
|
+
"{{PROJECT_NAME}}-config": "*",
|
|
14
|
+
"@rebasepro/server": "workspace:*",
|
|
15
|
+
"@rebasepro/server-postgres": "workspace:*",
|
|
16
|
+
"@rebasepro/types": "workspace:*",
|
|
17
|
+
"drizzle-orm": "^0.45.2",
|
|
17
18
|
"hono": "^4.12.10",
|
|
18
19
|
"@hono/node-server": "^1.19.12",
|
|
19
20
|
"pg": "^8.11.3",
|
|
20
21
|
"ws": "^8.16.0",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
22
|
+
"dotenv": "^16.0.0",
|
|
23
|
+
"zod": "^4.4.3"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@types/pg": "^8.6.5",
|
|
26
27
|
"@types/node": "^20.10.5",
|
|
27
28
|
"@types/ws": "^8.5.10",
|
|
28
|
-
"drizzle-kit": "^0.31.4",
|
|
29
29
|
"tsx": "^4.20.6",
|
|
30
30
|
"typescript": "^5.9.2"
|
|
31
31
|
}
|
|
@@ -1,52 +1,23 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import * as dotenv from "dotenv";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { fileURLToPath } from "url";
|
|
4
|
+
import { loadEnv } from "@rebasepro/server";
|
|
5
|
+
import { z } from "zod";
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = path.dirname(__filename);
|
|
8
9
|
|
|
9
10
|
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
FRONTEND_URL: z.string().optional(),
|
|
23
|
-
DB_POOL_MAX: z.string().default("20").transform(Number),
|
|
24
|
-
DB_POOL_IDLE_TIMEOUT: z.string().default("30000").transform(Number),
|
|
25
|
-
DB_POOL_CONNECT_TIMEOUT: z.string().default("10000").transform(Number),
|
|
26
|
-
FORCE_LOCAL_STORAGE: z.enum(["true", "false", ""]).optional().transform(v => v === "true"),
|
|
27
|
-
STORAGE_TYPE: z.enum(["local", "s3"]).default("local"),
|
|
28
|
-
STORAGE_PATH: z.string().optional(),
|
|
29
|
-
S3_BUCKET: z.string().optional(),
|
|
30
|
-
S3_REGION: z.string().optional(),
|
|
31
|
-
S3_ACCESS_KEY_ID: z.string().optional(),
|
|
32
|
-
S3_SECRET_ACCESS_KEY: z.string().optional(),
|
|
33
|
-
S3_ENDPOINT: z.string().url().optional(),
|
|
34
|
-
S3_FORCE_PATH_STYLE: z.enum(["true", "false", ""]).optional().transform(v => v === "true"),
|
|
35
|
-
/**
|
|
36
|
-
* Service key for admin-level script / server-to-server authentication.
|
|
37
|
-
* When set, scripts can send `Authorization: Bearer <key>` to get admin access.
|
|
38
|
-
* Generate with: node -e "console.log(require('crypto').randomBytes(48).toString('base64'))"
|
|
39
|
-
*/
|
|
40
|
-
REBASE_SERVICE_KEY: z.string().min(32, "REBASE_SERVICE_KEY must be at least 32 characters").optional()
|
|
41
|
-
}).superRefine((data, ctx) => {
|
|
42
|
-
if (data.NODE_ENV === "production" && !data.CORS_ORIGINS && !data.FRONTEND_URL) {
|
|
43
|
-
ctx.addIssue({
|
|
44
|
-
code: z.ZodIssueCode.custom,
|
|
45
|
-
message: "CORS_ORIGINS or FRONTEND_URL must be set in production to secure the API.",
|
|
46
|
-
path: ["CORS_ORIGINS"]
|
|
47
|
-
});
|
|
48
|
-
}
|
|
12
|
+
export const env = loadEnv({
|
|
13
|
+
extend: z.object({
|
|
14
|
+
SMTP_HOST: z.string().optional(),
|
|
15
|
+
SMTP_PORT: z.string().default("587").transform(Number),
|
|
16
|
+
SMTP_SECURE: z.enum(["true", "false", ""]).default("false").transform(v => v === "true"),
|
|
17
|
+
SMTP_USER: z.string().optional(),
|
|
18
|
+
SMTP_PASS: z.string().optional(),
|
|
19
|
+
SMTP_FROM: z.string().optional(),
|
|
20
|
+
SMTP_NAME: z.string().optional(),
|
|
21
|
+
APP_NAME: z.string().default("Rebase")
|
|
22
|
+
})
|
|
49
23
|
});
|
|
50
|
-
|
|
51
|
-
// Parse and export
|
|
52
|
-
export const env = envSchema.parse(process.env);
|
|
@@ -7,25 +7,36 @@ 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 { createPostgresDatabaseConnection,
|
|
17
|
-
import { enums, relations, tables } from "./schema.generated";
|
|
18
|
-
import { env } from "./env";
|
|
16
|
+
} from "@rebasepro/server";
|
|
17
|
+
import { createPostgresDatabaseConnection, createPostgresAdapter } from "@rebasepro/server-postgres";
|
|
18
|
+
import { enums, relations, tables } from "./schema.generated.js";
|
|
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);
|
|
22
24
|
|
|
23
25
|
// ─── App ─────────────────────────────────────────────────────────────
|
|
24
|
-
const app = new Hono<HonoEnv>();
|
|
26
|
+
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({
|
|
@@ -54,26 +65,47 @@ async function startServer() {
|
|
|
54
65
|
functionsDir: path.resolve(__dirname, "../functions"),
|
|
55
66
|
server,
|
|
56
67
|
app,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
schema: { tables,
|
|
68
|
+
database: createPostgresAdapter({
|
|
69
|
+
connection: db,
|
|
70
|
+
schema: { tables,
|
|
61
71
|
enums,
|
|
62
72
|
relations },
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
],
|
|
73
|
+
adminConnectionString: env.ADMIN_CONNECTION_STRING || databaseUrl,
|
|
74
|
+
connectionString
|
|
75
|
+
}),
|
|
67
76
|
auth: {
|
|
77
|
+
collection: usersCollection,
|
|
68
78
|
jwtSecret,
|
|
69
79
|
accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN,
|
|
70
80
|
refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN,
|
|
71
81
|
serviceKey: env.REBASE_SERVICE_KEY,
|
|
82
|
+
// Cookie-based auth: the refresh token is stored in an httpOnly
|
|
83
|
+
// cookie (not readable by JS) instead of localStorage, so it is
|
|
84
|
+
// not exposed to XSS. The frontend opts in via
|
|
85
|
+
// `authFlowMode: "cookie"` on createRebaseClient. Requires CORS
|
|
86
|
+
// `credentials: true` (set above).
|
|
87
|
+
cookieAuth: { sameSite: "Lax" },
|
|
72
88
|
google: env.GOOGLE_CLIENT_ID
|
|
73
89
|
? { clientId: env.GOOGLE_CLIENT_ID }
|
|
74
90
|
: undefined,
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
allowRegistration: env.ALLOW_REGISTRATION,
|
|
92
|
+
email: env.SMTP_HOST
|
|
93
|
+
? {
|
|
94
|
+
from: env.SMTP_FROM || `${env.APP_NAME} <noreply@rebase.pro>`,
|
|
95
|
+
smtp: {
|
|
96
|
+
host: env.SMTP_HOST,
|
|
97
|
+
port: env.SMTP_PORT,
|
|
98
|
+
secure: env.SMTP_SECURE,
|
|
99
|
+
auth: env.SMTP_USER
|
|
100
|
+
? { user: env.SMTP_USER,
|
|
101
|
+
pass: env.SMTP_PASS! }
|
|
102
|
+
: undefined,
|
|
103
|
+
name: env.SMTP_NAME
|
|
104
|
+
},
|
|
105
|
+
appName: env.APP_NAME,
|
|
106
|
+
resetPasswordUrl: env.FRONTEND_URL
|
|
107
|
+
}
|
|
108
|
+
: undefined
|
|
77
109
|
},
|
|
78
110
|
storage: env.STORAGE_TYPE === "s3"
|
|
79
111
|
? {
|
|
@@ -111,7 +143,7 @@ relations },
|
|
|
111
143
|
if (!isProduction) {
|
|
112
144
|
// Dev mode: retry the next port if the current one is in use
|
|
113
145
|
const projectRoot = path.resolve(__dirname, "../..");
|
|
114
|
-
const actualPort = await listenWithPortRetry(server, PORT, { portFileDir: projectRoot });
|
|
146
|
+
const actualPort = await listenWithPortRetry(server, PORT, { portFileDir: projectRoot, serviceKey: env.REBASE_SERVICE_KEY });
|
|
115
147
|
|
|
116
148
|
// Clean up port file on exit
|
|
117
149
|
const cleanup = () => cleanupDevPortFile(projectRoot);
|
|
@@ -127,17 +159,11 @@ relations },
|
|
|
127
159
|
}
|
|
128
160
|
|
|
129
161
|
// ─── Graceful Shutdown ───────────────────────────────────────────────
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
await pool.end();
|
|
136
|
-
logger.info("Database pool closed");
|
|
137
|
-
process.exit(0);
|
|
138
|
-
};
|
|
139
|
-
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
140
|
-
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
162
|
+
// Drains HTTP, stops crons, tears down realtime, then closes the pool.
|
|
163
|
+
// Guards against double signals and force-exits if shutdown hangs.
|
|
164
|
+
installShutdownHandlers(backend, {
|
|
165
|
+
onCleanup: () => pool.end()
|
|
166
|
+
});
|
|
141
167
|
}
|
|
142
168
|
|
|
143
169
|
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",
|
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
import postsCollection from "./posts";
|
|
2
|
-
import authorsCollection from "./authors";
|
|
3
|
-
import tagsCollection from "./tags";
|
|
1
|
+
import postsCollection from "./posts.js";
|
|
2
|
+
import authorsCollection from "./authors.js";
|
|
3
|
+
import tagsCollection from "./tags.js";
|
|
4
|
+
import usersCollection from "./users.js";
|
|
5
|
+
import type { SecurityRule } from "@rebasepro/types";
|
|
4
6
|
|
|
5
|
-
export const collections = [postsCollection, authorsCollection, tagsCollection];
|
|
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 {
|
|
2
|
-
import authorsCollection from "./authors";
|
|
3
|
-
import tagsCollection from "./tags";
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import authorsCollection from "./authors.js";
|
|
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",
|
|
@@ -12,9 +12,7 @@ const postsCollection: EntityCollection = {
|
|
|
12
12
|
id: {
|
|
13
13
|
name: "ID",
|
|
14
14
|
type: "number",
|
|
15
|
-
|
|
16
|
-
required: true
|
|
17
|
-
}
|
|
15
|
+
isId: "increment"
|
|
18
16
|
},
|
|
19
17
|
title: {
|
|
20
18
|
name: "Title",
|
|
@@ -26,7 +24,9 @@ const postsCollection: EntityCollection = {
|
|
|
26
24
|
content: {
|
|
27
25
|
name: "Content",
|
|
28
26
|
type: "string",
|
|
29
|
-
|
|
27
|
+
ui: {
|
|
28
|
+
markdown: true
|
|
29
|
+
}
|
|
30
30
|
},
|
|
31
31
|
status: {
|
|
32
32
|
name: "Status",
|
|
@@ -52,40 +52,20 @@ const postsCollection: EntityCollection = {
|
|
|
52
52
|
author: {
|
|
53
53
|
name: "Author",
|
|
54
54
|
type: "relation",
|
|
55
|
-
relationName: "author",
|
|
56
|
-
relation: {
|
|
57
|
-
relationName: "author",
|
|
58
|
-
cardinality: "one",
|
|
59
|
-
direction: "owning",
|
|
60
|
-
target: () => authorsCollection
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
tags: {
|
|
64
|
-
name: "Tags",
|
|
65
|
-
type: "relation",
|
|
66
|
-
relationName: "tags",
|
|
67
|
-
relation: {
|
|
68
|
-
relationName: "tags",
|
|
69
|
-
cardinality: "many",
|
|
70
|
-
direction: "owning",
|
|
71
|
-
target: () => tagsCollection
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
relations: [
|
|
76
|
-
{
|
|
77
55
|
relationName: "author",
|
|
78
56
|
target: () => authorsCollection,
|
|
79
57
|
cardinality: "one",
|
|
80
58
|
direction: "owning"
|
|
81
59
|
},
|
|
82
|
-
{
|
|
60
|
+
tags: {
|
|
61
|
+
name: "Tags",
|
|
62
|
+
type: "relation",
|
|
83
63
|
relationName: "tags",
|
|
84
64
|
target: () => tagsCollection,
|
|
85
65
|
cardinality: "many",
|
|
86
66
|
direction: "owning"
|
|
87
67
|
}
|
|
88
|
-
|
|
68
|
+
}
|
|
89
69
|
};
|
|
90
70
|
|
|
91
71
|
export default postsCollection;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
const categoriesCollection: CollectionConfig = {
|
|
4
|
+
name: "Categories",
|
|
5
|
+
singularName: "Category",
|
|
6
|
+
slug: "categories",
|
|
7
|
+
table: "categories",
|
|
8
|
+
icon: "Category",
|
|
9
|
+
properties: {
|
|
10
|
+
id: {
|
|
11
|
+
name: "ID",
|
|
12
|
+
type: "number",
|
|
13
|
+
isId: "increment"
|
|
14
|
+
},
|
|
15
|
+
name: {
|
|
16
|
+
name: "Name",
|
|
17
|
+
type: "string",
|
|
18
|
+
validation: { required: true }
|
|
19
|
+
},
|
|
20
|
+
slug: {
|
|
21
|
+
name: "Slug",
|
|
22
|
+
type: "string",
|
|
23
|
+
validation: { required: true }
|
|
24
|
+
},
|
|
25
|
+
description: {
|
|
26
|
+
name: "Description",
|
|
27
|
+
type: "string",
|
|
28
|
+
ui: {
|
|
29
|
+
multiline: true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
icon: {
|
|
33
|
+
name: "Icon",
|
|
34
|
+
type: "string"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
propertiesOrder: ["id", "name", "slug", "description", "icon"]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default categoriesCollection;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import productsCollection from "./products.js";
|
|
2
|
+
import categoriesCollection from "./categories.js";
|
|
3
|
+
import ordersCollection from "./orders.js";
|
|
4
|
+
import usersCollection from "../../users.js";
|
|
5
|
+
|
|
6
|
+
export const collections = [productsCollection, categoriesCollection, ordersCollection, usersCollection];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
const ordersCollection: CollectionConfig = {
|
|
4
|
+
name: "Orders",
|
|
5
|
+
singularName: "Order",
|
|
6
|
+
slug: "orders",
|
|
7
|
+
table: "orders",
|
|
8
|
+
icon: "Receipt",
|
|
9
|
+
properties: {
|
|
10
|
+
id: {
|
|
11
|
+
name: "ID",
|
|
12
|
+
type: "number",
|
|
13
|
+
isId: "increment"
|
|
14
|
+
},
|
|
15
|
+
customerEmail: {
|
|
16
|
+
name: "Customer Email",
|
|
17
|
+
type: "string",
|
|
18
|
+
columnName: "customer_email",
|
|
19
|
+
validation: { required: true }
|
|
20
|
+
},
|
|
21
|
+
status: {
|
|
22
|
+
name: "Status",
|
|
23
|
+
type: "string",
|
|
24
|
+
enum: [
|
|
25
|
+
{ id: "pending",
|
|
26
|
+
label: "Pending",
|
|
27
|
+
color: "gray" },
|
|
28
|
+
{ id: "processing",
|
|
29
|
+
label: "Processing",
|
|
30
|
+
color: "blue" },
|
|
31
|
+
{ id: "shipped",
|
|
32
|
+
label: "Shipped",
|
|
33
|
+
color: "orange" },
|
|
34
|
+
{ id: "delivered",
|
|
35
|
+
label: "Delivered",
|
|
36
|
+
color: "green" },
|
|
37
|
+
{ id: "cancelled",
|
|
38
|
+
label: "Cancelled",
|
|
39
|
+
color: "red" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
total: {
|
|
43
|
+
name: "Total",
|
|
44
|
+
type: "number",
|
|
45
|
+
validation: { required: true }
|
|
46
|
+
},
|
|
47
|
+
notes: {
|
|
48
|
+
name: "Notes",
|
|
49
|
+
type: "string",
|
|
50
|
+
ui: {
|
|
51
|
+
multiline: true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
createdAt: {
|
|
55
|
+
name: "Created At",
|
|
56
|
+
type: "date",
|
|
57
|
+
columnName: "created_at",
|
|
58
|
+
autoValue: "on_create",
|
|
59
|
+
ui: {
|
|
60
|
+
readOnly: true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default ordersCollection;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import categoriesCollection from "./categories.js";
|
|
3
|
+
|
|
4
|
+
const productsCollection: CollectionConfig = {
|
|
5
|
+
name: "Products",
|
|
6
|
+
singularName: "Product",
|
|
7
|
+
slug: "products",
|
|
8
|
+
table: "products",
|
|
9
|
+
icon: "ShoppingCart",
|
|
10
|
+
properties: {
|
|
11
|
+
id: {
|
|
12
|
+
name: "ID",
|
|
13
|
+
type: "number",
|
|
14
|
+
isId: "increment"
|
|
15
|
+
},
|
|
16
|
+
name: {
|
|
17
|
+
name: "Name",
|
|
18
|
+
type: "string",
|
|
19
|
+
validation: { required: true }
|
|
20
|
+
},
|
|
21
|
+
description: {
|
|
22
|
+
name: "Description",
|
|
23
|
+
type: "string",
|
|
24
|
+
ui: {
|
|
25
|
+
markdown: true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
price: {
|
|
29
|
+
name: "Price",
|
|
30
|
+
type: "number",
|
|
31
|
+
validation: { required: true }
|
|
32
|
+
},
|
|
33
|
+
image: {
|
|
34
|
+
name: "Image",
|
|
35
|
+
type: "string",
|
|
36
|
+
storage: { storagePath: "product_images/" }
|
|
37
|
+
},
|
|
38
|
+
status: {
|
|
39
|
+
name: "Status",
|
|
40
|
+
type: "string",
|
|
41
|
+
enum: [
|
|
42
|
+
{ id: "draft",
|
|
43
|
+
label: "Draft",
|
|
44
|
+
color: "gray" },
|
|
45
|
+
{ id: "active",
|
|
46
|
+
label: "Active",
|
|
47
|
+
color: "green" },
|
|
48
|
+
{ id: "archived",
|
|
49
|
+
label: "Archived",
|
|
50
|
+
color: "orange" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
category: {
|
|
54
|
+
name: "Category",
|
|
55
|
+
type: "relation",
|
|
56
|
+
relationName: "category",
|
|
57
|
+
target: () => categoriesCollection,
|
|
58
|
+
cardinality: "one",
|
|
59
|
+
direction: "owning"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default productsCollection;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
2
|
|
|
3
|
-
const tagsCollection:
|
|
3
|
+
const tagsCollection: CollectionConfig = {
|
|
4
4
|
name: "Tags",
|
|
5
5
|
singularName: "Tag",
|
|
6
6
|
slug: "tags",
|
|
@@ -10,9 +10,7 @@ const tagsCollection: EntityCollection = {
|
|
|
10
10
|
id: {
|
|
11
11
|
name: "ID",
|
|
12
12
|
type: "number",
|
|
13
|
-
|
|
14
|
-
required: true
|
|
15
|
-
}
|
|
13
|
+
isId: "increment"
|
|
16
14
|
},
|
|
17
15
|
name: {
|
|
18
16
|
name: "Tag Name",
|