iobroker.eos-admin 7.10.4 → 7.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/INSTALL_TEST_V7.10.6.md +8 -0
  2. package/MERGE_UPDATE_README_V7.10.6.md +7 -0
  3. package/NEXOWATT_EOS_BUILD_INFO.json +19 -18
  4. package/NEXOWATT_EOS_PREBUILT_MANIFEST.json +40 -41
  5. package/PUBLISH_STABLE_V7.10.6.md +8 -0
  6. package/README_STABILITY_V7.10.6.md +5 -0
  7. package/RELEASE_ACCEPTANCE_V7.10.6.md +11 -0
  8. package/RELEASE_NOTES_V7.10.6.md +14 -0
  9. package/SECURITY_ROLE_MATRIX_V7.10.6.md +12 -0
  10. package/adminWww/assets/Intro-DkwRiz1n-v84.js +2 -2
  11. package/adminWww/assets/Objects-DPan0bzw-v84.js +1 -1
  12. package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
  13. package/adminWww/css/nexowatt-native-shell.css +12 -1
  14. package/adminWww/index.html +14 -15
  15. package/adminWww/js/eos-role-bootstrap.js +5 -5
  16. package/adminWww/js/eos-role-ui.js +211 -17
  17. package/adminWww/js/nexowatt-native-shell.js +1 -1
  18. package/adminWww/static/js/{nexowatt-stable-v7101.js → nexowatt-stable-v7106.js} +1 -1
  19. package/build/lib/web.js +34 -0
  20. package/build/main.js +13 -15
  21. package/io-package.json +32 -5
  22. package/package.json +11 -10
  23. package/tools/nexowatt-account-password-flow-selftest.cjs +3 -3
  24. package/tools/nexowatt-clean-legacy-runtime.cjs +6 -0
  25. package/tools/nexowatt-internal-reserve-selftest.cjs +1 -1
  26. package/tools/nexowatt-login-layout-selftest.cjs +3 -3
  27. package/tools/nexowatt-merge-update-selftest.cjs +1 -1
  28. package/tools/nexowatt-modern-ui-selftest.cjs +2 -2
  29. package/tools/nexowatt-role-security-selftest.cjs +147 -41
  30. package/tools/nexowatt-runtime-cleanup-selftest.cjs +4 -0
  31. package/tools/nexowatt-stable-v7103-selftest.cjs +4 -4
  32. package/tools/nexowatt-ui-overview-runtime-selftest.cjs +2 -2
  33. package/tools/nexowatt-user-area-selftest.cjs +36 -0
  34. package/tools/nexowatt-validate-package.cjs +9 -8
  35. package/INSTALL_TEST_V7.10.4.md +0 -9
  36. package/MERGE_UPDATE_README_V7.10.4.md +0 -3
  37. package/PUBLISH_STABLE_V7.10.4.md +0 -10
  38. package/README_STABILITY_V7.10.4.md +0 -12
  39. package/RELEASE_ACCEPTANCE_V7.10.4.md +0 -3
  40. package/RELEASE_NOTES_V7.10.4.md +0 -11
  41. package/adminWww/nexowatt-role-security.js +0 -336
  42. package/build/lib/eosRoleSecurity.js +0 -310
  43. package/src-admin/README.md +0 -69
  44. package/src-admin/nexowatt-role-security.js +0 -336
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "eos-admin",
4
- "version": "7.10.4",
4
+ "version": "7.10.6",
5
5
  "titleLang": {
6
6
  "en": "NexoWatt EOS Admin",
7
7
  "de": "NexoWatt EOS Admin",
@@ -18,6 +18,16 @@
18
18
  "connectionType": "local",
19
19
  "dataSource": "push",
20
20
  "news": {
21
+ "7.10.6": {
22
+ "de": "Stable 7.10.6 bereinigt ausschließlich den Endkundenbereich: Datenpunkte bleiben sichtbar und nur lesbar, Bearbeiten-/Löschen-/Einstellungsaktionen werden ausgeblendet, und die Systeminfokarte erhält Plattform, RAM, Node.js und NPM über einen authentifizierten Nur-Lese-Endpunkt. Die Administratoroberfläche und sämtliche Adminrechte bleiben unverändert.",
23
+ "en": "Stable 7.10.6 cleans up the End User area only: datapoints stay visible and read-only, edit/delete/settings actions are hidden, and the system information card receives platform, RAM, Node.js and NPM through an authenticated read-only endpoint. The administrator UI and all Admin rights remain unchanged.",
24
+ "nl": "Stable 7.10.6 ruimt uitsluitend het eindgebruikersgedeelte op: datapunten blijven zichtbaar en alleen-lezen, bewerk-/verwijder-/instellingsacties worden verborgen en de systeeminformatiekaart ontvangt platform, RAM, Node.js en NPM via een geauthenticeerd alleen-lezen eindpunt. De beheerdersinterface en alle beheerdersrechten blijven ongewijzigd."
25
+ },
26
+ "7.10.5": {
27
+ "de": "Stable 7.10.5 stellt die vollständigen Administratorrechte wieder her und trennt die Rollen verbindlich über den authentifizierten Backend-Kontext. Admin darf Datenpunkte schreiben, den Expertenmodus in beide Richtungen schalten und sieht Admin-/XTerm-Kacheln. Endkunden sehen Datenpunkte ausschließlich lesend; App-Center, Lizenz, Simulation, Benutzerverwaltung, ioBroker Admin, XTerm und Expertenmodus bleiben für Installateur und Endkunde gesperrt. Navigationsänderungen werden neustartsicher im Adapter-State gespeichert; Sicherung erscheint nur bei aktiver Backup-Instanz.",
28
+ "en": "Stable 7.10.5 restores full administrator authority and binds roles exclusively to the authenticated backend context. Admin can write datapoints, toggle expert mode in both directions and sees the Admin/XTerm cards. End users can view datapoints read-only; App Center, License, Simulation, account management, ioBroker Admin, XTerm and expert mode remain blocked for Installer and End User. Navigation changes are stored restart-safely in an adapter state, and Backup is shown only for an enabled backup instance.",
29
+ "nl": "Stable 7.10.5 herstelt de volledige beheerdersrechten en bepaalt rollen uitsluitend via de geauthenticeerde backendcontext. Admin kan datapunten schrijven, de expertmodus in beide richtingen schakelen en ziet de Admin-/XTerm-kaarten. Eindgebruikers kunnen datapunten alleen lezen; App Center, Licentie, Simulatie, accountbeheer, ioBroker Admin, XTerm en expertmodus blijven geblokkeerd voor Installateur en Eindgebruiker. Navigatiewijzigingen worden herstartveilig in een adapterstatus opgeslagen en Back-up verschijnt alleen bij een ingeschakelde back-upinstantie."
30
+ },
21
31
  "7.10.4": {
22
32
  "de": "Stable 7.10.4 verschiebt die Einstellung für automatische NexoWatt-Adapter-Updates vollständig in die Systemeinstellungen hinter dem Schraubenschlüssel. Auf Übersicht, Module und allen anderen Hauptseiten wird die Update-Karte nicht mehr eingeblendet. Die stabile Update-Logik und die Absturzkorrekturen aus 7.10.2 bleiben unverändert.",
23
33
  "en": "Stable 7.10.4 moves the automatic NexoWatt adapter update control entirely into System Settings behind the wrench button. The update card is no longer shown on Overview, Modules or any other main page. Stable update behavior and the 7.10.2 crash fixes remain unchanged.",
@@ -349,7 +359,7 @@
349
359
  "icon": "admin.svg",
350
360
  "messagebox": true,
351
361
  "enabled": true,
352
- "extIcon": "https://unpkg.com/iobroker.eos-admin@7.10.4/admin/admin.svg",
362
+ "extIcon": "https://unpkg.com/iobroker.eos-admin@7.10.6/admin/admin.svg",
353
363
  "keywords": [
354
364
  "NexoWatt",
355
365
  "EOS",
@@ -360,7 +370,7 @@
360
370
  "licensed"
361
371
  ],
362
372
  "compact": true,
363
- "readme": "https://unpkg.com/iobroker.eos-admin@7.10.4/README.md",
373
+ "readme": "https://unpkg.com/iobroker.eos-admin@7.10.6/README.md",
364
374
  "authors": [
365
375
  "NexoWatt"
366
376
  ],
@@ -428,7 +438,7 @@
428
438
  "nondeletable": false,
429
439
  "allowAdapterUpdate": true,
430
440
  "allowAdapterDelete": false,
431
- "meta": "https://unpkg.com/iobroker.eos-admin@7.10.4/io-package.json",
441
+ "meta": "https://unpkg.com/iobroker.eos-admin@7.10.6/io-package.json",
432
442
  "npmPackage": "iobroker.eos-admin",
433
443
  "packetName": "iobroker.eos-admin",
434
444
  "publisher": "NexoWatt"
@@ -1001,6 +1011,23 @@
1001
1011
  "def": ""
1002
1012
  },
1003
1013
  "native": {}
1014
+ },
1015
+ {
1016
+ "_id": "info.uiTabsVisible",
1017
+ "type": "state",
1018
+ "common": {
1019
+ "name": {
1020
+ "en": "EOS navigation layout",
1021
+ "de": "EOS-Navigationslayout",
1022
+ "nl": "EOS-navigatielay-out"
1023
+ },
1024
+ "role": "json",
1025
+ "type": "string",
1026
+ "read": true,
1027
+ "write": true,
1028
+ "def": "[]"
1029
+ },
1030
+ "native": {}
1004
1031
  }
1005
1032
  ],
1006
1033
  "schemas": {
@@ -1188,5 +1215,5 @@
1188
1215
  }
1189
1216
  }
1190
1217
  },
1191
- "version": "7.10.4"
1218
+ "version": "7.10.6"
1192
1219
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iobroker.eos-admin",
3
3
  "description": "NexoWatt EOS Admin – repaired stable baseline with restart-safe automatic updates, native account passwords and persistent cockpit scrolling.",
4
- "version": "7.10.4",
4
+ "version": "7.10.6",
5
5
  "contributors": [
6
6
  "bluefox <dogafox@gmail.com>",
7
7
  "apollon77",
@@ -133,15 +133,16 @@
133
133
  "NEXOWATT_EOS_PREBUILT_MANIFEST.json",
134
134
  "tools/nexowatt-prebuilt-release-selftest.cjs",
135
135
  "tools/nexowatt-stable-v7103-selftest.cjs",
136
- "README_STABILITY_V7.10.4.md",
137
- "RELEASE_NOTES_V7.10.4.md",
138
- "PUBLISH_STABLE_V7.10.4.md",
139
- "INSTALL_TEST_V7.10.4.md",
140
- "RELEASE_ACCEPTANCE_V7.10.4.md",
141
- "MERGE_UPDATE_README_V7.10.4.md",
136
+ "README_STABILITY_V7.10.6.md",
137
+ "RELEASE_NOTES_V7.10.6.md",
138
+ "PUBLISH_STABLE_V7.10.6.md",
139
+ "INSTALL_TEST_V7.10.6.md",
140
+ "RELEASE_ACCEPTANCE_V7.10.6.md",
141
+ "MERGE_UPDATE_README_V7.10.6.md",
142
142
  "tools/nexowatt-auto-update-placement-selftest.cjs",
143
143
  "tools/nexowatt-role-security-selftest.cjs",
144
- "src-admin/nexowatt-role-security.js"
144
+ "SECURITY_ROLE_MATRIX_V7.10.6.md",
145
+ "tools/nexowatt-user-area-selftest.cjs"
145
146
  ],
146
147
  "scripts": {
147
148
  "npm": "npm i -f && cd src-admin && npm i -f",
@@ -176,7 +177,7 @@
176
177
  "nexowatt:repo-entry": "node tools/nexowatt-generate-repo-entry.cjs",
177
178
  "nexowatt:patch-repo": "node tools/nexowatt-patch-repo.cjs",
178
179
  "nexowatt:repair-update": "node tools/nexowatt-repair-eos-admin-update.cjs",
179
- "check:eos-stability": "node tools/nexowatt-stability-check.cjs && node tools/nexowatt-esm-syntax-selftest.cjs && node tools/nexowatt-backend-runtime-selftest.cjs && node tools/nexowatt-prebuilt-release-selftest.cjs && node tools/nexowatt-import-integrity-selftest.cjs && node tools/nexowatt-entrypoint-smoke-selftest.cjs && node tools/nexowatt-frontend-graph-check.cjs && node tools/nexowatt-policy-selftest.cjs && node tools/nexowatt-publish-channel-selftest.cjs && node tools/nexowatt-release-defaults-selftest.cjs && node tools/nexowatt-role-access-selftest.cjs && node tools/nexowatt-internal-reserve-selftest.cjs && node tools/nexowatt-modern-ui-selftest.cjs && node tools/nexowatt-login-layout-selftest.cjs && node tools/nexowatt-branding-selftest.cjs && node tools/nexowatt-default-port-selftest.cjs && node tools/nexowatt-module-popup-selftest.cjs && node tools/nexowatt-datapoint-selftest.cjs && node tools/nexowatt-update-selftest.cjs && node tools/nexowatt-datapoint-interaction-selftest.cjs && node tools/nexowatt-manual-write-policy-selftest.cjs && node tools/nexowatt-value-column-selftest.cjs && node tools/nexowatt-native-shell-selftest.cjs && node tools/nexowatt-assistant-separation-selftest.cjs && node tools/nexowatt-runtime-cleanup-selftest.cjs && node tools/nexowatt-version-sync-selftest.cjs && node tools/nexowatt-merge-update-selftest.cjs && node tools/nexowatt-password-write-selftest.cjs && node tools/nexowatt-account-password-flow-selftest.cjs && npm run check:eos-stable-v7103 && node tools/nexowatt-ems-overview-selftest.cjs && node tools/nexowatt-ui-overview-runtime-selftest.cjs && node tools/nexowatt-auto-update-selftest.cjs && node tools/nexowatt-auto-update-placement-selftest.cjs && node tools/nexowatt-role-security-selftest.cjs",
180
+ "check:eos-stability": "node tools/nexowatt-stability-check.cjs && node tools/nexowatt-esm-syntax-selftest.cjs && node tools/nexowatt-backend-runtime-selftest.cjs && node tools/nexowatt-prebuilt-release-selftest.cjs && node tools/nexowatt-import-integrity-selftest.cjs && node tools/nexowatt-entrypoint-smoke-selftest.cjs && node tools/nexowatt-frontend-graph-check.cjs && node tools/nexowatt-policy-selftest.cjs && node tools/nexowatt-publish-channel-selftest.cjs && node tools/nexowatt-release-defaults-selftest.cjs && node tools/nexowatt-role-access-selftest.cjs && node tools/nexowatt-internal-reserve-selftest.cjs && node tools/nexowatt-modern-ui-selftest.cjs && node tools/nexowatt-login-layout-selftest.cjs && node tools/nexowatt-branding-selftest.cjs && node tools/nexowatt-default-port-selftest.cjs && node tools/nexowatt-module-popup-selftest.cjs && node tools/nexowatt-datapoint-selftest.cjs && node tools/nexowatt-update-selftest.cjs && node tools/nexowatt-datapoint-interaction-selftest.cjs && node tools/nexowatt-manual-write-policy-selftest.cjs && node tools/nexowatt-value-column-selftest.cjs && node tools/nexowatt-native-shell-selftest.cjs && node tools/nexowatt-assistant-separation-selftest.cjs && node tools/nexowatt-runtime-cleanup-selftest.cjs && node tools/nexowatt-version-sync-selftest.cjs && node tools/nexowatt-merge-update-selftest.cjs && node tools/nexowatt-password-write-selftest.cjs && node tools/nexowatt-account-password-flow-selftest.cjs && npm run check:eos-stable-v7103 && node tools/nexowatt-ems-overview-selftest.cjs && node tools/nexowatt-ui-overview-runtime-selftest.cjs && node tools/nexowatt-auto-update-selftest.cjs && node tools/nexowatt-auto-update-placement-selftest.cjs && node tools/nexowatt-role-security-selftest.cjs && node tools/nexowatt-user-area-selftest.cjs",
180
181
  "nexowatt:patch-built-frontend": "node tools/nexowatt-patch-built-frontend.cjs",
181
182
  "clean:eos-runtime": "node tools/nexowatt-clean-legacy-runtime.cjs",
182
183
  "precheck:eos-package": "npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime",
@@ -216,7 +217,7 @@
216
217
  },
217
218
  "nexowattReleasePolicy": {
218
219
  "distTag": "latest",
219
- "purpose": "accepted stable NexoWatt EOS sales-product candidate 7.10.4"
220
+ "purpose": "accepted stable NexoWatt EOS End User read-only UI and system information correction 7.10.6"
220
221
  },
221
222
  "publisher": "NexoWatt"
222
223
  }
@@ -6,10 +6,10 @@ for(const code of [source,built]){
6
6
  must(code.includes("EOS_STABLE_INITIAL_PASSWORD = 'nexowatt'"),'initial password constant missing');
7
7
  must(code.includes("if (created || !String(object.common?.password || '').trim())")||code.includes("if (created || !String(object.common?.password || '').trim())"),'password is not limited to new/passwordless accounts');
8
8
  for(const marker of ['nexowattPasswordChangeRequired: false','eosPasswordChangeRequired: false','nexowattFirstLoginPending: false','eosFirstLoginRequired: false','forcePasswordChange: false','standardPasswordMode: true']) must(code.includes(marker),`legacy first-login flag is not cleared: ${marker}`);
9
- must(code.includes("command === 'changePassword'")&&code.includes('password target outside EOS role scope'),'native password command role guard missing');
9
+ must(code.includes("command === 'changePassword'")&&code.includes('password administration is Service-only'),'native password command role guard missing');
10
10
  }
11
11
  must(io.native.eosRequireFirstLoginPassword===false&&io.native.eosRequireFirstLoginPasswordChange===false,'forced first-password defaults remain');
12
12
  must(!boot.includes('installIntegratedFirstLogin(base);')&&!boot.includes('showFirstLoginPassword(resolved, base);'),'forced password surface remains active');
13
13
  must(!index.includes('eos-account-management.js'),'custom reset runtime remains active');
14
- must(role.includes('filterNativeUsersPage')&&role.includes("route === 'tab-users'"),'role-scoped native Users page missing');
15
- console.log('[NexoWatt EOS standard password flow] OK (nexowatt only on creation, native password editor, no forced reset flow)');
14
+ must(role.includes("route !== 'tab-users'")&&role.includes('filterNativeUsersPage'),'native Users page is not blocked for Installer/End User');
15
+ console.log('[NexoWatt EOS standard password flow] OK (initial password on creation, Admin-only native password editor, no forced reset flow)');
@@ -23,6 +23,12 @@ const LEGACY_OVERLAYS = [
23
23
  'src-admin/public/js/eos-runtime-fixes.js',
24
24
  'src-admin/public/js/eos-hard-logout.js',
25
25
  'src-admin/public/css/eos-branding.css',
26
+ // Removed 7.10.4 role-security overlays. These files must be deleted when
27
+ // a complete repository ZIP is accidentally extracted over an older tree;
28
+ // otherwise the sealed-release check correctly rejects them as stale input.
29
+ 'adminWww/nexowatt-role-security.js',
30
+ 'src-admin/nexowatt-role-security.js',
31
+ 'build/lib/eosRoleSecurity.js',
26
32
  ];
27
33
 
28
34
  const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -23,7 +23,7 @@ for (const code of [mainSource, mainBuilt]) {
23
23
  "LEGACY_BACKUP_ADAPTER_NAME = 'backitup'", 'shouldHideLegacyBackupFromNonAdmins',
24
24
  'getLegacyBackupAclObjectIds', 'ensureLegacyBackupVisibleOnlyToAdmins',
25
25
  'EOS ACL guard restricted internal', '/^system\\.adapter\\.(?:admin|backitup)(?:\\.|$)/',
26
- '/^(?:admin|backitup)\\.\\d+(?:\\.|$)/', 'internal Service reserve is Admin/Service-only',
26
+ '/^(?:admin|backitup|xterm)\\.\\d+(?:\\.|$)/', 'internal Service reserve is Admin/Service-only',
27
27
  ]) if (!code.includes(marker)) fail(`backend reserve marker missing: ${marker}`);
28
28
  }
29
29
  for (const code of [webSource, webBuilt]) {
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  const fs=require('node:fs'),path=require('node:path');const root=path.resolve(__dirname,'..');const read=r=>fs.readFileSync(path.join(root,r),'utf8');const fail=m=>{console.error('[NexoWatt EOS login layout] '+m);process.exit(1)};const must=(v,m)=>{if(!v)fail(m)};
4
- const css=read('adminWww/css/nexowatt-native-shell.css'),source=read('src-admin/public/css/nexowatt-native-shell.css'),login=read('src-admin/src/login/Login.tsx'),stableLogin=read('adminWww/static/js/nexowatt-stable-login.js'),stable=read('adminWww/static/js/nexowatt-stable-v7101.js'),index=read('adminWww/index.html');
4
+ const info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json')),shellTag=String(info.shellCacheTag||info.shellCacheVersion);const css=read('adminWww/css/nexowatt-native-shell.css'),source=read('src-admin/public/css/nexowatt-native-shell.css'),login=read('src-admin/src/login/Login.tsx'),stableLogin=read('adminWww/static/js/nexowatt-stable-login.js'),stable=read(`adminWww/static/js/nexowatt-stable-v${shellTag}.js`),index=read('adminWww/index.html');
5
5
  must(css===source,'CSS mirror drift');
6
6
  for(const marker of ['NexoWatt EOS 7.9.98 – stable scrolling','width: min(456px','width: min(400px','overflow: visible !important'])must(css.includes(marker),`base stable login marker missing: ${marker}`);
7
7
  must(login.includes('placeholder={`${I18n.t(\'enterLogin\')} *`}')&&login.includes('placeholder={`${I18n.t(\'enterPassword\')} *`}'),'clean placeholder login missing');
8
8
  must(!/data-nw-login-card|createElement\(['"]style/.test(stableLogin),'stable login runtime mutates geometry');
9
- must(stable.includes("VERSION = 'v7101-scroll-standard-password'")&&!stable.includes('first-password'),'stable v7101 runtime incorrect');
10
- must(index.includes('nexowatt-stable-v7101.js?v=7101'),'v7101 login runtime not active');
9
+ must(stable.includes(`VERSION = 'v${shellTag}-scroll-standard-password'`)&&!stable.includes('first-password'),'stable shell runtime incorrect');
10
+ must(index.includes(`nexowatt-stable-v${shellTag}.js?v=${shellTag}`),'current stable login runtime not active');
11
11
  console.log('[NexoWatt EOS login layout] OK');
@@ -16,7 +16,7 @@ for (const file of [
16
16
  'MERGE_UPDATE.ps1',
17
17
  'MERGE_UPDATE.cmd',
18
18
  'MERGE_UPDATE.sh',
19
- 'MERGE_UPDATE_README_V7.10.4.md',
19
+ 'MERGE_UPDATE_README_V7.10.6.md',
20
20
  'tools/nexowatt-apply-ems-overview.cjs',
21
21
  'tools/nexowatt-sync-release-version.cjs',
22
22
  'tools/nexowatt-version-sync-selftest.cjs',
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  const fs=require('node:fs'),path=require('node:path');const root=path.resolve(__dirname,'..');const read=r=>fs.readFileSync(path.join(root,r),'utf8');const fail=m=>{console.error('[NexoWatt EOS modern UI] '+m);process.exit(1)};const must=(v,m)=>{if(!v)fail(m)};
4
- const css=read('adminWww/css/nexowatt-native-shell.css'),sourceCss=read('src-admin/public/css/nexowatt-native-shell.css'),index=read('adminWww/index.html'),boot=read('adminWww/js/eos-role-bootstrap.js'),shell=read('adminWww/js/nexowatt-native-shell.js'),role=read('adminWww/js/eos-role-ui.js'),assist=read('adminWww/js/eos-assistant.js');
4
+ const info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json')),shellTag=String(info.shellCacheTag||info.shellCacheVersion);const css=read('adminWww/css/nexowatt-native-shell.css'),sourceCss=read('src-admin/public/css/nexowatt-native-shell.css'),index=read('adminWww/index.html'),boot=read('adminWww/js/eos-role-bootstrap.js'),shell=read('adminWww/js/nexowatt-native-shell.js'),role=read('adminWww/js/eos-role-ui.js'),assist=read('adminWww/js/eos-assistant.js');
5
5
  must(css===sourceCss,'CSS source/build drift');
6
6
  for(const marker of ['#app-paper','.MuiDialog-paper','.MuiButton-root',':focus-visible','persistent native overview scrolling','eos-overview-edit-controls'])must(css.includes(marker),`CSS marker missing: ${marker}`);
7
- for(const marker of ['nexowatt-native-shell.css','eos-role-bootstrap.js','eos-auto-update.js','nexowatt-stable-v7101.js'])must(index.includes(marker),`active asset missing: ${marker}`);
7
+ for(const marker of ['nexowatt-native-shell.css','eos-role-bootstrap.js','eos-auto-update.js',`nexowatt-stable-v${shellTag}.js`])must(index.includes(marker),`active asset missing: ${marker}`);
8
8
  must(!index.includes('eos-account-management.js'),'custom reset asset active');
9
9
  must(boot.includes('NEXOWATT_EOS_STANDARD_PASSWORD_MODE')&&!boot.includes('installIntegratedFirstLogin(base);'),'standard login mode missing');
10
10
  must(shell.includes('ensureModernOverview'),'modern overview missing');
@@ -1,43 +1,149 @@
1
- "use strict";
2
- const assert = require("node:assert/strict");
3
- const fs = require("node:fs");
4
- const path = require("node:path");
5
- const root = path.resolve(__dirname, "..");
6
- const securityFile = path.join(root, "build", "lib", "eosRoleSecurity.js");
7
- assert.ok(fs.existsSync(securityFile), "build/lib/eosRoleSecurity.js is missing");
8
- const security = require(securityFile);
9
- assert.equal(security.classifyRole("admin"), "admin");
10
- assert.equal(security.classifyRole("Administrator"), "admin");
11
- assert.equal(security.classifyRole("installer"), "installer");
12
- assert.equal(security.classifyRole("Gast / Endkunde"), "customer");
13
- assert.equal(security.isAdminOnlyPath("/#/app-center"), true);
14
- assert.equal(security.isAdminOnlyPath("/license"), true);
15
- assert.equal(security.isAdminOnlyPath("/simulation"), true);
16
- assert.equal(security.isAdminOnlyPath("/cockpit"), false);
17
- assert.equal(security.isRightsPath("/#/users-and-groups"), true);
18
- assert.equal(security.isRightsPath("/cockpit"), false);
19
- const htmlFiles = [];
20
- (function walk(dir) {
21
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
22
- if (entry.name === "node_modules" || entry.name === ".git") continue;
23
- const full = path.join(dir, entry.name);
24
- if (entry.isDirectory()) walk(full);
25
- else if (entry.name === "index.html") htmlFiles.push(full);
26
- }
27
- })(root);
28
- const runtimeHtml = htmlFiles.filter(file => /<script[^>]+(?:assets\/|src=)/i.test(fs.readFileSync(file, "utf8")));
29
- assert.ok(runtimeHtml.length > 0, "No runtime index.html was found");
30
- for (const file of runtimeHtml) {
31
- const html = fs.readFileSync(file, "utf8");
32
- assert.match(html, /nexowatt-role-security\.js\?v=7104/, `Role guard missing in ${path.relative(root, file)}`);
33
- assert.ok(fs.existsSync(path.join(path.dirname(file), "nexowatt-role-security.js")), `Client guard missing next to ${path.relative(root, file)}`);
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const assert = require('node:assert/strict');
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
7
+
8
+ const root = path.resolve(__dirname, '..');
9
+ const read = relative => fs.readFileSync(path.join(root, relative), 'utf8');
10
+ const json = relative => JSON.parse(read(relative));
11
+
12
+ const index = read('adminWww/index.html');
13
+ const sourceIndex = read('src-admin/index.html');
14
+ const bootstrap = read('adminWww/js/eos-role-bootstrap.js');
15
+ const roleUi = read('adminWww/js/eos-role-ui.js');
16
+ const sourceRoleUi = read('src-admin/public/js/eos-role-ui.js');
17
+ const sourceMain = read('src/main.ts');
18
+ const builtMain = read('build/main.js');
19
+ const webSource = read('src/lib/web.ts');
20
+ const webBuilt = read('build/lib/web.js');
21
+ const appSource = read('src-admin/src/App.tsx');
22
+ const objectsSource = read('src-admin/src/tabs/Objects.tsx');
23
+ const introSource = read('src-admin/src/tabs/Intro.tsx');
24
+ const drawerSource = read('src-admin/src/components/Drawer.tsx');
25
+ const bootstrapBundle = read('adminWww/assets/bootstrap-COulQZax-v84.js');
26
+ const objectsBundle = read('adminWww/assets/Objects-DPan0bzw-v84.js');
27
+ const introBundle = read('adminWww/assets/Intro-DkwRiz1n-v84.js');
28
+ const io = json('io-package.json');
29
+ const cleanupSource = read('tools/nexowatt-clean-legacy-runtime.cjs');
30
+
31
+ // The removed DOM-text heuristic classified a real administrator as end user when the
32
+ // visible header happened to contain "user". Only authenticated backend policy may define the role.
33
+ for (const html of [index, sourceIndex]) {
34
+ assert.doesNotMatch(html, /nexowatt-role-security\.js/i, 'legacy heuristic role guard is still loaded');
35
+ assert.match(html, /eos-role-bootstrap\.js\?v=7106/, 'authoritative role bootstrap cache tag is missing');
36
+ assert.match(html, /eos-policy-client\.js\?v=7106/, 'authoritative policy client cache tag is missing');
37
+ assert.match(html, /eos-role-ui\.js\?v=7106/, 'authoritative role UI cache tag is missing');
34
38
  }
35
- const main = fs.readFileSync(path.join(root, "build", "main.js"), "utf8");
36
- assert.match(main, /eosRoleSecurity/);
37
- assert.match(main, /installHttpGuard/);
38
- assert.match(main, /enforceRoleAcls/);
39
- const client = runtimeHtml.length ? fs.readFileSync(path.join(path.dirname(runtimeHtml[0]), "nexowatt-role-security.js"), "utf8") : "";
40
- for (const token of ["app-center", "lizenz", "simulation", "expertenmodus", "xterm", "Nur-Lesezugriff", "backupActive"]) {
41
- assert.ok(client.toLowerCase().includes(token.toLowerCase()), `Client guard is missing ${token}`);
39
+ assert.equal(fs.existsSync(path.join(root, 'adminWww/nexowatt-role-security.js')), false, 'legacy runtime still exists');
40
+ assert.equal(fs.existsSync(path.join(root, 'build/lib/eosRoleSecurity.js')), false, 'legacy backend heuristic still exists');
41
+ assert.doesNotMatch(builtMain, /eosRoleSecurity|installHttpGuard|enforceRoleAcls/);
42
+ for (const obsolete of [
43
+ 'adminWww/nexowatt-role-security.js',
44
+ 'src-admin/nexowatt-role-security.js',
45
+ 'build/lib/eosRoleSecurity.js',
46
+ ]) {
47
+ assert.ok(cleanupSource.includes(`'${obsolete}'`), `runtime cleanup must remove stale ${obsolete}`);
42
48
  }
43
- console.log("[NexoWatt EOS role security] OK");
49
+ for (const code of [bootstrap, roleUi, sourceRoleUi]) {
50
+ assert.match(code, /policy\?\.user === 'system\.user\.admin'/, 'system.user.admin is not explicitly authoritative');
51
+ assert.match(code, /policy\?\.isAdministrator/, 'backend administrator capability is not honored');
52
+ }
53
+
54
+ // Admin is the unconditional full-rights bypass. Restrictions below this branch must never run for Admin.
55
+ for (const code of [sourceMain, builtMain]) {
56
+ const bypass = code.indexOf("if (role === 'admin')");
57
+ const endUserWriteDeny = code.indexOf('end-user datapoints are read-only in EOS Admin');
58
+ assert.ok(bypass >= 0 && endUserWriteDeny > bypass, 'Admin socket bypass must precede all restricted-role rules');
59
+ assert.match(code, /password administration is Service-only/);
60
+ assert.match(code, /\.info\.uiTabsVisible/);
61
+ }
62
+
63
+ // Expert mode is an Admin-only control, but the administrator can freely toggle both directions.
64
+ assert.match(appSource, /const isEosAdministrator = \(\): boolean => \['admin', 'service'\]\.includes/);
65
+ assert.match(appSource, /newState\.expertMode = isEosAdministrator\(\)/);
66
+ assert.match(appSource, /data-eos-expert-control="1"/);
67
+ assert.match(appSource, /this\.setState\(\{ expertMode: !this\.state\.expertMode \}\)/);
68
+ assert.match(bootstrapBundle, /"data-eos-expert-control":"1"/);
69
+ assert.match(bootstrapBundle, /l\.expertMode=\["admin","service"\]\.includes\(String\(window\.NEXOWATT_EOS_ACCESS_ROLE/);
70
+ assert.match(bootstrapBundle, /c\?jsxRuntimeExports\.jsx\(IsVisible,\{name:"admin\.appBar\.expertMode"/);
71
+ assert.match(bootstrapBundle, /:null,c&&this\.state\.expertMode\?jsxRuntimeExports\.jsx/);
72
+ assert.match(roleUi, /if \(state\.role === 'admin'\) return false;/, 'Admin expert-mode cleanup must be a no-op');
73
+ assert.match(roleUi, /if \(state\.role === 'admin' \|\| state\.role === 'unknown'\) return;/, 'Admin click handling must not be intercepted');
74
+
75
+ // Datapoints: Admin/Installer retain native write support; only End User is read-only.
76
+ assert.match(objectsSource, /const readOnly = eosRole === 'enduser'/);
77
+ for (const marker of [
78
+ 'enableStateValueEdit={!readOnly}',
79
+ 'objectEditBoolean={!readOnly}',
80
+ 'objectAddBoolean={!readOnly}',
81
+ 'objectImportExport={!readOnly}',
82
+ 'objectEditOfAccessControl={!readOnly}',
83
+ ]) assert.ok(objectsSource.includes(marker), `Objects source missing ${marker}`);
84
+ assert.match(objectsBundle, /_eosReadOnly=_eosRole==="enduser"/);
85
+ for (const marker of [
86
+ 'enableStateValueEdit:!_eosReadOnly',
87
+ 'objectEditBoolean:!_eosReadOnly',
88
+ 'objectAddBoolean:!_eosReadOnly',
89
+ 'objectImportExport:!_eosReadOnly',
90
+ 'objectEditOfAccessControl:!_eosReadOnly',
91
+ ]) assert.ok(objectsBundle.includes(marker), `Objects runtime missing ${marker}`);
92
+ assert.match(bootstrap, /route === 'tab-objects'/, 'End User datapoint route is not released');
93
+ assert.match(roleUi, /clean === 'tab-objects'/, 'End User datapoint route is not released in runtime UI policy');
94
+
95
+ // The Admin and XTerm cards stay visible for Admin and are removed only for non-admin roles.
96
+ assert.match(introSource, /if \(!isAdministrator && \/\^\(\?:admin\|xterm\)/);
97
+ assert.match(introSource, /forceVisibleAdminCard = isAdministrator/);
98
+ assert.match(introBundle, /if\(!_eosAdmin&&\/\^\(\?:admin\|xterm\)/);
99
+ assert.match(introBundle, /_forceAdmin=_eosAdmin/);
100
+ assert.doesNotMatch(introSource, /NEXOWATT_EOS_ACCESS_ROLE \|\| 'admin'/, 'Intro source must fail closed while role is unresolved');
101
+ assert.doesNotMatch(introBundle, /NEXOWATT_EOS_ACCESS_ROLE\|\|"admin"/, 'Intro runtime must fail closed while role is unresolved');
102
+
103
+ // App Center, License, Simulation and normal account administration are not available to Installer/End User.
104
+ for (const token of ['app center', 'app-center', 'appcenter', 'simulation', 'simulator', 'lizenz', 'license']) {
105
+ assert.ok(roleUi.toLowerCase().includes(token), `role UI is missing protected action ${token}`);
106
+ }
107
+ assert.match(roleUi, /applyPrivilegedIframePolicy/);
108
+ assert.match(roleUi, /:8188\/logout\?/);
109
+ assert.match(roleUi, /route !== 'tab-users'/);
110
+ assert.match(bootstrap, /tab-users\|tab-hosts\|tab-files/);
111
+
112
+ // Pencil/menu edits must not touch system.config (which restarts the running adapter).
113
+ const drawerMethod = drawerSource.slice(
114
+ drawerSource.indexOf('async getTabs(update?: boolean)'),
115
+ drawerSource.indexOf('getNavigationItems(): JSX.Element[]'),
116
+ );
117
+ assert.match(drawerSource, /info\.uiTabsVisible/);
118
+ assert.match(drawerMethod, /socket\.setState/);
119
+ assert.doesNotMatch(drawerMethod, /setSystemConfig|getSystemConfig\(/);
120
+ const builtDrawer = bootstrapBundle.slice(
121
+ bootstrapBundle.indexOf('async getTabs(e){'),
122
+ bootstrapBundle.indexOf('getNavigationItems(){', bootstrapBundle.indexOf('async getTabs(e){')),
123
+ );
124
+ assert.match(builtDrawer, /info\.uiTabsVisible/);
125
+ assert.match(builtDrawer, /socket\.setState/);
126
+ assert.doesNotMatch(builtDrawer, /setSystemConfig|getSystemConfig\(/);
127
+ assert.ok((io.instanceObjects || []).some(object => object?._id === 'info.uiTabsVisible'), 'navigation state object missing');
128
+
129
+ // Backup navigation is emitted only for an actually enabled instance.
130
+ assert.match(drawerSource, /backupAdapters\.has\(adapterName\) && instance\.enabled !== true/);
131
+ assert.match(builtDrawer, /o\.has\(A\)&&d\.enabled!==!0/);
132
+
133
+ // 7.10.6 End User cleanup: system information is read-only and object actions disappear only for End User.
134
+ for (const code of [webSource, webBuilt]) {
135
+ assert.match(code, /nexowatt\/readonly\/system-info/, 'authenticated read-only system-info route is missing');
136
+ assert.match(code, /authenticationRequired/, 'read-only system-info route must require an authenticated session');
137
+ assert.match(code, /ramMb/, 'read-only system-info payload is incomplete');
138
+ }
139
+ for (const code of [roleUi, sourceRoleUi]) {
140
+ assert.match(code, /state\.role !== 'enduser' \|\| currentRoute\(\) !== 'tab-intro'/, 'system-info hydration is not End User-only');
141
+ assert.match(code, /data-eos-enduser-action-hidden/, 'End User object action hiding marker is missing');
142
+ assert.match(code, /svg\[data-testid\*="Edit"\]/, 'Edit icon fallback selector is missing');
143
+ assert.match(code, /svg\[data-testid\*="Settings"\]/, 'Settings icon fallback selector is missing');
144
+ assert.match(code, /svg\[data-testid\*="Delete"\]/, 'Delete icon fallback selector is missing');
145
+ assert.match(code, /state\.role !== 'admin'/, 'Admin restriction guard is missing');
146
+ }
147
+ assert.match(sourceMain, /role === 'enduser' && \['readLogs', 'sendToHost'\]\.includes\(command\)/, 'End User technical diagnostics must remain denied');
148
+
149
+ console.log('[NexoWatt EOS role security] OK (authoritative Admin rights, End User read-only datapoints, protected tools, restart-safe menu storage)');
@@ -15,6 +15,7 @@ try {
15
15
  fs.mkdirSync(path.join(temp, 'src-admin', 'public', 'js'), { recursive: true });
16
16
  fs.mkdirSync(path.join(temp, 'src-admin', 'public', 'static', 'js'), { recursive: true });
17
17
  fs.mkdirSync(path.join(temp, 'adminWww', 'static', 'js'), { recursive: true });
18
+ fs.mkdirSync(path.join(temp, 'build', 'lib'), { recursive: true });
18
19
  fs.writeFileSync(path.join(temp, 'NEXOWATT_EOS_BUILD_INFO.json'), JSON.stringify({ runtimeEntry: 'v82', shellCacheVersion: 98 }));
19
20
 
20
21
  const keep = [
@@ -36,6 +37,9 @@ try {
36
37
  'src-admin/public/js/eos-security-ui.js',
37
38
  'adminWww/static/js/nexowatt-stable-v97.js',
38
39
  'src-admin/public/static/js/nexowatt-stable-v95.js',
40
+ 'adminWww/nexowatt-role-security.js',
41
+ 'src-admin/nexowatt-role-security.js',
42
+ 'build/lib/eosRoleSecurity.js',
39
43
  ];
40
44
  for (const rel of [...keep, ...remove]) {
41
45
  const file = path.join(temp, rel);
@@ -25,18 +25,18 @@ const index = read('adminWww/index.html');
25
25
  const ems = read('adminWww/js/eos-ems-overview.js');
26
26
  const intro = read('src-admin/src/tabs/Intro.tsx');
27
27
  const bundle = read('adminWww/assets/Intro-DkwRiz1n-v84.js');
28
- const stable = read('adminWww/static/js/nexowatt-stable-v7101.js');
28
+ const stable = read(`adminWww/static/js/nexowatt-stable-v${String(info.shellCacheTag || info.shellCacheVersion)}.js`);
29
29
  const autoBackend = read('src/lib/eosAutoUpdate.ts');
30
30
  const autoUi = read('adminWww/js/eos-auto-update.js');
31
31
  const autoCss = read('adminWww/css/eos-auto-update.css');
32
32
  const autoTag = String(info.autoUpdateCacheTag || info.autoUpdateCacheVersion || info.shellCacheTag || info.shellCacheVersion);
33
33
 
34
- must(pkg.version === '7.10.4' && io.version === '7.10.4' && io.common.version === '7.10.4' && info.version === '7.10.4', 'version drift');
34
+ must(pkg.version === '7.10.6' && io.version === '7.10.6' && io.common.version === '7.10.6' && info.version === '7.10.6', 'version drift');
35
35
  must(io.native.eosRequireFirstLoginPassword === false && io.native.eosRequireFirstLoginPasswordChange === false, 'forced first password still enabled');
36
36
  must(!index.includes('eos-account-management.js'), 'custom reset UI still loaded');
37
37
  must(!boot.includes('installIntegratedFirstLogin(base);') && !boot.includes('showFirstLoginPassword(resolved, base);'), 'forced first-login UI still active');
38
38
  must(main.includes('standardPasswordMode: true') && main.includes('nexowattPasswordChangeRequired: false'), 'account migration missing');
39
- must(role.includes("route === 'tab-users'") && main.includes("command === 'changePassword'"), 'role-aware native password editing missing');
39
+ must(role.includes("route !== 'tab-users'") && main.includes("command === 'changePassword'") && main.includes('password administration is Service-only'), 'non-admin account/password administration guard missing');
40
40
  must(css.includes('persistent native overview scrolling') && stable.includes("overflow-y','scroll"), 'persistent scroll missing');
41
41
  must(intro.includes('eos-overview-edit-controls') && bundle.includes('eos-overview-edit-controls'), 'edit control not in bottom grid');
42
42
  must(!ems.includes('Vollständige EMS-Diagnose öffnen'), 'unsupported EMS link remains');
@@ -49,7 +49,7 @@ must(!autoBackend.includes('extendForeignObjectAsync(this.instanceId'), 'runtime
49
49
  must(web.includes('this.nexowattStableUpdateManager?.stop();'), 'auto-update manager is not stopped with the web server');
50
50
  must(fs.existsSync(path.join(root, 'build/lib/eosRequestSecurity.js')), 'eosRequestSecurity runtime module missing');
51
51
 
52
- // 7.10.4 placement acceptance: System Settings only.
52
+ // 7.10.4 placement acceptance remains mandatory in 7.10.6: System Settings only.
53
53
  must(index.includes(`eos-auto-update.js?v=${autoTag}`), 'auto-update JavaScript cache key missing');
54
54
  must(index.includes(`eos-auto-update.css?v=${autoTag}`), 'auto-update CSS cache key missing');
55
55
  must(autoUi.includes('[role="dialog"][aria-labelledby="system-settings-dialog-title"]'), 'System Settings selector missing');
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  const fs=require('node:fs');const path=require('node:path');const root=path.resolve(__dirname,'..');const read=rel=>fs.readFileSync(path.join(root,rel),'utf8');const fail=msg=>{console.error(`[NexoWatt EOS UI overview runtime] ${msg}`);process.exit(1)};
4
- const runtime=read('adminWww/js/eos-ems-overview.js');const source=read('src-admin/public/js/eos-ems-overview.js');const css=read('adminWww/css/eos-ems-overview.css');const index=read('adminWww/index.html');const role=read('adminWww/js/eos-role-ui.js');const shell=read('adminWww/js/nexowatt-native-shell.js');const intro=read('src-admin/src/tabs/Intro.tsx');const introBundle=read('adminWww/assets/Intro-DkwRiz1n-v84.js');const bootstrap=read('adminWww/assets/bootstrap-COulQZax-v84.js');const generic=read('adminWww/assets/index-D2ymscJA-v84.js');
4
+ const info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json')),shellTag=String(info.shellCacheTag||info.shellCacheVersion);const runtime=read('adminWww/js/eos-ems-overview.js');const source=read('src-admin/public/js/eos-ems-overview.js');const css=read('adminWww/css/eos-ems-overview.css');const index=read('adminWww/index.html');const role=read('adminWww/js/eos-role-ui.js');const shell=read('adminWww/js/nexowatt-native-shell.js');const intro=read('src-admin/src/tabs/Intro.tsx');const introBundle=read('adminWww/assets/Intro-DkwRiz1n-v84.js');const bootstrap=read('adminWww/assets/bootstrap-COulQZax-v84.js');const generic=read('adminWww/assets/index-D2ymscJA-v84.js');
5
5
  if(runtime!==source)fail('runtime source/build drift');
6
6
  if(runtime.includes('Vollständige EMS-Diagnose öffnen'))fail('unsupported EMS link remains');
7
7
  for(const marker of ['nexowatt-ems-overview-v1','nexowatt-ui.*.info.adminOverview.*','system.adapter.nexowatt-ui.*.alive','POLL_MS = 5000','STALE_MS = 20000',"REQUIRED_UI_VERSION = '0.8.198'",'getForeignStates','Keine Kachel besitzt Schreibhoheit','currentDecisions','§14a-Fallback','nexowatt-eos-admin-socket-ready','readUiAdapterInfo','socketFromReact'])if(!runtime.includes(marker))fail(`runtime marker missing: ${marker}`);
8
8
  for(const forbidden of ['setForeignState','setState(','sendToHost','writeFile','extendObject'])if(runtime.includes(forbidden))fail(`read-only runtime contains ${forbidden}`);
9
- for(const marker of ['eos-ems-overview.css?v=7101','eos-ems-overview.js?v=7101','nexowatt-stable-v7101.js?v=7101'])if(!index.includes(marker))fail(`index marker missing: ${marker}`);
9
+ for(const marker of [`eos-ems-overview.css?v=${shellTag}`,`eos-ems-overview.js?v=${shellTag}`,`nexowatt-stable-v${shellTag}.js?v=${shellTag}`])if(!index.includes(marker))fail(`index marker missing: ${marker}`);
10
10
  if(!css.includes('#eos-ems-overview-runtime')||!css.includes('.eos-ems-overview-metrics')||!css.includes('startup and compatibility messages stay compact'))fail('EMS card CSS incomplete');
11
11
  if(role.includes('overview.innerHTML = `')||!role.includes('never cover the real Admin Intro'))fail('obsolete role overview still active');
12
12
  if(!shell.includes("label: 'Installateur'")||!shell.includes("label: 'Endkunde'")||!shell.includes('eosNativeReactOverview'))fail('native overview role bridge incomplete');
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const assert = require('node:assert/strict');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const root = path.resolve(__dirname, '..');
7
+ const read = file => fs.readFileSync(path.join(root, file), 'utf8');
8
+ const pkg = JSON.parse(read('package.json'));
9
+ const sourceWeb = read('src/lib/web.ts');
10
+ const builtWeb = read('build/lib/web.js');
11
+ const sourceRole = read('src-admin/public/js/eos-role-ui.js');
12
+ const runtimeRole = read('adminWww/js/eos-role-ui.js');
13
+ const sourceMain = read('src/main.ts');
14
+ const builtMain = read('build/main.js');
15
+
16
+ assert.equal(pkg.version, '7.10.6', 'release must use a new npm version');
17
+ for (const web of [sourceWeb, builtWeb]) {
18
+ assert.match(web, /nexowatt\/readonly\/system-info/, 'read-only system information endpoint missing');
19
+ assert.match(web, /sendEosReadonlySystemInfo/, 'read-only system information handler missing');
20
+ assert.match(web, /platform/, 'platform value missing');
21
+ assert.match(web, /ramMb/, 'RAM value missing');
22
+ assert.match(web, /nodejs/, 'Node.js value missing');
23
+ assert.match(web, /npm/, 'NPM value missing');
24
+ }
25
+ for (const role of [sourceRole, runtimeRole]) {
26
+ assert.match(role, /state\.role !== 'enduser' \|\| currentRoute\(\) !== 'tab-intro'/, 'system-card correction must be End User-only');
27
+ assert.match(role, /applyReadonlySystemInfoCard/, 'End User system-card correction missing');
28
+ assert.match(role, /data-eos-enduser-action-hidden/, 'End User action-column suppression missing');
29
+ assert.match(role, /Edit.*Delete.*Settings.*Tune.*Build/s, 'edit/delete/settings icon suppression missing');
30
+ assert.match(role, /const readonly = state\.role === 'enduser' && currentRoute\(\) === 'tab-objects'/, 'datapoint restriction must be End User-only');
31
+ }
32
+ for (const main of [sourceMain, builtMain]) {
33
+ assert.match(main, /if \(role === 'admin'\)/, 'Admin full-rights bypass missing');
34
+ assert.match(main, /end-user datapoints are read-only in EOS Admin/, 'backend End User write denial missing');
35
+ }
36
+ console.log('[NexoWatt EOS user area] OK (End User read-only actions and system info; Admin unchanged)');
@@ -68,6 +68,7 @@ if (io.native?.nexowattHideLegacyBackupFromNonAdmins !== true) fail('NexoWatt ba
68
68
  const instanceObjectIds = new Set((io.instanceObjects || []).map(object => object?._id));
69
69
  if (!instanceObjectIds.has('info.nexowattStableUpdatesEnabled')) fail('restart-safe auto-update enabled state is missing');
70
70
  if (!instanceObjectIds.has('info.nexowattStableUpdatesState')) fail('restart-safe auto-update runtime state is missing');
71
+ if (!instanceObjectIds.has('info.uiTabsVisible')) fail('restart-safe navigation state info.uiTabsVisible is missing');
71
72
  for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
72
73
  if (JSON.stringify(pkgLock.packages?.['']?.[key]) !== JSON.stringify(pkg[key])) {
73
74
  fail(`package-lock root ${key} must match package.json`);
@@ -133,14 +134,14 @@ for (const file of [
133
134
  'LICENSE',
134
135
  'NEXOWATT_PROPRIETARY_LICENSE.md',
135
136
  'THIRD_PARTY_NOTICES.md',
136
- 'README_STABILITY_V7.10.4.md',
137
- 'RELEASE_NOTES_V7.10.4.md',
138
- 'PUBLISH_STABLE_V7.10.4.md',
139
- 'INSTALL_TEST_V7.10.4.md',
140
- 'RELEASE_ACCEPTANCE_V7.10.4.md',
137
+ 'README_STABILITY_V7.10.6.md',
138
+ 'RELEASE_NOTES_V7.10.6.md',
139
+ 'PUBLISH_STABLE_V7.10.6.md',
140
+ 'INSTALL_TEST_V7.10.6.md',
141
+ 'RELEASE_ACCEPTANCE_V7.10.6.md',
141
142
  'MERGE_UPDATE.ps1',
142
143
  'MERGE_UPDATE.cmd',
143
- 'MERGE_UPDATE_README_V7.10.4.md',
144
+ 'MERGE_UPDATE_README_V7.10.6.md',
144
145
  'tools/nexowatt-patch-built-frontend.cjs',
145
146
  'tools/nexowatt-native-shell-selftest.cjs',
146
147
  'tools/nexowatt-clean-legacy-runtime.cjs',
@@ -173,7 +174,7 @@ for (const file of [
173
174
  'tools/nexowatt-ui-overview-runtime-selftest.cjs',
174
175
  'adminWww/js/eos-ems-overview.js',
175
176
  'adminWww/css/eos-ems-overview.css',
176
- 'adminWww/static/js/nexowatt-stable-v7101.js',
177
+ 'adminWww/static/js/nexowatt-stable-v7106.js',
177
178
  'src-admin/src/components/Intro/NexoWattEmsOverview.tsx',
178
179
  'tools/nexowatt-publish-channel-guard.cjs',
179
180
  'tools/nexowatt-publish-channel-selftest.cjs',
@@ -219,7 +220,7 @@ if (!index.includes(`eos-auto-update.css?v=${autoUpdateTag}`)) fail('auto-update
219
220
  if (!index.includes(`eos-assistant.js?v=${shellTag}`)) fail('EOS Assist cache key mismatch');
220
221
  if (!index.includes(`eos-ems-overview.js?v=${shellTag}`)) fail('EMS overview cache key mismatch');
221
222
  if (!index.includes(`eos-ems-overview.css?v=${shellTag}`)) fail('EMS overview CSS cache key mismatch');
222
- if (!index.includes(`nexowatt-stable-v7101.js?v=${shellTag}`)) fail('stable v98 cache key mismatch');
223
+ if (!index.includes(`nexowatt-stable-v${shellTag}.js?v=${shellTag}`)) fail('stable shell overlay cache key mismatch');
223
224
  if (index.indexOf(`eos-manual-write-policy.js?v=${shellTag}`) > index.indexOf(`hostInit-${runtime}.js?v=${runtimeNumber}`)) fail('manual-write policy must load before the React runtime');
224
225
  if (!index.includes('class="eos-native-shell"')) fail('native NexoWatt shell class missing');
225
226
  for (const legacy of ['eos-branding.js', 'eos-security-ui.js', 'eos-console-quiet.js', 'eos-objects-state-tools.js']) {
@@ -1,9 +0,0 @@
1
- # Installationstest 7.10.4
2
-
3
- 1. Version 7.10.4 in einem neuen Arbeitsordner veröffentlichen bzw. installieren.
4
- 2. EOS Admin mindestens 60 Sekunden ohne Start-/Stop-Schleife beobachten.
5
- 3. Als Administrator App-Center, Lizenz, Simulation, Benutzer/Rechte, Expertenmodus, ioBroker Admin und XTerm prüfen.
6
- 4. Als Installateur müssen diese Bereiche vollständig fehlen bzw. bei direktem Aufruf gesperrt sein.
7
- 5. Als Endkunde müssen Datenpunkte sichtbar, aber nicht beschreibbar sein; Benutzer/Rechte und Passwortverwaltung dürfen nicht erreichbar sein.
8
- 6. Backup-Instanz deaktivieren: der Navigationspunkt Sicherung muss verschwinden. Backup-Instanz aktivieren: der Punkt muss wieder erscheinen.
9
- 7. Systemeinstellungen öffnen: die Karte Automatische Adapter-Updates darf nur dort sichtbar sein.
@@ -1,3 +0,0 @@
1
- # Merge-/Update-Hinweis 7.10.4
2
-
3
- Den bisherigen Arbeitsordner vollständig ersetzen oder die Repository-ZIP in einen leeren Ordner entpacken. Nicht über eine ältere Version kopieren, weil veraltete Browser-Bundles, Rollen-Skripte oder Paket-Lifecycle-Einträge sonst bestehen bleiben können. Danach zuerst `npm publish --dry-run` ausführen.