@tamyla/clodo-framework 2.0.19 → 3.0.2

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +206 -62
  2. package/bin/clodo-service.js +32 -56
  3. package/bin/database/README.md +33 -0
  4. package/bin/database/deployment-db-manager.js +527 -0
  5. package/bin/database/enterprise-db-manager.js +736 -0
  6. package/bin/database/wrangler-d1-manager.js +775 -0
  7. package/bin/shared/cloudflare/domain-discovery.js +636 -0
  8. package/bin/shared/cloudflare/domain-manager.js +952 -0
  9. package/bin/shared/cloudflare/index.js +8 -0
  10. package/bin/shared/cloudflare/ops.js +359 -0
  11. package/bin/shared/config/index.js +1 -1
  12. package/bin/shared/database/connection-manager.js +374 -0
  13. package/bin/shared/database/index.js +7 -0
  14. package/bin/shared/database/orchestrator.js +726 -0
  15. package/bin/shared/deployment/auditor.js +969 -0
  16. package/bin/shared/deployment/index.js +10 -0
  17. package/bin/shared/deployment/rollback-manager.js +570 -0
  18. package/bin/shared/deployment/validator.js +779 -0
  19. package/bin/shared/index.js +32 -0
  20. package/bin/shared/monitoring/health-checker.js +484 -0
  21. package/bin/shared/monitoring/index.js +8 -0
  22. package/bin/shared/monitoring/memory-manager.js +387 -0
  23. package/bin/shared/monitoring/production-monitor.js +391 -0
  24. package/bin/shared/production-tester/api-tester.js +82 -0
  25. package/bin/shared/production-tester/auth-tester.js +132 -0
  26. package/bin/shared/production-tester/core.js +197 -0
  27. package/bin/shared/production-tester/database-tester.js +109 -0
  28. package/bin/shared/production-tester/index.js +77 -0
  29. package/bin/shared/production-tester/load-tester.js +131 -0
  30. package/bin/shared/production-tester/performance-tester.js +103 -0
  31. package/bin/shared/security/api-token-manager.js +312 -0
  32. package/bin/shared/security/index.js +8 -0
  33. package/bin/shared/security/secret-generator.js +937 -0
  34. package/bin/shared/security/secure-token-manager.js +398 -0
  35. package/bin/shared/utils/error-recovery.js +225 -0
  36. package/bin/shared/utils/graceful-shutdown-manager.js +390 -0
  37. package/bin/shared/utils/index.js +9 -0
  38. package/bin/shared/utils/interactive-prompts.js +146 -0
  39. package/bin/shared/utils/interactive-utils.js +530 -0
  40. package/bin/shared/utils/rate-limiter.js +246 -0
  41. package/dist/database/database-orchestrator.js +34 -12
  42. package/dist/deployment/index.js +2 -2
  43. package/dist/orchestration/multi-domain-orchestrator.js +8 -6
  44. package/dist/service-management/GenerationEngine.js +76 -28
  45. package/dist/service-management/ServiceInitializer.js +5 -3
  46. package/dist/shared/cloudflare/domain-manager.js +1 -1
  47. package/dist/shared/cloudflare/ops.js +27 -12
  48. package/dist/shared/config/index.js +1 -1
  49. package/dist/shared/deployment/index.js +2 -2
  50. package/dist/shared/security/secret-generator.js +4 -2
  51. package/dist/shared/utils/error-recovery.js +1 -1
  52. package/dist/shared/utils/graceful-shutdown-manager.js +4 -3
  53. package/dist/utils/deployment/secret-generator.js +19 -6
  54. package/package.json +7 -6
  55. package/bin/shared/config/customer-cli.js +0 -182
  56. package/dist/shared/config/customer-cli.js +0 -175
@@ -0,0 +1,312 @@
1
+ /**
2
+ * API Token Manager
3
+ * Secure management of external service API tokens (Cloudflare, etc.)
4
+ *
5
+ * Features:
6
+ * - Secure token storage with encryption
7
+ * - Interactive token collection
8
+ * - Token validation and testing
9
+ * - Automatic token refresh and management
10
+ */
11
+
12
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
13
+ import { join, dirname } from 'path';
14
+ import { createHash, createCipheriv, createDecipheriv, randomBytes } from 'crypto';
15
+ import { askUser, askPassword } from '../utils/interactive-prompts.js';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ export class ApiTokenManager {
22
+ constructor(options = {}) {
23
+ this.projectRoot = options.projectRoot || join(__dirname, '..', '..');
24
+ this.configPath = join(this.projectRoot, '.config-cache', 'api-tokens.json');
25
+ this.encryptionKey = this.getOrCreateEncryptionKey();
26
+
27
+ // Ensure config directory exists
28
+ this.ensureDirectory(dirname(this.configPath));
29
+
30
+ // Load existing tokens
31
+ this.tokens = this.loadTokens();
32
+
33
+ console.log('🔑 API Token Manager initialized');
34
+ console.log(`📁 Config: ${this.configPath}`);
35
+ console.log(`🔐 Encrypted storage: ${this.isEncrypted() ? 'Yes' : 'No'}`);
36
+ }
37
+
38
+ /**
39
+ * Get or create encryption key for secure token storage
40
+ */
41
+ getOrCreateEncryptionKey() {
42
+ const keyPath = join(this.projectRoot, '.config-cache', '.token-key');
43
+
44
+ if (existsSync(keyPath)) {
45
+ return readFileSync(keyPath, 'utf-8').trim();
46
+ }
47
+
48
+ // Generate new encryption key
49
+ const key = randomBytes(32).toString('hex');
50
+ this.ensureDirectory(dirname(keyPath));
51
+ writeFileSync(keyPath, key, { mode: 0o600 }); // Secure file permissions
52
+
53
+ return key;
54
+ }
55
+
56
+ /**
57
+ * Load tokens from encrypted storage
58
+ */
59
+ loadTokens() {
60
+ if (!existsSync(this.configPath)) {
61
+ return {};
62
+ }
63
+
64
+ try {
65
+ const data = readFileSync(this.configPath, 'utf-8');
66
+ const config = JSON.parse(data);
67
+
68
+ // Decrypt tokens if they're encrypted
69
+ if (config.encrypted) {
70
+ const decryptedTokens = {};
71
+ for (const [service, encryptedToken] of Object.entries(config.tokens)) {
72
+ try {
73
+ decryptedTokens[service] = this.decryptToken(encryptedToken);
74
+ } catch (error) {
75
+ console.warn(`⚠️ Failed to decrypt token for ${service}`);
76
+ }
77
+ }
78
+ return decryptedTokens;
79
+ }
80
+
81
+ return config.tokens || {};
82
+ } catch (error) {
83
+ console.warn('⚠️ Failed to load API tokens, starting fresh');
84
+ return {};
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Save tokens to encrypted storage
90
+ */
91
+ saveTokens() {
92
+ try {
93
+ const encryptedTokens = {};
94
+
95
+ // Encrypt each token
96
+ for (const [service, token] of Object.entries(this.tokens)) {
97
+ encryptedTokens[service] = this.encryptToken(token);
98
+ }
99
+
100
+ const config = {
101
+ encrypted: true,
102
+ lastUpdated: new Date().toISOString(),
103
+ services: Object.keys(this.tokens),
104
+ tokens: encryptedTokens
105
+ };
106
+
107
+ writeFileSync(this.configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
108
+ console.log(`✅ API tokens saved securely to ${this.configPath}`);
109
+ } catch (error) {
110
+ console.error('❌ Failed to save API tokens:', error.message);
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Encrypt token for secure storage
117
+ */
118
+ encryptToken(token) {
119
+ try {
120
+ const algorithm = 'aes-256-cbc';
121
+ const iv = randomBytes(16);
122
+ const cipher = createCipheriv(algorithm, Buffer.from(this.encryptionKey, 'hex'), iv);
123
+ let encrypted = cipher.update(token, 'utf8', 'hex');
124
+ encrypted += cipher.final('hex');
125
+ return iv.toString('hex') + ':' + encrypted;
126
+ } catch (error) {
127
+ console.warn('⚠️ Token encryption failed, storing in plain text');
128
+ return token;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Decrypt token from storage
134
+ */
135
+ decryptToken(encryptedToken) {
136
+ try {
137
+ const algorithm = 'aes-256-cbc';
138
+ const parts = encryptedToken.split(':');
139
+ if (parts.length !== 2) {
140
+ // Old format or plain text, return as is
141
+ return encryptedToken;
142
+ }
143
+ const iv = Buffer.from(parts[0], 'hex');
144
+ const encrypted = parts[1];
145
+ const decipher = createDecipheriv(algorithm, Buffer.from(this.encryptionKey, 'hex'), iv);
146
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
147
+ decrypted += decipher.final('utf8');
148
+ return decrypted;
149
+ } catch (error) {
150
+ // If decryption fails, assume it's plain text (backward compatibility)
151
+ return encryptedToken;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Get API token for a service, prompting if not available
157
+ */
158
+ async getToken(service, options = {}) {
159
+ const serviceName = service.toLowerCase();
160
+
161
+ // Check if token already exists
162
+ if (this.tokens[serviceName]) {
163
+ console.log(`🔑 Using cached ${service} API token`);
164
+ return this.tokens[serviceName];
165
+ }
166
+
167
+ // Prompt user for token
168
+ console.log(`\\n🔑 ${service} API Token Required`);
169
+ console.log('═'.repeat(40));
170
+
171
+ if (options.description) {
172
+ console.log(`📋 ${options.description}`);
173
+ }
174
+
175
+ if (options.instructions) {
176
+ console.log('💡 Instructions:');
177
+ options.instructions.forEach(instruction => {
178
+ console.log(` • ${instruction}`);
179
+ });
180
+ }
181
+
182
+ const token = await askPassword(`Enter your ${service} API token:`);
183
+
184
+ if (!token || token.trim() === '') {
185
+ throw new Error(`${service} API token is required but not provided`);
186
+ }
187
+
188
+ // Validate token if validator provided
189
+ if (options.validator) {
190
+ console.log(`🔍 Validating ${service} API token...`);
191
+ try {
192
+ const isValid = await options.validator(token.trim());
193
+ if (!isValid) {
194
+ throw new Error(`Invalid ${service} API token`);
195
+ }
196
+ console.log(`✅ ${service} API token validated successfully`);
197
+ } catch (error) {
198
+ console.error(`❌ Token validation failed: ${error.message}`);
199
+ throw error;
200
+ }
201
+ }
202
+
203
+ // Store token
204
+ this.tokens[serviceName] = token.trim();
205
+ this.saveTokens();
206
+
207
+ console.log(`✅ ${service} API token saved securely`);
208
+ return this.tokens[serviceName];
209
+ }
210
+
211
+ /**
212
+ * Check if token exists for a service
213
+ */
214
+ hasToken(service) {
215
+ return !!this.tokens[service.toLowerCase()];
216
+ }
217
+
218
+ /**
219
+ * Remove token for a service
220
+ */
221
+ removeToken(service) {
222
+ const serviceName = service.toLowerCase();
223
+ if (this.tokens[serviceName]) {
224
+ delete this.tokens[serviceName];
225
+ this.saveTokens();
226
+ console.log(`🗑️ Removed ${service} API token`);
227
+ return true;
228
+ }
229
+ return false;
230
+ }
231
+
232
+ /**
233
+ * List available tokens (without revealing actual values)
234
+ */
235
+ listTokens() {
236
+ const services = Object.keys(this.tokens);
237
+
238
+ if (services.length === 0) {
239
+ console.log('📝 No API tokens stored');
240
+ return [];
241
+ }
242
+
243
+ console.log('🔑 Stored API tokens:');
244
+ services.forEach(service => {
245
+ const token = this.tokens[service];
246
+ const preview = token.substring(0, 8) + '...';
247
+ console.log(` • ${service}: ${preview}`);
248
+ });
249
+
250
+ return services;
251
+ }
252
+
253
+ /**
254
+ * Utility methods
255
+ */
256
+ isEncrypted() {
257
+ return !!this.encryptionKey;
258
+ }
259
+
260
+ ensureDirectory(dirPath) {
261
+ if (!existsSync(dirPath)) {
262
+ mkdirSync(dirPath, { recursive: true, mode: 0o700 });
263
+ }
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Cloudflare API Token Manager
269
+ * Specialized manager for Cloudflare API tokens with validation
270
+ */
271
+ export class CloudflareTokenManager extends ApiTokenManager {
272
+ constructor(options = {}) {
273
+ super(options);
274
+ }
275
+
276
+ /**
277
+ * Get Cloudflare API token with validation
278
+ */
279
+ async getCloudflareToken() {
280
+ return this.getToken('cloudflare', {
281
+ description: 'Cloudflare API token is required for domain verification and deployment operations.',
282
+ instructions: [
283
+ 'Go to Cloudflare Dashboard → My Profile → API Tokens',
284
+ 'Create a token with Zone:Read and Worker:Edit permissions',
285
+ 'Copy the token (it will only be shown once)'
286
+ ],
287
+ validator: this.validateCloudflareToken.bind(this)
288
+ });
289
+ }
290
+
291
+ /**
292
+ * Validate Cloudflare API token
293
+ */
294
+ async validateCloudflareToken(token) {
295
+ try {
296
+ const response = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
297
+ headers: {
298
+ 'Authorization': `Bearer ${token}`,
299
+ 'Content-Type': 'application/json'
300
+ }
301
+ });
302
+
303
+ const data = await response.json();
304
+ return response.ok && data.success;
305
+ } catch (error) {
306
+ console.warn('⚠️ Token validation failed (network error)');
307
+ return false;
308
+ }
309
+ }
310
+ }
311
+
312
+ export default ApiTokenManager;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Security Module
3
+ * Exports all security and authentication related utilities
4
+ */
5
+
6
+ export { ApiTokenManager } from './api-token-manager.js';
7
+ export { SecureTokenManager } from './secure-token-manager.js';
8
+ export { SecretGenerator } from './secret-generator.js';