@edgible-team/cli 1.0.1
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/LICENSE +136 -0
- package/README.md +450 -0
- package/dist/client/api-client.js +1057 -0
- package/dist/client/index.js +21 -0
- package/dist/commands/agent.js +1280 -0
- package/dist/commands/ai.js +608 -0
- package/dist/commands/application.js +885 -0
- package/dist/commands/auth.js +570 -0
- package/dist/commands/base/BaseCommand.js +93 -0
- package/dist/commands/base/CommandHandler.js +7 -0
- package/dist/commands/base/command-wrapper.js +58 -0
- package/dist/commands/base/middleware.js +77 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/connectivity.js +59 -0
- package/dist/commands/debug.js +98 -0
- package/dist/commands/discover.js +144 -0
- package/dist/commands/examples/migrated-command-example.js +180 -0
- package/dist/commands/gateway.js +494 -0
- package/dist/commands/managedGateway.js +787 -0
- package/dist/commands/utils/config-validator.js +76 -0
- package/dist/commands/utils/gateway-prompt.js +79 -0
- package/dist/commands/utils/input-parser.js +120 -0
- package/dist/commands/utils/output-formatter.js +109 -0
- package/dist/config/app-config.js +99 -0
- package/dist/detection/SystemCapabilityDetector.js +1244 -0
- package/dist/detection/ToolDetector.js +305 -0
- package/dist/detection/WorkloadDetector.js +314 -0
- package/dist/di/bindings.js +99 -0
- package/dist/di/container.js +88 -0
- package/dist/di/types.js +32 -0
- package/dist/index.js +52 -0
- package/dist/interfaces/IDaemonManager.js +3 -0
- package/dist/repositories/config-repository.js +62 -0
- package/dist/repositories/gateway-repository.js +35 -0
- package/dist/scripts/postinstall.js +101 -0
- package/dist/services/AgentStatusManager.js +299 -0
- package/dist/services/ConnectivityTester.js +271 -0
- package/dist/services/DependencyInstaller.js +475 -0
- package/dist/services/LocalAgentManager.js +2216 -0
- package/dist/services/application/ApplicationService.js +299 -0
- package/dist/services/auth/AuthService.js +214 -0
- package/dist/services/aws.js +644 -0
- package/dist/services/daemon/DaemonManagerFactory.js +65 -0
- package/dist/services/daemon/DockerDaemonManager.js +395 -0
- package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
- package/dist/services/daemon/PodmanDaemonManager.js +369 -0
- package/dist/services/daemon/SystemdDaemonManager.js +221 -0
- package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
- package/dist/services/daemon/index.js +16 -0
- package/dist/services/edgible.js +3060 -0
- package/dist/services/gateway/GatewayService.js +334 -0
- package/dist/state/config.js +146 -0
- package/dist/types/AgentConfig.js +5 -0
- package/dist/types/AgentStatus.js +5 -0
- package/dist/types/ApiClient.js +5 -0
- package/dist/types/ApiRequests.js +5 -0
- package/dist/types/ApiResponses.js +5 -0
- package/dist/types/Application.js +5 -0
- package/dist/types/CaddyJson.js +5 -0
- package/dist/types/UnifiedAgentStatus.js +56 -0
- package/dist/types/WireGuard.js +5 -0
- package/dist/types/Workload.js +5 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/command-options.js +5 -0
- package/dist/types/connectivity.js +5 -0
- package/dist/types/errors.js +250 -0
- package/dist/types/gateway-types.js +5 -0
- package/dist/types/index.js +48 -0
- package/dist/types/models/ApplicationData.js +5 -0
- package/dist/types/models/CertificateData.js +5 -0
- package/dist/types/models/DeviceData.js +5 -0
- package/dist/types/models/DevicePoolData.js +5 -0
- package/dist/types/models/OrganizationData.js +5 -0
- package/dist/types/models/OrganizationInviteData.js +5 -0
- package/dist/types/models/ProviderConfiguration.js +5 -0
- package/dist/types/models/ResourceData.js +5 -0
- package/dist/types/models/ServiceResourceData.js +5 -0
- package/dist/types/models/UserData.js +5 -0
- package/dist/types/route.js +5 -0
- package/dist/types/validation/schemas.js +218 -0
- package/dist/types/validation.js +5 -0
- package/dist/utils/FileIntegrityManager.js +256 -0
- package/dist/utils/PathMigration.js +219 -0
- package/dist/utils/PathResolver.js +235 -0
- package/dist/utils/PlatformDetector.js +277 -0
- package/dist/utils/console-logger.js +130 -0
- package/dist/utils/docker-compose-parser.js +179 -0
- package/dist/utils/errors.js +130 -0
- package/dist/utils/health-checker.js +155 -0
- package/dist/utils/json-logger.js +72 -0
- package/dist/utils/log-formatter.js +293 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/network-utils.js +217 -0
- package/dist/utils/output.js +182 -0
- package/dist/utils/passwordValidation.js +91 -0
- package/dist/utils/progress.js +167 -0
- package/dist/utils/sudo-checker.js +22 -0
- package/dist/utils/urls.js +32 -0
- package/dist/utils/validation.js +31 -0
- package/dist/validation/schemas.js +175 -0
- package/dist/validation/validator.js +67 -0
- package/package.json +83 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility to parse docker-compose.yml files and extract container/port information
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.parseDockerComposeFile = parseDockerComposeFile;
|
|
40
|
+
exports.findDockerComposeFile = findDockerComposeFile;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const yaml = __importStar(require("js-yaml"));
|
|
44
|
+
/**
|
|
45
|
+
* Parse a docker-compose file and extract container and port information
|
|
46
|
+
* @param filePath Path to the docker-compose.yml or docker-compose.yaml file
|
|
47
|
+
* @returns Structured data containing containers and their ports
|
|
48
|
+
*/
|
|
49
|
+
function parseDockerComposeFile(filePath) {
|
|
50
|
+
if (!fs.existsSync(filePath)) {
|
|
51
|
+
throw new Error(`Docker compose file not found: ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
54
|
+
let composeData;
|
|
55
|
+
try {
|
|
56
|
+
composeData = yaml.load(fileContent);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new Error(`Failed to parse docker-compose file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
60
|
+
}
|
|
61
|
+
if (!composeData || typeof composeData !== 'object') {
|
|
62
|
+
throw new Error('Invalid docker-compose file: root must be an object');
|
|
63
|
+
}
|
|
64
|
+
const services = composeData.services;
|
|
65
|
+
if (!services || typeof services !== 'object') {
|
|
66
|
+
throw new Error('Invalid docker-compose file: no services found');
|
|
67
|
+
}
|
|
68
|
+
const containers = [];
|
|
69
|
+
for (const [serviceName, serviceConfig] of Object.entries(services)) {
|
|
70
|
+
if (!serviceConfig || typeof serviceConfig !== 'object') {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const config = serviceConfig;
|
|
74
|
+
const ports = [];
|
|
75
|
+
// Parse ports from the ports array
|
|
76
|
+
if (Array.isArray(config.ports)) {
|
|
77
|
+
for (const portMapping of config.ports) {
|
|
78
|
+
const parsed = parsePortMapping(portMapping);
|
|
79
|
+
if (parsed) {
|
|
80
|
+
ports.push(parsed);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (typeof config.ports === 'string') {
|
|
85
|
+
// Single port mapping as string
|
|
86
|
+
const parsed = parsePortMapping(config.ports);
|
|
87
|
+
if (parsed) {
|
|
88
|
+
ports.push(parsed);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Also check expose array (internal ports, but we'll include them)
|
|
92
|
+
if (Array.isArray(config.expose)) {
|
|
93
|
+
for (const exposedPort of config.expose) {
|
|
94
|
+
const portStr = String(exposedPort);
|
|
95
|
+
const match = portStr.match(/^(\d+)(?:\/(tcp|udp))?$/);
|
|
96
|
+
if (match) {
|
|
97
|
+
const containerPort = parseInt(match[1], 10);
|
|
98
|
+
const protocol = match[2] || 'tcp';
|
|
99
|
+
// For expose, we use the container port as the host port
|
|
100
|
+
ports.push({
|
|
101
|
+
host: containerPort,
|
|
102
|
+
container: containerPort,
|
|
103
|
+
protocol,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (ports.length > 0) {
|
|
109
|
+
containers.push({
|
|
110
|
+
name: serviceName,
|
|
111
|
+
ports,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { containers };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parse a port mapping string or object
|
|
119
|
+
* Supports formats:
|
|
120
|
+
* - "8080:80"
|
|
121
|
+
* - "8080:80/tcp"
|
|
122
|
+
* - "127.0.0.1:8080:80"
|
|
123
|
+
* - "127.0.0.1:8080:80/tcp"
|
|
124
|
+
* - { target: 80, published: 8080, protocol: 'tcp' }
|
|
125
|
+
*/
|
|
126
|
+
function parsePortMapping(portMapping) {
|
|
127
|
+
if (typeof portMapping === 'object' && portMapping !== null) {
|
|
128
|
+
// Docker Compose v3+ format: { target: 80, published: 8080, protocol: 'tcp' }
|
|
129
|
+
const mapping = portMapping;
|
|
130
|
+
if (typeof mapping.target === 'number' && typeof mapping.published === 'number') {
|
|
131
|
+
return {
|
|
132
|
+
host: mapping.published,
|
|
133
|
+
container: mapping.target,
|
|
134
|
+
protocol: mapping.protocol || 'tcp',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const portStr = String(portMapping);
|
|
140
|
+
// Match formats: "8080:80", "8080:80/tcp", "127.0.0.1:8080:80", "127.0.0.1:8080:80/tcp"
|
|
141
|
+
// Pattern: [IP:]HOST_PORT:CONTAINER_PORT[/PROTOCOL]
|
|
142
|
+
const match = portStr.match(/^(?:(\d+\.\d+\.\d+\.\d+):)?(\d+):(\d+)(?:\/(tcp|udp))?$/);
|
|
143
|
+
if (match) {
|
|
144
|
+
const hostPort = parseInt(match[2], 10);
|
|
145
|
+
const containerPort = parseInt(match[3], 10);
|
|
146
|
+
const protocol = match[4] || 'tcp';
|
|
147
|
+
return {
|
|
148
|
+
host: hostPort,
|
|
149
|
+
container: containerPort,
|
|
150
|
+
protocol,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Find docker-compose file in the current directory or specified path
|
|
157
|
+
* @param providedPath Optional path provided by user
|
|
158
|
+
* @returns Path to docker-compose file or null if not found
|
|
159
|
+
*/
|
|
160
|
+
function findDockerComposeFile(providedPath) {
|
|
161
|
+
if (providedPath) {
|
|
162
|
+
if (fs.existsSync(providedPath)) {
|
|
163
|
+
return providedPath;
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`Docker compose file not found at: ${providedPath}`);
|
|
166
|
+
}
|
|
167
|
+
const currentDir = process.cwd();
|
|
168
|
+
const possibleFiles = [
|
|
169
|
+
path.join(currentDir, 'docker-compose.yml'),
|
|
170
|
+
path.join(currentDir, 'docker-compose.yaml'),
|
|
171
|
+
];
|
|
172
|
+
for (const filePath of possibleFiles) {
|
|
173
|
+
if (fs.existsSync(filePath)) {
|
|
174
|
+
return filePath;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=docker-compose-parser.js.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ErrorHandler = exports.NetworkError = exports.ConfigError = exports.ValidationError = exports.CLIError = exports.ExitCode = void 0;
|
|
7
|
+
exports.withErrorHandling = withErrorHandling;
|
|
8
|
+
exports.wrapCommandAction = wrapCommandAction;
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
/**
|
|
11
|
+
* Exit codes following Unix conventions
|
|
12
|
+
*/
|
|
13
|
+
var ExitCode;
|
|
14
|
+
(function (ExitCode) {
|
|
15
|
+
ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS";
|
|
16
|
+
ExitCode[ExitCode["ERROR"] = 1] = "ERROR";
|
|
17
|
+
ExitCode[ExitCode["USAGE_ERROR"] = 2] = "USAGE_ERROR";
|
|
18
|
+
ExitCode[ExitCode["CONFIG_ERROR"] = 3] = "CONFIG_ERROR";
|
|
19
|
+
ExitCode[ExitCode["NETWORK_ERROR"] = 4] = "NETWORK_ERROR";
|
|
20
|
+
})(ExitCode || (exports.ExitCode = ExitCode = {}));
|
|
21
|
+
/**
|
|
22
|
+
* CLI Error classes for better error handling
|
|
23
|
+
*/
|
|
24
|
+
class CLIError extends Error {
|
|
25
|
+
constructor(message, exitCode = ExitCode.ERROR, showHelp = false) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.exitCode = exitCode;
|
|
28
|
+
this.showHelp = showHelp;
|
|
29
|
+
this.name = 'CLIError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.CLIError = CLIError;
|
|
33
|
+
class ValidationError extends CLIError {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message, ExitCode.USAGE_ERROR);
|
|
36
|
+
this.name = 'ValidationError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.ValidationError = ValidationError;
|
|
40
|
+
class ConfigError extends CLIError {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super(message, ExitCode.CONFIG_ERROR);
|
|
43
|
+
this.name = 'ConfigError';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.ConfigError = ConfigError;
|
|
47
|
+
class NetworkError extends CLIError {
|
|
48
|
+
constructor(message) {
|
|
49
|
+
super(message, ExitCode.NETWORK_ERROR);
|
|
50
|
+
this.name = 'NetworkError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.NetworkError = NetworkError;
|
|
54
|
+
/**
|
|
55
|
+
* Centralized error handler with consistent output
|
|
56
|
+
*/
|
|
57
|
+
class ErrorHandler {
|
|
58
|
+
/**
|
|
59
|
+
* Configure color output
|
|
60
|
+
*/
|
|
61
|
+
static setColorEnabled(enabled) {
|
|
62
|
+
this.colorEnabled = enabled ? chalk_1.default.red : (text) => text;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Handle and display error with consistent format
|
|
66
|
+
*/
|
|
67
|
+
static handleError(error) {
|
|
68
|
+
if (error instanceof CLIError) {
|
|
69
|
+
console.error(this.colorEnabled('Error:'), error.message);
|
|
70
|
+
if (error.showHelp) {
|
|
71
|
+
console.error('\nUse --help for usage information.');
|
|
72
|
+
}
|
|
73
|
+
process.exit(error.exitCode);
|
|
74
|
+
}
|
|
75
|
+
else if (error instanceof Error) {
|
|
76
|
+
console.error(this.colorEnabled('Error:'), error.message);
|
|
77
|
+
process.exit(ExitCode.ERROR);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.error(this.colorEnabled('Error:'), 'An unexpected error occurred');
|
|
81
|
+
process.exit(ExitCode.ERROR);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Display validation error
|
|
86
|
+
*/
|
|
87
|
+
static handleValidationError(message) {
|
|
88
|
+
console.error(this.colorEnabled('Validation Error:'), message);
|
|
89
|
+
process.exit(ExitCode.USAGE_ERROR);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Display network error
|
|
93
|
+
*/
|
|
94
|
+
static handleNetworkError(message) {
|
|
95
|
+
console.error(this.colorEnabled('Network Error:'), message);
|
|
96
|
+
console.error(chalk_1.default.gray('Please check your internet connection and try again.'));
|
|
97
|
+
process.exit(ExitCode.NETWORK_ERROR);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Display configuration error
|
|
101
|
+
*/
|
|
102
|
+
static handleConfigError(message) {
|
|
103
|
+
console.error(this.colorEnabled('Configuration Error:'), message);
|
|
104
|
+
console.error(chalk_1.default.gray('Use "edgible config reset" to reset your configuration.'));
|
|
105
|
+
process.exit(ExitCode.CONFIG_ERROR);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.ErrorHandler = ErrorHandler;
|
|
109
|
+
ErrorHandler.colorEnabled = (text) => chalk_1.default.red(text);
|
|
110
|
+
/**
|
|
111
|
+
* Wrapper for async command handlers with error handling
|
|
112
|
+
*/
|
|
113
|
+
function withErrorHandling(handler) {
|
|
114
|
+
return async (...args) => {
|
|
115
|
+
try {
|
|
116
|
+
await handler(...args);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
ErrorHandler.handleError(error);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Wrap a command action with standardized error handling
|
|
125
|
+
* Use this for all command actions to ensure consistent error handling
|
|
126
|
+
*/
|
|
127
|
+
function wrapCommandAction(action) {
|
|
128
|
+
return withErrorHandling(action);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HealthChecker = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
class HealthChecker {
|
|
9
|
+
/**
|
|
10
|
+
* Perform HTTP health check
|
|
11
|
+
*/
|
|
12
|
+
static async checkHttpHealth(url, config, options = {}) {
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
const timeout = options.timeout || config.timeout * 1000 || 5000;
|
|
15
|
+
try {
|
|
16
|
+
const response = await (0, axios_1.default)({
|
|
17
|
+
method: 'GET',
|
|
18
|
+
url,
|
|
19
|
+
timeout,
|
|
20
|
+
headers: {
|
|
21
|
+
'User-Agent': 'Edgible-CLI-HealthCheck/1.0',
|
|
22
|
+
...config.headers,
|
|
23
|
+
...options.headers
|
|
24
|
+
},
|
|
25
|
+
validateStatus: () => true, // Don't throw on any status code
|
|
26
|
+
maxRedirects: options.followRedirects ? 5 : 0
|
|
27
|
+
});
|
|
28
|
+
const responseTime = Date.now() - startTime;
|
|
29
|
+
const success = this.evaluateHealthResponse(response, config, options);
|
|
30
|
+
return {
|
|
31
|
+
success,
|
|
32
|
+
url,
|
|
33
|
+
method: 'GET',
|
|
34
|
+
statusCode: response.status,
|
|
35
|
+
responseTime,
|
|
36
|
+
latency: responseTime,
|
|
37
|
+
headers: response.headers,
|
|
38
|
+
body: response.data,
|
|
39
|
+
host: new URL(url).hostname,
|
|
40
|
+
port: parseInt(new URL(url).port) || (url.startsWith('https') ? 443 : 80),
|
|
41
|
+
protocol: url.startsWith('https') ? 'tcp' : 'tcp',
|
|
42
|
+
timestamp: new Date(),
|
|
43
|
+
error: success ? undefined : this.generateHealthErrorMessage(response, config, options)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const responseTime = Date.now() - startTime;
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
url,
|
|
51
|
+
method: 'GET',
|
|
52
|
+
host: new URL(url).hostname,
|
|
53
|
+
port: parseInt(new URL(url).port) || (url.startsWith('https') ? 443 : 80),
|
|
54
|
+
protocol: 'tcp',
|
|
55
|
+
timestamp: new Date(),
|
|
56
|
+
latency: responseTime,
|
|
57
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Evaluate HTTP response for health check
|
|
63
|
+
*/
|
|
64
|
+
static evaluateHealthResponse(response, config, options) {
|
|
65
|
+
// Check status code
|
|
66
|
+
const expectedStatus = options.expectedStatus || config.expectedStatus;
|
|
67
|
+
if (expectedStatus && response.status !== expectedStatus) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Check for successful status codes (2xx)
|
|
71
|
+
if (!expectedStatus && response.status < 200 || response.status >= 300) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Check response content
|
|
75
|
+
const expectedContent = options.expectedContent || config.expectedContent;
|
|
76
|
+
if (expectedContent && response.data) {
|
|
77
|
+
const body = typeof response.data === 'string' ? response.data : JSON.stringify(response.data);
|
|
78
|
+
if (!body.includes(expectedContent)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate error message for failed health check
|
|
86
|
+
*/
|
|
87
|
+
static generateHealthErrorMessage(response, config, options) {
|
|
88
|
+
const expectedStatus = options.expectedStatus || config.expectedStatus;
|
|
89
|
+
const expectedContent = options.expectedContent || config.expectedContent;
|
|
90
|
+
if (expectedStatus && response.status !== expectedStatus) {
|
|
91
|
+
return `Expected status ${expectedStatus}, got ${response.status}`;
|
|
92
|
+
}
|
|
93
|
+
if (expectedContent && response.data) {
|
|
94
|
+
const body = typeof response.data === 'string' ? response.data : JSON.stringify(response.data);
|
|
95
|
+
if (!body.includes(expectedContent)) {
|
|
96
|
+
return `Expected content "${expectedContent}" not found in response`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (response.status >= 400) {
|
|
100
|
+
return `HTTP error: ${response.status} ${response.statusText}`;
|
|
101
|
+
}
|
|
102
|
+
return 'Health check failed';
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Perform multiple health checks with retries
|
|
106
|
+
*/
|
|
107
|
+
static async performHealthChecks(url, config, options = {}) {
|
|
108
|
+
const retries = options.retries || config.retries || 3;
|
|
109
|
+
const results = [];
|
|
110
|
+
for (let i = 0; i < retries; i++) {
|
|
111
|
+
const result = await this.checkHttpHealth(url, config, options);
|
|
112
|
+
results.push(result);
|
|
113
|
+
if (result.success) {
|
|
114
|
+
break; // Stop on first success
|
|
115
|
+
}
|
|
116
|
+
// Wait before retry (exponential backoff)
|
|
117
|
+
if (i < retries - 1) {
|
|
118
|
+
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if a service is responding to basic connectivity
|
|
125
|
+
*/
|
|
126
|
+
static async checkBasicConnectivity(host, port, protocol = 'http') {
|
|
127
|
+
try {
|
|
128
|
+
const url = `${protocol}://${host}:${port}`;
|
|
129
|
+
const response = await axios_1.default.get(url, { timeout: 5000 });
|
|
130
|
+
return response.status >= 200 && response.status < 500;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Generate health check summary
|
|
138
|
+
*/
|
|
139
|
+
static generateHealthSummary(results) {
|
|
140
|
+
const successful = results.filter(r => r.success);
|
|
141
|
+
const successRate = results.length > 0 ? (successful.length / results.length) * 100 : 0;
|
|
142
|
+
const averageResponseTime = successful.length > 0
|
|
143
|
+
? successful.reduce((sum, r) => sum + (r.responseTime || 0), 0) / successful.length
|
|
144
|
+
: 0;
|
|
145
|
+
const errors = results.filter(r => !r.success).map(r => r.error || 'Unknown error');
|
|
146
|
+
return {
|
|
147
|
+
overall: successRate === 100,
|
|
148
|
+
successRate,
|
|
149
|
+
averageResponseTime,
|
|
150
|
+
errors
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.HealthChecker = HealthChecker;
|
|
155
|
+
//# sourceMappingURL=health-checker.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JsonLogger = void 0;
|
|
4
|
+
const logger_1 = require("./logger");
|
|
5
|
+
/**
|
|
6
|
+
* JSON logger implementation for scripting and automation
|
|
7
|
+
* Outputs structured JSON logs that can be easily parsed
|
|
8
|
+
*/
|
|
9
|
+
class JsonLogger {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.level = logger_1.LogLevel.INFO;
|
|
12
|
+
this.level = options.level ?? logger_1.LogLevel.INFO;
|
|
13
|
+
this.outputStream = options.outputStream ?? process.stderr;
|
|
14
|
+
}
|
|
15
|
+
setLevel(level) {
|
|
16
|
+
this.level = level;
|
|
17
|
+
}
|
|
18
|
+
getLevel() {
|
|
19
|
+
return this.level;
|
|
20
|
+
}
|
|
21
|
+
debug(message, ...args) {
|
|
22
|
+
if (this.shouldLog(logger_1.LogLevel.DEBUG)) {
|
|
23
|
+
this.log(logger_1.LogLevel.DEBUG, message, args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
info(message, ...args) {
|
|
27
|
+
if (this.shouldLog(logger_1.LogLevel.INFO)) {
|
|
28
|
+
this.log(logger_1.LogLevel.INFO, message, args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
warn(message, ...args) {
|
|
32
|
+
if (this.shouldLog(logger_1.LogLevel.WARN)) {
|
|
33
|
+
this.log(logger_1.LogLevel.WARN, message, args);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
error(message, ...args) {
|
|
37
|
+
if (this.shouldLog(logger_1.LogLevel.ERROR)) {
|
|
38
|
+
this.log(logger_1.LogLevel.ERROR, message, args);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
shouldLog(level) {
|
|
42
|
+
return level >= this.level;
|
|
43
|
+
}
|
|
44
|
+
log(level, message, args) {
|
|
45
|
+
const entry = {
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
level: this.getLevelName(level),
|
|
48
|
+
message,
|
|
49
|
+
...(args.length > 0 && { data: args.length === 1 ? args[0] : args }),
|
|
50
|
+
};
|
|
51
|
+
const json = JSON.stringify(entry);
|
|
52
|
+
this.outputStream.write(json + '\n');
|
|
53
|
+
}
|
|
54
|
+
getLevelName(level) {
|
|
55
|
+
switch (level) {
|
|
56
|
+
case logger_1.LogLevel.DEBUG:
|
|
57
|
+
return 'DEBUG';
|
|
58
|
+
case logger_1.LogLevel.INFO:
|
|
59
|
+
return 'INFO';
|
|
60
|
+
case logger_1.LogLevel.WARN:
|
|
61
|
+
return 'WARN';
|
|
62
|
+
case logger_1.LogLevel.ERROR:
|
|
63
|
+
return 'ERROR';
|
|
64
|
+
case logger_1.LogLevel.NONE:
|
|
65
|
+
return 'NONE';
|
|
66
|
+
default:
|
|
67
|
+
return 'INFO';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.JsonLogger = JsonLogger;
|
|
72
|
+
//# sourceMappingURL=json-logger.js.map
|