@tamyla/clodo-framework 2.0.20 ā 3.0.3
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 +74 -0
- package/bin/clodo-service.js +1 -1
- package/bin/database/README.md +33 -0
- package/bin/database/deployment-db-manager.js +527 -0
- package/bin/database/enterprise-db-manager.js +736 -0
- package/bin/database/wrangler-d1-manager.js +775 -0
- package/bin/shared/cloudflare/domain-discovery.js +636 -0
- package/bin/shared/cloudflare/domain-manager.js +952 -0
- package/bin/shared/cloudflare/index.js +8 -0
- package/bin/shared/cloudflare/ops.js +359 -0
- package/bin/shared/config/index.js +1 -1
- package/bin/shared/database/connection-manager.js +374 -0
- package/bin/shared/database/index.js +7 -0
- package/bin/shared/database/orchestrator.js +726 -0
- package/bin/shared/deployment/auditor.js +969 -0
- package/bin/shared/deployment/index.js +10 -0
- package/bin/shared/deployment/rollback-manager.js +570 -0
- package/bin/shared/deployment/validator.js +779 -0
- package/bin/shared/index.js +32 -0
- package/bin/shared/monitoring/health-checker.js +484 -0
- package/bin/shared/monitoring/index.js +8 -0
- package/bin/shared/monitoring/memory-manager.js +387 -0
- package/bin/shared/monitoring/production-monitor.js +391 -0
- package/bin/shared/production-tester/api-tester.js +82 -0
- package/bin/shared/production-tester/auth-tester.js +132 -0
- package/bin/shared/production-tester/core.js +197 -0
- package/bin/shared/production-tester/database-tester.js +109 -0
- package/bin/shared/production-tester/index.js +77 -0
- package/bin/shared/production-tester/load-tester.js +131 -0
- package/bin/shared/production-tester/performance-tester.js +103 -0
- package/bin/shared/security/api-token-manager.js +312 -0
- package/bin/shared/security/index.js +8 -0
- package/bin/shared/security/secret-generator.js +937 -0
- package/bin/shared/security/secure-token-manager.js +398 -0
- package/bin/shared/utils/error-recovery.js +225 -0
- package/bin/shared/utils/graceful-shutdown-manager.js +390 -0
- package/bin/shared/utils/index.js +9 -0
- package/bin/shared/utils/interactive-prompts.js +146 -0
- package/bin/shared/utils/interactive-utils.js +530 -0
- package/bin/shared/utils/rate-limiter.js +246 -0
- package/dist/database/database-orchestrator.js +34 -12
- package/dist/deployment/index.js +2 -2
- package/dist/orchestration/multi-domain-orchestrator.js +26 -10
- package/dist/service-management/GenerationEngine.js +76 -28
- package/dist/service-management/ServiceInitializer.js +5 -3
- package/dist/shared/cloudflare/domain-manager.js +1 -1
- package/dist/shared/cloudflare/ops.js +27 -12
- package/dist/shared/config/index.js +1 -1
- package/dist/shared/deployment/index.js +2 -2
- package/dist/shared/security/secret-generator.js +4 -2
- package/dist/shared/utils/error-recovery.js +1 -1
- package/dist/shared/utils/graceful-shutdown-manager.js +4 -3
- package/dist/utils/deployment/secret-generator.js +19 -6
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
+
## [3.0.3](https://github.com/tamylaa/clodo-framework/compare/v3.0.2...v3.0.3) (2025-10-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* resolve database creation redundancy in deployments ([4a54f2c](https://github.com/tamylaa/clodo-framework/commit/4a54f2ce4bd1bb17d49c4d911171a01179fbd519))
|
|
7
|
+
|
|
8
|
+
## [3.0.2](https://github.com/tamylaa/clodo-framework/compare/v3.0.1...v3.0.2) (2025-10-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* suppress secret audit logging in test/CI environments ([daa58e0](https://github.com/tamylaa/clodo-framework/commit/daa58e013f8c3c37bb251a658b78f011a56dab3f))
|
|
14
|
+
|
|
15
|
+
## [3.0.1](https://github.com/tamylaa/clodo-framework/compare/v3.0.0...v3.0.1) (2025-10-14)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* reset version to 2.0.20 for clean semantic release ([724df16](https://github.com/tamylaa/clodo-framework/commit/724df162d3fdb4a35cfc50a2cc045a714c56ba6f))
|
|
21
|
+
|
|
22
|
+
# [3.0.0](https://github.com/tamylaa/clodo-framework/compare/v2.0.20...v3.0.0) (2025-10-14)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* include all bin/shared files and correct imports to use dist/ ([268b525](https://github.com/tamylaa/clodo-framework/commit/268b5254d269951e78d481840a9a0fbba486c879)), closes [#deploy-v3](https://github.com/tamylaa/clodo-framework/issues/deploy-v3)
|
|
28
|
+
* resolve async logging and migration command issues (v3.0.2) ([0ed0112](https://github.com/tamylaa/clodo-framework/commit/0ed0112b2d26983fbd1329dce88286d4eca6d63b))
|
|
29
|
+
* resolve test failures and add comprehensive validation ([5abcacb](https://github.com/tamylaa/clodo-framework/commit/5abcacb39f97bcf58f92a75a8ed2602381b6c266))
|
|
30
|
+
* use temp directory in generation-engine-unit test for CI/CD compatibility ([a28a923](https://github.com/tamylaa/clodo-framework/commit/a28a92311fa6d8b1c6bd95a354c5e7cd4ada3b48))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### BREAKING CHANGES
|
|
34
|
+
|
|
35
|
+
* none
|
|
36
|
+
VALIDATION: Dry-run test PASSED with real Cloudflare API integration
|
|
37
|
+
|
|
38
|
+
Validation Reports:
|
|
39
|
+
- PRODUCTION_VALIDATION_REPORT.md - Full validation analysis
|
|
40
|
+
- DEPLOY_COMMAND_VALIDATION.md - Deploy command documentation
|
|
41
|
+
- DRY_RUN_TEST_RESULTS.md - Dry-run test with real API
|
|
42
|
+
- VALIDATION_SUMMARY.md - Executive summary
|
|
43
|
+
|
|
44
|
+
Confidence: 95% - Ready for production deployment
|
|
45
|
+
|
|
46
|
+
## [3.0.1](https://github.com/tamylaa/clodo-framework/compare/v3.0.0...v3.0.1) (2025-10-14)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Bug Fixes
|
|
50
|
+
|
|
51
|
+
* include all bin/shared files and correct imports to use dist/ ([268b525](https://github.com/tamylaa/clodo-framework/commit/268b5254d269951e78d481840a9a0fbba486c879)), closes [#deploy-v3](https://github.com/tamylaa/clodo-framework/issues/deploy-v3)
|
|
52
|
+
|
|
53
|
+
# [3.0.0](https://github.com/tamylaa/clodo-framework/compare/v2.0.20...v3.0.0) (2025-10-14)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Bug Fixes
|
|
57
|
+
|
|
58
|
+
* resolve test failures and add comprehensive validation ([5abcacb](https://github.com/tamylaa/clodo-framework/commit/5abcacb39f97bcf58f92a75a8ed2602381b6c266))
|
|
59
|
+
* use temp directory in generation-engine-unit test for CI/CD compatibility ([a28a923](https://github.com/tamylaa/clodo-framework/commit/a28a92311fa6d8b1c6bd95a354c5e7cd4ada3b48))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### BREAKING CHANGES
|
|
63
|
+
|
|
64
|
+
* none
|
|
65
|
+
VALIDATION: Dry-run test PASSED with real Cloudflare API integration
|
|
66
|
+
|
|
67
|
+
Validation Reports:
|
|
68
|
+
- PRODUCTION_VALIDATION_REPORT.md - Full validation analysis
|
|
69
|
+
- DEPLOY_COMMAND_VALIDATION.md - Deploy command documentation
|
|
70
|
+
- DRY_RUN_TEST_RESULTS.md - Dry-run test with real API
|
|
71
|
+
- VALIDATION_SUMMARY.md - Executive summary
|
|
72
|
+
|
|
73
|
+
Confidence: 95% - Ready for production deployment
|
|
74
|
+
|
|
1
75
|
## [2.0.20](https://github.com/tamylaa/clodo-framework/compare/v2.0.19...v2.0.20) (2025-10-13)
|
|
2
76
|
|
|
3
77
|
|
package/bin/clodo-service.js
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Database Tools
|
|
2
|
+
|
|
3
|
+
Database management and operations tools for Clodo Framework services.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### enterprise-db-manager.js
|
|
8
|
+
Enterprise database management CLI for Cloudflare D1 and other databases.
|
|
9
|
+
|
|
10
|
+
**Features:**
|
|
11
|
+
- Multi-database orchestration
|
|
12
|
+
- Schema management and migrations
|
|
13
|
+
- Backup and restore operations
|
|
14
|
+
- Performance monitoring and optimization
|
|
15
|
+
- Connection pooling and management
|
|
16
|
+
- Security and access control
|
|
17
|
+
- Audit logging and compliance
|
|
18
|
+
|
|
19
|
+
**Usage:**
|
|
20
|
+
```bash
|
|
21
|
+
# Database operations
|
|
22
|
+
node bin/database/enterprise-db-manager.js migrate --service my-service
|
|
23
|
+
node bin/database/enterprise-db-manager.js backup --database my-db
|
|
24
|
+
node bin/database/enterprise-db-manager.js monitor --service my-service
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Commands:**
|
|
28
|
+
- `migrate` - Run database migrations
|
|
29
|
+
- `backup` - Create database backups
|
|
30
|
+
- `restore` - Restore from backups
|
|
31
|
+
- `monitor` - Monitor database performance
|
|
32
|
+
- `schema` - Manage database schemas
|
|
33
|
+
- `connections` - Manage database connections
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Database Manager Module
|
|
3
|
+
* Handles D1 database operations specifically during service deployment context.
|
|
4
|
+
*
|
|
5
|
+
* This module is focused on single-service deployment operations and works in coordination
|
|
6
|
+
* with the enterprise-db-manager.js for broader portfolio management tasks.
|
|
7
|
+
*
|
|
8
|
+
* Scope:
|
|
9
|
+
* - D1 database validation during deployment
|
|
10
|
+
* - Database configuration for specific service deployments
|
|
11
|
+
* - Deployment-time migrations and health checks
|
|
12
|
+
* - Integration with deployment orchestration
|
|
13
|
+
*
|
|
14
|
+
* For broader database management (monitoring, backups, multi-domain operations),
|
|
15
|
+
* use the enterprise-db-manager.js CLI tool.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { promisify } from 'util';
|
|
19
|
+
import { exec } from 'child_process';
|
|
20
|
+
import { askYesNo, askChoice, askUser } from '../shared/utils/interactive-prompts.js';
|
|
21
|
+
import {
|
|
22
|
+
databaseExists,
|
|
23
|
+
createDatabase,
|
|
24
|
+
getDatabaseId,
|
|
25
|
+
runMigrations,
|
|
26
|
+
executeSql,
|
|
27
|
+
listDatabases,
|
|
28
|
+
deleteDatabase
|
|
29
|
+
} from '../shared/cloudflare/ops.js';
|
|
30
|
+
|
|
31
|
+
const execAsync = promisify(exec);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Manages all D1 database operations including creation, validation,
|
|
35
|
+
* migration management, and health checks
|
|
36
|
+
*/
|
|
37
|
+
export class DeploymentDatabaseManager {
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.state = {
|
|
41
|
+
databasesChecked: [],
|
|
42
|
+
migrationsRun: false,
|
|
43
|
+
validationComplete: false
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Handle comprehensive database setup and configuration
|
|
49
|
+
*/
|
|
50
|
+
async handleDatabase(config) {
|
|
51
|
+
console.log('\nšļø Database Configuration');
|
|
52
|
+
console.log('=========================');
|
|
53
|
+
|
|
54
|
+
// Generate database name if not provided
|
|
55
|
+
if (!config.database.name) {
|
|
56
|
+
config.database.name = `${config.domain}-auth-db`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`\nš Database name: ${config.database.name}`);
|
|
60
|
+
|
|
61
|
+
const useGeneratedName = await askYesNo('Use this database name?', true);
|
|
62
|
+
if (!useGeneratedName) {
|
|
63
|
+
config.database.name = await askUser(
|
|
64
|
+
'Enter custom database name',
|
|
65
|
+
config.database.name
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for existing database
|
|
70
|
+
console.log('\nš Checking for existing database...');
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const dbExists = await databaseExists(config.database.name);
|
|
74
|
+
|
|
75
|
+
if (dbExists) {
|
|
76
|
+
return await this.handleExistingDatabase(config);
|
|
77
|
+
} else {
|
|
78
|
+
return await this.handleNewDatabase(config);
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Error(`Database check failed: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Handle existing database scenario
|
|
87
|
+
*/
|
|
88
|
+
async handleExistingDatabase(config) {
|
|
89
|
+
console.log(` š Database '${config.database.name}' already exists`);
|
|
90
|
+
|
|
91
|
+
const databaseChoice = await askChoice(
|
|
92
|
+
'What would you like to do with the existing database?',
|
|
93
|
+
[
|
|
94
|
+
'Use the existing database (recommended)',
|
|
95
|
+
'Create a new database with different name',
|
|
96
|
+
'Delete existing and create new (DANGER: DATA LOSS)'
|
|
97
|
+
],
|
|
98
|
+
0
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
switch (databaseChoice) {
|
|
102
|
+
case 0: {
|
|
103
|
+
// Use existing database
|
|
104
|
+
return await this.useExistingDatabase(config);
|
|
105
|
+
}
|
|
106
|
+
case 1: {
|
|
107
|
+
// Create new database with different name
|
|
108
|
+
return await this.createNewDatabaseWithDifferentName(config);
|
|
109
|
+
}
|
|
110
|
+
case 2: {
|
|
111
|
+
// Delete existing and create new
|
|
112
|
+
return await this.deleteAndRecreateDatabase(config);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Use existing database
|
|
119
|
+
*/
|
|
120
|
+
async useExistingDatabase(config) {
|
|
121
|
+
try {
|
|
122
|
+
// Extract database ID from the list command output
|
|
123
|
+
const dbListResult = await execAsync('npx wrangler d1 list');
|
|
124
|
+
const lines = dbListResult.stdout.split('\n');
|
|
125
|
+
|
|
126
|
+
for (const line of lines) {
|
|
127
|
+
if (line.includes(config.database.name)) {
|
|
128
|
+
const match = line.match(/([a-f0-9-]{36})/);
|
|
129
|
+
if (match) {
|
|
130
|
+
config.database.id = match[1];
|
|
131
|
+
console.log(` ā
Using existing database ID: ${config.database.id}`);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!config.database.id) {
|
|
138
|
+
throw new Error('Could not extract database ID from existing database');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if migrations should be run
|
|
142
|
+
if (config.database.enableMigrations) {
|
|
143
|
+
const runMigs = await askYesNo(
|
|
144
|
+
'Run pending migrations on existing database?',
|
|
145
|
+
true
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (runMigs) {
|
|
149
|
+
await this.runDatabaseMigrations(config);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return config.database;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new Error(`Failed to use existing database: ${error.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create new database with different name
|
|
161
|
+
*/
|
|
162
|
+
async createNewDatabaseWithDifferentName(config) {
|
|
163
|
+
const newName = await askUser('Enter new database name');
|
|
164
|
+
config.database.name = newName;
|
|
165
|
+
config.database.createNew = true;
|
|
166
|
+
|
|
167
|
+
return await this.handleNewDatabase(config);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Delete existing database and create new one
|
|
172
|
+
*/
|
|
173
|
+
async deleteAndRecreateDatabase(config) {
|
|
174
|
+
const confirmDelete = await askYesNo(
|
|
175
|
+
'ā ļø Are you ABSOLUTELY SURE? This will permanently delete ALL data!',
|
|
176
|
+
false
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (!confirmDelete) {
|
|
180
|
+
console.log(' ā
Database deletion cancelled');
|
|
181
|
+
return await this.handleExistingDatabase(config);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Additional confirmation
|
|
185
|
+
const doubleConfirm = await askUser(
|
|
186
|
+
`Type "${config.database.name}" to confirm deletion`
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (doubleConfirm !== config.database.name) {
|
|
190
|
+
console.log(' ā Database name mismatch. Deletion cancelled for safety.');
|
|
191
|
+
return await this.handleExistingDatabase(config);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
console.log(' šļø Deleting existing database...');
|
|
196
|
+
await deleteDatabase(config.database.name);
|
|
197
|
+
console.log(' ā
Database deleted');
|
|
198
|
+
|
|
199
|
+
// Now create new database
|
|
200
|
+
return await this.handleNewDatabase(config);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
throw new Error(`Database deletion failed: ${error.message}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Handle new database creation
|
|
208
|
+
*/
|
|
209
|
+
async handleNewDatabase(config) {
|
|
210
|
+
console.log(` ā
Database name '${config.database.name}' is available`);
|
|
211
|
+
|
|
212
|
+
const shouldCreate = await askYesNo(
|
|
213
|
+
`Create new D1 database '${config.database.name}'?`,
|
|
214
|
+
true
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
if (!shouldCreate) {
|
|
218
|
+
throw new Error('Database creation cancelled by user');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
console.log(' šØ Creating database...');
|
|
223
|
+
const createResult = await createDatabase(config.database.name);
|
|
224
|
+
|
|
225
|
+
if (createResult && createResult.success) {
|
|
226
|
+
config.database.id = createResult.id || await getDatabaseId(config.database.name);
|
|
227
|
+
console.log(` ā
Database created successfully: ${config.database.id}`);
|
|
228
|
+
|
|
229
|
+
// Run initial migrations if enabled
|
|
230
|
+
if (config.database.enableMigrations) {
|
|
231
|
+
const runInitialMigrations = await askYesNo(
|
|
232
|
+
'Run initial database migrations?',
|
|
233
|
+
true
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (runInitialMigrations) {
|
|
237
|
+
await this.runDatabaseMigrations(config);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return config.database;
|
|
242
|
+
} else {
|
|
243
|
+
throw new Error('Database creation returned unsuccessful result');
|
|
244
|
+
}
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.log(` ā Database creation failed: ${error.message}`);
|
|
247
|
+
throw new Error(`Database creation failed: ${error.message}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Run database migrations
|
|
253
|
+
*/
|
|
254
|
+
async runDatabaseMigrations(config) {
|
|
255
|
+
console.log('\nš Running database migrations...');
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await runMigrations(config.database.name);
|
|
259
|
+
console.log(' ā
Database migrations completed');
|
|
260
|
+
this.state.migrationsRun = true;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.log(` ā ļø Migration warning: ${error.message}`);
|
|
263
|
+
|
|
264
|
+
const continueAnyway = await askYesNo(
|
|
265
|
+
'Continue deployment despite migration issues?',
|
|
266
|
+
false
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
if (!continueAnyway) {
|
|
270
|
+
throw new Error(`Migration failed: ${error.message}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(' ā ļø Continuing with migration warnings');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Validate database configuration and connectivity
|
|
279
|
+
*/
|
|
280
|
+
async validateDatabaseConfiguration(config) {
|
|
281
|
+
console.log('\nš Validating database configuration...');
|
|
282
|
+
|
|
283
|
+
const validationResults = {
|
|
284
|
+
exists: false,
|
|
285
|
+
accessible: false,
|
|
286
|
+
migrationsApplied: false,
|
|
287
|
+
issues: []
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
// Check if database exists
|
|
292
|
+
const exists = await databaseExists(config.database.name);
|
|
293
|
+
validationResults.exists = exists;
|
|
294
|
+
|
|
295
|
+
if (!exists) {
|
|
296
|
+
validationResults.issues.push(`Database '${config.database.name}' does not exist`);
|
|
297
|
+
return validationResults;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(` ā
Database '${config.database.name}' exists`);
|
|
301
|
+
|
|
302
|
+
// Test basic connectivity
|
|
303
|
+
try {
|
|
304
|
+
await executeSql(config.database.name, 'SELECT 1 as test');
|
|
305
|
+
validationResults.accessible = true;
|
|
306
|
+
console.log(' ā
Database is accessible');
|
|
307
|
+
} catch (error) {
|
|
308
|
+
validationResults.issues.push(`Database connectivity test failed: ${error.message}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Check migrations status if enabled
|
|
312
|
+
if (config.database.enableMigrations) {
|
|
313
|
+
try {
|
|
314
|
+
const migrationsResult = await execAsync(
|
|
315
|
+
`npx wrangler d1 migrations list ${config.database.name}`
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
if (migrationsResult.stdout.includes('Applied')) {
|
|
319
|
+
validationResults.migrationsApplied = true;
|
|
320
|
+
console.log(' ā
Migrations are applied');
|
|
321
|
+
} else {
|
|
322
|
+
validationResults.issues.push('No migrations have been applied');
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
validationResults.issues.push(`Migration status check failed: ${error.message}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
} catch (error) {
|
|
330
|
+
validationResults.issues.push(`Database validation failed: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.state.validationComplete = true;
|
|
334
|
+
this.state.databasesChecked.push({
|
|
335
|
+
name: config.database.name,
|
|
336
|
+
results: validationResults,
|
|
337
|
+
timestamp: new Date().toISOString()
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
return validationResults;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Perform database health check
|
|
345
|
+
*/
|
|
346
|
+
async performHealthCheck(config) {
|
|
347
|
+
console.log('\nš„ Database Health Check');
|
|
348
|
+
console.log('========================');
|
|
349
|
+
|
|
350
|
+
const healthResults = {
|
|
351
|
+
overall: 'unknown',
|
|
352
|
+
connectivity: false,
|
|
353
|
+
responseTime: 0,
|
|
354
|
+
tableCount: 0,
|
|
355
|
+
issues: []
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const startTime = Date.now();
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
// Test basic query
|
|
362
|
+
await executeSql(config.database.name, 'SELECT 1 as health_check');
|
|
363
|
+
healthResults.connectivity = true;
|
|
364
|
+
healthResults.responseTime = Date.now() - startTime;
|
|
365
|
+
|
|
366
|
+
console.log(` ā
Database responding (${healthResults.responseTime}ms)`);
|
|
367
|
+
|
|
368
|
+
// Count tables
|
|
369
|
+
try {
|
|
370
|
+
const tablesResult = await executeSql(
|
|
371
|
+
config.database.name,
|
|
372
|
+
"SELECT COUNT(*) as table_count FROM sqlite_master WHERE type='table'"
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// Parse table count from result
|
|
376
|
+
if (tablesResult && tablesResult.results && tablesResult.results.length > 0) {
|
|
377
|
+
healthResults.tableCount = tablesResult.results[0].table_count || 0;
|
|
378
|
+
console.log(` š Database contains ${healthResults.tableCount} tables`);
|
|
379
|
+
}
|
|
380
|
+
} catch (error) {
|
|
381
|
+
healthResults.issues.push(`Table count check failed: ${error.message}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Overall health determination
|
|
385
|
+
if (healthResults.connectivity && healthResults.responseTime < 5000) {
|
|
386
|
+
healthResults.overall = 'healthy';
|
|
387
|
+
} else if (healthResults.connectivity) {
|
|
388
|
+
healthResults.overall = 'slow';
|
|
389
|
+
} else {
|
|
390
|
+
healthResults.overall = 'unhealthy';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
} catch (error) {
|
|
394
|
+
healthResults.overall = 'unhealthy';
|
|
395
|
+
healthResults.responseTime = Date.now() - startTime;
|
|
396
|
+
healthResults.issues.push(`Health check failed: ${error.message}`);
|
|
397
|
+
console.log(` ā Database health check failed: ${error.message}`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return healthResults;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Get database management summary
|
|
405
|
+
*/
|
|
406
|
+
getDatabaseSummary() {
|
|
407
|
+
return {
|
|
408
|
+
databasesManaged: this.state.databasesChecked.length,
|
|
409
|
+
migrationsRun: this.state.migrationsRun,
|
|
410
|
+
validationComplete: this.state.validationComplete,
|
|
411
|
+
lastCheck: this.state.databasesChecked.length > 0
|
|
412
|
+
? this.state.databasesChecked[this.state.databasesChecked.length - 1].timestamp
|
|
413
|
+
: null
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* List all available databases
|
|
419
|
+
*/
|
|
420
|
+
async listAvailableDatabases() {
|
|
421
|
+
try {
|
|
422
|
+
console.log('\nš Available D1 Databases:');
|
|
423
|
+
const databases = await listDatabases();
|
|
424
|
+
|
|
425
|
+
if (databases && databases.length > 0) {
|
|
426
|
+
databases.forEach((db, index) => {
|
|
427
|
+
console.log(` ${index + 1}. ${db.name} (${db.id})`);
|
|
428
|
+
});
|
|
429
|
+
return databases;
|
|
430
|
+
} else {
|
|
431
|
+
console.log(' ā¹ļø No D1 databases found in your account');
|
|
432
|
+
return [];
|
|
433
|
+
}
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.log(` ā Failed to list databases: ${error.message}`);
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Interactive database selection from existing databases
|
|
442
|
+
*/
|
|
443
|
+
async selectFromExistingDatabases(config) {
|
|
444
|
+
const databases = await this.listAvailableDatabases();
|
|
445
|
+
|
|
446
|
+
if (databases.length === 0) {
|
|
447
|
+
const createNew = await askYesNo(
|
|
448
|
+
'No existing databases found. Create a new one?',
|
|
449
|
+
true
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
if (createNew) {
|
|
453
|
+
return await this.handleNewDatabase(config);
|
|
454
|
+
} else {
|
|
455
|
+
throw new Error('Database selection cancelled');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const dbOptions = databases.map(db => `${db.name} (${db.id})`);
|
|
460
|
+
dbOptions.push('Create new database');
|
|
461
|
+
|
|
462
|
+
const choice = await askChoice(
|
|
463
|
+
'Select a database to use:',
|
|
464
|
+
dbOptions
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
if (choice === dbOptions.length - 1) {
|
|
468
|
+
// Create new database option selected
|
|
469
|
+
return await this.handleNewDatabase(config);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Existing database selected
|
|
473
|
+
const selectedDb = databases[choice];
|
|
474
|
+
config.database.name = selectedDb.name;
|
|
475
|
+
config.database.id = selectedDb.id;
|
|
476
|
+
|
|
477
|
+
console.log(` ā
Selected database: ${selectedDb.name} (${selectedDb.id})`);
|
|
478
|
+
|
|
479
|
+
// Check if migrations should be run
|
|
480
|
+
if (config.database.enableMigrations) {
|
|
481
|
+
const runMigrations = await askYesNo(
|
|
482
|
+
'Run migrations on selected database?',
|
|
483
|
+
false
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
if (runMigrations) {
|
|
487
|
+
await this.runDatabaseMigrations(config);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return config.database;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Cleanup and rollback database operations
|
|
496
|
+
*/
|
|
497
|
+
async rollbackDatabaseChanges(rollbackActions) {
|
|
498
|
+
console.log('\nš Rolling back database changes...');
|
|
499
|
+
|
|
500
|
+
const databaseRollbacks = rollbackActions.filter(action =>
|
|
501
|
+
action.type.includes('database') || action.type.includes('migration')
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
for (const action of databaseRollbacks) {
|
|
505
|
+
try {
|
|
506
|
+
console.log(` š ${action.description}`);
|
|
507
|
+
|
|
508
|
+
switch (action.type) {
|
|
509
|
+
case 'delete-database':
|
|
510
|
+
await deleteDatabase(action.name);
|
|
511
|
+
console.log(` ā
Database '${action.name}' deleted`);
|
|
512
|
+
break;
|
|
513
|
+
|
|
514
|
+
case 'rollback-migration':
|
|
515
|
+
// Note: D1 doesn't support migration rollback, so we log this
|
|
516
|
+
console.log(` ā ļø Migration rollback not supported by D1: ${action.migration}`);
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
default:
|
|
520
|
+
console.log(` ā ļø Unknown rollback action: ${action.type}`);
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.log(` ā Rollback failed: ${error.message}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|