@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,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WslVersionParser = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* WSL 版本解析器
|
|
6
|
+
*
|
|
7
|
+
* 負責解析 WSL 命令輸出的版本資訊
|
|
8
|
+
* 支援英文與中文(繁體/簡體)系統語言
|
|
9
|
+
*
|
|
10
|
+
* 設計原則:
|
|
11
|
+
* - 將 Regex 解析邏輯隔離,方便測試
|
|
12
|
+
* - 容錯編碼問題(Unicode BOM、不同編碼)
|
|
13
|
+
* - 支援多語系輸出
|
|
14
|
+
*/
|
|
15
|
+
class WslVersionParser {
|
|
16
|
+
/**
|
|
17
|
+
* 解析 WSL 版本號
|
|
18
|
+
* @param output wsl --version 的輸出
|
|
19
|
+
* @returns WSL 版本號,解析失敗返回 null
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // English: "WSL version: 2.0.9.0"
|
|
23
|
+
* // Chinese: "WSL 版本: 2.0.9.0"
|
|
24
|
+
* parseWslVersion(output) // "2.0.9.0"
|
|
25
|
+
*/
|
|
26
|
+
static parseWslVersion(output) {
|
|
27
|
+
if (!output || typeof output !== 'string') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// 清理輸出(移除 BOM、標準化空白)
|
|
31
|
+
const cleanOutput = this.cleanOutput(output);
|
|
32
|
+
// 支援多種格式:
|
|
33
|
+
// - English: "WSL version: 2.0.9.0" or "WSL version 2.0.9.0"
|
|
34
|
+
// - Chinese (Traditional): "WSL 版本: 2.0.9.0" or "WSL 版本: 2.0.9.0"
|
|
35
|
+
// - Chinese (Simplified): "WSL 版本: 2.0.9.0"
|
|
36
|
+
const patterns = [
|
|
37
|
+
/WSL\s+version[:\s]+([0-9.]+)/i,
|
|
38
|
+
/WSL\s*版本[::\s]+([0-9.]+)/,
|
|
39
|
+
];
|
|
40
|
+
for (const pattern of patterns) {
|
|
41
|
+
const match = cleanOutput.match(pattern);
|
|
42
|
+
if (match && match[1]) {
|
|
43
|
+
return match[1];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 解析 Kernel 版本號
|
|
50
|
+
* @param output wsl --version 的輸出
|
|
51
|
+
* @returns Kernel 版本號,解析失敗返回 null
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // English: "Kernel version: 5.15.133.1-1"
|
|
55
|
+
* // Chinese: "核心版本: 5.15.133.1-1"
|
|
56
|
+
* parseKernelVersion(output) // "5.15.133.1-1"
|
|
57
|
+
*/
|
|
58
|
+
static parseKernelVersion(output) {
|
|
59
|
+
if (!output || typeof output !== 'string') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const cleanOutput = this.cleanOutput(output);
|
|
63
|
+
// 支援多種格式:
|
|
64
|
+
// - English: "Kernel version: 5.15.133.1-1"
|
|
65
|
+
// - Chinese: "核心版本: 5.15.133.1-1"
|
|
66
|
+
const patterns = [
|
|
67
|
+
/Kernel\s+version[:\s]+([0-9.-]+)/i,
|
|
68
|
+
/核心版本[::\s]+([0-9.-]+)/,
|
|
69
|
+
];
|
|
70
|
+
for (const pattern of patterns) {
|
|
71
|
+
const match = cleanOutput.match(pattern);
|
|
72
|
+
if (match && match[1]) {
|
|
73
|
+
return match[1];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 解析預設 WSL 版本
|
|
80
|
+
* @param output wsl --status 的輸出
|
|
81
|
+
* @returns 預設版本號 (1 或 2),解析失敗返回 null
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* // English: "Default Version: 2"
|
|
85
|
+
* // Chinese: "預設版本: 2" or "默认版本: 2"
|
|
86
|
+
* parseDefaultVersion(output) // 2
|
|
87
|
+
*/
|
|
88
|
+
static parseDefaultVersion(output) {
|
|
89
|
+
if (!output || typeof output !== 'string') {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const cleanOutput = this.cleanOutput(output);
|
|
93
|
+
// 支援多種格式:
|
|
94
|
+
// - English: "Default Version: 2"
|
|
95
|
+
// - Chinese (Traditional): "預設版本: 2"
|
|
96
|
+
// - Chinese (Simplified): "默认版本: 2"
|
|
97
|
+
const patterns = [
|
|
98
|
+
/Default\s+Version[:\s]+(\d+)/i,
|
|
99
|
+
/預設版本[::\s]+(\d+)/,
|
|
100
|
+
/默认版本[::\s]+(\d+)/,
|
|
101
|
+
];
|
|
102
|
+
for (const pattern of patterns) {
|
|
103
|
+
const match = cleanOutput.match(pattern);
|
|
104
|
+
if (match && match[1]) {
|
|
105
|
+
const version = parseInt(match[1], 10);
|
|
106
|
+
if (!isNaN(version) && (version === 1 || version === 2)) {
|
|
107
|
+
return version;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 清理命令輸出
|
|
115
|
+
* - 移除 Unicode BOM
|
|
116
|
+
* - 標準化換行符
|
|
117
|
+
* - 移除多餘空白
|
|
118
|
+
*/
|
|
119
|
+
static cleanOutput(output) {
|
|
120
|
+
return output
|
|
121
|
+
// 移除 UTF-8 BOM
|
|
122
|
+
.replace(/^\uFEFF/, '')
|
|
123
|
+
// 移除 UTF-16 BOM
|
|
124
|
+
.replace(/^\uFFFE/, '')
|
|
125
|
+
// 標準化換行符
|
|
126
|
+
.replace(/\r\n/g, '\n')
|
|
127
|
+
.replace(/\r/g, '\n')
|
|
128
|
+
// 移除多餘空白但保留結構
|
|
129
|
+
.trim();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.WslVersionParser = WslVersionParser;
|
|
133
|
+
//# sourceMappingURL=wsl-version.parser.js.map
|
|
@@ -0,0 +1,168 @@
|
|
|
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.MicroK8sService = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../../../di/tokens");
|
|
18
|
+
const index_1 = require("../../../../constants/index");
|
|
19
|
+
const input_validator_util_1 = require("../../../utils/input-validator.util");
|
|
20
|
+
/**
|
|
21
|
+
* MicroK8s 服務
|
|
22
|
+
*
|
|
23
|
+
* 負責 MicroK8s 的狀態檢查、啟動、停止與等待操作
|
|
24
|
+
*/
|
|
25
|
+
let MicroK8sService = class MicroK8sService {
|
|
26
|
+
constructor(envFactory, logger) {
|
|
27
|
+
this.envFactory = envFactory;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 檢查 MicroK8s 是否正在執行
|
|
32
|
+
*/
|
|
33
|
+
async isRunning(instanceName) {
|
|
34
|
+
try {
|
|
35
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
36
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
37
|
+
const builder = env.shell('systemctl')
|
|
38
|
+
.arg('is-active')
|
|
39
|
+
.arg('snap.microk8s.daemon-kubelite');
|
|
40
|
+
const result = await builder.exec();
|
|
41
|
+
return result.exitCode === 0 && result.stdout.trim() === 'active';
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 啟動 MicroK8s
|
|
49
|
+
*/
|
|
50
|
+
async start(instanceName) {
|
|
51
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
52
|
+
this.logger.debug('[MicroK8s] Starting MicroK8s', { instanceName });
|
|
53
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
54
|
+
const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
|
|
55
|
+
.arg('start');
|
|
56
|
+
await builder.exec();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 停止 MicroK8s
|
|
60
|
+
*/
|
|
61
|
+
async stop(instanceName) {
|
|
62
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
63
|
+
this.logger.debug('[MicroK8s] Stopping MicroK8s', { instanceName });
|
|
64
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
65
|
+
const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
|
|
66
|
+
.arg('stop');
|
|
67
|
+
await builder.exec();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 等待 MicroK8s 停止
|
|
71
|
+
*/
|
|
72
|
+
async waitForStop() {
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, index_1.TIMEOUTS.MICROK8S_STARTUP));
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 等待 MicroK8s 就緒
|
|
78
|
+
*/
|
|
79
|
+
async waitForReady(instanceName) {
|
|
80
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
81
|
+
this.logger.debug('[MicroK8s] Waiting for MicroK8s to be ready', { instanceName });
|
|
82
|
+
try {
|
|
83
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
84
|
+
const builder = env.shell(index_1.LINUX_PATHS.MICROK8S_BIN)
|
|
85
|
+
.arg('status')
|
|
86
|
+
.arg('--wait-ready');
|
|
87
|
+
const result = await builder.exec();
|
|
88
|
+
return result.exitCode === 0;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 等待所有 Pods 就緒
|
|
96
|
+
*/
|
|
97
|
+
async waitForPodsReady(instanceName, timeoutMs = index_1.TIMEOUTS.K8S_READY_TIMEOUT) {
|
|
98
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
99
|
+
const startTime = Date.now();
|
|
100
|
+
let lastCheckTime = 0;
|
|
101
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
102
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
103
|
+
const now = Date.now();
|
|
104
|
+
if (now - lastCheckTime < index_1.TIMEOUTS.K8S_READY_CHECK_INTERVAL) {
|
|
105
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
lastCheckTime = now;
|
|
109
|
+
try {
|
|
110
|
+
const builder = env.kubectl('get')
|
|
111
|
+
.arg('pods')
|
|
112
|
+
.arg('--all-namespaces')
|
|
113
|
+
.arg('-o', 'json');
|
|
114
|
+
const { stdout, exitCode } = await builder.exec();
|
|
115
|
+
if (exitCode !== 0)
|
|
116
|
+
continue;
|
|
117
|
+
const pods = JSON.parse(stdout);
|
|
118
|
+
if (!pods.items || pods.items.length === 0) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const allReady = pods.items.every((pod) => {
|
|
122
|
+
if (pod.status.phase === 'Succeeded') {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (pod.status.phase === 'Failed') {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
const isRunning = pod.status.phase === 'Running';
|
|
129
|
+
const readyCondition = pod.status.conditions?.find((c) => c.type === 'Ready');
|
|
130
|
+
const isReady = readyCondition && readyCondition.status === 'True';
|
|
131
|
+
return isRunning && isReady;
|
|
132
|
+
});
|
|
133
|
+
if (allReady) {
|
|
134
|
+
this.logger.debug('[MicroK8s] All pods are ready', { instanceName });
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Ignore and retry
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
this.logger.warn('[MicroK8s] Timeout waiting for pods to be ready', { instanceName, timeoutMs });
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 重啟 containerd 服務
|
|
147
|
+
*/
|
|
148
|
+
async restartContainerd(instanceName) {
|
|
149
|
+
(0, input_validator_util_1.requireValidInstanceName)(instanceName);
|
|
150
|
+
this.logger.debug('[MicroK8s] Restarting containerd', { instanceName });
|
|
151
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
152
|
+
// 停止
|
|
153
|
+
const restartBuilder = env.shell('sudo')
|
|
154
|
+
.arg('systemctl')
|
|
155
|
+
.arg('restart')
|
|
156
|
+
.arg('snap.microk8s.daemon-containerd');
|
|
157
|
+
await restartBuilder.exec();
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
exports.MicroK8sService = MicroK8sService;
|
|
162
|
+
exports.MicroK8sService = MicroK8sService = __decorate([
|
|
163
|
+
(0, tsyringe_1.injectable)(),
|
|
164
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
|
|
165
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
166
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
167
|
+
], MicroK8sService);
|
|
168
|
+
//# sourceMappingURL=microk8s.service.js.map
|
|
@@ -0,0 +1,336 @@
|
|
|
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
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.RootfsManagerService = void 0;
|
|
49
|
+
const tsyringe_1 = require("tsyringe");
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const os = __importStar(require("os"));
|
|
53
|
+
const crypto = __importStar(require("crypto"));
|
|
54
|
+
const tokens_1 = require("../../../../di/tokens");
|
|
55
|
+
const errors_1 = require("../../../errors");
|
|
56
|
+
const index_1 = require("../../../../constants/index");
|
|
57
|
+
/**
|
|
58
|
+
* Rootfs 管理相關常數
|
|
59
|
+
*/
|
|
60
|
+
const CACHE_DIR = path.join(os.homedir(), index_1.WINDOWS_PATHS.USER_CONFIG_DIR);
|
|
61
|
+
const ROOTFS_FILENAME = 'ubuntu-22.04-root.tar.xz';
|
|
62
|
+
const ROOTFS_URL = 'https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64-root.tar.xz';
|
|
63
|
+
const SHA256SUMS_URL = 'https://cloud-images.ubuntu.com/releases/jammy/release/SHA256SUMS';
|
|
64
|
+
/**
|
|
65
|
+
* Ubuntu Rootfs 管理服務
|
|
66
|
+
*/
|
|
67
|
+
let RootfsManagerService = class RootfsManagerService {
|
|
68
|
+
/**
|
|
69
|
+
* 建立新的 RootfsManager 實例
|
|
70
|
+
* @param httpClient - 用於下載檔案的 HTTP 客戶端 (依賴注入)
|
|
71
|
+
* @param appLogger - 應用程式日誌記錄器
|
|
72
|
+
* @param cliProgress - CLI 進度條服務
|
|
73
|
+
*/
|
|
74
|
+
constructor(httpClient, appLogger, cliProgress) {
|
|
75
|
+
this.appLogger = appLogger;
|
|
76
|
+
this.cliProgress = cliProgress;
|
|
77
|
+
this.httpClient = httpClient;
|
|
78
|
+
this.cacheDir = CACHE_DIR;
|
|
79
|
+
this.cachePath = path.join(this.cacheDir, ROOTFS_FILENAME);
|
|
80
|
+
this.rootfsUrl = ROOTFS_URL;
|
|
81
|
+
this.sha256SumsUrl = SHA256SUMS_URL;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 確保快取目錄存在
|
|
85
|
+
* 若不存在則建立目錄
|
|
86
|
+
*/
|
|
87
|
+
ensureCacheDirectory() {
|
|
88
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
89
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 檢查快取 rootfs 是否存在且有效
|
|
94
|
+
* @returns 快取檢查結果
|
|
95
|
+
*/
|
|
96
|
+
checkCache() {
|
|
97
|
+
// 檢查檔案是否存在
|
|
98
|
+
if (!fs.existsSync(this.cachePath)) {
|
|
99
|
+
return {
|
|
100
|
+
exists: false,
|
|
101
|
+
path: this.cachePath,
|
|
102
|
+
valid: false,
|
|
103
|
+
reason: 'Cache file does not exist',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// 取得檔案狀態
|
|
107
|
+
const stats = fs.statSync(this.cachePath);
|
|
108
|
+
// 基本驗證 - 檔案應大於 100MB (rootfs 約 400MB)
|
|
109
|
+
if (stats.size < index_1.FILE_SIZE.MIN_ROOTFS_SIZE) {
|
|
110
|
+
return {
|
|
111
|
+
exists: true,
|
|
112
|
+
path: this.cachePath,
|
|
113
|
+
valid: false,
|
|
114
|
+
size: stats.size,
|
|
115
|
+
reason: 'Cache file is too small, may be corrupted',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
exists: true,
|
|
120
|
+
path: this.cachePath,
|
|
121
|
+
valid: true,
|
|
122
|
+
size: stats.size,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 計算檔案的 SHA256 雜湊值
|
|
127
|
+
* @param filePath - 檔案路徑
|
|
128
|
+
* @returns 解析為 hex 編碼 SHA256 雜湊值的 Promise
|
|
129
|
+
*/
|
|
130
|
+
calculateSHA256(filePath) {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
const hash = crypto.createHash('sha256');
|
|
133
|
+
const stream = fs.createReadStream(filePath);
|
|
134
|
+
stream.on('data', (data) => hash.update(data));
|
|
135
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
136
|
+
stream.on('error', (error) => reject(error));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 下載並解析 SHA256SUMS 檔案
|
|
141
|
+
* @returns 檔案名稱對應 SHA256 雜湊值的 Map
|
|
142
|
+
*/
|
|
143
|
+
async downloadSHA256Sums() {
|
|
144
|
+
try {
|
|
145
|
+
this.appLogger?.debug('Downloading SHA256SUMS file', { url: this.sha256SumsUrl });
|
|
146
|
+
const responseText = await this.httpClient.fetchText(this.sha256SumsUrl);
|
|
147
|
+
const sums = new Map();
|
|
148
|
+
const lines = responseText.split('\n');
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
const trimmed = line.trim();
|
|
151
|
+
if (!trimmed)
|
|
152
|
+
continue;
|
|
153
|
+
// 格式: <hash> *<filename> 或 <hash> <filename>
|
|
154
|
+
const match = trimmed.match(/^([a-f0-9]{64})\s+\*?(.+)$/);
|
|
155
|
+
if (match) {
|
|
156
|
+
const [, hash, filename] = match;
|
|
157
|
+
sums.set(filename, hash);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return sums;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
this.appLogger?.error(error instanceof Error ? error : new Error(String(error)), {
|
|
164
|
+
operation: 'downloadSHA256Sums',
|
|
165
|
+
url: this.sha256SumsUrl,
|
|
166
|
+
});
|
|
167
|
+
throw new errors_1.AppError('Failed to download SHA256SUMS file', {
|
|
168
|
+
exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
|
|
169
|
+
solution: 'Check your internet connection and try again',
|
|
170
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
171
|
+
context: { operation: 'downloadSHA256Sums', resource: this.sha256SumsUrl }
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 驗證快取檔案是否符合預期的 SHA256 雜湊值
|
|
177
|
+
* @param expectedHash - 預期的 SHA256 雜湊值
|
|
178
|
+
* @returns 若雜湊值相符則回傳 true,否則回傳 false
|
|
179
|
+
*/
|
|
180
|
+
async verifyCachedFile(expectedHash) {
|
|
181
|
+
if (!fs.existsSync(this.cachePath)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
const actualHash = await this.calculateSHA256(this.cachePath);
|
|
185
|
+
return actualHash.toLowerCase() === expectedHash.toLowerCase();
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 取得 rootfs 檔案的預期 SHA256 雜湊值
|
|
189
|
+
* @returns 預期的 SHA256 雜湊值
|
|
190
|
+
*/
|
|
191
|
+
async getExpectedHash() {
|
|
192
|
+
const sums = await this.downloadSHA256Sums();
|
|
193
|
+
const rootfsFilename = path.basename(this.rootfsUrl);
|
|
194
|
+
const hash = sums.get(rootfsFilename);
|
|
195
|
+
if (!hash) {
|
|
196
|
+
throw new Error(`SHA256 hash not found for ${rootfsFilename}`);
|
|
197
|
+
}
|
|
198
|
+
return hash;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* 下載 rootfs 並顯示進度
|
|
202
|
+
* @param onProgress - 可選的進度回調函式
|
|
203
|
+
* @returns 下載結果
|
|
204
|
+
*/
|
|
205
|
+
async downloadRootfs(onProgress) {
|
|
206
|
+
// 確保快取目錄存在
|
|
207
|
+
this.ensureCacheDirectory();
|
|
208
|
+
// 建立臨時檔案路徑
|
|
209
|
+
const tempPath = `${this.cachePath}.tmp`;
|
|
210
|
+
try {
|
|
211
|
+
this.appLogger?.debug('Starting rootfs download', {
|
|
212
|
+
url: this.rootfsUrl,
|
|
213
|
+
tempPath,
|
|
214
|
+
});
|
|
215
|
+
// 使用注入的 HTTP 客戶端下載
|
|
216
|
+
await this.httpClient.downloadFile(this.rootfsUrl, tempPath, onProgress);
|
|
217
|
+
// 驗證下載的檔案
|
|
218
|
+
const expectedHash = await this.getExpectedHash();
|
|
219
|
+
const actualHash = await this.calculateSHA256(tempPath);
|
|
220
|
+
const verified = actualHash.toLowerCase() === expectedHash.toLowerCase();
|
|
221
|
+
if (!verified) {
|
|
222
|
+
// 清理無效檔案
|
|
223
|
+
fs.unlinkSync(tempPath);
|
|
224
|
+
throw new Error('SHA256 verification failed - downloaded file may be corrupted');
|
|
225
|
+
}
|
|
226
|
+
// 移動臨時檔案到最終位置
|
|
227
|
+
if (fs.existsSync(this.cachePath)) {
|
|
228
|
+
fs.unlinkSync(this.cachePath);
|
|
229
|
+
}
|
|
230
|
+
fs.renameSync(tempPath, this.cachePath);
|
|
231
|
+
// 取得檔案大小
|
|
232
|
+
const stats = fs.statSync(this.cachePath);
|
|
233
|
+
this.appLogger?.debug('Rootfs download completed and verified', {
|
|
234
|
+
path: this.cachePath,
|
|
235
|
+
size: stats.size,
|
|
236
|
+
});
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
path: this.cachePath,
|
|
240
|
+
size: stats.size,
|
|
241
|
+
verified: true,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// 發生錯誤時清理臨時檔案
|
|
246
|
+
if (fs.existsSync(tempPath)) {
|
|
247
|
+
fs.unlinkSync(tempPath);
|
|
248
|
+
}
|
|
249
|
+
this.appLogger?.error(error instanceof Error ? error : new Error(String(error)), {
|
|
250
|
+
operation: 'downloadRootfs',
|
|
251
|
+
url: this.rootfsUrl,
|
|
252
|
+
});
|
|
253
|
+
throw new errors_1.AppError('Failed to download Ubuntu rootfs', {
|
|
254
|
+
exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
|
|
255
|
+
solution: 'Check your internet connection and try again. The rootfs file will be downloaded from Ubuntu cloud images.',
|
|
256
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
257
|
+
context: { operation: 'downloadAndVerifyRootfs', resource: this.rootfsUrl }
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 下載 rootfs 並顯示內建進度條
|
|
263
|
+
* @returns 下載結果
|
|
264
|
+
*/
|
|
265
|
+
async downloadWithProgress() {
|
|
266
|
+
let progressBar;
|
|
267
|
+
try {
|
|
268
|
+
const result = await this.downloadRootfs((loaded, total) => {
|
|
269
|
+
if (!progressBar && this.cliProgress) {
|
|
270
|
+
progressBar = this.cliProgress.createDownloadProgress('Downloading Ubuntu rootfs', total);
|
|
271
|
+
}
|
|
272
|
+
progressBar?.update(loaded);
|
|
273
|
+
});
|
|
274
|
+
progressBar?.stop();
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
progressBar?.stop();
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 取得或下載 rootfs
|
|
284
|
+
* 僅在快取不存在或無效時下載
|
|
285
|
+
* @param progress - 進度通知回調
|
|
286
|
+
* @returns rootfs 檔案路徑
|
|
287
|
+
*/
|
|
288
|
+
async getOrDownloadRootfs(output) {
|
|
289
|
+
// 檢查快取
|
|
290
|
+
const cacheCheck = this.checkCache();
|
|
291
|
+
if (cacheCheck.exists && cacheCheck.valid) {
|
|
292
|
+
// 驗證雜湊值
|
|
293
|
+
output?.info('Checking cached rootfs...').flush();
|
|
294
|
+
const expectedHash = await this.getExpectedHash();
|
|
295
|
+
const isValid = await this.verifyCachedFile(expectedHash);
|
|
296
|
+
if (isValid) {
|
|
297
|
+
output?.success('Using cached rootfs').flush();
|
|
298
|
+
return this.cachePath;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
output?.warning('! Cached rootfs checksum mismatch, re-downloading...').flush();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// 下載
|
|
305
|
+
output?.info('Downloading Ubuntu rootfs...').flush();
|
|
306
|
+
output?.item('arrow', `Source: ${this.rootfsUrl}`).flush();
|
|
307
|
+
output?.item('arrow', `Destination: ${this.cachePath}`).flush();
|
|
308
|
+
const result = await this.downloadWithProgress();
|
|
309
|
+
output?.success('SHA256 checksum verified').flush();
|
|
310
|
+
output?.success('Ubuntu rootfs cached successfully').flush();
|
|
311
|
+
return result.path;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 取得快取路徑
|
|
315
|
+
* @returns 快取 rootfs 檔案路徑
|
|
316
|
+
*/
|
|
317
|
+
getCachePath() {
|
|
318
|
+
return this.cachePath;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 取得快取目錄
|
|
322
|
+
* @returns 快取目錄路徑
|
|
323
|
+
*/
|
|
324
|
+
getCacheDirectory() {
|
|
325
|
+
return this.cacheDir;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
exports.RootfsManagerService = RootfsManagerService;
|
|
329
|
+
exports.RootfsManagerService = RootfsManagerService = __decorate([
|
|
330
|
+
(0, tsyringe_1.injectable)(),
|
|
331
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IHttpClient)),
|
|
332
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
333
|
+
__param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.IProgress)),
|
|
334
|
+
__metadata("design:paramtypes", [Object, Object, Object])
|
|
335
|
+
], RootfsManagerService);
|
|
336
|
+
//# sourceMappingURL=rootfs-manager.service.js.map
|