homebridge-config-ui-x 5.11.2-beta.8 → 5.11.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 +3 -21
- package/dist/core/auth/auth.controller.d.ts +0 -4
- package/dist/core/config/config.interfaces.d.ts +0 -14
- package/dist/core/config/config.service.d.ts +0 -6
- package/dist/core/config/config.service.js +0 -8
- package/dist/core/config/config.service.js.map +1 -1
- package/dist/core/config/config.startup.js +17 -48
- package/dist/core/config/config.startup.js.map +1 -1
- package/dist/core/feature-flags/feature-flags.registry.js +0 -5
- package/dist/core/feature-flags/feature-flags.registry.js.map +1 -1
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js +0 -1
- package/dist/core/homebridge-ipc/homebridge-ipc.service.js.map +1 -1
- package/dist/core/spa/spa.filter.d.ts +0 -3
- package/dist/core/spa/spa.filter.js +2 -22
- package/dist/core/spa/spa.filter.js.map +1 -1
- package/dist/globalDefaults.js +0 -3
- package/dist/globalDefaults.js.map +1 -1
- package/dist/main.js +4 -18
- package/dist/main.js.map +1 -1
- package/dist/modules/accessories/accessories.controller.d.ts +1 -1
- package/dist/modules/accessories/accessories.module.js +0 -2
- package/dist/modules/accessories/accessories.module.js.map +1 -1
- package/dist/modules/accessories/accessories.service.d.ts +3 -21
- package/dist/modules/accessories/accessories.service.js +17 -280
- package/dist/modules/accessories/accessories.service.js.map +1 -1
- package/dist/modules/child-bridges/child-bridges.interfaces.d.ts +0 -9
- package/dist/modules/config-editor/config-editor.controller.d.ts +0 -4
- package/dist/modules/config-editor/config-editor.controller.js +0 -64
- package/dist/modules/config-editor/config-editor.controller.js.map +1 -1
- package/dist/modules/config-editor/config-editor.service.d.ts +1 -6
- package/dist/modules/config-editor/config-editor.service.js +1 -45
- package/dist/modules/config-editor/config-editor.service.js.map +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js +1 -1
- package/dist/modules/custom-plugins/plugins-settings-ui/plugins-settings-ui.service.js.map +1 -1
- package/dist/modules/plugins/plugins.module.js +1 -1
- package/dist/modules/plugins/plugins.service.js +2 -17
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/server/server.controller.d.ts +0 -50
- package/dist/modules/server/server.controller.js +2 -201
- package/dist/modules/server/server.controller.js.map +1 -1
- package/dist/modules/server/server.service.d.ts +0 -48
- package/dist/modules/server/server.service.js +14 -502
- package/dist/modules/server/server.service.js.map +1 -1
- package/dist/modules/status/status.gateway.d.ts +0 -2
- package/dist/modules/status/status.interfaces.d.ts +0 -11
- package/dist/modules/status/status.service.d.ts +1 -4
- package/dist/modules/status/status.service.js +2 -21
- package/dist/modules/status/status.service.js.map +1 -1
- package/package.json +7 -9
- package/public/3rdpartylicenses.txt +620 -594
- package/public/assets/plugin-ui-utils/ui.js +0 -3
- package/public/assets/plugin-ui-utils/ui.js.map +1 -1
- package/public/chunk-2BB4D4TW.js +1 -0
- package/public/chunk-2GCZ3AMH.js +1 -0
- package/public/chunk-2K5FWBCK.js +8 -0
- package/public/chunk-2LQBMVWH.js +1 -0
- package/public/chunk-34ZN4GVA.js +3 -0
- package/public/chunk-3JW6FD5V.js +1 -0
- package/public/chunk-57XTQ6PK.js +1 -0
- package/public/chunk-5DBV5PYR.js +19 -0
- package/public/chunk-5GKTPXPE.js +1 -0
- package/public/chunk-5X7ARCKG.js +5 -0
- package/public/chunk-67HOYJJ2.js +1 -0
- package/public/chunk-6MZHUZRT.js +4 -0
- package/public/chunk-77CMUO6J.js +1 -0
- package/public/chunk-7LJYL7EH.js +1 -0
- package/public/chunk-7TFBELRD.js +1 -0
- package/public/chunk-AXOHPUQR.js +1 -0
- package/public/chunk-BMAZBLAT.js +1 -0
- package/public/chunk-C554TVZA.js +2 -0
- package/public/chunk-DGDNSN6I.js +1 -0
- package/public/chunk-E6RSPO2D.js +1 -0
- package/public/chunk-ESQ665GW.js +5 -0
- package/public/chunk-G4QH622U.js +5 -0
- package/public/chunk-GKPR5M7S.js +1 -0
- package/public/chunk-GT7UMMFF.js +2 -0
- package/public/chunk-HBIQJCSE.js +1 -0
- package/public/{chunk-BMOLDRJ3.js → chunk-JLZ5WU6N.js} +1 -1
- package/public/chunk-JSZIAFBJ.js +1 -0
- package/public/chunk-JWQRV434.js +1 -0
- package/public/chunk-JX72JCMA.js +1 -0
- package/public/chunk-KT26K4DO.js +1 -0
- package/public/chunk-L63FND3A.js +1 -0
- package/public/chunk-LSXTHVKB.js +1 -0
- package/public/chunk-N5VXONVW.js +1 -0
- package/public/{chunk-H7F7H5MW.js → chunk-N6JGA2PG.js} +1 -1
- package/public/chunk-NVCJ3EJS.js +1 -0
- package/public/chunk-OPJZWDSO.js +1 -0
- package/public/chunk-OXBXHVWY.js +8 -0
- package/public/chunk-QHROK73U.js +50 -0
- package/public/chunk-QNEXPAIT.js +1 -0
- package/public/chunk-R2G2YWV4.js +51 -0
- package/public/chunk-RCNF365Q.js +1 -0
- package/public/chunk-RT6EDRIH.js +1 -0
- package/public/chunk-RWM6K46M.js +1 -0
- package/public/chunk-SVVR6UMH.js +1 -0
- package/public/chunk-UI4U37MO.js +1 -0
- package/public/chunk-ULKYNDD3.js +16 -0
- package/public/chunk-UPZ5UT6G.js +1 -0
- package/public/chunk-UQFIBBEZ.js +1 -0
- package/public/chunk-UWUTC3BD.js +1 -0
- package/public/chunk-UZ6QYM64.js +1 -0
- package/public/{chunk-CP6FP6S2.js → chunk-VKHX6FUQ.js} +2 -52
- package/public/chunk-VM2MV3DC.js +1 -0
- package/public/chunk-VM6FMRHY.js +1 -0
- package/public/chunk-VYK3U745.js +1 -0
- package/public/chunk-VZJJWHRX.js +1 -0
- package/public/chunk-WHC4E7WY.js +1 -0
- package/public/chunk-WJKVPIP2.js +1 -0
- package/public/chunk-WOBQOQPU.js +1 -0
- package/public/chunk-WTGHGHPV.js +1 -0
- package/public/chunk-WY6UQ47H.js +1 -0
- package/public/chunk-X4ZEQSRY.js +4 -0
- package/public/{chunk-WVG4A2SW.js → chunk-XVO5247V.js} +1 -1
- package/public/chunk-XWNU2MFJ.js +1 -0
- package/public/chunk-YD3GJ2C6.js +1 -0
- package/public/chunk-YUEGV7HK.js +4 -0
- package/public/chunk-ZLRFR32W.js +1 -0
- package/public/chunk-ZZ5IX26U.js +40 -0
- package/public/chunk-ZZQEWRDE.js +1 -0
- package/public/index.html +2 -2
- package/public/main-VKVUZHHA.js +1 -0
- package/public/polyfills-5KWHJ7II.js +2 -0
- package/public/styles-7EFV5QBG.css +1 -0
- package/scripts/extract-plugin-alias.js +2 -53
- package/dist/core/spa/spa-html.service.d.ts +0 -5
- package/dist/core/spa/spa-html.service.js +0 -32
- package/dist/core/spa/spa-html.service.js.map +0 -1
- package/dist/core/ssl/ssl-cert-generator.service.d.ts +0 -15
- package/dist/core/ssl/ssl-cert-generator.service.js +0 -125
- package/dist/core/ssl/ssl-cert-generator.service.js.map +0 -1
- package/dist/modules/accessories/accessories.interfaces.d.ts +0 -94
- package/dist/modules/accessories/accessories.interfaces.js +0 -2
- package/dist/modules/accessories/accessories.interfaces.js.map +0 -1
- package/docs/matter-todo.md +0 -15
- package/docs/ssl-upload-pr.md +0 -103
- package/public/assets/matter.svg +0 -8
- package/public/chunk-2V4LIZUH.js +0 -1
- package/public/chunk-4UVYLGCZ.js +0 -1
- package/public/chunk-4WEBTV3N.js +0 -1
- package/public/chunk-4YNYBAGL.js +0 -1
- package/public/chunk-52X3LPJH.js +0 -1
- package/public/chunk-5R6AJZGA.js +0 -1
- package/public/chunk-5WSUQV2C.js +0 -1
- package/public/chunk-6DLZEENG.js +0 -1
- package/public/chunk-6INJQUJQ.js +0 -16
- package/public/chunk-6QWPGE2L.js +0 -1
- package/public/chunk-77S5CL3V.js +0 -1
- package/public/chunk-7BMHIIBT.js +0 -1
- package/public/chunk-7DFQDPRM.js +0 -1
- package/public/chunk-7WKZ7EMI.js +0 -1
- package/public/chunk-7WUBDTA6.js +0 -1
- package/public/chunk-AXMO2G45.js +0 -1
- package/public/chunk-B46EWHBD.js +0 -1
- package/public/chunk-B4AJQJMI.js +0 -1
- package/public/chunk-B75B723G.js +0 -1
- package/public/chunk-BB35SJH2.js +0 -2
- package/public/chunk-BFGRDXJK.js +0 -35
- package/public/chunk-BQRKHSGH.js +0 -1
- package/public/chunk-BXRCPOIK.js +0 -1
- package/public/chunk-BZ3XB6O4.js +0 -1
- package/public/chunk-CEMVTOZG.js +0 -4
- package/public/chunk-CMERG3AC.js +0 -1
- package/public/chunk-CMO4UANJ.js +0 -1
- package/public/chunk-CPT52C2F.js +0 -1
- package/public/chunk-D3Q5QTFY.js +0 -1
- package/public/chunk-ETWBSGPI.js +0 -1
- package/public/chunk-EWS4VSZB.js +0 -1
- package/public/chunk-F5AJLGGI.js +0 -1
- package/public/chunk-G2GGYHA2.js +0 -3
- package/public/chunk-G2KWTNHR.js +0 -1
- package/public/chunk-GDWF2NMA.js +0 -2
- package/public/chunk-GGTS2YBW.js +0 -4
- package/public/chunk-GJG6QZ7P.js +0 -1
- package/public/chunk-GU5F3H4I.js +0 -1
- package/public/chunk-H3BKW5W3.js +0 -1
- package/public/chunk-H4WASUXW.js +0 -1
- package/public/chunk-HUTYOGRH.js +0 -4
- package/public/chunk-IBUR2RIO.js +0 -1
- package/public/chunk-IDJSTIFL.js +0 -1
- package/public/chunk-IP6FLYVL.js +0 -1
- package/public/chunk-IPI77WYV.js +0 -1
- package/public/chunk-J2DDS6ED.js +0 -1
- package/public/chunk-J6ND4K5Q.js +0 -1
- package/public/chunk-J7UDRE66.js +0 -2
- package/public/chunk-JUQRWAIR.js +0 -1
- package/public/chunk-JWGWPSC6.js +0 -1
- package/public/chunk-JWRZMMRO.js +0 -68
- package/public/chunk-JWUEJV6S.js +0 -1
- package/public/chunk-JY5ZEEL2.js +0 -1
- package/public/chunk-KFPYUDGJ.js +0 -1
- package/public/chunk-KIRW7NMH.js +0 -1
- package/public/chunk-KTQBVBY4.js +0 -1
- package/public/chunk-LKJRMUOE.js +0 -1
- package/public/chunk-LVQWSVHA.js +0 -1
- package/public/chunk-LZ7S7WKH.js +0 -1
- package/public/chunk-MDHWQ5V2.js +0 -8
- package/public/chunk-MJP4XVUI.js +0 -1
- package/public/chunk-N5ONV6TU.js +0 -1
- package/public/chunk-N7UWTPTT.js +0 -1
- package/public/chunk-NAICVRJH.js +0 -1
- package/public/chunk-NCFDA3W4.js +0 -1
- package/public/chunk-NIS65GVY.js +0 -1
- package/public/chunk-OBV66CW4.js +0 -1
- package/public/chunk-OF2SRCQW.js +0 -5
- package/public/chunk-OOILCTQ3.js +0 -19
- package/public/chunk-PAZUAFSZ.js +0 -1
- package/public/chunk-PEUDPLH6.js +0 -1
- package/public/chunk-PRGOUVRO.js +0 -1
- package/public/chunk-PWCLNQBI.js +0 -1
- package/public/chunk-Q7EXN4JA.js +0 -1
- package/public/chunk-QE7VI3JB.js +0 -1
- package/public/chunk-QES44GQE.js +0 -1
- package/public/chunk-QMWN5KPC.js +0 -1
- package/public/chunk-R6MQZ2DW.js +0 -1
- package/public/chunk-RESZLSD7.js +0 -1
- package/public/chunk-RGWFM4OK.js +0 -1
- package/public/chunk-RI4GLH22.js +0 -1
- package/public/chunk-RP5JPYDI.js +0 -1
- package/public/chunk-RT25QBCQ.js +0 -1
- package/public/chunk-RVWZNTDJ.js +0 -1
- package/public/chunk-RXDY2NMJ.js +0 -1
- package/public/chunk-SDRRZFEQ.js +0 -1
- package/public/chunk-SPC2DLZR.js +0 -3
- package/public/chunk-TCUQZ3QO.js +0 -1
- package/public/chunk-TPUA4UIN.js +0 -1
- package/public/chunk-TQBVDY26.js +0 -1
- package/public/chunk-U24HWDVU.js +0 -1
- package/public/chunk-U2NM4L2D.js +0 -1
- package/public/chunk-UWYTPKKV.js +0 -1
- package/public/chunk-VKR3GG6K.js +0 -1
- package/public/chunk-VY7HWBNV.js +0 -5
- package/public/chunk-W55SP3LJ.js +0 -1
- package/public/chunk-WP3AD6W5.js +0 -1
- package/public/chunk-XNZA4UBO.js +0 -12
- package/public/chunk-XOPZ7U6G.js +0 -1
- package/public/chunk-XORPUWNC.js +0 -1
- package/public/chunk-XPFMPBEG.js +0 -1
- package/public/chunk-XTYMIZVV.js +0 -1
- package/public/chunk-Y3PXWRYQ.js +0 -1
- package/public/chunk-Y5WP66WK.js +0 -1
- package/public/chunk-YCQCWGBG.js +0 -1
- package/public/chunk-Z5PUNNUZ.js +0 -1
- package/public/chunk-ZCGZM7LU.js +0 -1
- package/public/chunk-ZNN6GWYK.js +0 -19
- package/public/main-O5PA2XOA.js +0 -1
- package/public/media/matter-P563JGDL.svg +0 -8
- package/public/polyfills-34U4WL3Z.js +0 -1
- package/public/styles-CT2LPGES.css +0 -1
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { EventEmitter } from 'node:events'
|
|
8
|
-
import { readFileSync } from 'node:fs'
|
|
9
8
|
import { createRequire } from 'node:module'
|
|
10
9
|
import path from 'node:path'
|
|
11
10
|
import process from 'node:process'
|
|
@@ -41,26 +40,6 @@ const HomebridgeApiMock = {
|
|
|
41
40
|
serverVersion: '1.2.3',
|
|
42
41
|
on: () => { /** mock */ },
|
|
43
42
|
emit: () => { /** mock */ },
|
|
44
|
-
// Mock Matter API
|
|
45
|
-
isMatterAvailable() {
|
|
46
|
-
return true
|
|
47
|
-
},
|
|
48
|
-
isMatterEnabled() {
|
|
49
|
-
return true
|
|
50
|
-
},
|
|
51
|
-
matterDeviceTypes: new Proxy({}, {
|
|
52
|
-
get() {
|
|
53
|
-
return {} // Return empty object for any device type
|
|
54
|
-
},
|
|
55
|
-
}),
|
|
56
|
-
matterClusters: new Proxy({}, {
|
|
57
|
-
get() {
|
|
58
|
-
return {} // Return empty object for any cluster
|
|
59
|
-
},
|
|
60
|
-
}),
|
|
61
|
-
registerMatterAccessory: () => { /** mock */ },
|
|
62
|
-
unregisterMatterAccessory: () => { /** mock */ },
|
|
63
|
-
updateMatterAccessoryState: () => { /** mock */ },
|
|
64
43
|
hap: {
|
|
65
44
|
Characteristic: new class Characteristic extends EventEmitter {
|
|
66
45
|
constructor() {
|
|
@@ -124,41 +103,11 @@ const HomebridgeApiMock = {
|
|
|
124
103
|
},
|
|
125
104
|
}
|
|
126
105
|
|
|
127
|
-
|
|
106
|
+
function main() {
|
|
128
107
|
try {
|
|
129
108
|
let pluginInitializer
|
|
130
109
|
const pluginPath = process.env.UIX_EXTRACT_PLUGIN_PATH
|
|
131
|
-
|
|
132
|
-
// Read package.json to get the proper entry point
|
|
133
|
-
let actualEntryPoint = pluginPath
|
|
134
|
-
try {
|
|
135
|
-
const packageJsonPath = path.join(pluginPath, 'package.json')
|
|
136
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
|
|
137
|
-
|
|
138
|
-
if (packageJson.main) {
|
|
139
|
-
actualEntryPoint = path.join(pluginPath, packageJson.main)
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error('[extract-plugin-alias] Could not read package.json, using directory path')
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
let pluginModules
|
|
146
|
-
|
|
147
|
-
// Try to load as CommonJS first
|
|
148
|
-
try {
|
|
149
|
-
pluginModules = require(actualEntryPoint)
|
|
150
|
-
} catch (requireError) {
|
|
151
|
-
// If require fails, try dynamic import for ESM modules
|
|
152
|
-
try {
|
|
153
|
-
// For ESM, we need to use file:// URL on some platforms
|
|
154
|
-
const importPath = actualEntryPoint.startsWith('/') || actualEntryPoint.startsWith('file://')
|
|
155
|
-
? actualEntryPoint
|
|
156
|
-
: path.resolve(actualEntryPoint)
|
|
157
|
-
pluginModules = await import(importPath)
|
|
158
|
-
} catch (importError) {
|
|
159
|
-
throw requireError // Throw the original error
|
|
160
|
-
}
|
|
161
|
-
}
|
|
110
|
+
const pluginModules = require(pluginPath)
|
|
162
111
|
|
|
163
112
|
if (typeof pluginModules === 'function') {
|
|
164
113
|
pluginInitializer = pluginModules
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
import { pathExists as exists } from 'fs-extra/esm';
|
|
5
|
-
export class SpaHtmlService {
|
|
6
|
-
static async updateIndexHtml(webroot) {
|
|
7
|
-
const indexPath = resolve(process.env.UIX_BASE_PATH, 'public/index.html');
|
|
8
|
-
if (!(await exists(indexPath))) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
const originalHtml = await readFile(indexPath, 'utf-8');
|
|
12
|
-
const normalizedWebroot = this.normalizeWebroot(webroot);
|
|
13
|
-
const modifiedHtml = this.transformHtmlForWebroot(originalHtml, normalizedWebroot);
|
|
14
|
-
await writeFile(indexPath, modifiedHtml);
|
|
15
|
-
}
|
|
16
|
-
static normalizeWebroot(webroot) {
|
|
17
|
-
if (!webroot || webroot === '/') {
|
|
18
|
-
return '';
|
|
19
|
-
}
|
|
20
|
-
return webroot.replace(/\/+/g, '/').replace(/^\/+|\/+$/g, '');
|
|
21
|
-
}
|
|
22
|
-
static transformHtmlForWebroot(html, webroot) {
|
|
23
|
-
const baseHref = webroot ? `/${webroot}/` : '/';
|
|
24
|
-
return html
|
|
25
|
-
.replace(/<base href="[^"]*"(\s*)\/?>/g, `<base href="${baseHref}"$1/>`)
|
|
26
|
-
.replace(/href="\/(?:.*?\/)?assets\//g, 'href="assets/')
|
|
27
|
-
.replace(/href="\/(?:.*?\/)?favicon\.ico"/g, 'href="favicon.ico"')
|
|
28
|
-
.replace(/href="\/(?:.*?\/)?([^"/]+\.(png|svg|webmanifest))"/g, 'href="$1"')
|
|
29
|
-
.replace(/src="\/(?:.*?\/)?assets\//g, 'src="assets/');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=spa-html.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"spa-html.service.js","sourceRoot":"","sources":["../../../src/core/spa/spa-html.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,OAAO,MAAM,cAAc,CAAA;AAElC,OAAO,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,cAAc,CAAA;AAEnD,MAAM,OAAO,cAAc;IAKzB,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAe;QAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAA;QACzE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAC/B,OAAM;QACR,CAAC;QACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACvD,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAA;QACxD,MAAM,YAAY,GAAG,IAAI,CAAC,uBAAuB,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAA;QAClF,MAAM,SAAS,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;IAC1C,CAAC;IAOO,MAAM,CAAC,gBAAgB,CAAC,OAAe;QAC7C,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YAChC,OAAO,EAAE,CAAA;QACX,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;IAC/D,CAAC;IAQO,MAAM,CAAC,uBAAuB,CAAC,IAAY,EAAE,OAAe;QAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAE/C,OAAO,IAAI;aACR,OAAO,CAAC,8BAA8B,EAAE,eAAe,QAAQ,OAAO,CAAC;aACvE,OAAO,CAAC,6BAA6B,EAAE,eAAe,CAAC;aACvD,OAAO,CAAC,kCAAkC,EAAE,oBAAoB,CAAC;aACjE,OAAO,CAAC,qDAAqD,EAAE,WAAW,CAAC;aAC3E,OAAO,CAAC,4BAA4B,EAAE,cAAc,CAAC,CAAA;IAC1D,CAAC;CACF"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'node:buffer';
|
|
2
|
-
interface SslCertificateData {
|
|
3
|
-
privateKey: Buffer;
|
|
4
|
-
certificate: Buffer;
|
|
5
|
-
}
|
|
6
|
-
export declare class SslCertGeneratorService {
|
|
7
|
-
private readonly logger;
|
|
8
|
-
private readonly certDir;
|
|
9
|
-
private readonly privateKeyPath;
|
|
10
|
-
private readonly certificatePath;
|
|
11
|
-
constructor();
|
|
12
|
-
generateOrLoadCertificate(hostnames?: string[]): Promise<SslCertificateData>;
|
|
13
|
-
generateCertificate(hostnames: string[]): Promise<SslCertificateData>;
|
|
14
|
-
}
|
|
15
|
-
export {};
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
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;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
-
};
|
|
10
|
-
var SslCertGeneratorService_1;
|
|
11
|
-
import { Buffer } from 'node:buffer';
|
|
12
|
-
import { existsSync } from 'node:fs';
|
|
13
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
14
|
-
import { homedir } from 'node:os';
|
|
15
|
-
import { join, resolve } from 'node:path';
|
|
16
|
-
import process from 'node:process';
|
|
17
|
-
import { Injectable } from '@nestjs/common';
|
|
18
|
-
import { ensureDir } from 'fs-extra/esm';
|
|
19
|
-
import forge from 'node-forge';
|
|
20
|
-
import { Logger } from '../logger/logger.service.js';
|
|
21
|
-
let SslCertGeneratorService = SslCertGeneratorService_1 = class SslCertGeneratorService {
|
|
22
|
-
logger = new Logger(SslCertGeneratorService_1.name);
|
|
23
|
-
certDir;
|
|
24
|
-
privateKeyPath;
|
|
25
|
-
certificatePath;
|
|
26
|
-
constructor() {
|
|
27
|
-
const storagePath = process.env.UIX_STORAGE_PATH || resolve(homedir(), '.homebridge');
|
|
28
|
-
this.certDir = join(storagePath, 'ssl-certs');
|
|
29
|
-
this.privateKeyPath = join(this.certDir, 'private-key.pem');
|
|
30
|
-
this.certificatePath = join(this.certDir, 'certificate.pem');
|
|
31
|
-
}
|
|
32
|
-
async generateOrLoadCertificate(hostnames = ['localhost']) {
|
|
33
|
-
if (existsSync(this.privateKeyPath) && existsSync(this.certificatePath)) {
|
|
34
|
-
try {
|
|
35
|
-
const privateKey = await readFile(this.privateKeyPath);
|
|
36
|
-
const certificate = await readFile(this.certificatePath);
|
|
37
|
-
this.logger.log('Loaded existing self-signed certificate');
|
|
38
|
-
return { privateKey, certificate };
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
this.logger.warn('Failed to load existing certificate, generating new one:', error.message);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return this.generateCertificate(hostnames);
|
|
45
|
-
}
|
|
46
|
-
async generateCertificate(hostnames) {
|
|
47
|
-
this.logger.log('Generating self-signed certificate...');
|
|
48
|
-
try {
|
|
49
|
-
await ensureDir(this.certDir);
|
|
50
|
-
const keys = forge.pki.rsa.generateKeyPair(2048);
|
|
51
|
-
const cert = forge.pki.createCertificate();
|
|
52
|
-
cert.publicKey = keys.publicKey;
|
|
53
|
-
cert.serialNumber = '01';
|
|
54
|
-
cert.validity.notBefore = new Date();
|
|
55
|
-
cert.validity.notAfter = new Date('2050-01-01T00:00:00Z');
|
|
56
|
-
const attrs = [
|
|
57
|
-
{ name: 'commonName', value: hostnames[0] || 'localhost' },
|
|
58
|
-
{ name: 'countryName', value: 'US' },
|
|
59
|
-
{ shortName: 'ST', value: 'State' },
|
|
60
|
-
{ name: 'localityName', value: 'City' },
|
|
61
|
-
{ name: 'organizationName', value: 'Homebridge' },
|
|
62
|
-
{ shortName: 'OU', value: 'Homebridge UI' },
|
|
63
|
-
];
|
|
64
|
-
cert.setSubject(attrs);
|
|
65
|
-
cert.setIssuer(attrs);
|
|
66
|
-
const extensions = [
|
|
67
|
-
{
|
|
68
|
-
name: 'basicConstraints',
|
|
69
|
-
cA: true,
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'keyUsage',
|
|
73
|
-
keyCertSign: true,
|
|
74
|
-
digitalSignature: true,
|
|
75
|
-
nonRepudiation: true,
|
|
76
|
-
keyEncipherment: true,
|
|
77
|
-
dataEncipherment: true,
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
name: 'extKeyUsage',
|
|
81
|
-
serverAuth: true,
|
|
82
|
-
clientAuth: true,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
name: 'subjectAltName',
|
|
86
|
-
altNames: hostnames.map((hostname) => {
|
|
87
|
-
if (/^(?:\d{1,3}\.){3}\d{1,3}$/.test(hostname) || /^(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}$/i.test(hostname)) {
|
|
88
|
-
return {
|
|
89
|
-
type: 7,
|
|
90
|
-
ip: hostname,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
return {
|
|
95
|
-
type: 2,
|
|
96
|
-
value: hostname,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}),
|
|
100
|
-
},
|
|
101
|
-
];
|
|
102
|
-
cert.setExtensions(extensions);
|
|
103
|
-
cert.sign(keys.privateKey, forge.md.sha256.create());
|
|
104
|
-
const privateKeyPem = forge.pki.privateKeyToPem(keys.privateKey);
|
|
105
|
-
const certificatePem = forge.pki.certificateToPem(cert);
|
|
106
|
-
await writeFile(this.privateKeyPath, privateKeyPem, 'utf8');
|
|
107
|
-
await writeFile(this.certificatePath, certificatePem, 'utf8');
|
|
108
|
-
this.logger.log(`Self-signed certificate generated successfully for: ${hostnames.join(', ')}`);
|
|
109
|
-
return {
|
|
110
|
-
privateKey: Buffer.from(privateKeyPem),
|
|
111
|
-
certificate: Buffer.from(certificatePem),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
this.logger.error('Failed to generate self-signed certificate:', error);
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
SslCertGeneratorService = SslCertGeneratorService_1 = __decorate([
|
|
121
|
-
Injectable(),
|
|
122
|
-
__metadata("design:paramtypes", [])
|
|
123
|
-
], SslCertGeneratorService);
|
|
124
|
-
export { SslCertGeneratorService };
|
|
125
|
-
//# sourceMappingURL=ssl-cert-generator.service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ssl-cert-generator.service.js","sourceRoot":"","sources":["../../../src/core/ssl/ssl-cert-generator.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
export interface MatterEvent {
|
|
2
|
-
type: 'accessoriesData' | 'accessoryUpdate' | 'accessoryAdded' | 'accessoryRemoved' | 'accessoryControlResponse';
|
|
3
|
-
data?: MatterAccessoriesResponse | MatterAccessoryInfo | MatterStateUpdate | MatterControlResponse;
|
|
4
|
-
}
|
|
5
|
-
export interface MatterAccessoriesResponse {
|
|
6
|
-
accessories: MatterAccessory[];
|
|
7
|
-
error?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface MatterAccessory {
|
|
10
|
-
uuid: string;
|
|
11
|
-
displayName: string;
|
|
12
|
-
deviceType: string;
|
|
13
|
-
clusters: Record<string, Record<string, unknown>>;
|
|
14
|
-
manufacturer?: string;
|
|
15
|
-
model?: string;
|
|
16
|
-
serialNumber?: string;
|
|
17
|
-
firmwareRevision?: string;
|
|
18
|
-
bridge?: {
|
|
19
|
-
name?: string;
|
|
20
|
-
username?: string;
|
|
21
|
-
};
|
|
22
|
-
plugin?: string;
|
|
23
|
-
platform?: string;
|
|
24
|
-
commissioned?: boolean;
|
|
25
|
-
fabricCount?: number;
|
|
26
|
-
fabrics?: unknown[];
|
|
27
|
-
parts?: MatterAccessoryPart[];
|
|
28
|
-
}
|
|
29
|
-
export interface MatterAccessoryPart {
|
|
30
|
-
id: string;
|
|
31
|
-
displayName: string;
|
|
32
|
-
deviceType: string;
|
|
33
|
-
clusters: Record<string, Record<string, unknown>>;
|
|
34
|
-
}
|
|
35
|
-
export interface MatterAccessoryInfo extends MatterAccessory {
|
|
36
|
-
error?: string;
|
|
37
|
-
}
|
|
38
|
-
export interface MatterService {
|
|
39
|
-
uniqueId: string;
|
|
40
|
-
uuid: string;
|
|
41
|
-
serviceName: string;
|
|
42
|
-
displayName: string;
|
|
43
|
-
deviceType: string;
|
|
44
|
-
clusters: Record<string, Record<string, unknown>>;
|
|
45
|
-
partId?: string;
|
|
46
|
-
protocol: 'matter';
|
|
47
|
-
instance: {
|
|
48
|
-
name: string;
|
|
49
|
-
username: string;
|
|
50
|
-
};
|
|
51
|
-
accessoryInformation: {
|
|
52
|
-
'Name': string;
|
|
53
|
-
'Manufacturer': string;
|
|
54
|
-
'Model': string;
|
|
55
|
-
'Serial Number': string;
|
|
56
|
-
'Firmware Revision': string;
|
|
57
|
-
};
|
|
58
|
-
bridge?: {
|
|
59
|
-
name?: string;
|
|
60
|
-
username?: string;
|
|
61
|
-
};
|
|
62
|
-
plugin?: string;
|
|
63
|
-
platform?: string;
|
|
64
|
-
commissioned?: boolean;
|
|
65
|
-
fabricCount?: number;
|
|
66
|
-
fabrics?: unknown[];
|
|
67
|
-
aid: 0;
|
|
68
|
-
iid: 0;
|
|
69
|
-
}
|
|
70
|
-
export interface MatterStateUpdate {
|
|
71
|
-
uuid: string;
|
|
72
|
-
cluster: string;
|
|
73
|
-
state: Record<string, unknown>;
|
|
74
|
-
partId?: string;
|
|
75
|
-
}
|
|
76
|
-
export interface MatterControlResponse {
|
|
77
|
-
success: boolean;
|
|
78
|
-
error?: string;
|
|
79
|
-
}
|
|
80
|
-
export interface MatterControlRequest {
|
|
81
|
-
uniqueId: string;
|
|
82
|
-
cluster: string;
|
|
83
|
-
attributes: Record<string, unknown>;
|
|
84
|
-
}
|
|
85
|
-
export interface AccessoryControlMessage {
|
|
86
|
-
set?: {
|
|
87
|
-
uniqueId: string;
|
|
88
|
-
iid?: number;
|
|
89
|
-
value?: string | number | boolean;
|
|
90
|
-
cluster?: string;
|
|
91
|
-
attributes?: Record<string, unknown>;
|
|
92
|
-
};
|
|
93
|
-
refresh?: boolean;
|
|
94
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"accessories.interfaces.js","sourceRoot":"","sources":["../../../src/modules/accessories/accessories.interfaces.ts"],"names":[],"mappings":""}
|
package/docs/matter-todo.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
- diagnose issue with all matter accessories reporting 'Unsupported' after restart
|
|
2
|
-
- reduce debug logging especially for case when user has not enabled matter anywhere
|
|
3
|
-
- reduce debug logging for ipc connection for accessory control via ui
|
|
4
|
-
|
|
5
|
-
- document step in iphone settings to properly 'forget' bridge connections
|
|
6
|
-
|
|
7
|
-
- ensure deselecting 'enable matter' in bridge settings properly removes all matter accessories + bridge
|
|
8
|
-
- ensure 'remove individual accessories' properly cleans up matter accessory
|
|
9
|
-
- ensure 'remove bridge accessories' properly cleans up all matter accessories
|
|
10
|
-
- ensure 'reset individual bridges' properly cleans up matter accessories + bridge (or external accessory)
|
|
11
|
-
- ensure 'reset all bridges' properly cleans up all matter accessories + bridges (+ external accessories)
|
|
12
|
-
|
|
13
|
-
- wire up fan accessory in the ui accessory control page
|
|
14
|
-
- wire up thermostat accessory in the ui accessory control page
|
|
15
|
-
- enable more 'show as' service types especially for common switch
|
package/docs/ssl-upload-pr.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# SSL uploads, validation, and self-signed generation
|
|
2
|
-
|
|
3
|
-
This PR adds a complete HTTPS management experience to Settings → Security → HTTPS:
|
|
4
|
-
|
|
5
|
-
- Upload PEM key + certificate with server-side validation
|
|
6
|
-
- Upload PKCS#12 (.pfx/.p12) with optional passphrase and validation
|
|
7
|
-
- Validate the currently configured HTTPS mode from the UI (“Validate SSL”)
|
|
8
|
-
- Generate a self-signed certificate from the UI with hostnames/SANs
|
|
9
|
-
- Generate Self‑Signed & Use as Key + Cert (writes files, selects key/cert mode)
|
|
10
|
-
- Generate & Enable Self‑Signed Mode (enables runtime self-signed mode)
|
|
11
|
-
|
|
12
|
-
See the updated mockup: `screenshots/ssl-upload-mock.svg`.
|
|
13
|
-
|
|
14
|
-
## Target branch
|
|
15
|
-
|
|
16
|
-
Per repo guidelines, this PR targets the current beta branch: `beta-5.8.1`.
|
|
17
|
-
|
|
18
|
-
## Backend (NestJS)
|
|
19
|
-
|
|
20
|
-
New/updated endpoints under Server module:
|
|
21
|
-
|
|
22
|
-
- POST `/server/ssl/keycert` — multipart upload for `key.pem` + `cert.pem`
|
|
23
|
-
- Validates key↔cert pair via Node.js X509/tls before persisting
|
|
24
|
-
- Saves to `{storagePath}/ssl-certs/ui-ssl.key` and `ui-ssl.crt`
|
|
25
|
-
- Updates `config.json` → `ui.ssl` to key/cert mode; clears pfx/selfSigned
|
|
26
|
-
- POST `/server/ssl/pfx` — multipart upload for `ui-ssl.pfx` with optional `passphrase`
|
|
27
|
-
- Validates by creating a SecureContext
|
|
28
|
-
- Saves to `{storagePath}/ssl-certs/ui-ssl.pfx`
|
|
29
|
-
- Updates `config.json` → `ui.ssl` to pfx mode; clears key/cert/selfSigned
|
|
30
|
-
- POST `/server/ssl/validate` — validates the current HTTPS configuration
|
|
31
|
-
- Handles `off`, `selfsigned`, `keycert`, and `pfx` modes; returns status/details
|
|
32
|
-
- POST `/server/ssl/selfsigned/generate` — generates a self-signed cert
|
|
33
|
-
- Body: `{ hostnames?: string[], mode?: 'keycert'|'selfsigned' }`
|
|
34
|
-
- Uses existing SSL generator service
|
|
35
|
-
- If `mode='keycert'`: writes `private-key.pem`/`certificate.pem`, sets key/cert mode
|
|
36
|
-
- If `mode='selfsigned'`: enables runtime self-signed mode and records hostnames
|
|
37
|
-
|
|
38
|
-
Security: endpoints are admin-only and write under `{storagePath}/ssl-certs`.
|
|
39
|
-
|
|
40
|
-
## Frontend (Angular)
|
|
41
|
-
|
|
42
|
-
Settings → Security → HTTPS additions:
|
|
43
|
-
|
|
44
|
-
- HTTPS mode selector: Off, Self-Signed, Key+Cert, PFX
|
|
45
|
-
- “Validate SSL” button next to the selector
|
|
46
|
-
- Self-signed section:
|
|
47
|
-
- Hostnames/SANs input (e.g. `localhost, 127.0.0.1, homebridge.local`)
|
|
48
|
-
- Two actions:
|
|
49
|
-
- “Generate Self‑Signed & Use as Key + Cert”
|
|
50
|
-
- “Generate & Enable Self‑Signed Mode”
|
|
51
|
-
- Key/Cert upload (with unified “Upload” button) and PFX upload with passphrase field
|
|
52
|
-
- Toastr notifications and restart prompt after changes
|
|
53
|
-
- i18n keys added in `ui/src/i18n/en.json` for new labels and toasts
|
|
54
|
-
|
|
55
|
-
## Storage layout
|
|
56
|
-
|
|
57
|
-
All artifacts are saved under `{storagePath}/ssl-certs`:
|
|
58
|
-
|
|
59
|
-
- `ui-ssl.key` / `ui-ssl.crt` (file-based key/cert mode)
|
|
60
|
-
- `ui-ssl.pfx` (pfx mode)
|
|
61
|
-
- `private-key.pem` / `certificate.pem` (generated self-signed files)
|
|
62
|
-
|
|
63
|
-
## Manual validation
|
|
64
|
-
|
|
65
|
-
Follow the project’s validation steps:
|
|
66
|
-
|
|
67
|
-
1. Build and test:
|
|
68
|
-
- `npm run build`
|
|
69
|
-
- `npm run lint`
|
|
70
|
-
- `npm run test`
|
|
71
|
-
2. Start the app (`npm run watch` for UI + backend) and open Settings → Security → HTTPS
|
|
72
|
-
3. Try each flow:
|
|
73
|
-
- Upload Key+Cert and validate
|
|
74
|
-
- Upload PFX (with/without passphrase) and validate
|
|
75
|
-
- Generate self-signed with custom hostnames and select one of the two buttons
|
|
76
|
-
- Use “Validate SSL” to verify the mode
|
|
77
|
-
|
|
78
|
-
Backend-only check (optional):
|
|
79
|
-
|
|
80
|
-
- POST `/server/ssl/validate` should return OK plus details of the active mode
|
|
81
|
-
|
|
82
|
-
## Notes
|
|
83
|
-
|
|
84
|
-
- Raspbian image: the self-signed UI option is hidden/disabled since nginx terminates TLS
|
|
85
|
-
- Admin-only scope maintained for all write endpoints
|
|
86
|
-
|
|
87
|
-
## Quality gates
|
|
88
|
-
|
|
89
|
-
- Build: PASS
|
|
90
|
-
- Lint: PASS
|
|
91
|
-
- Tests: PASS (310 e2e tests)
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
Compare URL (after pushing this branch):
|
|
96
|
-
|
|
97
|
-
https://github.com/homebridge/homebridge-config-ui-x/compare/beta-5.8.1...ssl-upload
|
|
98
|
-
|
|
99
|
-
Suggested PR title:
|
|
100
|
-
|
|
101
|
-
feat(settings/https): upload + validate SSL (PEM/PFX) and generate self-signed from UI
|
|
102
|
-
|
|
103
|
-
Suggested labels: `feature`, `settings`, `security`, `beta`
|
package/public/assets/matter.svg
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<svg height="512px" width="512px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<g transform="translate(256, 280) scale(1.37) translate(-256, -256)">
|
|
3
|
-
<path
|
|
4
|
-
fill="currentColor"
|
|
5
|
-
d="M256 212.26666666666665c-21.333333333333332 0 -41.421866666666666 -3.8218666666666667 -60.266666666666666 -11.466666666666665 -18.8448 -7.6448 -35.733333333333334 -18.0448 -50.666666666666664 -31.199999999999996l29.333333333333332 -17.599999999999998c8.533333333333333 6.3999999999999995 18.311466666666664 12 29.333333333333332 16.799999999999997 11.021866666666668 4.8 23.111466666666665 8.266666666666666 36.266666666666666 10.399999999999999V62.93333333333334L256 53.33333333333333l16 9.6v116.26666666666667c12.799999999999999 -2.1333333333333333 24.8 -5.6 36 -10.399999999999999 11.2 -4.8 21.066666666666666 -10.399999999999999 29.599999999999998 -16.799999999999997l29.333333333333332 17.599999999999998c-14.933333333333332 13.1552 -31.821866666666665 23.5552 -50.666666666666664 31.199999999999996C297.42186666666663 208.4448 277.3333333333333 212.26666666666665 256 212.26666666666665Zm-35.199999999999996 211.73333333333335 -29.866666666666664 -16c0.3552 -2.8447999999999998 0.6218666666666666 -5.511466666666667 0.7999999999999999 -8 0.1781333333333333 -2.4885333333333333 0.26666666666666666 -5.1552 0.26666666666666666 -8 0 -8.533333333333333 -0.9781333333333333 -17.511466666666664 -2.9333333333333336 -26.93333333333333 -1.9551999999999998 -9.421866666666666 -4.7114666666666665 -18.7552 -8.266666666666666 -28l-100.26666666666667 60.8L64 388.8V370.1333333333333l99.73333333333332 -60.266666666666666c-8.178133333333333 -9.6 -17.1552 -18.0448 -26.93333333333333 -25.333333333333332 -9.778133333333333 -7.2885333333333335 -19.821866666666665 -12.888533333333331 -30.133333333333333 -16.799999999999997v-34.13333333333333c34.13333333333333 10.311466666666666 62.22186666666667 29.955199999999998 84.26666666666667 58.93333333333334 22.0448 28.978133333333332 33.06666666666666 62.13333333333333 33.06666666666666 99.46666666666665 0 5.688533333333333 -0.26666666666666666 11.2 -0.7999999999999999 16.53333333333333 -0.5333333333333333 5.333333333333333 -1.3333333333333333 10.488533333333333 -2.4 15.466666666666665Zm70.39999999999999 0c-1.0666666666666667 -4.978133333333333 -1.8666666666666665 -10.133333333333333 -2.4 -15.466666666666665 -0.5333333333333333 -5.333333333333333 -0.7999999999999999 -10.8448 -0.7999999999999999 -16.53333333333333 0 -37.33333333333333 11.021866666666668 -70.48853333333332 33.06666666666666 -99.46666666666665 22.0448 -28.978133333333332 50.13333333333333 -48.62186666666666 84.26666666666667 -58.93333333333334v34.13333333333333c-10.311466666666666 3.9114666666666666 -20.3552 9.511466666666667 -30.133333333333333 16.799999999999997 -9.778133333333333 7.2885333333333335 -18.7552 15.733333333333334 -26.93333333333333 25.333333333333332L448 370.1333333333333v18.666666666666664l-16.53333333333333 9.066666666666666 -100.26666666666667 -60.8c-3.5551999999999997 9.2448 -6.311466666666666 18.578133333333334 -8.266666666666666 28 -1.9551999999999998 9.421866666666666 -2.9333333333333336 18.4 -2.9333333333333336 26.93333333333333 0 2.8447999999999998 0.08853333333333332 5.511466666666667 0.26666666666666666 8 0.1781333333333333 2.4885333333333333 0.4448 5.1552 0.7999999999999999 8l-29.866666666666664 16Z"
|
|
6
|
-
/>
|
|
7
|
-
</g>
|
|
8
|
-
</svg>
|
package/public/chunk-2V4LIZUH.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as Ce}from"./chunk-MDHWQ5V2.js";import{a as we}from"./chunk-WVG4A2SW.js";import{n as R}from"./chunk-PRGOUVRO.js";import{g as ie,m as F,n as xe}from"./chunk-OF2SRCQW.js";import{c as be}from"./chunk-EWS4VSZB.js";import{a as fe}from"./chunk-Z5PUNNUZ.js";import{b as K}from"./chunk-KTQBVBY4.js";import{B as ve,C as G,D as z,b as W,c as N,d as g,e as j,f as X,g as P,j as E,m as Y,q as Z,r as ee,s as te}from"./chunk-TPUA4UIN.js";import{a as Te,b as _e}from"./chunk-OBV66CW4.js";import{E as D,F as U,H as I,K as V}from"./chunk-J7UDRE66.js";import{Ab as h,Cb as oe,Db as se,Dc as o,Eb as le,Ec as s,Fb as y,Fc as ue,Gb as a,Hb as i,Ib as c,Jb as $,Kb as k,Lb as q,Rb as A,Sb as H,Xb as _,Yb as L,Za as e,Zb as f,aa as p,ea as ae,ga as B,ha as M,ic as O,jc as me,kc as m,lc as d,ma as re,mb as Q,mc as v,nb as S,nc as de,sa as T,xc as pe,yb as u,yc as ce,zb as w}from"./chunk-HUTYOGRH.js";import{g as Ue,h as C}from"./chunk-B4AJQJMI.js";function ke(n,x){n&1&&(a(0,"ngb-alert",13)(1,"p",20),m(2),o(3,"translate"),i()()),n&2&&(y("dismissible",!1),e(2),d(s(3,2,"login.invalid_password")))}var he=(()=>{class n{$activeModal=p(F);$api=p(V);$toastr=p(I);$translate=p(D);invalidCredentials=T(!1,{});formGroup=new P({password:new E("",[g.required])});disable2fa(){return C(this,null,function*(){this.invalidCredentials.set(!1);try{yield this.$api.post("/users/otp/deactivate",this.formGroup.value),this.$activeModal.close(),this.$toastr.success(this.$translate.instant("users.setup_2fa_disable_success"),this.$translate.instant("toast.title_success"))}catch{this.formGroup.setValue({password:""}),this.invalidCredentials.set(!0)}})}dismissModal(){this.$activeModal.dismiss("Dismiss")}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["ng-component"]],decls:34,vars:27,consts:[[1,"modal-content",2,"min-height","300px"],[1,"modal-header"],[1,"modal-title"],["type","button","data-bs-dismiss","modal",1,"btn-close",3,"click"],[1,"modal-body"],[1,"text-center","mb-3"],[1,"fas","fa-key","primary-text",2,"font-size","75px"],[1,"mb-3"],[1,"list-group","list-group-box","mb-0"],[1,"list-group-item","d-flex","flex-column","flex-md-row","align-items-center"],[1,"mb-2","mb-md-0","w-100","w-md-50"],[1,"text-start","text-md-end","w-100","w-md-50"],["type","password","autocomplete","current-password",1,"form-control","custom-input",3,"formControl","placeholder"],["type","error",1,"mt-3","mb-0",3,"dismissible"],[1,"modal-footer","justify-content-between"],[1,"text-start"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant",3,"click"],[1,"text-center"],[1,"text-end"],["type","button","data-bs-dismiss","modal",1,"btn","btn-danger",3,"click","disabled"],[1,"mb-0"]],template:function(l,r){l&1&&(a(0,"div",0)(1,"div",1)(2,"h5",2),m(3),o(4,"translate"),i(),a(5,"button",3),o(6,"translate"),_("click",function(){return r.dismissModal()}),i()(),a(7,"div",4)(8,"div",5),c(9,"i",6),i(),a(10,"ul",7)(11,"li"),m(12),o(13,"translate"),i()(),a(14,"ul",8)(15,"li",9)(16,"label",10),m(17),o(18,"translate"),i(),a(19,"div",11),c(20,"input",12),o(21,"translate"),i()()(),w(22,ke,4,4,"ngb-alert",13),i(),a(23,"div",14)(24,"div",15)(25,"button",16),o(26,"translate"),_("click",function(){return r.dismissModal()}),m(27),o(28,"translate"),i()(),c(29,"div",17),a(30,"div",18)(31,"button",19),_("click",function(){return r.disable2fa()}),m(32),o(33,"translate"),i()()()()),l&2&&(e(3),d(s(4,11,"users.setup_2fa_disable")),e(2),u("aria-label",s(6,13,"form.button_close")),e(7),d(s(13,15,"users.setup_2fa_disable_current_password")),e(5),d(s(18,17,"users.label_password")),e(3),y("formControl",r.formGroup.controls.password)("placeholder",s(21,19,"users.label_password")),e(2),h(r.invalidCredentials()&&!r.formGroup.controls.password.value?22:-1),e(3),u("aria-label",s(26,21,"form.button_close")),e(2),v(" ",s(28,23,"form.button_close")," "),e(4),y("disabled",r.formGroup.invalid),e(),v(" ",s(33,25,"form.button_disable")," "))},dependencies:[G,N,j,z,Z,ie,U],encapsulation:2})}return n})();var ge=Ue(Te(),1);var Be=n=>({timeDiffError:n});function Me(n,x){n&1&&(a(0,"li"),m(1),o(2,"translate"),i()),n&2&&(e(),d(s(2,1,"users.setup_2fa_scan_qr_code")))}function De(n,x){if(n&1&&(a(0,"ngb-alert",8)(1,"p",16),m(2),o(3,"translate"),i(),a(4,"p",17),m(5),o(6,"translate"),i()()),n&2){let t=f();y("dismissible",!1),e(2),d(s(3,3,"users.setup_2fa_cannot_setup_2fa")),e(3),d(ue(6,5,"users.setup_2fa_server_time_out",pe(8,Be,t.timeDiffError())))}}function Ie(n,x){n&1&&c(0,"i",27)}function Ve(n,x){n&1&&c(0,"i",28)}function Fe(n,x){if(n&1){let t=A();a(0,"div",21),m(1),o(2,"translate"),c(3,"br"),m(4),a(5,"a",26),_("click",function(){B(t);let r=f(2);return M(r.copySecretToClipboard())}),w(6,Ie,1,0,"i",27)(7,Ve,1,0,"i",28),i()()}if(n&2){let t=f(2);e(),v(" ",s(2,3,"users.setup_2fa_scan_qr_manual")),e(3),v(" ",t.otpSecret()," "),e(2),h(t.secretCopied()?6:7)}}function Ae(n,x){if(n&1&&(a(0,"ul",9)(1,"li",18)(2,"div",19),c(3,"app-qrcode",20),i(),w(4,Fe,8,5,"div",21),i(),a(5,"li",22)(6,"label",23),m(7),o(8,"translate"),i(),a(9,"div",24),c(10,"input",25),i()()()),n&2){let t=f();e(3),y("data",t.otpString()),e(),h(t.otpSecret()?4:-1),e(3),d(s(8,4,"users.setup_2fa_enter_code")),e(3),y("formControl",t.formGroup.controls.code)}}function Ne(n,x){if(n&1){let t=A();a(0,"button",29),o(1,"translate"),_("click",function(){B(t);let r=f();return M(r.dismissModal())}),m(2),o(3,"translate"),i()}n&2&&(u("aria-label",s(1,2,"form.button_close")),e(2),v(" ",s(3,4,"form.button_close")," "))}function je(n,x){if(n&1){let t=A();a(0,"button",29),o(1,"translate"),_("click",function(){B(t);let r=f();return M(r.dismissModal())}),m(2),o(3,"translate"),i()}n&2&&(u("aria-label",s(1,2,"form.button_close")),e(2),v(" ",s(3,4,"form.button_close")," "))}function Pe(n,x){if(n&1){let t=A();a(0,"button",30),_("click",function(){B(t);let r=f();return M(r.enable2fa())}),m(1),o(2,"translate"),i()}if(n&2){let t=f();y("disabled",t.formGroup.invalid),e(),v(" ",s(2,2,"form.button_enable")," ")}}var Ee=(()=>{class n{$activeModal=p(F);$api=p(V);$toastr=p(I);$translate=p(D);modalData=p(R);user=this.modalData.user;timeDiffError=T(null,{});otpString=T(void 0,{});otpSecret=T(void 0,{});secretCopied=T(!1,{});copyTimeout=null;formGroup=new P({code:new E("",[g.required,g.minLength(6),g.maxLength(6)])});ngOnInit(){this.initialize()}initialize(){return C(this,null,function*(){try{let t=yield this.$api.post("/users/otp/setup",{});this.checkTimeDiff(t.timestamp),this.timeDiffError()||(this.otpString.set(t.otpauth),this.otpSecret.set(new URL(t.otpauth).searchParams.get("secret")||""))}catch(t){this.$activeModal.dismiss(),console.error(t),this.$toastr.error(this.$translate.instant("users.setup_2fa_enable_error"),this.$translate.instant("toast.title_error"))}})}enable2fa(){return C(this,null,function*(){try{yield this.$api.post("/users/otp/activate",this.formGroup.value),this.$toastr.success(this.$translate.instant("users.setup_2fa_enabled_success"),this.$translate.instant("toast.title_success")),this.$activeModal.close()}catch(t){console.error(t),this.$toastr.error(this.$translate.instant("users.setup_2fa_activate_error"),this.$translate.instant("toast.title_error"))}})}copySecretToClipboard(){return C(this,null,function*(){let t=this.otpSecret();t&&(yield navigator.clipboard.writeText(t),this.secretCopied.set(!0),this.copyTimeout&&clearTimeout(this.copyTimeout),this.copyTimeout=setTimeout(()=>{this.secretCopied.set(!1)},3e3))})}dismissModal(){this.$activeModal.dismiss("Dismiss")}checkTimeDiff(t){let l=(0,ge.default)(t).diff(new Date,"millisecond");l<-5e3||l>5e3?this.timeDiffError.set(l):this.timeDiffError.set(null)}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["ng-component"]],decls:24,vars:15,consts:[[1,"modal-content","setup2fa"],[1,"modal-header"],[1,"modal-title"],["type","button","data-bs-dismiss","modal",1,"btn-close",3,"click"],[1,"modal-body"],[1,"text-center","mb-3"],[1,"fas","fa-key","primary-text",2,"font-size","75px"],[1,"mb-3"],["type","error",1,"mb-0",3,"dismissible"],[1,"list-group","list-group-box","mb-0"],[1,"modal-footer","justify-content-between"],[1,"text-start"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant"],[1,"text-center"],[1,"text-end"],["type","button","data-bs-dismiss","modal",1,"btn","btn-primary",3,"disabled"],[1,"fw-bold","mb-1"],[1,"mb-0"],[1,"list-group-item","text-center"],[1,"text-center","w-100","d-flex","justify-content-center","my-2"],[1,"mx-auto",2,"width","175px","height","175px",3,"data"],[1,"grey-text","small","mt-0","mb-1"],[1,"list-group-item","d-flex","flex-column","flex-md-row","align-items-center"],[1,"mb-2","mb-md-0","w-100","w-md-50"],[1,"text-start","text-md-end","w-100","w-md-50"],["type","text","placeholder","eg. 123456","autocomplete","one-time-code","autocapitalize","none","inputmode","numeric","pattern","[0-9]*",1,"form-control","custom-input",3,"formControl"],["href","javascript:void(0)",1,"ms-1","text-primary",3,"click"],[1,"fas","fa-check","green-text"],[1,"fas","fa-copy"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant",3,"click"],["type","button","data-bs-dismiss","modal",1,"btn","btn-primary",3,"click","disabled"]],template:function(l,r){l&1&&(a(0,"div",0)(1,"div",1)(2,"h5",2),m(3),o(4,"translate"),i(),a(5,"button",3),o(6,"translate"),_("click",function(){return r.dismissModal()}),i()(),a(7,"div",4)(8,"div",5),c(9,"i",6),i(),a(10,"ul",7)(11,"li"),m(12),o(13,"translate"),i(),w(14,Me,3,3,"li"),i(),w(15,De,7,10,"ngb-alert",8),w(16,Ae,11,6,"ul",9),i(),a(17,"div",10)(18,"div",11),w(19,Ne,4,6,"button",12),i(),a(20,"div",13),w(21,je,4,6,"button",12),i(),a(22,"div",14),w(23,Pe,3,4,"button",15),i()()()),l&2&&(e(3),d(s(4,9,"users.setup_2fa")),e(2),u("aria-label",s(6,11,"form.button_close")),e(7),d(s(13,13,"users.setup_2fa_warning")),e(2),h(r.timeDiffError()?-1:14),e(),h(r.timeDiffError()?15:-1),e(),h(r.timeDiffError()?-1:16),e(3),h(r.timeDiffError()?-1:19),e(2),h(r.timeDiffError()?21:-1),e(2),h(r.timeDiffError()?-1:23))},dependencies:[ie,Ce,G,N,j,ve,z,Z,U],encapsulation:2})}return n})();var ye=(()=>{class n{$activeModal=p(F);$api=p(V);$toastr=p(I);$translate=p(D);form=new P({username:new E("",[g.required]),name:new E("",[g.required]),password:new E("",[g.compose([g.required,g.minLength(4)])]),passwordConfirm:new E("",[g.required]),admin:new E(!0)},this.matchPassword);onSubmit(l){return C(this,arguments,function*({value:t}){try{yield this.$api.post("/users",t),this.$activeModal.close(),this.$toastr.success(this.$translate.instant("users.toast_added_new_user"),this.$translate.instant("toast.title_success"))}catch(r){this.$toastr.error(r.error.message||this.$translate.instant("users.toast_failed_to_add_user"),this.$translate.instant("toast.title_error"))}})}dismissModal(){this.$activeModal.dismiss("Dismiss")}matchPassword(t){let l=t.get("password")?.value,r=t.get("passwordConfirm")?.value;return l!==r?(t.get("passwordConfirm")?.setErrors({matchPassword:!0}),{matchPassword:!0}):null}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["ng-component"]],decls:58,vars:48,consts:[[1,"modal-content"],["novalidate","",3,"ngSubmit","formGroup"],[1,"modal-header"],[1,"modal-title"],["type","button","data-bs-dismiss","modal",1,"btn-close",3,"click"],[1,"modal-body"],[1,"text-center","mb-3"],[1,"fas","fa-user-plus","primary-text",2,"font-size","75px"],[1,"list-group","list-group-box","mb-0"],[1,"list-group-item","d-flex","flex-column","flex-md-row","align-items-center"],["for","form-username",1,"mb-2","mb-md-0","w-100","w-md-50"],[1,"text-start","text-md-end","w-100","w-md-50"],["formControlName","username","type","text","id","form-username","autocomplete","username","autocapitalize","none",1,"form-control","custom-input"],["for","form-name",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","name","type","text","id","form-name","autocomplete","name",1,"form-control","custom-input"],["for","form-pass",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","password","type","password","autocomplete","new-password","id","form-pass",1,"form-control","custom-input"],["for","form-pass-confirm",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","passwordConfirm","type","password","autocomplete","new-password","id","form-pass-confirm",1,"form-control","custom-input"],[1,"list-group-item","d-flex","justify-content-between","align-items-center","flex-row","pb-2"],[1,"text-start"],[1,"text-end","grey-text","d-flex","align-items-center"],["formControlName","admin","type","checkbox","id","isAdmin",1,"rendux-input"],["for","isAdmin",1,"rendux-label","ms-3"],[1,"modal-footer","justify-content-between"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant",3,"click"],[1,"text-center"],[1,"text-end"],["type","submit",1,"btn","btn-primary",3,"disabled"]],template:function(l,r){l&1&&(a(0,"div",0)(1,"form",1),_("ngSubmit",function(){return r.onSubmit(r.form)}),a(2,"div",2)(3,"h5",3),m(4),o(5,"translate"),i(),a(6,"button",4),o(7,"translate"),_("click",function(){return r.dismissModal()}),i()(),a(8,"div",5)(9,"div",6),c(10,"i",7),i(),a(11,"ul",8)(12,"li",9)(13,"label",10),m(14),o(15,"translate"),i(),a(16,"div",11),c(17,"input",12),o(18,"translate"),i()(),a(19,"li",9)(20,"label",13),m(21),o(22,"translate"),i(),a(23,"div",11),c(24,"input",14),o(25,"translate"),i()(),a(26,"li",9)(27,"label",15),m(28),o(29,"translate"),i(),a(30,"div",11),c(31,"input",16),o(32,"translate"),i()(),a(33,"li",9)(34,"label",17),m(35),o(36,"translate"),i(),a(37,"div",11),c(38,"input",18),o(39,"translate"),i()(),a(40,"li",19)(41,"span",20),m(42),o(43,"translate"),i(),a(44,"div",21),c(45,"input",22),o(46,"translate"),c(47,"label",23),i()()()(),a(48,"div",24)(49,"div",20)(50,"button",25),_("click",function(){return r.dismissModal()}),m(51),o(52,"translate"),i()(),c(53,"div",26),a(54,"div",27)(55,"button",28),m(56),o(57,"translate"),i()()()()()),l&2&&(e(),y("formGroup",r.form),e(3),d(s(5,20,"users.title_add_user")),e(2),u("aria-label",s(7,22,"form.button_close")),e(8),d(s(15,24,"users.label_username")),e(3),u("aria-label",s(18,26,"users.label_username")),e(4),d(s(22,28,"users.label_full_name")),e(3),u("aria-label",s(25,30,"users.label_full_name")),e(4),d(s(29,32,"users.label_password")),e(3),O("is-invalid",r.form.controls.password.dirty&&r.form.controls.password.errors),u("aria-label",s(32,34,"users.label_password")),e(4),d(s(36,36,"users.label_confirm_password")),e(3),O("is-invalid",r.form.controls.passwordConfirm.dirty&&r.form.controls.passwordConfirm.errors),u("aria-label",s(39,38,"users.label_confirm_password")),e(4),d(s(43,40,"users.label_admin_user")),e(3),u("aria-label",s(46,42,"users.label_admin_user")),e(6),v(" ",s(52,44,"form.button_close")," "),e(4),y("disabled",!r.form.valid),e(),v(" ",s(57,46,"form.button_save")," "))},dependencies:[G,Y,N,W,j,X,z,te,ee,U],encapsulation:2})}return n})();function Ge(n,x){if(n&1&&(a(0,"li",9)(1,"label",27),m(2),o(3,"translate"),i(),a(4,"div",11),c(5,"input",28),o(6,"translate"),i()()),n&2){let t=f();e(2),d(s(3,4,"users.label_confirm_password")),e(3),O("is-invalid",t.form.controls.passwordConfirm.dirty&&t.form.controls.passwordConfirm.errors),u("aria-label",s(6,6,"users.label_confirm_password"))}}var Se=(()=>{class n{$activeModal=p(F);$api=p(V);$auth=p(K);$toastr=p(I);$translate=p(D);modalData=p(R);user=this.modalData.user;isCurrentUser=T(!1,{});initialFormValue={};form=new P({username:new E("",[g.required]),name:new E("",[g.required]),password:new E(""),passwordConfirm:new E(""),admin:new E(!0)},this.matchPassword);ngOnInit(){this.user&&(this.isCurrentUser.set(this.$auth.user.username===this.user.username),this.form.patchValue(this.user),this.initialFormValue=this.form.getRawValue())}onSubmit(l){return C(this,arguments,function*({value:t}){if(this.user)try{yield this.$api.patch(`/users/${this.user.id}`,t),this.$activeModal.close(),this.$toastr.success(this.$translate.instant("users.toast_updated_user"),this.$translate.instant("toast.title_success")),this.isCurrentUser()&&t.username!==this.$auth.user.username&&this.$auth.logout()}catch(r){console.error(r),this.$toastr.error(r.error?.message||this.$translate.instant("users.toast_failed_to_add_user"),this.$translate.instant("toast.title_error"))}})}isFormUnchanged(){return this.form.controls.password.value?!1:JSON.stringify(this.form.getRawValue())===JSON.stringify(this.initialFormValue)}dismissModal(){this.$activeModal.dismiss("Dismiss")}matchPassword(t){let l=t.get("password")?.value,r=t.get("passwordConfirm")?.value;return l!==r?(t.get("passwordConfirm")?.setErrors({matchPassword:!0}),{matchPassword:!0}):null}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["ng-component"]],decls:52,vars:41,consts:[[1,"modal-content"],["novalidate","",3,"ngSubmit","formGroup"],[1,"modal-header"],[1,"modal-title"],["type","button","data-bs-dismiss","modal",1,"btn-close",3,"click"],[1,"modal-body"],[1,"text-center","mb-3"],[1,"fas","fa-user-pen","primary-text",2,"font-size","75px"],[1,"list-group","list-group-box","mb-0"],[1,"list-group-item","d-flex","flex-column","flex-md-row","align-items-center"],["for","form-username",1,"mb-2","mb-md-0","w-100","w-md-50"],[1,"text-start","text-md-end","w-100","w-md-50"],["formControlName","username","type","text","id","form-username","autocomplete","off","autocapitalize","none",1,"form-control","custom-input"],["for","form-name",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","name","type","text","id","form-name","autocomplete","name",1,"form-control","custom-input"],["for","form-pass",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","password","type","password","autocomplete","new-password","id","form-pass",1,"form-control","custom-input"],[1,"list-group-item","d-flex","justify-content-between","align-items-center","flex-row","pb-2"],[1,"text-start"],[1,"text-end","grey-text","d-flex","align-items-center"],["type","checkbox","formControlName","admin","id","isAdmin",1,"rendux-input"],["for","isAdmin",1,"rendux-label","ms-3"],[1,"modal-footer","justify-content-between"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant",3,"click"],[1,"text-center"],[1,"text-end"],["type","submit",1,"btn","btn-primary",3,"disabled"],["for","form-pass-confirm",1,"mb-2","mb-md-0","w-100","w-md-50"],["formControlName","passwordConfirm","type","password","autocomplete","new-password","id","form-pass-confirm",1,"form-control","custom-input"]],template:function(l,r){l&1&&(a(0,"div",0)(1,"form",1),_("ngSubmit",function(){return r.onSubmit(r.form)}),a(2,"div",2)(3,"h5",3),m(4),o(5,"translate"),i(),a(6,"button",4),o(7,"translate"),_("click",function(){return r.dismissModal()}),i()(),a(8,"div",5)(9,"div",6),c(10,"i",7),i(),a(11,"ul",8)(12,"li",9)(13,"label",10),m(14),o(15,"translate"),i(),a(16,"div",11),c(17,"input",12),o(18,"translate"),i()(),a(19,"li",9)(20,"label",13),m(21),o(22,"translate"),i(),a(23,"div",11),c(24,"input",14),o(25,"translate"),i()(),a(26,"li",9)(27,"label",15),m(28),o(29,"translate"),i(),a(30,"div",11),c(31,"input",16),o(32,"translate"),i()(),w(33,Ge,7,8,"li",9),a(34,"li",17)(35,"span",18),m(36),o(37,"translate"),i(),a(38,"div",19),c(39,"input",20),o(40,"translate"),c(41,"label",21),i()()()(),a(42,"div",22)(43,"div",18)(44,"button",23),_("click",function(){return r.dismissModal()}),m(45),o(46,"translate"),i()(),c(47,"div",24),a(48,"div",25)(49,"button",26),m(50),o(51,"translate"),i()()()()()),l&2&&(e(),y("formGroup",r.form),e(3),d(s(5,17,"users.title_edit_user")),e(2),u("aria-label",s(7,19,"form.button_close")),e(8),d(s(15,21,"users.label_username")),e(3),u("aria-label",s(18,23,"users.label_username")),e(4),d(s(22,25,"users.label_full_name")),e(3),u("aria-label",s(25,27,"users.label_full_name")),e(4),d(s(29,29,"users.label_new_password")),e(3),O("is-invalid",r.form.controls.password.dirty&&r.form.controls.password.errors),u("aria-label",s(32,31,"users.label_new_password")),e(2),h(r.form.controls.password.value?33:-1),e(3),d(s(37,33,"users.label_admin_user")),e(3),u("aria-label",s(40,35,"users.label_admin_user")),e(6),v(" ",s(46,37,"form.button_close")," "),e(4),y("disabled",!r.form.valid||r.isFormUnchanged()),e(),v(" ",s(51,39,"form.button_save")," "))},dependencies:[G,Y,N,W,j,X,z,te,ee,U],encapsulation:2})}return n})();var $e=(()=>{class n{$activeModal=p(F);dismissModal(){this.$activeModal.dismiss("Dismiss")}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["ng-component"]],decls:46,vars:36,consts:[[1,"modal-content"],[1,"modal-header"],[1,"modal-title"],["type","button","data-bs-dismiss","modal",1,"btn-close",3,"click"],[1,"modal-body"],[1,"text-center","mb-3"],[1,"fa","fa-question-circle-o","primary-text",2,"font-size","75px"],[1,"ms-2"],[1,"mb-0"],[1,"mb-2","grey-text"],[1,"modal-footer","justify-content-between"],[1,"text-start"],[1,"text-center"],["type","button","data-bs-dismiss","modal",1,"btn","btn-elegant",3,"click"],[1,"text-end"]],template:function(l,r){l&1&&(a(0,"div",0)(1,"form")(2,"div",1)(3,"h5",2),m(4),o(5,"translate"),i(),a(6,"button",3),o(7,"translate"),_("click",function(){return r.dismissModal()}),i()(),a(8,"div",4)(9,"div",5),c(10,"i",6),i(),a(11,"h5",7),m(12),o(13,"translate"),i(),a(14,"ul",8)(15,"li"),m(16),o(17,"translate"),i(),a(18,"ul",9)(19,"li"),m(20),o(21,"translate"),i(),a(22,"li"),m(23),o(24,"translate"),i(),a(25,"li"),m(26),o(27,"translate"),o(28,"translate"),i(),a(29,"li"),m(30),o(31,"translate"),i(),a(32,"li"),m(33),o(34,"translate"),i()(),a(35,"li"),m(36),o(37,"translate"),i()(),c(38,"app-support-banner"),i(),a(39,"div",10),c(40,"div",11),a(41,"div",12)(42,"button",13),_("click",function(){return r.dismissModal()}),m(43),o(44,"translate"),i()(),c(45,"div",14),i()()()),l&2&&(e(4),d(s(5,12,"support.title")),e(2),u("aria-label",s(7,14,"form.button_close")),e(6),d(s(13,16,"users.title_users")),e(4),d(s(17,18,"users.support.acc_1")),e(4),d(s(21,20,"menu.label_status")),e(3),d(s(24,22,"menu.label_plugins")),e(3),de("",s(27,24,"menu.label_accessories")," (",s(28,26,"users.support.control"),")"),e(4),d(s(31,28,"menu.linux.label_logs")),e(3),d(s(34,30,"support.title")),e(3),d(s(37,32,"users.support.acc_2")),e(7),v(" ",s(44,34,"form.button_close")," "))},dependencies:[we,U],encapsulation:2})}return n})();var ze=(n,x)=>({"fa-user-secret":n,"fa-user":x});function Re(n,x){if(n&1){let t=A();$(0,"button",23),L("click",function(){B(t);let r=f(2).$implicit,b=f();return M(b.disable2fa(r))}),m(1),o(2,"translate"),k()}if(n&2){let t=f(2).$implicit,l=f();H("disabled",t.username!==l.username),e(),v(" ",s(2,2,"users.setup_2fa_disable")," ")}}function qe(n,x){if(n&1){let t=A();$(0,"button",24),L("click",function(){B(t);let r=f(2).$implicit,b=f();return M(b.setup2fa(r))}),m(1),o(2,"translate"),k()}if(n&2){let t=f(2).$implicit,l=f();H("disabled",t.username!==l.username),e(),v(" ",s(2,2,"users.setup_2fa")," ")}}function Le(n,x){if(n&1&&w(0,Re,3,4,"button",21)(1,qe,3,4,"button",22),n&2){let t=f().$implicit;h(t.otpActive?0:1)}}function Oe(n,x){n&1&&q(0,"div")}function Je(n,x){if(n&1){let t=A();$(0,"div",9)(1,"div",10)(2,"div",11)(3,"div",12)(4,"span",13)(5,"h4"),m(6),k(),$(7,"h5",14),q(8,"i",15),m(9),k()()(),$(10,"div",16),w(11,Le,2,1)(12,Oe,1,0,"div"),$(13,"div")(14,"button",17),o(15,"translate"),L("click",function(){let r=B(t).$implicit,b=f();return M(b.openEditUser(r))}),q(16,"i",18),k(),$(17,"button",19),o(18,"translate"),L("click",function(){let r=B(t).$implicit,b=f();return M(b.deleteUser(r.id))}),q(19,"i",20),k()()()()()()}if(n&2){let t=x.$implicit,l=f();e(6),d(t.name),e(2),me(ce(12,ze,t.admin,!t.admin)),e(),v(" ",t.username," "),e(2),h(t.admin?11:12),e(3),u("aria-label",s(15,8,"form.button_edit")),e(3),H("disabled",l.username===t.username),u("aria-label",s(18,10,"form.button_delete"))}}var ti=(()=>{class n{injector=p(ae);destroyRef=p(re);$api=p(V);$auth=p(K);$modal=p(xe);$route=p(be);$settings=p(_e);$toastr=p(I);$translate=p(D);homebridgeUsers=T([],{});username=this.$auth.user.username;isAdmin=this.$auth.user.admin;ngOnInit(){let t=this.$translate.instant("users.title_users");this.$settings.setPageTitle(t),this.$route.data.pipe(fe(this.destroyRef)).subscribe(l=>{this.homebridgeUsers.set(l.homebridgeUsers)})}reloadUsers(){return C(this,null,function*(){let t=yield this.$api.get("/users");this.homebridgeUsers.set(t)})}openAddNewUser(){return C(this,null,function*(){let t=this.$modal.open(ye,{size:"lg",backdrop:"static"});try{yield t.result,this.reloadUsers()}catch{}})}openEditUser(t){return C(this,null,function*(){let l=Q([{provide:R,useValue:{user:t}}],this.injector),r=this.$modal.open(Se,{size:"lg",backdrop:"static",injector:l});try{yield r.result,this.reloadUsers()}catch{}})}deleteUser(t){return C(this,null,function*(){try{yield this.$api.delete(`/users/${t}`),this.$toastr.success(this.$translate.instant("users.toast_user_deleted"),this.$translate.instant("toast.title_success")),this.reloadUsers()}catch(l){console.error(l),this.$toastr.error(l.error?.message||this.$translate.instant("users.toast_failed_to_delete_user"),this.$translate.instant("toast.title_error"))}})}setup2fa(t){return C(this,null,function*(){let l=Q([{provide:R,useValue:{user:t}}],this.injector),r=this.$modal.open(Ee,{size:"lg",backdrop:"static",injector:l});try{yield r.result,this.reloadUsers()}catch{}})}disable2fa(t){return C(this,null,function*(){let l=Q([{provide:R,useValue:{user:t}}],this.injector),r=this.$modal.open(he,{size:"lg",backdrop:"static",injector:l});try{yield r.result,this.reloadUsers()}catch{}})}openSupport(){this.$modal.open($e,{size:"lg",backdrop:"static"})}static \u0275fac=function(l){return new(l||n)};static \u0275cmp=S({type:n,selectors:[["app-users"]],decls:15,vars:9,consts:[[1,"row","mb-3"],[1,"col-6"],[1,"primary-text","m-0"],[1,"col-6","text-end"],[1,"btn","btn-elegant","waves-effect","m-0","me-2",3,"click"],[1,"fas","fa-user-plus"],["type","button",1,"btn","btn-elegant","my-0","me-0",3,"click"],[1,"fa","fa-question-circle-o"],[1,"row"],[1,"col-md-6","mb-4"],[1,"card","card-body"],[1,"d-flex","flex-column"],[1,"d-flex","flex-row","mb-3"],[1,"me-auto","my-0"],[1,"small","text-truncate","grey-text"],[1,"fas"],[1,"d-flex","flex-row","justify-content-between"],[1,"btn","btn-elegant","m-0",3,"click"],[1,"fas","fa-user-pen"],[1,"btn","btn-danger","m-0","ms-2",3,"click","disabled"],[1,"fas","fa-trash"],[1,"btn","btn-elegant","m-0",3,"disabled"],[1,"btn","btn-primary","m-0",3,"disabled"],[1,"btn","btn-elegant","m-0",3,"click","disabled"],[1,"btn","btn-primary","m-0",3,"click","disabled"]],template:function(l,r){l&1&&($(0,"div",0)(1,"div",1)(2,"h3",2),m(3),o(4,"translate"),k()(),$(5,"div",3)(6,"button",4),o(7,"translate"),L("click",function(){return r.openAddNewUser()}),q(8,"i",5),k(),$(9,"button",6),o(10,"translate"),L("click",function(){return r.openSupport()}),q(11,"i",7),k()()(),$(12,"div",8),se(13,Je,20,15,"div",9,oe),k()),l&2&&(e(3),d(s(4,3,"users.title_users")),e(3),u("aria-label",s(7,5,"users.button_add_user")),e(3),u("aria-label",s(10,7,"support.title")),e(4),le(r.homebridgeUsers()))},dependencies:[U],encapsulation:2})}return n})();export{ti as UsersComponent};
|