@brightsideoy/kama 0.1.0-alpha.22 ā 0.1.0-alpha.23
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/cli.js +40 -10
- package/package.json +6 -5
- package/schema.sql +193 -0
package/bin/cli.js
CHANGED
|
@@ -36,7 +36,9 @@ async function createFullWorkspace() {
|
|
|
36
36
|
const siteDomain = (await rl.question('First Website Domain (e.g., www.example.com): ')) || 'www.example.com';
|
|
37
37
|
|
|
38
38
|
const rootDir = join(process.cwd(), projName);
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
// Changed to 'let' so we can rename it if it already exists
|
|
41
|
+
let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
40
42
|
|
|
41
43
|
console.log(`\nš Creating workspace folder at ./${projName}...`);
|
|
42
44
|
mkdirSync(rootDir, { recursive: true });
|
|
@@ -63,21 +65,49 @@ async function createFullWorkspace() {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
if (isAuthenticated) {
|
|
66
|
-
console.log(`\nšļø Attempting to auto-create Cloudflare D1 Database '${dbName}'...`);
|
|
67
68
|
try {
|
|
68
|
-
|
|
69
|
+
console.log(`\nšļø Checking existing Cloudflare D1 databases...`);
|
|
70
|
+
const listOutput = execSync(`npx wrangler d1 list --json`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
71
|
+
|
|
72
|
+
// Safely parse JSON in case Wrangler prints update warnings before the JSON array
|
|
73
|
+
const jsonStart = listOutput.indexOf('[');
|
|
74
|
+
const jsonEnd = listOutput.lastIndexOf(']') + 1;
|
|
75
|
+
let databases = [];
|
|
69
76
|
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if (jsonStart !== -1 && jsonEnd !== -1) {
|
|
78
|
+
databases = JSON.parse(listOutput.substring(jsonStart, jsonEnd));
|
|
79
|
+
}
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
const existingDb = databases.find(db => db.name === dbName);
|
|
82
|
+
|
|
83
|
+
if (existingDb) {
|
|
84
|
+
console.log(`ā ļø Database '${dbName}' already exists on your account.`);
|
|
85
|
+
const useExisting = await rl.question(`Do you want to link this existing database? (y/n) [y]: `);
|
|
86
|
+
|
|
87
|
+
if (useExisting.toLowerCase() !== 'n') {
|
|
88
|
+
d1DatabaseId = existingDb.uuid;
|
|
89
|
+
console.log(`ā
Linked existing database ID: ${d1DatabaseId}`);
|
|
90
|
+
} else {
|
|
91
|
+
dbName = (await rl.question(`Enter a new database name: `)).trim();
|
|
92
|
+
console.log(`\nšļø Creating new database '${dbName}'...`);
|
|
93
|
+
const d1Output = execSync(`npx wrangler d1 create ${dbName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
94
|
+
const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
|
|
95
|
+
if (match) {
|
|
96
|
+
d1DatabaseId = match[0];
|
|
97
|
+
console.log(`ā
Success! Database ID bound: ${d1DatabaseId}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
76
100
|
} else {
|
|
77
|
-
console.log(
|
|
101
|
+
console.log(`\nšļø Attempting to auto-create Cloudflare D1 Database '${dbName}'...`);
|
|
102
|
+
const d1Output = execSync(`npx wrangler d1 create ${dbName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
103
|
+
const match = d1Output.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/);
|
|
104
|
+
if (match) {
|
|
105
|
+
d1DatabaseId = match[0];
|
|
106
|
+
console.log(`ā
Success! Database ID bound: ${d1DatabaseId}`);
|
|
107
|
+
}
|
|
78
108
|
}
|
|
79
109
|
} catch (error) {
|
|
80
|
-
console.log(`ā ļø
|
|
110
|
+
console.log(`ā ļø Database operation failed. We'll leave a placeholder.`);
|
|
81
111
|
}
|
|
82
112
|
} else {
|
|
83
113
|
console.log(`ā ļø Skipping database creation. We'll leave a placeholder in wrangler.json.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightsideoy/kama",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -16,15 +16,16 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
-
"bin"
|
|
19
|
+
"bin",
|
|
20
|
+
"schema.sql"
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"dev": "concurrently \"vite\" \"tsx watch server/index.ts\"",
|
|
23
24
|
"build": "vite build",
|
|
24
25
|
"build:cms": "vite build",
|
|
25
|
-
"build:
|
|
26
|
-
"db:seed": "npm run build:
|
|
27
|
-
"d1:
|
|
26
|
+
"build:schema": "tsx src/db/generate-schema.ts",
|
|
27
|
+
"db:seed": "npm run build:schema && tsx -e \"import Database from 'better-sqlite3'; import fs from 'node:fs'; const db = new Database('data/cms.db'); db.exec(fs.readFileSync('schema.sql', 'utf8')); console.log('ā
Database schema applied successfully!');\"",
|
|
28
|
+
"d1:schema:remote": "npm run build:schema && npx wrangler d1 execute YOUR_D1_NAME --remote --file=./schema.sql",
|
|
28
29
|
"serve": "tsx server/index.ts"
|
|
29
30
|
},
|
|
30
31
|
"publishConfig": {
|
package/schema.sql
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
-- Kama Core System Schema
|
|
2
|
+
-- Auto-generated from server/db/dynamic-sync.ts
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS "users" (
|
|
5
|
+
id TEXT PRIMARY KEY,
|
|
6
|
+
email TEXT UNIQUE NOT NULL,
|
|
7
|
+
name TEXT NOT NULL,
|
|
8
|
+
globalRole TEXT DEFAULT 'editor',
|
|
9
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
CREATE TABLE IF NOT EXISTS "user_accounts" (
|
|
14
|
+
id TEXT PRIMARY KEY,
|
|
15
|
+
userId TEXT NOT NULL,
|
|
16
|
+
provider TEXT NOT NULL,
|
|
17
|
+
providerAccountId TEXT NOT NULL,
|
|
18
|
+
passwordHash TEXT NOT NULL,
|
|
19
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
20
|
+
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE,
|
|
21
|
+
UNIQUE(provider, providerAccountId)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS "sessions" (
|
|
25
|
+
id TEXT PRIMARY KEY,
|
|
26
|
+
userId TEXT NOT NULL,
|
|
27
|
+
expiresAt TEXT NOT NULL,
|
|
28
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
+
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS "channels" (
|
|
33
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
34
|
+
key TEXT UNIQUE NOT NULL,
|
|
35
|
+
label TEXT NOT NULL,
|
|
36
|
+
channelType TEXT,
|
|
37
|
+
domain TEXT,
|
|
38
|
+
baseUrl TEXT,
|
|
39
|
+
defaultLanguage TEXT DEFAULT 'en',
|
|
40
|
+
isHidden INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
dataJson TEXT,
|
|
42
|
+
createdById TEXT,
|
|
43
|
+
updatedById TEXT,
|
|
44
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
45
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
46
|
+
FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
|
|
47
|
+
FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
CREATE TABLE IF NOT EXISTS "trees" (
|
|
51
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
52
|
+
key TEXT UNIQUE NOT NULL,
|
|
53
|
+
label TEXT NOT NULL,
|
|
54
|
+
treeType TEXT,
|
|
55
|
+
channelId INTEGER,
|
|
56
|
+
isHidden INTEGER NOT NULL DEFAULT 0,
|
|
57
|
+
dataJson TEXT,
|
|
58
|
+
createdById TEXT,
|
|
59
|
+
updatedById TEXT,
|
|
60
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
61
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
62
|
+
FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE SET NULL,
|
|
63
|
+
FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
|
|
64
|
+
FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
CREATE TABLE IF NOT EXISTS "channel_trees" (
|
|
68
|
+
channelId INTEGER NOT NULL,
|
|
69
|
+
treeId INTEGER NOT NULL,
|
|
70
|
+
key TEXT NOT NULL DEFAULT 'main',
|
|
71
|
+
metaJson TEXT,
|
|
72
|
+
PRIMARY KEY (channelId, treeId, key),
|
|
73
|
+
FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE,
|
|
74
|
+
FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE TABLE IF NOT EXISTS "content_nodes" (
|
|
78
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
+
contentType TEXT NOT NULL,
|
|
80
|
+
title TEXT NOT NULL,
|
|
81
|
+
slug TEXT,
|
|
82
|
+
language TEXT DEFAULT 'en',
|
|
83
|
+
isHidden INTEGER NOT NULL DEFAULT 0,
|
|
84
|
+
status TEXT DEFAULT 'draft',
|
|
85
|
+
disabledRegionsJson TEXT DEFAULT '{}',
|
|
86
|
+
dataJson TEXT,
|
|
87
|
+
draftDataJson TEXT,
|
|
88
|
+
publishedDataJson TEXT,
|
|
89
|
+
publishedAt TEXT,
|
|
90
|
+
createdById TEXT,
|
|
91
|
+
updatedById TEXT,
|
|
92
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
93
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
94
|
+
deletedAt TEXT,
|
|
95
|
+
FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL,
|
|
96
|
+
FOREIGN KEY (updatedById) REFERENCES users(id) ON DELETE SET NULL
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
CREATE TABLE IF NOT EXISTS "node_channels" (
|
|
100
|
+
nodeId INTEGER NOT NULL,
|
|
101
|
+
channelId INTEGER NOT NULL,
|
|
102
|
+
PRIMARY KEY (nodeId, channelId),
|
|
103
|
+
FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
|
|
104
|
+
FOREIGN KEY (channelId) REFERENCES channels(id) ON DELETE CASCADE
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
CREATE TABLE IF NOT EXISTS "tree_nodes" (
|
|
108
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
109
|
+
treeId INTEGER NOT NULL,
|
|
110
|
+
contentId INTEGER NOT NULL,
|
|
111
|
+
parentId INTEGER,
|
|
112
|
+
sortOrder INTEGER DEFAULT 0,
|
|
113
|
+
FOREIGN KEY (treeId) REFERENCES trees(id) ON DELETE CASCADE,
|
|
114
|
+
FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE,
|
|
115
|
+
FOREIGN KEY (parentId) REFERENCES tree_nodes(id) ON DELETE CASCADE
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE IF NOT EXISTS "content_blocks" (
|
|
119
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
120
|
+
contentId INTEGER NOT NULL,
|
|
121
|
+
region TEXT DEFAULT 'main',
|
|
122
|
+
elementType TEXT NOT NULL,
|
|
123
|
+
dataJson TEXT,
|
|
124
|
+
sortOrder INTEGER DEFAULT 0,
|
|
125
|
+
isDraft INTEGER NOT NULL DEFAULT 1,
|
|
126
|
+
FOREIGN KEY (contentId) REFERENCES content_nodes(id) ON DELETE CASCADE
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
CREATE TABLE IF NOT EXISTS "node_revisions" (
|
|
130
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
131
|
+
nodeId INTEGER NOT NULL,
|
|
132
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
133
|
+
snapshotJson TEXT NOT NULL,
|
|
134
|
+
createdById TEXT,
|
|
135
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
136
|
+
FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
|
|
137
|
+
FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
CREATE TABLE IF NOT EXISTS "assets" (
|
|
141
|
+
id TEXT PRIMARY KEY,
|
|
142
|
+
filename TEXT NOT NULL,
|
|
143
|
+
mimeType TEXT NOT NULL,
|
|
144
|
+
sizeBytes INTEGER NOT NULL,
|
|
145
|
+
storageKeyPrivate TEXT,
|
|
146
|
+
cdnUrl TEXT,
|
|
147
|
+
parentAssetId TEXT,
|
|
148
|
+
cropCoordinatesJson TEXT,
|
|
149
|
+
isSystemVariant INTEGER DEFAULT 0,
|
|
150
|
+
dataJson TEXT DEFAULT '{}',
|
|
151
|
+
createdById TEXT,
|
|
152
|
+
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
153
|
+
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
154
|
+
deletedAt TEXT,
|
|
155
|
+
FOREIGN KEY (parentAssetId) REFERENCES assets(id) ON DELETE SET NULL,
|
|
156
|
+
FOREIGN KEY (createdById) REFERENCES users(id) ON DELETE SET NULL
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
CREATE TABLE IF NOT EXISTS "node_assets" (
|
|
160
|
+
nodeId INTEGER NOT NULL,
|
|
161
|
+
assetId TEXT NOT NULL,
|
|
162
|
+
fieldKey TEXT NOT NULL,
|
|
163
|
+
sortOrder INTEGER DEFAULT 0,
|
|
164
|
+
PRIMARY KEY (nodeId, assetId, fieldKey),
|
|
165
|
+
FOREIGN KEY (nodeId) REFERENCES content_nodes(id) ON DELETE CASCADE,
|
|
166
|
+
FOREIGN KEY (assetId) REFERENCES assets(id) ON DELETE CASCADE
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(userId);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expiresAt);
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_channels_visibility ON channels(isHidden);
|
|
172
|
+
CREATE INDEX IF NOT EXISTS idx_trees_visibility ON trees(isHidden);
|
|
173
|
+
CREATE INDEX IF NOT EXISTS idx_content_nodes_visibility ON content_nodes(isHidden, status);
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_content_nodes_slug ON content_nodes(slug);
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_content_nodes_type ON content_nodes(contentType);
|
|
176
|
+
CREATE INDEX IF NOT EXISTS idx_content_nodes_deleted_updated ON content_nodes(deletedAt, updatedAt DESC);
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_tree_nodes_hierarchy ON tree_nodes(treeId, parentId, sortOrder);
|
|
178
|
+
CREATE INDEX IF NOT EXISTS idx_tree_nodes_content ON tree_nodes(contentId, treeId);
|
|
179
|
+
CREATE INDEX IF NOT EXISTS idx_content_blocks_content ON content_blocks(contentId, isDraft, region, sortOrder);
|
|
180
|
+
CREATE INDEX IF NOT EXISTS idx_node_channels_channel ON node_channels(channelId, nodeId);
|
|
181
|
+
CREATE INDEX IF NOT EXISTS idx_node_channels_node ON node_channels(nodeId, channelId);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_node_assets_node_asset ON node_assets(nodeId, assetId);
|
|
183
|
+
CREATE INDEX IF NOT EXISTS idx_node_assets_asset_node ON node_assets(assetId, nodeId);
|
|
184
|
+
CREATE INDEX IF NOT EXISTS idx_node_revisions_node ON node_revisions(nodeId, version);
|
|
185
|
+
CREATE INDEX IF NOT EXISTS idx_assets_system_created ON assets(isSystemVariant, createdAt DESC);
|
|
186
|
+
CREATE INDEX IF NOT EXISTS idx_assets_system_mime ON assets(isSystemVariant, mimeType);
|
|
187
|
+
CREATE INDEX IF NOT EXISTS idx_assets_deleted ON assets(deletedAt);
|
|
188
|
+
CREATE INDEX IF NOT EXISTS idx_assets_parent ON assets(parentAssetId);
|
|
189
|
+
|
|
190
|
+
-- System Defaults
|
|
191
|
+
INSERT OR IGNORE INTO channels (id, key, label, channelType, defaultLanguage, isHidden, dataJson) VALUES (1, 'default-channel', 'Default Channel', 'website', 'en', 0, '{}');
|
|
192
|
+
INSERT OR IGNORE INTO trees (id, key, label, treeType, channelId, isHidden, dataJson) VALUES (1, 'default-tree', 'Default Navigation', 'navigation', 1, 0, '{}');
|
|
193
|
+
INSERT OR IGNORE INTO channel_trees (channelId, treeId, key) VALUES (1, 1, 'main');
|