iobroker.eos-admin 7.9.92 → 7.9.95
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.92.md → INSTALL_TEST_V7.9.95.md} +2 -5
- package/NEXOWATT_EOS_BUILD_INFO.json +18 -14
- package/NEXOWATT_EOS_STABLE_7.9.94.md +16 -0
- package/PUBLISH_STABLE_V7.9.95.md +16 -0
- package/README_STABILITY_V7.9.95.md +12 -0
- package/RELEASE_ACCEPTANCE_V7.9.95.md +11 -0
- package/RELEASE_NOTES_V7.9.95.md +12 -0
- package/adminWww/css/nexowatt-native-shell.css +2 -2
- package/adminWww/index.html +2 -0
- package/adminWww/js/eos-assistant.js +5 -124
- package/adminWww/static/js/nexowatt-stable-login.js +79 -0
- package/build/lib/nexowattStableAuth.js +74 -0
- package/build/lib/web.js +24 -7
- package/build/main.js +122 -1
- package/io-package.json +24 -15
- package/package.json +22 -31
- package/PUBLISH_STABLE_V7.9.92.md +0 -19
- package/README_STABILITY_V7.9.92.md +0 -14
- package/RELEASE_ACCEPTANCE_V7.9.92.md +0 -12
- package/RELEASE_NOTES_V7.9.92.md +0 -17
- package/tools/nexowatt-account-management-selftest.cjs +0 -61
- package/tools/nexowatt-clean-legacy-runtime.cjs +0 -147
- package/tools/nexowatt-ensure-release-defaults.cjs +0 -72
- package/tools/nexowatt-generate-eos-admin-repo-entry.cjs +0 -37
- package/tools/nexowatt-generate-repo-entry.cjs +0 -3
- package/tools/nexowatt-internal-reserve-selftest.cjs +0 -55
- package/tools/nexowatt-login-layout-selftest.cjs +0 -81
- package/tools/nexowatt-modern-ui-selftest.cjs +0 -50
- package/tools/nexowatt-patch-repo.cjs +0 -54
- package/tools/nexowatt-publish-channel-guard.cjs +0 -58
- package/tools/nexowatt-release-defaults-selftest.cjs +0 -29
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +0 -47
- package/tools/nexowatt-validate-package.cjs +0 -246
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Installationstest 7.9.95
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
4
|
cd /opt/iobroker
|
|
5
|
-
iobroker backup
|
|
6
5
|
iobroker stop eos-admin.0
|
|
7
|
-
npm install --no-save --omit=dev iobroker.eos-admin@
|
|
6
|
+
npm install --no-save --omit=dev iobroker.eos-admin@7.9.95
|
|
8
7
|
iobroker upload eos-admin
|
|
9
8
|
iobroker restart eos-admin.0
|
|
10
9
|
```
|
|
@@ -14,5 +13,3 @@ Version prüfen:
|
|
|
14
13
|
```bash
|
|
15
14
|
node -p "require('/opt/iobroker/node_modules/iobroker.eos-admin/package.json').version"
|
|
16
15
|
```
|
|
17
|
-
|
|
18
|
-
Erwartet: `7.9.92`.
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "7.9.
|
|
2
|
+
"version": "7.9.95",
|
|
3
3
|
"channel": "stable-latest",
|
|
4
4
|
"baseFrontend": "validated Admin 7 runtime with NexoWatt EOS compact login, role, overview and service-reserve hardening",
|
|
5
5
|
"runtimeEntry": "v84",
|
|
6
|
-
"releaseType": "stable-
|
|
7
|
-
"release": "stable-
|
|
8
|
-
"label": "
|
|
6
|
+
"releaseType": "stable-merge-safe-packaging",
|
|
7
|
+
"release": "stable-merge-safe-packaging",
|
|
8
|
+
"label": "v95",
|
|
9
9
|
"notes": [
|
|
10
|
-
"7.9.
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"The compact login, first activation, clickable overview and role-safe services from 7.9.91 are unchanged."
|
|
10
|
+
"7.9.95 is the merge-safe NexoWatt EOS Admin release for npm dist-tag latest.",
|
|
11
|
+
"The flat update ZIP can be extracted directly over an existing repository folder.",
|
|
12
|
+
"All version-bearing release files are synchronized automatically before validation and npm publishing.",
|
|
13
|
+
"The 7.9.94 login, roles, password bootstrap, port 8081 and disabled EOS Assist remain functionally unchanged."
|
|
15
14
|
],
|
|
16
15
|
"changes": [
|
|
17
|
-
"Adds
|
|
18
|
-
"
|
|
19
|
-
"Adds
|
|
20
|
-
"
|
|
16
|
+
"Adds deterministic synchronization for package, io-package, lockfiles, frontend version, build info and repository entry.",
|
|
17
|
+
"Adds guarded Windows merge verification scripts without deleting .git or node_modules.",
|
|
18
|
+
"Adds a regression test proving that a stale 7.9.92 io-package is repaired before publication.",
|
|
19
|
+
"Adds a flat merge ZIP intended for direct extraction into the existing working folder.",
|
|
20
|
+
"Cleans the npm files manifest and rejects missing distribution entries before publication.",
|
|
21
|
+
"Aligns account-management and modern-UI self-tests with the documented stable initial-password and disabled-assistant contracts.",
|
|
22
|
+
"Synchronizes package-lock root scripts and release policy with package.json and validates lock metadata drift.",
|
|
23
|
+
"Builds a genuinely flat merge ZIP with repository files at archive root.",
|
|
24
|
+
"Keeps source-only validation helpers out of the installed npm runtime while retaining the supported update-repair helper."
|
|
21
25
|
],
|
|
22
26
|
"brandingCacheVersion": 91,
|
|
23
27
|
"shellCacheVersion": 91,
|
|
24
28
|
"nativeShellVersion": 91,
|
|
25
29
|
"shellCacheTag": "91",
|
|
26
|
-
"repositoryEntry": "nexowatt-eos-admin-repository-entry-
|
|
30
|
+
"repositoryEntry": "nexowatt-eos-admin-repository-entry-v95.json"
|
|
27
31
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# NexoWatt EOS Admin 7.9.94 – Stable candidate
|
|
2
|
+
|
|
3
|
+
## Stable scope
|
|
4
|
+
|
|
5
|
+
- Standard port 8081 and authentication enabled.
|
|
6
|
+
- Initial commissioning credentials for `installer`, `guest`, and `user`: password `nexowatt`.
|
|
7
|
+
- A personal password is mandatory immediately after the first successful login.
|
|
8
|
+
- The old passwordless activation flow is disabled.
|
|
9
|
+
- Admin/Service credentials are never changed by the bootstrap.
|
|
10
|
+
- EOS Assist is disabled in this stable release and will return only after its separate read-only redesign and acceptance.
|
|
11
|
+
- The login card remains fully usable when validation or authentication errors are shown.
|
|
12
|
+
- Customer-visible publisher: NexoWatt; technical npm identity remains `iobroker.eos-admin` for update compatibility.
|
|
13
|
+
|
|
14
|
+
## Security note
|
|
15
|
+
|
|
16
|
+
The shared commissioning password is only an initial credential. Commissioning is not complete until installer and customer accounts have replaced it with personal passwords. Shipping a system with the initial credential still active is not permitted.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# npm-Veröffentlichung 7.9.95
|
|
2
|
+
|
|
3
|
+
Nach dem Entpacken der Merge-ZIP zuerst `MERGE_UPDATE.cmd` ausführen. Danach:
|
|
4
|
+
|
|
5
|
+
```powershell
|
|
6
|
+
npm login
|
|
7
|
+
npm whoami
|
|
8
|
+
npm publish --dry-run --tag latest
|
|
9
|
+
npm publish --tag latest
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Kontrolle:
|
|
13
|
+
|
|
14
|
+
```powershell
|
|
15
|
+
npm view iobroker.eos-admin@latest version
|
|
16
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# NexoWatt EOS Admin 7.9.95 – Stabilität
|
|
2
|
+
|
|
3
|
+
Version 7.9.95 ist ein packaging-only Stable-Update. Die produktive Laufzeit, Rollenlogik, Erstanmeldung mit dem Startpasswort `nexowatt`, Standardport 8081 und die Deaktivierung des EOS Assist bleiben gegenüber 7.9.94 unverändert.
|
|
4
|
+
|
|
5
|
+
Neu ist die mergesichere Release-Vorbereitung: Alle versionsführenden Dateien werden vor Prüfung, Packen und npm-Veröffentlichung automatisch an `package.json` angeglichen.
|
|
6
|
+
|
|
7
|
+
Die npm-Dateiliste wird nun zusätzlich gegen das reale Dateisystem geprüft. Nicht vorhandene oder nicht gepackte Pflichtdateien brechen die Veröffentlichung vor dem Upload ab.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Zusätzlich werden die Rootmetadaten der Lockdateien gegen die zugehörigen `package.json`-Dateien geprüft. Dadurch können veraltete `prepublishOnly`-, `prepack`- oder Stabilitätsskripte nicht mehr unbemerkt in einer gemischten Arbeitskopie verbleiben.
|
|
11
|
+
|
|
12
|
+
Die Merge-Datei für bestehende Arbeitsordner wird ohne übergeordneten Versionsordner erzeugt. `package.json`, `io-package.json` und `MERGE_UPDATE.cmd` liegen unmittelbar auf der ersten ZIP-Ebene.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Release-Abnahme 7.9.95
|
|
2
|
+
|
|
3
|
+
- [ ] Merge-ZIP direkt über einen alten Arbeitsordner entpackt
|
|
4
|
+
- [ ] `MERGE_UPDATE.cmd` vollständig grün
|
|
5
|
+
- [ ] Alle Versionsdateien melden 7.9.95
|
|
6
|
+
- [ ] `npm publish --dry-run --tag latest` erfolgreich
|
|
7
|
+
- [ ] Installation über npm erfolgreich
|
|
8
|
+
- [ ] Admin-Login unverändert
|
|
9
|
+
- [ ] Installer/User-Erstlogin mit Startpasswort `nexowatt` geprüft
|
|
10
|
+
- [ ] EOS Assist in Stable deaktiviert
|
|
11
|
+
- [ ] EOS Admin auf Port 8081 erreichbar
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Release Notes – 7.9.95
|
|
2
|
+
|
|
3
|
+
- Flache Merge-ZIP für das direkte Entpacken in einen vorhandenen Repository-Ordner.
|
|
4
|
+
- Automatischer Versionsabgleich für `io-package.json`, Lockdateien, Frontend-Version, Build-Info und Repository-Eintrag.
|
|
5
|
+
- Selbsttest mit absichtlich veraltetem Stand 7.9.92.
|
|
6
|
+
- Windows-Helfer `MERGE_UPDATE.cmd` und `MERGE_UPDATE.ps1`.
|
|
7
|
+
- Keine funktionale Änderung an Login, Rollen, Port oder EOS-Assist-Sperre.
|
|
8
|
+
- npm-Dateiliste bereinigt: keine nicht vorhandenen 7.9.94-Dateien mehr; aktuelle Stable-, Merge- und Prüfscripte werden vollständig gepackt.
|
|
9
|
+
- Selbsttests an den dokumentierten Stable-Vertrag angeglichen: Startpasswort `nexowatt` mit Pflichtwechsel; EOS Assist bleibt deaktiviert.
|
|
10
|
+
- `package-lock.json`-Rootmetadaten (Skripte und Release-Policy) werden gemeinsam mit `package.json` synchronisiert und validiert.
|
|
11
|
+
- Die veröffentlichte npm-Tarball-Dateiliste enthält nur Runtime-Dateien, aktuelle Release-Dokumente und den installierten Update-Reparaturhelfer; Source-only-Prüfwerkzeuge bleiben in der Merge-/Repository-ZIP.
|
|
12
|
+
- Die ausgelieferte Merge-ZIP wird tatsächlich flach erzeugt, damit `package.json` direkt im bestehenden Repository-Ordner landet.
|
|
@@ -856,7 +856,7 @@ html.eos-modern-login body::before {
|
|
|
856
856
|
|
|
857
857
|
|
|
858
858
|
/* ==========================================================================
|
|
859
|
-
NexoWatt EOS 7.9.
|
|
859
|
+
NexoWatt EOS 7.9.94 – stable product layout
|
|
860
860
|
Integrated first login, header assistant, role-safe overview and account
|
|
861
861
|
management exclusively below "Zugänge & Rechte".
|
|
862
862
|
========================================================================== */
|
|
@@ -1193,7 +1193,7 @@ html.eos-role-enduser [data-instance-id^="backitup."] { display: none !important
|
|
|
1193
1193
|
|
|
1194
1194
|
|
|
1195
1195
|
|
|
1196
|
-
/* Final 7.9.
|
|
1196
|
+
/* Final 7.9.94 interaction guards. */
|
|
1197
1197
|
html.eos-safe-overview-active #app-paper { position: relative !important; }
|
|
1198
1198
|
html.eos-safe-overview-active #app-paper > :not(#eos-role-safe-overview):not(.MuiSnackbar-root):not([role="presentation"]) {
|
|
1199
1199
|
pointer-events: none !important;
|
package/adminWww/index.html
CHANGED
|
@@ -171,5 +171,7 @@
|
|
|
171
171
|
<body>
|
|
172
172
|
<noscript>Für NexoWatt EOS muss JavaScript aktiviert sein.</noscript>
|
|
173
173
|
<div id="root"></div>
|
|
174
|
+
<script defer src="static/js/nexowatt-stable-login.js?v=95"></script>
|
|
175
|
+
<script defer src="static/js/nexowatt-stable-v95.js?v=95"></script>
|
|
174
176
|
</body>
|
|
175
177
|
</html>
|
|
@@ -1,127 +1,8 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const scriptUrl = document.currentScript?.src || document.querySelector('script[src*="eos-assistant.js"]')?.src || location.href;
|
|
10
|
-
const logoUrl = new URL('../img/eos/nexowatt-192.png', scriptUrl).href;
|
|
11
|
-
const abort = new AbortController();
|
|
12
|
-
let unsubscribeDom = null;
|
|
13
|
-
let timer = 0;
|
|
14
|
-
let destroyed = false;
|
|
15
|
-
|
|
16
|
-
const currentContext = () => {
|
|
17
|
-
const hash = String(location.hash || '').toLowerCase();
|
|
18
|
-
if (hash.includes('tab-adapters')) return ['Module', 'Module suchen, Version prüfen, Instanz konfigurieren und Datenpunkte kontrollieren.'];
|
|
19
|
-
if (hash.includes('tab-instances')) return ['Dienste', 'Status, Log und Abhängigkeiten prüfen, bevor ein Dienst neu gestartet wird.'];
|
|
20
|
-
if (hash.includes('tab-objects')) return ['Datenpunkte', 'Read-DPs bleiben lesend. Write-DPs lassen sich typgerecht bedienen; sicherheitsrelevante Writes benötigen Admin/Service.'];
|
|
21
|
-
if (hash.includes('tab-users')) return ['Zugänge & Rechte', 'Zugänge einer EOS-Rolle zuordnen und persönliche Passwörter sicher zurücksetzen.'];
|
|
22
|
-
if (hash.includes('tab-intro')) return ['Übersicht', 'Anlagenstatus, Module, Dienste und die für deine Rolle freigegebenen Bereiche im Blick behalten.'];
|
|
23
|
-
return ['EOS Assist', 'NexoWatt EOS unterstützt dich beim Einrichten, Prüfen und Absichern des Systems.'];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const answer = query => {
|
|
27
|
-
const text = String(query || '').toLowerCase();
|
|
28
|
-
if (/datenpunkt|write|schreib|wert|state/.test(text)) return 'Prüfe Schreibfreigabe, Datentyp und EOS-Rolle. Boolean und Trigger werden direkt bedient; Zahlen, Texte und Auswahlwerte öffnen den nativen Wertdialog.';
|
|
29
|
-
if (/update|aktualis/.test(text)) return 'Warte, bis Module und Verbindung vollständig geladen sind. Danach das Update starten und den Befehlsdialog bis zum Abschluss geöffnet lassen.';
|
|
30
|
-
if (/wallbox|ocpp|lade/.test(text)) return 'Für Ladepunkte zuerst Status und Frische der OCPP-Daten prüfen. Schreibbefehle nur über die vorgesehenen Steuerdatenpunkte senden.';
|
|
31
|
-
if (/rechte|benutzer|rolle|passwort/.test(text)) return 'Admin/Service besitzt Vollzugriff. Installateur erhält Inbetriebnahme und Fehlersuche sowie Endkunden-Reset. Endkunde erhält freigegebene Bedien- und Smart-Home-Funktionen.';
|
|
32
|
-
if (/backup|sicherung/.test(text)) return 'Für den normalen Betrieb die NexoWatt Sicherung verwenden. Die interne System-Notfallsicherung ist ausschließlich für Admin/Service sichtbar.';
|
|
33
|
-
return 'Beschreibe Gerät, Datenpunkt oder Fehlerbild genauer. EOS Assist grenzt danach Oberfläche, Modul, Protokoll oder Gerätekommunikation ein.';
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const findToolbar = () => document.querySelector('.eos-top-toolbar, #root > .MuiPaper-root > .MuiAppBar-root .MuiToolbar-root, .MuiAppBar-root .MuiToolbar-root');
|
|
37
|
-
const findUserAnchor = toolbar => Array.from(toolbar?.children || []).find(child => child.querySelector?.('.MuiAvatar-root') || /\b(admin|installer|guest|endkunde|service)\b/i.test(child.textContent || '')) || null;
|
|
38
|
-
|
|
39
|
-
const remove = () => document.getElementById('eos-assist-root')?.remove();
|
|
40
|
-
const ensure = () => {
|
|
41
|
-
if (destroyed) return;
|
|
42
|
-
const hasApp = !!document.getElementById('app-paper');
|
|
43
|
-
const login = document.documentElement.classList.contains('eos-login');
|
|
44
|
-
const toolbar = findToolbar();
|
|
45
|
-
if (!hasApp || login || !toolbar) {
|
|
46
|
-
remove();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
toolbar.classList.add('eos-top-toolbar');
|
|
50
|
-
let root = document.getElementById('eos-assist-root');
|
|
51
|
-
if (!root) {
|
|
52
|
-
root = document.createElement('section');
|
|
53
|
-
root.id = 'eos-assist-root';
|
|
54
|
-
root.className = 'eos-assist-root eos-assist-header-root';
|
|
55
|
-
root.innerHTML = `
|
|
56
|
-
<button class="eos-assist-button" type="button" aria-expanded="false" title="EOS Assist">
|
|
57
|
-
<span class="eos-assist-dot"></span><strong>EOS Assist</strong><small>Hilfe</small>
|
|
58
|
-
</button>
|
|
59
|
-
<div class="eos-assist-panel" role="dialog" aria-label="EOS Assist">
|
|
60
|
-
<div class="eos-assist-head"><img src="${logoUrl}" alt="NexoWatt EOS"><div><strong class="eos-assist-title"></strong><span class="eos-assist-text"></span></div><button class="eos-assist-close" type="button" aria-label="Schließen">×</button></div>
|
|
61
|
-
<div class="eos-assist-input-row"><input class="eos-assist-input" placeholder="Frage zu EOS, Modul oder Datenpunkt"><button class="eos-assist-send" type="button">Fragen</button></div>
|
|
62
|
-
<div class="eos-assist-answer">Beschreibe kurz, was geprüft oder eingerichtet werden soll.</div>
|
|
63
|
-
</div>`;
|
|
64
|
-
const userAnchor = findUserAnchor(toolbar);
|
|
65
|
-
toolbar.insertBefore(root, userAnchor);
|
|
66
|
-
root.addEventListener('click', event => {
|
|
67
|
-
const button = event.target?.closest?.('button');
|
|
68
|
-
if (!button) return;
|
|
69
|
-
if (button.classList.contains('eos-assist-button')) {
|
|
70
|
-
const open = !root.classList.contains('eos-assist-open');
|
|
71
|
-
root.classList.toggle('eos-assist-open', open);
|
|
72
|
-
button.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
73
|
-
if (open) setTimeout(() => root.querySelector('.eos-assist-input')?.focus(), 30);
|
|
74
|
-
} else if (button.classList.contains('eos-assist-close')) {
|
|
75
|
-
root.classList.remove('eos-assist-open');
|
|
76
|
-
root.querySelector('.eos-assist-button')?.setAttribute('aria-expanded', 'false');
|
|
77
|
-
} else if (button.classList.contains('eos-assist-send')) {
|
|
78
|
-
const input = root.querySelector('.eos-assist-input');
|
|
79
|
-
root.querySelector('.eos-assist-answer').textContent = answer(input?.value || '');
|
|
80
|
-
}
|
|
81
|
-
}, { signal: abort.signal });
|
|
82
|
-
root.addEventListener('keydown', event => {
|
|
83
|
-
if (event.key === 'Enter' && event.target?.classList?.contains('eos-assist-input')) {
|
|
84
|
-
root.querySelector('.eos-assist-answer').textContent = answer(event.target.value || '');
|
|
85
|
-
}
|
|
86
|
-
if (event.key === 'Escape') {
|
|
87
|
-
root.classList.remove('eos-assist-open');
|
|
88
|
-
root.querySelector('.eos-assist-button')?.setAttribute('aria-expanded', 'false');
|
|
89
|
-
}
|
|
90
|
-
}, { signal: abort.signal });
|
|
91
|
-
} else if (root.parentElement !== toolbar) {
|
|
92
|
-
const userAnchor = findUserAnchor(toolbar);
|
|
93
|
-
toolbar.insertBefore(root, userAnchor);
|
|
94
|
-
}
|
|
95
|
-
const [title, text] = currentContext();
|
|
96
|
-
root.querySelector('.eos-assist-title').textContent = title;
|
|
97
|
-
root.querySelector('.eos-assist-text').textContent = text;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const connect = () => {
|
|
101
|
-
const coordinator = window.NEXOWATT_EOS_DOM_COORDINATOR;
|
|
102
|
-
if (!coordinator?.subscribe) {
|
|
103
|
-
timer = setTimeout(connect, 250);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
unsubscribeDom = coordinator.subscribe(ensure);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
window.addEventListener('hashchange', ensure, { signal: abort.signal });
|
|
110
|
-
window.addEventListener('resize', ensure, { signal: abort.signal });
|
|
111
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ensure, { once: true, signal: abort.signal });
|
|
112
|
-
else ensure();
|
|
113
|
-
connect();
|
|
114
|
-
[250, 800, 1600].forEach(delay => setTimeout(ensure, delay));
|
|
115
|
-
|
|
116
|
-
window.NEXOWATT_EOS_ASSIST = {
|
|
117
|
-
version: VERSION,
|
|
118
|
-
refresh: ensure,
|
|
119
|
-
destroy() {
|
|
120
|
-
destroyed = true;
|
|
121
|
-
abort.abort();
|
|
122
|
-
unsubscribeDom?.();
|
|
123
|
-
if (timer) clearTimeout(timer);
|
|
124
|
-
remove();
|
|
125
|
-
},
|
|
126
|
-
};
|
|
3
|
+
// NexoWatt EOS stable 7.9.95: EOS Assist remains disabled until the
|
|
4
|
+
// read-only system assistant is rebuilt and accepted separately.
|
|
5
|
+
window.NEXOWATT_EOS_ASSIST_DISABLED = true;
|
|
6
|
+
document.documentElement.classList.add('eos-assist-disabled');
|
|
7
|
+
document.querySelectorAll('.eos-assist-root,.eos-assist-header-root,[data-eos-assist-root]').forEach(element => element.remove());
|
|
127
8
|
})();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* NexoWatt EOS stable runtime v94 */
|
|
2
|
+
(() => {
|
|
3
|
+
'use strict';
|
|
4
|
+
const MANAGED_USERS = new Set(['installer', 'guest', 'user']);
|
|
5
|
+
const ASSIST_TEXT = /EOS\s*Assist/i;
|
|
6
|
+
const PASSWORDLESS_TEXT = /Erstanmeldung\s+ohne\s+Passwort|first\s+sign-?in\s+without\s+password/i;
|
|
7
|
+
const isLogin = () => /(?:\?|&)login(?:[=&]|$)/i.test(location.search) || /\/login(?:[/?#]|$)/i.test(location.pathname + location.hash);
|
|
8
|
+
const visible = el => !!el && el.getClientRects().length > 0;
|
|
9
|
+
const findInput = kind => {
|
|
10
|
+
const inputs = [...document.querySelectorAll('input')].filter(visible);
|
|
11
|
+
if (kind === 'password') return inputs.find(i => i.type === 'password');
|
|
12
|
+
return inputs.find(i => i.type !== 'password' && /user|login|name/i.test(`${i.name} ${i.id} ${i.autocomplete} ${i.getAttribute('aria-label')||''}`)) || inputs.find(i => i.type !== 'password');
|
|
13
|
+
};
|
|
14
|
+
const findSubmit = () => [...document.querySelectorAll('button,input[type="submit"]')].find(el => visible(el) && (/anmelden|login|sign in/i.test((el.textContent||el.value||'').trim()) || el.type === 'submit'));
|
|
15
|
+
const commonAncestor = (a,b) => {
|
|
16
|
+
if (!a || !b) return null;
|
|
17
|
+
const parents = new Set(); let n=a;
|
|
18
|
+
while(n){parents.add(n);n=n.parentElement;}
|
|
19
|
+
n=b; while(n&&!parents.has(n))n=n.parentElement;
|
|
20
|
+
return n;
|
|
21
|
+
};
|
|
22
|
+
const markLogin = () => {
|
|
23
|
+
if (!isLogin()) return;
|
|
24
|
+
document.documentElement.classList.add('nw-eos-login-v94');
|
|
25
|
+
document.body?.classList.add('nw-eos-login-v94');
|
|
26
|
+
const u=findInput('user'), p=findInput('password'), submit=findSubmit();
|
|
27
|
+
if (u && p) {
|
|
28
|
+
let card=commonAncestor(u,p);
|
|
29
|
+
while(card && card.parentElement && card.querySelectorAll('input').length < 2) card=card.parentElement;
|
|
30
|
+
if (card) {
|
|
31
|
+
card.setAttribute('data-nw-login-card','true');
|
|
32
|
+
let outer=card.parentElement;
|
|
33
|
+
for(let i=0;i<3 && outer?.parentElement;i++){
|
|
34
|
+
if(outer.getBoundingClientRect().width > card.getBoundingClientRect().width+20) break;
|
|
35
|
+
outer=outer.parentElement;
|
|
36
|
+
}
|
|
37
|
+
outer?.setAttribute('data-nw-login-shell','true');
|
|
38
|
+
}
|
|
39
|
+
const sync=()=>{
|
|
40
|
+
const name=(u.value||'').trim().toLowerCase();
|
|
41
|
+
const pass=p.value||'';
|
|
42
|
+
if(submit && MANAGED_USERS.has(name) && !pass){
|
|
43
|
+
submit.disabled=true;
|
|
44
|
+
submit.setAttribute('aria-disabled','true');
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
if(!u.dataset.nwV94){u.dataset.nwV94='1';u.addEventListener('input',sync);p.addEventListener('input',sync);sync();}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const hideLegacyUi = () => {
|
|
51
|
+
for (const el of document.querySelectorAll('button,a,[role="button"],div,span')) {
|
|
52
|
+
const text=(el.textContent||'').trim();
|
|
53
|
+
if(!text || text.length>100) continue;
|
|
54
|
+
if(ASSIST_TEXT.test(text) || PASSWORDLESS_TEXT.test(text)){
|
|
55
|
+
const target=el.closest('button,a,[role="button"],[data-eos-assist],.eos-assist') || el;
|
|
56
|
+
target.style.setProperty('display','none','important');
|
|
57
|
+
target.setAttribute('aria-hidden','true');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const apply=()=>{markLogin();hideLegacyUi();};
|
|
62
|
+
const style=document.createElement('style');
|
|
63
|
+
style.id='nw-eos-stable-v94-css';
|
|
64
|
+
style.textContent=`
|
|
65
|
+
html.nw-eos-login-v94,body.nw-eos-login-v94{min-height:100%;overflow-x:hidden!important;overflow-y:auto!important}
|
|
66
|
+
.nw-eos-login-v94 [data-nw-login-shell="true"]{width:min(410px,calc(100vw - 28px))!important;min-height:540px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding:22px!important}
|
|
67
|
+
.nw-eos-login-v94 [data-nw-login-card="true"]{width:min(354px,calc(100vw - 56px))!important;min-height:474px!important;height:auto!important;max-height:none!important;overflow:visible!important;box-sizing:border-box!important;padding-bottom:26px!important}
|
|
68
|
+
.nw-eos-login-v94 [data-nw-login-card="true"] *{scrollbar-width:none}
|
|
69
|
+
.nw-eos-login-v94 [data-nw-login-card="true"] *::-webkit-scrollbar{display:none!important;width:0!important;height:0!important}
|
|
70
|
+
.nw-eos-login-v94 [data-nw-login-card="true"] [role="alert"]{position:static!important;max-height:none!important;overflow:visible!important;white-space:normal!important}
|
|
71
|
+
[data-eos-assist],.eos-assist,.nexowatt-eos-assist{display:none!important}
|
|
72
|
+
@media(max-height:620px){.nw-eos-login-v94 [data-nw-login-shell="true"]{min-height:0!important;margin:16px auto!important}.nw-eos-login-v94 [data-nw-login-card="true"]{min-height:0!important}}
|
|
73
|
+
`;
|
|
74
|
+
(document.head||document.documentElement).appendChild(style);
|
|
75
|
+
const observer=new MutationObserver(()=>requestAnimationFrame(apply));
|
|
76
|
+
observer.observe(document.documentElement,{subtree:true,childList:true,attributes:true,attributeFilter:['class','style']});
|
|
77
|
+
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',apply,{once:true});else apply();
|
|
78
|
+
setInterval(apply,1500);
|
|
79
|
+
})();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startNexowattStableAuth = startNexowattStableAuth;
|
|
4
|
+
const START_PASSWORD = 'nexowatt';
|
|
5
|
+
const MARKER_KEYS = ["eosFirstLoginPending", "eosMustChangePassword", "eosPasswordSetupRequired", "firstLoginRequired", "mustChangePassword", "nexowattFirstLoginPending", "nexowattMustChangePassword", "nexowattPasswordSetupRequired", "passwordChangeRequired"];
|
|
6
|
+
const timers = new WeakMap();
|
|
7
|
+
function pendingByNative(native) {
|
|
8
|
+
if (!native || typeof native !== 'object') return false;
|
|
9
|
+
return MARKER_KEYS.some(key => native[key] === true || native[key] === 'pending' || native[key] === 'required' || native[key] === 'starter');
|
|
10
|
+
}
|
|
11
|
+
async function setPassword(adapter, user, password) {
|
|
12
|
+
if (typeof adapter.setPasswordAsync === 'function') return adapter.setPasswordAsync(user, password, { user: 'system.user.admin' });
|
|
13
|
+
return new Promise((resolve, reject) => adapter.setPassword(user, password, { user: 'system.user.admin' }, err => err ? reject(err) : resolve()));
|
|
14
|
+
}
|
|
15
|
+
async function ensureGroupMembership(adapter, userId, groupId) {
|
|
16
|
+
const group = await adapter.getForeignObjectAsync(groupId);
|
|
17
|
+
if (!group || group.type !== 'group') return;
|
|
18
|
+
group.common = group.common || { name: groupId.split('.').pop(), members: [] };
|
|
19
|
+
group.common.members = Array.isArray(group.common.members) ? group.common.members : [];
|
|
20
|
+
if (!group.common.members.includes(userId)) {
|
|
21
|
+
group.common.members.push(userId);
|
|
22
|
+
await adapter.setForeignObjectAsync(groupId, group, { user: 'system.user.admin' });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function createUser(adapter, name) {
|
|
26
|
+
const id = `system.user.${name}`;
|
|
27
|
+
await adapter.setForeignObjectAsync(id, {
|
|
28
|
+
_id: id, type: 'user',
|
|
29
|
+
common: { name, enabled: true, password: '' },
|
|
30
|
+
native: { nexowattManagedAccount: true, nexowattMustChangePassword: true, nexowattPasswordState: 'starter' }
|
|
31
|
+
}, { user: 'system.user.admin' });
|
|
32
|
+
return adapter.getForeignObjectAsync(id);
|
|
33
|
+
}
|
|
34
|
+
async function prepareOne(adapter, name, group, createMissing) {
|
|
35
|
+
const id = `system.user.${name}`;
|
|
36
|
+
let obj = await adapter.getForeignObjectAsync(id);
|
|
37
|
+
let created = false;
|
|
38
|
+
if (!obj && createMissing) { obj = await createUser(adapter, name); created = true; }
|
|
39
|
+
if (!obj || obj.type !== 'user') return;
|
|
40
|
+
obj.native = obj.native || {};
|
|
41
|
+
const empty = !obj.common || !obj.common.password;
|
|
42
|
+
const pending = created || empty || pendingByNative(obj.native) || obj.native.nexowattPasswordState === 'starter';
|
|
43
|
+
if (pending) {
|
|
44
|
+
await setPassword(adapter, name, START_PASSWORD);
|
|
45
|
+
obj = await adapter.getForeignObjectAsync(id) || obj;
|
|
46
|
+
obj.native = obj.native || {};
|
|
47
|
+
obj.native.nexowattManagedAccount = true;
|
|
48
|
+
obj.native.nexowattStarterPasswordActive = true;
|
|
49
|
+
obj.native.nexowattMustChangePassword = true;
|
|
50
|
+
obj.native.nexowattPasswordState = 'starter';
|
|
51
|
+
obj.native.nexowattStarterPasswordUpdatedAt = new Date().toISOString();
|
|
52
|
+
for (const key of MARKER_KEYS) {
|
|
53
|
+
if (/required|must|pending|first/i.test(key)) obj.native[key] = true;
|
|
54
|
+
}
|
|
55
|
+
await adapter.setForeignObjectAsync(id, obj, { user: 'system.user.admin' });
|
|
56
|
+
}
|
|
57
|
+
await ensureGroupMembership(adapter, id, group);
|
|
58
|
+
}
|
|
59
|
+
async function reconcile(adapter) {
|
|
60
|
+
try {
|
|
61
|
+
await prepareOne(adapter, 'installer', 'system.group.installer', true);
|
|
62
|
+
const guest = await adapter.getForeignObjectAsync('system.user.guest');
|
|
63
|
+
const user = await adapter.getForeignObjectAsync('system.user.user');
|
|
64
|
+
if (guest || !user) await prepareOne(adapter, 'guest', 'system.group.endkunden', true);
|
|
65
|
+
if (user) await prepareOne(adapter, 'user', 'system.group.endkunden', false);
|
|
66
|
+
} catch (e) { adapter.log.warn(`[NexoWatt EOS login] Starter-account reconciliation failed: ${e && e.message || e}`); }
|
|
67
|
+
}
|
|
68
|
+
function startNexowattStableAuth(adapter) {
|
|
69
|
+
if (timers.has(adapter)) return;
|
|
70
|
+
void reconcile(adapter);
|
|
71
|
+
const timer = setInterval(() => void reconcile(adapter), 5000);
|
|
72
|
+
if (timer.unref) timer.unref();
|
|
73
|
+
timers.set(adapter, timer);
|
|
74
|
+
}
|
package/build/lib/web.js
CHANGED
|
@@ -675,7 +675,11 @@ class Web {
|
|
|
675
675
|
const account = (native.nexowattEosAccount || {});
|
|
676
676
|
const initialized = account.passwordInitialized === true
|
|
677
677
|
|| Number(account.passwordSetupVersion || account.passwordInitializationVersion || 0) >= 1;
|
|
678
|
-
const
|
|
678
|
+
const stableForced = native.nexowattPasswordChangeRequired === true
|
|
679
|
+
|| native.eosPasswordChangeRequired === true
|
|
680
|
+
|| native.nexowattFirstLoginPending === true
|
|
681
|
+
|| native.eosFirstLoginRequired === true;
|
|
682
|
+
const forced = account.forcePasswordChange === true || stableForced;
|
|
679
683
|
return { required: forced || !initialized, initialized: initialized && !forced, minLength, version: 1 };
|
|
680
684
|
}
|
|
681
685
|
catch (e) {
|
|
@@ -789,6 +793,12 @@ class Web {
|
|
|
789
793
|
account.firstLoginCompletedAt = now;
|
|
790
794
|
account.forcePasswordChange = false;
|
|
791
795
|
account.passwordlessFirstLoginAllowed = false;
|
|
796
|
+
native.nexowattPasswordChangeRequired = false;
|
|
797
|
+
native.eosPasswordChangeRequired = false;
|
|
798
|
+
native.nexowattFirstLoginPending = false;
|
|
799
|
+
native.eosFirstLoginRequired = false;
|
|
800
|
+
native.nexowattInitialPasswordApplied = false;
|
|
801
|
+
native.nexowattPasswordChangedAt = now;
|
|
792
802
|
await this.adapter.setForeignObjectAsync(access.userId, userObject);
|
|
793
803
|
await this.destroyEosRequestSessions(req);
|
|
794
804
|
for (const cookie of ['access_token', 'refresh_token', 'connect.sid']) {
|
|
@@ -1133,8 +1143,7 @@ class Web {
|
|
|
1133
1143
|
res.status(409).json({ error: 'accountDisabled' });
|
|
1134
1144
|
return;
|
|
1135
1145
|
}
|
|
1136
|
-
|
|
1137
|
-
await this.setEosUserPassword(targetUserId, bootstrapSecret);
|
|
1146
|
+
await this.setEosUserPassword(targetUserId, 'nexowatt');
|
|
1138
1147
|
const user = (await this.adapter.getForeignObjectAsync(targetUserId));
|
|
1139
1148
|
if (!user) {
|
|
1140
1149
|
throw new Error('userObjectUnavailableAfterPasswordReset');
|
|
@@ -1146,21 +1155,29 @@ class Web {
|
|
|
1146
1155
|
account.passwordSetupVersion = 0;
|
|
1147
1156
|
account.passwordInitializationVersion = 0;
|
|
1148
1157
|
account.forcePasswordChange = true;
|
|
1149
|
-
account.passwordlessFirstLoginAllowed =
|
|
1158
|
+
account.passwordlessFirstLoginAllowed = false;
|
|
1150
1159
|
account.passwordResetAt = now;
|
|
1151
1160
|
account.passwordResetBy = access.userId;
|
|
1152
|
-
account.passwordResetMode = '
|
|
1161
|
+
account.passwordResetMode = 'initial-password';
|
|
1162
|
+
native.nexowattPasswordChangeRequired = true;
|
|
1163
|
+
native.eosPasswordChangeRequired = true;
|
|
1164
|
+
native.nexowattFirstLoginPending = true;
|
|
1165
|
+
native.eosFirstLoginRequired = true;
|
|
1166
|
+
native.nexowattInitialPasswordApplied = true;
|
|
1167
|
+
native.nexowattInitialPasswordVersion = 1;
|
|
1168
|
+
native.nexowattStableInitialCredentialVersion = 1;
|
|
1153
1169
|
delete account.passwordSetAt;
|
|
1154
1170
|
delete account.firstLoginCompletedAt;
|
|
1155
1171
|
await this.adapter.setForeignObjectAsync(targetUserId, user);
|
|
1156
1172
|
this.invalidateEosPasswordClaimsForUser(targetUserId);
|
|
1157
|
-
this.adapter.log.warn(`EOS account ${targetUserId} was reset
|
|
1173
|
+
this.adapter.log.warn(`EOS account ${targetUserId} was reset to the mandatory initial-password flow by ${access.userId}`);
|
|
1158
1174
|
res.status(200).json({
|
|
1159
1175
|
success: true,
|
|
1160
1176
|
user: targetUserId,
|
|
1161
1177
|
role: explicitlyManagedRole,
|
|
1162
1178
|
firstLoginRequired: true,
|
|
1163
|
-
passwordlessFirstLoginAllowed:
|
|
1179
|
+
passwordlessFirstLoginAllowed: false,
|
|
1180
|
+
initialPasswordRequired: true,
|
|
1164
1181
|
});
|
|
1165
1182
|
}
|
|
1166
1183
|
async getEosHistoryInstances() {
|