iobroker.eos-admin 7.9.98 → 7.9.99
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/INSTALL_TEST_V7.9.99.md +11 -0
- package/MERGE_UPDATE_README_V7.9.99.md +5 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +21 -20
- package/PUBLISH_STABLE_V7.9.99.md +15 -0
- package/README.md +6 -16
- package/README_STABILITY_V7.9.99.md +18 -0
- package/RELEASE_ACCEPTANCE_V7.9.99.md +11 -0
- package/RELEASE_NOTES_V7.9.99.md +9 -0
- package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
- package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
- package/adminWww/css/eos-ems-overview.css +3 -5
- package/adminWww/css/nexowatt-native-shell.css +26 -112
- package/adminWww/index.html +19 -18
- package/adminWww/js/eos-account-management.js +19 -28
- package/adminWww/js/eos-assistant.js +1 -1
- package/adminWww/js/eos-ems-overview.js +2 -3
- package/adminWww/js/eos-release-watch.js +77 -0
- package/adminWww/js/eos-role-bootstrap.js +11 -8
- package/adminWww/js/eos-role-ui.js +2 -2
- package/adminWww/js/nexowatt-native-shell.js +1 -1
- package/adminWww/static/js/nexowatt-stable-login.js +3 -3
- package/adminWww/static/js/{nexowatt-stable-v98.js → nexowatt-stable-v99.js} +2 -2
- package/build/lib/eosPassword.js +62 -0
- package/build/lib/eosRequestSecurity.js +31 -0
- package/build/lib/web.js +149 -154
- package/build/main.js +2 -1
- package/io-package.json +5 -10
- package/package.json +17 -13
- package/tools/nexowatt-account-management-selftest.cjs +19 -15
- package/tools/nexowatt-apply-ems-overview.cjs +2 -4
- package/tools/nexowatt-cache-update-selftest.cjs +11 -0
- package/tools/nexowatt-clean-legacy-runtime.cjs +1 -1
- package/tools/nexowatt-ems-overview-selftest.cjs +1 -1
- package/tools/nexowatt-login-layout-selftest.cjs +11 -11
- package/tools/nexowatt-merge-update-selftest.cjs +1 -1
- package/tools/nexowatt-modern-ui-selftest.cjs +2 -2
- package/tools/nexowatt-password-write-selftest.cjs +42 -44
- package/tools/nexowatt-request-security-selftest.cjs +11 -0
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +6 -6
- package/tools/nexowatt-stable-v99-selftest.cjs +25 -0
- package/tools/nexowatt-sync-release-version.cjs +1 -42
- package/tools/nexowatt-ui-overview-runtime-selftest.cjs +3 -1
- package/tools/nexowatt-validate-package.cjs +23 -23
- package/tools/nexowatt-version-sync-selftest.cjs +1 -18
- package/INSTALL_TEST_V7.9.98.md +0 -39
- package/MERGE_UPDATE_README_V7.9.98.md +0 -29
- package/PUBLISH_STABLE_V7.9.98.md +0 -33
- package/README_STABILITY_V7.9.98.md +0 -22
- package/RELEASE_ACCEPTANCE_V7.9.98.md +0 -48
- package/RELEASE_NOTES_V7.9.98.md +0 -32
- package/tools/nexowatt-account-password-flow-selftest.cjs +0 -186
- package/tools/nexowatt-stable-v98-selftest.cjs +0 -14
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
#eos-ems-overview-runtime { width: calc(100% - 32px); max-width: none; margin: 0 16px 20px; }
|
|
19
19
|
.eos-ems-overview-card * { box-sizing: border-box; }
|
|
20
|
-
.eos-ems-overview-header { display:flex; align-items:flex-start; justify-content:
|
|
20
|
+
.eos-ems-overview-header { display:flex; align-items:flex-start; justify-content:flex-start; gap:20px; }
|
|
21
21
|
.eos-ems-overview-header h2 { margin:5px 0 6px; font-size:clamp(1.2rem,2vw,1.65rem); color:#f5fbff; }
|
|
22
22
|
.eos-ems-overview-header p { margin:0; max-width:760px; color:#9eb2c2; line-height:1.45; }
|
|
23
23
|
.eos-ems-overview-eyebrow { color:#49e39e; font-size:.72rem; font-weight:900; letter-spacing:.13em; text-transform:uppercase; }
|
|
@@ -48,9 +48,7 @@
|
|
|
48
48
|
.eos-ems-overview-event time { color:#6f8799; font-size:.68rem; font-variant-numeric:tabular-nums; }
|
|
49
49
|
.eos-ems-overview-event div { display:grid; gap:2px; min-width:0; }
|
|
50
50
|
.eos-ems-overview-empty { color:#718797; font-size:.76rem; }
|
|
51
|
-
.eos-ems-overview-footer { display:flex; justify-content:
|
|
52
|
-
.eos-ems-overview-footer a { color:#5ee0c2; font-weight:800; text-decoration:none; }
|
|
53
|
-
.eos-ems-overview-footer a:hover,.eos-ems-overview-footer a:focus-visible { color:#9af2dd; text-decoration:underline; }
|
|
51
|
+
.eos-ems-overview-footer { display:flex; justify-content:flex-start; gap:12px; margin-top:12px; color:#60798a; font-size:.67rem; }
|
|
54
52
|
@media (max-width: 1100px) { .eos-ems-overview-card { flex-basis:100%; max-width:none; } }
|
|
55
53
|
@media (max-width: 760px) {
|
|
56
54
|
#eos-ems-overview-runtime,.eos-ems-overview-card { min-width:0; width:calc(100% - 16px); margin:8px; padding:16px; }
|
|
@@ -61,7 +59,7 @@
|
|
|
61
59
|
.eos-ems-overview-footer { display:grid; }
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
/*
|
|
62
|
+
/* v99: startup and compatibility messages stay compact. */
|
|
65
63
|
.eos-ems-overview-card.is-loading,
|
|
66
64
|
.eos-ems-overview-card.is-unavailable {
|
|
67
65
|
min-height: 0;
|
|
@@ -1292,7 +1292,7 @@ html.eos-route-intro #app-paper [class*="card"] {
|
|
|
1292
1292
|
.eos-assist-header-root .eos-assist-panel { right: 8px !important; width: calc(100vw - 16px); }
|
|
1293
1293
|
}
|
|
1294
1294
|
|
|
1295
|
-
/* NexoWatt EOS 7.9.
|
|
1295
|
+
/* NexoWatt EOS 7.9.99 – orderly login, moderately enlarged. */
|
|
1296
1296
|
html.eos-login,
|
|
1297
1297
|
html.eos-login body {
|
|
1298
1298
|
min-height: 100%;
|
|
@@ -1407,119 +1407,33 @@ html.eos-login form { max-width: 100% !important; }
|
|
|
1407
1407
|
html.eos-safe-overview-active #app-paper > :not(.MuiSnackbar-root):not([role="presentation"]) { pointer-events: auto !important; }
|
|
1408
1408
|
.eos-intro-admin-edit-hidden { display: none !important; }
|
|
1409
1409
|
|
|
1410
|
-
/* NexoWatt EOS 7.9.98 – stable scrolling, clean login fields and expandable errors. */
|
|
1411
|
-
html.eos-app,
|
|
1412
|
-
html.eos-app body,
|
|
1413
|
-
html.eos-app #root {
|
|
1414
|
-
height: 100% !important;
|
|
1415
|
-
max-height: 100% !important;
|
|
1416
|
-
overflow: hidden !important;
|
|
1417
|
-
}
|
|
1418
|
-
html.eos-app #app-paper {
|
|
1419
|
-
height: calc(100vh - var(--nx-content-top) - 12px) !important;
|
|
1420
|
-
height: calc(100dvh - var(--nx-content-top) - 12px) !important;
|
|
1421
|
-
min-height: 0 !important;
|
|
1422
|
-
max-height: calc(100vh - var(--nx-content-top) - 12px) !important;
|
|
1423
|
-
max-height: calc(100dvh - var(--nx-content-top) - 12px) !important;
|
|
1424
|
-
overflow-x: hidden !important;
|
|
1425
|
-
overflow-y: auto !important;
|
|
1426
|
-
overscroll-behavior: contain;
|
|
1427
|
-
scrollbar-gutter: stable;
|
|
1428
|
-
scrollbar-width: thin;
|
|
1429
|
-
scrollbar-color: rgba(0, 229, 138, .88) rgba(3, 18, 29, .72);
|
|
1430
|
-
}
|
|
1431
|
-
html.eos-app #app-paper::-webkit-scrollbar { width: 11px; }
|
|
1432
|
-
html.eos-app #app-paper::-webkit-scrollbar-track {
|
|
1433
|
-
border-radius: 12px;
|
|
1434
|
-
background: rgba(3, 18, 29, .72);
|
|
1435
|
-
}
|
|
1436
|
-
html.eos-app #app-paper::-webkit-scrollbar-thumb {
|
|
1437
|
-
min-height: 46px;
|
|
1438
|
-
border: 2px solid rgba(3, 18, 29, .72);
|
|
1439
|
-
border-radius: 12px;
|
|
1440
|
-
background: linear-gradient(180deg, #00e58a, #39c9ef);
|
|
1441
|
-
}
|
|
1442
|
-
html.eos-app #app-paper::-webkit-scrollbar-thumb:hover {
|
|
1443
|
-
background: linear-gradient(180deg, #21f2a1, #60dcff);
|
|
1444
|
-
}
|
|
1445
1410
|
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
border-radius: 24px !important;
|
|
1450
|
-
}
|
|
1451
|
-
html.eos-login main.MuiPaper-root > .eos-login-card-modern,
|
|
1452
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
|
|
1453
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) {
|
|
1454
|
-
width: min(400px, calc(100vw - 48px)) !important;
|
|
1455
|
-
max-width: 400px !important;
|
|
1456
|
-
min-height: 0 !important;
|
|
1457
|
-
height: auto !important;
|
|
1458
|
-
padding: 30px 32px 28px !important;
|
|
1459
|
-
gap: 0 !important;
|
|
1460
|
-
overflow: visible !important;
|
|
1461
|
-
border-radius: 22px !important;
|
|
1462
|
-
}
|
|
1463
|
-
html.eos-login .eos-login-card-modern > .MuiGrid2-root,
|
|
1464
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username) > .MuiGrid2-root,
|
|
1465
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) > .MuiGrid2-root {
|
|
1466
|
-
width: 100% !important;
|
|
1467
|
-
min-width: 0 !important;
|
|
1468
|
-
}
|
|
1469
|
-
html.eos-login .MuiFormControl-root {
|
|
1411
|
+
/* NexoWatt EOS 7.9.99 – edit controls belong to the lowest Intro card grid. */
|
|
1412
|
+
html.eos-route-intro .eos-native-intro-grid {
|
|
1413
|
+
position: relative !important;
|
|
1470
1414
|
width: 100% !important;
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
html.eos-login .MuiOutlinedInput-root {
|
|
1475
|
-
min-height: 46px !important;
|
|
1476
|
-
border-radius: 6px !important;
|
|
1477
|
-
background: rgba(4, 19, 31, .76) !important;
|
|
1478
|
-
}
|
|
1479
|
-
html.eos-login .MuiOutlinedInput-input {
|
|
1480
|
-
min-height: 0 !important;
|
|
1481
|
-
padding: 11px 14px !important;
|
|
1482
|
-
line-height: 22px !important;
|
|
1483
|
-
}
|
|
1484
|
-
html.eos-login .MuiOutlinedInput-input::placeholder {
|
|
1485
|
-
color: rgba(245, 251, 255, .92) !important;
|
|
1486
|
-
opacity: 1 !important;
|
|
1487
|
-
}
|
|
1488
|
-
html.eos-login .MuiOutlinedInput-notchedOutline legend {
|
|
1489
|
-
width: 0 !important;
|
|
1490
|
-
max-width: 0 !important;
|
|
1491
|
-
padding: 0 !important;
|
|
1492
|
-
}
|
|
1493
|
-
html.eos-login .MuiFormControlLabel-root {
|
|
1494
|
-
margin: 7px 0 5px !important;
|
|
1495
|
-
}
|
|
1496
|
-
html.eos-login .MuiButton-root[type="submit"] {
|
|
1497
|
-
min-height: 44px !important;
|
|
1498
|
-
margin: 8px 0 0 !important;
|
|
1499
|
-
}
|
|
1500
|
-
html.eos-login .eos-login-card-modern > .MuiBox-root:last-child,
|
|
1501
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username) > .MuiBox-root:last-child,
|
|
1502
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) > .MuiBox-root:last-child {
|
|
1503
|
-
margin-top: 28px !important;
|
|
1415
|
+
min-height: 80px !important;
|
|
1416
|
+
padding-bottom: 80px !important;
|
|
1417
|
+
overflow: visible !important;
|
|
1504
1418
|
}
|
|
1505
|
-
html.eos-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1419
|
+
html.eos-route-intro .eos-overview-edit-controls {
|
|
1420
|
+
position: absolute !important;
|
|
1421
|
+
right: 16px !important;
|
|
1422
|
+
bottom: 16px !important;
|
|
1423
|
+
z-index: 3 !important;
|
|
1424
|
+
display: flex !important;
|
|
1425
|
+
align-items: center !important;
|
|
1426
|
+
gap: 8px !important;
|
|
1427
|
+
min-width: 52px !important;
|
|
1428
|
+
min-height: 52px !important;
|
|
1429
|
+
padding: 6px !important;
|
|
1430
|
+
border: 1px solid rgba(0, 229, 138, .24) !important;
|
|
1431
|
+
border-radius: 16px !important;
|
|
1432
|
+
background: rgba(3, 23, 34, .94) !important;
|
|
1433
|
+
box-shadow: 0 12px 30px rgba(0, 0, 0, .26) !important;
|
|
1510
1434
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
min-height: min(520px, calc(100dvh - 12px)) !important;
|
|
1516
|
-
}
|
|
1517
|
-
html.eos-login main.MuiPaper-root > .eos-login-card-modern,
|
|
1518
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#username),
|
|
1519
|
-
html.eos-login main.MuiPaper-root > .MuiPaper-root:has(#password) {
|
|
1520
|
-
width: calc(100vw - 38px) !important;
|
|
1521
|
-
max-width: none !important;
|
|
1522
|
-
padding: 25px 22px 23px !important;
|
|
1523
|
-
}
|
|
1435
|
+
html.eos-route-intro .eos-overview-edit-controls .MuiFab-root {
|
|
1436
|
+
position: static !important;
|
|
1437
|
+
inset: auto !important;
|
|
1438
|
+
margin: 0 !important;
|
|
1524
1439
|
}
|
|
1525
|
-
|
package/adminWww/index.html
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
|
|
9
9
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
10
10
|
<meta http-equiv="Expires" content="0" />
|
|
11
|
-
<script src="./js/eos-
|
|
12
|
-
<script
|
|
11
|
+
<script src="./js/eos-release-watch.js?v=99"></script>
|
|
12
|
+
<script src="./js/eos-manual-write-policy.js?v=99"></script>
|
|
13
|
+
<script type="module" src="./assets/hostInit-v84.js?v=99"></script>
|
|
13
14
|
|
|
14
15
|
<meta charset="utf-8" />
|
|
15
16
|
<link
|
|
@@ -36,8 +37,8 @@
|
|
|
36
37
|
rel="stylesheet"
|
|
37
38
|
href="css/leaflet.css"
|
|
38
39
|
/>
|
|
39
|
-
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=
|
|
40
|
-
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=
|
|
40
|
+
<link rel="stylesheet" href="./css/nexowatt-native-shell.css?v=99" />
|
|
41
|
+
<link rel="stylesheet" href="./css/eos-ems-overview.css?v=99" />
|
|
41
42
|
<link
|
|
42
43
|
rel="manifest"
|
|
43
44
|
href="manifest.json"
|
|
@@ -157,23 +158,23 @@
|
|
|
157
158
|
false,
|
|
158
159
|
);
|
|
159
160
|
</script>
|
|
160
|
-
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
|
|
161
|
-
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
|
|
162
|
-
<script defer src="./js/eos-role-bootstrap.js?v=
|
|
163
|
-
<script defer src="./js/eos-policy-client.js?v=
|
|
164
|
-
<script defer src="./js/eos-branding-sanitizer.js?v=
|
|
165
|
-
<script defer src="./js/nexowatt-native-shell.js?v=
|
|
166
|
-
<script defer src="./js/eos-native-security.js?v=
|
|
167
|
-
<script defer src="./js/eos-basic-settings.js?v=
|
|
168
|
-
<script defer src="./js/eos-role-ui.js?v=
|
|
169
|
-
<script defer src="./js/eos-ems-overview.js?v=
|
|
170
|
-
<script defer src="./js/eos-account-management.js?v=
|
|
171
|
-
<script defer src="./js/eos-assistant.js?v=
|
|
161
|
+
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js?v=99">
|
|
162
|
+
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js?v=99">
|
|
163
|
+
<script defer src="./js/eos-role-bootstrap.js?v=99" data-eos-entry="./assets/index-CQZugZ1z-v84.js?v=99"></script>
|
|
164
|
+
<script defer src="./js/eos-policy-client.js?v=99"></script>
|
|
165
|
+
<script defer src="./js/eos-branding-sanitizer.js?v=99"></script>
|
|
166
|
+
<script defer src="./js/nexowatt-native-shell.js?v=99"></script>
|
|
167
|
+
<script defer src="./js/eos-native-security.js?v=99"></script>
|
|
168
|
+
<script defer src="./js/eos-basic-settings.js?v=99"></script>
|
|
169
|
+
<script defer src="./js/eos-role-ui.js?v=99"></script>
|
|
170
|
+
<script defer src="./js/eos-ems-overview.js?v=99"></script>
|
|
171
|
+
<script defer src="./js/eos-account-management.js?v=99"></script>
|
|
172
|
+
<script defer src="./js/eos-assistant.js?v=99"></script>
|
|
172
173
|
</head>
|
|
173
174
|
<body>
|
|
174
175
|
<noscript>Für NexoWatt EOS muss JavaScript aktiviert sein.</noscript>
|
|
175
176
|
<div id="root"></div>
|
|
176
|
-
<script defer src="static/js/nexowatt-stable-login.js?v=
|
|
177
|
-
<script defer src="static/js/nexowatt-stable-
|
|
177
|
+
<script defer src="static/js/nexowatt-stable-login.js?v=99"></script>
|
|
178
|
+
<script defer src="static/js/nexowatt-stable-v99.js?v=99"></script>
|
|
178
179
|
</body>
|
|
179
180
|
</html>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v99-account-management-password-write';
|
|
5
5
|
const script = document.currentScript || document.querySelector('script[src*="eos-account-management.js"]');
|
|
6
6
|
const base = new URL('../', script?.src || window.location.href);
|
|
7
7
|
const abort = new AbortController();
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
introInstaller: 'Du kannst Endkundenzugänge zurücksetzen. Admin- und Installateurzugänge bleiben NexoWatt Service vorbehalten.',
|
|
20
20
|
loading: 'Zugänge werden geladen …', empty: 'Keine passenden Konten gefunden.', close: 'Schließen', refresh: 'Aktualisieren',
|
|
21
21
|
installer: 'Installateur', enduser: 'Endkunde / Gast', active: 'Passwort aktiv', first: 'Erstanmeldung offen', disabled: 'Deaktiviert',
|
|
22
|
-
reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden?
|
|
23
|
-
resetDone: name => `Der Zugang „${name}“
|
|
24
|
-
resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.',
|
|
22
|
+
reset: 'Passwort zurücksetzen', resetting: 'Wird zurückgesetzt …', resetConfirm: name => `Soll der Zugang „${name}“ wirklich zurückgesetzt werden? Danach gilt einmalig das Startpasswort „nexowatt“ und anschließend muss ein persönliches Passwort vergeben werden.`,
|
|
23
|
+
resetDone: name => `Der Zugang „${name}“ wurde im EOS-Benutzerkonto auf „nexowatt“ zurückgesetzt. Nach der nächsten Anmeldung muss sofort ein persönliches Passwort vergeben werden.`,
|
|
24
|
+
resetFailed: 'Der Zugang konnte nicht zurückgesetzt werden.', lastSet: 'Letzte Passwortvergabe', lastReset: 'Letzter Reset', by: 'durch',
|
|
25
25
|
security: 'Admin/Service darf Installateur und Endkunde zurücksetzen. Installateure dürfen ausschließlich Endkunden zurücksetzen.',
|
|
26
|
+
errors: { permissionError: 'Die Admin-/Installateurberechtigung konnte nicht bestätigt werden. Bitte neu anmelden.', invalidRequest: 'Die Sicherheitsbestätigung fehlt. Bitte die Seite neu laden.', invalidRequestOrigin: 'Die Sitzung oder Adresse hat sich geändert. Bitte die Seite neu laden.', invalidTarget: 'Dieses Konto darf nicht über EOS zurückgesetzt werden.', accountDisabled: 'Das Konto ist deaktiviert.', accountNotFound: 'Das Benutzerkonto wurde nicht gefunden.', passwordApiUnavailable: 'Die EOS-Passwortverwaltung ist nicht verfügbar.', passwordNotPersisted: 'Das Startpasswort wurde nicht gespeichert.', passwordVerificationFailed: 'Das Startpasswort wurde geschrieben, aber nicht als gültige Anmeldung bestätigt.', passwordMetadataNotPersisted: 'Der Erstanmeldungsstatus wurde nicht gespeichert.', accountResetFailed: 'Das Konto konnte serverseitig nicht zurückgesetzt werden.' },
|
|
26
27
|
},
|
|
27
28
|
en: {
|
|
28
29
|
launcher: 'Accounts', titleAdmin: 'Accounts & roles', titleInstaller: 'End-user accounts',
|
|
@@ -30,10 +31,11 @@
|
|
|
30
31
|
introInstaller: 'You may reset end-user accounts. Admin and installer accounts remain reserved for NexoWatt Service.',
|
|
31
32
|
loading: 'Loading accounts …', empty: 'No matching accounts found.', close: 'Close', refresh: 'Refresh',
|
|
32
33
|
installer: 'Installer', enduser: 'End user / Guest', active: 'Password active', first: 'First activation open', disabled: 'Disabled',
|
|
33
|
-
reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The
|
|
34
|
-
resetDone: name => `Account “${name}” was reset
|
|
35
|
-
resetFailed: 'The account could not be reset.',
|
|
34
|
+
reset: 'Reset password', resetting: 'Resetting …', resetConfirm: name => `Reset account “${name}”? The one-time initial password will be “nexowatt”; a personal password must then be created immediately.`,
|
|
35
|
+
resetDone: name => `Account “${name}” was reset in the EOS user account to “nexowatt”. The next sign-in requires an immediate personal password change.`,
|
|
36
|
+
resetFailed: 'The account could not be reset.', lastSet: 'Last password setup', lastReset: 'Last reset', by: 'by',
|
|
36
37
|
security: 'Admin/Service may reset installer and end-user accounts. Installers may reset end-user accounts only.',
|
|
38
|
+
errors: { permissionError: 'Admin/Installer permission could not be confirmed. Sign in again.', invalidRequest: 'The security confirmation is missing. Reload the page.', invalidRequestOrigin: 'The session or address changed. Reload the page.', invalidTarget: 'This account cannot be reset through EOS.', accountDisabled: 'The account is disabled.', accountNotFound: 'The user account was not found.', passwordApiUnavailable: 'The EOS password service is unavailable.', passwordNotPersisted: 'The initial password was not saved.', passwordVerificationFailed: 'The initial password was written but could not be verified.', passwordMetadataNotPersisted: 'The first-login status was not saved.', accountResetFailed: 'The account could not be reset on the server.' },
|
|
37
39
|
},
|
|
38
40
|
nl: {
|
|
39
41
|
launcher: 'Toegangen', titleAdmin: 'Toegangen & rollen', titleInstaller: 'Eindgebruikerstoegangen',
|
|
@@ -41,26 +43,15 @@
|
|
|
41
43
|
introInstaller: 'Je kunt eindgebruikersaccounts resetten. Admin- en installateurstoegang blijft voor NexoWatt Service.',
|
|
42
44
|
loading: 'Toegangen worden geladen …', empty: 'Geen passende accounts gevonden.', close: 'Sluiten', refresh: 'Vernieuwen',
|
|
43
45
|
installer: 'Installateur', enduser: 'Eindgebruiker / Guest', active: 'Wachtwoord actief', first: 'Eerste activering open', disabled: 'Uitgeschakeld',
|
|
44
|
-
reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten?
|
|
45
|
-
resetDone: name => `Account „${name}“ is
|
|
46
|
-
resetFailed: 'Het account kon niet worden gereset.',
|
|
46
|
+
reset: 'Wachtwoord resetten', resetting: 'Wordt gereset …', resetConfirm: name => `Account „${name}“ resetten? Eenmalig geldt het startwachtwoord „nexowatt“, waarna direct een persoonlijk wachtwoord moet worden ingesteld.`,
|
|
47
|
+
resetDone: name => `Account „${name}“ is in het EOS-gebruikersaccount teruggezet op „nexowatt“. Bij de volgende aanmelding moet direct een persoonlijk wachtwoord worden ingesteld.`,
|
|
48
|
+
resetFailed: 'Het account kon niet worden gereset.', lastSet: 'Laatste wachtwoordinstelling', lastReset: 'Laatste reset', by: 'door',
|
|
47
49
|
security: 'Admin/service mag installateur- en eindgebruikersaccounts resetten. Installateurs alleen eindgebruikers.',
|
|
50
|
+
errors: { permissionError: 'De Admin-/Installateurrechten konden niet worden bevestigd. Meld opnieuw aan.', invalidRequest: 'De beveiligingsbevestiging ontbreekt. Herlaad de pagina.', invalidRequestOrigin: 'De sessie of het adres is gewijzigd. Herlaad de pagina.', invalidTarget: 'Dit account kan niet via EOS worden gereset.', accountDisabled: 'Het account is uitgeschakeld.', accountNotFound: 'Het gebruikersaccount is niet gevonden.', passwordApiUnavailable: 'De EOS-wachtwoordservice is niet beschikbaar.', passwordNotPersisted: 'Het startwachtwoord is niet opgeslagen.', passwordVerificationFailed: 'Het startwachtwoord is geschreven maar kon niet worden geverifieerd.', passwordMetadataNotPersisted: 'De eerste-aanmeldstatus is niet opgeslagen.', accountResetFailed: 'Het account kon server-side niet worden gereset.' },
|
|
48
51
|
},
|
|
49
52
|
};
|
|
50
53
|
const t = () => i18n[language()];
|
|
51
|
-
const
|
|
52
|
-
const text = t();
|
|
53
|
-
return ({
|
|
54
|
-
permissionError: text.permissionError,
|
|
55
|
-
invalidRequestOrigin: text.originError,
|
|
56
|
-
accountDisabled: text.disabledError,
|
|
57
|
-
accountNotFound: text.notFoundError,
|
|
58
|
-
passwordApiUnavailable: text.writeError,
|
|
59
|
-
passwordWriteFailed: text.writeError,
|
|
60
|
-
passwordVerificationFailed: text.writeError,
|
|
61
|
-
userObjectUnavailableAfterPasswordChange: text.writeError,
|
|
62
|
-
})[String(code || '')] || text.resetFailed;
|
|
63
|
-
};
|
|
54
|
+
const errorText = code => t().errors?.[String(code || '')] || String(code || t().resetFailed);
|
|
64
55
|
const roleFromPolicy = policy => {
|
|
65
56
|
const raw = normalize(policy?.role || '');
|
|
66
57
|
if (policy?.isAdmin || policy?.isEosAdminGroup || /admin|service/.test(raw)) return 'admin';
|
|
@@ -157,17 +148,17 @@
|
|
|
157
148
|
reset.disabled = true; reset.textContent = t().resetting; setStatus('', '');
|
|
158
149
|
try {
|
|
159
150
|
const response = await fetch(new URL('nexowatt/account/reset', base).href, {
|
|
160
|
-
method: 'POST', credentials: '
|
|
151
|
+
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
|
161
152
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Account-Reset': '1' },
|
|
162
153
|
body: JSON.stringify({ user: account.id }),
|
|
163
154
|
});
|
|
164
155
|
const payload = await response.json().catch(() => ({}));
|
|
165
|
-
if (!response.ok || payload.error) throw new Error(
|
|
156
|
+
if (!response.ok || payload.error) throw new Error(payload.error || t().resetFailed);
|
|
166
157
|
setStatus(t().resetDone(label), 'success');
|
|
167
158
|
await loadAccounts();
|
|
168
159
|
} catch (error) {
|
|
169
160
|
reset.disabled = false; reset.textContent = t().reset;
|
|
170
|
-
setStatus(error?.message
|
|
161
|
+
setStatus(errorText(error?.message), 'error');
|
|
171
162
|
}
|
|
172
163
|
});
|
|
173
164
|
actions.appendChild(reset);
|
|
@@ -189,12 +180,12 @@
|
|
|
189
180
|
if (!state.overlay) return;
|
|
190
181
|
setStatus(t().loading, '');
|
|
191
182
|
try {
|
|
192
|
-
const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: '
|
|
183
|
+
const response = await fetch(new URL('nexowatt/account/manage', base).href, { credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' } });
|
|
193
184
|
const data = await response.json().catch(() => ({}));
|
|
194
185
|
if (!response.ok || data.error) throw new Error(data.error || t().resetFailed);
|
|
195
186
|
renderAccounts(data); setStatus('', '');
|
|
196
187
|
} catch (error) {
|
|
197
|
-
setStatus(error?.message
|
|
188
|
+
setStatus(errorText(error?.message), 'error');
|
|
198
189
|
}
|
|
199
190
|
};
|
|
200
191
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
// NexoWatt EOS stable 7.9.
|
|
3
|
+
// NexoWatt EOS stable 7.9.99: EOS Assist remains disabled until the
|
|
4
4
|
// read-only system assistant is rebuilt and accepted separately.
|
|
5
5
|
window.NEXOWATT_EOS_ASSIST_DISABLED = true;
|
|
6
6
|
document.documentElement.classList.add('eos-assist-disabled');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
const VERSION = '
|
|
3
|
+
const VERSION = 'v99-nexowatt-ems-overview-runtime';
|
|
4
4
|
const CONTRACT = 'nexowatt-ems-overview-v1';
|
|
5
5
|
const ROOT_ID = 'eos-ems-overview-runtime';
|
|
6
6
|
const POLL_MS = 5000;
|
|
@@ -161,11 +161,10 @@
|
|
|
161
161
|
if (tariff.available) tags.push({ label: `Tarif: ${text(tariff.state, 'aktiv')}${tariff.priceEurPerKwh != null ? ` · ${num(tariff.priceEurPerKwh).toFixed(3)} €/kWh` : ''}` });
|
|
162
162
|
if (forecast.available) tags.push({ label: `Prognose: ${text(forecast.source, 'aktiv')}${forecast.fresh === false ? ' · veraltet' : ''}`, warning: forecast.fresh === false });
|
|
163
163
|
if (ems.safetyActive || ems.safetyEmergencyStop || ems.safetyValid === false) tags.push({ label: 'EOS Safety aktiv', warning: true });
|
|
164
|
-
const detailsPort = Math.max(1, num(summary.details?.port, 8188)); const detailsPath = text(summary.details?.path, '/ems-apps.html?tab=status'); const detailsUrl = `${location.protocol}//${location.hostname}:${detailsPort}${detailsPath.startsWith('/') ? detailsPath : `/${detailsPath}`}`;
|
|
165
164
|
const metrics = `<div class="eos-ems-overview-metric"><span>EMS-Budget</span><strong>${totalBudget > 0 ? esc(formatPower(totalBudget)) : 'nicht begrenzt'}</strong><small>Rest ${esc(formatPower(remaining))}</small></div><div class="eos-ems-overview-metric"><span>Lademanagement</span><strong>${esc(formatPower(charging.actualW))} / ${esc(formatPower(charging.targetW))}</strong><small>${num(charging.activeCount)} lädt · ${num(charging.waitingCount)} wartet</small></div>${storage.available ? `<div class="eos-ems-overview-metric"><span>Speicher ${esc(text(storage.topology))}</span><strong>${esc(formatPower(storage.actualW))} / ${esc(formatPower(storage.targetW))}</strong><small>${storage.socPct != null ? `SoC ${Math.round(num(storage.socPct))} %` : esc(text(storage.reason, 'aktiv'))}</small></div>` : `<div class="eos-ems-overview-metric"><span>PV-Budget</span><strong>${esc(formatPower(budget.pvBudgetW))}</strong><small>PV-Rest ${esc(formatPower(budget.remainingPvW))}</small></div>`}<div class="eos-ems-overview-metric"><span>Letzter Regeltick</span><strong>${esc(formatAge(ems.lastTickTs || data.updatedAt))}</strong><small>${isTechnical ? `Zyklus ${num(ems.cycleMs)} ms` : esc(text(ems.decision, 'EMS aktiv'))}</small></div>`;
|
|
166
165
|
const decisionsHtml = decisions.length ? decisions.map(item => `<div class="eos-ems-overview-decision eos-ems-overview-decision--${severity(item.severity)}"><strong>${esc(text(item.title, 'EMS-Entscheidung'))}</strong><span>${esc(text(item.reason, 'Keine zusätzliche Begrenzung'))}</span>${isTechnical && item.details ? `<small>${esc(item.details)}</small>` : ''}</div>`).join('') : '<div class="eos-ems-overview-empty">Keine aktiven Regelentscheidungen. EOS arbeitet mit den aktuellen Messwerten.</div>';
|
|
167
166
|
const eventsHtml = isTechnical ? (events.length ? events.map(item => `<div class="eos-ems-overview-event eos-ems-overview-event--${severity(item.severity)}"><time>${esc(formatClock(item.ts))}</time><div><strong>${esc(text(item.title, 'EMS'))}</strong><span>${esc(text(item.message || item.reason, 'Zustand aktualisiert'))}</span></div></div>`).join('') : '<div class="eos-ems-overview-empty">Noch keine Zustandsänderung im begrenzten Ereignispuffer.</div>') : `<div class="eos-ems-overview-decision eos-ems-overview-decision--info"><strong>${esc(text(summary.headline, 'EMS aktiv'))}</strong><span>${esc(text(summary.reason, 'EOS optimiert die Anlage innerhalb aller Sicherheitsgrenzen.'))}</span></div>`;
|
|
168
|
-
root.innerHTML = `<header class="eos-ems-overview-header"><div><span class="eos-ems-overview-eyebrow">NexoWatt EMS · Live-Diagnose</span><h2>${esc(stale ? 'EMS-Diagnose ist nicht aktuell' : text(summary.headline, 'EMS arbeitet normal'))}</h2><p>${esc(stale ? 'Adapter oder Diagnosewerte sind älter als 20 Sekunden. Die Werte werden als veraltet markiert.' : text(summary.reason, 'EMS arbeitet innerhalb aller aktiven Grenzen.'))}</p></div><div class="eos-ems-overview-state"><span class="eos-ems-overview-state-dot"></span><strong>${stale ? 'Offline / veraltet' : ({ok:'Normal',info:'Information',warning:'Begrenzt',error:'Störung'}[status])}</strong><small>${esc(data.instance)} · ${esc(formatAge(data.updatedAt))}</small></div></header><div class="eos-ems-overview-metrics">${metrics}</div>${totalBudget > 0 ? `<div class="eos-ems-overview-budget" title="${percent.toFixed(1)} % Restbudget"><span style="width:${percent}%"></span></div>` : ''}${tags.length ? `<div class="eos-ems-overview-tags">${tags.map(tag => `<span class="${tag.warning ? 'is-warning' : ''}">${esc(tag.label)}</span>`).join('')}</div>` : ''}<div class="eos-ems-overview-columns"><div class="eos-ems-overview-panel"><h3>Aktuelle Regelentscheidungen</h3>${decisionsHtml}</div><div class="eos-ems-overview-panel"><h3>${isTechnical ? 'Letzte EMS-Ereignisse' : 'Systemerklärung'}</h3>${eventsHtml}</div></div><footer class="eos-ems-overview-footer"><span>Diagnose ist rein lesend. Keine Kachel besitzt Schreibhoheit.</span
|
|
167
|
+
root.innerHTML = `<header class="eos-ems-overview-header"><div><span class="eos-ems-overview-eyebrow">NexoWatt EMS · Live-Diagnose</span><h2>${esc(stale ? 'EMS-Diagnose ist nicht aktuell' : text(summary.headline, 'EMS arbeitet normal'))}</h2><p>${esc(stale ? 'Adapter oder Diagnosewerte sind älter als 20 Sekunden. Die Werte werden als veraltet markiert.' : text(summary.reason, 'EMS arbeitet innerhalb aller aktiven Grenzen.'))}</p></div><div class="eos-ems-overview-state"><span class="eos-ems-overview-state-dot"></span><strong>${stale ? 'Offline / veraltet' : ({ok:'Normal',info:'Information',warning:'Begrenzt',error:'Störung'}[status])}</strong><small>${esc(data.instance)} · ${esc(formatAge(data.updatedAt))}</small></div></header><div class="eos-ems-overview-metrics">${metrics}</div>${totalBudget > 0 ? `<div class="eos-ems-overview-budget" title="${percent.toFixed(1)} % Restbudget"><span style="width:${percent}%"></span></div>` : ''}${tags.length ? `<div class="eos-ems-overview-tags">${tags.map(tag => `<span class="${tag.warning ? 'is-warning' : ''}">${esc(tag.label)}</span>`).join('')}</div>` : ''}<div class="eos-ems-overview-columns"><div class="eos-ems-overview-panel"><h3>Aktuelle Regelentscheidungen</h3>${decisionsHtml}</div><div class="eos-ems-overview-panel"><h3>${isTechnical ? 'Letzte EMS-Ereignisse' : 'Systemerklärung'}</h3>${eventsHtml}</div></div><footer class="eos-ems-overview-footer"><span>Diagnose ist rein lesend. Keine Kachel besitzt Schreibhoheit.</span></footer>`;
|
|
169
168
|
};
|
|
170
169
|
|
|
171
170
|
const scheduleFastRetry = () => {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
'use strict';
|
|
3
|
+
const RELEASE = '7.9.99';
|
|
4
|
+
const RELEASE_KEY = 'nexowatt.eos.loadedRelease';
|
|
5
|
+
const RELOAD_KEY = 'nexowatt.eos.reloadTarget';
|
|
6
|
+
const CHECK_MS = 15000;
|
|
7
|
+
let refreshing = false;
|
|
8
|
+
|
|
9
|
+
const normalize = value => String(value || '').trim().replace(/^v/i, '');
|
|
10
|
+
const validRelease = value => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(normalize(value));
|
|
11
|
+
const reloadUrl = version => {
|
|
12
|
+
const url = new URL(window.location.href);
|
|
13
|
+
url.searchParams.set('_eosRelease', normalize(version) || RELEASE);
|
|
14
|
+
url.searchParams.set('_eosReload', String(Date.now()));
|
|
15
|
+
return url.toString();
|
|
16
|
+
};
|
|
17
|
+
const purgeBrowserCaches = async () => {
|
|
18
|
+
const jobs = [];
|
|
19
|
+
try {
|
|
20
|
+
if ('caches' in window) {
|
|
21
|
+
jobs.push(caches.keys().then(keys => Promise.all(keys.map(key => caches.delete(key)))));
|
|
22
|
+
}
|
|
23
|
+
} catch (_) { /* Cache API is optional */ }
|
|
24
|
+
try {
|
|
25
|
+
if (navigator.serviceWorker?.getRegistrations) {
|
|
26
|
+
jobs.push(navigator.serviceWorker.getRegistrations().then(rows => Promise.all(rows.map(row => row.unregister()))));
|
|
27
|
+
}
|
|
28
|
+
} catch (_) { /* service workers may be unavailable */ }
|
|
29
|
+
await Promise.allSettled(jobs);
|
|
30
|
+
};
|
|
31
|
+
const reloadFor = async version => {
|
|
32
|
+
const target = normalize(version) || RELEASE;
|
|
33
|
+
if (refreshing || sessionStorage.getItem(RELOAD_KEY) === target) return;
|
|
34
|
+
refreshing = true;
|
|
35
|
+
sessionStorage.setItem(RELOAD_KEY, target);
|
|
36
|
+
document.documentElement.classList.add('eos-release-refreshing');
|
|
37
|
+
await purgeBrowserCaches();
|
|
38
|
+
window.location.replace(reloadUrl(target));
|
|
39
|
+
};
|
|
40
|
+
const checkServerVersion = async () => {
|
|
41
|
+
try {
|
|
42
|
+
const endpoint = new URL('./version', window.location.href);
|
|
43
|
+
endpoint.searchParams.set('_eosTs', String(Date.now()));
|
|
44
|
+
const response = await fetch(endpoint, {
|
|
45
|
+
cache: 'no-store',
|
|
46
|
+
credentials: 'same-origin',
|
|
47
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) return;
|
|
50
|
+
const serverVersion = normalize(await response.text());
|
|
51
|
+
if (validRelease(serverVersion) && serverVersion !== RELEASE) {
|
|
52
|
+
await reloadFor(serverVersion);
|
|
53
|
+
}
|
|
54
|
+
} catch (_) { /* adapter restart or temporary network interruption */ }
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
window.NEXOWATT_EOS_RELEASE = RELEASE;
|
|
58
|
+
const previous = normalize(localStorage.getItem(RELEASE_KEY));
|
|
59
|
+
localStorage.setItem(RELEASE_KEY, RELEASE);
|
|
60
|
+
const loadedFor = normalize(new URL(window.location.href).searchParams.get('_eosRelease'));
|
|
61
|
+
if (previous && previous !== RELEASE && loadedFor !== RELEASE) {
|
|
62
|
+
void reloadFor(RELEASE);
|
|
63
|
+
} else {
|
|
64
|
+
sessionStorage.removeItem(RELOAD_KEY);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
window.addEventListener('focus', () => void checkServerVersion());
|
|
68
|
+
document.addEventListener('visibilitychange', () => {
|
|
69
|
+
if (document.visibilityState === 'visible') void checkServerVersion();
|
|
70
|
+
});
|
|
71
|
+
window.setInterval(() => void checkServerVersion(), CHECK_MS);
|
|
72
|
+
window.NEXOWATT_EOS_RELEASE_WATCH = Object.freeze({
|
|
73
|
+
release: RELEASE,
|
|
74
|
+
check: checkServerVersion,
|
|
75
|
+
purge: purgeBrowserCaches,
|
|
76
|
+
});
|
|
77
|
+
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v99-first-login-password-persisted';
|
|
5
5
|
const script = document.currentScript || document.querySelector('script[src*="eos-role-bootstrap.js"]');
|
|
6
6
|
const entry = script?.dataset?.eosEntry || '';
|
|
7
7
|
let launched = false;
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
required: 'Bitte beide Passwortfelder ausfüllen.', mismatch: 'Die Passwörter stimmen nicht überein.',
|
|
81
81
|
length: min => `Das Passwort muss mindestens ${min} Zeichen lang sein.`, complexity: 'Groß-/Kleinbuchstabe, Zahl und Sonderzeichen sind erforderlich.',
|
|
82
82
|
easy: 'Das Passwort ist zu leicht oder enthält den Kontonamen.', generic: 'Das Passwort konnte nicht gespeichert werden.', success: 'Passwort gespeichert. Die Anmeldung wird neu gestartet …',
|
|
83
|
+
backendErrors: { invalidRequest: 'Die Sicherheitsbestätigung fehlt. Bitte erneut anmelden.', invalidRequestOrigin: 'Die Sitzung oder aufgerufene Adresse hat sich geändert. Bitte erneut anmelden.', passwordApiUnavailable: 'Die ioBroker-Passwortverwaltung ist nicht verfügbar.', passwordNotPersisted: 'Das Passwort wurde nicht im ioBroker-Benutzerkonto gespeichert.', passwordVerificationFailed: 'Das neue Passwort wurde geschrieben, aber nicht als gültige Anmeldung bestätigt.', passwordMetadataNotPersisted: 'Das Passwort wurde gespeichert, aber der Erstanmeldungsstatus konnte nicht abgeschlossen werden.', passwordSetupFailed: 'Das Passwort konnte serverseitig nicht gespeichert werden. Bitte das EOS-Systemlog prüfen.' },
|
|
83
84
|
securityTitle: 'Zugriff wird geprüft', securityIntro: 'Die sichere Anmeldung wird gerade geprüft. Die Oberfläche bleibt gesperrt, bis die Berechtigungen eindeutig geladen wurden.',
|
|
84
85
|
securityRetry: 'Jetzt erneut prüfen', securityLogout: 'Zur Anmeldung', securityAuto: seconds => `Automatischer neuer Versuch in ${seconds} Sekunden …`,
|
|
85
86
|
claimLauncher: 'Erstanmeldung', claimTitle: 'Konto erstmals aktivieren',
|
|
@@ -99,6 +100,7 @@
|
|
|
99
100
|
required: 'Please complete both password fields.', mismatch: 'The passwords do not match.',
|
|
100
101
|
length: min => `The password must contain at least ${min} characters.`, complexity: 'Upper/lower case, a number and a special character are required.',
|
|
101
102
|
easy: 'The password is too easy or contains the account name.', generic: 'The password could not be saved.', success: 'Password saved. Restarting sign-in …',
|
|
103
|
+
backendErrors: { invalidRequest: 'The security confirmation is missing. Sign in again.', invalidRequestOrigin: 'The session or address changed. Sign in again.', passwordApiUnavailable: 'The ioBroker password service is unavailable.', passwordNotPersisted: 'The password was not persisted in the ioBroker user account.', passwordVerificationFailed: 'The new password was written but could not be verified as a valid login.', passwordMetadataNotPersisted: 'The password was saved, but the first-login status could not be completed.', passwordSetupFailed: 'The password could not be saved on the server. Check the EOS system log.' },
|
|
102
104
|
securityTitle: 'Checking access', securityIntro: 'The secure sign-in is being verified. The interface stays locked until the permissions are loaded unambiguously.',
|
|
103
105
|
securityRetry: 'Check again now', securityLogout: 'Return to sign-in', securityAuto: seconds => `Retrying automatically in ${seconds} seconds …`,
|
|
104
106
|
claimLauncher: 'First sign-in', claimTitle: 'Activate account for the first time',
|
|
@@ -118,6 +120,7 @@
|
|
|
118
120
|
required: 'Vul beide wachtwoordvelden in.', mismatch: 'De wachtwoorden zijn niet gelijk.',
|
|
119
121
|
length: min => `Het wachtwoord moet minimaal ${min} tekens bevatten.`, complexity: 'Hoofdletter, kleine letter, cijfer en speciaal teken zijn verplicht.',
|
|
120
122
|
easy: 'Het wachtwoord is te eenvoudig of bevat de accountnaam.', generic: 'Het wachtwoord kon niet worden opgeslagen.', success: 'Wachtwoord opgeslagen. De aanmelding wordt opnieuw gestart …',
|
|
123
|
+
backendErrors: { invalidRequest: 'De beveiligingsbevestiging ontbreekt. Meld opnieuw aan.', invalidRequestOrigin: 'De sessie of het adres is gewijzigd. Meld opnieuw aan.', passwordApiUnavailable: 'De ioBroker-wachtwoordservice is niet beschikbaar.', passwordNotPersisted: 'Het wachtwoord is niet in het ioBroker-gebruikersaccount opgeslagen.', passwordVerificationFailed: 'Het nieuwe wachtwoord is geschreven maar kon niet als geldige aanmelding worden bevestigd.', passwordMetadataNotPersisted: 'Het wachtwoord is opgeslagen, maar de eerste-aanmeldstatus kon niet worden afgerond.', passwordSetupFailed: 'Het wachtwoord kon server-side niet worden opgeslagen. Controleer het EOS-systeemlogboek.' },
|
|
121
124
|
securityTitle: 'Toegang wordt gecontroleerd', securityIntro: 'De veilige aanmelding wordt gecontroleerd. De interface blijft vergrendeld totdat de rechten eenduidig zijn geladen.',
|
|
122
125
|
securityRetry: 'Nu opnieuw controleren', securityLogout: 'Terug naar aanmelden', securityAuto: seconds => `Automatisch opnieuw proberen over ${seconds} seconden …`,
|
|
123
126
|
claimLauncher: 'Eerste aanmelding', claimTitle: 'Account voor het eerst activeren',
|
|
@@ -192,13 +195,13 @@
|
|
|
192
195
|
submit.disabled = true; submit.textContent = t.saving; setStatus('', '');
|
|
193
196
|
try {
|
|
194
197
|
const response = await fetch(endpoint, {
|
|
195
|
-
method: 'POST', credentials: '
|
|
198
|
+
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
|
196
199
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-First-Login': '1' },
|
|
197
200
|
body: JSON.stringify({ password: password.value, passwordRepeat: repeat.value }),
|
|
198
201
|
});
|
|
199
202
|
const data = await response.json().catch(() => ({}));
|
|
200
203
|
if (!response.ok || data.error) {
|
|
201
|
-
const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy,
|
|
204
|
+
const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, ...(t.backendErrors || {}) };
|
|
202
205
|
throw new Error(messages[data.error] || t.generic);
|
|
203
206
|
}
|
|
204
207
|
setStatus(t.success, 'success');
|
|
@@ -265,13 +268,13 @@
|
|
|
265
268
|
submit.disabled = true; submit.textContent = t.saving; setStatus('', '');
|
|
266
269
|
try {
|
|
267
270
|
const response = await fetch(endpoint, {
|
|
268
|
-
method: 'POST', credentials: '
|
|
271
|
+
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
|
269
272
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Passwordless-Password': '1' },
|
|
270
273
|
body: JSON.stringify({ password: password.value, passwordRepeat: repeat.value }),
|
|
271
274
|
});
|
|
272
275
|
const data = await response.json().catch(() => ({}));
|
|
273
276
|
if (!response.ok || data.error) {
|
|
274
|
-
const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, claimExpired: t.claimExpired };
|
|
277
|
+
const messages = { passwordRequired: t.required, passwordMismatch: t.mismatch, passwordLength: t.length(minLength), passwordComplexity: t.complexity, passwordTooEasy: t.easy, claimExpired: t.claimExpired, ...(t.backendErrors || {}) };
|
|
275
278
|
throw new Error(messages[data.error] || t.generic);
|
|
276
279
|
}
|
|
277
280
|
overlay.querySelector('.eos-activation-steps span:nth-of-type(2)')?.classList.add('done');
|
|
@@ -473,7 +476,7 @@
|
|
|
473
476
|
setStatus(card, t.claimChecking, '');
|
|
474
477
|
try {
|
|
475
478
|
const response = await fetch(new URL('nexowatt/account/passwordless-claim', base).href, {
|
|
476
|
-
method: 'POST', credentials: '
|
|
479
|
+
method: 'POST', credentials: 'same-origin', cache: 'no-store',
|
|
477
480
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'X-NexoWatt-EOS-Passwordless-Claim': '1' },
|
|
478
481
|
body: JSON.stringify({ user: account }),
|
|
479
482
|
});
|
|
@@ -500,7 +503,7 @@
|
|
|
500
503
|
const { username, password, card, submit } = loginElements();
|
|
501
504
|
if (!username || !password || !card) return;
|
|
502
505
|
card.classList.add('eos-login-card-modern');
|
|
503
|
-
//
|
|
506
|
+
// v99 normal-login first activation: do not add role buttons or a second panel.
|
|
504
507
|
// The account is entered in the existing Loginname field; a blank password is accepted only
|
|
505
508
|
// after the backend confirms that this exact managed account is eligible.
|
|
506
509
|
card.querySelectorAll('.eos-login-role-selector').forEach(element => element.remove());
|
|
@@ -634,7 +637,7 @@
|
|
|
634
637
|
const timeout = window.setTimeout(() => controller.abort(), 8000);
|
|
635
638
|
|
|
636
639
|
window.NEXOWATT_EOS_BOOTSTRAP_READY = fetch(contextUrl, {
|
|
637
|
-
credentials: '
|
|
640
|
+
credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' }, signal: controller.signal,
|
|
638
641
|
})
|
|
639
642
|
.then(response => { if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); })
|
|
640
643
|
.then(policy => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v99-native-overview-ems-live-reserve-filter';
|
|
5
5
|
window.NEXOWATT_EOS_ROLE_UI_VERSION = VERSION;
|
|
6
6
|
|
|
7
7
|
const state = {
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
});
|
|
279
279
|
};
|
|
280
280
|
const ensureSafeOverview = () => safe(() => {
|
|
281
|
-
// 7.9.
|
|
281
|
+
// 7.9.99: never cover the real Admin Intro with a second tile surface.
|
|
282
282
|
// Installer and end-user use the native card component; security remains
|
|
283
283
|
// enforced by route/instance filters and backend permissions.
|
|
284
284
|
removeSafeOverview();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const VERSION = '
|
|
4
|
+
const VERSION = 'v99-nexowatt-native-shell-native-overview';
|
|
5
5
|
const previous = window.NEXOWATT_NATIVE_SHELL;
|
|
6
6
|
if (previous?.version === VERSION) return;
|
|
7
7
|
previous?.destroy?.();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* NexoWatt EOS stable runtime
|
|
1
|
+
/* NexoWatt EOS stable runtime v99 */
|
|
2
2
|
(() => {
|
|
3
3
|
'use strict';
|
|
4
4
|
const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
const findSubmit = () => [...document.querySelectorAll('button,input[type="submit"]')].find(element => visible(element) && (/anmelden|login|sign in/i.test((element.textContent || element.value || '').trim()) || element.type === 'submit'));
|
|
15
15
|
const sync = () => {
|
|
16
16
|
if (!isLogin()) return;
|
|
17
|
-
document.documentElement.classList.add('nw-eos-login-
|
|
18
|
-
document.body?.classList.add('nw-eos-login-
|
|
17
|
+
document.documentElement.classList.add('nw-eos-login-v99');
|
|
18
|
+
document.body?.classList.add('nw-eos-login-v99');
|
|
19
19
|
const username = findInput('user');
|
|
20
20
|
const password = findInput('password');
|
|
21
21
|
const submit = findSubmit();
|