homebridge-config-ui-x 5.9.1-beta.0 → 5.9.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -2
- package/dist/app.controller.d.ts +1 -1
- package/dist/app.controller.js +14 -12
- package/dist/app.controller.js.map +1 -1
- package/dist/app.gateway.js +7 -10
- package/dist/app.gateway.js.map +1 -1
- package/dist/app.module.js +40 -43
- package/dist/app.module.js.map +1 -1
- package/dist/app.service.js +4 -7
- package/dist/app.service.js.map +1 -1
- package/dist/bin/base-platform.d.ts +1 -1
- package/dist/bin/base-platform.js +11 -17
- package/dist/bin/base-platform.js.map +1 -1
- package/dist/bin/fork.js +7 -45
- package/dist/bin/fork.js.map +1 -1
- package/dist/bin/hb-service.js +224 -243
- package/dist/bin/hb-service.js.map +1 -1
- package/dist/bin/platforms/darwin.d.ts +1 -1
- package/dist/bin/platforms/darwin.js +61 -65
- package/dist/bin/platforms/darwin.js.map +1 -1
- package/dist/bin/platforms/freebsd.d.ts +1 -1
- package/dist/bin/platforms/freebsd.js +42 -47
- package/dist/bin/platforms/freebsd.js.map +1 -1
- package/dist/bin/platforms/linux.d.ts +1 -1
- package/dist/bin/platforms/linux.js +127 -132
- package/dist/bin/platforms/linux.js.map +1 -1
- package/dist/bin/platforms/win32.d.ts +1 -1
- package/dist/bin/platforms/win32.js +30 -36
- package/dist/bin/platforms/win32.js.map +1 -1
- package/dist/bin/standalone.js +15 -53
- package/dist/bin/standalone.js.map +1 -1
- package/dist/core/auth/auth.controller.d.ts +3 -3
- package/dist/core/auth/auth.controller.js +40 -39
- package/dist/core/auth/auth.controller.js.map +1 -1
- package/dist/core/auth/auth.dto.js +17 -18
- package/dist/core/auth/auth.dto.js.map +1 -1
- package/dist/core/auth/auth.module.js +28 -31
- package/dist/core/auth/auth.module.js.map +1 -1
- package/dist/core/auth/auth.service.d.ts +3 -3
- package/dist/core/auth/auth.service.js +65 -62
- package/dist/core/auth/auth.service.js.map +1 -1
- package/dist/core/auth/guards/admin.guard.js +4 -7
- package/dist/core/auth/guards/admin.guard.js.map +1 -1
- package/dist/core/auth/guards/custom.guard.js +6 -9
- package/dist/core/auth/guards/custom.guard.js.map +1 -1
- package/dist/core/auth/guards/ws-admin-guard.d.ts +1 -1
- package/dist/core/auth/guards/ws-admin-guard.js +13 -11
- package/dist/core/auth/guards/ws-admin-guard.js.map +1 -1
- package/dist/core/auth/guards/ws.guard.d.ts +1 -1
- package/dist/core/auth/guards/ws.guard.js +13 -11
- package/dist/core/auth/guards/ws.guard.js.map +1 -1
- package/dist/core/auth/jwt.strategy.d.ts +3 -2
- package/dist/core/auth/jwt.strategy.js +21 -16
- package/dist/core/auth/jwt.strategy.js.map +1 -1
- package/dist/core/config/config.interfaces.js +1 -2
- package/dist/core/config/config.module.js +8 -10
- package/dist/core/config/config.module.js.map +1 -1
- package/dist/core/config/config.service.d.ts +2 -2
- package/dist/core/config/config.service.js +88 -82
- package/dist/core/config/config.service.js.map +1 -1
- package/dist/core/config/config.startup.d.ts +1 -1
- package/dist/core/config/config.startup.js +23 -28
- package/dist/core/config/config.startup.js.map +1 -1
- package/dist/core/feature-flags/feature-flags.registry.js +1 -4
- package/dist/core/feature-flags/feature-flags.registry.js.map +1 -1
- package/dist/core/homebridge-ipc/homebridge-ipc.module.js +11 -14
- package/dist/core/homebridge-ipc/homebridge-ipc.module.js.map +1 -1
- package/dist/core/homebridge-ipc/homebridge-ipc.service.d.ts +2 -2
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js +24 -19
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js.map +1 -1
- package/dist/core/logger/logger.module.js +8 -10
- package/dist/core/logger/logger.module.js.map +1 -1
- package/dist/core/logger/logger.service.js +13 -23
- package/dist/core/logger/logger.service.js.map +1 -1
- package/dist/core/node-pty/node-pty.module.js +7 -10
- package/dist/core/node-pty/node-pty.module.js.map +1 -1
- package/dist/core/node-pty/node-pty.service.js +6 -11
- package/dist/core/node-pty/node-pty.service.js.map +1 -1
- package/dist/core/node-version.constants.js +4 -11
- package/dist/core/node-version.constants.js.map +1 -1
- package/dist/core/scheduler/scheduler.module.js +7 -10
- package/dist/core/scheduler/scheduler.module.js.map +1 -1
- package/dist/core/scheduler/scheduler.service.js +9 -14
- package/dist/core/scheduler/scheduler.service.js.map +1 -1
- package/dist/core/spa/spa-html.service.js +9 -15
- package/dist/core/spa/spa-html.service.js.map +1 -1
- package/dist/core/spa/spa.filter.d.ts +1 -1
- package/dist/core/spa/spa.filter.js +11 -16
- package/dist/core/spa/spa.filter.js.map +1 -1
- package/dist/core/ssl/ssl-cert-generator.service.d.ts +1 -1
- package/dist/core/ssl/ssl-cert-generator.service.js +34 -36
- package/dist/core/ssl/ssl-cert-generator.service.js.map +1 -1
- package/dist/env-setup.d.ts +1 -0
- package/dist/env-setup.js +7 -0
- package/dist/env-setup.js.map +1 -0
- package/dist/globalDefaults.d.ts +1 -0
- package/dist/globalDefaults.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -19
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +5 -3
- package/dist/main.js +44 -51
- package/dist/main.js.map +1 -1
- package/dist/modules/accessories/accessories.controller.d.ts +2 -2
- package/dist/modules/accessories/accessories.controller.js +27 -29
- package/dist/modules/accessories/accessories.controller.js.map +1 -1
- package/dist/modules/accessories/accessories.dto.js +11 -13
- package/dist/modules/accessories/accessories.dto.js.map +1 -1
- package/dist/modules/accessories/accessories.gateway.d.ts +1 -1
- package/dist/modules/accessories/accessories.gateway.js +18 -16
- package/dist/modules/accessories/accessories.gateway.js.map +1 -1
- package/dist/modules/accessories/accessories.module.js +17 -20
- package/dist/modules/accessories/accessories.module.js.map +1 -1
- package/dist/modules/accessories/accessories.service.d.ts +2 -2
- package/dist/modules/accessories/accessories.service.js +37 -35
- package/dist/modules/accessories/accessories.service.js.map +1 -1
- package/dist/modules/backup/backup.controller.d.ts +2 -2
- package/dist/modules/backup/backup.controller.js +68 -67
- package/dist/modules/backup/backup.controller.js.map +1 -1
- package/dist/modules/backup/backup.gateway.d.ts +2 -2
- package/dist/modules/backup/backup.gateway.js +25 -21
- package/dist/modules/backup/backup.gateway.js.map +1 -1
- package/dist/modules/backup/backup.module.js +22 -25
- package/dist/modules/backup/backup.module.js.map +1 -1
- package/dist/modules/backup/backup.service.d.ts +5 -5
- package/dist/modules/backup/backup.service.js +143 -133
- package/dist/modules/backup/backup.service.js.map +1 -1
- package/dist/modules/child-bridges/child-bridges.gateway.d.ts +2 -2
- package/dist/modules/child-bridges/child-bridges.gateway.js +23 -21
- package/dist/modules/child-bridges/child-bridges.gateway.js.map +1 -1
- package/dist/modules/child-bridges/child-bridges.interfaces.js +1 -2
- package/dist/modules/child-bridges/child-bridges.module.js +19 -22
- package/dist/modules/child-bridges/child-bridges.module.js.map +1 -1
- package/dist/modules/child-bridges/child-bridges.service.d.ts +3 -3
- package/dist/modules/child-bridges/child-bridges.service.js +15 -11
- package/dist/modules/child-bridges/child-bridges.service.js.map +1 -1
- package/dist/modules/config-editor/config-editor.controller.d.ts +6 -6
- package/dist/modules/config-editor/config-editor.controller.js +94 -95
- package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
- package/dist/modules/config-editor/config-editor.module.js +21 -24
- package/dist/modules/config-editor/config-editor.module.js.map +1 -1
- package/dist/modules/config-editor/config-editor.service.d.ts +6 -6
- package/dist/modules/config-editor/config-editor.service.js +65 -57
- package/dist/modules/config-editor/config-editor.service.js.map +1 -1
- package/dist/modules/custom-plugins/custom-plugins.module.js +10 -13
- package/dist/modules/custom-plugins/custom-plugins.module.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.controller.d.ts +1 -1
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.controller.js +21 -19
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.controller.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.module.js +14 -17
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.module.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.service.d.ts +1 -1
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.service.js +18 -15
- package/dist/modules/custom-plugins/homebridge-deconz/homebridge-deconz.service.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.controller.d.ts +1 -1
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.controller.js +21 -19
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.controller.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.module.js +14 -17
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.module.js.map +1 -1
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.service.d.ts +1 -1
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.service.js +18 -15
- package/dist/modules/custom-plugins/homebridge-hue/homebridge-hue.service.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.controller.d.ts +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.controller.js +18 -19
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.controller.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.gateway.d.ts +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.gateway.js +15 -13
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.gateway.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.module.js +18 -21
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.module.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.d.ts +4 -4
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +47 -41
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
- package/dist/modules/log/log.gateway.d.ts +2 -2
- package/dist/modules/log/log.gateway.js +15 -13
- package/dist/modules/log/log.gateway.js.map +1 -1
- package/dist/modules/log/log.interfaces.js +1 -2
- package/dist/modules/log/log.module.js +14 -17
- package/dist/modules/log/log.module.js.map +1 -1
- package/dist/modules/log/log.service.d.ts +3 -3
- package/dist/modules/log/log.service.js +47 -43
- package/dist/modules/log/log.service.js.map +1 -1
- package/dist/modules/platform-tools/docker/docker.controller.d.ts +1 -1
- package/dist/modules/platform-tools/docker/docker.controller.js +24 -25
- package/dist/modules/platform-tools/docker/docker.controller.js.map +1 -1
- package/dist/modules/platform-tools/docker/docker.module.js +14 -17
- package/dist/modules/platform-tools/docker/docker.module.js.map +1 -1
- package/dist/modules/platform-tools/docker/docker.service.d.ts +2 -2
- package/dist/modules/platform-tools/docker/docker.service.js +20 -16
- package/dist/modules/platform-tools/docker/docker.service.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.controller.d.ts +2 -2
- package/dist/modules/platform-tools/hb-service/hb-service.controller.js +35 -36
- package/dist/modules/platform-tools/hb-service/hb-service.controller.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.dto.js +20 -19
- package/dist/modules/platform-tools/hb-service/hb-service.dto.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.module.js +14 -17
- package/dist/modules/platform-tools/hb-service/hb-service.module.js.map +1 -1
- package/dist/modules/platform-tools/hb-service/hb-service.service.d.ts +3 -3
- package/dist/modules/platform-tools/hb-service/hb-service.service.js +37 -30
- package/dist/modules/platform-tools/hb-service/hb-service.service.js.map +1 -1
- package/dist/modules/platform-tools/linux/linux.controller.d.ts +1 -1
- package/dist/modules/platform-tools/linux/linux.controller.js +23 -21
- package/dist/modules/platform-tools/linux/linux.controller.js.map +1 -1
- package/dist/modules/platform-tools/linux/linux.module.js +14 -17
- package/dist/modules/platform-tools/linux/linux.module.js.map +1 -1
- package/dist/modules/platform-tools/linux/linux.service.d.ts +2 -2
- package/dist/modules/platform-tools/linux/linux.service.js +18 -14
- package/dist/modules/platform-tools/linux/linux.service.js.map +1 -1
- package/dist/modules/platform-tools/platform-tools.module.js +12 -15
- package/dist/modules/platform-tools/platform-tools.module.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.controller.d.ts +1 -1
- package/dist/modules/platform-tools/terminal/terminal.controller.js +15 -13
- package/dist/modules/platform-tools/terminal/terminal.controller.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.gateway.d.ts +2 -2
- package/dist/modules/platform-tools/terminal/terminal.gateway.js +17 -15
- package/dist/modules/platform-tools/terminal/terminal.gateway.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.interfaces.js +1 -2
- package/dist/modules/platform-tools/terminal/terminal.module.js +18 -21
- package/dist/modules/platform-tools/terminal/terminal.module.js.map +1 -1
- package/dist/modules/platform-tools/terminal/terminal.service.d.ts +4 -4
- package/dist/modules/platform-tools/terminal/terminal.service.js +33 -28
- package/dist/modules/platform-tools/terminal/terminal.service.js.map +1 -1
- package/dist/modules/plugins/plugins.controller.d.ts +6 -6
- package/dist/modules/plugins/plugins.controller.js +51 -52
- package/dist/modules/plugins/plugins.controller.js.map +1 -1
- package/dist/modules/plugins/plugins.dto.js +26 -24
- package/dist/modules/plugins/plugins.dto.js.map +1 -1
- package/dist/modules/plugins/plugins.gateway.d.ts +3 -3
- package/dist/modules/plugins/plugins.gateway.js +38 -34
- package/dist/modules/plugins/plugins.gateway.js.map +1 -1
- package/dist/modules/plugins/plugins.interfaces.js +1 -2
- package/dist/modules/plugins/plugins.module.js +23 -26
- package/dist/modules/plugins/plugins.module.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +9 -7
- package/dist/modules/plugins/plugins.service.js +231 -202
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/server/server.controller.d.ts +34 -4
- package/dist/modules/server/server.controller.js +294 -177
- package/dist/modules/server/server.controller.js.map +1 -1
- package/dist/modules/server/server.dto.js +13 -16
- package/dist/modules/server/server.dto.js.map +1 -1
- package/dist/modules/server/server.module.js +23 -26
- package/dist/modules/server/server.module.js.map +1 -1
- package/dist/modules/server/server.service.d.ts +36 -6
- package/dist/modules/server/server.service.js +406 -150
- package/dist/modules/server/server.service.js.map +1 -1
- package/dist/modules/setup-wizard/setup-wizard.controller.d.ts +2 -2
- package/dist/modules/setup-wizard/setup-wizard.controller.js +19 -20
- package/dist/modules/setup-wizard/setup-wizard.controller.js.map +1 -1
- package/dist/modules/setup-wizard/setup-wizard.gateway.d.ts +2 -2
- package/dist/modules/setup-wizard/setup-wizard.gateway.js +25 -21
- package/dist/modules/setup-wizard/setup-wizard.gateway.js.map +1 -1
- package/dist/modules/setup-wizard/setup-wizard.guard.d.ts +1 -1
- package/dist/modules/setup-wizard/setup-wizard.guard.js +11 -9
- package/dist/modules/setup-wizard/setup-wizard.guard.js.map +1 -1
- package/dist/modules/setup-wizard/setup-wizard.module.js +12 -15
- package/dist/modules/setup-wizard/setup-wizard.module.js.map +1 -1
- package/dist/modules/status/status.controller.d.ts +5 -5
- package/dist/modules/status/status.controller.js +40 -36
- package/dist/modules/status/status.controller.js.map +1 -1
- package/dist/modules/status/status.gateway.d.ts +9 -9
- package/dist/modules/status/status.gateway.js +51 -47
- package/dist/modules/status/status.gateway.js.map +1 -1
- package/dist/modules/status/status.interfaces.js +2 -5
- package/dist/modules/status/status.interfaces.js.map +1 -1
- package/dist/modules/status/status.module.js +26 -29
- package/dist/modules/status/status.module.js.map +1 -1
- package/dist/modules/status/status.service.d.ts +7 -7
- package/dist/modules/status/status.service.js +100 -88
- package/dist/modules/status/status.service.js.map +1 -1
- package/dist/modules/users/users.controller.d.ts +2 -2
- package/dist/modules/users/users.controller.js +59 -60
- package/dist/modules/users/users.controller.js.map +1 -1
- package/dist/modules/users/users.dto.js +51 -45
- package/dist/modules/users/users.dto.js.map +1 -1
- package/dist/modules/users/users.module.js +14 -17
- package/dist/modules/users/users.module.js.map +1 -1
- package/dist/self-check.js +25 -25
- package/dist/self-check.js.map +1 -1
- package/docs/ssl-upload-pr.md +103 -0
- package/package.json +6 -7
- package/public/{chunk-PPA66JI2.js → chunk-2FNQEOXF.js} +1 -1
- package/public/{chunk-HERKNFLL.js → chunk-2SCTALGY.js} +1 -1
- package/public/{chunk-BQTGMLQB.js → chunk-3IJXQMYZ.js} +1 -1
- package/public/chunk-425ZEUU6.js +1 -0
- package/public/{chunk-EHHNHG6Q.js → chunk-4T6CM7M3.js} +1 -1
- package/public/{chunk-IKY3F7MM.js → chunk-7SKTJO5M.js} +1 -1
- package/public/{chunk-E4ZKIFZH.js → chunk-7UDDRMZZ.js} +1 -1
- package/public/{chunk-Y2N6WV2O.js → chunk-ATMY4YI2.js} +1 -1
- package/public/{chunk-UKEKNQEJ.js → chunk-AZIRJR4B.js} +1 -1
- package/public/{chunk-2UQL2OTF.js → chunk-B2XE365J.js} +1 -1
- package/public/{chunk-JWHZQ7YI.js → chunk-BMKJTHFC.js} +1 -1
- package/public/{chunk-R2PMDKTD.js → chunk-CGYD722J.js} +1 -1
- package/public/{chunk-BI2TS6NN.js → chunk-CQREAKPC.js} +4 -4
- package/public/{chunk-WMGV3KS6.js → chunk-CYBDQV2B.js} +1 -1
- package/public/{chunk-TJEQFEML.js → chunk-D5RKKI2A.js} +1 -1
- package/public/{chunk-NUCXRR5D.js → chunk-EAGKQ5OJ.js} +1 -1
- package/public/{chunk-EYO3XCE2.js → chunk-FGNMMSZH.js} +1 -1
- package/public/{chunk-FGS4HIPD.js → chunk-FTKWC2Q2.js} +1 -1
- package/public/{chunk-QU4I4QSX.js → chunk-HFW5E2OW.js} +1 -1
- package/public/chunk-IIDLQ7XY.js +1 -0
- package/public/{chunk-4E4YMHG4.js → chunk-INX52LH2.js} +1 -1
- package/public/{chunk-VFIOPF3O.js → chunk-JD5JQTN5.js} +1 -1
- package/public/{chunk-RLWY3GD4.js → chunk-JFRCT5V7.js} +1 -1
- package/public/{chunk-JWCVCB6V.js → chunk-JJ7TVAIH.js} +4 -4
- package/public/{chunk-JWY7RFE6.js → chunk-JPCV77W6.js} +1 -1
- package/public/{chunk-ISV4M72Q.js → chunk-K7D2P3SM.js} +1 -1
- package/public/chunk-L66ATRVT.js +1 -0
- package/public/{chunk-IXIZVUXX.js → chunk-LARQLRNL.js} +1 -1
- package/public/{chunk-BVTPAZF3.js → chunk-LHCMU3MO.js} +1 -1
- package/public/{chunk-5T6O4JCR.js → chunk-LZQVKA4S.js} +1 -1
- package/public/{chunk-7F6JT34E.js → chunk-M7QUT5ZZ.js} +1 -1
- package/public/{chunk-2MFBHUTD.js → chunk-MDBZMB6Y.js} +1 -1
- package/public/{chunk-HVGUNGBB.js → chunk-MNVUSYOZ.js} +1 -1
- package/public/{chunk-ZC3JEQZ5.js → chunk-N2TWGDNX.js} +1 -1
- package/public/{chunk-XS5JK2LY.js → chunk-NKNWXFAK.js} +1 -1
- package/public/{chunk-SOA3BB36.js → chunk-OS2SEJZU.js} +1 -1
- package/public/{chunk-IEO2AG2N.js → chunk-PXSEH255.js} +1 -1
- package/public/{chunk-QJLKBB62.js → chunk-PZ2ZCCFR.js} +1 -1
- package/public/{chunk-CWTDHTPI.js → chunk-QCZ4VVDL.js} +1 -1
- package/public/{chunk-5W7EKO7F.js → chunk-QSVZNI3M.js} +1 -1
- package/public/{chunk-4H7ODI34.js → chunk-QUA46GZJ.js} +1 -1
- package/public/{chunk-D4LRW76J.js → chunk-QVLCTKQH.js} +1 -1
- package/public/{chunk-DL2QDFLD.js → chunk-RQ4B2IIV.js} +1 -1
- package/public/{chunk-OTOQR5Z2.js → chunk-RQAFAZAK.js} +1 -1
- package/public/{chunk-4AWJ2PWG.js → chunk-S4OE2ITZ.js} +1 -1
- package/public/{chunk-ZF6W5Z5R.js → chunk-SA5C7QVC.js} +1 -1
- package/public/{chunk-USXMCNIE.js → chunk-SSZBNSOM.js} +1 -1
- package/public/{chunk-E7S36RMO.js → chunk-TA6DOQC5.js} +1 -1
- package/public/{chunk-VB4UAZWJ.js → chunk-TC77PFBQ.js} +1 -1
- package/public/{chunk-5WTTTYNS.js → chunk-TKGKVKOW.js} +1 -1
- package/public/{chunk-FOSFENMV.js → chunk-TSFGO3MA.js} +1 -1
- package/public/{chunk-IBO6BDNZ.js → chunk-UEDMHOPK.js} +1 -1
- package/public/{chunk-LI4BZAUI.js → chunk-UOPWP4RR.js} +1 -1
- package/public/chunk-UTFHCUKB.js +1 -0
- package/public/{chunk-SXOLKD36.js → chunk-UTN2R7WG.js} +1 -1
- package/public/chunk-UV24H47X.js +1 -0
- package/public/{chunk-B5Z4M7ZJ.js → chunk-VSBBKD6B.js} +1 -1
- package/public/{chunk-AO7VT6PL.js → chunk-VYC5JLE6.js} +1 -1
- package/public/{chunk-AOALMLGY.js → chunk-W7UNXMCL.js} +1 -1
- package/public/{chunk-NIERW5KT.js → chunk-WVCOONJF.js} +1 -1
- package/public/{chunk-RON3LUMX.js → chunk-WWSLIQVD.js} +1 -1
- package/public/{chunk-4DVF333O.js → chunk-X3IVSHIL.js} +1 -1
- package/public/{chunk-5G7T74GR.js → chunk-XE5VJDQL.js} +1 -1
- package/public/chunk-XMNXI4QP.js +50 -0
- package/public/{chunk-W26VKO62.js → chunk-XX5GWADI.js} +1 -1
- package/public/{chunk-TGX55EFG.js → chunk-Y5XBENWZ.js} +1 -1
- package/public/{chunk-Z6VQEXUS.js → chunk-YNONXMOG.js} +1 -1
- package/public/{chunk-DIWD6B4C.js → chunk-ZEI3HZ6P.js} +1 -1
- package/public/index.html +2 -2
- package/public/{main-QP5R7HI3.js → main-UGYHSCMY.js} +1 -1
- package/public/{styles-CSF457UW.css → styles-WNHDEKE4.css} +1 -1
- package/scripts/extract-plugin-alias.js +8 -4
- package/public/chunk-2ADAW5AD.js +0 -1
- package/public/chunk-2Y63WBU6.js +0 -1
- package/public/chunk-I3QQTGGZ.js +0 -1
- package/public/chunk-LGE6SGDN.js +0 -1
- package/public/chunk-PZD3OMMY.js +0 -1
- package/public/chunk-QOSAHW73.js +0 -50
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -8,70 +7,80 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
9
|
};
|
|
11
|
-
var
|
|
12
|
-
return (
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
12
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
import { Buffer } from 'node:buffer';
|
|
14
|
+
import { exec, spawn } from 'node:child_process';
|
|
15
|
+
import { createPrivateKey, createPublicKey, X509Certificate } from 'node:crypto';
|
|
16
|
+
import { createWriteStream } from 'node:fs';
|
|
17
|
+
import { readdir, unlink } from 'node:fs/promises';
|
|
18
|
+
import { extname, join, resolve } from 'node:path';
|
|
19
|
+
import process from 'node:process';
|
|
20
|
+
import { pipeline } from 'node:stream';
|
|
21
|
+
import { createSecureContext } from 'node:tls';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
import { Categories } from '@homebridge/hap-client/dist/hap-types.js';
|
|
24
|
+
import { BadRequestException, Inject, Injectable, InternalServerErrorException, NotFoundException, ServiceUnavailableException, } from '@nestjs/common';
|
|
25
|
+
import { pathExists, readJson, remove, writeJson } from 'fs-extra/esm';
|
|
26
|
+
import NodeCache from 'node-cache';
|
|
27
|
+
import { networkInterfaces } from 'systeminformation';
|
|
28
|
+
import { check as tcpCheck } from 'tcp-port-used';
|
|
29
|
+
import { ConfigService } from '../../core/config/config.service.js';
|
|
30
|
+
import { HomebridgeIpcService } from '../../core/homebridge-ipc/homebridge-ipc.service.js';
|
|
31
|
+
import { Logger } from '../../core/logger/logger.service.js';
|
|
32
|
+
import { SslCertGeneratorService } from '../../core/ssl/ssl-cert-generator.service.js';
|
|
33
|
+
import { AccessoriesService } from '../accessories/accessories.service.js';
|
|
34
|
+
import { ConfigEditorService } from '../config-editor/config-editor.service.js';
|
|
35
|
+
const pump = promisify(pipeline);
|
|
34
36
|
let ServerService = class ServerService {
|
|
37
|
+
configService;
|
|
38
|
+
configEditorService;
|
|
39
|
+
accessoriesService;
|
|
40
|
+
homebridgeIpcService;
|
|
41
|
+
logger;
|
|
42
|
+
serverServiceCache = new NodeCache({ stdTTL: 300 });
|
|
43
|
+
accessoryId;
|
|
44
|
+
accessoryInfoPath;
|
|
45
|
+
setupCode = null;
|
|
46
|
+
paired = false;
|
|
35
47
|
constructor(configService, configEditorService, accessoriesService, homebridgeIpcService, logger) {
|
|
36
48
|
this.configService = configService;
|
|
37
49
|
this.configEditorService = configEditorService;
|
|
38
50
|
this.accessoriesService = accessoriesService;
|
|
39
51
|
this.homebridgeIpcService = homebridgeIpcService;
|
|
40
52
|
this.logger = logger;
|
|
41
|
-
this.serverServiceCache = new node_cache_1.default({ stdTTL: 300 });
|
|
42
|
-
this.setupCode = null;
|
|
43
|
-
this.paired = false;
|
|
44
53
|
this.accessoryId = this.configService.homebridgeConfig.bridge.username.split(':').join('');
|
|
45
|
-
this.accessoryInfoPath =
|
|
54
|
+
this.accessoryInfoPath = join(this.configService.storagePath, 'persist', `AccessoryInfo.${this.accessoryId}.json`);
|
|
46
55
|
}
|
|
47
56
|
async deleteSingleDeviceAccessories(id, cachedAccessoriesDir, protocol = 'both') {
|
|
48
57
|
if (protocol === 'hap' || protocol === 'both') {
|
|
49
|
-
const cachedAccessories =
|
|
50
|
-
const cachedAccessoriesBackup =
|
|
51
|
-
if (await
|
|
52
|
-
await
|
|
58
|
+
const cachedAccessories = join(cachedAccessoriesDir, `cachedAccessories.${id}`);
|
|
59
|
+
const cachedAccessoriesBackup = join(cachedAccessoriesDir, `.cachedAccessories.${id}.bak`);
|
|
60
|
+
if (await pathExists(cachedAccessories)) {
|
|
61
|
+
await unlink(cachedAccessories);
|
|
53
62
|
this.logger.warn(`Bridge ${id} HAP accessory removal: removed ${cachedAccessories}.`);
|
|
54
63
|
}
|
|
55
|
-
if (await
|
|
56
|
-
await
|
|
64
|
+
if (await pathExists(cachedAccessoriesBackup)) {
|
|
65
|
+
await unlink(cachedAccessoriesBackup);
|
|
57
66
|
this.logger.warn(`Bridge ${id} HAP accessory removal: removed ${cachedAccessoriesBackup}.`);
|
|
58
67
|
}
|
|
59
68
|
}
|
|
60
69
|
if (protocol === 'matter' || protocol === 'both') {
|
|
61
70
|
const deviceId = id.split(':').join('').toUpperCase();
|
|
62
|
-
const matterPath =
|
|
63
|
-
if (await
|
|
64
|
-
await
|
|
71
|
+
const matterPath = join(this.configService.storagePath, 'matter', deviceId);
|
|
72
|
+
if (await pathExists(matterPath)) {
|
|
73
|
+
await remove(matterPath);
|
|
65
74
|
this.logger.warn(`Bridge ${id} Matter accessory removal: removed Matter bridge storage at ${matterPath}.`);
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
77
|
}
|
|
69
78
|
async deleteSingleDevicePairing(id, resetPairingInfo) {
|
|
70
|
-
const persistPath =
|
|
71
|
-
const accessoryInfo =
|
|
72
|
-
const identifierCache =
|
|
79
|
+
const persistPath = join(this.configService.storagePath, 'persist');
|
|
80
|
+
const accessoryInfo = join(persistPath, `AccessoryInfo.${id}.json`);
|
|
81
|
+
const identifierCache = join(persistPath, `IdentifierCache.${id}.json`);
|
|
73
82
|
const deviceId = id.includes(':') ? id.split(':').join('').toUpperCase() : id.toUpperCase();
|
|
74
|
-
const matterPath =
|
|
83
|
+
const matterPath = join(this.configService.storagePath, 'matter', deviceId);
|
|
75
84
|
try {
|
|
76
85
|
const configFile = await this.configEditorService.getConfigFile();
|
|
77
86
|
const username = id.includes(':') ? id.toUpperCase() : id.match(/.{1,2}/g)?.join(':').toUpperCase() || id.toUpperCase();
|
|
@@ -116,16 +125,16 @@ let ServerService = class ServerService {
|
|
|
116
125
|
catch (e) {
|
|
117
126
|
this.logger.error(`Failed to reset username and pin for child bridge ${id} as ${e.message}.`);
|
|
118
127
|
}
|
|
119
|
-
if (await
|
|
120
|
-
await
|
|
128
|
+
if (await pathExists(accessoryInfo)) {
|
|
129
|
+
await unlink(accessoryInfo);
|
|
121
130
|
this.logger.warn(`Bridge ${id} reset: removed ${accessoryInfo}.`);
|
|
122
131
|
}
|
|
123
|
-
if (await
|
|
124
|
-
await
|
|
132
|
+
if (await pathExists(identifierCache)) {
|
|
133
|
+
await unlink(identifierCache);
|
|
125
134
|
this.logger.warn(`Bridge ${id} reset: removed ${identifierCache}.`);
|
|
126
135
|
}
|
|
127
|
-
if (await
|
|
128
|
-
await
|
|
136
|
+
if (await pathExists(matterPath)) {
|
|
137
|
+
await remove(matterPath);
|
|
129
138
|
this.logger.warn(`Bridge ${id} reset: removed Matter bridge storage at ${matterPath}.`);
|
|
130
139
|
}
|
|
131
140
|
await this.deleteDeviceAccessories(id);
|
|
@@ -141,7 +150,7 @@ let ServerService = class ServerService {
|
|
|
141
150
|
setTimeout(() => {
|
|
142
151
|
if (this.configService.ui.restart) {
|
|
143
152
|
this.logger.log(`Executing restart command ${this.configService.ui.restart}.`);
|
|
144
|
-
|
|
153
|
+
exec(this.configService.ui.restart, (err) => {
|
|
145
154
|
if (err) {
|
|
146
155
|
this.logger.log('Restart command exited with an error, failed to restart Homebridge.');
|
|
147
156
|
}
|
|
@@ -149,7 +158,7 @@ let ServerService = class ServerService {
|
|
|
149
158
|
}
|
|
150
159
|
else {
|
|
151
160
|
this.logger.log('Sending SIGTERM to process...');
|
|
152
|
-
|
|
161
|
+
process.kill(process.pid, 'SIGTERM');
|
|
153
162
|
}
|
|
154
163
|
}, 500);
|
|
155
164
|
return { ok: true, command: this.configService.ui.restart, restartingUI: true };
|
|
@@ -169,33 +178,33 @@ let ServerService = class ServerService {
|
|
|
169
178
|
}
|
|
170
179
|
this.logger.warn(`Homebridge bridge reset: new username ${configFile.bridge.username} and new pin ${configFile.bridge.pin}.`);
|
|
171
180
|
await this.configEditorService.updateConfigFile(configFile);
|
|
172
|
-
await
|
|
173
|
-
await
|
|
181
|
+
await remove(resolve(this.configService.storagePath, 'accessories'));
|
|
182
|
+
await remove(resolve(this.configService.storagePath, 'persist'));
|
|
174
183
|
const deviceId = oldUsername.split(':').join('').toUpperCase();
|
|
175
|
-
const matterPath =
|
|
176
|
-
if (await
|
|
177
|
-
await
|
|
184
|
+
const matterPath = join(this.configService.storagePath, 'matter', deviceId);
|
|
185
|
+
if (await pathExists(matterPath)) {
|
|
186
|
+
await remove(matterPath);
|
|
178
187
|
this.logger.warn(`Bridge ${oldUsername} reset: removed Matter bridge storage at ${matterPath}.`);
|
|
179
188
|
}
|
|
180
189
|
this.logger.log('Homebridge bridge reset: accessories and persist directories were removed.');
|
|
181
190
|
}
|
|
182
191
|
async getDevicePairings() {
|
|
183
|
-
const persistPath =
|
|
184
|
-
const devices = (await
|
|
192
|
+
const persistPath = join(this.configService.storagePath, 'persist');
|
|
193
|
+
const devices = (await readdir(persistPath))
|
|
185
194
|
.filter(x => x.match(/AccessoryInfo\.([A-Fa-f0-9]+)\.json$/));
|
|
186
195
|
const configFile = await this.configEditorService.getConfigFile();
|
|
187
196
|
const hapDevices = await Promise.all(devices.map(async (x) => {
|
|
188
197
|
return await this.getDevicePairingById(x.split('.')[1], configFile);
|
|
189
198
|
}));
|
|
190
|
-
const matterExternalDevices = await this.getMatterExternalAccessories(
|
|
199
|
+
const matterExternalDevices = await this.getMatterExternalAccessories(hapDevices);
|
|
191
200
|
return [...hapDevices, ...matterExternalDevices].sort((a, b) => a.name.localeCompare(b.name));
|
|
192
201
|
}
|
|
193
|
-
async getMatterExternalAccessories(
|
|
194
|
-
const matterPath =
|
|
195
|
-
if (!await
|
|
202
|
+
async getMatterExternalAccessories(hapDevices) {
|
|
203
|
+
const matterPath = join(this.configService.storagePath, 'matter');
|
|
204
|
+
if (!await pathExists(matterPath)) {
|
|
196
205
|
return [];
|
|
197
206
|
}
|
|
198
|
-
const matterDirs = (await
|
|
207
|
+
const matterDirs = (await readdir(matterPath))
|
|
199
208
|
.filter(x => x.match(/^[A-F0-9]{12}$/));
|
|
200
209
|
const matterExternalDevices = [];
|
|
201
210
|
for (const deviceId of matterDirs) {
|
|
@@ -208,19 +217,19 @@ let ServerService = class ServerService {
|
|
|
208
217
|
if (deviceId.toUpperCase() === mainBridgeId) {
|
|
209
218
|
continue;
|
|
210
219
|
}
|
|
211
|
-
const accessoriesPath =
|
|
212
|
-
if (!await
|
|
220
|
+
const accessoriesPath = join(matterPath, deviceId, 'accessories.json');
|
|
221
|
+
if (!await pathExists(accessoriesPath)) {
|
|
213
222
|
continue;
|
|
214
223
|
}
|
|
215
|
-
const accessories = await
|
|
224
|
+
const accessories = await readJson(accessoriesPath);
|
|
216
225
|
if (!Array.isArray(accessories) || accessories.length === 0) {
|
|
217
226
|
continue;
|
|
218
227
|
}
|
|
219
228
|
const accessory = accessories[0];
|
|
220
|
-
const commissioningPath =
|
|
229
|
+
const commissioningPath = join(matterPath, deviceId, 'commissioning.json');
|
|
221
230
|
let commissioned = false;
|
|
222
|
-
if (await
|
|
223
|
-
const commissioningInfo = await
|
|
231
|
+
if (await pathExists(commissioningPath)) {
|
|
232
|
+
const commissioningInfo = await readJson(commissioningPath);
|
|
224
233
|
commissioned = commissioningInfo.commissioned || false;
|
|
225
234
|
}
|
|
226
235
|
const device = {
|
|
@@ -248,13 +257,13 @@ let ServerService = class ServerService {
|
|
|
248
257
|
return matterExternalDevices;
|
|
249
258
|
}
|
|
250
259
|
async getDevicePairingById(deviceId, configFile = null) {
|
|
251
|
-
const persistPath =
|
|
260
|
+
const persistPath = join(this.configService.storagePath, 'persist');
|
|
252
261
|
let device;
|
|
253
262
|
try {
|
|
254
|
-
device = await
|
|
263
|
+
device = await readJson(join(persistPath, `AccessoryInfo.${deviceId}.json`));
|
|
255
264
|
}
|
|
256
265
|
catch (e) {
|
|
257
|
-
throw new
|
|
266
|
+
throw new NotFoundException();
|
|
258
267
|
}
|
|
259
268
|
if (!configFile) {
|
|
260
269
|
configFile = await this.configEditorService.getConfigFile();
|
|
@@ -266,7 +275,7 @@ let ServerService = class ServerService {
|
|
|
266
275
|
.concat([{ _bridge: configFile.bridge }])
|
|
267
276
|
.find((block) => block._bridge?.username?.toUpperCase() === username.toUpperCase());
|
|
268
277
|
try {
|
|
269
|
-
device._category = Object.entries(
|
|
278
|
+
device._category = Object.entries(Categories).find(([, value]) => value === device.category)[0].toLowerCase();
|
|
270
279
|
}
|
|
271
280
|
catch (e) {
|
|
272
281
|
device._category = 'Other';
|
|
@@ -307,7 +316,7 @@ let ServerService = class ServerService {
|
|
|
307
316
|
const pluginBlock = pluginBlocks.find((block) => block._bridge?.matter);
|
|
308
317
|
if (!pluginBlock) {
|
|
309
318
|
this.logger.error(`Failed to find Matter configuration for child bridge ${id}.`);
|
|
310
|
-
throw new
|
|
319
|
+
throw new NotFoundException(`Matter configuration not found for bridge ${id}`);
|
|
311
320
|
}
|
|
312
321
|
if ('accessory' in pluginBlock) {
|
|
313
322
|
this.logger.warn(`Removing Matter configuration from accessory-based plugin block for bridge ${id}. Matter is only supported for platform-based plugins.`);
|
|
@@ -317,18 +326,18 @@ let ServerService = class ServerService {
|
|
|
317
326
|
await this.configEditorService.updateConfigFile(configFile);
|
|
318
327
|
}
|
|
319
328
|
catch (e) {
|
|
320
|
-
if (e instanceof
|
|
329
|
+
if (e instanceof NotFoundException) {
|
|
321
330
|
throw e;
|
|
322
331
|
}
|
|
323
332
|
this.logger.error(`Failed to remove Matter configuration for child bridge ${id} as ${e.message}.`);
|
|
324
|
-
throw new
|
|
333
|
+
throw new InternalServerErrorException(`Failed to remove Matter configuration: ${e.message}`);
|
|
325
334
|
}
|
|
326
335
|
this.logger.warn(`Shutting down Homebridge before removing Matter storage for bridge ${id}...`);
|
|
327
336
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
328
337
|
const deviceId = id.includes(':') ? id.split(':').join('').toUpperCase() : id.toUpperCase();
|
|
329
|
-
const matterPath =
|
|
330
|
-
if (await
|
|
331
|
-
await
|
|
338
|
+
const matterPath = join(this.configService.storagePath, 'matter', deviceId);
|
|
339
|
+
if (await pathExists(matterPath)) {
|
|
340
|
+
await remove(matterPath);
|
|
332
341
|
this.logger.warn(`Bridge ${id} Matter storage removed at ${matterPath}.`);
|
|
333
342
|
}
|
|
334
343
|
return { ok: true };
|
|
@@ -349,13 +358,13 @@ let ServerService = class ServerService {
|
|
|
349
358
|
async deleteDeviceAccessories(id) {
|
|
350
359
|
this.logger.warn(`Shutting down Homebridge before removing accessories for paired bridge ${id}...`);
|
|
351
360
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
352
|
-
const cachedAccessoriesDir =
|
|
361
|
+
const cachedAccessoriesDir = join(this.configService.storagePath, 'accessories');
|
|
353
362
|
await this.deleteSingleDeviceAccessories(id, cachedAccessoriesDir);
|
|
354
363
|
}
|
|
355
364
|
async deleteDevicesAccessories(bridges) {
|
|
356
365
|
this.logger.warn(`Shutting down Homebridge before removing accessories for paired bridges ${bridges.map(x => x.id).join(', ')}...`);
|
|
357
366
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
358
|
-
const cachedAccessoriesDir =
|
|
367
|
+
const cachedAccessoriesDir = join(this.configService.storagePath, 'accessories');
|
|
359
368
|
for (const { id, protocol } of bridges) {
|
|
360
369
|
try {
|
|
361
370
|
await this.deleteSingleDeviceAccessories(id, cachedAccessoriesDir, protocol || 'both');
|
|
@@ -366,12 +375,12 @@ let ServerService = class ServerService {
|
|
|
366
375
|
}
|
|
367
376
|
}
|
|
368
377
|
async getCachedAccessories() {
|
|
369
|
-
const cachedAccessoriesDir =
|
|
370
|
-
const cachedAccessoryFiles = (await
|
|
378
|
+
const cachedAccessoriesDir = join(this.configService.storagePath, 'accessories');
|
|
379
|
+
const cachedAccessoryFiles = (await readdir(cachedAccessoriesDir))
|
|
371
380
|
.filter(x => x.match(/^cachedAccessories\.([A-F,0-9]+)$/) || x === 'cachedAccessories');
|
|
372
381
|
const cachedAccessories = [];
|
|
373
382
|
await Promise.all(cachedAccessoryFiles.map(async (x) => {
|
|
374
|
-
const accessories = await
|
|
383
|
+
const accessories = await readJson(join(cachedAccessoriesDir, x));
|
|
375
384
|
for (const accessory of accessories) {
|
|
376
385
|
accessory.$cacheFile = x;
|
|
377
386
|
cachedAccessories.push(accessory);
|
|
@@ -381,19 +390,19 @@ let ServerService = class ServerService {
|
|
|
381
390
|
}
|
|
382
391
|
async deleteCachedAccessory(uuid, cacheFile) {
|
|
383
392
|
cacheFile = cacheFile || 'cachedAccessories';
|
|
384
|
-
const cachedAccessoriesPath =
|
|
393
|
+
const cachedAccessoriesPath = resolve(this.configService.storagePath, 'accessories', cacheFile);
|
|
385
394
|
this.logger.warn(`Shutting down Homebridge before removing cached accessory ${uuid}...`);
|
|
386
395
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
387
|
-
const cachedAccessories = await
|
|
396
|
+
const cachedAccessories = await readJson(cachedAccessoriesPath);
|
|
388
397
|
const accessoryIndex = cachedAccessories.findIndex(x => x.UUID === uuid);
|
|
389
398
|
if (accessoryIndex > -1) {
|
|
390
399
|
cachedAccessories.splice(accessoryIndex, 1);
|
|
391
|
-
await
|
|
400
|
+
await writeJson(cachedAccessoriesPath, cachedAccessories);
|
|
392
401
|
this.logger.warn(`Removed cached accessory with UUID ${uuid} from file ${cacheFile}.`);
|
|
393
402
|
}
|
|
394
403
|
else {
|
|
395
404
|
this.logger.error(`Cannot find cached accessory with UUID ${uuid} from file ${cacheFile}.`);
|
|
396
|
-
throw new
|
|
405
|
+
throw new NotFoundException();
|
|
397
406
|
}
|
|
398
407
|
return { ok: true };
|
|
399
408
|
}
|
|
@@ -409,8 +418,8 @@ let ServerService = class ServerService {
|
|
|
409
418
|
accessoriesByCacheFile.get(accessoryCacheFile).push({ uuid });
|
|
410
419
|
}
|
|
411
420
|
for (const [cacheFile, accessories] of accessoriesByCacheFile.entries()) {
|
|
412
|
-
const cachedAccessoriesPath =
|
|
413
|
-
const cachedAccessories = await
|
|
421
|
+
const cachedAccessoriesPath = resolve(this.configService.storagePath, 'accessories', cacheFile);
|
|
422
|
+
const cachedAccessories = await readJson(cachedAccessoriesPath);
|
|
414
423
|
for (const { uuid } of accessories) {
|
|
415
424
|
try {
|
|
416
425
|
const accessoryIndex = cachedAccessories.findIndex(x => x.UUID === uuid);
|
|
@@ -426,53 +435,53 @@ let ServerService = class ServerService {
|
|
|
426
435
|
this.logger.error(`Failed to remove cached accessory with UUID ${uuid} from file ${cacheFile} as ${e.message}.`);
|
|
427
436
|
}
|
|
428
437
|
}
|
|
429
|
-
await
|
|
438
|
+
await writeJson(cachedAccessoriesPath, cachedAccessories);
|
|
430
439
|
}
|
|
431
440
|
return { ok: true };
|
|
432
441
|
}
|
|
433
442
|
async deleteAllCachedAccessories() {
|
|
434
|
-
const cachedAccessoriesDir =
|
|
435
|
-
const cachedAccessoryPaths = (await
|
|
443
|
+
const cachedAccessoriesDir = join(this.configService.storagePath, 'accessories');
|
|
444
|
+
const cachedAccessoryPaths = (await readdir(cachedAccessoriesDir))
|
|
436
445
|
.filter(x => x.match(/cachedAccessories\.([A-F,0-9]+)/) || x === 'cachedAccessories' || x === '.cachedAccessories.bak')
|
|
437
|
-
.map(x =>
|
|
438
|
-
const cachedAccessoriesPath =
|
|
446
|
+
.map(x => resolve(cachedAccessoriesDir, x));
|
|
447
|
+
const cachedAccessoriesPath = resolve(this.configService.storagePath, 'accessories', 'cachedAccessories');
|
|
439
448
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
440
449
|
this.logger.warn('Shutting down Homebridge before removing cached accessories');
|
|
441
450
|
try {
|
|
442
451
|
this.logger.log('Clearing all HAP cached accessories...');
|
|
443
452
|
for (const thisCachedAccessoriesPath of cachedAccessoryPaths) {
|
|
444
|
-
if (await
|
|
445
|
-
await
|
|
453
|
+
if (await pathExists(thisCachedAccessoriesPath)) {
|
|
454
|
+
await unlink(thisCachedAccessoriesPath);
|
|
446
455
|
this.logger.warn(`Removed ${thisCachedAccessoriesPath}.`);
|
|
447
456
|
}
|
|
448
457
|
}
|
|
449
|
-
const matterDir =
|
|
450
|
-
if (await
|
|
458
|
+
const matterDir = join(this.configService.storagePath, 'matter');
|
|
459
|
+
if (await pathExists(matterDir)) {
|
|
451
460
|
this.logger.log('Clearing all Matter cached accessories...');
|
|
452
|
-
await
|
|
461
|
+
await remove(matterDir);
|
|
453
462
|
this.logger.warn(`Removed Matter storage directory at ${matterDir}.`);
|
|
454
463
|
}
|
|
455
464
|
}
|
|
456
465
|
catch (e) {
|
|
457
466
|
this.logger.error(`Failed to clear all cached accessories at ${cachedAccessoriesPath} as ${e.message}.`);
|
|
458
467
|
console.error(e);
|
|
459
|
-
throw new
|
|
468
|
+
throw new InternalServerErrorException('Failed to clear Homebridge accessory cache - see logs.');
|
|
460
469
|
}
|
|
461
470
|
return { ok: true };
|
|
462
471
|
}
|
|
463
472
|
async getMatterAccessories() {
|
|
464
|
-
const matterDir =
|
|
465
|
-
if (!await
|
|
473
|
+
const matterDir = join(this.configService.storagePath, 'matter');
|
|
474
|
+
if (!await pathExists(matterDir)) {
|
|
466
475
|
return [];
|
|
467
476
|
}
|
|
468
|
-
const matterBridges = (await
|
|
477
|
+
const matterBridges = (await readdir(matterDir))
|
|
469
478
|
.filter(x => x.match(/^[A-F0-9]+$/));
|
|
470
479
|
const matterAccessories = [];
|
|
471
480
|
await Promise.all(matterBridges.map(async (deviceId) => {
|
|
472
481
|
try {
|
|
473
|
-
const accessoriesPath =
|
|
474
|
-
if (await
|
|
475
|
-
const accessories = await
|
|
482
|
+
const accessoriesPath = join(matterDir, deviceId, 'accessories.json');
|
|
483
|
+
if (await pathExists(accessoriesPath)) {
|
|
484
|
+
const accessories = await readJson(accessoriesPath);
|
|
476
485
|
if (Array.isArray(accessories)) {
|
|
477
486
|
for (const accessory of accessories) {
|
|
478
487
|
accessory.$deviceId = deviceId;
|
|
@@ -489,23 +498,23 @@ let ServerService = class ServerService {
|
|
|
489
498
|
return matterAccessories;
|
|
490
499
|
}
|
|
491
500
|
async deleteMatterAccessory(deviceId, uuid) {
|
|
492
|
-
const matterAccessoriesPath =
|
|
493
|
-
if (!await
|
|
501
|
+
const matterAccessoriesPath = join(this.configService.storagePath, 'matter', deviceId, 'accessories.json');
|
|
502
|
+
if (!await pathExists(matterAccessoriesPath)) {
|
|
494
503
|
this.logger.error(`Matter accessories file not found for bridge ${deviceId}`);
|
|
495
|
-
throw new
|
|
504
|
+
throw new NotFoundException();
|
|
496
505
|
}
|
|
497
506
|
this.logger.warn(`Shutting down Homebridge before removing Matter accessory ${uuid} from bridge ${deviceId}...`);
|
|
498
507
|
await this.homebridgeIpcService.restartAndWaitForClose();
|
|
499
|
-
const matterAccessories = await
|
|
508
|
+
const matterAccessories = await readJson(matterAccessoriesPath);
|
|
500
509
|
const accessoryIndex = matterAccessories.findIndex(x => x.uuid === uuid);
|
|
501
510
|
if (accessoryIndex > -1) {
|
|
502
511
|
matterAccessories.splice(accessoryIndex, 1);
|
|
503
|
-
await
|
|
512
|
+
await writeJson(matterAccessoriesPath, matterAccessories, { spaces: 2 });
|
|
504
513
|
this.logger.warn(`Removed Matter accessory with UUID ${uuid} from bridge ${deviceId}.`);
|
|
505
514
|
}
|
|
506
515
|
else {
|
|
507
516
|
this.logger.error(`Cannot find Matter accessory with UUID ${uuid} in bridge ${deviceId}.`);
|
|
508
|
-
throw new
|
|
517
|
+
throw new NotFoundException();
|
|
509
518
|
}
|
|
510
519
|
return { ok: true };
|
|
511
520
|
}
|
|
@@ -520,13 +529,13 @@ let ServerService = class ServerService {
|
|
|
520
529
|
accessoriesByBridge.get(deviceId).push({ uuid });
|
|
521
530
|
}
|
|
522
531
|
for (const [deviceId, bridgeAccessories] of accessoriesByBridge.entries()) {
|
|
523
|
-
const matterAccessoriesPath =
|
|
532
|
+
const matterAccessoriesPath = join(this.configService.storagePath, 'matter', deviceId, 'accessories.json');
|
|
524
533
|
try {
|
|
525
|
-
if (!await
|
|
534
|
+
if (!await pathExists(matterAccessoriesPath)) {
|
|
526
535
|
this.logger.error(`Matter accessories file not found for bridge ${deviceId}`);
|
|
527
536
|
continue;
|
|
528
537
|
}
|
|
529
|
-
const matterAccessories = await
|
|
538
|
+
const matterAccessories = await readJson(matterAccessoriesPath);
|
|
530
539
|
for (const { uuid } of bridgeAccessories) {
|
|
531
540
|
try {
|
|
532
541
|
const accessoryIndex = matterAccessories.findIndex(x => x.uuid === uuid);
|
|
@@ -542,7 +551,7 @@ let ServerService = class ServerService {
|
|
|
542
551
|
this.logger.error(`Failed to remove Matter accessory with UUID ${uuid} from bridge ${deviceId} as ${e.message}.`);
|
|
543
552
|
}
|
|
544
553
|
}
|
|
545
|
-
await
|
|
554
|
+
await writeJson(matterAccessoriesPath, matterAccessories, { spaces: 2 });
|
|
546
555
|
}
|
|
547
556
|
catch (e) {
|
|
548
557
|
this.logger.error(`Failed to process Matter accessories for bridge ${deviceId} as ${e.message}.`);
|
|
@@ -555,16 +564,16 @@ let ServerService = class ServerService {
|
|
|
555
564
|
return this.setupCode;
|
|
556
565
|
}
|
|
557
566
|
else {
|
|
558
|
-
if (!await
|
|
567
|
+
if (!await pathExists(this.accessoryInfoPath)) {
|
|
559
568
|
return null;
|
|
560
569
|
}
|
|
561
|
-
const accessoryInfo = await
|
|
570
|
+
const accessoryInfo = await readJson(this.accessoryInfoPath);
|
|
562
571
|
this.setupCode = this.generateSetupCode(accessoryInfo);
|
|
563
572
|
return this.setupCode;
|
|
564
573
|
}
|
|
565
574
|
}
|
|
566
575
|
generateSetupCode(accessoryInfo) {
|
|
567
|
-
const buffer =
|
|
576
|
+
const buffer = Buffer.allocUnsafe(8);
|
|
568
577
|
let valueLow = Number.parseInt(accessoryInfo.pincode.replace(/-/g, ''), 10);
|
|
569
578
|
const valueHigh = accessoryInfo.category >> 1;
|
|
570
579
|
valueLow |= 1 << 28;
|
|
@@ -582,10 +591,10 @@ let ServerService = class ServerService {
|
|
|
582
591
|
return `X-HM://${encodedPayload}${accessoryInfo.setupID}`;
|
|
583
592
|
}
|
|
584
593
|
async getBridgePairingInformation() {
|
|
585
|
-
if (!await
|
|
586
|
-
return new
|
|
594
|
+
if (!await pathExists(this.accessoryInfoPath)) {
|
|
595
|
+
return new ServiceUnavailableException('Pairing Information Not Available Yet');
|
|
587
596
|
}
|
|
588
|
-
const accessoryInfo = await
|
|
597
|
+
const accessoryInfo = await readJson(this.accessoryInfoPath);
|
|
589
598
|
return {
|
|
590
599
|
displayName: accessoryInfo.displayName,
|
|
591
600
|
pincode: accessoryInfo.pincode,
|
|
@@ -595,7 +604,7 @@ let ServerService = class ServerService {
|
|
|
595
604
|
}
|
|
596
605
|
async getSystemNetworkInterfaces() {
|
|
597
606
|
const fromCache = this.serverServiceCache.get('network-interfaces');
|
|
598
|
-
const interfaces = fromCache || (await
|
|
607
|
+
const interfaces = fromCache || (await networkInterfaces()).filter((adapter) => {
|
|
599
608
|
return !adapter.internal
|
|
600
609
|
&& (adapter.ip4 || (adapter.ip6));
|
|
601
610
|
});
|
|
@@ -649,7 +658,7 @@ let ServerService = class ServerService {
|
|
|
649
658
|
const max = this.configService.homebridgeConfig.ports?.end ?? 60000;
|
|
650
659
|
const randomPort = () => Math.floor(Math.random() * (max - min + 1) + min);
|
|
651
660
|
let port = randomPort();
|
|
652
|
-
while (await (
|
|
661
|
+
while (await tcpCheck(port)) {
|
|
653
662
|
port = randomPort();
|
|
654
663
|
}
|
|
655
664
|
return { port };
|
|
@@ -672,11 +681,11 @@ let ServerService = class ServerService {
|
|
|
672
681
|
}
|
|
673
682
|
}
|
|
674
683
|
for (let port = min; port <= max; port += 1) {
|
|
675
|
-
if (!usedMatterPorts.has(port) && !await (
|
|
684
|
+
if (!usedMatterPorts.has(port) && !await tcpCheck(port)) {
|
|
676
685
|
return { port };
|
|
677
686
|
}
|
|
678
687
|
}
|
|
679
|
-
throw new
|
|
688
|
+
throw new InternalServerErrorException('No available ports in the Matter port range (5530-5541)');
|
|
680
689
|
}
|
|
681
690
|
async getHomebridgePort() {
|
|
682
691
|
const config = await this.configEditorService.getConfigFile();
|
|
@@ -698,7 +707,7 @@ let ServerService = class ServerService {
|
|
|
698
707
|
}
|
|
699
708
|
async setHomebridgeName(name) {
|
|
700
709
|
if (!name || !(/^[\p{L}\p{N}][\p{L}\p{N} ']*[\p{L}\p{N}]$/u).test(name)) {
|
|
701
|
-
throw new
|
|
710
|
+
throw new BadRequestException('Invalid name');
|
|
702
711
|
}
|
|
703
712
|
const config = await this.configEditorService.getConfigFile();
|
|
704
713
|
config.bridge.name = name;
|
|
@@ -706,7 +715,7 @@ let ServerService = class ServerService {
|
|
|
706
715
|
}
|
|
707
716
|
async setHomebridgePort(port) {
|
|
708
717
|
if (!port || typeof port !== 'number' || !Number.isInteger(port) || port < 1025 || port > 65533) {
|
|
709
|
-
throw new
|
|
718
|
+
throw new BadRequestException('Invalid port number');
|
|
710
719
|
}
|
|
711
720
|
const config = await this.configEditorService.getConfigFile();
|
|
712
721
|
config.bridge.port = port;
|
|
@@ -721,19 +730,19 @@ let ServerService = class ServerService {
|
|
|
721
730
|
delete value.end;
|
|
722
731
|
}
|
|
723
732
|
if ('start' in value && (typeof value.start !== 'number' || value.start < 1025 || value.start > 65533)) {
|
|
724
|
-
throw new
|
|
733
|
+
throw new BadRequestException('Port start must be a number between 1025 and 65533.');
|
|
725
734
|
}
|
|
726
735
|
if ('end' in value && (typeof value.end !== 'number' || value.end < 1025 || value.end > 65533)) {
|
|
727
|
-
throw new
|
|
736
|
+
throw new BadRequestException('Port end must be a number between 1025 and 65533.');
|
|
728
737
|
}
|
|
729
738
|
if ('start' in value && 'end' in value && value.start >= value.end) {
|
|
730
|
-
throw new
|
|
739
|
+
throw new BadRequestException('Ports start must be less than end.');
|
|
731
740
|
}
|
|
732
741
|
if ('start' in value && !('end' in value) && config.ports?.end && value.start >= config.ports.end) {
|
|
733
|
-
throw new
|
|
742
|
+
throw new BadRequestException('Ports start must be less than end.');
|
|
734
743
|
}
|
|
735
744
|
if ('end' in value && !('start' in value) && config.ports?.start && config.ports.start >= value.end) {
|
|
736
|
-
throw new
|
|
745
|
+
throw new BadRequestException('Ports start must be less than end.');
|
|
737
746
|
}
|
|
738
747
|
if (!value.start && !value.end) {
|
|
739
748
|
delete config.ports;
|
|
@@ -756,10 +765,10 @@ let ServerService = class ServerService {
|
|
|
756
765
|
const uiConfigBlock = configFile.platforms.find(x => x.platform === 'config');
|
|
757
766
|
if (uiConfigBlock) {
|
|
758
767
|
if (uiConfigBlock.wallpaper) {
|
|
759
|
-
const oldPath =
|
|
760
|
-
if (await
|
|
768
|
+
const oldPath = join(this.configService.storagePath, uiConfigBlock.wallpaper);
|
|
769
|
+
if (await pathExists(oldPath)) {
|
|
761
770
|
try {
|
|
762
|
-
await
|
|
771
|
+
await unlink(oldPath);
|
|
763
772
|
this.logger.log(`Old wallpaper file ${oldPath} deleted successfully.`);
|
|
764
773
|
}
|
|
765
774
|
catch (e) {
|
|
@@ -767,9 +776,9 @@ let ServerService = class ServerService {
|
|
|
767
776
|
}
|
|
768
777
|
}
|
|
769
778
|
}
|
|
770
|
-
const fileExtension =
|
|
771
|
-
const newPath =
|
|
772
|
-
await pump(data.file,
|
|
779
|
+
const fileExtension = extname(data.filename);
|
|
780
|
+
const newPath = join(this.configService.storagePath, `ui-wallpaper${fileExtension}`);
|
|
781
|
+
await pump(data.file, createWriteStream(newPath));
|
|
773
782
|
uiConfigBlock.wallpaper = `ui-wallpaper${fileExtension}`;
|
|
774
783
|
await this.configEditorService.updateConfigFile(configFile);
|
|
775
784
|
this.logger.log('Wallpaper uploaded and set in the config file.');
|
|
@@ -778,11 +787,11 @@ let ServerService = class ServerService {
|
|
|
778
787
|
async deleteWallpaper() {
|
|
779
788
|
const configFile = await this.configEditorService.getConfigFile();
|
|
780
789
|
const uiConfigBlock = configFile.platforms.find(x => x.platform === 'config');
|
|
781
|
-
const fullPath =
|
|
790
|
+
const fullPath = join(this.configService.storagePath, uiConfigBlock.wallpaper);
|
|
782
791
|
if (uiConfigBlock && uiConfigBlock.wallpaper) {
|
|
783
|
-
if (await
|
|
792
|
+
if (await pathExists(fullPath)) {
|
|
784
793
|
try {
|
|
785
|
-
await
|
|
794
|
+
await unlink(fullPath);
|
|
786
795
|
this.logger.log(`Wallpaper file ${uiConfigBlock.wallpaper} deleted successfully.`);
|
|
787
796
|
}
|
|
788
797
|
catch (e) {
|
|
@@ -798,9 +807,9 @@ let ServerService = class ServerService {
|
|
|
798
807
|
async nodeVersionChanged() {
|
|
799
808
|
return new Promise((res) => {
|
|
800
809
|
let result = false;
|
|
801
|
-
const child =
|
|
810
|
+
const child = spawn(process.execPath, ['-v']);
|
|
802
811
|
child.stdout.once('data', (data) => {
|
|
803
|
-
result = data.toString().trim() !==
|
|
812
|
+
result = data.toString().trim() !== process.version;
|
|
804
813
|
});
|
|
805
814
|
child.on('error', () => {
|
|
806
815
|
result = true;
|
|
@@ -810,14 +819,261 @@ let ServerService = class ServerService {
|
|
|
810
819
|
});
|
|
811
820
|
});
|
|
812
821
|
}
|
|
822
|
+
async uploadSslKeyCert(req) {
|
|
823
|
+
const parts = req.parts ? req.parts() : null;
|
|
824
|
+
const files = [];
|
|
825
|
+
if (parts) {
|
|
826
|
+
for await (const part of parts) {
|
|
827
|
+
if (part.file) {
|
|
828
|
+
files.push(part);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
const single = await req.file();
|
|
834
|
+
if (single?.file) {
|
|
835
|
+
files.push(single);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (!files.length) {
|
|
839
|
+
throw new BadRequestException('No files uploaded. Please upload both the private key and certificate files.');
|
|
840
|
+
}
|
|
841
|
+
const readStreamToBuffer = async (stream) => {
|
|
842
|
+
const chunks = [];
|
|
843
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
844
|
+
stream.on('data', (d) => chunks.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
|
|
845
|
+
stream.on('end', () => resolvePromise());
|
|
846
|
+
stream.on('error', rejectPromise);
|
|
847
|
+
});
|
|
848
|
+
return Buffer.concat(chunks);
|
|
849
|
+
};
|
|
850
|
+
let keyPem = null;
|
|
851
|
+
let certPem = null;
|
|
852
|
+
for (const f of files) {
|
|
853
|
+
if (f.file?.truncated) {
|
|
854
|
+
throw new InternalServerErrorException(`Upload exceeds maximum size ${globalThis.backup.maxBackupSizeText}.`);
|
|
855
|
+
}
|
|
856
|
+
const buf = await readStreamToBuffer(f.file);
|
|
857
|
+
const text = buf.toString('utf8');
|
|
858
|
+
if (/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/.test(text)) {
|
|
859
|
+
keyPem = buf;
|
|
860
|
+
}
|
|
861
|
+
else if (/-----BEGIN CERTIFICATE-----/.test(text)) {
|
|
862
|
+
certPem = buf;
|
|
863
|
+
}
|
|
864
|
+
else if (f.fieldname === 'key') {
|
|
865
|
+
keyPem = buf;
|
|
866
|
+
}
|
|
867
|
+
else if (f.fieldname === 'cert') {
|
|
868
|
+
certPem = buf;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (!keyPem || !certPem) {
|
|
872
|
+
throw new BadRequestException('Both a PEM private key and certificate must be provided.');
|
|
873
|
+
}
|
|
874
|
+
try {
|
|
875
|
+
const x509 = new X509Certificate(certPem);
|
|
876
|
+
const certPub = x509.publicKey.export({ type: 'spki', format: 'der' });
|
|
877
|
+
const priv = createPrivateKey({ key: keyPem });
|
|
878
|
+
const pubFromPriv = createPublicKey(priv).export({ type: 'spki', format: 'der' });
|
|
879
|
+
if (!certPub.equals(pubFromPriv)) {
|
|
880
|
+
throw new BadRequestException('The private key does not match the certificate public key.');
|
|
881
|
+
}
|
|
882
|
+
createSecureContext({ key: keyPem, cert: certPem });
|
|
883
|
+
}
|
|
884
|
+
catch (e) {
|
|
885
|
+
if (e instanceof BadRequestException) {
|
|
886
|
+
throw e;
|
|
887
|
+
}
|
|
888
|
+
throw new BadRequestException(`Invalid key/certificate: ${e?.message || e}`);
|
|
889
|
+
}
|
|
890
|
+
const sslDir = join(this.configService.storagePath, 'ssl-certs');
|
|
891
|
+
const keyPath = join(sslDir, 'ui-ssl.key');
|
|
892
|
+
const certPath = join(sslDir, 'ui-ssl.crt');
|
|
893
|
+
const { ensureDir, writeFile } = await import('fs-extra');
|
|
894
|
+
await ensureDir(sslDir);
|
|
895
|
+
await writeFile(keyPath, keyPem);
|
|
896
|
+
await writeFile(certPath, certPem);
|
|
897
|
+
const configFile = await this.configEditorService.getConfigFile();
|
|
898
|
+
const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
|
|
899
|
+
if (!uiConfigBlock) {
|
|
900
|
+
throw new InternalServerErrorException('Config platform block not found.');
|
|
901
|
+
}
|
|
902
|
+
if (!uiConfigBlock.ssl) {
|
|
903
|
+
uiConfigBlock.ssl = {};
|
|
904
|
+
}
|
|
905
|
+
uiConfigBlock.ssl.key = keyPath;
|
|
906
|
+
uiConfigBlock.ssl.cert = certPath;
|
|
907
|
+
delete uiConfigBlock.ssl.pfx;
|
|
908
|
+
delete uiConfigBlock.ssl.passphrase;
|
|
909
|
+
uiConfigBlock.ssl.selfSigned = false;
|
|
910
|
+
await this.configEditorService.updateConfigFile(configFile);
|
|
911
|
+
return {
|
|
912
|
+
ok: true,
|
|
913
|
+
type: 'keycert',
|
|
914
|
+
keyPath,
|
|
915
|
+
certPath,
|
|
916
|
+
details: 'Certificate and key validated and saved.',
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
async uploadSslPfx(req) {
|
|
920
|
+
let passphrase;
|
|
921
|
+
let filePart;
|
|
922
|
+
if (req.parts) {
|
|
923
|
+
for await (const part of req.parts()) {
|
|
924
|
+
if (part.type === 'file' || part.file) {
|
|
925
|
+
filePart = part;
|
|
926
|
+
}
|
|
927
|
+
else if (part.type === 'field' || part.value) {
|
|
928
|
+
if (part.fieldname === 'passphrase') {
|
|
929
|
+
passphrase = part.value;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
filePart = await req.file();
|
|
936
|
+
passphrase = req.body?.passphrase;
|
|
937
|
+
}
|
|
938
|
+
if (!filePart) {
|
|
939
|
+
throw new BadRequestException('No PFX file uploaded.');
|
|
940
|
+
}
|
|
941
|
+
if (filePart.file?.truncated) {
|
|
942
|
+
throw new InternalServerErrorException(`Upload exceeds maximum size ${globalThis.backup.maxBackupSizeText}.`);
|
|
943
|
+
}
|
|
944
|
+
const readStreamToBuffer = async (stream) => {
|
|
945
|
+
const chunks = [];
|
|
946
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
947
|
+
stream.on('data', (d) => chunks.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
|
|
948
|
+
stream.on('end', () => resolvePromise());
|
|
949
|
+
stream.on('error', rejectPromise);
|
|
950
|
+
});
|
|
951
|
+
return Buffer.concat(chunks);
|
|
952
|
+
};
|
|
953
|
+
const pfxBuffer = await readStreamToBuffer(filePart.file);
|
|
954
|
+
try {
|
|
955
|
+
createSecureContext({ pfx: pfxBuffer, passphrase });
|
|
956
|
+
}
|
|
957
|
+
catch (e) {
|
|
958
|
+
throw new BadRequestException(`Invalid PFX or passphrase: ${e?.message || e}`);
|
|
959
|
+
}
|
|
960
|
+
const sslDir = join(this.configService.storagePath, 'ssl-certs');
|
|
961
|
+
const pfxPath = join(sslDir, 'ui-ssl.pfx');
|
|
962
|
+
const { ensureDir, writeFile } = await import('fs-extra');
|
|
963
|
+
await ensureDir(sslDir);
|
|
964
|
+
await writeFile(pfxPath, pfxBuffer);
|
|
965
|
+
const configFile = await this.configEditorService.getConfigFile();
|
|
966
|
+
const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
|
|
967
|
+
if (!uiConfigBlock) {
|
|
968
|
+
throw new InternalServerErrorException('Config platform block not found.');
|
|
969
|
+
}
|
|
970
|
+
if (!uiConfigBlock.ssl) {
|
|
971
|
+
uiConfigBlock.ssl = {};
|
|
972
|
+
}
|
|
973
|
+
uiConfigBlock.ssl.pfx = pfxPath;
|
|
974
|
+
uiConfigBlock.ssl.passphrase = passphrase || '';
|
|
975
|
+
delete uiConfigBlock.ssl.key;
|
|
976
|
+
delete uiConfigBlock.ssl.cert;
|
|
977
|
+
uiConfigBlock.ssl.selfSigned = false;
|
|
978
|
+
await this.configEditorService.updateConfigFile(configFile);
|
|
979
|
+
return {
|
|
980
|
+
ok: true,
|
|
981
|
+
type: 'pfx',
|
|
982
|
+
pfxPath,
|
|
983
|
+
details: 'PFX validated and saved.',
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
async validateCurrentSslConfig() {
|
|
987
|
+
const configFile = await this.configEditorService.getConfigFile();
|
|
988
|
+
const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
|
|
989
|
+
const ssl = uiConfigBlock?.ssl || {};
|
|
990
|
+
if (!ssl || (!ssl.selfSigned && !ssl.key && !ssl.cert && !ssl.pfx)) {
|
|
991
|
+
return { ok: true, valid: true, type: 'off', details: 'HTTPS is disabled.' };
|
|
992
|
+
}
|
|
993
|
+
if (ssl.selfSigned) {
|
|
994
|
+
return { ok: true, valid: true, type: 'selfsigned', details: 'Self-signed mode enabled.' };
|
|
995
|
+
}
|
|
996
|
+
try {
|
|
997
|
+
if (ssl.key && ssl.cert) {
|
|
998
|
+
const { readFile } = await import('fs-extra');
|
|
999
|
+
const keyPem = await readFile(ssl.key);
|
|
1000
|
+
const certPem = await readFile(ssl.cert);
|
|
1001
|
+
const x509 = new X509Certificate(certPem);
|
|
1002
|
+
const certPub = x509.publicKey.export({ type: 'spki', format: 'der' });
|
|
1003
|
+
const priv = createPrivateKey({ key: keyPem });
|
|
1004
|
+
const pubFromPriv = createPublicKey(priv).export({ type: 'spki', format: 'der' });
|
|
1005
|
+
if (!certPub.equals(pubFromPriv)) {
|
|
1006
|
+
return { ok: true, valid: false, type: 'keycert', details: 'Private key does not match certificate.' };
|
|
1007
|
+
}
|
|
1008
|
+
createSecureContext({ key: keyPem, cert: certPem });
|
|
1009
|
+
return { ok: true, valid: true, type: 'keycert', details: 'Key and certificate are valid and match.' };
|
|
1010
|
+
}
|
|
1011
|
+
if (ssl.pfx) {
|
|
1012
|
+
const { readFile } = await import('fs-extra');
|
|
1013
|
+
const pfx = await readFile(ssl.pfx);
|
|
1014
|
+
createSecureContext({ pfx, passphrase: ssl.passphrase });
|
|
1015
|
+
return { ok: true, valid: true, type: 'pfx', details: 'PFX file and passphrase are valid.' };
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
catch (e) {
|
|
1019
|
+
return { ok: true, valid: false, type: ssl.pfx ? 'pfx' : 'keycert', details: e?.message || String(e) };
|
|
1020
|
+
}
|
|
1021
|
+
return { ok: true, valid: false, type: 'off', details: 'No SSL configuration found.' };
|
|
1022
|
+
}
|
|
1023
|
+
async generateSelfSignedCertificate(options = {}) {
|
|
1024
|
+
const hostnames = Array.isArray(options.hostnames) && options.hostnames.length
|
|
1025
|
+
? options.hostnames.map(h => String(h).trim()).filter(Boolean)
|
|
1026
|
+
: ['localhost', '127.0.0.1'];
|
|
1027
|
+
const mode = options.mode || 'keycert';
|
|
1028
|
+
const generator = new SslCertGeneratorService();
|
|
1029
|
+
await generator.generateCertificate(hostnames);
|
|
1030
|
+
const sslDir = join(this.configService.storagePath, 'ssl-certs');
|
|
1031
|
+
const keyPath = join(sslDir, 'private-key.pem');
|
|
1032
|
+
const certPath = join(sslDir, 'certificate.pem');
|
|
1033
|
+
const configFile = await this.configEditorService.getConfigFile();
|
|
1034
|
+
const uiConfigBlock = configFile.platforms.find((x) => x.platform === 'config');
|
|
1035
|
+
if (!uiConfigBlock.ssl) {
|
|
1036
|
+
uiConfigBlock.ssl = {};
|
|
1037
|
+
}
|
|
1038
|
+
if (mode === 'keycert') {
|
|
1039
|
+
uiConfigBlock.ssl.key = keyPath;
|
|
1040
|
+
uiConfigBlock.ssl.cert = certPath;
|
|
1041
|
+
delete uiConfigBlock.ssl.pfx;
|
|
1042
|
+
delete uiConfigBlock.ssl.passphrase;
|
|
1043
|
+
uiConfigBlock.ssl.selfSigned = false;
|
|
1044
|
+
uiConfigBlock.ssl.selfSignedHostnames = hostnames;
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
delete uiConfigBlock.ssl.key;
|
|
1048
|
+
delete uiConfigBlock.ssl.cert;
|
|
1049
|
+
delete uiConfigBlock.ssl.pfx;
|
|
1050
|
+
delete uiConfigBlock.ssl.passphrase;
|
|
1051
|
+
uiConfigBlock.ssl.selfSigned = true;
|
|
1052
|
+
uiConfigBlock.ssl.selfSignedHostnames = hostnames;
|
|
1053
|
+
}
|
|
1054
|
+
await this.configEditorService.updateConfigFile(configFile);
|
|
1055
|
+
return {
|
|
1056
|
+
ok: true,
|
|
1057
|
+
type: 'generated',
|
|
1058
|
+
mode,
|
|
1059
|
+
keyPath: mode === 'keycert' ? keyPath : undefined,
|
|
1060
|
+
certPath: mode === 'keycert' ? certPath : undefined,
|
|
1061
|
+
details: `Self-signed certificate generated for ${hostnames.join(', ')}`,
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
813
1064
|
};
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
(0,
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1065
|
+
ServerService = __decorate([
|
|
1066
|
+
Injectable(),
|
|
1067
|
+
__param(0, Inject(ConfigService)),
|
|
1068
|
+
__param(1, Inject(ConfigEditorService)),
|
|
1069
|
+
__param(2, Inject(AccessoriesService)),
|
|
1070
|
+
__param(3, Inject(HomebridgeIpcService)),
|
|
1071
|
+
__param(4, Inject(Logger)),
|
|
1072
|
+
__metadata("design:paramtypes", [ConfigService,
|
|
1073
|
+
ConfigEditorService,
|
|
1074
|
+
AccessoriesService,
|
|
1075
|
+
HomebridgeIpcService,
|
|
1076
|
+
Logger])
|
|
822
1077
|
], ServerService);
|
|
1078
|
+
export { ServerService };
|
|
823
1079
|
//# sourceMappingURL=server.service.js.map
|