@tamyla/clodo-framework 3.1.4 → 3.1.8
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 +29 -0
- package/bin/clodo-service.js +29 -989
- package/bin/database/enterprise-db-manager.js +7 -5
- package/bin/security/security-cli.js +0 -0
- package/bin/service-management/create-service.js +0 -0
- package/bin/service-management/init-service.js +0 -0
- package/bin/shared/cloudflare/domain-discovery.js +11 -10
- package/bin/shared/cloudflare/ops.js +1 -1
- package/bin/shared/config/ConfigurationManager.js +539 -0
- package/bin/shared/config/index.js +13 -1
- package/bin/shared/database/connection-manager.js +2 -2
- package/bin/shared/database/orchestrator.js +5 -4
- package/bin/shared/deployment/auditor.js +9 -8
- package/bin/shared/logging/Logger.js +214 -0
- package/bin/shared/monitoring/production-monitor.js +21 -9
- package/bin/shared/utils/ErrorHandler.js +675 -0
- package/bin/shared/utils/error-recovery.js +33 -13
- package/bin/shared/utils/file-manager.js +162 -0
- package/bin/shared/utils/formatters.js +247 -0
- package/bin/shared/utils/index.js +14 -4
- package/bin/shared/validation/ValidationRegistry.js +143 -0
- package/dist/deployment/auditor.js +23 -8
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +426 -0
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +401 -0
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +273 -0
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +231 -0
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +662 -0
- package/dist/deployment/orchestration/index.js +17 -0
- package/dist/index.js +12 -0
- package/dist/orchestration/modules/DomainResolver.js +8 -6
- package/dist/orchestration/multi-domain-orchestrator.js +13 -1
- package/dist/security/index.js +2 -2
- package/dist/service-management/ConfirmationEngine.js +8 -7
- package/dist/service-management/ErrorTracker.js +7 -2
- package/dist/service-management/InputCollector.js +31 -16
- package/dist/service-management/ServiceCreator.js +22 -7
- package/dist/service-management/ServiceInitializer.js +12 -18
- package/dist/shared/cloudflare/domain-discovery.js +11 -10
- package/dist/shared/cloudflare/ops.js +1 -1
- package/dist/shared/config/ConfigurationManager.js +519 -0
- package/dist/shared/config/index.js +5 -1
- package/dist/shared/database/connection-manager.js +2 -2
- package/dist/shared/database/orchestrator.js +13 -4
- package/dist/shared/deployment/auditor.js +23 -8
- package/dist/shared/logging/Logger.js +209 -0
- package/dist/shared/monitoring/production-monitor.js +24 -8
- package/dist/{utils → shared/utils}/ErrorHandler.js +306 -28
- package/dist/shared/utils/error-recovery.js +33 -13
- package/dist/shared/utils/file-manager.js +155 -0
- package/dist/shared/utils/formatters.js +215 -0
- package/dist/shared/utils/index.js +14 -4
- package/dist/shared/validation/ValidationRegistry.js +126 -0
- package/dist/utils/config/unified-config-manager.js +14 -12
- package/dist/utils/deployment/config-cache.js +3 -1
- package/dist/utils/deployment/secret-generator.js +32 -29
- package/dist/utils/framework-config.js +6 -3
- package/dist/utils/ui-structures-loader.js +3 -0
- package/dist/worker/integration.js +11 -1
- package/package.json +31 -3
- package/dist/config/FeatureManager.js +0 -426
- package/dist/config/features.js +0 -230
- package/dist/utils/error-recovery.js +0 -240
|
@@ -13,6 +13,7 @@ import { existsSync } from 'fs';
|
|
|
13
13
|
import { join, dirname } from 'path';
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { promisify } from 'util';
|
|
16
|
+
import { logger } from '../logging/Logger.js';
|
|
16
17
|
|
|
17
18
|
const execAsync = promisify(exec);
|
|
18
19
|
|
|
@@ -186,7 +187,7 @@ export class DatabaseOrchestrator {
|
|
|
186
187
|
console.log(`✅ ${env} environment completed successfully`);
|
|
187
188
|
|
|
188
189
|
} catch (error) {
|
|
189
|
-
|
|
190
|
+
logger.error(`${env} environment failed`, { error: error.message });
|
|
190
191
|
|
|
191
192
|
results.environments[env] = {
|
|
192
193
|
status: 'failed',
|
|
@@ -261,7 +262,7 @@ export class DatabaseOrchestrator {
|
|
|
261
262
|
results.migrationsApplied += dbResult.migrationsApplied || 0;
|
|
262
263
|
|
|
263
264
|
} catch (error) {
|
|
264
|
-
|
|
265
|
+
logger.error(`Database ${dbConfig.name} migration failed`, { error: error.message });
|
|
265
266
|
results.databases[dbConfig.name] = {
|
|
266
267
|
status: 'failed',
|
|
267
268
|
error: error.message
|
|
@@ -522,7 +523,7 @@ export class DatabaseOrchestrator {
|
|
|
522
523
|
);
|
|
523
524
|
cleanupResults.operations[dbConfig.name] = cleanupResult;
|
|
524
525
|
} catch (error) {
|
|
525
|
-
|
|
526
|
+
logger.error(`Cleanup failed for ${dbConfig.name}`, { error: error.message });
|
|
526
527
|
cleanupResults.operations[dbConfig.name] = {
|
|
527
528
|
status: 'failed',
|
|
528
529
|
error: error.message
|
|
@@ -718,7 +719,7 @@ export class DatabaseOrchestrator {
|
|
|
718
719
|
writeFile(this.backupPaths.audit, logLine);
|
|
719
720
|
}
|
|
720
721
|
} catch (error) {
|
|
721
|
-
|
|
722
|
+
logger.warn('Failed to log audit event', { error: error.message });
|
|
722
723
|
}
|
|
723
724
|
}
|
|
724
725
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { existsSync, writeFileSync, appendFileSync, mkdirSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
20
20
|
import { join, dirname } from 'path';
|
|
21
21
|
import { execSync } from 'child_process';
|
|
22
|
+
import { logger } from '../logging/Logger.js';
|
|
22
23
|
|
|
23
24
|
export class DeploymentAuditor {
|
|
24
25
|
constructor(options = {}) {
|
|
@@ -253,7 +254,7 @@ export class DeploymentAuditor {
|
|
|
253
254
|
const deployment = this.currentSession.deployments.get(deploymentId);
|
|
254
255
|
|
|
255
256
|
if (!deployment) {
|
|
256
|
-
|
|
257
|
+
logger.warn('Deployment not found for phase logging', { deploymentId });
|
|
257
258
|
return;
|
|
258
259
|
}
|
|
259
260
|
|
|
@@ -319,7 +320,7 @@ export class DeploymentAuditor {
|
|
|
319
320
|
context
|
|
320
321
|
});
|
|
321
322
|
|
|
322
|
-
|
|
323
|
+
logger.error('Deployment error logged', { deploymentId });
|
|
323
324
|
}
|
|
324
325
|
|
|
325
326
|
/**
|
|
@@ -519,7 +520,7 @@ export class DeploymentAuditor {
|
|
|
519
520
|
}
|
|
520
521
|
|
|
521
522
|
} catch (error) {
|
|
522
|
-
|
|
523
|
+
logger.error('Failed to write audit log', { error: error.message });
|
|
523
524
|
}
|
|
524
525
|
}
|
|
525
526
|
|
|
@@ -542,7 +543,7 @@ export class DeploymentAuditor {
|
|
|
542
543
|
});
|
|
543
544
|
|
|
544
545
|
} catch (error) {
|
|
545
|
-
|
|
546
|
+
logger.error('Failed to rotate log file', { error: error.message });
|
|
546
547
|
}
|
|
547
548
|
}
|
|
548
549
|
|
|
@@ -555,7 +556,7 @@ export class DeploymentAuditor {
|
|
|
555
556
|
const deployment = this.currentSession.deployments.get(deploymentId);
|
|
556
557
|
|
|
557
558
|
if (!deployment) {
|
|
558
|
-
|
|
559
|
+
logger.warn('Cannot generate report: deployment not found', { deploymentId });
|
|
559
560
|
return null;
|
|
560
561
|
}
|
|
561
562
|
|
|
@@ -808,7 +809,7 @@ export class DeploymentAuditor {
|
|
|
808
809
|
*/
|
|
809
810
|
async sendSecurityAlert(securityEvent) {
|
|
810
811
|
if (!this.config.alertWebhook) {
|
|
811
|
-
|
|
812
|
+
logger.warn('Security alert webhook not configured');
|
|
812
813
|
return;
|
|
813
814
|
}
|
|
814
815
|
|
|
@@ -830,7 +831,7 @@ export class DeploymentAuditor {
|
|
|
830
831
|
});
|
|
831
832
|
|
|
832
833
|
} catch (error) {
|
|
833
|
-
|
|
834
|
+
logger.error('Failed to send security alert', { error: error.message });
|
|
834
835
|
}
|
|
835
836
|
}
|
|
836
837
|
|
|
@@ -896,7 +897,7 @@ export class DeploymentAuditor {
|
|
|
896
897
|
console.log(`🧹 Audit cleanup completed: ${cleanupResults.filesRemoved} files removed`);
|
|
897
898
|
|
|
898
899
|
} catch (error) {
|
|
899
|
-
|
|
900
|
+
logger.error('Audit cleanup failed', { error: error.message });
|
|
900
901
|
cleanupResults.errors.push({ general: error.message });
|
|
901
902
|
}
|
|
902
903
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Logger for Clodo Framework
|
|
3
|
+
* Replaces: 6 separate logging implementations
|
|
4
|
+
* Savings: 300+ lines, unified logging across entire codebase
|
|
5
|
+
*
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { appendFileSync, mkdirSync, existsSync } from 'fs';
|
|
10
|
+
import { dirname } from 'path';
|
|
11
|
+
|
|
12
|
+
const LOG_LEVELS = {
|
|
13
|
+
DEBUG: 0,
|
|
14
|
+
INFO: 1,
|
|
15
|
+
WARN: 2,
|
|
16
|
+
ERROR: 3,
|
|
17
|
+
FATAL: 4
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class Logger {
|
|
21
|
+
constructor(context = {}) {
|
|
22
|
+
this.context = context;
|
|
23
|
+
this.logLevel = this._parseLogLevel(process.env.LOG_LEVEL || 'info');
|
|
24
|
+
this.logDir = process.env.LOG_DIR || null;
|
|
25
|
+
this.logFile = null;
|
|
26
|
+
this.cache = new Map();
|
|
27
|
+
this.isDev = process.env.NODE_ENV !== 'production';
|
|
28
|
+
|
|
29
|
+
if (this.logDir) {
|
|
30
|
+
mkdirSync(this.logDir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* DEBUG level logging
|
|
36
|
+
*/
|
|
37
|
+
debug(message, data = {}) {
|
|
38
|
+
if (this.logLevel > LOG_LEVELS.DEBUG) return;
|
|
39
|
+
this._log('DEBUG', message, data);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* INFO level logging
|
|
44
|
+
*/
|
|
45
|
+
info(message, data = {}) {
|
|
46
|
+
if (this.logLevel > LOG_LEVELS.INFO) return;
|
|
47
|
+
this._log('INFO', message, data);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* WARN level logging
|
|
52
|
+
*/
|
|
53
|
+
warn(message, data = {}) {
|
|
54
|
+
if (this.logLevel > LOG_LEVELS.WARN) return;
|
|
55
|
+
this._log('WARN', message, data);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* ERROR level logging
|
|
60
|
+
*/
|
|
61
|
+
error(message, data = {}) {
|
|
62
|
+
if (this.logLevel > LOG_LEVELS.ERROR) return;
|
|
63
|
+
this._log('ERROR', message, data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* FATAL level logging (exits process)
|
|
68
|
+
*/
|
|
69
|
+
fatal(message, data = {}) {
|
|
70
|
+
this._log('FATAL', message, data);
|
|
71
|
+
if (!this.isDev) {
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Log HTTP request
|
|
78
|
+
*/
|
|
79
|
+
logRequest(request, context = {}) {
|
|
80
|
+
this.info('HTTP Request', {
|
|
81
|
+
method: request.method,
|
|
82
|
+
url: request.url,
|
|
83
|
+
userAgent: request.headers?.get?.('User-Agent'),
|
|
84
|
+
...context
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Log deployment event
|
|
90
|
+
*/
|
|
91
|
+
logDeployment(deploymentId, message, context = {}) {
|
|
92
|
+
this.info(`[Deployment ${deploymentId}] ${message}`, {
|
|
93
|
+
deploymentId,
|
|
94
|
+
...context
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Log audit event
|
|
100
|
+
*/
|
|
101
|
+
logAudit(eventType, domain, details = {}) {
|
|
102
|
+
this.info(`[Audit] ${eventType} for ${domain}`, {
|
|
103
|
+
eventType,
|
|
104
|
+
domain,
|
|
105
|
+
...details
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Log performance metric
|
|
111
|
+
*/
|
|
112
|
+
logPerformance(operation, durationMs, context = {}) {
|
|
113
|
+
const level = durationMs > 5000 ? 'warn' : 'info';
|
|
114
|
+
this[level](`Performance: ${operation}`, {
|
|
115
|
+
operation,
|
|
116
|
+
durationMs,
|
|
117
|
+
...context
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Redact sensitive information from text
|
|
123
|
+
*/
|
|
124
|
+
redact(text) {
|
|
125
|
+
if (typeof text !== 'string') return text;
|
|
126
|
+
|
|
127
|
+
const patterns = [
|
|
128
|
+
// Cloudflare tokens
|
|
129
|
+
[/(CLOUDFLARE_API_TOKEN=?)(\w{20,})/gi, '$1[REDACTED]'],
|
|
130
|
+
// Generic tokens
|
|
131
|
+
[/(token|api[_-]?key|auth[_-]?token)["']?[:=]\s*["']?([a-zA-Z0-9_-]{20,})["']?/gi, '$1: [REDACTED]'],
|
|
132
|
+
// Passwords
|
|
133
|
+
[/(password|passwd|pwd)["']?[:=]\s*["']?([^"'\s]{3,})["']?/gi, '$1: [REDACTED]'],
|
|
134
|
+
// Secrets
|
|
135
|
+
[/(secret|key)["']?[:=]\s*["']?([a-zA-Z0-9_-]{10,})["']?/gi, '$1: [REDACTED]'],
|
|
136
|
+
// Account/Zone IDs (partial)
|
|
137
|
+
[/(account[_-]?id|zone[_-]?id)["']?[:=]\s*["']?([a-zA-Z0-9]{8})([a-zA-Z0-9]{24,})["']?/gi, '$1: $2[REDACTED]']
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
let redacted = text;
|
|
141
|
+
patterns.forEach(([pattern, replacement]) => {
|
|
142
|
+
redacted = redacted.replace(pattern, replacement);
|
|
143
|
+
});
|
|
144
|
+
return redacted;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Set log file for file output
|
|
149
|
+
*/
|
|
150
|
+
setLogFile(filePath) {
|
|
151
|
+
this.logFile = filePath;
|
|
152
|
+
const dir = dirname(filePath);
|
|
153
|
+
if (!existsSync(dir)) {
|
|
154
|
+
mkdirSync(dir, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Private: Core logging implementation
|
|
160
|
+
*/
|
|
161
|
+
_log(level, message, data = {}) {
|
|
162
|
+
const timestamp = new Date().toISOString();
|
|
163
|
+
const contextStr = Object.keys(this.context).length > 0
|
|
164
|
+
? ` [${JSON.stringify(this.context)}]`
|
|
165
|
+
: '';
|
|
166
|
+
|
|
167
|
+
const logMessage = `[${timestamp}] ${level}${contextStr}: ${message}`;
|
|
168
|
+
const redactedMessage = this.redact(logMessage);
|
|
169
|
+
|
|
170
|
+
// Console output
|
|
171
|
+
const display = data && Object.keys(data).length > 0
|
|
172
|
+
? `${redactedMessage} ${this.redact(JSON.stringify(data))}`
|
|
173
|
+
: redactedMessage;
|
|
174
|
+
|
|
175
|
+
console.log(display);
|
|
176
|
+
|
|
177
|
+
// File output
|
|
178
|
+
if (this.logFile) {
|
|
179
|
+
try {
|
|
180
|
+
const fileEntry = JSON.stringify({
|
|
181
|
+
timestamp,
|
|
182
|
+
level,
|
|
183
|
+
message: this.redact(message),
|
|
184
|
+
context: this.context,
|
|
185
|
+
data: this.redact(JSON.stringify(data))
|
|
186
|
+
}) + '\n';
|
|
187
|
+
|
|
188
|
+
appendFileSync(this.logFile, fileEntry);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error('Failed to write to log file:', error.message);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Parse log level string to numeric value
|
|
197
|
+
*/
|
|
198
|
+
_parseLogLevel(levelStr) {
|
|
199
|
+
const upper = levelStr.toUpperCase();
|
|
200
|
+
return LOG_LEVELS[upper] ?? LOG_LEVELS.INFO;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Export singleton logger
|
|
206
|
+
*/
|
|
207
|
+
export const logger = new Logger({ framework: 'clodo' });
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Backwards compatible factory function (for gradual migration)
|
|
211
|
+
*/
|
|
212
|
+
export const createLogger = (prefix = 'ClodoFramework') => {
|
|
213
|
+
return new Logger({ prefix });
|
|
214
|
+
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { writeFile, appendFile, mkdir } from 'fs/promises';
|
|
7
7
|
import { join } from 'path';
|
|
8
|
+
import { logger } from '../logging/Logger.js';
|
|
8
9
|
|
|
9
10
|
export class ProductionMonitor {
|
|
10
11
|
constructor(options = {}) {
|
|
@@ -104,18 +105,29 @@ export class ProductionMonitor {
|
|
|
104
105
|
}
|
|
105
106
|
};
|
|
106
107
|
|
|
107
|
-
//
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
// Use Logger for output
|
|
109
|
+
const methodMap = {
|
|
110
|
+
debug: 'debug',
|
|
111
|
+
info: 'info',
|
|
112
|
+
warn: 'warn',
|
|
113
|
+
error: 'error',
|
|
114
|
+
fatal: 'fatal'
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const logMethod = methodMap[level] || 'info';
|
|
118
|
+
logger[logMethod](message, data);
|
|
111
119
|
|
|
112
|
-
// File logging
|
|
120
|
+
// File logging through Logger
|
|
121
|
+
if (!this.config.logDir) {
|
|
122
|
+
this.config.logDir = 'logs';
|
|
123
|
+
}
|
|
113
124
|
try {
|
|
114
125
|
const logFile = join(this.config.logDir, `${new Date().toISOString().split('T')[0]}.log`);
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
if (!logger.logFile) {
|
|
127
|
+
logger.setLogFile(logFile);
|
|
128
|
+
}
|
|
117
129
|
} catch (error) {
|
|
118
|
-
|
|
130
|
+
logger.error('Failed to set up log file', { error: error.message });
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
// Track errors for metrics
|
|
@@ -354,7 +366,7 @@ export class ProductionMonitor {
|
|
|
354
366
|
const metricsData = this.getDetailedMetrics();
|
|
355
367
|
await writeFile(metricsFile, JSON.stringify(metricsData, null, 2));
|
|
356
368
|
} catch (error) {
|
|
357
|
-
|
|
369
|
+
logger.error('Failed to save metrics', { error: error.message });
|
|
358
370
|
}
|
|
359
371
|
}
|
|
360
372
|
|