@rebasepro/cli 0.0.1-canary.2 → 0.0.1-canary.4829d6e
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/auth.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/db.d.ts +1 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/generate_sdk.d.ts +18 -0
- package/dist/commands/init.d.ts +40 -0
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/skills.d.ts +1 -0
- package/dist/commands/start.d.ts +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.es.js +3952 -245
- package/dist/index.es.js.map +1 -1
- package/dist/utils/package-manager.d.ts +33 -0
- package/dist/utils/project.d.ts +60 -0
- package/package.json +35 -23
- 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/.dockerignore +28 -0
- package/templates/template/.env.example +120 -0
- 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 +88 -21
- package/templates/template/ai-instructions.md +17 -0
- package/templates/template/backend/Dockerfile +57 -11
- package/templates/template/backend/functions/hello.ts +56 -0
- package/templates/template/backend/package.json +30 -34
- package/templates/template/backend/src/env.ts +23 -0
- package/templates/template/backend/src/index.ts +140 -100
- package/templates/template/backend/tsconfig.json +1 -1
- package/templates/template/config/collections/authors.ts +45 -0
- package/templates/template/config/collections/index.ts +26 -0
- package/templates/template/{shared → config}/collections/posts.ts +28 -10
- 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 +25 -0
- package/templates/template/config/collections/users.ts +142 -0
- package/templates/template/{shared → config}/index.ts +1 -1
- package/templates/template/config/package.json +28 -0
- package/templates/template/docker-compose.yml +50 -14
- package/templates/template/frontend/Dockerfile +39 -15
- package/templates/template/frontend/nginx.conf +40 -0
- package/templates/template/frontend/package.json +13 -13
- package/templates/template/frontend/src/App.tsx +30 -116
- package/templates/template/frontend/src/index.css +15 -1
- package/templates/template/frontend/src/main.tsx +6 -2
- 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 +48 -5
- package/templates/template/package.json +21 -12
- package/templates/template/pnpm-workspace.yaml +13 -0
- package/templates/template/scripts/example.ts +94 -0
- package/dist/auth.d.ts +0 -5
- package/dist/index.cjs +0 -293
- package/dist/index.cjs.map +0 -1
- package/templates/template/.env.template +0 -31
- package/templates/template/backend/drizzle.config.ts +0 -22
- package/templates/template/backend/scripts/db-generate.ts +0 -60
- package/templates/template/shared/collections/index.ts +0 -3
- package/templates/template/shared/package.json +0 -28
- /package/templates/template/{shared → config}/tsconfig.json +0 -0
|
@@ -1,134 +1,174 @@
|
|
|
1
|
-
import
|
|
2
|
-
import cors from "cors";
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { cors } from "hono/cors";
|
|
3
|
+
import { secureHeaders } from "hono/secure-headers";
|
|
4
|
+
import { getRequestListener } from "@hono/node-server";
|
|
5
|
+
import { createServer } from "http";
|
|
3
6
|
import path from "path";
|
|
4
7
|
import { fileURLToPath } from "url";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import {
|
|
9
|
+
initializeRebaseBackend,
|
|
10
|
+
installShutdownHandlers,
|
|
11
|
+
serveSPA,
|
|
12
|
+
HonoEnv,
|
|
13
|
+
listenWithPortRetry,
|
|
14
|
+
cleanupDevPortFile,
|
|
15
|
+
logger
|
|
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";
|
|
11
21
|
|
|
12
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
23
|
const __dirname = path.dirname(__filename);
|
|
14
24
|
|
|
15
|
-
//
|
|
16
|
-
|
|
25
|
+
// ─── App ─────────────────────────────────────────────────────────────
|
|
26
|
+
const app: Hono<HonoEnv> = new Hono<HonoEnv>();
|
|
27
|
+
|
|
28
|
+
const isProduction = env.NODE_ENV === "production";
|
|
29
|
+
const allowedOrigins = isProduction
|
|
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
|
+
})()
|
|
40
|
+
: [];
|
|
41
|
+
|
|
42
|
+
app.use("/*", cors({
|
|
43
|
+
origin: (origin) => {
|
|
44
|
+
if (!isProduction) return origin || "*";
|
|
45
|
+
return allowedOrigins.includes(origin) ? origin : null;
|
|
46
|
+
},
|
|
47
|
+
credentials: true
|
|
48
|
+
}));
|
|
17
49
|
|
|
18
|
-
|
|
19
|
-
const server = createServer(app);
|
|
50
|
+
app.use("/*", secureHeaders());
|
|
20
51
|
|
|
21
|
-
//
|
|
22
|
-
const databaseUrl =
|
|
23
|
-
if (!databaseUrl) {
|
|
24
|
-
throw new Error("DATABASE_URL environment variable is not set");
|
|
25
|
-
}
|
|
26
|
-
const db = createPostgresDatabaseConnection(databaseUrl);
|
|
52
|
+
// ─── Database ────────────────────────────────────────────────────────
|
|
53
|
+
const databaseUrl = env.DATABASE_URL;
|
|
27
54
|
|
|
28
|
-
|
|
29
|
-
app.use(cors());
|
|
30
|
-
app.use(express.json({ limit: "10mb" }));
|
|
55
|
+
const { db, pool, connectionString } = createPostgresDatabaseConnection(databaseUrl);
|
|
31
56
|
|
|
57
|
+
// ─── Start ───────────────────────────────────────────────────────────
|
|
32
58
|
async function startServer() {
|
|
33
|
-
const jwtSecret =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
59
|
+
const jwtSecret = env.JWT_SECRET;
|
|
60
|
+
const PORT = env.PORT;
|
|
61
|
+
const server = createServer(getRequestListener(app.fetch));
|
|
37
62
|
|
|
38
|
-
// Initialize Rebase Backend with auth
|
|
39
63
|
const backend = await initializeRebaseBackend({
|
|
40
|
-
collectionsDir: path.resolve(__dirname, "../../
|
|
64
|
+
collectionsDir: path.resolve(__dirname, "../../config/collections"),
|
|
65
|
+
functionsDir: path.resolve(__dirname, "../functions"),
|
|
41
66
|
server,
|
|
42
67
|
app,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
database: createPostgresAdapter({
|
|
69
|
+
connection: db,
|
|
70
|
+
schema: { tables,
|
|
71
|
+
enums,
|
|
72
|
+
relations },
|
|
73
|
+
adminConnectionString: env.ADMIN_CONNECTION_STRING || databaseUrl,
|
|
74
|
+
connectionString
|
|
75
|
+
}),
|
|
50
76
|
auth: {
|
|
77
|
+
collection: usersCollection,
|
|
51
78
|
jwtSecret,
|
|
52
|
-
accessExpiresIn:
|
|
53
|
-
refreshExpiresIn:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
accessExpiresIn: env.JWT_ACCESS_EXPIRES_IN,
|
|
80
|
+
refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN,
|
|
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" },
|
|
88
|
+
google: env.GOOGLE_CLIENT_ID
|
|
89
|
+
? { clientId: env.GOOGLE_CLIENT_ID }
|
|
90
|
+
: undefined,
|
|
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
|
|
72
107
|
}
|
|
73
|
-
|
|
108
|
+
: undefined
|
|
74
109
|
},
|
|
75
|
-
storage:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
110
|
+
storage: env.STORAGE_TYPE === "s3"
|
|
111
|
+
? {
|
|
112
|
+
type: "s3",
|
|
113
|
+
bucket: env.S3_BUCKET!,
|
|
114
|
+
region: env.S3_REGION || "auto",
|
|
115
|
+
accessKeyId: env.S3_ACCESS_KEY_ID || "",
|
|
116
|
+
secretAccessKey: env.S3_SECRET_ACCESS_KEY || "",
|
|
117
|
+
endpoint: env.S3_ENDPOINT,
|
|
118
|
+
forcePathStyle: env.S3_FORCE_PATH_STYLE
|
|
119
|
+
}
|
|
120
|
+
: {
|
|
121
|
+
type: "local",
|
|
122
|
+
basePath: env.STORAGE_PATH || path.resolve(__dirname, "../../uploads")
|
|
123
|
+
},
|
|
124
|
+
history: true
|
|
79
125
|
});
|
|
80
126
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
127
|
+
// ─── Health check ─────────────────────────────────────────────
|
|
128
|
+
app.get("/health", async (c) => {
|
|
129
|
+
const result = await backend.healthCheck();
|
|
130
|
+
const status = result.healthy ? 200 : 503;
|
|
131
|
+
return c.json({
|
|
132
|
+
status: result.healthy ? "ok" : "degraded",
|
|
133
|
+
latencyMs: result.latencyMs,
|
|
134
|
+
...(result.details ? { details: result.details } : {})
|
|
135
|
+
}, status);
|
|
91
136
|
});
|
|
92
137
|
|
|
93
|
-
// Serve
|
|
94
|
-
if (
|
|
95
|
-
serveSPA(app, {
|
|
96
|
-
frontendPath: path.join(__dirname, "../../frontend/dist"),
|
|
97
|
-
excludePaths: ["/health"]
|
|
98
|
-
});
|
|
138
|
+
// Serve the frontend in production
|
|
139
|
+
if (isProduction) {
|
|
140
|
+
serveSPA(app, { frontendPath: path.join(__dirname, "../../frontend/dist") });
|
|
99
141
|
}
|
|
100
142
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
if (!isProduction) {
|
|
144
|
+
// Dev mode: retry the next port if the current one is in use
|
|
145
|
+
const projectRoot = path.resolve(__dirname, "../..");
|
|
146
|
+
const actualPort = await listenWithPortRetry(server, PORT, { portFileDir: projectRoot, serviceKey: env.REBASE_SERVICE_KEY });
|
|
147
|
+
|
|
148
|
+
// Clean up port file on exit
|
|
149
|
+
const cleanup = () => cleanupDevPortFile(projectRoot);
|
|
150
|
+
process.on("SIGINT", cleanup);
|
|
151
|
+
process.on("SIGTERM", cleanup);
|
|
152
|
+
process.on("exit", cleanup);
|
|
153
|
+
|
|
154
|
+
logger.info(`Server running at http://localhost:${actualPort}`);
|
|
155
|
+
} else {
|
|
156
|
+
server.listen(PORT, () => {
|
|
157
|
+
logger.info(`Server running at http://localhost:${PORT}`);
|
|
115
158
|
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const PORT = process.env.PORT || 3001;
|
|
159
|
+
}
|
|
119
160
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
console.log(` • Health: http://localhost:${PORT}/health`);
|
|
161
|
+
// ─── Graceful Shutdown ───────────────────────────────────────────────
|
|
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()
|
|
126
166
|
});
|
|
127
167
|
}
|
|
128
168
|
|
|
129
169
|
startServer().catch(err => {
|
|
130
|
-
|
|
170
|
+
logger.error("Failed to start server", { error: err instanceof Error ? err : new Error(String(err)) });
|
|
131
171
|
process.exit(1);
|
|
132
172
|
});
|
|
133
173
|
|
|
134
|
-
export { app
|
|
174
|
+
export { app };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
const authorsCollection: CollectionConfig = {
|
|
4
|
+
name: "Authors",
|
|
5
|
+
singularName: "Author",
|
|
6
|
+
slug: "authors",
|
|
7
|
+
table: "authors",
|
|
8
|
+
icon: "Person",
|
|
9
|
+
properties: {
|
|
10
|
+
id: {
|
|
11
|
+
name: "ID",
|
|
12
|
+
type: "number",
|
|
13
|
+
isId: "increment"
|
|
14
|
+
},
|
|
15
|
+
name: {
|
|
16
|
+
name: "Name",
|
|
17
|
+
type: "string",
|
|
18
|
+
validation: {
|
|
19
|
+
required: true
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
email: {
|
|
23
|
+
name: "Email",
|
|
24
|
+
type: "string",
|
|
25
|
+
validation: {
|
|
26
|
+
required: true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
picture: {
|
|
30
|
+
name: "Picture",
|
|
31
|
+
type: "string",
|
|
32
|
+
storage: {
|
|
33
|
+
storagePath: "author_pictures/"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
propertiesOrder: [
|
|
38
|
+
"id",
|
|
39
|
+
"name",
|
|
40
|
+
"email",
|
|
41
|
+
"picture"
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default authorsCollection;
|
|
@@ -0,0 +1,26 @@
|
|
|
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";
|
|
6
|
+
|
|
7
|
+
export const collections = [postsCollection, authorsCollection, tagsCollection, usersCollection];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Applied to any collection in this directory that declares no
|
|
11
|
+
* `securityRules` of its own: anyone can read, only admins can write.
|
|
12
|
+
*
|
|
13
|
+
* These live here, next to the collections, because `rebase db push`
|
|
14
|
+
* generates the Postgres policies from these files — that is what actually
|
|
15
|
+
* enforces access. A default declared on the server could never reach the
|
|
16
|
+
* database.
|
|
17
|
+
*
|
|
18
|
+
* A collection that declares neither its own rules nor inherits these is
|
|
19
|
+
* locked by default: admin-only.
|
|
20
|
+
*/
|
|
21
|
+
export const defaultSecurityRules: SecurityRule[] = [
|
|
22
|
+
{ operation: "select",
|
|
23
|
+
access: "public" },
|
|
24
|
+
{ operations: ["insert", "update", "delete"],
|
|
25
|
+
roles: ["admin"] }
|
|
26
|
+
];
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import authorsCollection from "./authors.js";
|
|
3
|
+
import tagsCollection from "./tags.js";
|
|
2
4
|
|
|
3
|
-
const postsCollection:
|
|
5
|
+
const postsCollection: CollectionConfig = {
|
|
4
6
|
name: "Posts",
|
|
5
7
|
singularName: "Post",
|
|
6
8
|
slug: "posts",
|
|
7
|
-
|
|
9
|
+
table: "posts",
|
|
8
10
|
icon: "Article",
|
|
9
11
|
properties: {
|
|
10
12
|
id: {
|
|
11
13
|
name: "ID",
|
|
12
14
|
type: "number",
|
|
13
|
-
|
|
14
|
-
required: true
|
|
15
|
-
}
|
|
15
|
+
isId: "increment"
|
|
16
16
|
},
|
|
17
17
|
title: {
|
|
18
18
|
name: "Title",
|
|
@@ -24,7 +24,9 @@ const postsCollection: EntityCollection = {
|
|
|
24
24
|
content: {
|
|
25
25
|
name: "Content",
|
|
26
26
|
type: "string",
|
|
27
|
-
|
|
27
|
+
ui: {
|
|
28
|
+
markdown: true
|
|
29
|
+
}
|
|
28
30
|
},
|
|
29
31
|
status: {
|
|
30
32
|
name: "Status",
|
|
@@ -33,19 +35,35 @@ const postsCollection: EntityCollection = {
|
|
|
33
35
|
{
|
|
34
36
|
id: "draft",
|
|
35
37
|
label: "Draft",
|
|
36
|
-
color: "
|
|
38
|
+
color: "gray"
|
|
37
39
|
},
|
|
38
40
|
{
|
|
39
41
|
id: "review",
|
|
40
42
|
label: "In Review",
|
|
41
|
-
color: "
|
|
43
|
+
color: "orange"
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
46
|
id: "published",
|
|
45
47
|
label: "Published",
|
|
46
|
-
color: "
|
|
48
|
+
color: "green"
|
|
47
49
|
}
|
|
48
50
|
]
|
|
51
|
+
},
|
|
52
|
+
author: {
|
|
53
|
+
name: "Author",
|
|
54
|
+
type: "relation",
|
|
55
|
+
relationName: "author",
|
|
56
|
+
target: () => authorsCollection,
|
|
57
|
+
cardinality: "one",
|
|
58
|
+
direction: "owning"
|
|
59
|
+
},
|
|
60
|
+
tags: {
|
|
61
|
+
name: "Tags",
|
|
62
|
+
type: "relation",
|
|
63
|
+
relationName: "tags",
|
|
64
|
+
target: () => tagsCollection,
|
|
65
|
+
cardinality: "many",
|
|
66
|
+
direction: "owning"
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
69
|
};
|
|
@@ -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;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
const tagsCollection: CollectionConfig = {
|
|
4
|
+
name: "Tags",
|
|
5
|
+
singularName: "Tag",
|
|
6
|
+
slug: "tags",
|
|
7
|
+
table: "tags",
|
|
8
|
+
icon: "Tag",
|
|
9
|
+
properties: {
|
|
10
|
+
id: {
|
|
11
|
+
name: "ID",
|
|
12
|
+
type: "number",
|
|
13
|
+
isId: "increment"
|
|
14
|
+
},
|
|
15
|
+
name: {
|
|
16
|
+
name: "Tag Name",
|
|
17
|
+
type: "string",
|
|
18
|
+
validation: {
|
|
19
|
+
required: true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default tagsCollection;
|