@uofx/cli 1.0.0
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 +40 -0
- package/README.md +444 -0
- package/THIRD-PARTY-NOTICES.txt +894 -0
- package/dist/application/dtos/index.js +24 -0
- package/dist/application/dtos/request/delete-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/get-config.request.dto.js +3 -0
- package/dist/application/dtos/request/get-credentials.request.dto.js +3 -0
- package/dist/application/dtos/request/index.js +27 -0
- package/dist/application/dtos/request/install-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/list-charts.request.dto.js +3 -0
- package/dist/application/dtos/request/set-config.request.dto.js +16 -0
- package/dist/application/dtos/request/setup-environment.request.dto.js +16 -0
- package/dist/application/dtos/request/show-logs.request.dto.js +19 -0
- package/dist/application/dtos/request/start-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/stop-instance.request.dto.js +3 -0
- package/dist/application/dtos/response/credentials.response.dto.js +3 -0
- package/dist/application/dtos/response/delete-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/index.js +26 -0
- package/dist/application/dtos/response/install-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/instance-list.response.dto.js +3 -0
- package/dist/application/dtos/response/instance-status.response.dto.js +3 -0
- package/dist/application/dtos/response/setup-result.response.dto.js +3 -0
- package/dist/application/dtos/response/show-logs.response.dto.js +3 -0
- package/dist/application/dtos/response/start-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/stop-instance.response.dto.js +3 -0
- package/dist/application/index.js +25 -0
- package/dist/application/interfaces/index.js +24 -0
- package/dist/application/interfaces/use-case.interface.js +3 -0
- package/dist/application/use-cases/config/get-config.use-case.js +66 -0
- package/dist/application/use-cases/config/set-config.use-case.js +49 -0
- package/dist/application/use-cases/credentials/get-credentials.use-case.js +57 -0
- package/dist/application/use-cases/index.js +28 -0
- package/dist/application/use-cases/instance/delete-instance.use-case.js +81 -0
- package/dist/application/use-cases/instance/index.js +23 -0
- package/dist/application/use-cases/instance/install-instance.use-case.js +424 -0
- package/dist/application/use-cases/instance/list-charts.use-case.js +43 -0
- package/dist/application/use-cases/instance/list-instances.use-case.js +62 -0
- package/dist/application/use-cases/instance/start-instance.use-case.js +154 -0
- package/dist/application/use-cases/instance/stop-instance.use-case.js +55 -0
- package/dist/application/use-cases/logs/show-logs.use-case.js +66 -0
- package/dist/application/use-cases/setup/setup-environment.use-case.js +53 -0
- package/dist/cli.js +286 -0
- package/dist/constants/config-defaults.js +23 -0
- package/dist/constants/defaults.js +89 -0
- package/dist/constants/deployment.js +39 -0
- package/dist/constants/environments.js +93 -0
- package/dist/constants/index.js +53 -0
- package/dist/constants/oci-artifacts.js +25 -0
- package/dist/constants/paths.js +92 -0
- package/dist/constants/timeouts.js +60 -0
- package/dist/di/container.js +34 -0
- package/dist/di/index.js +22 -0
- package/dist/di/modules/application.module.js +54 -0
- package/dist/di/modules/infrastructure.module.js +206 -0
- package/dist/di/modules/interceptor.module.js +68 -0
- package/dist/di/modules/presentation.module.js +31 -0
- package/dist/di/tokens.js +149 -0
- package/dist/domain/decorators/sensitive.decorator.js +39 -0
- package/dist/domain/entities/credentials-resolver.entity.js +127 -0
- package/dist/domain/entities/credentials.entity.js +65 -0
- package/dist/domain/entities/delete-instance-validation.entity.js +100 -0
- package/dist/domain/entities/deployment-parameters.entity.js +120 -0
- package/dist/domain/entities/environment-validation.entity.js +125 -0
- package/dist/domain/entities/index.js +29 -0
- package/dist/domain/entities/instance-lifecycle-state.entity.js +100 -0
- package/dist/domain/entities/instance-list-aggregator.entity.js +104 -0
- package/dist/domain/entities/instance-metadata.entity.js +86 -0
- package/dist/domain/entities/instance-status.entity.js +79 -0
- package/dist/domain/entities/instance.entity.js +128 -0
- package/dist/domain/entities/log-filter.entity.js +141 -0
- package/dist/domain/index.js +29 -0
- package/dist/domain/interfaces/safe-loggable.interface.js +3 -0
- package/dist/domain/ports/app-config.port.js +3 -0
- package/dist/domain/ports/base-image.port.js +3 -0
- package/dist/domain/ports/chart-version.port.js +3 -0
- package/dist/domain/ports/correlation-id.port.js +3 -0
- package/dist/domain/ports/credentials.port.js +3 -0
- package/dist/domain/ports/deployment.port.js +3 -0
- package/dist/domain/ports/error-handler.port.js +3 -0
- package/dist/domain/ports/index.js +46 -0
- package/dist/domain/ports/instance-manager.port.js +3 -0
- package/dist/domain/ports/instance-metadata.port.js +8 -0
- package/dist/domain/ports/instance-storage.port.js +3 -0
- package/dist/domain/ports/k8s-deployer.port.js +3 -0
- package/dist/domain/ports/logger.port.js +14 -0
- package/dist/domain/ports/output.port.js +3 -0
- package/dist/domain/ports/runtime-environment.port.js +6 -0
- package/dist/domain/ports/user-interaction.port.js +9 -0
- package/dist/domain/ports/user-settings.port.js +3 -0
- package/dist/domain/types/index.js +22 -0
- package/dist/domain/types/logger.types.js +29 -0
- package/dist/domain/types/validation.types.js +9 -0
- package/dist/domain/value-objects/acr-credentials.value-object.js +92 -0
- package/dist/domain/value-objects/chart-version.value-object.js +124 -0
- package/dist/domain/value-objects/config-log-level.value-object.js +84 -0
- package/dist/domain/value-objects/connection-info.value-object.js +65 -0
- package/dist/domain/value-objects/index.js +25 -0
- package/dist/domain/value-objects/instance-name.value-object.js +91 -0
- package/dist/domain/value-objects/jwt-key.value-object.js +97 -0
- package/dist/domain/value-objects/mssql-password.value-object.js +140 -0
- package/dist/domain/value-objects/rsa-key-pair.value-object.js +181 -0
- package/dist/index.js +6 -0
- package/dist/infrastructure/config/app-config.interface.js +3 -0
- package/dist/infrastructure/config/app-config.service.js +280 -0
- package/dist/infrastructure/config/config-validator.js +31 -0
- package/dist/infrastructure/config/crypto.service.js +125 -0
- package/dist/infrastructure/deployment/deployment.adapter.js +118 -0
- package/dist/infrastructure/deployment/interfaces/acr-credential-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/app-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/helm-registry.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/infra-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/k8s-job-runner.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-database-init.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-helm-deployment.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-storage.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-user-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/oci-artifact.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/secret-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/service-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/version-compatibility.interface.js +3 -0
- package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +144 -0
- package/dist/infrastructure/deployment/services/app-manager.service.js +193 -0
- package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +163 -0
- package/dist/infrastructure/deployment/services/helm-registry.service.js +126 -0
- package/dist/infrastructure/deployment/services/infra-manager.service.js +130 -0
- package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +194 -0
- package/dist/infrastructure/deployment/services/mssql-database-init.service.js +139 -0
- package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +100 -0
- package/dist/infrastructure/deployment/services/mssql-storage.service.js +54 -0
- package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +66 -0
- package/dist/infrastructure/deployment/services/oci-artifact.service.js +289 -0
- package/dist/infrastructure/deployment/services/secret-manager.service.js +179 -0
- package/dist/infrastructure/deployment/services/service-manager.service.js +82 -0
- package/dist/infrastructure/deployment/services/version-compatibility.service.js +291 -0
- package/dist/infrastructure/environment/interfaces/hardware-info.interface.js +3 -0
- package/dist/infrastructure/environment/interfaces/network-checker.interface.js +3 -0
- package/dist/infrastructure/environment/services/hardware-info.service.js +135 -0
- package/dist/infrastructure/environment/services/network-checker.service.js +142 -0
- package/dist/infrastructure/environment/windows-environment.adapter.js +162 -0
- package/dist/infrastructure/errors/app-error.js +73 -0
- package/dist/infrastructure/errors/error-handler.interface.js +3 -0
- package/dist/infrastructure/errors/error-handler.js +218 -0
- package/dist/infrastructure/errors/exit-codes.js +27 -0
- package/dist/infrastructure/errors/index.js +25 -0
- package/dist/infrastructure/execution/builders/base-command.builder.js +122 -0
- package/dist/infrastructure/execution/builders/host-command.builder.js +58 -0
- package/dist/infrastructure/execution/builders/windows-host-command.builder.js +50 -0
- package/dist/infrastructure/execution/builders/wsl-command.builder.js +29 -0
- package/dist/infrastructure/execution/command-builder.js +252 -0
- package/dist/infrastructure/execution/command-executor.service.js +230 -0
- package/dist/infrastructure/execution/environments/wsl-execution.environment.js +70 -0
- package/dist/infrastructure/execution/execution-environment.factory.js +53 -0
- package/dist/infrastructure/execution/index.js +25 -0
- package/dist/infrastructure/execution/interfaces/command-builder.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/command-executor.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/execution-environment-factory.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/execution-environment.interface.js +7 -0
- package/dist/infrastructure/execution/interfaces/host-command-builder.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/index.js +23 -0
- package/dist/infrastructure/execution/interfaces/script-executor.interface.js +3 -0
- package/dist/infrastructure/execution/script-executor.service.js +171 -0
- package/dist/infrastructure/http/http-client.service.js +176 -0
- package/dist/infrastructure/http/index.js +18 -0
- package/dist/infrastructure/http/interfaces/http-client.interface.js +3 -0
- package/dist/infrastructure/interceptors/index.js +8 -0
- package/dist/infrastructure/interceptors/interceptor.factory.js +44 -0
- package/dist/infrastructure/interceptors/interceptor.interface.js +3 -0
- package/dist/infrastructure/interceptors/logging.interceptor.js +171 -0
- package/dist/infrastructure/logger/correlation-id.adapter.js +68 -0
- package/dist/infrastructure/logger/index.js +23 -0
- package/dist/infrastructure/logger/interfaces/index.js +22 -0
- package/dist/infrastructure/logger/interfaces/log-reader.repository.interface.js +7 -0
- package/dist/infrastructure/logger/interfaces/log-writer.repository.interface.js +7 -0
- package/dist/infrastructure/logger/logger.adapter.js +274 -0
- package/dist/infrastructure/logger/services/file-log-reader.repository.js +148 -0
- package/dist/infrastructure/logger/services/file-log-writer.repository.js +307 -0
- package/dist/infrastructure/logger/services/index.js +22 -0
- package/dist/infrastructure/persistence/index.js +25 -0
- package/dist/infrastructure/persistence/instance-metadata.adapter.js +100 -0
- package/dist/infrastructure/persistence/instance-storage.adapter.js +64 -0
- package/dist/infrastructure/persistence/interfaces/config.repository.interface.js +3 -0
- package/dist/infrastructure/persistence/interfaces/index.js +22 -0
- package/dist/infrastructure/persistence/interfaces/instance.repository.interface.js +3 -0
- package/dist/infrastructure/persistence/services/file-system-config.repository.js +168 -0
- package/dist/infrastructure/persistence/services/file-system-instance.repository.js +170 -0
- package/dist/infrastructure/persistence/services/index.js +22 -0
- package/dist/infrastructure/persistence/user-settings.adapter.js +55 -0
- package/dist/infrastructure/platform-detector.js +71 -0
- package/dist/infrastructure/platforms/windows/interfaces/microk8s.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/rootfs-manager.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/windows-features.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/windows-info.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-config.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-info.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-inspection.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-lifecycle.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-naming.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-manager.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-resources.interface.js +8 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-updater.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wslconfig-parser.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/parsers/wsl-version.parser.js +133 -0
- package/dist/infrastructure/platforms/windows/services/microk8s.service.js +168 -0
- package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +336 -0
- package/dist/infrastructure/platforms/windows/services/windows-features.service.js +191 -0
- package/dist/infrastructure/platforms/windows/services/windows-info.service.js +138 -0
- package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +171 -0
- package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +226 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +325 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +442 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-naming.service.js +93 -0
- package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +273 -0
- package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +222 -0
- package/dist/infrastructure/platforms/windows/wsl-base-image.adapter.js +41 -0
- package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +150 -0
- package/dist/infrastructure/utils/error-formatter.util.js +29 -0
- package/dist/infrastructure/utils/file-operations.util.js +201 -0
- package/dist/infrastructure/utils/input-validator.util.js +152 -0
- package/dist/infrastructure/utils/retry.util.js +98 -0
- package/dist/presentation/controllers/config.controller.js +146 -0
- package/dist/presentation/controllers/credentials.controller.js +105 -0
- package/dist/presentation/controllers/index.js +25 -0
- package/dist/presentation/controllers/instance.controller.js +363 -0
- package/dist/presentation/controllers/logs.controller.js +103 -0
- package/dist/presentation/controllers/setup.controller.js +175 -0
- package/dist/presentation/interfaces/cli-options.interface.js +8 -0
- package/dist/presentation/prompts/acr-credentials.prompt.js +76 -0
- package/dist/presentation/prompts/index.js +21 -0
- package/dist/presentation/ui/cli-progress.service.js +193 -0
- package/dist/presentation/ui/constants/output-symbols.js +42 -0
- package/dist/presentation/ui/index.js +27 -0
- package/dist/presentation/ui/interaction.service.js +276 -0
- package/dist/presentation/ui/interfaces/cli-progress.interface.js +9 -0
- package/dist/presentation/ui/interfaces/output-formatter.interface.js +23 -0
- package/dist/presentation/ui/log-level.enum.js +66 -0
- package/dist/presentation/ui/output-builder.service.js +378 -0
- package/dist/presentation/ui/output-formatter.service.js +393 -0
- package/package.json +65 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ScriptExecutorService = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../di/tokens");
|
|
18
|
+
const sensitive_decorator_1 = require("../../domain/decorators/sensitive.decorator");
|
|
19
|
+
const input_validator_util_1 = require("../utils/input-validator.util");
|
|
20
|
+
const errors_1 = require("../errors");
|
|
21
|
+
const index_1 = require("../../constants/index");
|
|
22
|
+
/**
|
|
23
|
+
* ScriptExecutor 類別負責在 WSL 實例中執行 bash 腳本
|
|
24
|
+
*/
|
|
25
|
+
let ScriptExecutorService = class ScriptExecutorService {
|
|
26
|
+
constructor(envFactory, commandExecutor, logger, ociArtifact) {
|
|
27
|
+
this.envFactory = envFactory;
|
|
28
|
+
this.commandExecutor = commandExecutor;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
this.ociArtifact = ociArtifact;
|
|
31
|
+
}
|
|
32
|
+
get hostEnv() {
|
|
33
|
+
return this.envFactory.getHostEnvironment();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 在 WSL 實例中執行 bash 腳本
|
|
37
|
+
* @param instanceName WSL 實例名稱
|
|
38
|
+
* @param scriptPath WSL 實例內的腳本路徑
|
|
39
|
+
* @param timeout 執行超時時間(毫秒),預設 30 分鐘
|
|
40
|
+
* @param showDetail 是否顯示詳細輸出(預設:false)
|
|
41
|
+
* @param progress 進度通知回調
|
|
42
|
+
* @returns 執行結果,包含 exitCode 和 output
|
|
43
|
+
*/
|
|
44
|
+
async executeScript(instanceName, scriptPath, timeout = index_1.TIMEOUTS.SCRIPT_EXECUTION, showDetail = false, output) {
|
|
45
|
+
// 驗證輸入
|
|
46
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
47
|
+
(0, input_validator_util_1.requireValidPath)(scriptPath);
|
|
48
|
+
// 使用 hostEnv.wslCmd 以 root 權限執行腳本
|
|
49
|
+
// 這是系統初始化所必需的,因為需要修改系統配置
|
|
50
|
+
const builder = this.hostEnv.wslCmd('-d')
|
|
51
|
+
.arg(instanceName)
|
|
52
|
+
.arg('-u')
|
|
53
|
+
.arg('root')
|
|
54
|
+
.arg('--')
|
|
55
|
+
.arg('bash')
|
|
56
|
+
.arg(scriptPath);
|
|
57
|
+
// 取得建構好的命令,加上 timeout 選項執行
|
|
58
|
+
const command = builder.buildActual();
|
|
59
|
+
let scriptOutput = '';
|
|
60
|
+
try {
|
|
61
|
+
const result = await this.commandExecutor.exec(command, (stdout) => {
|
|
62
|
+
scriptOutput += stdout;
|
|
63
|
+
if (showDetail) {
|
|
64
|
+
// 詳細模式:記錄所有輸出到 debug log
|
|
65
|
+
this.logger.debug('Script stdout', { output: stdout });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// 只記錄符合 "Step {num}: {desc}" 格式的行到 debug log
|
|
69
|
+
const lines = stdout.split('\n');
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
if (/^Step \d+:/.test(line.trim())) {
|
|
72
|
+
this.logger.debug('Script step', { step: line.trim() });
|
|
73
|
+
output?.indent().item('arrow', line.trim()).outdent().flush();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}, (stderr) => {
|
|
78
|
+
scriptOutput += stderr;
|
|
79
|
+
if (showDetail && stderr.trim()) {
|
|
80
|
+
// 記錄 stderr 到 error log
|
|
81
|
+
this.logger.error(new Error(`Script stderr: ${stderr}`));
|
|
82
|
+
}
|
|
83
|
+
}, { timeout, shell: true }, builder.buildSafe());
|
|
84
|
+
return { exitCode: result.exitCode, output: scriptOutput };
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// 如果是超時錯誤
|
|
88
|
+
if (error instanceof Error && error.message.includes('timeout')) {
|
|
89
|
+
throw new errors_1.AppError('Script execution timed out (30 minutes)', {
|
|
90
|
+
exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
|
|
91
|
+
solution: `Check WSL instance logs: wsl -d ${instanceName} -- journalctl -xe`,
|
|
92
|
+
cause: error,
|
|
93
|
+
context: { instanceName, scriptPath }
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 執行系統初始化腳本的完整流程
|
|
101
|
+
* @param instanceName WSL 實例名稱
|
|
102
|
+
* @param params 部署參數(包含 ACR 憑證)
|
|
103
|
+
* @param environmentVersion ubuntu-environment 版本(如 "1.26.100")
|
|
104
|
+
* @param output 輸出介面
|
|
105
|
+
*/
|
|
106
|
+
async runSystemInitialization(instanceName, params, environmentVersion, output) {
|
|
107
|
+
const remoteScriptPath = '/tmp/environment.sh';
|
|
108
|
+
// Step 1: 從 OCI registry 下載 ubuntu-environment 腳本到 WSL
|
|
109
|
+
output?.indent().item('arrow', `Downloading ubuntu-environment v${environmentVersion}...`).outdent().flush();
|
|
110
|
+
this.logger.info('Downloading environment script from OCI to instance', {
|
|
111
|
+
instanceName,
|
|
112
|
+
environmentVersion,
|
|
113
|
+
remotePath: remoteScriptPath,
|
|
114
|
+
repo: index_1.OCI_ARTIFACTS.UBUNTU_ENVIRONMENT
|
|
115
|
+
});
|
|
116
|
+
try {
|
|
117
|
+
await this.ociArtifact.downloadToInstance(instanceName, index_1.OCI_ARTIFACTS.UBUNTU_ENVIRONMENT, environmentVersion, remoteScriptPath, params.acrCredentials);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
this.logger.error(error instanceof Error ? error : new Error(String(error)), {
|
|
121
|
+
message: 'Failed to download environment script from OCI',
|
|
122
|
+
instanceName,
|
|
123
|
+
environmentVersion
|
|
124
|
+
});
|
|
125
|
+
throw new errors_1.AppError(`Failed to download ubuntu-environment v${environmentVersion} from OCI`, {
|
|
126
|
+
exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
|
|
127
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
128
|
+
solution: 'Check network connectivity and ACR credentials',
|
|
129
|
+
context: { instanceName, environmentVersion, repo: index_1.OCI_ARTIFACTS.UBUNTU_ENVIRONMENT }
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// Step 2: 執行腳本
|
|
133
|
+
output?.indent().item('arrow', 'Running system initialization (this may take a few minutes)...').outdent().flush();
|
|
134
|
+
const result = await this.executeScript(instanceName, remoteScriptPath, index_1.TIMEOUTS.SCRIPT_EXECUTION, false, output);
|
|
135
|
+
// Step 3: 處理結果
|
|
136
|
+
if (result.exitCode === 0) {
|
|
137
|
+
this.logger.info('System initialization completed', { instanceName, exitCode: 0 });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const errorMsg = `System initialization failed (exit code: ${result.exitCode})`;
|
|
141
|
+
output?.error(errorMsg).flush();
|
|
142
|
+
output?.item('bullet', 'Check the error output above for details').flush();
|
|
143
|
+
output?.item('bullet', 'You can retry the installation after fixing the issue').flush();
|
|
144
|
+
this.logger.error(new Error(errorMsg), {
|
|
145
|
+
instanceName,
|
|
146
|
+
exitCode: result.exitCode,
|
|
147
|
+
output: result.output
|
|
148
|
+
});
|
|
149
|
+
throw new errors_1.AppError('System initialization failed', {
|
|
150
|
+
exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
|
|
151
|
+
context: { instanceName, exitCode: result.exitCode }
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
exports.ScriptExecutorService = ScriptExecutorService;
|
|
157
|
+
__decorate([
|
|
158
|
+
__param(1, (0, sensitive_decorator_1.Sensitive)()),
|
|
159
|
+
__metadata("design:type", Function),
|
|
160
|
+
__metadata("design:paramtypes", [String, String, Number, Boolean, Object]),
|
|
161
|
+
__metadata("design:returntype", Promise)
|
|
162
|
+
], ScriptExecutorService.prototype, "executeScript", null);
|
|
163
|
+
exports.ScriptExecutorService = ScriptExecutorService = __decorate([
|
|
164
|
+
(0, tsyringe_1.injectable)(),
|
|
165
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
|
|
166
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.ICommandExecutor)),
|
|
167
|
+
__param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
168
|
+
__param(3, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IOciArtifactService)),
|
|
169
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object])
|
|
170
|
+
], ScriptExecutorService);
|
|
171
|
+
//# sourceMappingURL=script-executor.service.js.map
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.HttpClientService = void 0;
|
|
43
|
+
const tsyringe_1 = require("tsyringe");
|
|
44
|
+
const axios_1 = __importStar(require("axios"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const timeouts_1 = require("../../constants/timeouts");
|
|
48
|
+
/**
|
|
49
|
+
* HTTP 客戶端服務
|
|
50
|
+
*
|
|
51
|
+
* 使用 Axios 函式庫實作 IHttpClient 介面,提供真實的 HTTP 請求功能。
|
|
52
|
+
* 支援檔案下載、文字內容抓取,以及進度追蹤。
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const httpClient = new HttpClientService();
|
|
57
|
+
* await httpClient.downloadFile(
|
|
58
|
+
* 'https://example.com/file.tar.xz',
|
|
59
|
+
* '/tmp/file.tar.xz',
|
|
60
|
+
* (current, total) => console.log(`${current}/${total}`)
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
let HttpClientService = class HttpClientService {
|
|
65
|
+
/**
|
|
66
|
+
* 下載檔案到指定路徑
|
|
67
|
+
*
|
|
68
|
+
* 使用 axios stream mode 下載檔案,支援大檔案下載和進度追蹤。
|
|
69
|
+
*
|
|
70
|
+
* @param url - 檔案的 URL
|
|
71
|
+
* @param destPath - 目標檔案路徑
|
|
72
|
+
* @param onProgress - 可選的進度回調函式
|
|
73
|
+
* @throws 當網路錯誤、檔案寫入失敗或回應非 2xx 狀態碼時拋出錯誤
|
|
74
|
+
*/
|
|
75
|
+
async downloadFile(url, destPath, onProgress) {
|
|
76
|
+
// 確保目標目錄存在
|
|
77
|
+
const destDir = path.dirname(destPath);
|
|
78
|
+
if (!fs.existsSync(destDir)) {
|
|
79
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
// 建立寫入串流
|
|
82
|
+
const writer = fs.createWriteStream(destPath);
|
|
83
|
+
try {
|
|
84
|
+
// 使用 axios stream mode 下載
|
|
85
|
+
const response = await axios_1.default.get(url, {
|
|
86
|
+
responseType: 'stream',
|
|
87
|
+
onDownloadProgress: (progressEvent) => {
|
|
88
|
+
if (onProgress && progressEvent.total) {
|
|
89
|
+
onProgress(progressEvent.loaded, progressEvent.total);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// 將回應資料導入寫入串流
|
|
94
|
+
response.data.pipe(writer);
|
|
95
|
+
// 等待寫入完成
|
|
96
|
+
await new Promise((resolve, reject) => {
|
|
97
|
+
writer.on('finish', resolve);
|
|
98
|
+
writer.on('error', reject);
|
|
99
|
+
response.data.on('error', reject);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// 清理部分下載的檔案
|
|
104
|
+
if (fs.existsSync(destPath)) {
|
|
105
|
+
fs.unlinkSync(destPath);
|
|
106
|
+
}
|
|
107
|
+
// 包裝錯誤訊息
|
|
108
|
+
if (error instanceof axios_1.AxiosError) {
|
|
109
|
+
throw new Error(`Failed to download file from ${url}: ${error.message} (status: ${error.response?.status ?? 'unknown'})`);
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`Failed to download file from ${url}: ${String(error)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 抓取 URL 的文字內容
|
|
116
|
+
*
|
|
117
|
+
* 使用 axios.get 抓取文字內容。
|
|
118
|
+
*
|
|
119
|
+
* @param url - 要抓取的 URL
|
|
120
|
+
* @returns Promise,解析為文字內容
|
|
121
|
+
* @throws 當網路錯誤或回應非 2xx 狀態碼時拋出錯誤
|
|
122
|
+
*/
|
|
123
|
+
async fetchText(url) {
|
|
124
|
+
try {
|
|
125
|
+
const response = await axios_1.default.get(url, {
|
|
126
|
+
responseType: 'text',
|
|
127
|
+
});
|
|
128
|
+
return response.data;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
// 包裝錯誤訊息
|
|
132
|
+
if (error instanceof axios_1.AxiosError) {
|
|
133
|
+
throw new Error(`Failed to fetch text from ${url}: ${error.message} (status: ${error.response?.status ?? 'unknown'})`);
|
|
134
|
+
}
|
|
135
|
+
throw new Error(`Failed to fetch text from ${url}: ${String(error)}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 發送 GET 請求並解析 JSON 回應
|
|
140
|
+
*/
|
|
141
|
+
async getJson(url, headers) {
|
|
142
|
+
try {
|
|
143
|
+
const response = await axios_1.default.get(url, {
|
|
144
|
+
headers,
|
|
145
|
+
responseType: 'json',
|
|
146
|
+
});
|
|
147
|
+
return response.data;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (error instanceof axios_1.AxiosError) {
|
|
151
|
+
throw new Error(`Failed to get JSON from ${url}: ${error.message} (status: ${error.response?.status ?? 'unknown'})`);
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`Failed to get JSON from ${url}: ${String(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 執行 HTTP 健康檢查
|
|
158
|
+
*/
|
|
159
|
+
async checkHealth(url, timeoutMs = timeouts_1.TIMEOUTS.HEALTH_CHECK_REQUEST_TIMEOUT) {
|
|
160
|
+
try {
|
|
161
|
+
const response = await axios_1.default.get(url, {
|
|
162
|
+
timeout: timeoutMs,
|
|
163
|
+
validateStatus: (status) => status === 200,
|
|
164
|
+
});
|
|
165
|
+
return response.status === 200;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
exports.HttpClientService = HttpClientService;
|
|
173
|
+
exports.HttpClientService = HttpClientService = __decorate([
|
|
174
|
+
(0, tsyringe_1.injectable)()
|
|
175
|
+
], HttpClientService);
|
|
176
|
+
//# sourceMappingURL=http-client.service.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./http-client.service"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InterceptorFactory = exports.LoggingInterceptor = void 0;
|
|
4
|
+
var logging_interceptor_1 = require("./logging.interceptor");
|
|
5
|
+
Object.defineProperty(exports, "LoggingInterceptor", { enumerable: true, get: function () { return logging_interceptor_1.LoggingInterceptor; } });
|
|
6
|
+
var interceptor_factory_1 = require("./interceptor.factory");
|
|
7
|
+
Object.defineProperty(exports, "InterceptorFactory", { enumerable: true, get: function () { return interceptor_factory_1.InterceptorFactory; } });
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.InterceptorFactory = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const logging_interceptor_1 = require("./logging.interceptor");
|
|
18
|
+
/**
|
|
19
|
+
* 攔截器工廠
|
|
20
|
+
*
|
|
21
|
+
* 用於包裝服務並套用攔截器。
|
|
22
|
+
* 是否記錄 log 由 LoggingInterceptor 內部根據 logLevel 決定。
|
|
23
|
+
*/
|
|
24
|
+
let InterceptorFactory = class InterceptorFactory {
|
|
25
|
+
constructor(loggingInterceptor) {
|
|
26
|
+
this.loggingInterceptor = loggingInterceptor;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 用攔截器包裝服務
|
|
30
|
+
* @param target - 要包裝的服務實例
|
|
31
|
+
* @param serviceName - 服務名稱(用於 log 標識)
|
|
32
|
+
* @returns 被攔截器包裝的服務
|
|
33
|
+
*/
|
|
34
|
+
wrapWithInterceptors(target, serviceName) {
|
|
35
|
+
return this.loggingInterceptor.intercept(target, serviceName);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.InterceptorFactory = InterceptorFactory;
|
|
39
|
+
exports.InterceptorFactory = InterceptorFactory = __decorate([
|
|
40
|
+
(0, tsyringe_1.injectable)(),
|
|
41
|
+
__param(0, (0, tsyringe_1.inject)(logging_interceptor_1.LoggingInterceptor)),
|
|
42
|
+
__metadata("design:paramtypes", [logging_interceptor_1.LoggingInterceptor])
|
|
43
|
+
], InterceptorFactory);
|
|
44
|
+
//# sourceMappingURL=interceptor.factory.js.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var LoggingInterceptor_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.LoggingInterceptor = void 0;
|
|
17
|
+
const tsyringe_1 = require("tsyringe");
|
|
18
|
+
require("reflect-metadata");
|
|
19
|
+
const tokens_1 = require("../../di/tokens");
|
|
20
|
+
const sensitive_decorator_1 = require("../../domain/decorators/sensitive.decorator");
|
|
21
|
+
/**
|
|
22
|
+
* 日誌攔截器
|
|
23
|
+
*
|
|
24
|
+
* 使用 Proxy 模式攔截服務方法調用,自動記錄 debug log。
|
|
25
|
+
* 整合 @Sensitive、@NoLogArgs、@SensitiveReturn 裝飾器,自動遮蔽敏感資料。
|
|
26
|
+
*
|
|
27
|
+
* 特點:
|
|
28
|
+
* - 只在 logLevel=debug 時記錄
|
|
29
|
+
* - 自動遮蔽標記為敏感的參數和返回值
|
|
30
|
+
* - 記錄方法執行時間
|
|
31
|
+
*/
|
|
32
|
+
let LoggingInterceptor = LoggingInterceptor_1 = class LoggingInterceptor {
|
|
33
|
+
constructor(logger, appConfig) {
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.appConfig = appConfig;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 檢查是否應該記錄 log(只在 debug level 時記錄)
|
|
39
|
+
*/
|
|
40
|
+
shouldLog() {
|
|
41
|
+
return this.appConfig.getConfig().logLevel === 'debug';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 攔截目標物件的方法調用
|
|
45
|
+
*/
|
|
46
|
+
intercept(target, serviceName) {
|
|
47
|
+
const self = this;
|
|
48
|
+
return new Proxy(target, {
|
|
49
|
+
get(obj, prop) {
|
|
50
|
+
const value = Reflect.get(obj, prop);
|
|
51
|
+
if (typeof value !== 'function')
|
|
52
|
+
return value;
|
|
53
|
+
const methodName = String(prop);
|
|
54
|
+
if (LoggingInterceptor_1.EXCLUDED_METHODS.has(methodName) ||
|
|
55
|
+
methodName.startsWith('_') ||
|
|
56
|
+
methodName === 'constructor') {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return function (...args) {
|
|
60
|
+
// 每次調用時檢查 log level
|
|
61
|
+
if (!self.shouldLog()) {
|
|
62
|
+
return value.apply(obj, args);
|
|
63
|
+
}
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
// 讀取敏感資料 metadata
|
|
66
|
+
const noLogArgs = Reflect.getMetadata(sensitive_decorator_1.NO_LOG_ARGS_KEY, obj, prop) === true;
|
|
67
|
+
const sensitiveParams = Reflect.getMetadata(sensitive_decorator_1.SENSITIVE_PARAMS_KEY, obj, prop) || [];
|
|
68
|
+
const sensitiveReturn = Reflect.getMetadata(sensitive_decorator_1.SENSITIVE_RETURN_KEY, obj, prop) === true;
|
|
69
|
+
// 處理參數 - 遮蔽敏感資料
|
|
70
|
+
const safeArgs = noLogArgs ? '[REDACTED]' : self.summarizeArgs(args, sensitiveParams);
|
|
71
|
+
self.logger.debug(`[Interceptor] [${serviceName}] ${methodName} called`, {
|
|
72
|
+
source: 'interceptor',
|
|
73
|
+
class: serviceName,
|
|
74
|
+
method: methodName,
|
|
75
|
+
args: safeArgs,
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
const result = value.apply(obj, args);
|
|
79
|
+
if (result instanceof Promise) {
|
|
80
|
+
return result
|
|
81
|
+
.then((res) => {
|
|
82
|
+
self.logCompleted(serviceName, methodName, startTime, res, sensitiveReturn);
|
|
83
|
+
return res;
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
self.logFailed(serviceName, methodName, startTime, error);
|
|
87
|
+
throw error;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
self.logCompleted(serviceName, methodName, startTime, result, sensitiveReturn);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
self.logFailed(serviceName, methodName, startTime, error);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 記錄方法完成
|
|
103
|
+
*/
|
|
104
|
+
logCompleted(serviceName, methodName, startTime, result, sensitiveReturn) {
|
|
105
|
+
this.logger.debug(`[Interceptor] [${serviceName}] ${methodName} completed`, {
|
|
106
|
+
source: 'interceptor',
|
|
107
|
+
class: serviceName,
|
|
108
|
+
method: methodName,
|
|
109
|
+
duration: Date.now() - startTime,
|
|
110
|
+
result: sensitiveReturn ? '[REDACTED]' : this.summarizeResult(result),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 記錄方法失敗
|
|
115
|
+
*/
|
|
116
|
+
logFailed(serviceName, methodName, startTime, error) {
|
|
117
|
+
this.logger.debug(`[Interceptor] [${serviceName}] ${methodName} failed`, {
|
|
118
|
+
source: 'interceptor',
|
|
119
|
+
class: serviceName,
|
|
120
|
+
method: methodName,
|
|
121
|
+
duration: Date.now() - startTime,
|
|
122
|
+
error: error.message,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 摘要化參數(遮蔽敏感資料、截斷長字串)
|
|
127
|
+
*/
|
|
128
|
+
summarizeArgs(args, sensitiveParams) {
|
|
129
|
+
const sensitiveIndexes = new Set(sensitiveParams.map(p => p.index));
|
|
130
|
+
return args.map((arg, index) => {
|
|
131
|
+
// 敏感參數遮蔽
|
|
132
|
+
if (sensitiveIndexes.has(index)) {
|
|
133
|
+
return '[REDACTED]';
|
|
134
|
+
}
|
|
135
|
+
if (typeof arg === 'string' && arg.length > 100)
|
|
136
|
+
return arg.substring(0, 100) + '...';
|
|
137
|
+
if (Array.isArray(arg))
|
|
138
|
+
return { type: 'array', length: arg.length };
|
|
139
|
+
if (arg && typeof arg === 'object')
|
|
140
|
+
return { type: 'object', keys: Object.keys(arg) };
|
|
141
|
+
return arg;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 摘要化返回值
|
|
146
|
+
*/
|
|
147
|
+
summarizeResult(result) {
|
|
148
|
+
if (result === undefined || result === null)
|
|
149
|
+
return result;
|
|
150
|
+
if (Array.isArray(result))
|
|
151
|
+
return { type: 'array', length: result.length };
|
|
152
|
+
if (typeof result === 'object')
|
|
153
|
+
return { type: 'object', keys: Object.keys(result) };
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
exports.LoggingInterceptor = LoggingInterceptor;
|
|
158
|
+
LoggingInterceptor.EXCLUDED_METHODS = new Set([
|
|
159
|
+
'toString',
|
|
160
|
+
'valueOf',
|
|
161
|
+
'toJSON',
|
|
162
|
+
'Symbol.iterator',
|
|
163
|
+
'Symbol.toStringTag',
|
|
164
|
+
]);
|
|
165
|
+
exports.LoggingInterceptor = LoggingInterceptor = LoggingInterceptor_1 = __decorate([
|
|
166
|
+
(0, tsyringe_1.injectable)(),
|
|
167
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
168
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IAppConfig)),
|
|
169
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
170
|
+
], LoggingInterceptor);
|
|
171
|
+
//# sourceMappingURL=logging.interceptor.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Correlation ID Adapter
|
|
4
|
+
*
|
|
5
|
+
* 實作 ICorrelationIdPort
|
|
6
|
+
* 產生用於追蹤日誌的唯一識別碼
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
25
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
26
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
27
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
28
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
29
|
+
};
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.CorrelationIdAdapter = void 0;
|
|
49
|
+
const tsyringe_1 = require("tsyringe");
|
|
50
|
+
const crypto = __importStar(require("crypto"));
|
|
51
|
+
/**
|
|
52
|
+
* Correlation ID Adapter 實作
|
|
53
|
+
*/
|
|
54
|
+
let CorrelationIdAdapter = class CorrelationIdAdapter {
|
|
55
|
+
/**
|
|
56
|
+
* 產生 Correlation ID
|
|
57
|
+
* 使用 crypto.randomBytes 產生 8 bytes 的隨機十六進位字串
|
|
58
|
+
* @returns 16 字元的唯一識別碼
|
|
59
|
+
*/
|
|
60
|
+
generate() {
|
|
61
|
+
return crypto.randomBytes(8).toString('hex');
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.CorrelationIdAdapter = CorrelationIdAdapter;
|
|
65
|
+
exports.CorrelationIdAdapter = CorrelationIdAdapter = __decorate([
|
|
66
|
+
(0, tsyringe_1.injectable)()
|
|
67
|
+
], CorrelationIdAdapter);
|
|
68
|
+
//# sourceMappingURL=correlation-id.adapter.js.map
|