@tamyla/clodo-framework 3.0.10 → 3.0.12
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/CHANGELOG.md +16 -0
- package/bin/shared/cloudflare/ops.js +4 -4
- package/bin/shared/database/connection-manager.js +1 -1
- package/bin/shared/database/orchestrator.js +1 -1
- package/bin/shared/deployment/validator.js +1 -1
- package/bin/shared/monitoring/health-checker.js +1 -1
- package/bin/shared/security/secret-generator.js +1 -1
- package/bin/shared/security/secure-token-manager.js +1 -1
- package/dist/deployment/validator.js +1 -1
- package/dist/orchestration/multi-domain-orchestrator.js +65 -18
- package/dist/service-management/InputCollector.js +19 -2
- package/dist/shared/cloudflare/ops.js +4 -4
- package/dist/shared/database/connection-manager.js +1 -1
- package/dist/shared/database/orchestrator.js +1 -1
- package/dist/shared/deployment/validator.js +1 -1
- package/dist/shared/monitoring/health-checker.js +1 -1
- package/dist/shared/security/secret-generator.js +1 -1
- package/dist/shared/security/secure-token-manager.js +1 -1
- package/dist/utils/cloudflare/api.js +41 -1
- package/dist/utils/deployment/wrangler-config-manager.js +32 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [3.0.12](https://github.com/tamylaa/clodo-framework/compare/v3.0.11...v3.0.12) (2025-10-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add graceful API token permission handling and validation ([6c973b0](https://github.com/tamylaa/clodo-framework/commit/6c973b077b6e2a80b7a6d93f0b39070925bb89af))
|
|
7
|
+
* Add missing exists() method to WranglerConfigManager class ([44ee17c](https://github.com/tamylaa/clodo-framework/commit/44ee17c8931db085ccef502e7e7ac15209b222a5))
|
|
8
|
+
* Ensure wrangler uses correct account for API token operations ([f671b10](https://github.com/tamylaa/clodo-framework/commit/f671b1004057b94dd8ba55c5c1f3c2d5bca54706))
|
|
9
|
+
|
|
10
|
+
## [3.0.11](https://github.com/tamylaa/clodo-framework/compare/v3.0.10...v3.0.11) (2025-10-14)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* Update all bin/ imports from src/ to dist/ for published package compatibility ([c476528](https://github.com/tamylaa/clodo-framework/commit/c476528b575cf9d6338a967e740252ed4d41f66f))
|
|
16
|
+
|
|
1
17
|
## [3.0.10](https://github.com/tamylaa/clodo-framework/compare/v3.0.9...v3.0.10) (2025-10-14)
|
|
2
18
|
|
|
3
19
|
|
|
@@ -232,7 +232,7 @@ export async function listDatabases(options = {}) {
|
|
|
232
232
|
|
|
233
233
|
// Use API-based operation if credentials provided
|
|
234
234
|
if (apiToken && accountId) {
|
|
235
|
-
const { CloudflareAPI } = await import('../../../
|
|
235
|
+
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
236
236
|
const cf = new CloudflareAPI(apiToken);
|
|
237
237
|
return await cf.listD1Databases(accountId);
|
|
238
238
|
}
|
|
@@ -251,7 +251,7 @@ export async function databaseExists(databaseName, options = {}) {
|
|
|
251
251
|
|
|
252
252
|
// Use API-based operation if credentials provided
|
|
253
253
|
if (apiToken && accountId) {
|
|
254
|
-
const { CloudflareAPI } = await import('../../../
|
|
254
|
+
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
255
255
|
const cf = new CloudflareAPI(apiToken);
|
|
256
256
|
return await cf.d1DatabaseExists(accountId, databaseName);
|
|
257
257
|
}
|
|
@@ -270,7 +270,7 @@ export async function createDatabase(name, options = {}) {
|
|
|
270
270
|
|
|
271
271
|
// Use API-based operation if credentials provided
|
|
272
272
|
if (apiToken && accountId) {
|
|
273
|
-
const { CloudflareAPI } = await import('../../../
|
|
273
|
+
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
274
274
|
const cf = new CloudflareAPI(apiToken);
|
|
275
275
|
const result = await cf.createD1Database(accountId, name);
|
|
276
276
|
return result.uuid; // Return UUID to match CLI behavior
|
|
@@ -355,7 +355,7 @@ export async function getDatabaseId(databaseName, options = {}) {
|
|
|
355
355
|
|
|
356
356
|
// Use API-based operation if credentials provided
|
|
357
357
|
if (apiToken && accountId) {
|
|
358
|
-
const { CloudflareAPI } = await import('../../../
|
|
358
|
+
const { CloudflareAPI } = await import('../../../dist/utils/cloudflare/api.js');
|
|
359
359
|
const cf = new CloudflareAPI(apiToken);
|
|
360
360
|
const db = await cf.getD1Database(accountId, databaseName);
|
|
361
361
|
return db?.uuid || null;
|
|
@@ -20,7 +20,7 @@ export class DatabaseConnectionManager {
|
|
|
20
20
|
*/
|
|
21
21
|
async initialize() {
|
|
22
22
|
// Import framework config for consistent database connection settings
|
|
23
|
-
const { frameworkConfig } = await import('../../../
|
|
23
|
+
const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
|
|
24
24
|
const timing = frameworkConfig.getTiming();
|
|
25
25
|
const database = frameworkConfig.getDatabaseConfig();
|
|
26
26
|
|
|
@@ -115,7 +115,7 @@ export class DatabaseOrchestrator {
|
|
|
115
115
|
*/
|
|
116
116
|
async initialize() {
|
|
117
117
|
// Import framework config for consistent timing and database settings
|
|
118
|
-
const { frameworkConfig } = await import('../../../
|
|
118
|
+
const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
|
|
119
119
|
const timing = frameworkConfig.getTiming();
|
|
120
120
|
const database = frameworkConfig.getDatabaseConfig();
|
|
121
121
|
|
|
@@ -556,7 +556,7 @@ export class DeploymentValidator {
|
|
|
556
556
|
|
|
557
557
|
try {
|
|
558
558
|
// Import WranglerDeployer for D1 validation capabilities
|
|
559
|
-
const { WranglerDeployer } = await import('../../../
|
|
559
|
+
const { WranglerDeployer } = await import('../../../dist/deployment/wrangler-deployer.js');
|
|
560
560
|
|
|
561
561
|
// Check if this is a framework-level validation (no specific service)
|
|
562
562
|
if (!this.options?.servicePath) {
|
|
@@ -13,7 +13,7 @@ import http from 'http';
|
|
|
13
13
|
const execAsync = promisify(exec);
|
|
14
14
|
|
|
15
15
|
// Load framework configuration
|
|
16
|
-
const { frameworkConfig } = await import('../../../
|
|
16
|
+
const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
|
|
17
17
|
const timing = frameworkConfig.getTiming();
|
|
18
18
|
|
|
19
19
|
function makeHttpRequest(url, method = 'GET', timeout = 5000) {
|
|
@@ -65,7 +65,7 @@ export class EnhancedSecretManager {
|
|
|
65
65
|
*/
|
|
66
66
|
async initialize() {
|
|
67
67
|
// Import framework config for consistent timing and retry settings
|
|
68
|
-
const { frameworkConfig } = await import('../../../
|
|
68
|
+
const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
|
|
69
69
|
const timing = frameworkConfig.getTiming();
|
|
70
70
|
const security = frameworkConfig.getSecurity();
|
|
71
71
|
const configPaths = frameworkConfig.getPaths();
|
|
@@ -34,7 +34,7 @@ export class SecureTokenManager {
|
|
|
34
34
|
async initialize() {
|
|
35
35
|
try {
|
|
36
36
|
// Load framework configuration
|
|
37
|
-
const { frameworkConfig } = await import('../../../
|
|
37
|
+
const { frameworkConfig } = await import('../../../dist/utils/framework-config.js');
|
|
38
38
|
this.frameworkConfig = frameworkConfig;
|
|
39
39
|
|
|
40
40
|
// Update paths with framework config
|
|
@@ -470,7 +470,7 @@ export class DeploymentValidator {
|
|
|
470
470
|
// Import WranglerDeployer for D1 validation capabilities
|
|
471
471
|
const {
|
|
472
472
|
WranglerDeployer
|
|
473
|
-
} = await import('../../../
|
|
473
|
+
} = await import('../../../dist/deployment/wrangler-deployer.js');
|
|
474
474
|
|
|
475
475
|
// Check if this is a framework-level validation (no specific service)
|
|
476
476
|
if (!this.options?.servicePath) {
|
|
@@ -39,6 +39,13 @@ export class MultiDomainOrchestrator {
|
|
|
39
39
|
this.cloudflareToken = options.cloudflareToken;
|
|
40
40
|
this.cloudflareAccountId = options.cloudflareAccountId;
|
|
41
41
|
|
|
42
|
+
// Configure wrangler to use API token when available
|
|
43
|
+
// This ensures all wrangler operations use the same account as API operations
|
|
44
|
+
if (this.cloudflareToken) {
|
|
45
|
+
process.env.CLOUDFLARE_API_TOKEN = this.cloudflareToken;
|
|
46
|
+
console.log(`🔑 Configured wrangler to use API token authentication`);
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
// Initialize modular components
|
|
43
50
|
this.domainResolver = new DomainResolver({
|
|
44
51
|
environment: this.environment,
|
|
@@ -74,7 +81,8 @@ export class MultiDomainOrchestrator {
|
|
|
74
81
|
this.wranglerConfigManager = new WranglerConfigManager({
|
|
75
82
|
projectRoot: this.servicePath,
|
|
76
83
|
dryRun: this.dryRun,
|
|
77
|
-
verbose: options.verbose || false
|
|
84
|
+
verbose: options.verbose || false,
|
|
85
|
+
accountId: this.cloudflareAccountId
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
// ConfigurationValidator is a static class - don't instantiate
|
|
@@ -283,26 +291,60 @@ export class MultiDomainOrchestrator {
|
|
|
283
291
|
// Use API-based operations if credentials are available
|
|
284
292
|
if (this.cloudflareToken && this.cloudflareAccountId) {
|
|
285
293
|
console.log(` 🔑 Using API token authentication for account: ${this.cloudflareAccountId}`);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
accountId: this.cloudflareAccountId
|
|
289
|
-
});
|
|
290
|
-
if (exists) {
|
|
291
|
-
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
292
|
-
databaseId = await getDatabaseId(databaseName, {
|
|
293
|
-
apiToken: this.cloudflareToken,
|
|
294
|
-
accountId: this.cloudflareAccountId
|
|
295
|
-
});
|
|
296
|
-
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
297
|
-
} else {
|
|
298
|
-
console.log(` 📦 Creating database: ${databaseName}`);
|
|
299
|
-
databaseId = await createDatabase(databaseName, {
|
|
294
|
+
try {
|
|
295
|
+
exists = await databaseExists(databaseName, {
|
|
300
296
|
apiToken: this.cloudflareToken,
|
|
301
297
|
accountId: this.cloudflareAccountId
|
|
302
298
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
299
|
+
if (exists) {
|
|
300
|
+
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
301
|
+
databaseId = await getDatabaseId(databaseName, {
|
|
302
|
+
apiToken: this.cloudflareToken,
|
|
303
|
+
accountId: this.cloudflareAccountId
|
|
304
|
+
});
|
|
305
|
+
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
306
|
+
} else {
|
|
307
|
+
console.log(` 📦 Creating database: ${databaseName}`);
|
|
308
|
+
databaseId = await createDatabase(databaseName, {
|
|
309
|
+
apiToken: this.cloudflareToken,
|
|
310
|
+
accountId: this.cloudflareAccountId
|
|
311
|
+
});
|
|
312
|
+
console.log(` ✅ Database created: ${databaseName}`);
|
|
313
|
+
console.log(` 📊 Database ID: ${databaseId}`);
|
|
314
|
+
created = true;
|
|
315
|
+
}
|
|
316
|
+
} catch (apiError) {
|
|
317
|
+
// Check if this is an authentication or permission error
|
|
318
|
+
if (apiError.message.includes('permission denied') || apiError.message.includes('403') || apiError.message.includes('authentication failed') || apiError.message.includes('401')) {
|
|
319
|
+
if (apiError.message.includes('401')) {
|
|
320
|
+
console.log(` ❌ API token authentication failed (invalid/expired token)`);
|
|
321
|
+
console.log(` 🔗 Check/create token at: https://dash.cloudflare.com/profile/api-tokens`);
|
|
322
|
+
} else {
|
|
323
|
+
console.log(` ⚠️ API token lacks D1 database permissions`);
|
|
324
|
+
console.log(` 💡 Required permission: 'Cloudflare D1:Edit'`);
|
|
325
|
+
console.log(` 🔗 Update token at: https://dash.cloudflare.com/profile/api-tokens`);
|
|
326
|
+
}
|
|
327
|
+
console.log(` 🔄 Falling back to OAuth authentication...`);
|
|
328
|
+
console.log(` ⚠️ WARNING: OAuth uses your personal account, not the API token account!`);
|
|
329
|
+
|
|
330
|
+
// Fall back to OAuth-based operations with warning
|
|
331
|
+
console.log(` 🔐 Using OAuth authentication (wrangler CLI)`);
|
|
332
|
+
exists = await databaseExists(databaseName);
|
|
333
|
+
if (exists) {
|
|
334
|
+
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
335
|
+
databaseId = await getDatabaseId(databaseName);
|
|
336
|
+
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
337
|
+
} else {
|
|
338
|
+
console.log(` 📦 Creating database: ${databaseName}`);
|
|
339
|
+
databaseId = await createDatabase(databaseName);
|
|
340
|
+
console.log(` ✅ Database created: ${databaseName}`);
|
|
341
|
+
console.log(` 📊 Database ID: ${databaseId}`);
|
|
342
|
+
created = true;
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
// Re-throw non-auth/permission errors
|
|
346
|
+
throw apiError;
|
|
347
|
+
}
|
|
306
348
|
}
|
|
307
349
|
} else {
|
|
308
350
|
// Fallback to CLI-based operations (OAuth)
|
|
@@ -333,6 +375,11 @@ export class MultiDomainOrchestrator {
|
|
|
333
375
|
console.log(` 📁 Service path: ${this.servicePath}`);
|
|
334
376
|
console.log(` 📁 Current working directory: ${process.cwd()}`);
|
|
335
377
|
try {
|
|
378
|
+
// Set account_id if API credentials are available
|
|
379
|
+
if (this.cloudflareAccountId) {
|
|
380
|
+
await this.wranglerConfigManager.setAccountId(this.cloudflareAccountId);
|
|
381
|
+
}
|
|
382
|
+
|
|
336
383
|
// Ensure environment section exists
|
|
337
384
|
await this.wranglerConfigManager.ensureEnvironment(this.environment);
|
|
338
385
|
|
|
@@ -386,8 +386,25 @@ export class InputCollector {
|
|
|
386
386
|
CloudflareAPI
|
|
387
387
|
} = await import('../utils/cloudflare/api.js');
|
|
388
388
|
const cfApi = new CloudflareAPI(token);
|
|
389
|
-
const
|
|
390
|
-
if (
|
|
389
|
+
const tokenCheck = await cfApi.verifyToken();
|
|
390
|
+
if (tokenCheck.valid) {
|
|
391
|
+
// Check D1 permissions
|
|
392
|
+
const permissionCheck = await cfApi.checkD1Permissions();
|
|
393
|
+
if (!permissionCheck.hasPermission) {
|
|
394
|
+
console.log(chalk.yellow(`⚠️ ${permissionCheck.error}`));
|
|
395
|
+
console.log(chalk.white(' 💡 You can update permissions at: https://dash.cloudflare.com/profile/api-tokens'));
|
|
396
|
+
console.log(chalk.white(' 💡 Or continue and the framework will fall back to OAuth authentication'));
|
|
397
|
+
console.log('');
|
|
398
|
+
const {
|
|
399
|
+
askYesNo
|
|
400
|
+
} = await import('../utils/interactive-prompts.js');
|
|
401
|
+
const continueAnyway = await askYesNo('Continue with limited API token permissions?', false);
|
|
402
|
+
if (!continueAnyway) {
|
|
403
|
+
console.log(chalk.blue('Please update your API token permissions and try again.'));
|
|
404
|
+
process.exit(0);
|
|
405
|
+
}
|
|
406
|
+
console.log(chalk.yellow('⚠️ Proceeding with limited permissions - database operations will use OAuth'));
|
|
407
|
+
}
|
|
391
408
|
console.log(chalk.green('✓ API token verified successfully'));
|
|
392
409
|
return token;
|
|
393
410
|
}
|
|
@@ -240,7 +240,7 @@ export async function listDatabases(options = {}) {
|
|
|
240
240
|
if (apiToken && accountId) {
|
|
241
241
|
const {
|
|
242
242
|
CloudflareAPI
|
|
243
|
-
} = await import('../../../
|
|
243
|
+
} = await import('../../../dist/utils/cloudflare/api.js');
|
|
244
244
|
const cf = new CloudflareAPI(apiToken);
|
|
245
245
|
return await cf.listD1Databases(accountId);
|
|
246
246
|
}
|
|
@@ -265,7 +265,7 @@ export async function databaseExists(databaseName, options = {}) {
|
|
|
265
265
|
if (apiToken && accountId) {
|
|
266
266
|
const {
|
|
267
267
|
CloudflareAPI
|
|
268
|
-
} = await import('../../../
|
|
268
|
+
} = await import('../../../dist/utils/cloudflare/api.js');
|
|
269
269
|
const cf = new CloudflareAPI(apiToken);
|
|
270
270
|
return await cf.d1DatabaseExists(accountId, databaseName);
|
|
271
271
|
}
|
|
@@ -288,7 +288,7 @@ export async function createDatabase(name, options = {}) {
|
|
|
288
288
|
if (apiToken && accountId) {
|
|
289
289
|
const {
|
|
290
290
|
CloudflareAPI
|
|
291
|
-
} = await import('../../../
|
|
291
|
+
} = await import('../../../dist/utils/cloudflare/api.js');
|
|
292
292
|
const cf = new CloudflareAPI(apiToken);
|
|
293
293
|
const result = await cf.createD1Database(accountId, name);
|
|
294
294
|
return result.uuid; // Return UUID to match CLI behavior
|
|
@@ -403,7 +403,7 @@ export async function getDatabaseId(databaseName, options = {}) {
|
|
|
403
403
|
if (apiToken && accountId) {
|
|
404
404
|
const {
|
|
405
405
|
CloudflareAPI
|
|
406
|
-
} = await import('../../../
|
|
406
|
+
} = await import('../../../dist/utils/cloudflare/api.js');
|
|
407
407
|
const cf = new CloudflareAPI(apiToken);
|
|
408
408
|
const db = await cf.getD1Database(accountId, databaseName);
|
|
409
409
|
return db?.uuid || null;
|
|
@@ -21,7 +21,7 @@ export class DatabaseConnectionManager {
|
|
|
21
21
|
// Import framework config for consistent database connection settings
|
|
22
22
|
const {
|
|
23
23
|
frameworkConfig
|
|
24
|
-
} = await import('../../../
|
|
24
|
+
} = await import('../../../dist/utils/framework-config.js');
|
|
25
25
|
const timing = frameworkConfig.getTiming();
|
|
26
26
|
const database = frameworkConfig.getDatabaseConfig();
|
|
27
27
|
this.config = {
|
|
@@ -110,7 +110,7 @@ export class DatabaseOrchestrator {
|
|
|
110
110
|
// Import framework config for consistent timing and database settings
|
|
111
111
|
const {
|
|
112
112
|
frameworkConfig
|
|
113
|
-
} = await import('../../../
|
|
113
|
+
} = await import('../../../dist/utils/framework-config.js');
|
|
114
114
|
const timing = frameworkConfig.getTiming();
|
|
115
115
|
const database = frameworkConfig.getDatabaseConfig();
|
|
116
116
|
this.config = {
|
|
@@ -470,7 +470,7 @@ export class DeploymentValidator {
|
|
|
470
470
|
// Import WranglerDeployer for D1 validation capabilities
|
|
471
471
|
const {
|
|
472
472
|
WranglerDeployer
|
|
473
|
-
} = await import('../../../
|
|
473
|
+
} = await import('../../../dist/deployment/wrangler-deployer.js');
|
|
474
474
|
|
|
475
475
|
// Check if this is a framework-level validation (no specific service)
|
|
476
476
|
if (!this.options?.servicePath) {
|
|
@@ -14,7 +14,7 @@ const execAsync = promisify(exec);
|
|
|
14
14
|
// Load framework configuration
|
|
15
15
|
const {
|
|
16
16
|
frameworkConfig
|
|
17
|
-
} = await import('../../../
|
|
17
|
+
} = await import('../../../dist/utils/framework-config.js');
|
|
18
18
|
const timing = frameworkConfig.getTiming();
|
|
19
19
|
function makeHttpRequest(url, method = 'GET', timeout = 5000) {
|
|
20
20
|
return new Promise((resolve, reject) => {
|
|
@@ -105,7 +105,7 @@ export class EnhancedSecretManager {
|
|
|
105
105
|
// Import framework config for consistent timing and retry settings
|
|
106
106
|
const {
|
|
107
107
|
frameworkConfig
|
|
108
|
-
} = await import('../../../
|
|
108
|
+
} = await import('../../../dist/utils/framework-config.js');
|
|
109
109
|
const timing = frameworkConfig.getTiming();
|
|
110
110
|
const security = frameworkConfig.getSecurity();
|
|
111
111
|
const configPaths = frameworkConfig.getPaths();
|
|
@@ -36,7 +36,7 @@ export class SecureTokenManager {
|
|
|
36
36
|
// Load framework configuration
|
|
37
37
|
const {
|
|
38
38
|
frameworkConfig
|
|
39
|
-
} = await import('../../../
|
|
39
|
+
} = await import('../../../dist/utils/framework-config.js');
|
|
40
40
|
this.frameworkConfig = frameworkConfig;
|
|
41
41
|
|
|
42
42
|
// Update paths with framework config
|
|
@@ -34,7 +34,20 @@ export class CloudflareAPI {
|
|
|
34
34
|
const data = await response.json();
|
|
35
35
|
if (!response.ok) {
|
|
36
36
|
const errorMsg = data.errors?.[0]?.message || 'Unknown error';
|
|
37
|
-
|
|
37
|
+
const statusCode = response.status;
|
|
38
|
+
|
|
39
|
+
// Provide specific guidance for common authentication/permission errors
|
|
40
|
+
if (statusCode === 401) {
|
|
41
|
+
throw new Error(`Cloudflare API authentication failed (401). Your API token may be invalid or expired. Please check your token at https://dash.cloudflare.com/profile/api-tokens`);
|
|
42
|
+
}
|
|
43
|
+
if (statusCode === 403) {
|
|
44
|
+
// Check if this is a D1-related endpoint to provide specific guidance
|
|
45
|
+
if (endpoint.includes('/d1/')) {
|
|
46
|
+
throw new Error(`Cloudflare API permission denied (403). Your API token lacks D1 database permissions. Required permissions: 'Cloudflare D1:Edit'. Update your token at https://dash.cloudflare.com/profile/api-tokens`);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Cloudflare API permission denied (403). Your API token lacks required permissions for this operation. Please check your token permissions at https://dash.cloudflare.com/profile/api-tokens`);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`Cloudflare API error: ${errorMsg} (${statusCode})`);
|
|
38
51
|
}
|
|
39
52
|
if (!data.success) {
|
|
40
53
|
const errorMsg = data.errors?.[0]?.message || 'Request failed';
|
|
@@ -65,6 +78,33 @@ export class CloudflareAPI {
|
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Check if API token has D1 database permissions
|
|
83
|
+
* @returns {Promise<Object>} Permission check result
|
|
84
|
+
*/
|
|
85
|
+
async checkD1Permissions() {
|
|
86
|
+
try {
|
|
87
|
+
// Try to list D1 databases - this will fail if no D1 permissions
|
|
88
|
+
// We use a dummy account ID that should fail safely if permissions are missing
|
|
89
|
+
await this.request('/accounts/dummy/d1/database');
|
|
90
|
+
return {
|
|
91
|
+
hasPermission: true
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error.message.includes('403') || error.message.includes('permission denied')) {
|
|
95
|
+
return {
|
|
96
|
+
hasPermission: false,
|
|
97
|
+
error: 'API token lacks D1 database permissions. Required: Cloudflare D1:Edit'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// If it's a different error (like invalid account), assume permissions are OK
|
|
101
|
+
// The actual permission check happens during real operations
|
|
102
|
+
return {
|
|
103
|
+
hasPermission: true
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
68
108
|
/**
|
|
69
109
|
* List all zones (domains) accessible with this API token
|
|
70
110
|
* @param {Object} options - Query options
|
|
@@ -19,11 +19,13 @@ export class WranglerConfigManager {
|
|
|
19
19
|
this.projectRoot = dirname(options);
|
|
20
20
|
this.dryRun = false;
|
|
21
21
|
this.verbose = false;
|
|
22
|
+
this.accountId = null;
|
|
22
23
|
} else {
|
|
23
24
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
24
25
|
this.configPath = options.configPath || join(this.projectRoot, 'wrangler.toml');
|
|
25
26
|
this.dryRun = options.dryRun || false;
|
|
26
27
|
this.verbose = options.verbose || false;
|
|
28
|
+
this.accountId = options.accountId || null;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -52,6 +54,19 @@ export class WranglerConfigManager {
|
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Check if wrangler.toml file exists
|
|
59
|
+
* @returns {Promise<boolean>} True if file exists, false otherwise
|
|
60
|
+
*/
|
|
61
|
+
async exists() {
|
|
62
|
+
try {
|
|
63
|
+
await access(this.configPath, constants.F_OK);
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
/**
|
|
56
71
|
* Write configuration back to wrangler.toml
|
|
57
72
|
* @param {Object} config - Configuration object to write
|
|
@@ -82,16 +97,26 @@ export class WranglerConfigManager {
|
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @
|
|
100
|
+
* Set account_id in wrangler.toml
|
|
101
|
+
* @param {string} accountId - Cloudflare account ID
|
|
102
|
+
* @returns {Promise<boolean>} True if account_id was set
|
|
87
103
|
*/
|
|
88
|
-
async
|
|
89
|
-
|
|
90
|
-
await access(this.configPath, constants.F_OK);
|
|
91
|
-
return true;
|
|
92
|
-
} catch {
|
|
104
|
+
async setAccountId(accountId) {
|
|
105
|
+
if (!accountId) {
|
|
93
106
|
return false;
|
|
94
107
|
}
|
|
108
|
+
const config = await this.readConfig();
|
|
109
|
+
if (config.account_id === accountId) {
|
|
110
|
+
if (this.verbose) {
|
|
111
|
+
console.log(` ✓ account_id already set to ${accountId}`);
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
console.log(` 📝 Setting account_id to ${accountId} in wrangler.toml`);
|
|
116
|
+
config.account_id = accountId;
|
|
117
|
+
await this.writeConfig(config);
|
|
118
|
+
console.log(` ✅ account_id updated in wrangler.toml`);
|
|
119
|
+
return true;
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
/**
|