@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.
Files changed (240) hide show
  1. package/README.md +159 -3
  2. package/THIRD-PARTY-NOTICES.txt +84 -2
  3. package/dist/application/dtos/config/index.js +22 -0
  4. package/dist/application/dtos/config/request/index.js +19 -0
  5. package/dist/application/dtos/config/request/set-config.request.dto.js +3 -0
  6. package/dist/application/dtos/config/response/get-config.response.dto.js +8 -0
  7. package/dist/application/dtos/config/response/index.js +21 -0
  8. package/dist/application/dtos/dev/index.js +24 -0
  9. package/dist/application/dtos/dev/request/generate-component.request.dto.js +3 -0
  10. package/dist/application/dtos/dev/request/index.js +25 -0
  11. package/dist/application/dtos/dev/request/install-template.request.dto.js +3 -0
  12. package/dist/application/dtos/dev/request/new-project.request.dto.js +3 -0
  13. package/dist/application/dtos/dev/request/publish-plugin.request.dto.js +3 -0
  14. package/dist/application/dtos/dev/request/uninstall-template.request.dto.js +3 -0
  15. package/dist/application/dtos/dev/response/generate-component.response.dto.js +3 -0
  16. package/dist/application/dtos/dev/response/index.js +24 -0
  17. package/dist/application/dtos/dev/response/new-project.response.dto.js +3 -0
  18. package/dist/application/dtos/dev/response/publish-plugin.response.dto.js +3 -0
  19. package/dist/application/dtos/dev/response/template-info.response.dto.js +3 -0
  20. package/dist/application/dtos/env/index.js +24 -0
  21. package/dist/application/dtos/{request → env/request}/index.js +1 -3
  22. package/dist/application/dtos/env/request/list-running-instances.request.dto.js +3 -0
  23. package/dist/application/dtos/{response → env/response}/index.js +1 -1
  24. package/dist/application/dtos/env/response/list-running-instances.response.dto.js +3 -0
  25. package/dist/application/dtos/index.js +9 -2
  26. package/dist/application/dtos/logs/index.js +22 -0
  27. package/dist/application/dtos/logs/request/index.js +18 -0
  28. package/dist/application/dtos/logs/response/index.js +18 -0
  29. package/dist/application/index.js +1 -0
  30. package/dist/application/services/env/environment-validator.service.js +120 -0
  31. package/dist/application/services/env/index.js +11 -0
  32. package/dist/application/services/env/instance-cleanup.service.js +79 -0
  33. package/dist/application/services/index.js +21 -0
  34. package/dist/application/use-cases/config/get-all-config.use-case.js +54 -0
  35. package/dist/application/use-cases/config/get-config.use-case.js +7 -17
  36. package/dist/application/use-cases/config/index.js +23 -0
  37. package/dist/application/use-cases/config/set-config.use-case.js +49 -3
  38. package/dist/application/use-cases/dev/generate-component.use-case.js +138 -0
  39. package/dist/application/use-cases/dev/index.js +28 -0
  40. package/dist/application/use-cases/dev/install-template.use-case.js +60 -0
  41. package/dist/application/use-cases/dev/list-templates.use-case.js +45 -0
  42. package/dist/application/use-cases/dev/new-project.use-case.js +130 -0
  43. package/dist/application/use-cases/dev/publish-plugin.use-case.js +243 -0
  44. package/dist/application/use-cases/dev/uninstall-template.use-case.js +50 -0
  45. package/dist/application/use-cases/{instance → env}/delete-instance.use-case.js +2 -2
  46. package/dist/application/use-cases/{credentials → env}/get-credentials.use-case.js +1 -1
  47. package/dist/application/use-cases/{instance → env}/index.js +11 -0
  48. package/dist/application/use-cases/{instance → env}/install-instance.use-case.js +25 -136
  49. package/dist/application/use-cases/env/list-running-instances.use-case.js +47 -0
  50. package/dist/application/use-cases/{setup → env}/setup-environment.use-case.js +35 -4
  51. package/dist/application/use-cases/{instance → env}/start-instance.use-case.js +53 -41
  52. package/dist/application/use-cases/index.js +9 -6
  53. package/dist/application/use-cases/logs/index.js +21 -0
  54. package/dist/cli.js +175 -26
  55. package/dist/constants/defaults.js +8 -1
  56. package/dist/constants/oci-artifacts.js +4 -0
  57. package/dist/constants/paths.js +0 -4
  58. package/dist/constants/timeouts.js +5 -0
  59. package/dist/di/modules/application.module.js +13 -44
  60. package/dist/di/modules/config.module.js +19 -0
  61. package/dist/di/modules/dev.module.js +45 -0
  62. package/dist/di/modules/env.module.js +42 -0
  63. package/dist/di/modules/index.js +30 -0
  64. package/dist/di/modules/infrastructure.module.js +65 -82
  65. package/dist/di/modules/logs.module.js +15 -0
  66. package/dist/di/modules/presentation.module.js +18 -7
  67. package/dist/di/tokens.js +42 -30
  68. package/dist/domain/entities/credentials-resolver.entity.js +4 -1
  69. package/dist/domain/entities/credentials.entity.js +2 -5
  70. package/dist/domain/entities/delete-instance-validation.entity.js +4 -1
  71. package/dist/domain/entities/deployment-parameters.entity.js +11 -8
  72. package/dist/domain/entities/index.js +5 -1
  73. package/dist/domain/entities/instance-list-aggregator.entity.js +2 -2
  74. package/dist/domain/entities/instance-metadata.entity.js +2 -3
  75. package/dist/domain/entities/instance.entity.js +2 -1
  76. package/dist/domain/entities/log-filter.entity.js +6 -6
  77. package/dist/domain/entities/template-info.entity.js +63 -0
  78. package/dist/{infrastructure/utils/error-formatter.util.js → domain/errors/error-utils.js} +1 -1
  79. package/dist/domain/errors/index.js +19 -0
  80. package/dist/domain/index.js +6 -0
  81. package/dist/domain/interfaces/index.js +21 -0
  82. package/dist/domain/ports/dotnet-publisher.port.js +9 -0
  83. package/dist/{infrastructure/http/interfaces/http-client.interface.js → domain/ports/file-system.port.js} +1 -1
  84. package/dist/domain/ports/http-client.port.js +3 -0
  85. package/dist/domain/ports/index.js +10 -0
  86. package/dist/domain/ports/logger.port.js +3 -3
  87. package/dist/domain/ports/progress.port.js +10 -0
  88. package/dist/domain/ports/project-scaffolder.port.js +9 -0
  89. package/dist/domain/ports/schematics-runner.port.js +9 -0
  90. package/dist/domain/ports/template-manager.port.js +9 -0
  91. package/dist/domain/ports/wsl-setup.port.js +3 -0
  92. package/dist/domain/types/index.js +1 -0
  93. package/dist/domain/value-objects/acr-credentials.value-object.js +13 -1
  94. package/dist/domain/value-objects/chart-version.value-object.js +20 -3
  95. package/dist/domain/value-objects/config-log-level.value-object.js +2 -1
  96. package/dist/domain/value-objects/index.js +3 -1
  97. package/dist/domain/value-objects/instance-name.value-object.js +6 -3
  98. package/dist/domain/value-objects/jwt-key.value-object.js +27 -5
  99. package/dist/domain/value-objects/mssql-password.value-object.js +27 -5
  100. package/dist/domain/value-objects/output-path.value-object.js +85 -0
  101. package/dist/domain/value-objects/rsa-key-pair.value-object.js +29 -3
  102. package/dist/index.js +6 -1
  103. package/dist/infrastructure/config/{app-config.service.js → app-config.adapter.js} +29 -17
  104. package/dist/infrastructure/config/{config-validator.js → config-validator.util.js} +1 -1
  105. package/dist/infrastructure/config/index.js +14 -0
  106. package/dist/infrastructure/config/interfaces/index.js +3 -0
  107. package/dist/infrastructure/config/{crypto.service.js → services/crypto.service.js} +4 -1
  108. package/dist/infrastructure/config/services/index.js +9 -0
  109. package/dist/infrastructure/deployment/deployment.adapter.js +5 -5
  110. package/dist/infrastructure/deployment/index.js +9 -0
  111. package/dist/infrastructure/deployment/interfaces/index.js +3 -0
  112. package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +18 -7
  113. package/dist/infrastructure/deployment/services/app-manager.service.js +3 -3
  114. package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +13 -5
  115. package/dist/infrastructure/deployment/services/helm-registry.service.js +10 -3
  116. package/dist/infrastructure/deployment/services/index.js +35 -0
  117. package/dist/infrastructure/deployment/services/infra-manager.service.js +19 -15
  118. package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +24 -6
  119. package/dist/infrastructure/deployment/services/mssql-database-init.service.js +75 -11
  120. package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +17 -5
  121. package/dist/infrastructure/deployment/services/mssql-storage.service.js +5 -1
  122. package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +7 -3
  123. package/dist/infrastructure/deployment/services/oci-artifact.service.js +8 -3
  124. package/dist/infrastructure/deployment/services/secret-manager.service.js +14 -5
  125. package/dist/infrastructure/deployment/services/service-manager.service.js +1 -1
  126. package/dist/infrastructure/deployment/services/version-compatibility.service.js +9 -5
  127. package/dist/infrastructure/dotnet/dotnet-publisher.adapter.js +143 -0
  128. package/dist/infrastructure/dotnet/index.js +9 -0
  129. package/dist/infrastructure/environment/index.js +11 -0
  130. package/dist/infrastructure/environment/interfaces/index.js +3 -0
  131. package/dist/infrastructure/{platform-detector.js → environment/platform-detector.util.js} +1 -1
  132. package/dist/infrastructure/environment/services/hardware-info.service.js +8 -8
  133. package/dist/infrastructure/environment/services/index.js +11 -0
  134. package/dist/infrastructure/errors/{error-handler.js → error-handler.adapter.js} +17 -17
  135. package/dist/infrastructure/errors/index.js +5 -21
  136. package/dist/infrastructure/execution/builders/base-command.builder.js +32 -11
  137. package/dist/infrastructure/execution/builders/index.js +26 -0
  138. package/dist/infrastructure/execution/builders/wsl-command.builder.js +39 -1
  139. package/dist/infrastructure/execution/command-builder.util.js +94 -0
  140. package/dist/infrastructure/execution/environments/index.js +23 -0
  141. package/dist/infrastructure/execution/environments/wsl-execution.environment.js +22 -7
  142. package/dist/infrastructure/execution/index.js +1 -2
  143. package/dist/infrastructure/execution/services/command-executor.service.js +389 -0
  144. package/dist/infrastructure/execution/services/index.js +8 -0
  145. package/dist/infrastructure/execution/{script-executor.service.js → services/script-executor.service.js} +12 -15
  146. package/dist/infrastructure/filesystem/filesystem.adapter.js +86 -0
  147. package/dist/infrastructure/filesystem/index.js +23 -0
  148. package/dist/infrastructure/http/{http-client.service.js → http-client.adapter.js} +20 -13
  149. package/dist/infrastructure/http/index.js +1 -1
  150. package/dist/infrastructure/interceptors/decorators/index.js +23 -0
  151. package/dist/infrastructure/interceptors/index.js +2 -2
  152. package/dist/infrastructure/interceptors/interceptor.factory.js +3 -3
  153. package/dist/infrastructure/interceptors/interfaces/index.js +3 -0
  154. package/dist/infrastructure/interceptors/services/index.js +6 -0
  155. package/dist/infrastructure/interceptors/{logging.interceptor.js → services/logging-interceptor.service.js} +4 -4
  156. package/dist/infrastructure/logger/services/file-log-reader.repository.js +1 -1
  157. package/dist/infrastructure/logger/services/file-log-writer.repository.js +1 -1
  158. package/dist/infrastructure/persistence/instance-metadata.adapter.js +2 -1
  159. package/dist/infrastructure/persistence/services/file-system-config.repository.js +10 -4
  160. package/dist/infrastructure/persistence/services/file-system-instance.repository.js +2 -2
  161. package/dist/infrastructure/platforms/index.js +18 -0
  162. package/dist/infrastructure/platforms/windows/index.js +13 -0
  163. package/dist/infrastructure/platforms/windows/interfaces/index.js +3 -0
  164. package/dist/infrastructure/platforms/windows/parsers/index.js +9 -0
  165. package/dist/infrastructure/platforms/windows/services/index.js +33 -0
  166. package/dist/infrastructure/platforms/windows/services/microk8s.service.js +28 -10
  167. package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +7 -3
  168. package/dist/infrastructure/platforms/windows/services/windows-features.service.js +22 -8
  169. package/dist/infrastructure/platforms/windows/services/windows-info.service.js +10 -6
  170. package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +15 -6
  171. package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +12 -12
  172. package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +3 -3
  173. package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +76 -22
  174. package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +20 -15
  175. package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +3 -3
  176. package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +8 -3
  177. package/dist/infrastructure/platforms/windows/wsl-setup.adapter.js +100 -0
  178. package/dist/infrastructure/schematics/index.js +11 -0
  179. package/dist/infrastructure/schematics/project-scaffolder.adapter.js +314 -0
  180. package/dist/infrastructure/schematics/schematics-runner.adapter.js +175 -0
  181. package/dist/infrastructure/template/index.js +25 -0
  182. package/dist/infrastructure/template/interfaces/index.js +22 -0
  183. package/dist/infrastructure/template/interfaces/template-downloader.interface.js +6 -0
  184. package/dist/infrastructure/template/interfaces/template-registry.interface.js +6 -0
  185. package/dist/infrastructure/template/services/index.js +24 -0
  186. package/dist/infrastructure/template/services/template-downloader.service.js +319 -0
  187. package/dist/infrastructure/template/services/template-registry.service.js +175 -0
  188. package/dist/infrastructure/template/template-manager.adapter.js +196 -0
  189. package/dist/infrastructure/utils/index.js +23 -0
  190. package/dist/infrastructure/utils/input-validator.util.js +7 -6
  191. package/dist/presentation/controllers/base.controller.js +36 -0
  192. package/dist/presentation/controllers/{config.controller.js → config/config.controller.js} +17 -62
  193. package/dist/presentation/controllers/config/index.js +21 -0
  194. package/dist/presentation/controllers/dev/dev.controller.js +202 -0
  195. package/dist/presentation/controllers/dev/index.js +23 -0
  196. package/dist/presentation/controllers/dev/template.controller.js +158 -0
  197. package/dist/presentation/controllers/{credentials.controller.js → env/credentials.controller.js} +8 -14
  198. package/dist/presentation/controllers/env/index.js +23 -0
  199. package/dist/presentation/controllers/{instance.controller.js → env/instance.controller.js} +35 -92
  200. package/dist/presentation/controllers/{setup.controller.js → env/setup.controller.js} +33 -66
  201. package/dist/presentation/controllers/index.js +9 -5
  202. package/dist/presentation/controllers/logs/index.js +21 -0
  203. package/dist/presentation/controllers/{logs.controller.js → logs/logs.controller.js} +8 -14
  204. package/dist/presentation/interfaces/index.js +21 -0
  205. package/dist/presentation/prompts/acr-credentials.prompt.js +37 -9
  206. package/dist/presentation/ui/constants/index.js +23 -0
  207. package/dist/presentation/ui/interaction.service.js +4 -4
  208. package/dist/presentation/ui/interfaces/cli-progress.interface.js +0 -6
  209. package/dist/presentation/ui/interfaces/index.js +6 -0
  210. package/package.json +6 -1
  211. package/dist/application/dtos/request/set-config.request.dto.js +0 -16
  212. package/dist/infrastructure/errors/error-handler.interface.js +0 -3
  213. package/dist/infrastructure/execution/command-builder.js +0 -252
  214. package/dist/infrastructure/execution/command-executor.service.js +0 -230
  215. /package/dist/application/dtos/{request → config/request}/get-config.request.dto.js +0 -0
  216. /package/dist/application/dtos/{request → env/request}/delete-instance.request.dto.js +0 -0
  217. /package/dist/application/dtos/{request → env/request}/get-credentials.request.dto.js +0 -0
  218. /package/dist/application/dtos/{request → env/request}/install-instance.request.dto.js +0 -0
  219. /package/dist/application/dtos/{request → env/request}/list-charts.request.dto.js +0 -0
  220. /package/dist/application/dtos/{request → env/request}/setup-environment.request.dto.js +0 -0
  221. /package/dist/application/dtos/{request → env/request}/start-instance.request.dto.js +0 -0
  222. /package/dist/application/dtos/{request → env/request}/stop-instance.request.dto.js +0 -0
  223. /package/dist/application/dtos/{response → env/response}/credentials.response.dto.js +0 -0
  224. /package/dist/application/dtos/{response → env/response}/delete-instance.response.dto.js +0 -0
  225. /package/dist/application/dtos/{response → env/response}/install-instance.response.dto.js +0 -0
  226. /package/dist/application/dtos/{response → env/response}/instance-list.response.dto.js +0 -0
  227. /package/dist/application/dtos/{response → env/response}/instance-status.response.dto.js +0 -0
  228. /package/dist/application/dtos/{response → env/response}/setup-result.response.dto.js +0 -0
  229. /package/dist/application/dtos/{response → env/response}/start-instance.response.dto.js +0 -0
  230. /package/dist/application/dtos/{response → env/response}/stop-instance.response.dto.js +0 -0
  231. /package/dist/application/dtos/{request → logs/request}/show-logs.request.dto.js +0 -0
  232. /package/dist/application/dtos/{response → logs/response}/show-logs.response.dto.js +0 -0
  233. /package/dist/application/use-cases/{instance → env}/list-charts.use-case.js +0 -0
  234. /package/dist/application/use-cases/{instance → env}/list-instances.use-case.js +0 -0
  235. /package/dist/application/use-cases/{instance → env}/stop-instance.use-case.js +0 -0
  236. /package/dist/{infrastructure → domain}/errors/app-error.js +0 -0
  237. /package/dist/{infrastructure → domain}/errors/exit-codes.js +0 -0
  238. /package/dist/infrastructure/config/{app-config.interface.js → interfaces/app-config.interface.js} +0 -0
  239. /package/dist/{domain → infrastructure/interceptors}/decorators/sensitive.decorator.js +0 -0
  240. /package/dist/infrastructure/interceptors/{interceptor.interface.js → interfaces/interceptor.interface.js} +0 -0
@@ -16,7 +16,8 @@ exports.K8sJobRunnerService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const deployment_parameters_entity_1 = require("../../../domain/entities/deployment-parameters.entity");
19
- const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
19
+ const sensitive_decorator_1 = require("../../interceptors/decorators/sensitive.decorator");
20
+ const errors_1 = require("../../../domain/errors");
20
21
  /**
21
22
  * Kubernetes Job 執行服務
22
23
  *
@@ -35,6 +36,13 @@ let K8sJobRunnerService = class K8sJobRunnerService {
35
36
  */
36
37
  async createImagePullSecret(instanceName, params) {
37
38
  const { name: acrName, account: acrAccount, password: acrPassword } = params.acrCredentials;
39
+ // 防禦性驗證:確保 ACR 名稱與帳號僅包含安全字元
40
+ if (!/^[a-zA-Z0-9-]+$/.test(acrName)) {
41
+ throw new errors_1.AppError(`Invalid ACR name: "${acrName}"`, { exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR });
42
+ }
43
+ if (!/^[a-zA-Z0-9-]+$/.test(acrAccount)) {
44
+ throw new errors_1.AppError(`Invalid ACR account: "${acrAccount}"`, { exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR });
45
+ }
38
46
  const registry = `${acrName}.azurecr.io`;
39
47
  const env = this.envFactory.getForInstance(instanceName);
40
48
  const builder = env.kubectl('create secret docker-registry')
@@ -51,7 +59,10 @@ let K8sJobRunnerService = class K8sJobRunnerService {
51
59
  await deleteBuilder.exec();
52
60
  const result = await builder.exec();
53
61
  if (result.exitCode !== 0) {
54
- throw new Error(`Failed to create image pull secret: ${result.stderr}`);
62
+ this.logger.debug('kubectl create secret failed', { stderr: result.stderr });
63
+ throw new errors_1.AppError('Failed to create image pull secret', {
64
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
65
+ });
55
66
  }
56
67
  }
57
68
  /**
@@ -121,7 +132,10 @@ let K8sJobRunnerService = class K8sJobRunnerService {
121
132
  .sensitiveStdin(JSON.stringify(jobYaml));
122
133
  const applyResult = await applyBuilder.exec();
123
134
  if (applyResult.exitCode !== 0) {
124
- throw new Error(`Failed to start job ${jobName}: ${applyResult.stderr}`);
135
+ this.logger.debug(`kubectl apply job failed for ${jobName}`, { stderr: applyResult.stderr });
136
+ throw new errors_1.AppError(`Failed to start job ${jobName}`, {
137
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
138
+ });
125
139
  }
126
140
  // 等待完成
127
141
  const waitBuilder = env.kubectl('wait')
@@ -136,9 +150,13 @@ let K8sJobRunnerService = class K8sJobRunnerService {
136
150
  .arg(`job/${jobName}`)
137
151
  .arg('-n', 'mssql');
138
152
  const logs = await logsBuilder.exec();
139
- this.logger.error(new Error(`Job ${jobName} logs:
140
- ${logs.stdout}`));
141
- throw new Error(`Job ${jobName} failed or timed out`);
153
+ this.logger.error(new Error(`Job ${jobName} failed`), {
154
+ jobName,
155
+ logs: logs.stdout,
156
+ });
157
+ throw new errors_1.AppError(`Job ${jobName} failed or timed out`, {
158
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
159
+ });
142
160
  }
143
161
  // 清理 Job
144
162
  const cleanupBuilder = env.kubectl('delete')
@@ -16,14 +16,17 @@ exports.MssqlDatabaseInitService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const paths_1 = require("../../../constants/paths");
19
+ const oci_artifacts_1 = require("../../../constants/oci-artifacts");
20
+ const errors_1 = require("../../../domain/errors");
19
21
  /**
20
22
  * MSSQL 資料庫初始化服務
21
23
  *
22
24
  * 負責等待 MSSQL Pod 就緒和建立初始資料庫
23
25
  */
24
26
  let MssqlDatabaseInitService = class MssqlDatabaseInitService {
25
- constructor(envFactory, logger) {
27
+ constructor(envFactory, ociArtifact, logger) {
26
28
  this.envFactory = envFactory;
29
+ this.ociArtifact = ociArtifact;
27
30
  this.logger = logger;
28
31
  }
29
32
  /**
@@ -40,7 +43,10 @@ let MssqlDatabaseInitService = class MssqlDatabaseInitService {
40
43
  .arg('--timeout=300s');
41
44
  const result = await builder.exec();
42
45
  if (result.exitCode !== 0) {
43
- throw new Error(`MSSQL Pod not ready: ${result.stderr}`);
46
+ this.logger.debug('MSSQL Pod not ready', { stderr: result.stderr });
47
+ throw new errors_1.AppError('MSSQL Pod not ready', {
48
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
49
+ });
44
50
  }
45
51
  }
46
52
  /**
@@ -57,7 +63,10 @@ let MssqlDatabaseInitService = class MssqlDatabaseInitService {
57
63
  .arg('-o', "jsonpath='{.items[0].metadata.name}'");
58
64
  const result = await builder.exec();
59
65
  if (result.exitCode !== 0 || !result.stdout.trim()) {
60
- throw new Error(`Failed to get MSSQL Pod name: ${result.stderr}`);
66
+ this.logger.debug('Failed to get MSSQL Pod name', { stderr: result.stderr });
67
+ throw new errors_1.AppError('Failed to get MSSQL Pod name', {
68
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
69
+ });
61
70
  }
62
71
  return result.stdout.trim();
63
72
  }
@@ -82,9 +91,9 @@ let MssqlDatabaseInitService = class MssqlDatabaseInitService {
82
91
  .arg(paths_1.LINUX_PATHS.SQLCMD_BIN)
83
92
  .arg('-S', 'localhost')
84
93
  .arg('-U', 'sa')
85
- .sensitiveArg('-P', `'${saPassword}'`)
94
+ .sensitiveArg('-P', saPassword)
86
95
  .arg('-C')
87
- .arg('-Q', '"SELECT 1"');
96
+ .arg('-Q', 'SELECT 1');
88
97
  const result = await builder.exec();
89
98
  if (result.exitCode === 0) {
90
99
  this.logger.debug('[Mssql] SQL Server is ready');
@@ -95,7 +104,9 @@ let MssqlDatabaseInitService = class MssqlDatabaseInitService {
95
104
  await new Promise(resolve => setTimeout(resolve, retryDelay));
96
105
  }
97
106
  }
98
- throw new Error('SQL Server failed to start accepting connections after 60 seconds');
107
+ throw new errors_1.AppError('SQL Server failed to start accepting connections after 60 seconds', {
108
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
109
+ });
99
110
  }
100
111
  /**
101
112
  * 建立初始資料庫 (uofx_dev, uofx_search)
@@ -119,13 +130,65 @@ let MssqlDatabaseInitService = class MssqlDatabaseInitService {
119
130
  .arg(paths_1.LINUX_PATHS.SQLCMD_BIN)
120
131
  .arg('-S', 'localhost')
121
132
  .arg('-U', 'sa')
122
- .sensitiveArg('-P', `'${saPassword}'`)
133
+ .sensitiveArg('-P', saPassword)
123
134
  .arg('-C')
124
- .arg('-Q', '"CREATE DATABASE uofx_dev; CREATE DATABASE uofx_search;"');
135
+ .arg('-Q', 'CREATE DATABASE uofx_dev; CREATE DATABASE uofx_search;');
125
136
  this.logger.debug(`[Mssql] Creating databases uofx_dev, uofx_search...`);
126
137
  const result = await builder.exec();
127
138
  if (result.exitCode !== 0) {
128
- throw new Error(`Failed to create initial databases: ${result.stderr}`);
139
+ this.logger.debug('Failed to create initial databases', { stderr: result.stderr });
140
+ throw new errors_1.AppError('Failed to create initial databases', {
141
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
142
+ });
143
+ }
144
+ }
145
+ /**
146
+ * 安裝 Northwind 範例資料庫
147
+ * @param instanceName WSL 實例名稱
148
+ * @param saPassword SA 密碼
149
+ * @param acrCredentials ACR 憑證(用於下載 SQL 腳本)
150
+ */
151
+ async installNorthwindDatabase(instanceName, saPassword, acrCredentials) {
152
+ this.logger.debug('[Mssql] Installing Northwind sample database...');
153
+ const podName = await this.getMssqlPodName(instanceName);
154
+ const env = this.envFactory.getForInstance(instanceName);
155
+ const remoteScriptPath = '/tmp/instnwnd.sql';
156
+ // 1. 建立 Northwind 資料庫
157
+ const createDbBuilder = env.kubectl('exec')
158
+ .arg('-n', 'mssql')
159
+ .arg(podName)
160
+ .arg('--')
161
+ .arg(paths_1.LINUX_PATHS.SQLCMD_BIN)
162
+ .arg('-S', 'localhost')
163
+ .arg('-U', 'sa')
164
+ .sensitiveArg('-P', saPassword)
165
+ .arg('-C')
166
+ .arg('-Q', 'CREATE DATABASE Northwind;');
167
+ const createResult = await createDbBuilder.exec();
168
+ if (createResult.exitCode !== 0) {
169
+ this.logger.debug('Failed to create Northwind database', { stderr: createResult.stderr });
170
+ throw new errors_1.AppError('Failed to create Northwind database', {
171
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
172
+ });
173
+ }
174
+ // 2. 從 ACR 下載 SQL 腳本到 WSL /tmp/
175
+ await this.ociArtifact.downloadToInstance(instanceName, oci_artifacts_1.OCI_ARTIFACTS.NORTHWIND_SQL, oci_artifacts_1.OCI_ARTIFACT_TAGS.NORTHWIND_SQL, remoteScriptPath, acrCredentials);
176
+ // 3. 透過 shell pipeline 執行 SQL 腳本並清理暫存檔
177
+ const passB64 = Buffer.from(saPassword).toString('base64');
178
+ const kubectlPath = paths_1.LINUX_PATHS.MICROK8S_KUBECTL;
179
+ const sqlcmdPath = paths_1.LINUX_PATHS.SQLCMD_BIN;
180
+ const pipelineScript = [
181
+ 'set -e',
182
+ `PASS=$(echo '${passB64}' | base64 -d)`,
183
+ `cat ${remoteScriptPath} | ${kubectlPath} exec -i -n mssql ${podName} -- ${sqlcmdPath} -S localhost -U sa -P "\${PASS}" -d Northwind -C`,
184
+ `rm -f ${remoteScriptPath}`,
185
+ ].join('\n');
186
+ const result = await env.shell('bash').sensitiveStdin(pipelineScript).exec();
187
+ if (result.exitCode !== 0) {
188
+ this.logger.debug('Failed to execute Northwind SQL script', { stderr: result.stderr });
189
+ throw new errors_1.AppError('Failed to install Northwind sample database', {
190
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
191
+ });
129
192
  }
130
193
  }
131
194
  };
@@ -133,7 +196,8 @@ exports.MssqlDatabaseInitService = MssqlDatabaseInitService;
133
196
  exports.MssqlDatabaseInitService = MssqlDatabaseInitService = __decorate([
134
197
  (0, tsyringe_1.injectable)(),
135
198
  __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
136
- __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
137
- __metadata("design:paramtypes", [Object, Object])
199
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IOciArtifactService)),
200
+ __param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
201
+ __metadata("design:paramtypes", [Object, Object, Object])
138
202
  ], MssqlDatabaseInitService);
139
203
  //# sourceMappingURL=mssql-database-init.service.js.map
@@ -16,6 +16,7 @@ exports.MssqlHelmDeploymentService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const base_helm_deployment_service_1 = require("./base-helm-deployment.service");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * MSSQL Helm 部署服務
21
22
  *
@@ -66,9 +67,9 @@ let MssqlHelmDeploymentService = class MssqlHelmDeploymentService extends base_h
66
67
  .arg('--timeout', '10m')
67
68
  .arg('-n', 'mssql')
68
69
  .arg('database')
69
- .arg(`"${chartPath}"`)
70
+ .arg(chartPath)
70
71
  .arg('--create-namespace')
71
- .sensitiveArg('--set', `sa_password='${params.saPassword}'`);
72
+ .sensitiveArg('--set', `sa_password=${params.saPassword}`);
72
73
  this.logger.debug('[Mssql] Installing MSSQL chart', {
73
74
  chart: 'mssql-latest',
74
75
  chartPath: chartPath,
@@ -77,14 +78,25 @@ let MssqlHelmDeploymentService = class MssqlHelmDeploymentService extends base_h
77
78
  });
78
79
  const result = await builder.exec();
79
80
  if (result.exitCode !== 0) {
80
- throw new Error(`Failed to deploy MSSQL: ${result.stderr}`);
81
+ this.logger.debug('Failed to deploy MSSQL', { stderr: result.stderr });
82
+ throw new errors_1.AppError('Failed to deploy MSSQL', {
83
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
84
+ });
81
85
  }
82
86
  }
83
87
  /**
84
- * 安裝後處理:建立初始資料庫
88
+ * 安裝後處理:建立初始資料庫與 Northwind 範例資料庫
85
89
  */
86
- async postInstall(instanceName, params, _output) {
90
+ async postInstall(instanceName, params, output) {
87
91
  await this.dbInitService.createInitialDatabases(instanceName, params.saPassword.toString());
92
+ if (params.skipNorthwind) {
93
+ output?.item('bullet', 'Skipping Northwind sample database (--no-northwind)').flush();
94
+ }
95
+ else {
96
+ output?.item('arrow', 'Installing Northwind sample database...').flush();
97
+ await this.dbInitService.installNorthwindDatabase(instanceName, params.saPassword.toString(), params.acrCredentials);
98
+ output?.item('check', 'Northwind database installed').flush();
99
+ }
88
100
  }
89
101
  };
90
102
  exports.MssqlHelmDeploymentService = MssqlHelmDeploymentService;
@@ -16,6 +16,7 @@ exports.MssqlStorageService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const paths_1 = require("../../../constants/paths");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * MSSQL 存儲服務
21
22
  *
@@ -39,7 +40,10 @@ let MssqlStorageService = class MssqlStorageService {
39
40
  .args(...directories);
40
41
  const result = await builder.exec();
41
42
  if (result.exitCode !== 0) {
42
- throw new Error(`Failed to create MSSQL directories: ${result.stderr}`);
43
+ this.logger.debug('Failed to create MSSQL directories', { stderr: result.stderr });
44
+ throw new errors_1.AppError('Failed to create MSSQL directories', {
45
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
46
+ });
43
47
  }
44
48
  this.logger.debug('[Mssql] Directories created successfully');
45
49
  }
@@ -16,6 +16,7 @@ exports.MssqlUserManagerService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const paths_1 = require("../../../constants/paths");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * MSSQL 使用者管理服務
21
22
  *
@@ -45,13 +46,16 @@ let MssqlUserManagerService = class MssqlUserManagerService {
45
46
  .arg(paths_1.LINUX_PATHS.SQLCMD_BIN)
46
47
  .arg('-S', 'localhost')
47
48
  .arg('-U', 'sa')
48
- .sensitiveArg('-P', `'${params.saPassword}'`)
49
+ .sensitiveArg('-P', params.saPassword.toString())
49
50
  .arg('-d', 'uofx_dev')
50
51
  .arg('-C')
51
- .sensitiveArg('-Q', `"${query}"`);
52
+ .sensitiveArg('-Q', query);
52
53
  const result = await builder.exec();
53
54
  if (result.exitCode !== 0) {
54
- throw new Error(`Failed to create admin user: ${result.stderr}`);
55
+ this.logger.debug('Failed to create admin user', { stderr: result.stderr });
56
+ throw new errors_1.AppError('Failed to create admin user', {
57
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
58
+ });
55
59
  }
56
60
  }
57
61
  };
@@ -15,7 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.OciArtifactService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
- const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
18
+ const sensitive_decorator_1 = require("../../interceptors/decorators/sensitive.decorator");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * OCI Artifact 下載服務
21
22
  *
@@ -49,7 +50,9 @@ let OciArtifactService = class OciArtifactService {
49
50
  // 3. 取得 Blob Digest
50
51
  const digest = this.extractDigest(manifestResponse);
51
52
  if (!digest) {
52
- throw new Error(`Could not find digest in manifest for ${repo}:${tag}`);
53
+ throw new errors_1.AppError(`Could not find digest in manifest for ${repo}:${tag}`, {
54
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
55
+ });
53
56
  }
54
57
  // 4. 下載 Blob
55
58
  const blobUrl = `https://${registry}/v2/${repo}/blobs/${digest}`;
@@ -84,7 +87,9 @@ let OciArtifactService = class OciArtifactService {
84
87
  repo,
85
88
  tag
86
89
  });
87
- throw new Error(`Failed to download OCI artifact: ${errorDetail}`);
90
+ throw new errors_1.AppError(`Failed to download OCI artifact: ${errorDetail}`, {
91
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
92
+ });
88
93
  }
89
94
  this.logger.debug(`Successfully downloaded OCI artifact to instance: ${targetPath}`);
90
95
  }
@@ -15,7 +15,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SecretManagerService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
- const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
18
+ const sensitive_decorator_1 = require("../../interceptors/decorators/sensitive.decorator");
19
+ const errors_1 = require("../../../domain/errors");
19
20
  /**
20
21
  * Secret 管理服務
21
22
  *
@@ -53,7 +54,10 @@ let SecretManagerService = class SecretManagerService {
53
54
  .arg(namespace);
54
55
  const result = await builder.exec();
55
56
  if (result.exitCode !== 0) {
56
- throw new Error(`Failed to create namespace: ${result.stderr}`);
57
+ this.logger.debug('kubectl create namespace failed', { stderr: result.stderr });
58
+ throw new errors_1.AppError('Failed to create Kubernetes namespace', {
59
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
60
+ });
57
61
  }
58
62
  }
59
63
  /**
@@ -100,7 +104,10 @@ let SecretManagerService = class SecretManagerService {
100
104
  .sensitiveStdin(JSON.stringify(secretManifest));
101
105
  const result = await builder.exec();
102
106
  if (result.exitCode !== 0) {
103
- throw new Error(`Failed to create global config secret: ${result.stderr}`);
107
+ this.logger.debug('kubectl apply secret failed', { stderr: result.stderr });
108
+ throw new errors_1.AppError('Failed to create global config secret', {
109
+ exitCode: errors_1.EXIT_CODES.SYSTEM_ERROR,
110
+ });
104
111
  }
105
112
  output?.success(`Global config secret created in ${namespace}`).flush();
106
113
  }
@@ -120,8 +127,10 @@ let SecretManagerService = class SecretManagerService {
120
127
  .arg('-o', 'json');
121
128
  const result = await builder.exec();
122
129
  if (result.exitCode !== 0) {
123
- this.logger.error(new Error(`kubectl exited with code ${result.exitCode}`));
124
- this.logger.error(new Error(`Stderr: ${result.stderr}`));
130
+ this.logger.error(new Error('kubectl get secret failed'), {
131
+ exitCode: result.exitCode,
132
+ stderr: result.stderr,
133
+ });
125
134
  return null;
126
135
  }
127
136
  const secret = JSON.parse(result.stdout);
@@ -16,7 +16,7 @@ exports.ServiceManagerService = void 0;
16
16
  const tsyringe_1 = require("tsyringe");
17
17
  const tokens_1 = require("../../../di/tokens");
18
18
  const deployment_parameters_entity_1 = require("../../../domain/entities/deployment-parameters.entity");
19
- const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
19
+ const sensitive_decorator_1 = require("../../interceptors/decorators/sensitive.decorator");
20
20
  const base_helm_deployment_service_1 = require("./base-helm-deployment.service");
21
21
  /**
22
22
  * Service Manager Service
@@ -49,8 +49,8 @@ exports.VersionCompatibilityService = void 0;
49
49
  const tsyringe_1 = require("tsyringe");
50
50
  const semver = __importStar(require("semver"));
51
51
  const tokens_1 = require("../../../di/tokens");
52
- const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
53
- const errors_1 = require("../../errors");
52
+ const sensitive_decorator_1 = require("../../interceptors/decorators/sensitive.decorator");
53
+ const errors_1 = require("../../../domain/errors");
54
54
  const index_1 = require("../../../constants/index");
55
55
  /**
56
56
  * 版本相容性檢查服務
@@ -189,7 +189,9 @@ let VersionCompatibilityService = class VersionCompatibilityService {
189
189
  }
190
190
  const creds = this.acrCreds.getDefaultCredentials();
191
191
  if (!creds) {
192
- throw new Error('ACR credentials not found');
192
+ throw new errors_1.AppError('ACR credentials not found', {
193
+ exitCode: errors_1.EXIT_CODES.VALIDATION_ERROR,
194
+ });
193
195
  }
194
196
  const registry = `${creds.name}.azurecr.io`;
195
197
  const scope = `repository:${repo}:pull`;
@@ -217,7 +219,9 @@ let VersionCompatibilityService = class VersionCompatibilityService {
217
219
  digest = manifestResponse.config.digest;
218
220
  }
219
221
  if (!digest) {
220
- throw new Error('Could not find digest in manifest');
222
+ throw new errors_1.AppError('Could not find digest in manifest', {
223
+ exitCode: errors_1.EXIT_CODES.NETWORK_ERROR,
224
+ });
221
225
  }
222
226
  const blobUrl = `https://${registry}/v2/${repo}/blobs/${digest}`;
223
227
  const blob = await this.httpClient.getJson(blobUrl, {
@@ -283,7 +287,7 @@ __decorate([
283
287
  exports.VersionCompatibilityService = VersionCompatibilityService = __decorate([
284
288
  (0, tsyringe_1.injectable)(),
285
289
  __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IHttpClient)),
286
- __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IAppConfig)),
290
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.IAppConfigPort)),
287
291
  __param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IAcrCredentialManager)),
288
292
  __param(3, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
289
293
  __metadata("design:paramtypes", [Object, Object, Object, Object])
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Dotnet Publisher Adapter
4
+ *
5
+ * 實作 IDotnetPublisherPort 介面
6
+ * 透過 dotnet publish 建置 .NET 專案
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
+ var __metadata = (this && this.__metadata) || function (k, v) {
48
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
49
+ };
50
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
51
+ return function (target, key) { decorator(target, key, paramIndex); }
52
+ };
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.DotnetPublisherAdapter = void 0;
55
+ const tsyringe_1 = require("tsyringe");
56
+ const fs = __importStar(require("fs"));
57
+ const os = __importStar(require("os"));
58
+ const path = __importStar(require("path"));
59
+ const tokens_1 = require("../../di/tokens");
60
+ let DotnetPublisherAdapter = class DotnetPublisherAdapter {
61
+ constructor(logger, commandExecutor) {
62
+ this.logger = logger;
63
+ this.commandExecutor = commandExecutor;
64
+ }
65
+ /**
66
+ * 執行 dotnet publish 建置 .NET 專案
67
+ *
68
+ * 使用暫存目錄作為 dotnet publish 的輸出目標,避免 Microsoft.NET.Sdk.Web
69
+ * 將專案目錄內已存在的其他目錄當作 Content 複製進輸出。
70
+ *
71
+ * @param options - 發布選項
72
+ * @param output - 輸出端口,用於回報進度
73
+ * @returns 發布結果
74
+ */
75
+ async publish(options, output) {
76
+ const configuration = options.configuration || 'Release';
77
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'uofx-publish-'));
78
+ try {
79
+ const args = [
80
+ 'publish',
81
+ '-c', configuration,
82
+ '-o', tempDir,
83
+ ];
84
+ this.logger.debug(`Executing: dotnet ${args.join(' ')}`, {
85
+ cwd: options.projectPath,
86
+ });
87
+ output
88
+ ?.item('arrow', `Running: dotnet publish -c ${configuration}`)
89
+ .flush();
90
+ output?.divider().flush();
91
+ const result = await this.commandExecutor.spawnProcess('dotnet', args, undefined, { cwd: options.projectPath, inheritStdio: true });
92
+ output?.divider().flush();
93
+ if (result.exitCode === 0) {
94
+ // cpSync 支援跨磁碟,避免 renameSync 的 EXDEV 錯誤
95
+ fs.mkdirSync(options.outputPath, { recursive: true });
96
+ fs.cpSync(tempDir, options.outputPath, { recursive: true });
97
+ return {
98
+ success: true,
99
+ message: 'Dotnet publish completed successfully.',
100
+ outputPath: options.outputPath,
101
+ };
102
+ }
103
+ else {
104
+ const errorMsg = `dotnet publish exited with code ${result.exitCode}`;
105
+ output?.error(errorMsg).flush();
106
+ return {
107
+ success: false,
108
+ message: errorMsg,
109
+ outputPath: options.outputPath,
110
+ };
111
+ }
112
+ }
113
+ catch (err) {
114
+ const errorMsg = `Failed to execute dotnet publish: ${err instanceof Error ? err.message : String(err)}`;
115
+ this.logger.error(new Error(errorMsg));
116
+ output?.error(errorMsg).flush();
117
+ return {
118
+ success: false,
119
+ message: errorMsg,
120
+ outputPath: options.outputPath,
121
+ };
122
+ }
123
+ finally {
124
+ fs.rmSync(tempDir, { recursive: true, force: true });
125
+ }
126
+ }
127
+ /**
128
+ * 從 ClientApp 路徑推算專案根目錄
129
+ * @param clientAppPath - ClientApp 目錄路徑
130
+ * @returns 專案根目錄路徑(ClientApp 的上層目錄)
131
+ */
132
+ findProjectDir(clientAppPath) {
133
+ return path.dirname(clientAppPath);
134
+ }
135
+ };
136
+ exports.DotnetPublisherAdapter = DotnetPublisherAdapter;
137
+ exports.DotnetPublisherAdapter = DotnetPublisherAdapter = __decorate([
138
+ (0, tsyringe_1.injectable)(),
139
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
140
+ __param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.ICommandExecutor)),
141
+ __metadata("design:paramtypes", [Object, Object])
142
+ ], DotnetPublisherAdapter);
143
+ //# sourceMappingURL=dotnet-publisher.adapter.js.map
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DotnetPublisherAdapter = void 0;
4
+ /**
5
+ * Infrastructure Dotnet 模組匯出
6
+ */
7
+ var dotnet_publisher_adapter_1 = require("./dotnet-publisher.adapter");
8
+ Object.defineProperty(exports, "DotnetPublisherAdapter", { enumerable: true, get: function () { return dotnet_publisher_adapter_1.DotnetPublisherAdapter; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectPlatform = exports.WindowsEnvironmentAdapter = void 0;
4
+ /**
5
+ * Infrastructure Environment 模組匯出
6
+ */
7
+ var windows_environment_adapter_1 = require("./windows-environment.adapter");
8
+ Object.defineProperty(exports, "WindowsEnvironmentAdapter", { enumerable: true, get: function () { return windows_environment_adapter_1.WindowsEnvironmentAdapter; } });
9
+ var platform_detector_util_1 = require("./platform-detector.util");
10
+ Object.defineProperty(exports, "detectPlatform", { enumerable: true, get: function () { return platform_detector_util_1.detectPlatform; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=index.js.map
@@ -68,4 +68,4 @@ function isWindows() {
68
68
  function isLinux() {
69
69
  return detectPlatform() === 'linux';
70
70
  }
71
- //# sourceMappingURL=platform-detector.js.map
71
+ //# sourceMappingURL=platform-detector.util.js.map