homebridge-config-ui-x 5.9.0 → 5.9.1-beta.1

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 (138) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/core/auth/auth.controller.d.ts +5 -0
  3. package/dist/core/config/config.interfaces.d.ts +13 -0
  4. package/dist/core/config/config.service.d.ts +9 -0
  5. package/dist/core/config/config.service.js +18 -0
  6. package/dist/core/config/config.service.js.map +1 -1
  7. package/dist/core/config/config.startup.js +48 -17
  8. package/dist/core/config/config.startup.js.map +1 -1
  9. package/dist/core/feature-flags/feature-flags.registry.js +5 -0
  10. package/dist/core/feature-flags/feature-flags.registry.js.map +1 -1
  11. package/dist/core/spa/spa-html.service.d.ts +5 -0
  12. package/dist/core/spa/spa-html.service.js +38 -0
  13. package/dist/core/spa/spa-html.service.js.map +1 -0
  14. package/dist/core/spa/spa.filter.d.ts +3 -0
  15. package/dist/core/spa/spa.filter.js +21 -2
  16. package/dist/core/spa/spa.filter.js.map +1 -1
  17. package/dist/core/ssl/ssl-cert-generator.service.d.ts +15 -0
  18. package/dist/core/ssl/ssl-cert-generator.service.js +127 -0
  19. package/dist/core/ssl/ssl-cert-generator.service.js.map +1 -0
  20. package/dist/globalDefaults.js +3 -0
  21. package/dist/globalDefaults.js.map +1 -1
  22. package/dist/main.js +18 -4
  23. package/dist/main.js.map +1 -1
  24. package/dist/modules/child-bridges/child-bridges.interfaces.d.ts +9 -0
  25. package/dist/modules/config-editor/config-editor.controller.d.ts +3 -0
  26. package/dist/modules/config-editor/config-editor.controller.js +45 -1
  27. package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
  28. package/dist/modules/config-editor/config-editor.module.js +2 -0
  29. package/dist/modules/config-editor/config-editor.module.js.map +1 -1
  30. package/dist/modules/config-editor/config-editor.service.d.ts +8 -2
  31. package/dist/modules/config-editor/config-editor.service.js +46 -3
  32. package/dist/modules/config-editor/config-editor.service.js.map +1 -1
  33. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +1 -1
  34. package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
  35. package/dist/modules/plugins/plugins.service.js +17 -1
  36. package/dist/modules/plugins/plugins.service.js.map +1 -1
  37. package/dist/modules/server/server.controller.d.ts +50 -0
  38. package/dist/modules/server/server.controller.js +200 -1
  39. package/dist/modules/server/server.controller.js.map +1 -1
  40. package/dist/modules/server/server.service.d.ts +48 -0
  41. package/dist/modules/server/server.service.js +536 -15
  42. package/dist/modules/server/server.service.js.map +1 -1
  43. package/dist/modules/status/status.gateway.d.ts +2 -0
  44. package/dist/modules/status/status.interfaces.d.ts +11 -0
  45. package/dist/modules/status/status.service.d.ts +4 -1
  46. package/dist/modules/status/status.service.js +21 -2
  47. package/dist/modules/status/status.service.js.map +1 -1
  48. package/docs/ssl-upload-pr.md +103 -0
  49. package/package.json +3 -1
  50. package/public/assets/matter.svg +8 -0
  51. package/public/assets/plugin-ui-utils/ui.js +3 -0
  52. package/public/assets/plugin-ui-utils/ui.js.map +1 -1
  53. package/public/{chunk-PK7EWRSF.js → chunk-2CNQY2E7.js} +1 -1
  54. package/public/{chunk-FJCVFWLW.js → chunk-2FNQEOXF.js} +1 -1
  55. package/public/{chunk-7F2HVPC5.js → chunk-2KEG7SYT.js} +1 -1
  56. package/public/{chunk-LRD25TWX.js → chunk-2LXNPXT5.js} +1 -1
  57. package/public/{chunk-U3JDCGCU.js → chunk-2UOYBCTN.js} +1 -1
  58. package/public/{chunk-5PCQVX7H.js → chunk-325LPX6W.js} +1 -1
  59. package/public/{chunk-EUGVBS4N.js → chunk-3E2NZ5JW.js} +1 -1
  60. package/public/{chunk-X3DENUZZ.js → chunk-3IJXQMYZ.js} +1 -1
  61. package/public/{chunk-UHOVCHON.js → chunk-3IZSIVEA.js} +1 -1
  62. package/public/{chunk-WHIRPY75.js → chunk-3XDYZNNE.js} +1 -1
  63. package/public/{chunk-75E7S6CO.js → chunk-4QXLJX7D.js} +1 -1
  64. package/public/{chunk-KW266TAV.js → chunk-57DUDNZJ.js} +1 -1
  65. package/public/{chunk-LWOAR6BY.js → chunk-5ETJWAIW.js} +1 -1
  66. package/public/{chunk-OQ5J7Z5A.js → chunk-5T4JHJYL.js} +1 -1
  67. package/public/{chunk-KX4SDDFZ.js → chunk-6KT3BYU6.js} +1 -1
  68. package/public/chunk-732XLY23.js +16 -0
  69. package/public/{chunk-3HKQMTY5.js → chunk-73TX7DHV.js} +1 -1
  70. package/public/{chunk-2JSU6A57.js → chunk-7IYTG6GP.js} +1 -1
  71. package/public/{chunk-IESBL4DA.js → chunk-7UDDRMZZ.js} +1 -1
  72. package/public/{chunk-YZT252N4.js → chunk-ADW6BF5G.js} +1 -1
  73. package/public/{chunk-MFEME63Q.js → chunk-AI6E5JVH.js} +1 -1
  74. package/public/{chunk-M4T2RO3I.js → chunk-ASKB5DLO.js} +1 -1
  75. package/public/{chunk-VB3KKHCI.js → chunk-BLT2YCDN.js} +1 -1
  76. package/public/chunk-BTBGWLKK.js +1 -0
  77. package/public/{chunk-N5GOQDTB.js → chunk-CYBDQV2B.js} +1 -1
  78. package/public/{chunk-DT6ICOAZ.js → chunk-D5RKKI2A.js} +1 -1
  79. package/public/{chunk-57P3RXT7.js → chunk-EAGKQ5OJ.js} +1 -1
  80. package/public/{chunk-CDA3O4BZ.js → chunk-EVND2DL5.js} +1 -1
  81. package/public/chunk-FM6ZYPKR.js +4 -0
  82. package/public/{chunk-MMLBIEDP.js → chunk-JJ7TVAIH.js} +4 -4
  83. package/public/{chunk-AYMJQ6UO.js → chunk-L43N56JA.js} +1 -1
  84. package/public/{chunk-3PJWBNGU.js → chunk-L5I3DZ23.js} +1 -1
  85. package/public/chunk-LH2LMHIZ.js +1 -0
  86. package/public/{chunk-CFEDPWK5.js → chunk-LWAL5JUG.js} +1 -1
  87. package/public/chunk-LZOHFRHN.js +1 -0
  88. package/public/{chunk-BJ7NEXKS.js → chunk-N2TWGDNX.js} +1 -1
  89. package/public/{chunk-WGFPD4J2.js → chunk-NKNWXFAK.js} +1 -1
  90. package/public/{chunk-7PXKC75G.js → chunk-O4SS7KVP.js} +1 -1
  91. package/public/{chunk-XYVHCMEV.js → chunk-OAHDGV32.js} +1 -1
  92. package/public/{chunk-DWKQU3C2.js → chunk-OK5EJ7US.js} +1 -1
  93. package/public/chunk-ONTEIYY3.js +19 -0
  94. package/public/{chunk-MYSGML5C.js → chunk-OS2SEJZU.js} +1 -1
  95. package/public/chunk-P7TZIGJS.js +50 -0
  96. package/public/{chunk-PYFU3YSX.js → chunk-Q2IS3QQY.js} +1 -1
  97. package/public/{chunk-QTJ3CQHI.js → chunk-QK4V5DVW.js} +1 -1
  98. package/public/chunk-QQ5VZZQG.js +1 -0
  99. package/public/{chunk-DRO3CNQN.js → chunk-RMZRXORR.js} +1 -1
  100. package/public/chunk-RYTGW6B7.js +1 -0
  101. package/public/chunk-SOT54YHI.js +1 -0
  102. package/public/{chunk-EV5RCPGM.js → chunk-SY4VNLXW.js} +1 -1
  103. package/public/chunk-TGZNYEGN.js +1 -0
  104. package/public/{chunk-TCYDYM5A.js → chunk-TOYVIOBH.js} +1 -1
  105. package/public/{chunk-CUP4YO7L.js → chunk-TSFGO3MA.js} +1 -1
  106. package/public/chunk-U5JF2ZOK.js +1 -0
  107. package/public/{chunk-IDDSROZF.js → chunk-UF2IM5BE.js} +1 -1
  108. package/public/{chunk-5R3NYRBJ.js → chunk-UNGEHXRN.js} +1 -1
  109. package/public/chunk-VEGMNMLG.js +1 -0
  110. package/public/{chunk-LTQGLD2K.js → chunk-VYC5JLE6.js} +1 -1
  111. package/public/{chunk-UGSDROFZ.js → chunk-W3SNJ7TC.js} +1 -1
  112. package/public/{chunk-5WH5JZQ5.js → chunk-WLGXJQPD.js} +1 -1
  113. package/public/{chunk-4FV7QIK7.js → chunk-WWSLIQVD.js} +1 -1
  114. package/public/{chunk-WZ6CFZR4.js → chunk-X5AKILHJ.js} +1 -1
  115. package/public/{chunk-6W43YCZG.js → chunk-Y5FP6C55.js} +1 -1
  116. package/public/{chunk-AOCOR5BO.js → chunk-YH6MRXGX.js} +1 -1
  117. package/public/{chunk-F2E7FMEJ.js → chunk-YNONXMOG.js} +1 -1
  118. package/public/{chunk-FW6YLROT.js → chunk-YYJG2ENF.js} +1 -1
  119. package/public/{chunk-X2HK2VP5.js → chunk-ZEI3HZ6P.js} +1 -1
  120. package/public/{chunk-ICEM4QHU.js → chunk-ZJJ3EOBP.js} +1 -1
  121. package/public/index.html +2 -2
  122. package/public/{main-OQMKHLDX.js → main-27RPBS22.js} +1 -1
  123. package/public/media/matter-P563JGDL.svg +8 -0
  124. package/public/{styles-7EFV5QBG.css → styles-CSF457UW.css} +1 -1
  125. package/scripts/extract-plugin-alias.js +53 -2
  126. package/public/chunk-3DANXKYW.js +0 -1
  127. package/public/chunk-4E3QJA2Q.js +0 -1
  128. package/public/chunk-BJ4JCAZT.js +0 -1
  129. package/public/chunk-BQ2T2C4E.js +0 -1
  130. package/public/chunk-CLRVHL2M.js +0 -1
  131. package/public/chunk-F72JFLBX.js +0 -1
  132. package/public/chunk-J7EVJFYY.js +0 -16
  133. package/public/chunk-MY3FPHNS.js +0 -1
  134. package/public/chunk-RSNBQILW.js +0 -4
  135. package/public/chunk-UBFASDE7.js +0 -1
  136. package/public/chunk-VLG3LTSC.js +0 -50
  137. package/public/chunk-X7FSVUGO.js +0 -19
  138. package/public/chunk-XYUTAICH.js +0 -1
@@ -1,10 +1,43 @@
1
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
+ });
2
18
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
19
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
20
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
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;
6
22
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
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
+ })();
8
41
  var __metadata = (this && this.__metadata) || function (k, v) {
9
42
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
43
  };
@@ -15,9 +48,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
48
  exports.ServerService = void 0;
16
49
  const node_buffer_1 = require("node:buffer");
17
50
  const node_child_process_1 = require("node:child_process");
51
+ const node_crypto_1 = require("node:crypto");
18
52
  const node_path_1 = require("node:path");
19
53
  const node_process_1 = __importDefault(require("node:process"));
20
54
  const node_stream_1 = require("node:stream");
55
+ const node_tls_1 = require("node:tls");
21
56
  const node_util_1 = require("node:util");
22
57
  const hap_types_1 = require("@homebridge/hap-client/dist/hap-types");
23
58
  const common_1 = require("@nestjs/common");
@@ -28,6 +63,7 @@ const tcp_port_used_1 = require("tcp-port-used");
28
63
  const config_service_1 = require("../../core/config/config.service");
29
64
  const homebridge_ipc_service_1 = require("../../core/homebridge-ipc/homebridge-ipc.service");
30
65
  const logger_service_1 = require("../../core/logger/logger.service");
66
+ const ssl_cert_generator_service_1 = require("../../core/ssl/ssl-cert-generator.service");
31
67
  const accessories_service_1 = require("../accessories/accessories.service");
32
68
  const config_editor_service_1 = require("../config-editor/config-editor.service");
33
69
  const pump = (0, node_util_1.promisify)(node_stream_1.pipeline);
@@ -44,25 +80,37 @@ let ServerService = class ServerService {
44
80
  this.accessoryId = this.configService.homebridgeConfig.bridge.username.split(':').join('');
45
81
  this.accessoryInfoPath = (0, node_path_1.join)(this.configService.storagePath, 'persist', `AccessoryInfo.${this.accessoryId}.json`);
46
82
  }
47
- async deleteSingleDeviceAccessories(id, cachedAccessoriesDir) {
48
- const cachedAccessories = (0, node_path_1.join)(cachedAccessoriesDir, `cachedAccessories.${id}`);
49
- const cachedAccessoriesBackup = (0, node_path_1.join)(cachedAccessoriesDir, `.cachedAccessories.${id}.bak`);
50
- if (await (0, fs_extra_1.pathExists)(cachedAccessories)) {
51
- await (0, fs_extra_1.unlink)(cachedAccessories);
52
- this.logger.warn(`Bridge ${id} accessory removal: removed ${cachedAccessories}.`);
83
+ async deleteSingleDeviceAccessories(id, cachedAccessoriesDir, protocol = 'both') {
84
+ if (protocol === 'hap' || protocol === 'both') {
85
+ const cachedAccessories = (0, node_path_1.join)(cachedAccessoriesDir, `cachedAccessories.${id}`);
86
+ const cachedAccessoriesBackup = (0, node_path_1.join)(cachedAccessoriesDir, `.cachedAccessories.${id}.bak`);
87
+ if (await (0, fs_extra_1.pathExists)(cachedAccessories)) {
88
+ await (0, fs_extra_1.unlink)(cachedAccessories);
89
+ this.logger.warn(`Bridge ${id} HAP accessory removal: removed ${cachedAccessories}.`);
90
+ }
91
+ if (await (0, fs_extra_1.pathExists)(cachedAccessoriesBackup)) {
92
+ await (0, fs_extra_1.unlink)(cachedAccessoriesBackup);
93
+ this.logger.warn(`Bridge ${id} HAP accessory removal: removed ${cachedAccessoriesBackup}.`);
94
+ }
53
95
  }
54
- if (await (0, fs_extra_1.pathExists)(cachedAccessoriesBackup)) {
55
- await (0, fs_extra_1.unlink)(cachedAccessoriesBackup);
56
- this.logger.warn(`Bridge ${id} accessory removal: removed ${cachedAccessoriesBackup}.`);
96
+ if (protocol === 'matter' || protocol === 'both') {
97
+ const deviceId = id.split(':').join('').toUpperCase();
98
+ const matterPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId);
99
+ if (await (0, fs_extra_1.pathExists)(matterPath)) {
100
+ await (0, fs_extra_1.remove)(matterPath);
101
+ this.logger.warn(`Bridge ${id} Matter accessory removal: removed Matter bridge storage at ${matterPath}.`);
102
+ }
57
103
  }
58
104
  }
59
105
  async deleteSingleDevicePairing(id, resetPairingInfo) {
60
106
  const persistPath = (0, node_path_1.join)(this.configService.storagePath, 'persist');
61
107
  const accessoryInfo = (0, node_path_1.join)(persistPath, `AccessoryInfo.${id}.json`);
62
108
  const identifierCache = (0, node_path_1.join)(persistPath, `IdentifierCache.${id}.json`);
109
+ const deviceId = id.includes(':') ? id.split(':').join('').toUpperCase() : id.toUpperCase();
110
+ const matterPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId);
63
111
  try {
64
112
  const configFile = await this.configEditorService.getConfigFile();
65
- const username = id.match(/.{1,2}/g).join(':').toUpperCase();
113
+ const username = id.includes(':') ? id.toUpperCase() : id.match(/.{1,2}/g)?.join(':').toUpperCase() || id.toUpperCase();
66
114
  const uiConfig = configFile.platforms.find(x => x.platform === 'config');
67
115
  let blacklistChanged = false;
68
116
  if (uiConfig.accessoryControl?.instanceBlacklist?.includes(username)) {
@@ -112,6 +160,10 @@ let ServerService = class ServerService {
112
160
  await (0, fs_extra_1.unlink)(identifierCache);
113
161
  this.logger.warn(`Bridge ${id} reset: removed ${identifierCache}.`);
114
162
  }
163
+ if (await (0, fs_extra_1.pathExists)(matterPath)) {
164
+ await (0, fs_extra_1.remove)(matterPath);
165
+ this.logger.warn(`Bridge ${id} reset: removed Matter bridge storage at ${matterPath}.`);
166
+ }
115
167
  await this.deleteDeviceAccessories(id);
116
168
  }
117
169
  async restartServer() {
@@ -155,6 +207,12 @@ let ServerService = class ServerService {
155
207
  await this.configEditorService.updateConfigFile(configFile);
156
208
  await (0, fs_extra_1.remove)((0, node_path_1.resolve)(this.configService.storagePath, 'accessories'));
157
209
  await (0, fs_extra_1.remove)((0, node_path_1.resolve)(this.configService.storagePath, 'persist'));
210
+ const deviceId = oldUsername.split(':').join('').toUpperCase();
211
+ const matterPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId);
212
+ if (await (0, fs_extra_1.pathExists)(matterPath)) {
213
+ await (0, fs_extra_1.remove)(matterPath);
214
+ this.logger.warn(`Bridge ${oldUsername} reset: removed Matter bridge storage at ${matterPath}.`);
215
+ }
158
216
  this.logger.log('Homebridge bridge reset: accessories and persist directories were removed.');
159
217
  }
160
218
  async getDevicePairings() {
@@ -162,9 +220,68 @@ let ServerService = class ServerService {
162
220
  const devices = (await (0, fs_extra_1.readdir)(persistPath))
163
221
  .filter(x => x.match(/AccessoryInfo\.([A-Fa-f0-9]+)\.json$/));
164
222
  const configFile = await this.configEditorService.getConfigFile();
165
- return Promise.all(devices.map(async (x) => {
223
+ const hapDevices = await Promise.all(devices.map(async (x) => {
166
224
  return await this.getDevicePairingById(x.split('.')[1], configFile);
167
225
  }));
226
+ const matterExternalDevices = await this.getMatterExternalAccessories(configFile, hapDevices);
227
+ return [...hapDevices, ...matterExternalDevices].sort((a, b) => a.name.localeCompare(b.name));
228
+ }
229
+ async getMatterExternalAccessories(configFile, hapDevices) {
230
+ const matterPath = (0, node_path_1.join)(this.configService.storagePath, 'matter');
231
+ if (!await (0, fs_extra_1.pathExists)(matterPath)) {
232
+ return [];
233
+ }
234
+ const matterDirs = (await (0, fs_extra_1.readdir)(matterPath))
235
+ .filter(x => x.match(/^[A-F0-9]{12}$/));
236
+ const matterExternalDevices = [];
237
+ for (const deviceId of matterDirs) {
238
+ try {
239
+ const hasHapAccessoryInfo = hapDevices.some(d => d._id === deviceId);
240
+ if (hasHapAccessoryInfo) {
241
+ continue;
242
+ }
243
+ const mainBridgeId = this.configService.homebridgeConfig.bridge.username.split(':').join('').toUpperCase();
244
+ if (deviceId.toUpperCase() === mainBridgeId) {
245
+ continue;
246
+ }
247
+ const accessoriesPath = (0, node_path_1.join)(matterPath, deviceId, 'accessories.json');
248
+ if (!await (0, fs_extra_1.pathExists)(accessoriesPath)) {
249
+ continue;
250
+ }
251
+ const accessories = await (0, fs_extra_1.readJson)(accessoriesPath);
252
+ if (!Array.isArray(accessories) || accessories.length === 0) {
253
+ continue;
254
+ }
255
+ const accessory = accessories[0];
256
+ const commissioningPath = (0, node_path_1.join)(matterPath, deviceId, 'commissioning.json');
257
+ let commissioned = false;
258
+ if (await (0, fs_extra_1.pathExists)(commissioningPath)) {
259
+ const commissioningInfo = await (0, fs_extra_1.readJson)(commissioningPath);
260
+ commissioned = commissioningInfo.commissioned || false;
261
+ }
262
+ const device = {
263
+ _id: deviceId,
264
+ _username: deviceId.match(/.{1,2}/g)?.join(':').toUpperCase() || deviceId,
265
+ _main: false,
266
+ _category: 'other',
267
+ _matter: true,
268
+ _matterOnly: true,
269
+ _isPaired: commissioned,
270
+ _plugin: accessory.plugin,
271
+ name: accessory.displayName || 'Matter External Accessory',
272
+ displayName: accessory.displayName || 'Matter External Accessory',
273
+ manufacturer: accessory.manufacturer || 'Unknown',
274
+ model: accessory.model || 'Unknown',
275
+ serialNumber: accessory.serialNumber || deviceId,
276
+ category: 1,
277
+ };
278
+ matterExternalDevices.push(device);
279
+ }
280
+ catch (e) {
281
+ this.logger.error(`Failed to read Matter external accessory ${deviceId}: ${e.message}`);
282
+ }
283
+ }
284
+ return matterExternalDevices;
168
285
  }
169
286
  async getDevicePairingById(deviceId, configFile = null) {
170
287
  const persistPath = (0, node_path_1.join)(this.configService.storagePath, 'persist');
@@ -197,6 +314,10 @@ let ServerService = class ServerService {
197
314
  device._isPaired = device.pairedClients && Object.keys(device.pairedClients).length > 0;
198
315
  device._setupCode = this.generateSetupCode(device);
199
316
  device._couldBeStale = !device._main && device._category === 'bridge' && !pluginBlock;
317
+ device._matter = !!(pluginBlock?._bridge?.matter);
318
+ if (device._matter && pluginBlock && 'accessory' in pluginBlock) {
319
+ this.logger.warn(`Device ${deviceId} has Matter configuration on an accessory-based plugin. Matter is only supported for platform-based plugins.`);
320
+ }
200
321
  delete device.signSk;
201
322
  delete device.signPk;
202
323
  delete device.configHash;
@@ -210,6 +331,44 @@ let ServerService = class ServerService {
210
331
  await this.deleteSingleDevicePairing(id, resetPairingInfo);
211
332
  return { ok: true };
212
333
  }
334
+ async deleteDeviceMatterConfig(id) {
335
+ try {
336
+ const configFile = await this.configEditorService.getConfigFile();
337
+ const username = id.includes(':') ? id.toUpperCase() : id.match(/.{1,2}/g)?.join(':').toUpperCase() || id.toUpperCase();
338
+ const pluginBlocks = [
339
+ ...(configFile.accessories || []),
340
+ ...(configFile.platforms || []),
341
+ ]
342
+ .filter((block) => block._bridge?.username?.toUpperCase() === username.toUpperCase());
343
+ const pluginBlock = pluginBlocks.find((block) => block._bridge?.matter);
344
+ if (!pluginBlock) {
345
+ this.logger.error(`Failed to find Matter configuration for child bridge ${id}.`);
346
+ throw new common_1.NotFoundException(`Matter configuration not found for bridge ${id}`);
347
+ }
348
+ if ('accessory' in pluginBlock) {
349
+ this.logger.warn(`Removing Matter configuration from accessory-based plugin block for bridge ${id}. Matter is only supported for platform-based plugins.`);
350
+ }
351
+ delete pluginBlock._bridge.matter;
352
+ this.logger.warn(`Bridge ${id} Matter configuration removed from config.json.`);
353
+ await this.configEditorService.updateConfigFile(configFile);
354
+ }
355
+ catch (e) {
356
+ if (e instanceof common_1.NotFoundException) {
357
+ throw e;
358
+ }
359
+ this.logger.error(`Failed to remove Matter configuration for child bridge ${id} as ${e.message}.`);
360
+ throw new common_1.InternalServerErrorException(`Failed to remove Matter configuration: ${e.message}`);
361
+ }
362
+ this.logger.warn(`Shutting down Homebridge before removing Matter storage for bridge ${id}...`);
363
+ await this.homebridgeIpcService.restartAndWaitForClose();
364
+ const deviceId = id.includes(':') ? id.split(':').join('').toUpperCase() : id.toUpperCase();
365
+ const matterPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId);
366
+ if (await (0, fs_extra_1.pathExists)(matterPath)) {
367
+ await (0, fs_extra_1.remove)(matterPath);
368
+ this.logger.warn(`Bridge ${id} Matter storage removed at ${matterPath}.`);
369
+ }
370
+ return { ok: true };
371
+ }
213
372
  async deleteDevicesPairing(bridges) {
214
373
  this.logger.warn(`Shutting down Homebridge before resetting paired bridges ${bridges.map(x => x.id).join(', ')}...`);
215
374
  await this.homebridgeIpcService.restartAndWaitForClose();
@@ -233,9 +392,9 @@ let ServerService = class ServerService {
233
392
  this.logger.warn(`Shutting down Homebridge before removing accessories for paired bridges ${bridges.map(x => x.id).join(', ')}...`);
234
393
  await this.homebridgeIpcService.restartAndWaitForClose();
235
394
  const cachedAccessoriesDir = (0, node_path_1.join)(this.configService.storagePath, 'accessories');
236
- for (const { id } of bridges) {
395
+ for (const { id, protocol } of bridges) {
237
396
  try {
238
- await this.deleteSingleDeviceAccessories(id, cachedAccessoriesDir);
397
+ await this.deleteSingleDeviceAccessories(id, cachedAccessoriesDir, protocol || 'both');
239
398
  }
240
399
  catch (e) {
241
400
  this.logger.error(`Failed to remove accessories for bridge ${id} as ${e.message}.`);
@@ -316,13 +475,19 @@ let ServerService = class ServerService {
316
475
  await this.homebridgeIpcService.restartAndWaitForClose();
317
476
  this.logger.warn('Shutting down Homebridge before removing cached accessories');
318
477
  try {
319
- this.logger.log('Clearing all cached accessories...');
478
+ this.logger.log('Clearing all HAP cached accessories...');
320
479
  for (const thisCachedAccessoriesPath of cachedAccessoryPaths) {
321
480
  if (await (0, fs_extra_1.pathExists)(thisCachedAccessoriesPath)) {
322
481
  await (0, fs_extra_1.unlink)(thisCachedAccessoriesPath);
323
482
  this.logger.warn(`Removed ${thisCachedAccessoriesPath}.`);
324
483
  }
325
484
  }
485
+ const matterDir = (0, node_path_1.join)(this.configService.storagePath, 'matter');
486
+ if (await (0, fs_extra_1.pathExists)(matterDir)) {
487
+ this.logger.log('Clearing all Matter cached accessories...');
488
+ await (0, fs_extra_1.remove)(matterDir);
489
+ this.logger.warn(`Removed Matter storage directory at ${matterDir}.`);
490
+ }
326
491
  }
327
492
  catch (e) {
328
493
  this.logger.error(`Failed to clear all cached accessories at ${cachedAccessoriesPath} as ${e.message}.`);
@@ -331,6 +496,96 @@ let ServerService = class ServerService {
331
496
  }
332
497
  return { ok: true };
333
498
  }
499
+ async getMatterAccessories() {
500
+ const matterDir = (0, node_path_1.join)(this.configService.storagePath, 'matter');
501
+ if (!await (0, fs_extra_1.pathExists)(matterDir)) {
502
+ return [];
503
+ }
504
+ const matterBridges = (await (0, fs_extra_1.readdir)(matterDir))
505
+ .filter(x => x.match(/^[A-F0-9]+$/));
506
+ const matterAccessories = [];
507
+ await Promise.all(matterBridges.map(async (deviceId) => {
508
+ try {
509
+ const accessoriesPath = (0, node_path_1.join)(matterDir, deviceId, 'accessories.json');
510
+ if (await (0, fs_extra_1.pathExists)(accessoriesPath)) {
511
+ const accessories = await (0, fs_extra_1.readJson)(accessoriesPath);
512
+ if (Array.isArray(accessories)) {
513
+ for (const accessory of accessories) {
514
+ accessory.$deviceId = deviceId;
515
+ accessory.$protocol = 'matter';
516
+ matterAccessories.push(accessory);
517
+ }
518
+ }
519
+ }
520
+ }
521
+ catch (e) {
522
+ this.logger.error(`Failed to read Matter accessories for bridge ${deviceId}: ${e.message}`);
523
+ }
524
+ }));
525
+ return matterAccessories;
526
+ }
527
+ async deleteMatterAccessory(deviceId, uuid) {
528
+ const matterAccessoriesPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId, 'accessories.json');
529
+ if (!await (0, fs_extra_1.pathExists)(matterAccessoriesPath)) {
530
+ this.logger.error(`Matter accessories file not found for bridge ${deviceId}`);
531
+ throw new common_1.NotFoundException();
532
+ }
533
+ this.logger.warn(`Shutting down Homebridge before removing Matter accessory ${uuid} from bridge ${deviceId}...`);
534
+ await this.homebridgeIpcService.restartAndWaitForClose();
535
+ const matterAccessories = await (0, fs_extra_1.readJson)(matterAccessoriesPath);
536
+ const accessoryIndex = matterAccessories.findIndex(x => x.uuid === uuid);
537
+ if (accessoryIndex > -1) {
538
+ matterAccessories.splice(accessoryIndex, 1);
539
+ await (0, fs_extra_1.writeJson)(matterAccessoriesPath, matterAccessories, { spaces: 2 });
540
+ this.logger.warn(`Removed Matter accessory with UUID ${uuid} from bridge ${deviceId}.`);
541
+ }
542
+ else {
543
+ this.logger.error(`Cannot find Matter accessory with UUID ${uuid} in bridge ${deviceId}.`);
544
+ throw new common_1.NotFoundException();
545
+ }
546
+ return { ok: true };
547
+ }
548
+ async deleteMatterAccessories(accessories) {
549
+ this.logger.warn(`Shutting down Homebridge before removing Matter accessories ${accessories.map(x => x.uuid).join(', ')}.`);
550
+ await this.homebridgeIpcService.restartAndWaitForClose();
551
+ const accessoriesByBridge = new Map();
552
+ for (const { deviceId, uuid } of accessories) {
553
+ if (!accessoriesByBridge.has(deviceId)) {
554
+ accessoriesByBridge.set(deviceId, []);
555
+ }
556
+ accessoriesByBridge.get(deviceId).push({ uuid });
557
+ }
558
+ for (const [deviceId, bridgeAccessories] of accessoriesByBridge.entries()) {
559
+ const matterAccessoriesPath = (0, node_path_1.join)(this.configService.storagePath, 'matter', deviceId, 'accessories.json');
560
+ try {
561
+ if (!await (0, fs_extra_1.pathExists)(matterAccessoriesPath)) {
562
+ this.logger.error(`Matter accessories file not found for bridge ${deviceId}`);
563
+ continue;
564
+ }
565
+ const matterAccessories = await (0, fs_extra_1.readJson)(matterAccessoriesPath);
566
+ for (const { uuid } of bridgeAccessories) {
567
+ try {
568
+ const accessoryIndex = matterAccessories.findIndex(x => x.uuid === uuid);
569
+ if (accessoryIndex > -1) {
570
+ matterAccessories.splice(accessoryIndex, 1);
571
+ this.logger.warn(`Removed Matter accessory with UUID ${uuid} from bridge ${deviceId}.`);
572
+ }
573
+ else {
574
+ this.logger.error(`Cannot find Matter accessory with UUID ${uuid} in bridge ${deviceId}.`);
575
+ }
576
+ }
577
+ catch (e) {
578
+ this.logger.error(`Failed to remove Matter accessory with UUID ${uuid} from bridge ${deviceId} as ${e.message}.`);
579
+ }
580
+ }
581
+ await (0, fs_extra_1.writeJson)(matterAccessoriesPath, matterAccessories, { spaces: 2 });
582
+ }
583
+ catch (e) {
584
+ this.logger.error(`Failed to process Matter accessories for bridge ${deviceId} as ${e.message}.`);
585
+ }
586
+ }
587
+ return { ok: true };
588
+ }
334
589
  async getSetupCode() {
335
590
  if (this.setupCode) {
336
591
  return this.setupCode;
@@ -435,6 +690,30 @@ let ServerService = class ServerService {
435
690
  }
436
691
  return { port };
437
692
  }
693
+ async lookupUnusedMatterPort() {
694
+ const min = 5530;
695
+ const max = 5541;
696
+ const config = await this.configEditorService.getConfigFile();
697
+ const usedMatterPorts = new Set();
698
+ if (config.bridge?.matter?.port) {
699
+ usedMatterPorts.add(config.bridge.matter.port);
700
+ }
701
+ for (const block of [...(config.accessories || []), ...(config.platforms || [])]) {
702
+ if (block._bridge?.matter?.port) {
703
+ if ('accessory' in block) {
704
+ this.logger.warn(`Found Matter configuration on accessory-based plugin block, skipping port ${block._bridge.matter.port}`);
705
+ continue;
706
+ }
707
+ usedMatterPorts.add(block._bridge.matter.port);
708
+ }
709
+ }
710
+ for (let port = min; port <= max; port += 1) {
711
+ if (!usedMatterPorts.has(port) && !await (0, tcp_port_used_1.check)(port)) {
712
+ return { port };
713
+ }
714
+ }
715
+ throw new common_1.InternalServerErrorException('No available ports in the Matter port range (5530-5541)');
716
+ }
438
717
  async getHomebridgePort() {
439
718
  const config = await this.configEditorService.getConfigFile();
440
719
  return { port: config.bridge.port };
@@ -555,7 +834,7 @@ let ServerService = class ServerService {
555
834
  async nodeVersionChanged() {
556
835
  return new Promise((res) => {
557
836
  let result = false;
558
- const child = (0, node_child_process_1.spawn)(node_process_1.default.execPath, ['-v'], { shell: true });
837
+ const child = (0, node_child_process_1.spawn)(node_process_1.default.execPath, ['-v']);
559
838
  child.stdout.once('data', (data) => {
560
839
  result = data.toString().trim() !== node_process_1.default.version;
561
840
  });
@@ -567,6 +846,248 @@ let ServerService = class ServerService {
567
846
  });
568
847
  });
569
848
  }
849
+ async uploadSslKeyCert(req) {
850
+ const parts = req.parts ? req.parts() : null;
851
+ const files = [];
852
+ if (parts) {
853
+ for await (const part of parts) {
854
+ if (part.file) {
855
+ files.push(part);
856
+ }
857
+ }
858
+ }
859
+ else {
860
+ const single = await req.file();
861
+ if (single?.file) {
862
+ files.push(single);
863
+ }
864
+ }
865
+ if (!files.length) {
866
+ throw new common_1.BadRequestException('No files uploaded. Please upload both the private key and certificate files.');
867
+ }
868
+ const readStreamToBuffer = async (stream) => {
869
+ const chunks = [];
870
+ await new Promise((resolvePromise, rejectPromise) => {
871
+ stream.on('data', (d) => chunks.push(node_buffer_1.Buffer.isBuffer(d) ? d : node_buffer_1.Buffer.from(d)));
872
+ stream.on('end', () => resolvePromise());
873
+ stream.on('error', rejectPromise);
874
+ });
875
+ return node_buffer_1.Buffer.concat(chunks);
876
+ };
877
+ let keyPem = null;
878
+ let certPem = null;
879
+ for (const f of files) {
880
+ if (f.file?.truncated) {
881
+ throw new common_1.InternalServerErrorException(`Upload exceeds maximum size ${globalThis.backup.maxBackupSizeText}.`);
882
+ }
883
+ const buf = await readStreamToBuffer(f.file);
884
+ const text = buf.toString('utf8');
885
+ if (/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/.test(text)) {
886
+ keyPem = buf;
887
+ }
888
+ else if (/-----BEGIN CERTIFICATE-----/.test(text)) {
889
+ certPem = buf;
890
+ }
891
+ else if (f.fieldname === 'key') {
892
+ keyPem = buf;
893
+ }
894
+ else if (f.fieldname === 'cert') {
895
+ certPem = buf;
896
+ }
897
+ }
898
+ if (!keyPem || !certPem) {
899
+ throw new common_1.BadRequestException('Both a PEM private key and certificate must be provided.');
900
+ }
901
+ try {
902
+ const x509 = new node_crypto_1.X509Certificate(certPem);
903
+ const certPub = x509.publicKey.export({ type: 'spki', format: 'der' });
904
+ const priv = (0, node_crypto_1.createPrivateKey)({ key: keyPem });
905
+ const pubFromPriv = (0, node_crypto_1.createPublicKey)(priv).export({ type: 'spki', format: 'der' });
906
+ if (!certPub.equals(pubFromPriv)) {
907
+ throw new common_1.BadRequestException('The private key does not match the certificate public key.');
908
+ }
909
+ (0, node_tls_1.createSecureContext)({ key: keyPem, cert: certPem });
910
+ }
911
+ catch (e) {
912
+ if (e instanceof common_1.BadRequestException) {
913
+ throw e;
914
+ }
915
+ throw new common_1.BadRequestException(`Invalid key/certificate: ${e?.message || e}`);
916
+ }
917
+ const sslDir = (0, node_path_1.join)(this.configService.storagePath, 'ssl-certs');
918
+ const keyPath = (0, node_path_1.join)(sslDir, 'ui-ssl.key');
919
+ const certPath = (0, node_path_1.join)(sslDir, 'ui-ssl.crt');
920
+ const { ensureDir, writeFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
921
+ await ensureDir(sslDir);
922
+ await writeFile(keyPath, keyPem);
923
+ await writeFile(certPath, certPem);
924
+ const configFile = await this.configEditorService.getConfigFile();
925
+ const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
926
+ if (!uiConfigBlock) {
927
+ throw new common_1.InternalServerErrorException('Config platform block not found.');
928
+ }
929
+ if (!uiConfigBlock.ssl) {
930
+ uiConfigBlock.ssl = {};
931
+ }
932
+ uiConfigBlock.ssl.key = keyPath;
933
+ uiConfigBlock.ssl.cert = certPath;
934
+ delete uiConfigBlock.ssl.pfx;
935
+ delete uiConfigBlock.ssl.passphrase;
936
+ uiConfigBlock.ssl.selfSigned = false;
937
+ await this.configEditorService.updateConfigFile(configFile);
938
+ return {
939
+ ok: true,
940
+ type: 'keycert',
941
+ keyPath,
942
+ certPath,
943
+ details: 'Certificate and key validated and saved.',
944
+ };
945
+ }
946
+ async uploadSslPfx(req) {
947
+ let passphrase;
948
+ let filePart;
949
+ if (req.parts) {
950
+ for await (const part of req.parts()) {
951
+ if (part.type === 'file' || part.file) {
952
+ filePart = part;
953
+ }
954
+ else if (part.type === 'field' || part.value) {
955
+ if (part.fieldname === 'passphrase') {
956
+ passphrase = part.value;
957
+ }
958
+ }
959
+ }
960
+ }
961
+ else {
962
+ filePart = await req.file();
963
+ passphrase = req.body?.passphrase;
964
+ }
965
+ if (!filePart) {
966
+ throw new common_1.BadRequestException('No PFX file uploaded.');
967
+ }
968
+ if (filePart.file?.truncated) {
969
+ throw new common_1.InternalServerErrorException(`Upload exceeds maximum size ${globalThis.backup.maxBackupSizeText}.`);
970
+ }
971
+ const readStreamToBuffer = async (stream) => {
972
+ const chunks = [];
973
+ await new Promise((resolvePromise, rejectPromise) => {
974
+ stream.on('data', (d) => chunks.push(node_buffer_1.Buffer.isBuffer(d) ? d : node_buffer_1.Buffer.from(d)));
975
+ stream.on('end', () => resolvePromise());
976
+ stream.on('error', rejectPromise);
977
+ });
978
+ return node_buffer_1.Buffer.concat(chunks);
979
+ };
980
+ const pfxBuffer = await readStreamToBuffer(filePart.file);
981
+ try {
982
+ (0, node_tls_1.createSecureContext)({ pfx: pfxBuffer, passphrase });
983
+ }
984
+ catch (e) {
985
+ throw new common_1.BadRequestException(`Invalid PFX or passphrase: ${e?.message || e}`);
986
+ }
987
+ const sslDir = (0, node_path_1.join)(this.configService.storagePath, 'ssl-certs');
988
+ const pfxPath = (0, node_path_1.join)(sslDir, 'ui-ssl.pfx');
989
+ const { ensureDir, writeFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
990
+ await ensureDir(sslDir);
991
+ await writeFile(pfxPath, pfxBuffer);
992
+ const configFile = await this.configEditorService.getConfigFile();
993
+ const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
994
+ if (!uiConfigBlock) {
995
+ throw new common_1.InternalServerErrorException('Config platform block not found.');
996
+ }
997
+ if (!uiConfigBlock.ssl) {
998
+ uiConfigBlock.ssl = {};
999
+ }
1000
+ uiConfigBlock.ssl.pfx = pfxPath;
1001
+ uiConfigBlock.ssl.passphrase = passphrase || '';
1002
+ delete uiConfigBlock.ssl.key;
1003
+ delete uiConfigBlock.ssl.cert;
1004
+ uiConfigBlock.ssl.selfSigned = false;
1005
+ await this.configEditorService.updateConfigFile(configFile);
1006
+ return {
1007
+ ok: true,
1008
+ type: 'pfx',
1009
+ pfxPath,
1010
+ details: 'PFX validated and saved.',
1011
+ };
1012
+ }
1013
+ async validateCurrentSslConfig() {
1014
+ const configFile = await this.configEditorService.getConfigFile();
1015
+ const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
1016
+ const ssl = uiConfigBlock?.ssl || {};
1017
+ if (!ssl || (!ssl.selfSigned && !ssl.key && !ssl.cert && !ssl.pfx)) {
1018
+ return { ok: true, valid: true, type: 'off', details: 'HTTPS is disabled.' };
1019
+ }
1020
+ if (ssl.selfSigned) {
1021
+ return { ok: true, valid: true, type: 'selfsigned', details: 'Self-signed mode enabled.' };
1022
+ }
1023
+ try {
1024
+ if (ssl.key && ssl.cert) {
1025
+ const { readFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
1026
+ const keyPem = await readFile(ssl.key);
1027
+ const certPem = await readFile(ssl.cert);
1028
+ const x509 = new node_crypto_1.X509Certificate(certPem);
1029
+ const certPub = x509.publicKey.export({ type: 'spki', format: 'der' });
1030
+ const priv = (0, node_crypto_1.createPrivateKey)({ key: keyPem });
1031
+ const pubFromPriv = (0, node_crypto_1.createPublicKey)(priv).export({ type: 'spki', format: 'der' });
1032
+ if (!certPub.equals(pubFromPriv)) {
1033
+ return { ok: true, valid: false, type: 'keycert', details: 'Private key does not match certificate.' };
1034
+ }
1035
+ (0, node_tls_1.createSecureContext)({ key: keyPem, cert: certPem });
1036
+ return { ok: true, valid: true, type: 'keycert', details: 'Key and certificate are valid and match.' };
1037
+ }
1038
+ if (ssl.pfx) {
1039
+ const { readFile } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
1040
+ const pfx = await readFile(ssl.pfx);
1041
+ (0, node_tls_1.createSecureContext)({ pfx, passphrase: ssl.passphrase });
1042
+ return { ok: true, valid: true, type: 'pfx', details: 'PFX file and passphrase are valid.' };
1043
+ }
1044
+ }
1045
+ catch (e) {
1046
+ return { ok: true, valid: false, type: ssl.pfx ? 'pfx' : 'keycert', details: e?.message || String(e) };
1047
+ }
1048
+ return { ok: true, valid: false, type: 'off', details: 'No SSL configuration found.' };
1049
+ }
1050
+ async generateSelfSignedCertificate(options = {}) {
1051
+ const hostnames = Array.isArray(options.hostnames) && options.hostnames.length
1052
+ ? options.hostnames.map(h => String(h).trim()).filter(Boolean)
1053
+ : ['localhost', '127.0.0.1'];
1054
+ const mode = options.mode || 'keycert';
1055
+ const generator = new ssl_cert_generator_service_1.SslCertGeneratorService();
1056
+ await generator.generateCertificate(hostnames);
1057
+ const sslDir = (0, node_path_1.join)(this.configService.storagePath, 'ssl-certs');
1058
+ const keyPath = (0, node_path_1.join)(sslDir, 'private-key.pem');
1059
+ const certPath = (0, node_path_1.join)(sslDir, 'certificate.pem');
1060
+ const configFile = await this.configEditorService.getConfigFile();
1061
+ const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
1062
+ if (!uiConfigBlock.ssl) {
1063
+ uiConfigBlock.ssl = {};
1064
+ }
1065
+ if (mode === 'keycert') {
1066
+ uiConfigBlock.ssl.key = keyPath;
1067
+ uiConfigBlock.ssl.cert = certPath;
1068
+ delete uiConfigBlock.ssl.pfx;
1069
+ delete uiConfigBlock.ssl.passphrase;
1070
+ uiConfigBlock.ssl.selfSigned = false;
1071
+ uiConfigBlock.ssl.selfSignedHostnames = hostnames;
1072
+ }
1073
+ else {
1074
+ delete uiConfigBlock.ssl.key;
1075
+ delete uiConfigBlock.ssl.cert;
1076
+ delete uiConfigBlock.ssl.pfx;
1077
+ delete uiConfigBlock.ssl.passphrase;
1078
+ uiConfigBlock.ssl.selfSigned = true;
1079
+ uiConfigBlock.ssl.selfSignedHostnames = hostnames;
1080
+ }
1081
+ await this.configEditorService.updateConfigFile(configFile);
1082
+ return {
1083
+ ok: true,
1084
+ type: 'generated',
1085
+ mode,
1086
+ keyPath: mode === 'keycert' ? keyPath : undefined,
1087
+ certPath: mode === 'keycert' ? certPath : undefined,
1088
+ details: `Self-signed certificate generated for ${hostnames.join(', ')}`,
1089
+ };
1090
+ }
570
1091
  };
571
1092
  exports.ServerService = ServerService;
572
1093
  exports.ServerService = ServerService = __decorate([