@brightsideoy/kama 1.0.0-alpha.13 → 1.0.0-alpha.16
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 +282 -158
- package/dist/assets/AssetEditor-CEHxvQj0.js +1 -0
- package/dist/assets/AssetEditor-D5j7pwcK.css +1 -0
- package/dist/assets/CmsHelp-BO_vWABq.css +1 -0
- package/dist/assets/CmsHelp-Cnr801lx.js +1 -0
- package/dist/assets/CmsPublisher-C3fRoBte.js +3 -0
- package/dist/assets/CmsPublisher-CugEqTD8.css +1 -0
- package/dist/assets/CmsUserCenter-Ac0GywlR.css +1 -0
- package/dist/assets/CmsUserCenter-DiuXfjMc.js +1 -0
- package/dist/assets/EditorSplitPreview-BzKh4wFB.css +1 -0
- package/dist/assets/EditorSplitPreview-tpZZF1o5.js +1 -0
- package/dist/assets/NodeEditor-Bd89OptF.css +1 -0
- package/dist/assets/NodeEditor-DaCIVPGT.js +147 -0
- package/dist/assets/TrashWorkspace-CTq0OFA-.js +1 -0
- package/dist/assets/TrashWorkspace-DtMgxg13.css +1 -0
- package/dist/assets/_plugin-vue_export-helper-CTn6xl4w.js +5 -0
- package/dist/assets/history-XvWNE5zO.js +1 -0
- package/dist/assets/index-BbV9FaG-.css +1 -0
- package/dist/assets/index-CHczMewv.js +14 -0
- package/dist/assets/layers-D0DLWjff.js +1 -0
- package/dist/assets/link-Bs_SUS0O.js +1 -0
- package/dist/assets/rotate-ccw-CvD7yNQj.js +1 -0
- package/dist/assets/save-C8e-QRkP.js +1 -0
- package/dist/assets/smartphone-Wy6xQi_i.js +1 -0
- package/dist/{dist-kIJlDMvm.js → dist-Ca4GRlV3.js} +619 -535
- package/dist/index.html +3 -2
- package/dist/index.js +619 -535
- package/dist/schema-compiler.js +1 -1
- package/package.json +1 -1
- package/schema.sql +3 -0
- package/templates/astro-starter/src/components/blocks/NodeList.astro +59 -63
- package/templates/astro-starter/src/middleware.ts +63 -35
- package/templates/astro-starter/src/pages/[...slug].astro +5 -12
- package/templates/astro-starter/src/pages/api/load-nodes.ts +42 -14
- package/templates/astro-starter/src/pages/api/partials/node-cards.astro +101 -0
- package/templates/kama-worker/setup-local-db.mjs +17 -0
- package/dist/assets/index-B1ogCRCg.js +0 -164
- package/dist/assets/index-CGLx9oqF.css +0 -1
package/bin/cli.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, cpSync, unlinkSync, appendFileSync } from 'node:fs';
|
|
4
4
|
import { join, dirname, basename, resolve } from 'node:path';
|
|
5
|
-
import { fileURLToPath
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import crypto from 'node:crypto'; // 🎯 NEW: Cryptographically secure token generator
|
|
7
7
|
|
|
8
8
|
// 🎯 Modern CLI UI Components
|
|
9
|
-
import { intro, outro, text, select, confirm, spinner, note, isCancel, cancel } from '@clack/prompts';
|
|
9
|
+
import { intro, outro, text, password, select, confirm, spinner, note, isCancel, cancel } from '@clack/prompts';
|
|
10
10
|
|
|
11
11
|
// --- UI HELPERS (Zero Dependency Colors) ---
|
|
12
12
|
const pc = {
|
|
@@ -106,31 +106,29 @@ function getWranglerCmd() {
|
|
|
106
106
|
function runWrangler(cmd, options = {}) {
|
|
107
107
|
const baseWrangler = getWranglerCmd();
|
|
108
108
|
try {
|
|
109
|
-
|
|
109
|
+
const result = execSync(`${baseWrangler} ${cmd}`, {
|
|
110
110
|
encoding: 'utf-8',
|
|
111
|
-
stdio: options.stdio ||
|
|
111
|
+
stdio: options.stdio || 'pipe',
|
|
112
|
+
input: options.input,
|
|
113
|
+
cwd: options.cwd || process.cwd(),
|
|
112
114
|
env: {
|
|
113
115
|
...process.env,
|
|
114
116
|
PATH: process.env.PATH
|
|
115
117
|
}
|
|
116
118
|
});
|
|
119
|
+
return typeof result === 'string' ? result : '';
|
|
117
120
|
} catch (e) {
|
|
118
|
-
if (options.throw) throw e;
|
|
121
|
+
if (options.throw) throw new Error(`Wrangler command failed: ${cmd}\n${e.stderr || e.message}`);
|
|
119
122
|
return null;
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
function getOrCreateKV(title) {
|
|
127
|
-
const out = runWrangler(`kv namespace create "${title}"`);
|
|
128
|
-
if (out) {
|
|
129
|
-
const match = out.match(/"id":\s*"([a-fA-F0-9]{32})"/);
|
|
130
|
-
if (match) return match[1];
|
|
131
|
-
}
|
|
126
|
+
function shellQuote(value) {
|
|
127
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
128
|
+
}
|
|
132
129
|
|
|
133
|
-
|
|
130
|
+
function getOrCreateKV(title, cwd = process.cwd()) {
|
|
131
|
+
const listOut = runWrangler('kv namespace list', { cwd });
|
|
134
132
|
if (listOut) {
|
|
135
133
|
try {
|
|
136
134
|
const namespaces = JSON.parse(listOut);
|
|
@@ -139,7 +137,136 @@ function getOrCreateKV(title) {
|
|
|
139
137
|
} catch (err) { }
|
|
140
138
|
}
|
|
141
139
|
|
|
142
|
-
|
|
140
|
+
const createOut = runWrangler(`kv namespace create ${shellQuote(title)}`, { cwd, throw: true });
|
|
141
|
+
const match = createOut.match(/"id":\s*"([a-fA-F0-9]{32})"/);
|
|
142
|
+
if (match) return match[1];
|
|
143
|
+
|
|
144
|
+
throw new Error(`Unable to create or find Cloudflare KV namespace "${title}". Check Wrangler authentication and permissions.`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getOrCreateD1(name, cwd = process.cwd()) {
|
|
148
|
+
const listOutput = runWrangler('d1 list --json', { cwd, throw: true });
|
|
149
|
+
let databases;
|
|
150
|
+
try {
|
|
151
|
+
databases = JSON.parse(listOutput);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error('Wrangler returned invalid JSON while listing D1 databases.');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const existing = databases.find(database => database.name === name);
|
|
157
|
+
if (existing?.uuid) return existing.uuid;
|
|
158
|
+
|
|
159
|
+
const createOutput = runWrangler(`d1 create ${shellQuote(name)}`, { cwd, throw: true });
|
|
160
|
+
const created = createOutput.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}/);
|
|
161
|
+
if (created) return created[0];
|
|
162
|
+
|
|
163
|
+
throw new Error(`Cloudflare D1 database "${name}" was not created or returned a valid ID.`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function ensureR2Bucket(name, cwd = process.cwd()) {
|
|
167
|
+
if (runWrangler(`r2 bucket info ${shellQuote(name)} --json`, { cwd })) return;
|
|
168
|
+
|
|
169
|
+
const createOutput = runWrangler(`r2 bucket create ${shellQuote(name)}`, { cwd });
|
|
170
|
+
if (createOutput || runWrangler(`r2 bucket info ${shellQuote(name)} --json`, { cwd })) return;
|
|
171
|
+
|
|
172
|
+
throw new Error(`Unable to create or find Cloudflare R2 bucket "${name}". Check Wrangler authentication and permissions.`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function ensureCloudflareAuth(cwd) {
|
|
176
|
+
if (runWrangler('whoami', { cwd })) return;
|
|
177
|
+
|
|
178
|
+
console.log(pc.dim('Launching browser for Cloudflare login...'));
|
|
179
|
+
runWrangler('login', { cwd, stdio: 'inherit', throw: true });
|
|
180
|
+
if (!runWrangler('whoami', { cwd })) {
|
|
181
|
+
throw new Error('Cloudflare authentication could not be verified after login.');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function writeWorkerSecrets(workerDir, secret, cloudflareApiToken = '') {
|
|
186
|
+
const values = [`FRONTEND_API_SECRET=${secret}`];
|
|
187
|
+
if (cloudflareApiToken) values.push(`CLOUDFLARE_API_TOKEN=${cloudflareApiToken}`);
|
|
188
|
+
writeFileSync(join(workerDir, '.secrets'), `${values.join('\n')}\n`, { mode: 0o600 });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function readEnvValue(filePath, key) {
|
|
192
|
+
if (!existsSync(filePath)) return '';
|
|
193
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
194
|
+
const match = readFileSync(filePath, 'utf8').match(new RegExp(`^${escapedKey}=["']?([^"'\\r\\n]*)`, 'm'));
|
|
195
|
+
return match?.[1]?.trim() || '';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function resolveCloudflareZoneId(domain, token) {
|
|
199
|
+
if (!token) return '';
|
|
200
|
+
|
|
201
|
+
const labels = domain.split('.');
|
|
202
|
+
for (let index = 0; index < labels.length - 1; index++) {
|
|
203
|
+
const zoneName = labels.slice(index).join('.');
|
|
204
|
+
const response = await fetch(`https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(zoneName)}&per_page=50`, {
|
|
205
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
206
|
+
});
|
|
207
|
+
const data = await response.json();
|
|
208
|
+
if (!response.ok || !data.success) {
|
|
209
|
+
throw new Error(`Could not look up Cloudflare zone for ${domain}. Check the token's Zone Read permission.`);
|
|
210
|
+
}
|
|
211
|
+
const zone = data.result?.find(item => item.name === zoneName && item.status === 'active');
|
|
212
|
+
if (zone?.id) return zone.id;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
throw new Error(`No active Cloudflare zone was found for ${domain}. Add the domain to Cloudflare or enter its Zone ID manually.`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function promptForCloudflareApiToken(existingToken = '') {
|
|
219
|
+
const token = handleCancel(await password({
|
|
220
|
+
message: `Optional Cloudflare API token (Zone Read + Zone Cache Purge${existingToken ? '; leave blank to keep existing token' : ''}):`,
|
|
221
|
+
mask: '*'
|
|
222
|
+
}));
|
|
223
|
+
return token.trim() || existingToken;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function normalizeCloudflareDomain(value, label) {
|
|
227
|
+
const input = String(value || '').trim();
|
|
228
|
+
if (!input) throw new Error(`${label} is required for Cloudflare deployment.`);
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const url = new URL(input.includes('://') ? input : `https://${input}`);
|
|
232
|
+
if (!url.hostname.includes('.') || url.hostname === 'localhost') throw new Error();
|
|
233
|
+
return url.host.toLowerCase();
|
|
234
|
+
} catch {
|
|
235
|
+
throw new Error(`${label} must be a valid Cloudflare domain, for example example.com.`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function ensureGitIgnoreEntries(rootDir, entries) {
|
|
240
|
+
const gitignorePath = join(rootDir, '.gitignore');
|
|
241
|
+
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, 'utf8') : '';
|
|
242
|
+
const lines = new Set(existing.split(/\r?\n/));
|
|
243
|
+
const missing = entries.filter(line => !lines.has(line));
|
|
244
|
+
if (missing.length > 0) appendFileSync(gitignorePath, `${existing && !existing.endsWith('\n') ? '\n' : ''}${missing.join('\n')}\n`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function ignoreSecretFiles(rootDir) {
|
|
248
|
+
ensureGitIgnoreEntries(rootDir, ['kama-worker/.secrets', 'kama-worker/.dev.vars', 'sites/*/.secrets', 'sites/*/.dev.vars']);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function updateEnvFile(filePath, values, removeKeys = []) {
|
|
252
|
+
const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';
|
|
253
|
+
const keys = new Set(Object.keys(values));
|
|
254
|
+
const found = new Set();
|
|
255
|
+
const lines = existing.split(/\r?\n/).filter(line => {
|
|
256
|
+
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=/);
|
|
257
|
+
return !match || !removeKeys.includes(match[1]);
|
|
258
|
+
}).map(line => {
|
|
259
|
+
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=/);
|
|
260
|
+
if (!match || !keys.has(match[1])) return line;
|
|
261
|
+
found.add(match[1]);
|
|
262
|
+
return `${match[1]}=${JSON.stringify(values[match[1]])}`;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
for (const key of keys) {
|
|
266
|
+
if (!found.has(key)) lines.push(`${key}=${JSON.stringify(values[key])}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
writeFileSync(filePath, `${lines.filter((line, index) => line || index < lines.length - 1).join('\n')}\n`);
|
|
143
270
|
}
|
|
144
271
|
|
|
145
272
|
function showHelp() {
|
|
@@ -156,7 +283,7 @@ function showHelp() {
|
|
|
156
283
|
console.log(' npm run setup Initialize database schema (Run Once)');
|
|
157
284
|
console.log(' npm run seed -w kama-worker Seed the database with mock test data');
|
|
158
285
|
console.log(' npm run dev Boot up the CMS backend and Astro frontend concurrently');
|
|
159
|
-
console.log(' npm run deploy:
|
|
286
|
+
console.log(' npm run deploy:kama Deploy the backend worker and database to Cloudflare');
|
|
160
287
|
console.log(' npm run deploy:site Deploy the Astro frontend site to Cloudflare\n');
|
|
161
288
|
}
|
|
162
289
|
|
|
@@ -278,8 +405,14 @@ async function createFullWorkspace() {
|
|
|
278
405
|
defaultValue: ''
|
|
279
406
|
}));
|
|
280
407
|
}
|
|
408
|
+
|
|
409
|
+
cmsDomain = normalizeCloudflareDomain(cmsDomain, 'CMS Admin Domain');
|
|
410
|
+
if (shouldCreateSite) siteDomain = normalizeCloudflareDomain(siteDomain, 'Frontend Website Domain');
|
|
281
411
|
}
|
|
282
412
|
|
|
413
|
+
const cloudflareApiToken = isCloudflare ? await promptForCloudflareApiToken() : '';
|
|
414
|
+
const cloudflareZoneId = isCloudflare ? await resolveCloudflareZoneId(cmsDomain, cloudflareApiToken) : '';
|
|
415
|
+
|
|
283
416
|
const cmsProtocol = isCloudflare ? 'https://' : 'http://';
|
|
284
417
|
const cmsFullUrl = cmsDomain ? `${cmsProtocol}${cmsDomain.replace(/^https?:\/\//, '')}` : 'http://localhost:8787';
|
|
285
418
|
|
|
@@ -295,82 +428,42 @@ async function createFullWorkspace() {
|
|
|
295
428
|
|
|
296
429
|
if (!isCurrentDir) mkdirSync(rootDir, { recursive: true });
|
|
297
430
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
431
|
+
ensureGitIgnoreEntries(rootDir, [
|
|
432
|
+
'node_modules/', '.env', '.DS_Store', 'kama-worker/.dev.vars', 'kama-worker/.secrets',
|
|
433
|
+
'kama-worker/.env', 'kama-worker/.wrangler/', 'kama-worker/public/', 'kama-worker/*.db',
|
|
434
|
+
'sites/*/.env', 'sites/*/.secrets', 'sites/*/.astro/', 'sites/*/dist/',
|
|
435
|
+
'kama-worker/bundled-definitions.js'
|
|
436
|
+
]);
|
|
302
437
|
|
|
303
438
|
const sourceDefsDir = join(__dirname, '../content/definitions');
|
|
304
439
|
const targetDefsDir = join(rootDir, 'content', 'definitions');
|
|
305
440
|
if (existsSync(sourceDefsDir)) cpSync(sourceDefsDir, targetDefsDir, { recursive: true });
|
|
306
441
|
|
|
307
442
|
let d1DatabaseId = '00000000-0000-0000-0000-000000000000';
|
|
308
|
-
let backendCacheId = '
|
|
309
|
-
let isAuthenticated = false;
|
|
310
|
-
|
|
443
|
+
let backendCacheId = '';
|
|
311
444
|
if (isCloudflare) {
|
|
312
|
-
s.message('
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
console.log(pc.dim('Launching browser for Cloudflare login...'));
|
|
319
|
-
const login = runWrangler('login', { stdio: 'inherit' });
|
|
320
|
-
if (login !== null) isAuthenticated = true;
|
|
321
|
-
s.start('Resuming provisioning...');
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (isAuthenticated) {
|
|
325
|
-
s.message('Provisioning backend KV Cache...');
|
|
326
|
-
backendCacheId = getOrCreateKV(`CACHE_KV_${workerName.toUpperCase().replace(/-/g, '_')}`);
|
|
327
|
-
|
|
328
|
-
s.message('Checking existing Cloudflare D1 databases...');
|
|
329
|
-
const listOutput = runWrangler('d1 list --json');
|
|
330
|
-
if (listOutput) {
|
|
331
|
-
try {
|
|
332
|
-
const jsonStart = listOutput.indexOf('[');
|
|
333
|
-
const jsonEnd = listOutput.lastIndexOf(']') + 1;
|
|
334
|
-
let databases = [];
|
|
335
|
-
|
|
336
|
-
if (jsonStart !== -1 && jsonEnd !== -1) databases = JSON.parse(listOutput.substring(jsonStart, jsonEnd));
|
|
337
|
-
const existingDb = databases.find(db => db.name === dbName);
|
|
338
|
-
|
|
339
|
-
if (existingDb) {
|
|
340
|
-
s.stop(`Database '${dbName}' already exists on your account.`);
|
|
341
|
-
const useExisting = handleCancel(await confirm({ message: 'Do you want to link this existing database?', initialValue: true }));
|
|
342
|
-
|
|
343
|
-
if (useExisting) {
|
|
344
|
-
d1DatabaseId = existingDb.uuid;
|
|
345
|
-
s.start(`Linked existing database ID: ${d1DatabaseId}`);
|
|
346
|
-
} else {
|
|
347
|
-
dbName = handleCancel(await text({ message: 'Enter a new database name:' }));
|
|
348
|
-
s.start(`Creating new database '${dbName}'...`);
|
|
349
|
-
const d1Output = runWrangler(`d1 create ${dbName}`);
|
|
350
|
-
const match = d1Output ? 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}/) : null;
|
|
351
|
-
if (match) d1DatabaseId = match[0];
|
|
352
|
-
}
|
|
353
|
-
} else {
|
|
354
|
-
s.message(`Attempting to auto-create Cloudflare D1 Database '${dbName}'...`);
|
|
355
|
-
const d1Output = runWrangler(`d1 create ${dbName}`);
|
|
356
|
-
const match = d1Output ? 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}/) : null;
|
|
357
|
-
if (match) d1DatabaseId = match[0];
|
|
358
|
-
}
|
|
359
|
-
} catch (error) { }
|
|
360
|
-
}
|
|
361
|
-
}
|
|
445
|
+
s.message('Authenticating with Cloudflare...');
|
|
446
|
+
ensureCloudflareAuth(process.cwd());
|
|
447
|
+
s.message('Provisioning D1, KV, and R2 resources...');
|
|
448
|
+
backendCacheId = getOrCreateKV(`CACHE_KV_${workerName.toUpperCase().replace(/-/g, '_')}`, rootDir);
|
|
449
|
+
d1DatabaseId = getOrCreateD1(dbName, rootDir);
|
|
450
|
+
ensureR2Bucket(bucketName, rootDir);
|
|
362
451
|
}
|
|
363
452
|
|
|
364
453
|
s.message('Writing configuration files...');
|
|
365
454
|
|
|
455
|
+
const rootPkgPath = join(rootDir, 'package.json');
|
|
456
|
+
const existingRootPkg = existsSync(rootPkgPath) ? JSON.parse(readFileSync(rootPkgPath, 'utf-8')) : {};
|
|
457
|
+
const existingWorkspaces = Array.isArray(existingRootPkg.workspaces) ? existingRootPkg.workspaces : [];
|
|
366
458
|
const rootPkg = {
|
|
367
|
-
|
|
459
|
+
...existingRootPkg,
|
|
460
|
+
name: existingRootPkg.name || projName,
|
|
368
461
|
private: true,
|
|
369
|
-
type: 'module',
|
|
370
|
-
workspaces: ['kama-worker', 'sites/*'],
|
|
371
|
-
scripts: { 'setup': 'npm run setup:local -w kama-worker' },
|
|
372
|
-
dependencies: { '@brightsideoy/kama': currentKamaVersion },
|
|
373
|
-
devDependencies: {}
|
|
462
|
+
type: existingRootPkg.type || 'module',
|
|
463
|
+
workspaces: [...new Set([...existingWorkspaces, 'kama-worker', 'sites/*'])],
|
|
464
|
+
scripts: { ...existingRootPkg.scripts, 'setup': 'npx kama bundle && npm run setup:local -w kama-worker' },
|
|
465
|
+
dependencies: { ...existingRootPkg.dependencies, '@brightsideoy/kama': currentKamaVersion },
|
|
466
|
+
devDependencies: { ...existingRootPkg.devDependencies }
|
|
374
467
|
};
|
|
375
468
|
|
|
376
469
|
if (isCloudflare) rootPkg.devDependencies['wrangler'] = WRANGLER_LATEST;
|
|
@@ -381,14 +474,14 @@ async function createFullWorkspace() {
|
|
|
381
474
|
rootPkg.scripts['predev'] = 'npx kama bundle';
|
|
382
475
|
rootPkg.scripts['dev'] = 'npx concurrently "npm run dev -w kama-worker" "npm run dev -w sites/' + siteFolderName + '"';
|
|
383
476
|
rootPkg.scripts['deploy:site'] = `npm run deploy -w sites/${siteFolderName}`;
|
|
384
|
-
rootPkg.scripts['deploy:
|
|
477
|
+
rootPkg.scripts['deploy:kama'] = `npm run deploy -w kama-worker`;
|
|
385
478
|
} else {
|
|
386
479
|
rootPkg.scripts['predev'] = 'npx kama bundle';
|
|
387
480
|
rootPkg.scripts['dev'] = 'npm run dev -w kama-worker';
|
|
388
|
-
rootPkg.scripts['deploy:
|
|
481
|
+
rootPkg.scripts['deploy:kama'] = `npm run deploy -w kama-worker`;
|
|
389
482
|
}
|
|
390
483
|
|
|
391
|
-
writeFileSync(
|
|
484
|
+
writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2));
|
|
392
485
|
|
|
393
486
|
const cmsDir = join(rootDir, 'kama-worker');
|
|
394
487
|
mkdirSync(cmsDir, { recursive: true });
|
|
@@ -410,10 +503,10 @@ async function createFullWorkspace() {
|
|
|
410
503
|
"seed": "npm run seed:local",
|
|
411
504
|
"seed:local": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute " + dbName + " --local --file=\"$f\"; done",
|
|
412
505
|
"seed:remote": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && for f in seed-*.sql; do wrangler d1 execute " + dbName + " --remote --file=\"$f\"; done",
|
|
413
|
-
"setup:local": `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql
|
|
506
|
+
"setup:local": `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql`,
|
|
414
507
|
"setup:remote": `wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`,
|
|
415
508
|
"dev": "wrangler dev --port 8787 --inspector-port 9239",
|
|
416
|
-
"deploy": "npm run setup:remote && wrangler deploy"
|
|
509
|
+
"deploy": "npm run setup:remote && wrangler deploy --secrets-file .secrets"
|
|
417
510
|
},
|
|
418
511
|
dependencies: { '@brightsideoy/kama': currentKamaVersion, '@libsql/client': '^0.14.0' },
|
|
419
512
|
devDependencies: { "wrangler": WRANGLER_LATEST },
|
|
@@ -433,7 +526,7 @@ async function createFullWorkspace() {
|
|
|
433
526
|
vars: {
|
|
434
527
|
CDN_DOMAIN: cmsDomain || 'localhost:8787',
|
|
435
528
|
PUBLIC_FRONTEND_URL: (shouldCreateSite) ? (siteDomain && siteDomain !== 'localhost:4321' ? `https://${siteDomain}` : 'http://localhost:4321') : 'http://localhost:4321',
|
|
436
|
-
CLOUDFLARE_ZONE_ID:
|
|
529
|
+
CLOUDFLARE_ZONE_ID: cloudflareZoneId,
|
|
437
530
|
KAMA_RUNTIME: 'cloudflare'
|
|
438
531
|
},
|
|
439
532
|
d1_databases: [{ binding: 'DB', database_name: dbName, database_id: d1DatabaseId }],
|
|
@@ -444,7 +537,10 @@ async function createFullWorkspace() {
|
|
|
444
537
|
writeFileSync(join(cmsDir, 'wrangler.json'), JSON.stringify(cmsWranglerConfig, null, 2));
|
|
445
538
|
|
|
446
539
|
const publicFrontendUrl = (shouldCreateSite) ? (siteDomain && siteDomain !== 'localhost:4321' ? `https://${siteDomain}` : 'http://localhost:4321') : 'http://localhost:4321';
|
|
447
|
-
|
|
540
|
+
const devVars = [`# Local Development Overrides`, `CDN_DOMAIN=`, `PUBLIC_FRONTEND_URL=${publicFrontendUrl}`, ``, `# Server-to-Server API Security`, `FRONTEND_API_SECRET="${frontendApiSecret}"`];
|
|
541
|
+
if (cloudflareApiToken) devVars.push(`CLOUDFLARE_API_TOKEN="${cloudflareApiToken}"`);
|
|
542
|
+
writeFileSync(join(cmsDir, '.dev.vars'), `${devVars.join('\n')}\n`, { mode: 0o600 });
|
|
543
|
+
writeWorkerSecrets(cmsDir, frontendApiSecret, cloudflareApiToken);
|
|
448
544
|
} else {
|
|
449
545
|
const templateServerPath = join(__dirname, '../templates/kama-worker/server.js');
|
|
450
546
|
if (existsSync(templateServerPath)) cpSync(templateServerPath, join(cmsDir, 'server.js'));
|
|
@@ -457,8 +553,8 @@ async function createFullWorkspace() {
|
|
|
457
553
|
version: '1.0.0',
|
|
458
554
|
scripts: {
|
|
459
555
|
"seed": "npm run seed:local",
|
|
460
|
-
"seed:local": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js &&
|
|
461
|
-
"setup:local": "
|
|
556
|
+
"seed:local": "node ../node_modules/@brightsideoy/kama/dist/build-seed.js && node setup-local-db.mjs seed",
|
|
557
|
+
"setup:local": "node setup-local-db.mjs schema",
|
|
462
558
|
"dev": "tsx server.js",
|
|
463
559
|
"start": "node server.js"
|
|
464
560
|
},
|
|
@@ -468,6 +564,7 @@ async function createFullWorkspace() {
|
|
|
468
564
|
);
|
|
469
565
|
|
|
470
566
|
writeFileSync(join(cmsDir, '.env'), `PORT=8787\nKAMA_RUNTIME=node\nDB_PATH=./kama.db\nLOCAL_MEDIA_PATH=./public/media\nPUBLIC_CMS_URL=http://127.0.0.1:8787\nPUBLIC_API_URL=http://127.0.0.1:8787\nPUBLIC_FRONTEND_URL=http://127.0.0.1:4321\n\n# Server-to-Server API Security\nFRONTEND_API_SECRET="${frontendApiSecret}"\n`);
|
|
567
|
+
cpSync(join(__dirname, '../templates/kama-worker/setup-local-db.mjs'), join(cmsDir, 'setup-local-db.mjs'));
|
|
471
568
|
}
|
|
472
569
|
|
|
473
570
|
s.message('Bundling schemas...');
|
|
@@ -478,20 +575,23 @@ async function createFullWorkspace() {
|
|
|
478
575
|
await generateAstroSite(rootDir, siteFolderName, siteDomain, cmsFullUrl, workerName, isCloudflare, frontendApiSecret);
|
|
479
576
|
}
|
|
480
577
|
|
|
481
|
-
s.
|
|
578
|
+
s.message('Installing workspace dependencies...');
|
|
579
|
+
execSync('npm install', { cwd: rootDir, stdio: 'inherit' });
|
|
580
|
+
s.message('Bundling schemas and initializing the local database...');
|
|
581
|
+
execSync('npm run setup', { cwd: rootDir, stdio: 'inherit' });
|
|
582
|
+
|
|
583
|
+
s.stop('Workspace installed and initialized successfully.');
|
|
482
584
|
|
|
483
585
|
let nextSteps = '';
|
|
484
586
|
let stepCount = 1;
|
|
485
587
|
if (!isCurrentDir) nextSteps += `${stepCount++}. cd ${projName}\n`;
|
|
486
|
-
nextSteps += `${stepCount++}. npm install\n`;
|
|
487
|
-
nextSteps += `${stepCount++}. npm run setup ${pc.dim('<- Run this once to init the database schema!')}\n`;
|
|
488
588
|
nextSteps += `${stepCount++}. npm run dev\n`;
|
|
489
589
|
|
|
490
590
|
if (isCloudflare) {
|
|
491
591
|
nextSteps += `\n${pc.dim('Cloudflare Note:')}\n`;
|
|
492
|
-
nextSteps +=
|
|
493
|
-
|
|
494
|
-
|
|
592
|
+
nextSteps += cloudflareApiToken
|
|
593
|
+
? `${pc.dim('Zone ID was discovered automatically; the purge token is stored as an ignored Worker secret.')}`
|
|
594
|
+
: `${pc.dim('Zone-level cache purging is disabled. Run kama promote with a token to enable it.')}`;
|
|
495
595
|
}
|
|
496
596
|
|
|
497
597
|
note(nextSteps, 'Next steps');
|
|
@@ -544,22 +644,29 @@ async function createNewSiteOnly() {
|
|
|
544
644
|
|
|
545
645
|
if (isCloudflare) {
|
|
546
646
|
siteDomain = handleCancel(await text({ message: 'Website Domain (e.g., brand.com):', defaultValue: '' }));
|
|
647
|
+
siteDomain = normalizeCloudflareDomain(siteDomain, 'Frontend Website Domain');
|
|
547
648
|
if (detectedCmsDomain) {
|
|
548
649
|
cmsFullUrl = detectedCmsDomain.startsWith('http') ? detectedCmsDomain : `https://${detectedCmsDomain.replace(/^https?:\/\//, '')}`;
|
|
549
650
|
} else {
|
|
550
651
|
const cmsDomainInput = handleCancel(await text({ message: 'CMS API Domain (e.g., admin.brand.com):', defaultValue: '' }));
|
|
551
|
-
cmsFullUrl = `https://${cmsDomainInput
|
|
652
|
+
cmsFullUrl = `https://${normalizeCloudflareDomain(cmsDomainInput, 'CMS API Domain')}`;
|
|
552
653
|
}
|
|
654
|
+
ensureCloudflareAuth(rootDir);
|
|
553
655
|
}
|
|
554
656
|
|
|
657
|
+
ignoreSecretFiles(rootDir);
|
|
658
|
+
|
|
555
659
|
const s = spinner();
|
|
556
660
|
s.start(`Generating Astro site...`);
|
|
557
661
|
|
|
558
662
|
await generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, targetWorkerName, isCloudflare, frontendApiSecret);
|
|
663
|
+
ignoreSecretFiles(rootDir);
|
|
664
|
+
s.message('Installing workspace dependencies...');
|
|
665
|
+
execSync('npm install', { cwd: rootDir, stdio: 'inherit' });
|
|
559
666
|
|
|
560
667
|
s.stop(`Site created in sites/${siteFolder}!`);
|
|
561
668
|
|
|
562
|
-
note(`
|
|
669
|
+
note('Run `npm run dev` from the workspace root to start the new site.', 'Next steps');
|
|
563
670
|
outro('Site added successfully! 🚀');
|
|
564
671
|
}
|
|
565
672
|
|
|
@@ -575,10 +682,11 @@ async function bundleWorkspaceSchemas(targetRootDir, silent = false) {
|
|
|
575
682
|
try {
|
|
576
683
|
let compilerPath = join(__dirname, '../dist/schema-compiler.js');
|
|
577
684
|
if (!existsSync(compilerPath)) compilerPath = join(__dirname, '../scripts/schema-compiler.ts');
|
|
578
|
-
|
|
685
|
+
execSync(`${shellQuote(process.execPath)} ${shellQuote(compilerPath)}`, { cwd: rootDir, stdio: 'inherit' });
|
|
579
686
|
if (!silent) s.stop('Schemas bundled successfully.');
|
|
580
687
|
} catch (e) {
|
|
581
688
|
if (!silent) s.stop(`Schema compilation failed: ${e.message}`, true);
|
|
689
|
+
else throw e;
|
|
582
690
|
}
|
|
583
691
|
}
|
|
584
692
|
|
|
@@ -599,21 +707,16 @@ async function promoteLocalToCloudflare() {
|
|
|
599
707
|
|
|
600
708
|
const s = spinner();
|
|
601
709
|
s.start('Checking Cloudflare authentication...');
|
|
710
|
+
ensureCloudflareAuth(rootDir);
|
|
711
|
+
s.stop('Cloudflare authentication verified.');
|
|
602
712
|
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (login === null) {
|
|
611
|
-
cancel('Cloudflare login failed.');
|
|
612
|
-
process.exit(1);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const cmsDomain = handleCancel(await text({ message: 'CMS Admin Domain (e.g., admin.yourdomain.com):', defaultValue: '' }));
|
|
713
|
+
const cmsDomain = normalizeCloudflareDomain(
|
|
714
|
+
handleCancel(await text({ message: 'CMS Admin Domain (e.g., admin.yourdomain.com):', defaultValue: '' })),
|
|
715
|
+
'CMS Admin Domain'
|
|
716
|
+
);
|
|
717
|
+
const existingCloudflareToken = readEnvValue(join(workerDir, '.dev.vars'), 'CLOUDFLARE_API_TOKEN') || readEnvValue(join(workerDir, '.secrets'), 'CLOUDFLARE_API_TOKEN');
|
|
718
|
+
const cloudflareApiToken = await promptForCloudflareApiToken(existingCloudflareToken);
|
|
719
|
+
const cloudflareZoneId = await resolveCloudflareZoneId(cmsDomain, cloudflareApiToken);
|
|
617
720
|
|
|
618
721
|
const sitesFolderDir = join(rootDir, 'sites');
|
|
619
722
|
let siteDomain = '';
|
|
@@ -622,48 +725,24 @@ async function promoteLocalToCloudflare() {
|
|
|
622
725
|
const sites = readdirSync(sitesFolderDir);
|
|
623
726
|
if (sites.length > 0) {
|
|
624
727
|
siteDir = join(sitesFolderDir, sites[0]);
|
|
625
|
-
siteDomain =
|
|
728
|
+
siteDomain = normalizeCloudflareDomain(
|
|
729
|
+
handleCancel(await text({ message: `Frontend Website Domain for ${sites[0]}:`, defaultValue: '' })),
|
|
730
|
+
'Frontend Website Domain'
|
|
731
|
+
);
|
|
626
732
|
}
|
|
627
733
|
}
|
|
628
734
|
|
|
735
|
+
ignoreSecretFiles(rootDir);
|
|
736
|
+
|
|
629
737
|
s.start('Upgrading workspace to Cloudflare Edge...');
|
|
630
738
|
|
|
631
739
|
const projName = basename(rootDir);
|
|
632
|
-
let dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
633
|
-
|
|
634
|
-
// 🎯 1. ARCHITECTURE FIX: Check if the database already exists
|
|
635
|
-
let d1DatabaseId = '00000000-0000-0000-0000-000000000000';
|
|
636
|
-
const listOutput = runWrangler('d1 list --json');
|
|
637
|
-
let dbExists = false;
|
|
638
|
-
|
|
639
|
-
if (listOutput) {
|
|
640
|
-
try {
|
|
641
|
-
const jsonStart = listOutput.indexOf('[');
|
|
642
|
-
const jsonEnd = listOutput.lastIndexOf(']') + 1;
|
|
643
|
-
if (jsonStart !== -1 && jsonEnd !== -1) {
|
|
644
|
-
const databases = JSON.parse(listOutput.substring(jsonStart, jsonEnd));
|
|
645
|
-
const existingDb = databases.find(db => db.name === dbName);
|
|
646
|
-
if (existingDb) {
|
|
647
|
-
d1DatabaseId = existingDb.uuid;
|
|
648
|
-
dbExists = true;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
} catch (e) { }
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// 2. If it doesn't exist, create it safely
|
|
655
|
-
if (!dbExists) {
|
|
656
|
-
const d1Output = runWrangler(`d1 create ${dbName}`);
|
|
657
|
-
if (d1Output) {
|
|
658
|
-
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}/);
|
|
659
|
-
if (match) d1DatabaseId = match[0];
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
|
|
663
740
|
const workerName = `${projName}-worker`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
664
741
|
const bucketName = `${projName}-media-prod`.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
665
|
-
|
|
666
|
-
const
|
|
742
|
+
const dbName = `${projName}-db`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
743
|
+
const d1DatabaseId = getOrCreateD1(dbName, rootDir);
|
|
744
|
+
const backendCacheId = getOrCreateKV(`CACHE_KV_${workerName.toUpperCase().replace(/-/g, '_')}`, rootDir);
|
|
745
|
+
ensureR2Bucket(bucketName, rootDir);
|
|
667
746
|
|
|
668
747
|
const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
|
|
669
748
|
rootPkg.devDependencies = rootPkg.devDependencies || {};
|
|
@@ -679,10 +758,21 @@ async function promoteLocalToCloudflare() {
|
|
|
679
758
|
compatibility_flags: ['nodejs_compat'],
|
|
680
759
|
assets: { directory: '../node_modules/@brightsideoy/kama/dist', binding: 'ASSETS' },
|
|
681
760
|
images: { binding: 'IMAGES' },
|
|
682
|
-
vars: { CLOUDFLARE_ZONE_ID:
|
|
761
|
+
vars: { CLOUDFLARE_ZONE_ID: '', KAMA_RUNTIME: 'cloudflare' }
|
|
683
762
|
};
|
|
684
763
|
|
|
764
|
+
const cloudflareEntryPath = join(workerDir, 'index.js');
|
|
765
|
+
if (!existsSync(cloudflareEntryPath)) {
|
|
766
|
+
cpSync(join(__dirname, '../templates/kama-worker/index.js'), cloudflareEntryPath);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
currentConfig.main = 'index.js';
|
|
685
770
|
currentConfig.dev = currentConfig.dev || { host: 'localhost', port: 8787 };
|
|
771
|
+
currentConfig.vars = currentConfig.vars || {};
|
|
772
|
+
if (String(currentConfig.vars.CLOUDFLARE_ZONE_ID || '').startsWith('REPLACE_WITH_')) {
|
|
773
|
+
currentConfig.vars.CLOUDFLARE_ZONE_ID = '';
|
|
774
|
+
}
|
|
775
|
+
currentConfig.vars.CLOUDFLARE_ZONE_ID = cloudflareZoneId || currentConfig.vars.CLOUDFLARE_ZONE_ID || '';
|
|
686
776
|
currentConfig.vars.CDN_DOMAIN = cmsDomain;
|
|
687
777
|
if (siteDomain) currentConfig.vars.PUBLIC_FRONTEND_URL = `https://${siteDomain}`;
|
|
688
778
|
currentConfig.routes = [{ pattern: cmsDomain, custom_domain: true }];
|
|
@@ -692,15 +782,19 @@ async function promoteLocalToCloudflare() {
|
|
|
692
782
|
|
|
693
783
|
writeFileSync(workerWranglerPath, JSON.stringify(currentConfig, null, 2));
|
|
694
784
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
785
|
+
updateEnvFile(join(workerDir, '.dev.vars'), {
|
|
786
|
+
CDN_DOMAIN: '',
|
|
787
|
+
PUBLIC_FRONTEND_URL: 'http://localhost:4321',
|
|
788
|
+
FRONTEND_API_SECRET: frontendApiSecret,
|
|
789
|
+
...(cloudflareApiToken ? { CLOUDFLARE_API_TOKEN: cloudflareApiToken } : {})
|
|
790
|
+
});
|
|
791
|
+
writeWorkerSecrets(workerDir, frontendApiSecret, cloudflareApiToken);
|
|
698
792
|
|
|
699
793
|
const workerPkgPath = join(workerDir, 'package.json');
|
|
700
794
|
if (existsSync(workerPkgPath)) {
|
|
701
795
|
const workerPkg = JSON.parse(readFileSync(workerPkgPath, 'utf-8'));
|
|
702
796
|
workerPkg.scripts.dev = "wrangler dev --port 8787 --inspector-port 9239";
|
|
703
|
-
workerPkg.scripts.deploy = "npm run setup:remote && wrangler deploy";
|
|
797
|
+
workerPkg.scripts.deploy = "npm run setup:remote && wrangler deploy --secrets-file .secrets";
|
|
704
798
|
workerPkg.scripts["setup:local"] = `wrangler d1 execute ${dbName} --local --file=../node_modules/@brightsideoy/kama/schema.sql`;
|
|
705
799
|
workerPkg.scripts["setup:remote"] = `wrangler d1 execute ${dbName} --remote --file=../node_modules/@brightsideoy/kama/schema.sql`;
|
|
706
800
|
|
|
@@ -718,8 +812,8 @@ async function promoteLocalToCloudflare() {
|
|
|
718
812
|
|
|
719
813
|
if (siteDir && siteDomain) {
|
|
720
814
|
const prefix = basename(siteDir).replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
|
|
721
|
-
const sessionId = getOrCreateKV(`SESSION_${prefix}
|
|
722
|
-
const htmlCacheId = getOrCreateKV(`HTML_CACHE_${prefix}
|
|
815
|
+
const sessionId = getOrCreateKV(`SESSION_${prefix}`, rootDir);
|
|
816
|
+
const htmlCacheId = getOrCreateKV(`HTML_CACHE_${prefix}`, rootDir);
|
|
723
817
|
|
|
724
818
|
const siteWranglerPath = join(siteDir, 'wrangler.json');
|
|
725
819
|
const siteConfig = {
|
|
@@ -747,14 +841,35 @@ async function promoteLocalToCloudflare() {
|
|
|
747
841
|
sitePkg.dependencies['@astrojs/cloudflare'] = ASTRO_CF_LATEST;
|
|
748
842
|
sitePkg.devDependencies = sitePkg.devDependencies || {};
|
|
749
843
|
sitePkg.devDependencies['wrangler'] = WRANGLER_LATEST;
|
|
844
|
+
sitePkg.scripts = sitePkg.scripts || {};
|
|
845
|
+
sitePkg.scripts.deploy = 'astro build && wrangler deploy --secrets-file .secrets';
|
|
750
846
|
writeFileSync(sitePkgPath, JSON.stringify(sitePkg, null, 2));
|
|
751
847
|
}
|
|
752
848
|
|
|
849
|
+
writeWorkerSecrets(siteDir, frontendApiSecret);
|
|
850
|
+
updateEnvFile(join(siteDir, '.env'), {
|
|
851
|
+
PUBLIC_CMS_URL: `https://${cmsDomain}`,
|
|
852
|
+
PUBLIC_API_URL: `https://${cmsDomain}`
|
|
853
|
+
}, ['FRONTEND_API_SECRET']);
|
|
854
|
+
updateEnvFile(join(siteDir, '.dev.vars'), { FRONTEND_API_SECRET: frontendApiSecret });
|
|
753
855
|
convertAstroConfigToCloudflare(siteDir);
|
|
754
856
|
}
|
|
755
857
|
|
|
756
|
-
s.
|
|
757
|
-
|
|
858
|
+
s.message('Installing Cloudflare dependencies...');
|
|
859
|
+
execSync('npm install', { cwd: rootDir, stdio: 'inherit' });
|
|
860
|
+
s.message('Bundling workspace schemas...');
|
|
861
|
+
await bundleWorkspaceSchemas(rootDir, true);
|
|
862
|
+
s.message('Deploying CMS and initializing the remote database...');
|
|
863
|
+
execSync('npm run deploy -w kama-worker', { cwd: rootDir, stdio: 'inherit' });
|
|
864
|
+
|
|
865
|
+
if (siteDir && siteDomain) {
|
|
866
|
+
const siteFolder = basename(siteDir);
|
|
867
|
+
s.message(`Building and deploying ${siteFolder}...`);
|
|
868
|
+
execSync(`npm run deploy -w sites/${shellQuote(siteFolder)}`, { cwd: rootDir, stdio: 'inherit' });
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
s.stop('Workspace provisioned and deployed to Cloudflare.');
|
|
872
|
+
outro('Promotion complete.');
|
|
758
873
|
}
|
|
759
874
|
|
|
760
875
|
/**
|
|
@@ -792,6 +907,7 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
|
|
|
792
907
|
if (sitePkg.dependencies['wrangler']) delete sitePkg.dependencies['wrangler'];
|
|
793
908
|
sitePkg.devDependencies = sitePkg.devDependencies || {};
|
|
794
909
|
sitePkg.devDependencies['wrangler'] = WRANGLER_LATEST;
|
|
910
|
+
sitePkg.scripts.deploy = 'astro build && wrangler deploy --secrets-file .secrets';
|
|
795
911
|
convertAstroConfigToCloudflare(siteDir);
|
|
796
912
|
}
|
|
797
913
|
|
|
@@ -825,15 +941,23 @@ async function generateAstroSite(rootDir, siteFolder, siteDomain, cmsFullUrl, wo
|
|
|
825
941
|
|
|
826
942
|
if (siteDomain !== 'localhost:4321') siteConfig.routes = [{ pattern: siteDomain, custom_domain: true, enabled: true, previews_enabled: false }];
|
|
827
943
|
writeFileSync(join(siteDir, 'wrangler.json'), JSON.stringify(siteConfig, null, 2));
|
|
944
|
+
writeWorkerSecrets(siteDir, frontendApiSecret);
|
|
828
945
|
}
|
|
829
946
|
|
|
830
|
-
|
|
947
|
+
const localCmsUrl = isCloudflare ? cmsFullUrl : 'http://127.0.0.1:8787';
|
|
948
|
+
let envContent = `# Local Development Overrides\nPUBLIC_CMS_URL="${localCmsUrl}"\nPUBLIC_API_URL="${localCmsUrl}"\nPUBLIC_CHANNEL_ID="${channelKey}"\nPUBLIC_TREE_ID="${treeKey}"\nPUBLIC_SITE_NAME="${siteLabel.replace(/"/g, '\\"')}"\n`;
|
|
831
949
|
|
|
832
|
-
if (frontendApiSecret) {
|
|
950
|
+
if (frontendApiSecret && !isCloudflare) {
|
|
833
951
|
envContent += `\n# Server-to-Server API Security\nFRONTEND_API_SECRET="${frontendApiSecret}"\n`;
|
|
834
952
|
}
|
|
835
953
|
|
|
836
954
|
writeFileSync(join(siteDir, '.env'), envContent);
|
|
955
|
+
if (isCloudflare && frontendApiSecret) {
|
|
956
|
+
writeFileSync(join(siteDir, '.dev.vars'), `FRONTEND_API_SECRET="${frontendApiSecret}"\n`, { mode: 0o600 });
|
|
957
|
+
}
|
|
837
958
|
}
|
|
838
959
|
|
|
839
|
-
main().catch(
|
|
960
|
+
main().catch(error => {
|
|
961
|
+
console.error(error);
|
|
962
|
+
process.exitCode = 1;
|
|
963
|
+
});
|