@uofx/cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +444 -0
  3. package/THIRD-PARTY-NOTICES.txt +894 -0
  4. package/dist/application/dtos/index.js +24 -0
  5. package/dist/application/dtos/request/delete-instance.request.dto.js +3 -0
  6. package/dist/application/dtos/request/get-config.request.dto.js +3 -0
  7. package/dist/application/dtos/request/get-credentials.request.dto.js +3 -0
  8. package/dist/application/dtos/request/index.js +27 -0
  9. package/dist/application/dtos/request/install-instance.request.dto.js +3 -0
  10. package/dist/application/dtos/request/list-charts.request.dto.js +3 -0
  11. package/dist/application/dtos/request/set-config.request.dto.js +16 -0
  12. package/dist/application/dtos/request/setup-environment.request.dto.js +16 -0
  13. package/dist/application/dtos/request/show-logs.request.dto.js +19 -0
  14. package/dist/application/dtos/request/start-instance.request.dto.js +3 -0
  15. package/dist/application/dtos/request/stop-instance.request.dto.js +3 -0
  16. package/dist/application/dtos/response/credentials.response.dto.js +3 -0
  17. package/dist/application/dtos/response/delete-instance.response.dto.js +3 -0
  18. package/dist/application/dtos/response/index.js +26 -0
  19. package/dist/application/dtos/response/install-instance.response.dto.js +3 -0
  20. package/dist/application/dtos/response/instance-list.response.dto.js +3 -0
  21. package/dist/application/dtos/response/instance-status.response.dto.js +3 -0
  22. package/dist/application/dtos/response/setup-result.response.dto.js +3 -0
  23. package/dist/application/dtos/response/show-logs.response.dto.js +3 -0
  24. package/dist/application/dtos/response/start-instance.response.dto.js +3 -0
  25. package/dist/application/dtos/response/stop-instance.response.dto.js +3 -0
  26. package/dist/application/index.js +25 -0
  27. package/dist/application/interfaces/index.js +24 -0
  28. package/dist/application/interfaces/use-case.interface.js +3 -0
  29. package/dist/application/use-cases/config/get-config.use-case.js +66 -0
  30. package/dist/application/use-cases/config/set-config.use-case.js +49 -0
  31. package/dist/application/use-cases/credentials/get-credentials.use-case.js +57 -0
  32. package/dist/application/use-cases/index.js +28 -0
  33. package/dist/application/use-cases/instance/delete-instance.use-case.js +81 -0
  34. package/dist/application/use-cases/instance/index.js +23 -0
  35. package/dist/application/use-cases/instance/install-instance.use-case.js +424 -0
  36. package/dist/application/use-cases/instance/list-charts.use-case.js +43 -0
  37. package/dist/application/use-cases/instance/list-instances.use-case.js +62 -0
  38. package/dist/application/use-cases/instance/start-instance.use-case.js +154 -0
  39. package/dist/application/use-cases/instance/stop-instance.use-case.js +55 -0
  40. package/dist/application/use-cases/logs/show-logs.use-case.js +66 -0
  41. package/dist/application/use-cases/setup/setup-environment.use-case.js +53 -0
  42. package/dist/cli.js +286 -0
  43. package/dist/constants/config-defaults.js +23 -0
  44. package/dist/constants/defaults.js +89 -0
  45. package/dist/constants/deployment.js +39 -0
  46. package/dist/constants/environments.js +93 -0
  47. package/dist/constants/index.js +53 -0
  48. package/dist/constants/oci-artifacts.js +25 -0
  49. package/dist/constants/paths.js +92 -0
  50. package/dist/constants/timeouts.js +60 -0
  51. package/dist/di/container.js +34 -0
  52. package/dist/di/index.js +22 -0
  53. package/dist/di/modules/application.module.js +54 -0
  54. package/dist/di/modules/infrastructure.module.js +206 -0
  55. package/dist/di/modules/interceptor.module.js +68 -0
  56. package/dist/di/modules/presentation.module.js +31 -0
  57. package/dist/di/tokens.js +149 -0
  58. package/dist/domain/decorators/sensitive.decorator.js +39 -0
  59. package/dist/domain/entities/credentials-resolver.entity.js +127 -0
  60. package/dist/domain/entities/credentials.entity.js +65 -0
  61. package/dist/domain/entities/delete-instance-validation.entity.js +100 -0
  62. package/dist/domain/entities/deployment-parameters.entity.js +120 -0
  63. package/dist/domain/entities/environment-validation.entity.js +125 -0
  64. package/dist/domain/entities/index.js +29 -0
  65. package/dist/domain/entities/instance-lifecycle-state.entity.js +100 -0
  66. package/dist/domain/entities/instance-list-aggregator.entity.js +104 -0
  67. package/dist/domain/entities/instance-metadata.entity.js +86 -0
  68. package/dist/domain/entities/instance-status.entity.js +79 -0
  69. package/dist/domain/entities/instance.entity.js +128 -0
  70. package/dist/domain/entities/log-filter.entity.js +141 -0
  71. package/dist/domain/index.js +29 -0
  72. package/dist/domain/interfaces/safe-loggable.interface.js +3 -0
  73. package/dist/domain/ports/app-config.port.js +3 -0
  74. package/dist/domain/ports/base-image.port.js +3 -0
  75. package/dist/domain/ports/chart-version.port.js +3 -0
  76. package/dist/domain/ports/correlation-id.port.js +3 -0
  77. package/dist/domain/ports/credentials.port.js +3 -0
  78. package/dist/domain/ports/deployment.port.js +3 -0
  79. package/dist/domain/ports/error-handler.port.js +3 -0
  80. package/dist/domain/ports/index.js +46 -0
  81. package/dist/domain/ports/instance-manager.port.js +3 -0
  82. package/dist/domain/ports/instance-metadata.port.js +8 -0
  83. package/dist/domain/ports/instance-storage.port.js +3 -0
  84. package/dist/domain/ports/k8s-deployer.port.js +3 -0
  85. package/dist/domain/ports/logger.port.js +14 -0
  86. package/dist/domain/ports/output.port.js +3 -0
  87. package/dist/domain/ports/runtime-environment.port.js +6 -0
  88. package/dist/domain/ports/user-interaction.port.js +9 -0
  89. package/dist/domain/ports/user-settings.port.js +3 -0
  90. package/dist/domain/types/index.js +22 -0
  91. package/dist/domain/types/logger.types.js +29 -0
  92. package/dist/domain/types/validation.types.js +9 -0
  93. package/dist/domain/value-objects/acr-credentials.value-object.js +92 -0
  94. package/dist/domain/value-objects/chart-version.value-object.js +124 -0
  95. package/dist/domain/value-objects/config-log-level.value-object.js +84 -0
  96. package/dist/domain/value-objects/connection-info.value-object.js +65 -0
  97. package/dist/domain/value-objects/index.js +25 -0
  98. package/dist/domain/value-objects/instance-name.value-object.js +91 -0
  99. package/dist/domain/value-objects/jwt-key.value-object.js +97 -0
  100. package/dist/domain/value-objects/mssql-password.value-object.js +140 -0
  101. package/dist/domain/value-objects/rsa-key-pair.value-object.js +181 -0
  102. package/dist/index.js +6 -0
  103. package/dist/infrastructure/config/app-config.interface.js +3 -0
  104. package/dist/infrastructure/config/app-config.service.js +280 -0
  105. package/dist/infrastructure/config/config-validator.js +31 -0
  106. package/dist/infrastructure/config/crypto.service.js +125 -0
  107. package/dist/infrastructure/deployment/deployment.adapter.js +118 -0
  108. package/dist/infrastructure/deployment/interfaces/acr-credential-manager.interface.js +3 -0
  109. package/dist/infrastructure/deployment/interfaces/app-manager.interface.js +3 -0
  110. package/dist/infrastructure/deployment/interfaces/helm-registry.interface.js +3 -0
  111. package/dist/infrastructure/deployment/interfaces/infra-manager.interface.js +3 -0
  112. package/dist/infrastructure/deployment/interfaces/k8s-job-runner.interface.js +3 -0
  113. package/dist/infrastructure/deployment/interfaces/mssql-database-init.interface.js +3 -0
  114. package/dist/infrastructure/deployment/interfaces/mssql-helm-deployment.interface.js +3 -0
  115. package/dist/infrastructure/deployment/interfaces/mssql-storage.interface.js +3 -0
  116. package/dist/infrastructure/deployment/interfaces/mssql-user-manager.interface.js +3 -0
  117. package/dist/infrastructure/deployment/interfaces/oci-artifact.interface.js +3 -0
  118. package/dist/infrastructure/deployment/interfaces/secret-manager.interface.js +3 -0
  119. package/dist/infrastructure/deployment/interfaces/service-manager.interface.js +3 -0
  120. package/dist/infrastructure/deployment/interfaces/version-compatibility.interface.js +3 -0
  121. package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +144 -0
  122. package/dist/infrastructure/deployment/services/app-manager.service.js +193 -0
  123. package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +163 -0
  124. package/dist/infrastructure/deployment/services/helm-registry.service.js +126 -0
  125. package/dist/infrastructure/deployment/services/infra-manager.service.js +130 -0
  126. package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +194 -0
  127. package/dist/infrastructure/deployment/services/mssql-database-init.service.js +139 -0
  128. package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +100 -0
  129. package/dist/infrastructure/deployment/services/mssql-storage.service.js +54 -0
  130. package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +66 -0
  131. package/dist/infrastructure/deployment/services/oci-artifact.service.js +289 -0
  132. package/dist/infrastructure/deployment/services/secret-manager.service.js +179 -0
  133. package/dist/infrastructure/deployment/services/service-manager.service.js +82 -0
  134. package/dist/infrastructure/deployment/services/version-compatibility.service.js +291 -0
  135. package/dist/infrastructure/environment/interfaces/hardware-info.interface.js +3 -0
  136. package/dist/infrastructure/environment/interfaces/network-checker.interface.js +3 -0
  137. package/dist/infrastructure/environment/services/hardware-info.service.js +135 -0
  138. package/dist/infrastructure/environment/services/network-checker.service.js +142 -0
  139. package/dist/infrastructure/environment/windows-environment.adapter.js +162 -0
  140. package/dist/infrastructure/errors/app-error.js +73 -0
  141. package/dist/infrastructure/errors/error-handler.interface.js +3 -0
  142. package/dist/infrastructure/errors/error-handler.js +218 -0
  143. package/dist/infrastructure/errors/exit-codes.js +27 -0
  144. package/dist/infrastructure/errors/index.js +25 -0
  145. package/dist/infrastructure/execution/builders/base-command.builder.js +122 -0
  146. package/dist/infrastructure/execution/builders/host-command.builder.js +58 -0
  147. package/dist/infrastructure/execution/builders/windows-host-command.builder.js +50 -0
  148. package/dist/infrastructure/execution/builders/wsl-command.builder.js +29 -0
  149. package/dist/infrastructure/execution/command-builder.js +252 -0
  150. package/dist/infrastructure/execution/command-executor.service.js +230 -0
  151. package/dist/infrastructure/execution/environments/wsl-execution.environment.js +70 -0
  152. package/dist/infrastructure/execution/execution-environment.factory.js +53 -0
  153. package/dist/infrastructure/execution/index.js +25 -0
  154. package/dist/infrastructure/execution/interfaces/command-builder.interface.js +3 -0
  155. package/dist/infrastructure/execution/interfaces/command-executor.interface.js +3 -0
  156. package/dist/infrastructure/execution/interfaces/execution-environment-factory.interface.js +3 -0
  157. package/dist/infrastructure/execution/interfaces/execution-environment.interface.js +7 -0
  158. package/dist/infrastructure/execution/interfaces/host-command-builder.interface.js +3 -0
  159. package/dist/infrastructure/execution/interfaces/index.js +23 -0
  160. package/dist/infrastructure/execution/interfaces/script-executor.interface.js +3 -0
  161. package/dist/infrastructure/execution/script-executor.service.js +171 -0
  162. package/dist/infrastructure/http/http-client.service.js +176 -0
  163. package/dist/infrastructure/http/index.js +18 -0
  164. package/dist/infrastructure/http/interfaces/http-client.interface.js +3 -0
  165. package/dist/infrastructure/interceptors/index.js +8 -0
  166. package/dist/infrastructure/interceptors/interceptor.factory.js +44 -0
  167. package/dist/infrastructure/interceptors/interceptor.interface.js +3 -0
  168. package/dist/infrastructure/interceptors/logging.interceptor.js +171 -0
  169. package/dist/infrastructure/logger/correlation-id.adapter.js +68 -0
  170. package/dist/infrastructure/logger/index.js +23 -0
  171. package/dist/infrastructure/logger/interfaces/index.js +22 -0
  172. package/dist/infrastructure/logger/interfaces/log-reader.repository.interface.js +7 -0
  173. package/dist/infrastructure/logger/interfaces/log-writer.repository.interface.js +7 -0
  174. package/dist/infrastructure/logger/logger.adapter.js +274 -0
  175. package/dist/infrastructure/logger/services/file-log-reader.repository.js +148 -0
  176. package/dist/infrastructure/logger/services/file-log-writer.repository.js +307 -0
  177. package/dist/infrastructure/logger/services/index.js +22 -0
  178. package/dist/infrastructure/persistence/index.js +25 -0
  179. package/dist/infrastructure/persistence/instance-metadata.adapter.js +100 -0
  180. package/dist/infrastructure/persistence/instance-storage.adapter.js +64 -0
  181. package/dist/infrastructure/persistence/interfaces/config.repository.interface.js +3 -0
  182. package/dist/infrastructure/persistence/interfaces/index.js +22 -0
  183. package/dist/infrastructure/persistence/interfaces/instance.repository.interface.js +3 -0
  184. package/dist/infrastructure/persistence/services/file-system-config.repository.js +168 -0
  185. package/dist/infrastructure/persistence/services/file-system-instance.repository.js +170 -0
  186. package/dist/infrastructure/persistence/services/index.js +22 -0
  187. package/dist/infrastructure/persistence/user-settings.adapter.js +55 -0
  188. package/dist/infrastructure/platform-detector.js +71 -0
  189. package/dist/infrastructure/platforms/windows/interfaces/microk8s.interface.js +3 -0
  190. package/dist/infrastructure/platforms/windows/interfaces/rootfs-manager.interface.js +3 -0
  191. package/dist/infrastructure/platforms/windows/interfaces/windows-features.interface.js +3 -0
  192. package/dist/infrastructure/platforms/windows/interfaces/windows-info.interface.js +3 -0
  193. package/dist/infrastructure/platforms/windows/interfaces/wsl-config.interface.js +3 -0
  194. package/dist/infrastructure/platforms/windows/interfaces/wsl-info.interface.js +3 -0
  195. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-inspection.interface.js +3 -0
  196. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-lifecycle.interface.js +3 -0
  197. package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-naming.interface.js +3 -0
  198. package/dist/infrastructure/platforms/windows/interfaces/wsl-manager.interface.js +3 -0
  199. package/dist/infrastructure/platforms/windows/interfaces/wsl-resources.interface.js +8 -0
  200. package/dist/infrastructure/platforms/windows/interfaces/wsl-updater.interface.js +3 -0
  201. package/dist/infrastructure/platforms/windows/interfaces/wslconfig-parser.interface.js +3 -0
  202. package/dist/infrastructure/platforms/windows/parsers/wsl-version.parser.js +133 -0
  203. package/dist/infrastructure/platforms/windows/services/microk8s.service.js +168 -0
  204. package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +336 -0
  205. package/dist/infrastructure/platforms/windows/services/windows-features.service.js +191 -0
  206. package/dist/infrastructure/platforms/windows/services/windows-info.service.js +138 -0
  207. package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +171 -0
  208. package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +226 -0
  209. package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +325 -0
  210. package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +442 -0
  211. package/dist/infrastructure/platforms/windows/services/wsl-instance-naming.service.js +93 -0
  212. package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +273 -0
  213. package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +222 -0
  214. package/dist/infrastructure/platforms/windows/wsl-base-image.adapter.js +41 -0
  215. package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +150 -0
  216. package/dist/infrastructure/utils/error-formatter.util.js +29 -0
  217. package/dist/infrastructure/utils/file-operations.util.js +201 -0
  218. package/dist/infrastructure/utils/input-validator.util.js +152 -0
  219. package/dist/infrastructure/utils/retry.util.js +98 -0
  220. package/dist/presentation/controllers/config.controller.js +146 -0
  221. package/dist/presentation/controllers/credentials.controller.js +105 -0
  222. package/dist/presentation/controllers/index.js +25 -0
  223. package/dist/presentation/controllers/instance.controller.js +363 -0
  224. package/dist/presentation/controllers/logs.controller.js +103 -0
  225. package/dist/presentation/controllers/setup.controller.js +175 -0
  226. package/dist/presentation/interfaces/cli-options.interface.js +8 -0
  227. package/dist/presentation/prompts/acr-credentials.prompt.js +76 -0
  228. package/dist/presentation/prompts/index.js +21 -0
  229. package/dist/presentation/ui/cli-progress.service.js +193 -0
  230. package/dist/presentation/ui/constants/output-symbols.js +42 -0
  231. package/dist/presentation/ui/index.js +27 -0
  232. package/dist/presentation/ui/interaction.service.js +276 -0
  233. package/dist/presentation/ui/interfaces/cli-progress.interface.js +9 -0
  234. package/dist/presentation/ui/interfaces/output-formatter.interface.js +23 -0
  235. package/dist/presentation/ui/log-level.enum.js +66 -0
  236. package/dist/presentation/ui/output-builder.service.js +378 -0
  237. package/dist/presentation/ui/output-formatter.service.js +393 -0
  238. package/package.json +65 -0
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MssqlPassword = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ /**
9
+ * MSSQL 密碼 Value Object
10
+ *
11
+ * 封裝 MSSQL 密碼的生成與驗證邏輯
12
+ * 規則:
13
+ * - 長度至少 16 字元
14
+ * - 必須包含大寫、小寫、數字、特殊符號中的至少三種
15
+ * - 特殊符號限定於:!@#%_+-=
16
+ */
17
+ class MssqlPassword {
18
+ constructor(value) {
19
+ this.value = value;
20
+ }
21
+ /**
22
+ * 生成符合 MSSQL 複雜度要求的隨機密碼
23
+ * @param length - 密碼長度(預設:16)
24
+ * @returns 密碼物件
25
+ * @throws 若長度小於 16 或生成失敗
26
+ */
27
+ static generate(length = 16) {
28
+ if (length < this.MIN_LENGTH) {
29
+ throw new Error(`Password length must be at least ${this.MIN_LENGTH} characters`);
30
+ }
31
+ try {
32
+ // 確保包含所有必要字元類型
33
+ let password = '';
34
+ password += this.getRandomChar(this.UPPERCASE);
35
+ password += this.getRandomChar(this.LOWERCASE);
36
+ password += this.getRandomChar(this.NUMBERS);
37
+ password += this.getRandomChar(this.SPECIALS);
38
+ // 填充剩餘長度
39
+ const allChars = this.UPPERCASE + this.LOWERCASE + this.NUMBERS + this.SPECIALS;
40
+ for (let i = password.length; i < length; i++) {
41
+ password += this.getRandomChar(allChars);
42
+ }
43
+ // 打亂順序 (Fisher-Yates Shuffle)
44
+ const shuffled = this.shuffleString(password);
45
+ // 驗證生成的密碼
46
+ if (!this.isValid(shuffled)) {
47
+ throw new Error('Generated password does not meet MSSQL complexity requirements');
48
+ }
49
+ return new MssqlPassword(shuffled);
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Failed to generate MSSQL password: ${error instanceof Error ? error.message : String(error)}`);
53
+ }
54
+ }
55
+ /**
56
+ * 從現有密碼建立密碼物件
57
+ * @param password - 密碼字串
58
+ * @returns 密碼物件
59
+ * @throws 若密碼不符合規則
60
+ */
61
+ static create(password) {
62
+ if (!this.isValid(password)) {
63
+ throw new Error(`Invalid MSSQL password. Must be at least ${this.MIN_LENGTH} characters ` +
64
+ `and contain at least 3 of: uppercase, lowercase, numbers, special characters.`);
65
+ }
66
+ return new MssqlPassword(password);
67
+ }
68
+ /**
69
+ * 驗證密碼是否符合 MSSQL 複雜度要求
70
+ * @param password - 要驗證的密碼
71
+ * @returns 若符合要求則回傳 true
72
+ */
73
+ static isValid(password) {
74
+ if (!password || password.length < this.MIN_LENGTH) {
75
+ return false;
76
+ }
77
+ const hasUppercase = /[A-Z]/.test(password);
78
+ const hasLowercase = /[a-z]/.test(password);
79
+ const hasNumber = /[0-9]/.test(password);
80
+ const hasSpecial = /[!@#$%^&*()_+\-=[\]{}|;:,.<>?]/.test(password);
81
+ const categoryCount = [hasUppercase, hasLowercase, hasNumber, hasSpecial].filter(Boolean).length;
82
+ return categoryCount >= 3;
83
+ }
84
+ /**
85
+ * 從字元集中隨機取得一個字元
86
+ * @param charset - 字元集字串
87
+ * @returns 隨機字元
88
+ */
89
+ static getRandomChar(charset) {
90
+ const randomIndex = crypto_1.default.randomInt(0, charset.length);
91
+ return charset[randomIndex];
92
+ }
93
+ /**
94
+ * 將字串隨機打亂(Fisher-Yates Shuffle)
95
+ * @param str - 原始字串
96
+ * @returns 打亂後的字串
97
+ */
98
+ static shuffleString(str) {
99
+ const arr = str.split('');
100
+ for (let i = arr.length - 1; i > 0; i--) {
101
+ const j = crypto_1.default.randomInt(0, i + 1);
102
+ [arr[i], arr[j]] = [arr[j], arr[i]];
103
+ }
104
+ return arr.join('');
105
+ }
106
+ /**
107
+ * 取得密碼明文
108
+ * @returns 密碼字串
109
+ */
110
+ toString() {
111
+ return this.value;
112
+ }
113
+ /**
114
+ * 取得遮罩後的密碼(僅顯示前後 4 碼)
115
+ * @returns 遮罩後的密碼
116
+ */
117
+ toMasked() {
118
+ if (this.value.length <= 8) {
119
+ return '******';
120
+ }
121
+ const prefix = this.value.substring(0, 4);
122
+ const suffix = this.value.substring(this.value.length - 4);
123
+ return `${prefix}******${suffix}`;
124
+ }
125
+ /**
126
+ * 比較兩個密碼是否相等
127
+ * @param other - 另一個密碼
128
+ * @returns 若相等則回傳 true
129
+ */
130
+ equals(other) {
131
+ return this.value === other.value;
132
+ }
133
+ }
134
+ exports.MssqlPassword = MssqlPassword;
135
+ MssqlPassword.MIN_LENGTH = 16;
136
+ MssqlPassword.UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
137
+ MssqlPassword.LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
138
+ MssqlPassword.NUMBERS = '0123456789';
139
+ MssqlPassword.SPECIALS = '!@#%_+-=';
140
+ //# sourceMappingURL=mssql-password.value-object.js.map
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RsaKeyPair = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ /**
9
+ * RSA 金鑰對 Value Object
10
+ *
11
+ * 封裝 RSA 金鑰對的生成、驗證與格式化邏輯
12
+ * 格式:PKCS#1 PEM
13
+ */
14
+ class RsaKeyPair {
15
+ constructor(privateKey, publicKey) {
16
+ this.privateKey = privateKey;
17
+ this.publicKey = publicKey;
18
+ }
19
+ /**
20
+ * 生成 RSA 金鑰對
21
+ * @param modulusLength - 金鑰長度(預設:2048)
22
+ * @returns RSA 金鑰對物件
23
+ * @throws 若生成失敗
24
+ */
25
+ static generate(modulusLength = 2048) {
26
+ try {
27
+ const { privateKey, publicKey } = crypto_1.default.generateKeyPairSync('rsa', {
28
+ modulusLength,
29
+ publicKeyEncoding: { type: 'pkcs1', format: 'pem' },
30
+ privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
31
+ });
32
+ const keyPair = new RsaKeyPair(privateKey, publicKey);
33
+ // 驗證生成的金鑰對
34
+ if (!keyPair.validate()) {
35
+ throw new Error('Generated RSA key pair validation failed');
36
+ }
37
+ return keyPair;
38
+ }
39
+ catch (error) {
40
+ throw new Error(`Failed to generate RSA key pair: ${error instanceof Error ? error.message : String(error)}`);
41
+ }
42
+ }
43
+ /**
44
+ * 從現有金鑰建立金鑰對物件
45
+ * @param privateKey - 私鑰 (PEM)
46
+ * @param publicKey - 公鑰 (PEM)
47
+ * @returns RSA 金鑰對物件
48
+ * @throws 若金鑰對無效
49
+ */
50
+ static create(privateKey, publicKey) {
51
+ const keyPair = new RsaKeyPair(privateKey, publicKey);
52
+ if (!keyPair.validate()) {
53
+ throw new Error('Invalid RSA key pair: keys do not match');
54
+ }
55
+ return keyPair;
56
+ }
57
+ /**
58
+ * 驗證 RSA 金鑰對是否匹配
59
+ * 透過簽章與驗證流程測試
60
+ * @returns 若金鑰對有效且匹配則回傳 true
61
+ */
62
+ validate() {
63
+ try {
64
+ const testData = 'test data for RSA key pair validation';
65
+ // 使用私鑰簽章
66
+ const sign = crypto_1.default.createSign('SHA256');
67
+ sign.update(testData);
68
+ sign.end();
69
+ const signature = sign.sign(this.privateKey);
70
+ // 使用公鑰驗證
71
+ const verify = crypto_1.default.createVerify('SHA256');
72
+ verify.update(testData);
73
+ verify.end();
74
+ return verify.verify(this.publicKey, signature);
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }
80
+ /**
81
+ * 格式化 SignInRSASettings 金鑰 (PKCS#1 PEM -> Base64)
82
+ * @param type - 金鑰類型 ('private' 或 'public')
83
+ * @returns 格式化並編碼後的金鑰字串
84
+ */
85
+ toBase64ForSignIn(type) {
86
+ const pem = type === 'private' ? this.privateKey : this.publicKey;
87
+ // 將換行符號正規化為 \n
88
+ const normalized = pem.replace(/\r\n/g, '\n');
89
+ const headerMatch = normalized.match(/-----BEGIN [^-]+-----/);
90
+ const footerMatch = normalized.match(/-----END [^-]+-----/);
91
+ if (!headerMatch || !footerMatch) {
92
+ // 若非標準 PEM 則直接編碼
93
+ return this.encodeToBase64(normalized);
94
+ }
95
+ const header = headerMatch[0];
96
+ const footer = footerMatch[0];
97
+ // 提取主體:移除標頭、結尾和所有空白(包含換行)
98
+ const body = normalized
99
+ .replace(header, '')
100
+ .replace(footer, '')
101
+ .replace(/\s/g, '');
102
+ // 將主體分割為 64 字元的區塊以符合嚴格的 PEM 格式
103
+ const chunks = body.match(/.{1,64}/g);
104
+ const formattedBody = chunks ? chunks.join('\\n') : body;
105
+ // 重新組合並使用跳脫的換行符 (\\n)
106
+ // Helm 解碼 Base64 -> 得到包含 "\n" 字串的字串
107
+ // Helm 寫入 JSON -> "Key": "...\\n...\\n..."
108
+ // App 解析 JSON -> 得到包含真實換行符 (0x0A) 的字串
109
+ const safePem = `${header}\\n${formattedBody}\\n${footer}`;
110
+ return this.encodeToBase64(safePem);
111
+ }
112
+ /**
113
+ * 格式化 UofxRsaKey 金鑰 (PKCS#1 PEM -> XML -> Base64 -> Base64)
114
+ * @param type - 金鑰類型 ('private' 或 'public')
115
+ * @returns 格式化並雙重編碼後的金鑰字串
116
+ */
117
+ toBase64ForUofx(type) {
118
+ const xml = this.toXml(type);
119
+ return this.doubleEncodeToBase64(xml);
120
+ }
121
+ /**
122
+ * 將 PEM 格式金鑰轉換為 XML 格式 (相容 .NET RSA)
123
+ * @param type - 金鑰類型 ('private' 或 'public')
124
+ * @returns XML 格式字串
125
+ */
126
+ toXml(type) {
127
+ const pem = type === 'private' ? this.privateKey : this.publicKey;
128
+ const keyObject = type === 'private'
129
+ ? crypto_1.default.createPrivateKey(pem)
130
+ : crypto_1.default.createPublicKey(pem);
131
+ const jwk = keyObject.export({ format: 'jwk' });
132
+ const toBase64 = (base64Url) => {
133
+ if (!base64Url)
134
+ return '';
135
+ return Buffer.from(base64Url, 'base64url').toString('base64');
136
+ };
137
+ let xml = '<RSAKeyValue>';
138
+ xml += `<Modulus>${toBase64(jwk.n)}</Modulus>`;
139
+ xml += `<Exponent>${toBase64(jwk.e)}</Exponent>`;
140
+ if (type === 'private') {
141
+ xml += `<P>${toBase64(jwk.p)}</P>`;
142
+ xml += `<Q>${toBase64(jwk.q)}</Q>`;
143
+ xml += `<DP>${toBase64(jwk.dp)}</DP>`;
144
+ xml += `<DQ>${toBase64(jwk.dq)}</DQ>`;
145
+ xml += `<InverseQ>${toBase64(jwk.qi)}</InverseQ>`;
146
+ xml += `<D>${toBase64(jwk.d)}</D>`;
147
+ }
148
+ xml += '</RSAKeyValue>';
149
+ return xml;
150
+ }
151
+ /**
152
+ * 將字串編碼為 Base64
153
+ * @param data - 原始字串
154
+ * @returns Base64 編碼字串
155
+ */
156
+ encodeToBase64(data) {
157
+ return Buffer.from(data).toString('base64');
158
+ }
159
+ /**
160
+ * 雙重 Base64 編碼 (ASCII -> Base64 -> UTF8 -> Base64)
161
+ * 用於處理 Helm 參數傳遞的特殊需求
162
+ * @param data - 原始字串
163
+ * @returns 雙重編碼後的字串
164
+ */
165
+ doubleEncodeToBase64(data) {
166
+ // 第一次 Base64 編碼使用 ASCII (模擬 C# RsaHelper 行為)
167
+ const first = Buffer.from(data, 'ascii').toString('base64');
168
+ // 第二次 Base64 編碼使用 UTF-8 (Helm 參數需求)
169
+ return Buffer.from(first, 'utf8').toString('base64');
170
+ }
171
+ /**
172
+ * 比較兩個金鑰對是否相等
173
+ * @param other - 另一個金鑰對
174
+ * @returns 若相等則回傳 true
175
+ */
176
+ equals(other) {
177
+ return this.privateKey === other.privateKey && this.publicKey === other.publicKey;
178
+ }
179
+ }
180
+ exports.RsaKeyPair = RsaKeyPair;
181
+ //# sourceMappingURL=rsa-key-pair.value-object.js.map
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const cli_1 = require("./cli");
5
+ (0, cli_1.run)(process.argv);
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=app-config.interface.js.map
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.AppConfig = void 0;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const os = __importStar(require("os"));
52
+ const tsyringe_1 = require("tsyringe");
53
+ const config_validator_1 = require("./config-validator");
54
+ const acr_credentials_value_object_1 = require("../../domain/value-objects/acr-credentials.value-object");
55
+ const tokens_1 = require("../../di/tokens");
56
+ const crypto_service_1 = require("./crypto.service");
57
+ /**
58
+ * 混淆用字典字串
59
+ */
60
+ const DICT = 'Kp9Xw4LmNrT2vQcYbU7sE0oA5gHdI3lJfZ6WxjMyS8nVuROe1tCaBkFhGqDiP=/+z';
61
+ const OFFSET = 23;
62
+ /**
63
+ * 編碼後的 ACR 預設值
64
+ */
65
+ // ACR 名稱: "uofxcontainer"
66
+ const ENCODED_ACR_NAME = [67, 45, 55, 59, 37, 45, 65, 72, 74, 82, 65, 70, 32];
67
+ // ACR 帳戶: "clipull"
68
+ const ENCODED_ACR_ACCOUNT = [37, 53, 82, 24, 67, 53, 53];
69
+ // ACR 密碼
70
+ const ENCODED_ACR_PASSWORD = [28, 44, 67, 42, 54, 42, 59, 65, 44, 37, 68, 75, 68, 77, 54, 57, 33, 37, 73, 76, 42, 26, 39, 40, 71, 75, 81, 82, 80, 56, 67, 87, 62, 70, 52, 26, 45, 25, 73, 35, 71, 82, 86, 46, 73, 68, 75, 56, 69, 60, 78, 68];
71
+ /**
72
+ * 解碼混淆字串
73
+ */
74
+ function decodeObfuscated(indices) {
75
+ return indices.map(i => DICT[i - OFFSET]).join('');
76
+ }
77
+ /**
78
+ * 設定服務實作
79
+ * 負責載入、合併與驗證配置
80
+ *
81
+ * 實作:
82
+ * - IAppConfig: Infrastructure 層內部介面(完整配置功能)
83
+ * - IAppConfigPort: Domain Port(供 Application 層使用)
84
+ */
85
+ let AppConfig = class AppConfig {
86
+ constructor(logger) {
87
+ this.logger = logger;
88
+ }
89
+ /**
90
+ * 載入預設設定
91
+ * @returns 預設設定物件
92
+ *
93
+ * 回傳內嵌於程式碼中的預設設定(使用混淆方式儲存敏感值)
94
+ */
95
+ loadDefaultConfig() {
96
+ return {
97
+ logLevel: 'error',
98
+ acr: {
99
+ name: decodeObfuscated(ENCODED_ACR_NAME),
100
+ account: decodeObfuscated(ENCODED_ACR_ACCOUNT),
101
+ password: decodeObfuscated(ENCODED_ACR_PASSWORD),
102
+ },
103
+ };
104
+ }
105
+ /**
106
+ * 載入使用者設定
107
+ * @returns 使用者設定物件(部分),若檔案不存在返回空物件
108
+ *
109
+ * 如果密碼欄位已加密,會自動解密
110
+ */
111
+ loadUserConfig() {
112
+ const configPath = path.join(os.homedir(), '.uofx', 'config.json');
113
+ if (!fs.existsSync(configPath)) {
114
+ return {}; // 使用者設定可選
115
+ }
116
+ try {
117
+ const content = fs.readFileSync(configPath, 'utf-8');
118
+ const config = JSON.parse(content);
119
+ // 解密密碼欄位(如果已加密)
120
+ if (config.acr?.password && crypto_service_1.CryptoService.isEncrypted(config.acr.password)) {
121
+ config.acr = {
122
+ ...config.acr,
123
+ password: crypto_service_1.CryptoService.decrypt(config.acr.password),
124
+ };
125
+ }
126
+ return config;
127
+ }
128
+ catch (error) {
129
+ this.logger.warn('Failed to load user config, using defaults', { configPath, error });
130
+ return {};
131
+ }
132
+ }
133
+ /**
134
+ * 獲取最終設定(合併預設、使用者和命令列選項)
135
+ * @param cmdOptions 命令列選項
136
+ * @returns 最終設定物件
137
+ */
138
+ getConfig(_cmdOptions = {}) {
139
+ const defaultConfig = this.loadDefaultConfig();
140
+ const userConfig = this.loadUserConfig();
141
+ const merged = this.mergeConfigs(defaultConfig, userConfig);
142
+ // 驗證設定
143
+ (0, config_validator_1.validateConfig)(merged, this.logger);
144
+ return merged;
145
+ }
146
+ /**
147
+ * 確保使用者設定目錄存在
148
+ */
149
+ ensureUserConfigDir() {
150
+ const configDir = path.join(os.homedir(), '.uofx');
151
+ if (!fs.existsSync(configDir)) {
152
+ fs.mkdirSync(configDir, { recursive: true });
153
+ }
154
+ }
155
+ /**
156
+ * 儲存使用者設定
157
+ * @param config 要儲存的設定物件
158
+ *
159
+ * 密碼欄位會自動加密後儲存
160
+ */
161
+ saveConfig(config) {
162
+ const configPath = path.join(os.homedir(), '.uofx', 'config.json');
163
+ const configDir = path.dirname(configPath);
164
+ // 確保目錄存在
165
+ if (!fs.existsSync(configDir)) {
166
+ fs.mkdirSync(configDir, { recursive: true });
167
+ }
168
+ // 複製設定物件,加密密碼欄位
169
+ const configToSave = { ...config };
170
+ if (configToSave.acr?.password && !crypto_service_1.CryptoService.isEncrypted(configToSave.acr.password)) {
171
+ configToSave.acr = {
172
+ ...configToSave.acr,
173
+ password: crypto_service_1.CryptoService.encrypt(configToSave.acr.password),
174
+ };
175
+ }
176
+ fs.writeFileSync(configPath, JSON.stringify(configToSave, null, 2), 'utf-8');
177
+ this.logger.debug('Configuration saved', { configPath });
178
+ }
179
+ /**
180
+ * 取得 CLI 版本
181
+ * @returns CLI 版本號(如 1.0.0)
182
+ */
183
+ getCliVersion() {
184
+ try {
185
+ // 目錄結構:dist/infrastructure/config/app-config.service.js
186
+ // 需要往上 3 層才能到達專案根目錄
187
+ const possiblePaths = [
188
+ path.resolve(__dirname, '../../../package.json'), // dist 環境
189
+ path.resolve(__dirname, '../../../../package.json'), // 備用路徑
190
+ ];
191
+ for (const pkgPath of possiblePaths) {
192
+ if (fs.existsSync(pkgPath)) {
193
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
194
+ return pkg.version || '1.0.0';
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // 忽略錯誤,返回預設值
200
+ }
201
+ return '1.0.0';
202
+ }
203
+ /**
204
+ * 取得實例安裝路徑
205
+ * @param instanceName - 實例名稱
206
+ * @returns 完整安裝路徑
207
+ */
208
+ getInstallPath(instanceName) {
209
+ return path.join(os.homedir(), '.uofx', 'instances', instanceName);
210
+ }
211
+ /**
212
+ * 取得日誌級別
213
+ * @returns 日誌級別
214
+ */
215
+ getLogLevel() {
216
+ const config = this.getConfig();
217
+ return config.logLevel;
218
+ }
219
+ /**
220
+ * 取得 ACR 憑證(從合併後的配置)
221
+ * @returns ACR 憑證物件,若未配置則返回 null
222
+ */
223
+ getAcrCredentials() {
224
+ const config = this.getConfig();
225
+ const acr = config.acr;
226
+ if (acr_credentials_value_object_1.AcrCredentials.isValid(acr.name, acr.account, acr.password)) {
227
+ return acr_credentials_value_object_1.AcrCredentials.create(acr.name, acr.account, acr.password);
228
+ }
229
+ return null;
230
+ }
231
+ /**
232
+ * 從指定檔案載入 ACR 憑證
233
+ * @param filePath - 憑證檔案路徑
234
+ * @returns ACR 憑證物件
235
+ * @throws 若檔案不存在或格式不正確
236
+ */
237
+ loadAcrCredentialsFromFile(filePath) {
238
+ if (!fs.existsSync(filePath)) {
239
+ throw new Error(`ACR credentials file not found: ${filePath}`);
240
+ }
241
+ try {
242
+ const content = fs.readFileSync(filePath, 'utf-8');
243
+ const data = JSON.parse(content);
244
+ if (!acr_credentials_value_object_1.AcrCredentials.isValid(data.name, data.account, data.password)) {
245
+ throw new Error('Invalid ACR credentials format: name, account, and password are required');
246
+ }
247
+ return acr_credentials_value_object_1.AcrCredentials.create(data.name, data.account, data.password);
248
+ }
249
+ catch (error) {
250
+ if (error instanceof SyntaxError) {
251
+ throw new Error(`Invalid JSON in ACR credentials file: ${filePath}`);
252
+ }
253
+ throw error;
254
+ }
255
+ }
256
+ /**
257
+ * 深度合併設定物件
258
+ * @param defaultConfig 預設設定
259
+ * @param userConfig 使用者設定(部分)
260
+ * @returns 合併後的設定
261
+ */
262
+ mergeConfigs(defaultConfig, userConfig) {
263
+ // 使用簡單的深度合併
264
+ return {
265
+ ...defaultConfig,
266
+ ...userConfig,
267
+ acr: {
268
+ ...defaultConfig.acr,
269
+ ...(userConfig.acr || {}),
270
+ }
271
+ };
272
+ }
273
+ };
274
+ exports.AppConfig = AppConfig;
275
+ exports.AppConfig = AppConfig = __decorate([
276
+ (0, tsyringe_1.injectable)(),
277
+ __param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
278
+ __metadata("design:paramtypes", [Object])
279
+ ], AppConfig);
280
+ //# sourceMappingURL=app-config.service.js.map
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateLogLevel = validateLogLevel;
4
+ exports.validateConfig = validateConfig;
5
+ /**
6
+ * 驗證 logLevel 值
7
+ * 接受 'debug', 'info', 'error' 三個等級
8
+ * @param level 日誌等級
9
+ * @returns 是否有效
10
+ */
11
+ function validateLogLevel(level) {
12
+ const validLevels = ['debug', 'info', 'error'];
13
+ return validLevels.includes(level);
14
+ }
15
+ /**
16
+ * 驗證完整設定物件
17
+ * 無效的值會被警告並替換為預設值
18
+ * @param config 設定物件
19
+ * @param logger 可選的日誌記錄器,用於記錄警告
20
+ */
21
+ function validateConfig(config, logger) {
22
+ // 驗證 logLevel
23
+ if (!validateLogLevel(config.logLevel)) {
24
+ const message = `Invalid logLevel '${config.logLevel}', using 'error'`;
25
+ if (logger) {
26
+ logger.warn(message, { providedValue: config.logLevel, fallback: 'error' });
27
+ }
28
+ config.logLevel = 'error';
29
+ }
30
+ }
31
+ //# sourceMappingURL=config-validator.js.map