@uofx/cli 1.0.2 → 1.1.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/README.md +159 -3
- package/THIRD-PARTY-NOTICES.txt +84 -2
- package/dist/application/dtos/config/index.js +22 -0
- package/dist/application/dtos/config/request/index.js +19 -0
- package/dist/application/dtos/config/request/set-config.request.dto.js +3 -0
- package/dist/application/dtos/config/response/get-config.response.dto.js +8 -0
- package/dist/application/dtos/config/response/index.js +21 -0
- package/dist/application/dtos/dev/index.js +24 -0
- package/dist/application/dtos/dev/request/generate-component.request.dto.js +3 -0
- package/dist/application/dtos/dev/request/index.js +25 -0
- package/dist/application/dtos/dev/request/install-template.request.dto.js +3 -0
- package/dist/application/dtos/dev/request/new-project.request.dto.js +3 -0
- package/dist/application/dtos/dev/request/publish-plugin.request.dto.js +3 -0
- package/dist/application/dtos/dev/request/uninstall-template.request.dto.js +3 -0
- package/dist/application/dtos/dev/response/generate-component.response.dto.js +3 -0
- package/dist/application/dtos/dev/response/index.js +24 -0
- package/dist/application/dtos/dev/response/new-project.response.dto.js +3 -0
- package/dist/application/dtos/dev/response/publish-plugin.response.dto.js +3 -0
- package/dist/application/dtos/dev/response/template-info.response.dto.js +3 -0
- package/dist/application/dtos/env/index.js +24 -0
- package/dist/application/dtos/{request → env/request}/index.js +1 -3
- package/dist/application/dtos/env/request/list-running-instances.request.dto.js +3 -0
- package/dist/application/dtos/{response → env/response}/index.js +1 -1
- package/dist/application/dtos/env/response/list-running-instances.response.dto.js +3 -0
- package/dist/application/dtos/index.js +9 -2
- package/dist/application/dtos/logs/index.js +22 -0
- package/dist/application/dtos/logs/request/index.js +18 -0
- package/dist/application/dtos/logs/response/index.js +18 -0
- package/dist/application/index.js +1 -0
- package/dist/application/services/env/environment-validator.service.js +120 -0
- package/dist/application/services/env/index.js +11 -0
- package/dist/application/services/env/instance-cleanup.service.js +79 -0
- package/dist/application/services/index.js +21 -0
- package/dist/application/use-cases/config/get-all-config.use-case.js +54 -0
- package/dist/application/use-cases/config/get-config.use-case.js +7 -17
- package/dist/application/use-cases/config/index.js +23 -0
- package/dist/application/use-cases/config/set-config.use-case.js +49 -3
- package/dist/application/use-cases/dev/generate-component.use-case.js +138 -0
- package/dist/application/use-cases/dev/index.js +28 -0
- package/dist/application/use-cases/dev/install-template.use-case.js +60 -0
- package/dist/application/use-cases/dev/list-templates.use-case.js +45 -0
- package/dist/application/use-cases/dev/new-project.use-case.js +130 -0
- package/dist/application/use-cases/dev/publish-plugin.use-case.js +243 -0
- package/dist/application/use-cases/dev/uninstall-template.use-case.js +50 -0
- package/dist/application/use-cases/{instance → env}/delete-instance.use-case.js +2 -2
- package/dist/application/use-cases/{credentials → env}/get-credentials.use-case.js +1 -1
- package/dist/application/use-cases/{instance → env}/index.js +11 -0
- package/dist/application/use-cases/{instance → env}/install-instance.use-case.js +25 -136
- package/dist/application/use-cases/env/list-running-instances.use-case.js +47 -0
- package/dist/application/use-cases/{setup → env}/setup-environment.use-case.js +35 -4
- package/dist/application/use-cases/{instance → env}/start-instance.use-case.js +53 -41
- package/dist/application/use-cases/index.js +9 -6
- package/dist/application/use-cases/logs/index.js +21 -0
- package/dist/cli.js +175 -26
- package/dist/constants/defaults.js +8 -1
- package/dist/constants/oci-artifacts.js +4 -0
- package/dist/constants/paths.js +0 -4
- package/dist/constants/timeouts.js +5 -0
- package/dist/di/modules/application.module.js +13 -44
- package/dist/di/modules/config.module.js +19 -0
- package/dist/di/modules/dev.module.js +45 -0
- package/dist/di/modules/env.module.js +42 -0
- package/dist/di/modules/index.js +30 -0
- package/dist/di/modules/infrastructure.module.js +65 -82
- package/dist/di/modules/logs.module.js +15 -0
- package/dist/di/modules/presentation.module.js +18 -7
- package/dist/di/tokens.js +42 -30
- package/dist/domain/entities/credentials-resolver.entity.js +4 -1
- package/dist/domain/entities/credentials.entity.js +2 -5
- package/dist/domain/entities/delete-instance-validation.entity.js +4 -1
- package/dist/domain/entities/deployment-parameters.entity.js +11 -8
- package/dist/domain/entities/index.js +5 -1
- package/dist/domain/entities/instance-list-aggregator.entity.js +2 -2
- package/dist/domain/entities/instance-metadata.entity.js +2 -3
- package/dist/domain/entities/instance.entity.js +2 -1
- package/dist/domain/entities/log-filter.entity.js +6 -6
- package/dist/domain/entities/template-info.entity.js +63 -0
- package/dist/{infrastructure/utils/error-formatter.util.js → domain/errors/error-utils.js} +1 -1
- package/dist/domain/errors/index.js +19 -0
- package/dist/domain/index.js +6 -0
- package/dist/domain/interfaces/index.js +21 -0
- package/dist/domain/ports/dotnet-publisher.port.js +9 -0
- package/dist/{infrastructure/http/interfaces/http-client.interface.js → domain/ports/file-system.port.js} +1 -1
- package/dist/domain/ports/http-client.port.js +3 -0
- package/dist/domain/ports/index.js +10 -0
- package/dist/domain/ports/logger.port.js +3 -3
- package/dist/domain/ports/progress.port.js +10 -0
- package/dist/domain/ports/project-scaffolder.port.js +9 -0
- package/dist/domain/ports/schematics-runner.port.js +9 -0
- package/dist/domain/ports/template-manager.port.js +9 -0
- package/dist/domain/ports/wsl-setup.port.js +3 -0
- package/dist/domain/types/index.js +1 -0
- package/dist/domain/value-objects/acr-credentials.value-object.js +13 -1
- package/dist/domain/value-objects/chart-version.value-object.js +20 -3
- package/dist/domain/value-objects/config-log-level.value-object.js +2 -1
- package/dist/domain/value-objects/index.js +3 -1
- package/dist/domain/value-objects/instance-name.value-object.js +6 -3
- package/dist/domain/value-objects/jwt-key.value-object.js +27 -5
- package/dist/domain/value-objects/mssql-password.value-object.js +27 -5
- package/dist/domain/value-objects/output-path.value-object.js +85 -0
- package/dist/domain/value-objects/rsa-key-pair.value-object.js +29 -3
- package/dist/index.js +6 -1
- package/dist/infrastructure/config/{app-config.service.js → app-config.adapter.js} +29 -17
- package/dist/infrastructure/config/{config-validator.js → config-validator.util.js} +1 -1
- package/dist/infrastructure/config/index.js +14 -0
- package/dist/infrastructure/config/interfaces/index.js +3 -0
- package/dist/infrastructure/config/{crypto.service.js → services/crypto.service.js} +4 -1
- package/dist/infrastructure/config/services/index.js +9 -0
- package/dist/infrastructure/deployment/deployment.adapter.js +5 -5
- package/dist/infrastructure/deployment/index.js +9 -0
- package/dist/infrastructure/deployment/interfaces/index.js +3 -0
- package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +18 -7
- package/dist/infrastructure/deployment/services/app-manager.service.js +3 -3
- package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +13 -5
- package/dist/infrastructure/deployment/services/helm-registry.service.js +10 -3
- package/dist/infrastructure/deployment/services/index.js +35 -0
- package/dist/infrastructure/deployment/services/infra-manager.service.js +19 -15
- package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +24 -6
- package/dist/infrastructure/deployment/services/mssql-database-init.service.js +75 -11
- package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +17 -5
- package/dist/infrastructure/deployment/services/mssql-storage.service.js +5 -1
- package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +7 -3
- package/dist/infrastructure/deployment/services/oci-artifact.service.js +8 -3
- package/dist/infrastructure/deployment/services/secret-manager.service.js +14 -5
- package/dist/infrastructure/deployment/services/service-manager.service.js +1 -1
- package/dist/infrastructure/deployment/services/version-compatibility.service.js +9 -5
- package/dist/infrastructure/dotnet/dotnet-publisher.adapter.js +143 -0
- package/dist/infrastructure/dotnet/index.js +9 -0
- package/dist/infrastructure/environment/index.js +11 -0
- package/dist/infrastructure/environment/interfaces/index.js +3 -0
- package/dist/infrastructure/{platform-detector.js → environment/platform-detector.util.js} +1 -1
- package/dist/infrastructure/environment/services/hardware-info.service.js +8 -8
- package/dist/infrastructure/environment/services/index.js +11 -0
- package/dist/infrastructure/errors/{error-handler.js → error-handler.adapter.js} +17 -17
- package/dist/infrastructure/errors/index.js +5 -21
- package/dist/infrastructure/execution/builders/base-command.builder.js +32 -11
- package/dist/infrastructure/execution/builders/index.js +26 -0
- package/dist/infrastructure/execution/builders/wsl-command.builder.js +39 -1
- package/dist/infrastructure/execution/command-builder.util.js +94 -0
- package/dist/infrastructure/execution/environments/index.js +23 -0
- package/dist/infrastructure/execution/environments/wsl-execution.environment.js +22 -7
- package/dist/infrastructure/execution/index.js +1 -2
- package/dist/infrastructure/execution/services/command-executor.service.js +389 -0
- package/dist/infrastructure/execution/services/index.js +8 -0
- package/dist/infrastructure/execution/{script-executor.service.js → services/script-executor.service.js} +12 -15
- package/dist/infrastructure/filesystem/filesystem.adapter.js +86 -0
- package/dist/infrastructure/filesystem/index.js +23 -0
- package/dist/infrastructure/http/{http-client.service.js → http-client.adapter.js} +20 -13
- package/dist/infrastructure/http/index.js +1 -1
- package/dist/infrastructure/interceptors/decorators/index.js +23 -0
- package/dist/infrastructure/interceptors/index.js +2 -2
- package/dist/infrastructure/interceptors/interceptor.factory.js +3 -3
- package/dist/infrastructure/interceptors/interfaces/index.js +3 -0
- package/dist/infrastructure/interceptors/services/index.js +6 -0
- package/dist/infrastructure/interceptors/{logging.interceptor.js → services/logging-interceptor.service.js} +4 -4
- package/dist/infrastructure/logger/services/file-log-reader.repository.js +1 -1
- package/dist/infrastructure/logger/services/file-log-writer.repository.js +1 -1
- package/dist/infrastructure/persistence/instance-metadata.adapter.js +2 -1
- package/dist/infrastructure/persistence/services/file-system-config.repository.js +10 -4
- package/dist/infrastructure/persistence/services/file-system-instance.repository.js +2 -2
- package/dist/infrastructure/platforms/index.js +18 -0
- package/dist/infrastructure/platforms/windows/index.js +13 -0
- package/dist/infrastructure/platforms/windows/interfaces/index.js +3 -0
- package/dist/infrastructure/platforms/windows/parsers/index.js +9 -0
- package/dist/infrastructure/platforms/windows/services/index.js +33 -0
- package/dist/infrastructure/platforms/windows/services/microk8s.service.js +28 -10
- package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +7 -3
- package/dist/infrastructure/platforms/windows/services/windows-features.service.js +22 -8
- package/dist/infrastructure/platforms/windows/services/windows-info.service.js +10 -6
- package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +15 -6
- package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +12 -12
- package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +3 -3
- package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +76 -22
- package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +20 -15
- package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +3 -3
- package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +8 -3
- package/dist/infrastructure/platforms/windows/wsl-setup.adapter.js +100 -0
- package/dist/infrastructure/schematics/index.js +11 -0
- package/dist/infrastructure/schematics/project-scaffolder.adapter.js +314 -0
- package/dist/infrastructure/schematics/schematics-runner.adapter.js +175 -0
- package/dist/infrastructure/template/index.js +25 -0
- package/dist/infrastructure/template/interfaces/index.js +22 -0
- package/dist/infrastructure/template/interfaces/template-downloader.interface.js +6 -0
- package/dist/infrastructure/template/interfaces/template-registry.interface.js +6 -0
- package/dist/infrastructure/template/services/index.js +24 -0
- package/dist/infrastructure/template/services/template-downloader.service.js +319 -0
- package/dist/infrastructure/template/services/template-registry.service.js +175 -0
- package/dist/infrastructure/template/template-manager.adapter.js +196 -0
- package/dist/infrastructure/utils/index.js +23 -0
- package/dist/infrastructure/utils/input-validator.util.js +7 -6
- package/dist/presentation/controllers/base.controller.js +36 -0
- package/dist/presentation/controllers/{config.controller.js → config/config.controller.js} +17 -62
- package/dist/presentation/controllers/config/index.js +21 -0
- package/dist/presentation/controllers/dev/dev.controller.js +202 -0
- package/dist/presentation/controllers/dev/index.js +23 -0
- package/dist/presentation/controllers/dev/template.controller.js +158 -0
- package/dist/presentation/controllers/{credentials.controller.js → env/credentials.controller.js} +8 -14
- package/dist/presentation/controllers/env/index.js +23 -0
- package/dist/presentation/controllers/{instance.controller.js → env/instance.controller.js} +35 -92
- package/dist/presentation/controllers/{setup.controller.js → env/setup.controller.js} +33 -66
- package/dist/presentation/controllers/index.js +9 -5
- package/dist/presentation/controllers/logs/index.js +21 -0
- package/dist/presentation/controllers/{logs.controller.js → logs/logs.controller.js} +8 -14
- package/dist/presentation/interfaces/index.js +21 -0
- package/dist/presentation/prompts/acr-credentials.prompt.js +37 -9
- package/dist/presentation/ui/constants/index.js +23 -0
- package/dist/presentation/ui/interaction.service.js +4 -4
- package/dist/presentation/ui/interfaces/cli-progress.interface.js +0 -6
- package/dist/presentation/ui/interfaces/index.js +6 -0
- package/package.json +6 -1
- package/dist/application/dtos/request/set-config.request.dto.js +0 -16
- package/dist/infrastructure/errors/error-handler.interface.js +0 -3
- package/dist/infrastructure/execution/command-builder.js +0 -252
- package/dist/infrastructure/execution/command-executor.service.js +0 -230
- /package/dist/application/dtos/{request → config/request}/get-config.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/delete-instance.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/get-credentials.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/install-instance.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/list-charts.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/setup-environment.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/start-instance.request.dto.js +0 -0
- /package/dist/application/dtos/{request → env/request}/stop-instance.request.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/credentials.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/delete-instance.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/install-instance.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/instance-list.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/instance-status.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/setup-result.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/start-instance.response.dto.js +0 -0
- /package/dist/application/dtos/{response → env/response}/stop-instance.response.dto.js +0 -0
- /package/dist/application/dtos/{request → logs/request}/show-logs.request.dto.js +0 -0
- /package/dist/application/dtos/{response → logs/response}/show-logs.response.dto.js +0 -0
- /package/dist/application/use-cases/{instance → env}/list-charts.use-case.js +0 -0
- /package/dist/application/use-cases/{instance → env}/list-instances.use-case.js +0 -0
- /package/dist/application/use-cases/{instance → env}/stop-instance.use-case.js +0 -0
- /package/dist/{infrastructure → domain}/errors/app-error.js +0 -0
- /package/dist/{infrastructure → domain}/errors/exit-codes.js +0 -0
- /package/dist/infrastructure/config/{app-config.interface.js → interfaces/app-config.interface.js} +0 -0
- /package/dist/{domain → infrastructure/interceptors}/decorators/sensitive.decorator.js +0 -0
- /package/dist/infrastructure/interceptors/{interceptor.interface.js → interfaces/interceptor.interface.js} +0 -0
|
@@ -15,12 +15,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.InstallInstanceUseCase = void 0;
|
|
16
16
|
const tsyringe_1 = require("tsyringe");
|
|
17
17
|
const value_objects_1 = require("../../../domain/value-objects");
|
|
18
|
-
const
|
|
19
|
-
const exit_codes_1 = require("../../../infrastructure/errors/exit-codes");
|
|
18
|
+
const errors_1 = require("../../../domain/errors");
|
|
20
19
|
const entities_1 = require("../../../domain/entities");
|
|
21
|
-
const
|
|
20
|
+
const env_1 = require("../../services/env");
|
|
22
21
|
const tokens_1 = require("../../../di/tokens");
|
|
23
|
-
const runtime_environment_port_1 = require("../../../domain/ports/runtime-environment.port");
|
|
24
22
|
const timeouts_1 = require("../../../constants/timeouts");
|
|
25
23
|
/**
|
|
26
24
|
* 安裝實例 Use Case
|
|
@@ -29,15 +27,15 @@ const timeouts_1 = require("../../../constants/timeouts");
|
|
|
29
27
|
* 特點:完全無 UI 依賴,透過 Progress 介面回調通知進度
|
|
30
28
|
*/
|
|
31
29
|
let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
32
|
-
constructor(instanceManager, instanceStorage, deploymentPort, baseImagePort, appConfigPort, errorHandler,
|
|
30
|
+
constructor(instanceManager, instanceStorage, deploymentPort, baseImagePort, appConfigPort, errorHandler, envValidator, cleanupService, httpClient) {
|
|
33
31
|
this.instanceManager = instanceManager;
|
|
34
32
|
this.instanceStorage = instanceStorage;
|
|
35
33
|
this.deploymentPort = deploymentPort;
|
|
36
34
|
this.baseImagePort = baseImagePort;
|
|
37
35
|
this.appConfigPort = appConfigPort;
|
|
38
36
|
this.errorHandler = errorHandler;
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
37
|
+
this.envValidator = envValidator;
|
|
38
|
+
this.cleanupService = cleanupService;
|
|
41
39
|
this.httpClient = httpClient;
|
|
42
40
|
}
|
|
43
41
|
/**
|
|
@@ -49,28 +47,30 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
49
47
|
async execute(request, output) {
|
|
50
48
|
output?.info('Installing UOFX development environment...').newline().flush();
|
|
51
49
|
// [1/14] 檢查 WSL 是否已安裝
|
|
52
|
-
await this.checkWslInstalled(output);
|
|
50
|
+
await this.envValidator.checkWslInstalled(output);
|
|
53
51
|
output?.newline().flush();
|
|
54
52
|
// [2/14] 檢查記憶體
|
|
55
|
-
await this.checkMemoryAvailability(output);
|
|
53
|
+
await this.envValidator.checkMemoryAvailability(output);
|
|
56
54
|
output?.newline().flush();
|
|
57
55
|
// [3/14] 解析 Chart 版本
|
|
58
56
|
const instanceName = value_objects_1.InstanceName.create(request.name);
|
|
59
57
|
const chartVersion = await this.resolveChartVersion(request.chartVersion, output);
|
|
60
58
|
output?.newline().flush();
|
|
61
59
|
// [4/14] 檢查是否有其他實例正在運行
|
|
62
|
-
await this.ensureNoRunningInstances(output);
|
|
60
|
+
await this.envValidator.ensureNoRunningInstances(output);
|
|
63
61
|
output?.newline().flush();
|
|
64
62
|
// 檢查實例是否已存在
|
|
65
63
|
const exists = await this.instanceManager.exists(instanceName);
|
|
66
64
|
if (exists) {
|
|
67
|
-
throw new
|
|
65
|
+
throw new errors_1.AppError(`Instance "${request.name}" already exists`, {
|
|
66
|
+
exitCode: errors_1.EXIT_CODES.BUSINESS_ERROR,
|
|
67
|
+
});
|
|
68
68
|
}
|
|
69
69
|
// [5/14] 準備憑證
|
|
70
70
|
const acrCredentials = await this.getAcrCredentials(request.pullSecretPath, output);
|
|
71
71
|
output?.newline().flush();
|
|
72
72
|
// [6/14] 準備部署參數
|
|
73
|
-
const deploymentParams = this.prepareDeploymentParameters(instanceName, acrCredentials, output);
|
|
73
|
+
const deploymentParams = this.prepareDeploymentParameters(instanceName, acrCredentials, request.skipNorthwind ?? false, output);
|
|
74
74
|
// [7/14] 下載 Rootfs
|
|
75
75
|
output?.section('[7/14] Checking Ubuntu rootfs').flush();
|
|
76
76
|
output?.item('arrow', 'Checking rootfs image...').flush();
|
|
@@ -88,7 +88,7 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
88
88
|
output?.success('Instance created').flush();
|
|
89
89
|
// 註冊中斷清理 handler(傳入 output 用於輸出清理訊息)
|
|
90
90
|
this.errorHandler.registerCleanupHandler(async () => {
|
|
91
|
-
await this.
|
|
91
|
+
await this.cleanupService.cleanupFailedInstallation(instanceName, output);
|
|
92
92
|
}, output);
|
|
93
93
|
output?.newline().flush();
|
|
94
94
|
// 6. 儲存 metadata
|
|
@@ -129,7 +129,9 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
129
129
|
const version = value_objects_1.ChartVersion.create(versionInput);
|
|
130
130
|
const isValid = await this.deploymentPort.validateChartVersion('uofx', version);
|
|
131
131
|
if (!isValid) {
|
|
132
|
-
throw new
|
|
132
|
+
throw new errors_1.AppError(`Chart version "${versionInput}" does not exist`, {
|
|
133
|
+
exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR,
|
|
134
|
+
});
|
|
133
135
|
}
|
|
134
136
|
output?.success(`Using version: ${version.toString()}`).flush();
|
|
135
137
|
return version;
|
|
@@ -177,13 +179,14 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
177
179
|
* 準備部署參數
|
|
178
180
|
* @param instanceName - 實例名稱
|
|
179
181
|
* @param acrCredentials - ACR 憑證
|
|
182
|
+
* @param skipNorthwind - 是否跳過北風資料庫安裝
|
|
180
183
|
* @param output - 輸出介面
|
|
181
184
|
* @returns 部署參數
|
|
182
185
|
*/
|
|
183
|
-
prepareDeploymentParameters(instanceName, acrCredentials, output) {
|
|
186
|
+
prepareDeploymentParameters(instanceName, acrCredentials, skipNorthwind, output) {
|
|
184
187
|
output?.section('[6/14] Preparing deployment parameters').flush();
|
|
185
188
|
output?.item('arrow', 'Generating security credentials...').flush();
|
|
186
|
-
const params = entities_1.DeploymentParameters.createWithGeneratedCredentials(instanceName, acrCredentials);
|
|
189
|
+
const params = entities_1.DeploymentParameters.createWithGeneratedCredentials(instanceName, acrCredentials, skipNorthwind);
|
|
187
190
|
output?.success('Deployment parameters ready').newline().flush();
|
|
188
191
|
return params;
|
|
189
192
|
}
|
|
@@ -200,7 +203,7 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
200
203
|
// [10/14] 建立全域設定 Secret
|
|
201
204
|
output?.section('[10/14] Configuring Kubernetes secrets').flush();
|
|
202
205
|
output?.item('arrow', 'Creating global config secret...').flush();
|
|
203
|
-
await this.deploymentPort.createGlobalConfigSecret(fullName, params);
|
|
206
|
+
await this.deploymentPort.createGlobalConfigSecret(fullName, params, output);
|
|
204
207
|
output?.success('Secrets configured').newline().flush();
|
|
205
208
|
// [11/14] 部署 MSSQL
|
|
206
209
|
output?.section('[11/14] Deploying MSSQL').flush();
|
|
@@ -252,121 +255,6 @@ let InstallInstanceUseCase = class InstallInstanceUseCase {
|
|
|
252
255
|
throw error;
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
|
-
/**
|
|
256
|
-
* 確保沒有其他實例正在運行
|
|
257
|
-
* 安裝新實例前必須停止所有現有實例
|
|
258
|
-
* @param output - 輸出介面
|
|
259
|
-
* @throws AppError 若有運行中的實例
|
|
260
|
-
*/
|
|
261
|
-
async ensureNoRunningInstances(output) {
|
|
262
|
-
output?.section('[4/14] Checking for running instances').flush();
|
|
263
|
-
output?.item('arrow', 'Scanning running instances...').flush();
|
|
264
|
-
const runningInstances = await this.instanceManager.listRunningInstances();
|
|
265
|
-
if (runningInstances.length > 0) {
|
|
266
|
-
// 將全名轉換為顯示名稱(移除 uofx- 前綴)
|
|
267
|
-
const displayNames = runningInstances.map(name => name.replace('uofx-', ''));
|
|
268
|
-
const instanceList = displayNames
|
|
269
|
-
.map(name => ` - ${name}`)
|
|
270
|
-
.join('\n');
|
|
271
|
-
const stopCommand = displayNames.length === 1
|
|
272
|
-
? `uofx env stop --name ${displayNames[0]}`
|
|
273
|
-
: 'uofx env stop --name <instance-name>';
|
|
274
|
-
throw new app_error_1.AppError(`Cannot install: ${runningInstances.length} instance(s) currently running:\n${instanceList}`, {
|
|
275
|
-
exitCode: exit_codes_1.EXIT_CODES.BUSINESS_ERROR,
|
|
276
|
-
solution: `Stop all running instances before installing a new one.\nRun: ${stopCommand}`,
|
|
277
|
-
context: { runningInstances }
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
output?.success('No running instances detected').flush();
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* 清理未完成安裝的實體
|
|
284
|
-
* 用於安裝中斷時的清理
|
|
285
|
-
* @param instanceName - 實例名稱
|
|
286
|
-
* @param output - 輸出介面
|
|
287
|
-
*/
|
|
288
|
-
async cleanupInstance(instanceName, output) {
|
|
289
|
-
output?.item('arrow', `Removing incomplete instance: ${instanceName.toString()}`).flush();
|
|
290
|
-
try {
|
|
291
|
-
// 嘗試停止實體(如果正在運行)
|
|
292
|
-
const status = await this.instanceManager.getStatus(instanceName);
|
|
293
|
-
if (status.isRunning) {
|
|
294
|
-
output?.indent().item('arrow', 'Stopping instance...').outdent().flush();
|
|
295
|
-
await this.instanceManager.stop(instanceName, true);
|
|
296
|
-
output?.indent().success('Instance stopped').outdent().flush();
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
output?.indent().warning(`Failed to stop instance: ${(0, error_formatter_util_1.formatErrorMessage)(error)}`).outdent().flush();
|
|
301
|
-
}
|
|
302
|
-
try {
|
|
303
|
-
// 刪除實體
|
|
304
|
-
output?.indent().item('arrow', 'Deleting instance...').outdent().flush();
|
|
305
|
-
await this.instanceManager.delete(instanceName);
|
|
306
|
-
output?.indent().success('Instance deleted').outdent().flush();
|
|
307
|
-
}
|
|
308
|
-
catch (error) {
|
|
309
|
-
output?.indent().warning(`Failed to delete instance: ${(0, error_formatter_util_1.formatErrorMessage)(error)}`).outdent().flush();
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
// 刪除 metadata
|
|
313
|
-
output?.indent().item('arrow', 'Deleting metadata...').outdent().flush();
|
|
314
|
-
await this.instanceStorage.deleteInstance(instanceName);
|
|
315
|
-
output?.indent().success('Metadata deleted').outdent().flush();
|
|
316
|
-
}
|
|
317
|
-
catch (error) {
|
|
318
|
-
output?.indent().warning(`Failed to delete metadata: ${(0, error_formatter_util_1.formatErrorMessage)(error)}`).outdent().flush();
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* 檢查 WSL 是否已安裝
|
|
323
|
-
* @param output - 輸出介面
|
|
324
|
-
* @throws AppError 若 WSL 未安裝
|
|
325
|
-
*/
|
|
326
|
-
async checkWslInstalled(output) {
|
|
327
|
-
output?.section('[1/14] Checking WSL installation').flush();
|
|
328
|
-
output?.item('arrow', 'Verifying runtime environment...').flush();
|
|
329
|
-
const runtimeInfo = await this.runtimeEnvironment.getRuntimeInfo();
|
|
330
|
-
if (!runtimeInfo.isInstalled) {
|
|
331
|
-
throw new app_error_1.AppError(`${runtimeInfo.type} is not installed on this system`, {
|
|
332
|
-
exitCode: exit_codes_1.EXIT_CODES.ENVIRONMENT_ERROR,
|
|
333
|
-
solution: 'Please install WSL first. Run: uofx env setup',
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
output?.success(`${runtimeInfo.type} is installed (${runtimeInfo.version})`).flush();
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* 檢查記憶體可用性
|
|
340
|
-
* 綜合考量系統剩餘記憶體、WSL 限制、WSL 目前使用量
|
|
341
|
-
* @param output - 輸出介面
|
|
342
|
-
*/
|
|
343
|
-
async checkMemoryAvailability(output) {
|
|
344
|
-
output?.section('[2/14] Checking remaining memory').flush();
|
|
345
|
-
const memoryInfo = await this.runtimeEnvironment.getMemoryAvailability();
|
|
346
|
-
// 顯示詳細資訊
|
|
347
|
-
for (const detail of memoryInfo.details) {
|
|
348
|
-
const suffix = detail.suffix ? ` (${detail.suffix})` : '';
|
|
349
|
-
output?.item('bullet', `${detail.label}: ${detail.valueGB.toFixed(2)} GB${suffix}`).flush();
|
|
350
|
-
}
|
|
351
|
-
if (memoryInfo.availableGB < runtime_environment_port_1.UOFX_REQUIRED_MEMORY_GB) {
|
|
352
|
-
output?.newline().flush();
|
|
353
|
-
output?.warning(`Available memory (${memoryInfo.availableGB.toFixed(2)} GB) is below recommended ${runtime_environment_port_1.UOFX_REQUIRED_MEMORY_GB} GB`).flush();
|
|
354
|
-
output?.info('Insufficient memory may cause:').flush();
|
|
355
|
-
output?.item('bullet', 'Kubernetes pods failing to start').flush();
|
|
356
|
-
output?.item('bullet', 'Services becoming unresponsive or crashing').flush();
|
|
357
|
-
output?.newline().flush();
|
|
358
|
-
const shouldContinue = await this.userInteraction.confirm('Do you want to continue installation anyway?', false);
|
|
359
|
-
if (!shouldContinue) {
|
|
360
|
-
throw new app_error_1.AppError('Installation cancelled due to insufficient memory', {
|
|
361
|
-
exitCode: exit_codes_1.EXIT_CODES.BUSINESS_ERROR,
|
|
362
|
-
solution: 'Close other applications or increase memory available for the runtime environment'
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
output?.success('Memory check passed').flush();
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
258
|
/**
|
|
371
259
|
* 執行後台健康檢查(含重試機制)
|
|
372
260
|
* 超時後顯示警告但不中斷安裝
|
|
@@ -416,9 +304,10 @@ exports.InstallInstanceUseCase = InstallInstanceUseCase = __decorate([
|
|
|
416
304
|
__param(3, (0, tsyringe_1.inject)(tokens_1.TOKENS.IBaseImagePort)),
|
|
417
305
|
__param(4, (0, tsyringe_1.inject)(tokens_1.TOKENS.IAppConfigPort)),
|
|
418
306
|
__param(5, (0, tsyringe_1.inject)(tokens_1.TOKENS.IErrorHandlerPort)),
|
|
419
|
-
__param(6, (0, tsyringe_1.inject)(tokens_1.TOKENS.
|
|
420
|
-
__param(7, (0, tsyringe_1.inject)(tokens_1.TOKENS.
|
|
421
|
-
__param(8, (0, tsyringe_1.inject)(tokens_1.TOKENS.
|
|
422
|
-
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object,
|
|
307
|
+
__param(6, (0, tsyringe_1.inject)(tokens_1.TOKENS.EnvironmentValidator)),
|
|
308
|
+
__param(7, (0, tsyringe_1.inject)(tokens_1.TOKENS.InstanceCleanupService)),
|
|
309
|
+
__param(8, (0, tsyringe_1.inject)(tokens_1.TOKENS.IHttpClientPort)),
|
|
310
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, env_1.EnvironmentValidator,
|
|
311
|
+
env_1.InstanceCleanupService, Object])
|
|
423
312
|
], InstallInstanceUseCase);
|
|
424
313
|
//# sourceMappingURL=install-instance.use-case.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
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.ListRunningInstancesUseCase = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../../di/tokens");
|
|
18
|
+
const instance_name_value_object_1 = require("../../../domain/value-objects/instance-name.value-object");
|
|
19
|
+
/**
|
|
20
|
+
* 列出運行中實例 Use Case
|
|
21
|
+
*
|
|
22
|
+
* 查詢目前所有運行中的 UOFX 實例,可選擇排除特定實例
|
|
23
|
+
*/
|
|
24
|
+
let ListRunningInstancesUseCase = class ListRunningInstancesUseCase {
|
|
25
|
+
constructor(instanceManager) {
|
|
26
|
+
this.instanceManager = instanceManager;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 列出運行中實例
|
|
30
|
+
* @param request - 查詢請求(可指定排除的實例名稱)
|
|
31
|
+
* @returns 運行中實例清單
|
|
32
|
+
*/
|
|
33
|
+
async execute(request) {
|
|
34
|
+
const runningInstances = await this.instanceManager.listRunningInstances();
|
|
35
|
+
const filtered = request.excludeName
|
|
36
|
+
? runningInstances.filter(name => name !== instance_name_value_object_1.InstanceName.create(request.excludeName).fullName)
|
|
37
|
+
: runningInstances;
|
|
38
|
+
return { instances: filtered };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.ListRunningInstancesUseCase = ListRunningInstancesUseCase;
|
|
42
|
+
exports.ListRunningInstancesUseCase = ListRunningInstancesUseCase = __decorate([
|
|
43
|
+
(0, tsyringe_1.injectable)(),
|
|
44
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.IInstanceManagerPort)),
|
|
45
|
+
__metadata("design:paramtypes", [Object])
|
|
46
|
+
], ListRunningInstancesUseCase);
|
|
47
|
+
//# sourceMappingURL=list-running-instances.use-case.js.map
|
|
@@ -15,32 +15,62 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.SetupEnvironmentUseCase = void 0;
|
|
16
16
|
const tsyringe_1 = require("tsyringe");
|
|
17
17
|
const tokens_1 = require("../../../di/tokens");
|
|
18
|
+
const runtime_environment_port_1 = require("../../../domain/ports/runtime-environment.port");
|
|
18
19
|
const environment_validation_entity_1 = require("../../../domain/entities/environment-validation.entity");
|
|
19
20
|
/**
|
|
20
21
|
* 設置環境 Use Case
|
|
21
22
|
*
|
|
22
23
|
* 檢查系統環境是否符合 UOFX 運行需求
|
|
24
|
+
* 包含 OS、Runtime、記憶體、系統資源、網路檢查,以及 WSL 狀態檢查與互動式設定
|
|
23
25
|
*/
|
|
24
26
|
let SetupEnvironmentUseCase = class SetupEnvironmentUseCase {
|
|
25
|
-
constructor(runtimeEnv) {
|
|
27
|
+
constructor(runtimeEnv, wslSetupPort) {
|
|
26
28
|
this.runtimeEnv = runtimeEnv;
|
|
29
|
+
this.wslSetupPort = wslSetupPort;
|
|
27
30
|
}
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* 執行環境檢查
|
|
33
|
+
* @param output - 可選的輸出介面,用於 WSL 設定流程的互動輸出
|
|
34
|
+
* @returns 環境檢查結果
|
|
35
|
+
*/
|
|
36
|
+
async execute(output) {
|
|
29
37
|
// 並行執行所有檢查
|
|
30
|
-
const [osInfo, runtimeInfo, systemResources, networkStatus] = await Promise.all([
|
|
38
|
+
const [osInfo, runtimeInfo, systemResources, networkStatus, memoryAvailability] = await Promise.all([
|
|
31
39
|
this.runtimeEnv.getOsInfo(),
|
|
32
40
|
this.runtimeEnv.getRuntimeInfo(),
|
|
33
41
|
this.runtimeEnv.getSystemResources(),
|
|
34
42
|
this.runtimeEnv.checkNetworkConnectivity(),
|
|
43
|
+
this.runtimeEnv.getMemoryAvailability(),
|
|
35
44
|
]);
|
|
45
|
+
// WSL 狀態檢查與條件式設定
|
|
46
|
+
let wslSetup;
|
|
47
|
+
const wslStatus = await this.wslSetupPort.getWslStatus();
|
|
48
|
+
if (!wslStatus.isInstalled) {
|
|
49
|
+
// WSL 未安裝 → 透過 Port 進行互動式安裝
|
|
50
|
+
const result = await this.wslSetupPort.promptAndSetupWsl(wslStatus, output);
|
|
51
|
+
wslSetup = result;
|
|
52
|
+
}
|
|
53
|
+
else if (wslStatus.needsUpdate || wslStatus.needsDefaultVersion) {
|
|
54
|
+
// WSL 已安裝但需要更新或設定 → 透過 Port 進行互動式設定
|
|
55
|
+
const result = await this.wslSetupPort.promptAndSetupWsl(wslStatus, output);
|
|
56
|
+
wslSetup = result;
|
|
57
|
+
}
|
|
36
58
|
// 使用 Entity 進行驗證
|
|
37
59
|
const validation = environment_validation_entity_1.EnvironmentValidation.create(osInfo, runtimeInfo, systemResources, networkStatus);
|
|
60
|
+
// 記憶體可用性
|
|
61
|
+
const isSufficient = memoryAvailability.availableGB >= runtime_environment_port_1.UOFX_REQUIRED_MEMORY_GB;
|
|
38
62
|
return {
|
|
39
63
|
osInfo,
|
|
40
64
|
runtimeInfo,
|
|
41
65
|
systemResources,
|
|
42
66
|
networkStatus,
|
|
43
67
|
allChecksPassed: validation.allChecksPassed,
|
|
68
|
+
memoryInfo: {
|
|
69
|
+
availableGB: memoryAvailability.availableGB,
|
|
70
|
+
details: memoryAvailability.details,
|
|
71
|
+
isSufficient,
|
|
72
|
+
},
|
|
73
|
+
wslSetup,
|
|
44
74
|
};
|
|
45
75
|
}
|
|
46
76
|
};
|
|
@@ -48,6 +78,7 @@ exports.SetupEnvironmentUseCase = SetupEnvironmentUseCase;
|
|
|
48
78
|
exports.SetupEnvironmentUseCase = SetupEnvironmentUseCase = __decorate([
|
|
49
79
|
(0, tsyringe_1.injectable)(),
|
|
50
80
|
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.IRuntimeEnvironmentPort)),
|
|
51
|
-
|
|
81
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IWslSetupPort)),
|
|
82
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
52
83
|
], SetupEnvironmentUseCase);
|
|
53
84
|
//# sourceMappingURL=setup-environment.use-case.js.map
|
|
@@ -18,14 +18,14 @@ const tokens_1 = require("../../../di/tokens");
|
|
|
18
18
|
const instance_name_value_object_1 = require("../../../domain/value-objects/instance-name.value-object");
|
|
19
19
|
const connection_info_value_object_1 = require("../../../domain/value-objects/connection-info.value-object");
|
|
20
20
|
const instance_lifecycle_state_entity_1 = require("../../../domain/entities/instance-lifecycle-state.entity");
|
|
21
|
-
const
|
|
22
|
-
const exit_codes_1 = require("../../../infrastructure/errors/exit-codes");
|
|
21
|
+
const errors_1 = require("../../../domain/errors");
|
|
23
22
|
/**
|
|
24
23
|
* 啟動實例 Use Case
|
|
25
24
|
*/
|
|
26
25
|
let StartInstanceUseCase = class StartInstanceUseCase {
|
|
27
|
-
constructor(instanceManager) {
|
|
26
|
+
constructor(instanceManager, interaction) {
|
|
28
27
|
this.instanceManager = instanceManager;
|
|
28
|
+
this.interaction = interaction;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* 啟動實例
|
|
@@ -34,12 +34,21 @@ let StartInstanceUseCase = class StartInstanceUseCase {
|
|
|
34
34
|
* @returns 啟動結果與連線資訊
|
|
35
35
|
*/
|
|
36
36
|
async execute(request, output) {
|
|
37
|
-
const { name, waitForReady = true } = request;
|
|
37
|
+
const { name, waitForReady = true, resolveConflicts = false } = request;
|
|
38
38
|
const instanceName = instance_name_value_object_1.InstanceName.create(name);
|
|
39
39
|
// 驗證實例存在
|
|
40
40
|
await this.instanceManager.requireExists(instanceName);
|
|
41
|
-
//
|
|
42
|
-
await this.
|
|
41
|
+
// 檢查是否有其他實例正在運行,並處理衝突
|
|
42
|
+
const cancelled = await this.handleRunningInstanceConflicts(instanceName, resolveConflicts, output);
|
|
43
|
+
if (cancelled) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
instanceName: instanceName.fullName,
|
|
47
|
+
alreadyRunning: false,
|
|
48
|
+
podsReady: false,
|
|
49
|
+
connectionInfo: { instanceIp: null, adminUrl: null, sqlServer: null },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
43
52
|
// 取得生命週期狀態
|
|
44
53
|
// 重要:必須先檢查 WSL 狀態,再檢查 MicroK8s 狀態
|
|
45
54
|
// 因為 isMicroK8sRunning 會執行 wsl -d xxx -- ... 命令,這會觸發 WSL 自動啟動
|
|
@@ -61,7 +70,7 @@ let StartInstanceUseCase = class StartInstanceUseCase {
|
|
|
61
70
|
let podsReady = false;
|
|
62
71
|
if (waitForReady) {
|
|
63
72
|
output?.item('arrow', 'Waiting for all pods to be ready...').flush();
|
|
64
|
-
podsReady = await this.instanceManager.waitForPodsReady(instanceName
|
|
73
|
+
podsReady = await this.instanceManager.waitForPodsReady(instanceName);
|
|
65
74
|
if (podsReady) {
|
|
66
75
|
output?.success('All pods are ready').flush();
|
|
67
76
|
}
|
|
@@ -92,26 +101,6 @@ let StartInstanceUseCase = class StartInstanceUseCase {
|
|
|
92
101
|
const k8sRunning = await this.instanceManager.isMicroK8sRunning(instanceName);
|
|
93
102
|
return instance_lifecycle_state_entity_1.InstanceLifecycleState.fromStatus(status, k8sRunning);
|
|
94
103
|
}
|
|
95
|
-
/**
|
|
96
|
-
* 列出其他運行中的 UOFX 實例
|
|
97
|
-
* @param excludeName - 要排除的實例名稱
|
|
98
|
-
* @returns 其他運行中實例的名稱陣列
|
|
99
|
-
*/
|
|
100
|
-
async listOtherRunningInstances(excludeName) {
|
|
101
|
-
const instanceName = instance_name_value_object_1.InstanceName.create(excludeName);
|
|
102
|
-
const runningInstances = await this.instanceManager.listRunningInstances();
|
|
103
|
-
return runningInstances.filter(name => name !== instanceName.fullName);
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* 停止指定實例
|
|
107
|
-
* @param name - 要停止的實例名稱(完整名稱)
|
|
108
|
-
* @param output - 輸出介面
|
|
109
|
-
*/
|
|
110
|
-
async stopInstance(fullName, output) {
|
|
111
|
-
// 直接使用完整名稱建立 InstanceName
|
|
112
|
-
const instanceName = instance_name_value_object_1.InstanceName.createFromFullName(fullName);
|
|
113
|
-
await this.instanceManager.stop(instanceName, true, output);
|
|
114
|
-
}
|
|
115
104
|
/**
|
|
116
105
|
* 取得連線資訊
|
|
117
106
|
*/
|
|
@@ -121,34 +110,57 @@ let StartInstanceUseCase = class StartInstanceUseCase {
|
|
|
121
110
|
return connectionInfo.toDto();
|
|
122
111
|
}
|
|
123
112
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
113
|
+
* 處理運行中實例衝突
|
|
114
|
+
*
|
|
115
|
+
* 若有其他實例正在運行:
|
|
116
|
+
* - resolveConflicts=true:互動式詢問使用者是否停止衝突實例
|
|
117
|
+
* - resolveConflicts=false:直接拋出錯誤
|
|
118
|
+
*
|
|
126
119
|
* @param currentInstance - 當前要啟動的實例
|
|
127
|
-
* @
|
|
120
|
+
* @param resolveConflicts - 是否互動式解決衝突
|
|
121
|
+
* @param output - 輸出介面
|
|
122
|
+
* @returns true 表示使用者取消操作,false 表示可繼續
|
|
128
123
|
*/
|
|
129
|
-
async
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
async handleRunningInstanceConflicts(currentInstance, resolveConflicts, output) {
|
|
125
|
+
const runningInstances = await this.instanceManager.listRunningInstances();
|
|
126
|
+
const otherRunning = runningInstances.filter(name => name !== currentInstance.fullName);
|
|
127
|
+
if (otherRunning.length === 0) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (!resolveConflicts) {
|
|
131
|
+
// 非互動模式:直接拋出錯誤
|
|
133
132
|
const displayNames = otherRunning.map(name => name.replace('uofx-', ''));
|
|
134
|
-
const instanceList = displayNames
|
|
135
|
-
.map(name => ` - ${name}`)
|
|
136
|
-
.join('\n');
|
|
133
|
+
const instanceList = displayNames.map(name => ` - ${name}`).join('\n');
|
|
137
134
|
const stopCommand = displayNames.length === 1
|
|
138
135
|
? `uofx env stop --name ${displayNames[0]}`
|
|
139
136
|
: 'uofx env stop --name <instance-name>';
|
|
140
|
-
throw new
|
|
141
|
-
exitCode:
|
|
137
|
+
throw new errors_1.AppError(`Cannot start: another instance is already running:\n${instanceList}`, {
|
|
138
|
+
exitCode: errors_1.EXIT_CODES.BUSINESS_ERROR,
|
|
142
139
|
solution: `Only one instance can run at a time.\nRun: ${stopCommand}`,
|
|
143
|
-
context: { otherRunning }
|
|
140
|
+
context: { otherRunning },
|
|
144
141
|
});
|
|
145
142
|
}
|
|
143
|
+
// 互動模式:詢問使用者是否停止衝突實例
|
|
144
|
+
const otherName = otherRunning[0];
|
|
145
|
+
output?.newline().warning(`Another instance is already running: ${otherName}`).flush();
|
|
146
|
+
const shouldStop = await this.interaction.confirm(`Do you want to stop "${otherName}" and start "${currentInstance.fullName}"?`, false);
|
|
147
|
+
if (!shouldStop) {
|
|
148
|
+
output?.info('Start cancelled').render();
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
// 停止衝突實例
|
|
152
|
+
const stopInstanceName = instance_name_value_object_1.InstanceName.createFromFullName(otherName);
|
|
153
|
+
output?.info(`Stopping ${otherName}...`).flush();
|
|
154
|
+
await this.instanceManager.stop(stopInstanceName, true, output);
|
|
155
|
+
output?.success(`${otherName} stopped`).flush();
|
|
156
|
+
return false;
|
|
146
157
|
}
|
|
147
158
|
};
|
|
148
159
|
exports.StartInstanceUseCase = StartInstanceUseCase;
|
|
149
160
|
exports.StartInstanceUseCase = StartInstanceUseCase = __decorate([
|
|
150
161
|
(0, tsyringe_1.injectable)(),
|
|
151
162
|
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.IInstanceManagerPort)),
|
|
152
|
-
|
|
163
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IUserInteractionPort)),
|
|
164
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
153
165
|
], StartInstanceUseCase);
|
|
154
166
|
//# sourceMappingURL=start-instance.use-case.js.map
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Application Use Cases
|
|
4
4
|
*
|
|
5
5
|
* 業務流程編排層,協調 Domain 層與 Infrastructure 層
|
|
6
|
+
* 按指令群組組織
|
|
6
7
|
*/
|
|
7
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -19,10 +20,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
19
20
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
21
|
};
|
|
21
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
|
|
23
|
-
__exportStar(require("./
|
|
24
|
-
|
|
25
|
-
__exportStar(require("./
|
|
26
|
-
|
|
27
|
-
__exportStar(require("./
|
|
23
|
+
// Env 指令群組 (instance, setup, credentials)
|
|
24
|
+
__exportStar(require("./env"), exports);
|
|
25
|
+
// Config 指令群組
|
|
26
|
+
__exportStar(require("./config"), exports);
|
|
27
|
+
// Logs 指令群組
|
|
28
|
+
__exportStar(require("./logs"), exports);
|
|
29
|
+
// Dev 指令群組
|
|
30
|
+
__exportStar(require("./dev"), exports);
|
|
28
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logs 指令群組 Use Cases
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./show-logs.use-case"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|