iobroker.eos-admin 7.9.47 → 7.9.48

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.
@@ -253,6 +253,76 @@
253
253
  },
254
254
  ],
255
255
  },
256
+ eosInstallerGroups: {
257
+ newLine: true,
258
+ type: 'table',
259
+ label: 'EOS Installateurgruppen',
260
+ help: 'Mitglieder dieser Gruppen erhalten die Installateur-Rolle: Einrichtung, Service und Konfiguration ohne Admin-Systemverwaltung.',
261
+ xs: 12,
262
+ sm: 12,
263
+ items: [
264
+ {
265
+ type: 'text',
266
+ title: 'Gruppe',
267
+ attr: 'group',
268
+ width: '30%',
269
+ filter: false,
270
+ sort: false,
271
+ default: 'system.group.installateur',
272
+ },
273
+ {
274
+ type: 'text',
275
+ title: 'Beschreibung',
276
+ attr: 'note',
277
+ width: '55%',
278
+ filter: false,
279
+ sort: false,
280
+ default: 'Installateur / Service',
281
+ },
282
+ {
283
+ type: 'checkbox',
284
+ title: 'Aktiv',
285
+ attr: 'enabled',
286
+ width: '15%',
287
+ default: true,
288
+ },
289
+ ],
290
+ },
291
+ eosEndUserGroups: {
292
+ newLine: true,
293
+ type: 'table',
294
+ label: 'EOS Endkundengruppen',
295
+ help: 'Mitglieder dieser Gruppen werden auf das Endkunden-Cockpit geführt und sehen keine Admin-/Systembereiche.',
296
+ xs: 12,
297
+ sm: 12,
298
+ items: [
299
+ {
300
+ type: 'text',
301
+ title: 'Gruppe',
302
+ attr: 'group',
303
+ width: '30%',
304
+ filter: false,
305
+ sort: false,
306
+ default: 'system.group.endkunde',
307
+ },
308
+ {
309
+ type: 'text',
310
+ title: 'Beschreibung',
311
+ attr: 'note',
312
+ width: '55%',
313
+ filter: false,
314
+ sort: false,
315
+ default: 'Endkunde / Bediener',
316
+ },
317
+ {
318
+ type: 'checkbox',
319
+ title: 'Aktiv',
320
+ attr: 'enabled',
321
+ width: '15%',
322
+ default: true,
323
+ },
324
+ ],
325
+ },
256
326
  eosProtectAdapterDeletion: {
257
327
  newLine: true,
258
328
  type: 'checkbox',
@@ -3528,3 +3528,80 @@ html.eos-objects-surface #eos-assist-root {
3528
3528
  }
3529
3529
 
3530
3530
  /* === NexoWatt EOS v40: keep command dialogs technically accurate ========= */
3531
+
3532
+ /* === NexoWatt EOS v48: role-aware navigation and end-customer landing === */
3533
+ html.eos-role-loaded .eos-role-hidden,
3534
+ html.eos-role-loaded .eos-role-hidden * {
3535
+ display: none !important;
3536
+ visibility: hidden !important;
3537
+ pointer-events: none !important;
3538
+ }
3539
+
3540
+ .eos-role-landing {
3541
+ min-height: calc(100vh - 120px);
3542
+ width: 100%;
3543
+ display: flex;
3544
+ align-items: center;
3545
+ justify-content: center;
3546
+ padding: 32px;
3547
+ box-sizing: border-box;
3548
+ background:
3549
+ radial-gradient(circle at 20% 20%, rgba(0, 255, 170, .12), transparent 34%),
3550
+ radial-gradient(circle at 80% 70%, rgba(0, 140, 255, .13), transparent 36%),
3551
+ linear-gradient(135deg, rgba(0, 18, 28, .96), rgba(1, 8, 20, .98));
3552
+ }
3553
+
3554
+ .eos-role-landing-card {
3555
+ width: min(560px, calc(100vw - 48px));
3556
+ border: 1px solid rgba(0, 255, 170, .38);
3557
+ border-radius: 28px;
3558
+ padding: 34px;
3559
+ color: #f4fff9;
3560
+ background: rgba(4, 28, 39, .88);
3561
+ box-shadow: 0 24px 70px rgba(0, 0, 0, .45), 0 0 34px rgba(0, 255, 170, .12);
3562
+ text-align: left;
3563
+ }
3564
+
3565
+ .eos-role-landing-eyebrow {
3566
+ display: inline-flex;
3567
+ align-items: center;
3568
+ gap: 8px;
3569
+ color: #00ffc0;
3570
+ font-size: 12px;
3571
+ font-weight: 800;
3572
+ letter-spacing: .12em;
3573
+ text-transform: uppercase;
3574
+ margin-bottom: 12px;
3575
+ }
3576
+
3577
+ .eos-role-landing-card h1 {
3578
+ margin: 0 0 12px;
3579
+ font-size: clamp(28px, 4vw, 44px);
3580
+ line-height: 1.08;
3581
+ color: #ffffff;
3582
+ }
3583
+
3584
+ .eos-role-landing-card p {
3585
+ margin: 0 0 24px;
3586
+ color: rgba(244, 255, 249, .82);
3587
+ font-size: 16px;
3588
+ line-height: 1.55;
3589
+ }
3590
+
3591
+ .eos-role-open-cockpit {
3592
+ border: 0;
3593
+ border-radius: 999px;
3594
+ padding: 13px 24px;
3595
+ color: #001612;
3596
+ background: linear-gradient(135deg, #00ffc0, #00a2ff);
3597
+ font-weight: 900;
3598
+ cursor: pointer;
3599
+ box-shadow: 0 12px 30px rgba(0, 255, 170, .24);
3600
+ }
3601
+
3602
+ .eos-role-open-cockpit:hover,
3603
+ .eos-role-open-cockpit:focus-visible {
3604
+ transform: translateY(-1px);
3605
+ filter: brightness(1.08);
3606
+ outline: none;
3607
+ }
@@ -31,7 +31,7 @@
31
31
  rel="stylesheet"
32
32
  href="css/leaflet.css"
33
33
  />
34
- <link rel="stylesheet" href="./css/eos-branding.css?v=47" />
34
+ <link rel="stylesheet" href="./css/eos-branding.css?v=48" />
35
35
  <link
36
36
  rel="manifest"
37
37
  href="manifest.json"
@@ -154,10 +154,11 @@
154
154
  <script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></script>
155
155
  <link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
156
156
  <link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
157
- <script defer src="./js/eos-branding.js?v=47"></script>
158
- <script defer src="./js/eos-security-ui.js?v=47"></script>
159
- <script defer src="./js/eos-assistant.js?v=47"></script>
160
- <script defer src="./js/eos-objects-state-tools.js?v=47"></script>
157
+ <script defer src="./js/eos-branding.js?v=48"></script>
158
+ <script defer src="./js/eos-security-ui.js?v=48"></script>
159
+ <script defer src="./js/eos-role-ui.js?v=48"></script>
160
+ <script defer src="./js/eos-assistant.js?v=48"></script>
161
+ <script defer src="./js/eos-objects-state-tools.js?v=48"></script>
161
162
  </head>
162
163
  <body>
163
164
  <noscript>You need to enable JavaScript to run this app.</noscript>
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- window.NEXOWATT_EOS_UI_VERSION = 'v47-delete-logquiet-fix';
4
+ window.NEXOWATT_EOS_UI_VERSION = 'v48-role-guard-fix';
5
5
 
6
6
  const BRAND = 'NexoWatt EOS';
7
7
  const EOS_MEANING = 'Energy Operation System';
@@ -945,6 +945,8 @@
945
945
 
946
946
  const desiredPermission = (profile, block, perm) => {
947
947
  if (!perm) return null;
948
+ if (profile === 'enduser') profile = 'operator';
949
+ if (profile === 'installer') profile = 'service';
948
950
  if (profile === 'admin') return true;
949
951
  if (profile === 'viewer') return ['object', 'state', 'file'].includes(block) && ['list', 'read'].includes(perm);
950
952
  if (profile === 'operator') {
@@ -981,13 +983,12 @@
981
983
  const panel = document.createElement('section');
982
984
  panel.className = 'eos-permission-presets';
983
985
  panel.innerHTML = `
984
- <div class="eos-permission-presets-title">Rechte-Schnellprofile</div>
985
- <div class="eos-permission-presets-text">Wähle ein Profil und passe danach einzelne Rechte an. Administrator-Rollen bleiben bewusst transparent sichtbar.</div>
986
+ <div class="eos-permission-presets-title">EOS Rollen-Schnellprofile</div>
987
+ <div class="eos-permission-presets-text">NexoWatt EOS arbeitet mit drei Rollen: Admin, Installateur und Endkunde. Wähle ein Profil und passe danach bei Bedarf einzelne Rechte an.</div>
986
988
  <div class="eos-permission-presets-actions">
987
- <button type="button" data-profile="viewer">Nur lesen</button>
988
- <button type="button" data-profile="operator">Bedienung</button>
989
- <button type="button" data-profile="service">Service</button>
990
- <button type="button" data-profile="admin">Vollzugriff</button>
989
+ <button type="button" data-profile="enduser">Endkunde</button>
990
+ <button type="button" data-profile="installer">Installateur</button>
991
+ <button type="button" data-profile="admin">Admin</button>
991
992
  </div>
992
993
  `;
993
994
  panel.addEventListener('click', event => {
@@ -0,0 +1,304 @@
1
+ (() => {
2
+ 'use strict';
3
+
4
+ window.NEXOWATT_EOS_ROLE_UI_VERSION = 'v48-role-guard-fix';
5
+
6
+ const ASSET_BASE = (() => {
7
+ const script = document.currentScript?.src || document.querySelector('script[src*="eos-role-ui.js"]')?.src || window.location.href;
8
+ return new URL('../', script).href;
9
+ })();
10
+
11
+ const state = {
12
+ policy: null,
13
+ role: 'unknown',
14
+ scheduled: false,
15
+ observer: null,
16
+ fallbackTimer: null,
17
+ redirects: 0,
18
+ lastTarget: '',
19
+ };
20
+
21
+ const securityEndpointUrls = () => [
22
+ new URL('nexowatt/security/context', ASSET_BASE).href,
23
+ new URL('nexowatt/security/session', ASSET_BASE).href,
24
+ new URL('eos/security/status', ASSET_BASE).href,
25
+ ];
26
+
27
+ const safe = fn => {
28
+ try { return fn(); } catch (_) { return undefined; }
29
+ };
30
+
31
+ const normalize = value => String(value || '')
32
+ .toLowerCase()
33
+ .normalize('NFD')
34
+ .replace(/[\u0300-\u036f]/g, '')
35
+ .replace(/ß/g, 'ss')
36
+ .replace(/[^a-z0-9#/_:.-]+/g, ' ')
37
+ .trim();
38
+
39
+ const stripTab = value => String(value || '').replace(/^#?\/?/, '').replace(/^tab-/, '');
40
+
41
+ const hashTab = () => {
42
+ const hash = decodeURIComponent(window.location.hash || '');
43
+ const match = hash.match(/#\/?(tab-[a-z0-9_-]+(?:-\d+)?)\b/i);
44
+ if (match) return match[1];
45
+ if (!hash || hash === '#' || hash === '#/' || hash === '#/tab-intro') return 'tab-intro';
46
+ return '';
47
+ };
48
+
49
+ const tabFromHref = href => {
50
+ const value = String(href || '');
51
+ const match = value.match(/#\/?(tab-[a-z0-9_-]+(?:-\d+)?)\b/i);
52
+ return match ? match[1] : '';
53
+ };
54
+
55
+ const textOf = el => {
56
+ if (!el) return '';
57
+ const attrs = ['href', 'title', 'aria-label', 'data-name', 'data-tab', 'data-id', 'id'];
58
+ const values = [el.textContent || ''];
59
+ attrs.forEach(attr => {
60
+ if (el.getAttribute && el.hasAttribute(attr)) values.push(el.getAttribute(attr) || '');
61
+ });
62
+ return values.join(' ');
63
+ };
64
+
65
+ const isLoginView = () => /(?:^|[?&])login(?:[=&]|$)/i.test(window.location.search || '') ||
66
+ !!document.querySelector('.login-form, #login, [data-testid="login"]');
67
+
68
+ const roleFromPolicy = policy => {
69
+ const rawRole = normalize(policy?.role || policy?.eosRole || policy?.accessRole || '');
70
+ if (policy?.isAdmin || policy?.isEosAdminGroup || policy?.isAdministrator || rawRole === 'admin' || rawRole === 'administrator') return 'admin';
71
+ if (policy?.isInstaller || /installateur|installer|service|techniker|technician|integrator|partner|wartung|maintenance/.test(rawRole)) return 'installer';
72
+ if (policy?.isEndUser || /endkunde|endkunden|kunde|kunden|customer|bediener|operator|viewer|user|nutzer/.test(rawRole)) return 'enduser';
73
+
74
+ const groupText = normalize([
75
+ ...(Array.isArray(policy?.groups) ? policy.groups : []),
76
+ ...(Array.isArray(policy?.groupNames) ? policy.groupNames : []),
77
+ ].join(' '));
78
+ if (/system\.group\.administrator|\badministrator\b/.test(groupText)) return 'admin';
79
+ if (/installateur|installer|service|techniker|technician|integrator|partner|wartung|maintenance/.test(groupText)) return 'installer';
80
+ if (/endkunde|endkunden|kunde|kunden|customer|bediener|operator|viewer|nutzer/.test(groupText)) return 'enduser';
81
+
82
+ // Secure default: a normal, non-admin ioBroker user is an EOS end customer.
83
+ return 'enduser';
84
+ };
85
+
86
+ const setRoleClasses = role => safe(() => {
87
+ const root = document.documentElement;
88
+ root.classList.add('eos-role-loaded');
89
+ ['admin', 'installer', 'installateur', 'enduser', 'unknown'].forEach(name => root.classList.remove(`eos-role-${name}`));
90
+ root.classList.add(`eos-role-${role}`);
91
+ if (role === 'installer') root.classList.add('eos-role-installateur');
92
+ if (role === 'admin') {
93
+ root.classList.add('eos-security-admin');
94
+ root.classList.remove('eos-security-nonadmin');
95
+ } else {
96
+ root.classList.add('eos-security-nonadmin');
97
+ root.classList.remove('eos-security-admin');
98
+ }
99
+ });
100
+
101
+ const endUserAllowedByText = value => {
102
+ const text = normalize(value);
103
+ if (!text) return false;
104
+ if (/(eos cockpit|nexowatt ui|nexowatt cockpit|kunden cockpit|kundenbereich|endkundenbereich|visualisierung|visualisation|visu|dashboard|energie cockpit|energy cockpit|bedienung|smart home|smart-home)/.test(text)) return true;
105
+ if (/\b(lovelace|jarvis|vis|iqontrol|material)\b/.test(text)) return true;
106
+ return false;
107
+ };
108
+
109
+ const endUserDeniedByText = value => {
110
+ const text = normalize(value);
111
+ return /(module|dienste|datenpunkte|objekte|struktur|systemlogs|logs|zugange|rechte|benutzer|users|gruppen|groups|system hosts|system-hosts|hosts|dateien|files|sicherung|backup|konsole|console|terminal|xterm|admin|adapter|instances?|instanzen|geraetesuche|discovery|systemschutz|security)/.test(text);
112
+ };
113
+
114
+ const isEndUserTab = (tab, label = '') => {
115
+ const clean = normalize(tab);
116
+ const text = `${clean} ${normalize(label)}`;
117
+ if (!clean || clean === 'tab-intro') return false;
118
+ if (/^tab-(nexowatt-ui|nexowatt-cockpit|eos-cockpit|eos-dashboard|kunden-cockpit|endkunden-cockpit)(?:-|$)/.test(clean)) return true;
119
+ if (/^tab-(lovelace|jarvis|vis|iqontrol|material)(?:-|$)/.test(clean)) return true;
120
+ if (endUserDeniedByText(text)) return false;
121
+ return endUserAllowedByText(text);
122
+ };
123
+
124
+ const isInstallerDenied = (tab, label = '') => {
125
+ const text = `${normalize(tab)} ${normalize(label)}`;
126
+ return /(?:tab-users|tab-hosts|tab-xterm|tab-xtrem|tab-admin|tab-system|zugange\s*&?\s*rechte|zugange|rechte|benutzer|users|gruppen|system-hosts|system hosts|hosts|konsole|console|terminal|xterm|sicherung|backup|systemschutz|security|legacy admin)/.test(text);
127
+ };
128
+
129
+ const isRouteAllowed = (role, tab, label = '') => {
130
+ if (role === 'admin') return true;
131
+ if (role === 'installer') return !isInstallerDenied(tab, label);
132
+ if (role === 'enduser') return isEndUserTab(tab, label);
133
+ return false;
134
+ };
135
+
136
+ const navEntries = () => {
137
+ const anchors = Array.from(document.querySelectorAll('a[href*="#tab-"], a[href*="#/tab-"]'));
138
+ const seen = new Set();
139
+ return anchors.map(anchor => {
140
+ const tab = tabFromHref(anchor.getAttribute('href') || anchor.href || '');
141
+ if (!tab || seen.has(anchor)) return null;
142
+ seen.add(anchor);
143
+ const root = anchor.closest('.MuiListItem-root, .MuiButtonBase-root, li, [role="button"], [class*="DrawerItem"], [class*="dragWrapper"]') || anchor;
144
+ return { anchor, root, tab, label: textOf(root) || textOf(anchor) };
145
+ }).filter(Boolean);
146
+ };
147
+
148
+ const firstAllowedTab = role => {
149
+ const entries = navEntries();
150
+ const hit = entries.find(entry => isRouteAllowed(role, entry.tab, entry.label));
151
+ if (hit) return hit.tab;
152
+ if (role === 'enduser') return 'tab-nexowatt-ui';
153
+ if (role === 'installer') return 'tab-instances';
154
+ return 'tab-intro';
155
+ };
156
+
157
+ const applyMenuPolicy = () => safe(() => {
158
+ if (!state.policy || !state.role || state.role === 'unknown') return;
159
+ navEntries().forEach(entry => {
160
+ const hide = !isRouteAllowed(state.role, entry.tab, entry.label);
161
+ [entry.anchor, entry.root].forEach(el => {
162
+ if (!el) return;
163
+ el.classList.toggle('eos-role-hidden', hide);
164
+ if (hide) el.setAttribute('aria-hidden', 'true');
165
+ else el.removeAttribute('aria-hidden');
166
+ });
167
+ });
168
+ });
169
+
170
+ const setHashTab = tab => {
171
+ if (!tab) return;
172
+ if (state.lastTarget === tab && Date.now() - Number(sessionStorage.getItem('eosRoleLastRedirectAt') || 0) < 1200) return;
173
+ state.lastTarget = tab;
174
+ state.redirects += 1;
175
+ sessionStorage.setItem('eosRoleLastRedirectAt', String(Date.now()));
176
+ const next = `#${tab}`;
177
+ if (window.location.hash !== next) window.location.hash = next;
178
+ window.dispatchEvent(new CustomEvent('eos-role-navigate', { detail: { tab } }));
179
+ };
180
+
181
+ const redirectIfNeeded = () => safe(() => {
182
+ if (isLoginView()) return;
183
+ const role = state.role;
184
+ if (!role || role === 'unknown' || role === 'admin') return;
185
+ const tab = hashTab() || 'tab-intro';
186
+ if (isRouteAllowed(role, tab, '')) return;
187
+ const target = firstAllowedTab(role);
188
+ if (target && target !== tab && state.redirects < 6) setHashTab(target);
189
+ });
190
+
191
+ const appPaper = () => document.getElementById('app-paper') || document.querySelector('main, #root');
192
+
193
+ const appLooksBlank = () => {
194
+ const paper = appPaper();
195
+ if (!paper) return false;
196
+ const tab = hashTab();
197
+ const text = normalize(paper.textContent || '');
198
+ const interactive = paper.querySelectorAll('iframe, a, button, input, select, textarea, table, [role="grid"], .MuiDataGrid-root, .MuiTable-root').length;
199
+ if (state.role === 'enduser' && (!isEndUserTab(tab, text) || tab === 'tab-intro')) return true;
200
+ return text.length < 12 && interactive === 0;
201
+ };
202
+
203
+ const showLandingFallback = () => safe(() => {
204
+ if (isLoginView() || state.role !== 'enduser') return;
205
+ if (!appLooksBlank()) {
206
+ document.getElementById('eos-role-landing')?.remove();
207
+ return;
208
+ }
209
+ const paper = appPaper();
210
+ if (!paper) return;
211
+ let landing = document.getElementById('eos-role-landing');
212
+ if (!landing) {
213
+ landing = document.createElement('section');
214
+ landing.id = 'eos-role-landing';
215
+ landing.className = 'eos-role-landing';
216
+ landing.innerHTML = `
217
+ <div class="eos-role-landing-card">
218
+ <div class="eos-role-landing-eyebrow">NexoWatt EOS</div>
219
+ <h1>Endkundenbereich</h1>
220
+ <p>Dieser Benutzer ist als Endkunde angemeldet. Die Administrations-Startseite ist für diese Rolle gesperrt; EOS öffnet stattdessen das Kunden-Cockpit.</p>
221
+ <button type="button" class="eos-role-open-cockpit">EOS Cockpit öffnen</button>
222
+ </div>
223
+ `;
224
+ landing.querySelector('.eos-role-open-cockpit')?.addEventListener('click', event => {
225
+ event.preventDefault();
226
+ setHashTab(firstAllowedTab('enduser') || 'tab-nexowatt-ui');
227
+ });
228
+ }
229
+ if (!paper.contains(landing)) {
230
+ paper.appendChild(landing);
231
+ }
232
+ });
233
+
234
+ const scheduleFallbackCheck = () => {
235
+ clearTimeout(state.fallbackTimer);
236
+ state.fallbackTimer = setTimeout(() => {
237
+ showLandingFallback();
238
+ }, 1000);
239
+ };
240
+
241
+ const apply = () => {
242
+ if (!state.policy) return;
243
+ setRoleClasses(state.role);
244
+ applyMenuPolicy();
245
+ redirectIfNeeded();
246
+ scheduleFallbackCheck();
247
+ };
248
+
249
+ const scheduleApply = () => {
250
+ if (state.scheduled) return;
251
+ state.scheduled = true;
252
+ requestAnimationFrame(() => {
253
+ state.scheduled = false;
254
+ apply();
255
+ });
256
+ };
257
+
258
+ const fetchPolicy = async () => {
259
+ for (const url of securityEndpointUrls()) {
260
+ try {
261
+ const response = await fetch(url, { credentials: 'same-origin', cache: 'no-store' });
262
+ if (!response.ok) continue;
263
+ const json = await response.json();
264
+ const role = roleFromPolicy(json);
265
+ state.policy = { ...json, role };
266
+ state.role = role;
267
+ window.NEXOWATT_EOS_ROLE_POLICY = state.policy;
268
+ scheduleApply();
269
+ return;
270
+ } catch (_) {
271
+ // try next endpoint
272
+ }
273
+ }
274
+ state.policy = { role: 'enduser', isEndUser: true, groups: [] };
275
+ state.role = 'enduser';
276
+ window.NEXOWATT_EOS_ROLE_POLICY = state.policy;
277
+ scheduleApply();
278
+ };
279
+
280
+ const startObserver = () => safe(() => {
281
+ if (state.observer || !document.documentElement) return;
282
+ state.observer = new MutationObserver(scheduleApply);
283
+ state.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['href', 'class', 'aria-label', 'title'] });
284
+ });
285
+
286
+ const start = () => {
287
+ fetchPolicy();
288
+ startObserver();
289
+ window.addEventListener('hashchange', () => {
290
+ state.redirects = 0;
291
+ scheduleApply();
292
+ });
293
+ window.addEventListener('storage', scheduleApply);
294
+ setTimeout(scheduleApply, 400);
295
+ setTimeout(scheduleApply, 1500);
296
+ setTimeout(scheduleApply, 3500);
297
+ };
298
+
299
+ if (document.readyState === 'loading') {
300
+ document.addEventListener('DOMContentLoaded', start, { once: true });
301
+ } else {
302
+ start();
303
+ }
304
+ })();
@@ -1,7 +1,7 @@
1
1
  (() => {
2
2
  'use strict';
3
3
 
4
- const VERSION = 'v47-delete-logquiet-fix';
4
+ const VERSION = 'v48-role-guard-fix';
5
5
  const LEGACY_ADMIN = 'admin';
6
6
  const LEGACY_ADMIN_INSTANCE = 'admin.0';
7
7
  const CORE_PROTECTED_ADAPTERS = ['admin', 'eos-admin', 'backitup', 'nexowatt-devices', 'nexowatt-device', 'nexowatt-dev', 'nexowatt-ui'];
package/build/lib/web.js CHANGED
@@ -416,23 +416,115 @@ class Web {
416
416
  // settings so normal installed adapters/instances remain deletable.
417
417
  return [...new Set(CORE_PROTECTED_ADAPTER_NAMES)].sort();
418
418
  }
419
- async getEosGroupsForUser(userId) {
419
+ getEosConfiguredRoleGroups(...keys) {
420
420
  const groups = new Set();
421
+ const add = value => {
422
+ if (!value) return;
423
+ if (Array.isArray(value)) {
424
+ value.forEach(add);
425
+ return;
426
+ }
427
+ if (typeof value === 'object') {
428
+ if (value.enabled === false) return;
429
+ add(value.group || value.id || value.name);
430
+ return;
431
+ }
432
+ const group = this.normalizeEosGroupId(String(value));
433
+ if (group) groups.add(group);
434
+ };
435
+ const settings = this.settings || {};
436
+ keys.forEach(key => add(settings[key]));
437
+ return [...groups].sort();
438
+ }
439
+ getEosInstallerGroups() {
440
+ const groups = new Set(this.getEosConfiguredRoleGroups(
441
+ 'eosInstallerGroups',
442
+ 'eosInstallateurGroups',
443
+ 'eosServiceGroups',
444
+ ));
445
+ groups.add('system.group.installateur');
446
+ groups.add('system.group.installateure');
447
+ groups.add('system.group.installer');
448
+ groups.add('system.group.service');
449
+ groups.add('system.group.techniker');
450
+ return [...groups].sort();
451
+ }
452
+ getEosEndUserGroups() {
453
+ const groups = new Set(this.getEosConfiguredRoleGroups(
454
+ 'eosEndUserGroups',
455
+ 'eosEndkundeGroups',
456
+ 'eosCustomerGroups',
457
+ ));
458
+ groups.add('system.group.endkunde');
459
+ groups.add('system.group.endkunden');
460
+ groups.add('system.group.kunde');
461
+ groups.add('system.group.kunden');
462
+ groups.add('system.group.user');
463
+ groups.add('system.group.users');
464
+ return [...groups].sort();
465
+ }
466
+ getTranslatedName(value) {
467
+ if (!value) return '';
468
+ if (typeof value === 'string') return value;
469
+ return String(value.de || value.en || Object.values(value)[0] || '');
470
+ }
471
+ async getEosGroupDetailsForUser(userId) {
472
+ const groups = [];
421
473
  try {
422
474
  const result = await this.adapter.getObjectViewAsync('system', 'group', {
423
475
  startkey: 'system.group.',
424
476
  endkey: 'system.group.香',
477
+ include_docs: true,
425
478
  });
426
479
  for (const row of result.rows) {
427
- const group = row.value || row.doc;
480
+ const group = row.doc || row.value;
428
481
  const members = Array.isArray(group?.common?.members) ? group.common.members : [];
429
- if (members.includes(userId)) groups.add(row.id);
482
+ if (members.includes(userId)) {
483
+ groups.push({
484
+ id: row.id,
485
+ name: this.getTranslatedName(group?.common?.name),
486
+ desc: this.getTranslatedName(group?.common?.desc),
487
+ });
488
+ }
430
489
  }
431
490
  }
432
491
  catch (e) {
433
492
  this.adapter.log.debug(`Cannot resolve EOS security groups for ${userId}: ${e instanceof Error ? e.message : e}`);
434
493
  }
435
- return [...groups].sort();
494
+ return groups.sort((a, b) => a.id.localeCompare(b.id));
495
+ }
496
+ async getEosGroupsForUser(userId) {
497
+ return (await this.getEosGroupDetailsForUser(userId)).map(group => group.id);
498
+ }
499
+ normalizeEosRoleText(value) {
500
+ return String(value || '')
501
+ .toLowerCase()
502
+ .normalize('NFD')
503
+ .replace(/[\u0300-\u036f]/g, '')
504
+ .replace(/^system\.group\./, '')
505
+ .replace(/[^a-z0-9]+/g, ' ')
506
+ .trim();
507
+ }
508
+ resolveEosRole(userId, groups, groupNames, adminGroups) {
509
+ if (userId === 'system.user.admin' || groups.includes('system.group.administrator') || groups.some(group => adminGroups.includes(group))) {
510
+ return 'admin';
511
+ }
512
+ const installerGroups = this.getEosInstallerGroups();
513
+ if (groups.some(group => installerGroups.includes(group))) {
514
+ return 'installer';
515
+ }
516
+ const endUserGroups = this.getEosEndUserGroups();
517
+ if (groups.some(group => endUserGroups.includes(group))) {
518
+ return 'enduser';
519
+ }
520
+ const roleText = [...groups, ...groupNames].map(value => this.normalizeEosRoleText(value)).join(' ');
521
+ if (/(^| )(installateur|installateure|installer|installation|service|servicetechniker|techniker|technician|integrator|partner|wartung|maintenance)( |$)/i.test(roleText)) {
522
+ return 'installer';
523
+ }
524
+ if (/(^| )(endkunde|endkunden|kunde|kunden|customer|customers|bediener|operator|viewer|nutzer|benutzer)( |$)/i.test(roleText)) {
525
+ return 'enduser';
526
+ }
527
+ return 'enduser';
436
528
  }
437
529
  readAccessTokenFromRequest(req) {
438
530
  const cookieHeader = req.headers.cookie || '';
@@ -481,20 +573,31 @@ class Web {
481
573
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
482
574
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
483
575
  const userId = await this.readEosCurrentUser(req);
484
- const groups = userId ? await this.getEosGroupsForUser(userId) : [];
576
+ const groupDetails = userId ? await this.getEosGroupDetailsForUser(userId) : [];
577
+ const groups = groupDetails.map(group => group.id);
578
+ const groupNames = groupDetails.flatMap(group => [group.name, group.desc]).filter(Boolean);
485
579
  const adminGroups = this.getEosSecurityAdminGroups();
486
- const isAdministrator = userId === 'system.user.admin' || groups.includes('system.group.administrator');
487
- const isEosAdminGroup = isAdministrator || groups.some(group => adminGroups.includes(group));
580
+ const role = this.resolveEosRole(userId, groups, groupNames, adminGroups);
581
+ const isAdministrator = role === 'admin' && (userId === 'system.user.admin' || groups.includes('system.group.administrator'));
582
+ const isEosAdminGroup = role === 'admin';
583
+ const isInstaller = role === 'installer';
584
+ const isEndUser = role === 'enduser';
488
585
  res.status(200).json({
489
586
  user: userId,
490
587
  groups,
588
+ groupNames,
491
589
  adminGroups,
590
+ installerGroups: this.getEosInstallerGroups(),
591
+ endUserGroups: this.getEosEndUserGroups(),
592
+ role,
492
593
  isAdministrator,
493
594
  isEosAdminGroup,
595
+ isInstaller,
596
+ isEndUser,
494
597
  isAdmin: isEosAdminGroup,
495
598
  hideLegacyAdminForNonAdmins: this.settings.eosHideLegacyAdminForNonAdmins !== false && this.settings.eosHideLegacyAdminFromNonAdmins !== false,
496
599
  hideLegacyAdminFromNonAdmins: this.settings.eosHideLegacyAdminForNonAdmins !== false && this.settings.eosHideLegacyAdminFromNonAdmins !== false,
497
- restrictProtectedAdapterControls: this.settings.eosRestrictProtectedAdapterControls !== false && this.settings.eosApplyAdminOnlyAclToProtectedAdapters !== false,
600
+ restrictProtectedAdapterControls: this.settings.eosRestrictProtectedAdapterControls !== false,
498
601
  legacyAdminAdapter: 'admin',
499
602
  legacyAdminInstance: 'admin.0',
500
603
  protectedAdapters: this.getEosProtectedAdapterNames(),
@@ -700,9 +803,15 @@ class Web {
700
803
  res.status(200).json({
701
804
  user: null,
702
805
  groups: [],
806
+ groupNames: [],
703
807
  adminGroups: ['system.group.administrator'],
808
+ installerGroups: this.getEosInstallerGroups(),
809
+ endUserGroups: this.getEosEndUserGroups(),
810
+ role: 'enduser',
704
811
  isAdministrator: false,
705
812
  isEosAdminGroup: false,
813
+ isInstaller: false,
814
+ isEndUser: true,
706
815
  isAdmin: false,
707
816
  hideLegacyAdminForNonAdmins: true,
708
817
  hideLegacyAdminFromNonAdmins: true,
@@ -789,9 +898,15 @@ class Web {
789
898
  res.status(200).json({
790
899
  user: null,
791
900
  groups: [],
901
+ groupNames: [],
792
902
  adminGroups: ['system.group.administrator'],
903
+ installerGroups: this.getEosInstallerGroups(),
904
+ endUserGroups: this.getEosEndUserGroups(),
905
+ role: 'enduser',
793
906
  isAdministrator: false,
794
907
  isEosAdminGroup: false,
908
+ isInstaller: false,
909
+ isEndUser: true,
795
910
  isAdmin: false,
796
911
  hideLegacyAdminForNonAdmins: true,
797
912
  hideLegacyAdminFromNonAdmins: true,
@@ -1,29 +1,29 @@
1
- # NexoWatt EOS Admin Branding
2
-
3
- Diese Variante hält die Adapter-Basis bewusst nahe am upstream `admin`-Adapter, damit Updates sauber übernommen werden können.
4
-
5
- ## Update-Strategie
6
-
7
- 1. Fork/Branch auf den aktuellen upstream-Stand bringen.
8
- 2. Branding-Patch erneut anwenden.
9
- 3. Frontend/Backend bauen und testen.
10
- 4. Als internen PR oder Fork-PR mergen.
11
-
12
- Empfohlener Ablauf:
13
-
14
- ```bash
15
- git remote add upstream https://github.com/ioBroker/ioBroker.admin.git
16
- git fetch upstream
17
- git checkout -b nexowatt/eos-admin-branding upstream/master
18
- python scripts/apply-eos-branding.py
19
- npm install
20
- cd src-admin && npm install && cd ..
21
- npm run build
22
- npm test
23
- git add .
24
- git commit -m "feat: add NexoWatt EOS branded admin skin"
25
- ```
26
-
27
- ## Bewusste Entscheidung
28
-
29
- Der technische Paketname `iobroker.admin` bleibt erhalten. Das minimiert Update- und Rebase-Konflikte. Sichtbare UI-Bezeichnungen werden zur Laufzeit und in den relevanten Metadaten auf `NexoWatt EOS` gebrandet.
1
+ # NexoWatt EOS Admin Branding
2
+
3
+ Diese Variante hält die Adapter-Basis bewusst nahe am upstream `admin`-Adapter, damit Updates sauber übernommen werden können.
4
+
5
+ ## Update-Strategie
6
+
7
+ 1. Fork/Branch auf den aktuellen upstream-Stand bringen.
8
+ 2. Branding-Patch erneut anwenden.
9
+ 3. Frontend/Backend bauen und testen.
10
+ 4. Als internen PR oder Fork-PR mergen.
11
+
12
+ Empfohlener Ablauf:
13
+
14
+ ```bash
15
+ git remote add upstream https://github.com/ioBroker/ioBroker.admin.git
16
+ git fetch upstream
17
+ git checkout -b nexowatt/eos-admin-branding upstream/master
18
+ python scripts/apply-eos-branding.py
19
+ npm install
20
+ cd src-admin && npm install && cd ..
21
+ npm run build
22
+ npm test
23
+ git add .
24
+ git commit -m "feat: add NexoWatt EOS branded admin skin"
25
+ ```
26
+
27
+ ## Bewusste Entscheidung
28
+
29
+ Der technische Paketname `iobroker.admin` bleibt erhalten. Das minimiert Update- und Rebase-Konflikte. Sichtbare UI-Bezeichnungen werden zur Laufzeit und in den relevanten Metadaten auf `NexoWatt EOS` gebrandet.
@@ -1,9 +1,9 @@
1
- # NexoWatt EOS UI v6
2
-
3
- Änderungen:
4
-
5
- - Abmelden-Button wird auf der Login-Maske nicht mehr erzeugt und zusätzlich per CSS ausgeblendet.
6
- - Das temporäre EOS-/Kompass-Icon wurde durch das originale NexoWatt-Logo ersetzt.
7
- - Admin-SVGs, Favicons, Manifest-Icons und Runtime-Branding zeigen dasselbe NexoWatt-Symbol.
8
- - Logout-Redirect wurde vereinfacht, damit keine kodierten `/%2F.../index.html?login` 404-Pfade entstehen.
9
- - Frontend-Bundles bleiben unverändert; nur Branding-Overlay und Assets wurden angepasst.
1
+ # NexoWatt EOS UI v6
2
+
3
+ Änderungen:
4
+
5
+ - Abmelden-Button wird auf der Login-Maske nicht mehr erzeugt und zusätzlich per CSS ausgeblendet.
6
+ - Das temporäre EOS-/Kompass-Icon wurde durch das originale NexoWatt-Logo ersetzt.
7
+ - Admin-SVGs, Favicons, Manifest-Icons und Runtime-Branding zeigen dasselbe NexoWatt-Symbol.
8
+ - Logout-Redirect wurde vereinfacht, damit keine kodierten `/%2F.../index.html?login` 404-Pfade entstehen.
9
+ - Frontend-Bundles bleiben unverändert; nur Branding-Overlay und Assets wurden angepasst.
@@ -1,25 +1,25 @@
1
- # NexoWatt EOS UI v5
2
-
3
- Diese Version bleibt auf der bestehenden Admin-Adapter-Struktur und verändert nur das Frontend-/Login-Branding über ein kleines Overlay.
4
-
5
- ## Schwerpunkte v5
6
-
7
- - Login-Titel bereinigt: `NexoWatt EOS`, Unterzeile `Energy Operation System`.
8
- - Doppelte/unsaubere Login-Pseudo-Texte entfernt.
9
- - Linke Navigation als scrollbarer EOS-Bereich vorbereitet, damit zusätzliche Einträge später sauber per Slider/Scrollbar erreichbar bleiben.
10
- - NexoWatt/EOS-Kopfbereich im Drawer zusammengeführt; die alte doppelte Logozeile wird ausgeblendet.
11
- - Logout-Button wird fest sichtbar oben rechts platziert und liegt nicht mehr außerhalb des sichtbaren Bereichs.
12
- - Topbar und Navigation wurden responsiver ausgelegt.
13
- - Adapter-/Modul-Kacheln wurden weiter abgerundet und die alten blauen Kopfbereiche werden durch dunklere EOS-Flächen ersetzt.
14
- - Performance-Härtung: kein permanenter 1,5-Sekunden-Full-Scan mehr, kein Beobachten der häufig wechselnden `class`-Attribute, weniger globale Blur-/Shadow-Effekte.
15
-
16
- ## Technischer Ansatz
17
-
18
- Die React/Vite-Bundles bleiben unangetastet. Das Branding läuft über:
19
-
20
- - `adminWww/css/eos-branding.css`
21
- - `adminWww/js/eos-branding.js`
22
- - `src-admin/public/css/eos-branding.css`
23
- - `src-admin/public/js/eos-branding.js`
24
-
25
- Damit bleibt der Adapter updatefreundlicher, weil Upstream-Dateien nicht unnötig hart gepatcht werden.
1
+ # NexoWatt EOS UI v5
2
+
3
+ Diese Version bleibt auf der bestehenden Admin-Adapter-Struktur und verändert nur das Frontend-/Login-Branding über ein kleines Overlay.
4
+
5
+ ## Schwerpunkte v5
6
+
7
+ - Login-Titel bereinigt: `NexoWatt EOS`, Unterzeile `Energy Operation System`.
8
+ - Doppelte/unsaubere Login-Pseudo-Texte entfernt.
9
+ - Linke Navigation als scrollbarer EOS-Bereich vorbereitet, damit zusätzliche Einträge später sauber per Slider/Scrollbar erreichbar bleiben.
10
+ - NexoWatt/EOS-Kopfbereich im Drawer zusammengeführt; die alte doppelte Logozeile wird ausgeblendet.
11
+ - Logout-Button wird fest sichtbar oben rechts platziert und liegt nicht mehr außerhalb des sichtbaren Bereichs.
12
+ - Topbar und Navigation wurden responsiver ausgelegt.
13
+ - Adapter-/Modul-Kacheln wurden weiter abgerundet und die alten blauen Kopfbereiche werden durch dunklere EOS-Flächen ersetzt.
14
+ - Performance-Härtung: kein permanenter 1,5-Sekunden-Full-Scan mehr, kein Beobachten der häufig wechselnden `class`-Attribute, weniger globale Blur-/Shadow-Effekte.
15
+
16
+ ## Technischer Ansatz
17
+
18
+ Die React/Vite-Bundles bleiben unangetastet. Das Branding läuft über:
19
+
20
+ - `adminWww/css/eos-branding.css`
21
+ - `adminWww/js/eos-branding.js`
22
+ - `src-admin/public/css/eos-branding.css`
23
+ - `src-admin/public/js/eos-branding.js`
24
+
25
+ Damit bleibt der Adapter updatefreundlicher, weil Upstream-Dateien nicht unnötig hart gepatcht werden.
@@ -1,16 +1,16 @@
1
- # NexoWatt EOS UI v12 – Header-Logo und Navigationsleiste
2
-
3
- Diese Version basiert auf dem guten v11 Layout mit vollem Header, waagerechter Navigation und großem Inhaltsbereich.
4
-
5
- ## Änderungen
6
-
7
- - Der doppelte NexoWatt/EOS Bereich in der unteren Navigationsleiste wurde entfernt.
8
- - In der unteren Navigationsleiste bleibt links nur noch der Einklapp-Pfeil erhalten.
9
- - Das echte NexoWatt Logo im oberen Header ist größer, kontrastreicher und besser sichtbar.
10
- - Die Login-Zentrierung, die EOS Modul-Kacheln, das lesbare Drei-Punkte-Info-Overlay und die 404-sichere Asset-Struktur aus v11 bleiben erhalten.
11
-
12
- ## Technischer Ansatz
13
-
14
- Die upstream React/Vite Bundles bleiben weiterhin unverändert. Das Design wird über `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js` angewendet.
15
-
16
- Cache-Busting: `v=12`.
1
+ # NexoWatt EOS UI v12 – Header-Logo und Navigationsleiste
2
+
3
+ Diese Version basiert auf dem guten v11 Layout mit vollem Header, waagerechter Navigation und großem Inhaltsbereich.
4
+
5
+ ## Änderungen
6
+
7
+ - Der doppelte NexoWatt/EOS Bereich in der unteren Navigationsleiste wurde entfernt.
8
+ - In der unteren Navigationsleiste bleibt links nur noch der Einklapp-Pfeil erhalten.
9
+ - Das echte NexoWatt Logo im oberen Header ist größer, kontrastreicher und besser sichtbar.
10
+ - Die Login-Zentrierung, die EOS Modul-Kacheln, das lesbare Drei-Punkte-Info-Overlay und die 404-sichere Asset-Struktur aus v11 bleiben erhalten.
11
+
12
+ ## Technischer Ansatz
13
+
14
+ Die upstream React/Vite Bundles bleiben weiterhin unverändert. Das Design wird über `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js` angewendet.
15
+
16
+ Cache-Busting: `v=12`.
@@ -1,16 +1,16 @@
1
- # NexoWatt EOS UI v12
2
-
3
- Diese Version basiert auf dem horizontalen v11-Layout.
4
-
5
- ## Änderungen
6
-
7
- - Der globale Header bleibt über die komplette Seitenbreite erhalten.
8
- - Die Navigation bleibt waagerecht unter dem Header.
9
- - Der doppelte Branding-Block am Anfang der horizontalen Navigation wurde entfernt.
10
- - An dieser Stelle bleibt nur der native Einklapp-Pfeil sichtbar.
11
- - Das NexoWatt-EOS-Logo im Header wurde größer, kontrastreicher und besser sichtbar gestaltet.
12
- - Die Login-Zentrierung und die 404-Asset-Pfade aus v11 bleiben erhalten.
13
-
14
- ## Technischer Hinweis
15
-
16
- Die Änderung ist weiterhin als CSS/JS-Overlay umgesetzt. Die upstream React/Vite-Bundles bleiben unverändert.
1
+ # NexoWatt EOS UI v12
2
+
3
+ Diese Version basiert auf dem horizontalen v11-Layout.
4
+
5
+ ## Änderungen
6
+
7
+ - Der globale Header bleibt über die komplette Seitenbreite erhalten.
8
+ - Die Navigation bleibt waagerecht unter dem Header.
9
+ - Der doppelte Branding-Block am Anfang der horizontalen Navigation wurde entfernt.
10
+ - An dieser Stelle bleibt nur der native Einklapp-Pfeil sichtbar.
11
+ - Das NexoWatt-EOS-Logo im Header wurde größer, kontrastreicher und besser sichtbar gestaltet.
12
+ - Die Login-Zentrierung und die 404-Asset-Pfade aus v11 bleiben erhalten.
13
+
14
+ ## Technischer Hinweis
15
+
16
+ Die Änderung ist weiterhin als CSS/JS-Overlay umgesetzt. Die upstream React/Vite-Bundles bleiben unverändert.
@@ -1,8 +1,8 @@
1
- # NexoWatt EOS Admin UI v15
2
-
3
- Diese Version basiert auf v14 und korrigiert nur zwei optische Punkte:
4
-
5
- - Das NexoWatt-Logo im Header ist dunkler abgestimmt, damit es nicht überbelichtet wirkt. Der grüne Schein im Hintergrund bleibt erhalten.
6
- - Der Einklapp-Pfeil der horizontalen Navigation sitzt weiter links und native Abmelden-Einträge werden zuverlässig aus der Navigation entfernt, damit keine Überlappung entsteht.
7
-
8
- Die React/Vite-Bundles bleiben unverändert.
1
+ # NexoWatt EOS Admin UI v15
2
+
3
+ Diese Version basiert auf v14 und korrigiert nur zwei optische Punkte:
4
+
5
+ - Das NexoWatt-Logo im Header ist dunkler abgestimmt, damit es nicht überbelichtet wirkt. Der grüne Schein im Hintergrund bleibt erhalten.
6
+ - Der Einklapp-Pfeil der horizontalen Navigation sitzt weiter links und native Abmelden-Einträge werden zuverlässig aus der Navigation entfernt, damit keine Überlappung entsteht.
7
+
8
+ Die React/Vite-Bundles bleiben unverändert.
@@ -1,9 +1,9 @@
1
- # NexoWatt EOS UI v16
2
-
3
- Basis: v15.
4
-
5
- Korrekturen:
6
- - Header-Logo sauber zentriert, nicht überstrahlt und nicht zu dunkel.
7
- - Nativer Logout/Abmelden-Menüpunkt wird zuverlässig aus der horizontalen Navigation entfernt.
8
- - Einklapp-Pfeil bekommt einen eigenen Bereich ohne Überlappung.
9
- - Benutzername im Header ist hell und lesbar.
1
+ # NexoWatt EOS UI v16
2
+
3
+ Basis: v15.
4
+
5
+ Korrekturen:
6
+ - Header-Logo sauber zentriert, nicht überstrahlt und nicht zu dunkel.
7
+ - Nativer Logout/Abmelden-Menüpunkt wird zuverlässig aus der horizontalen Navigation entfernt.
8
+ - Einklapp-Pfeil bekommt einen eigenen Bereich ohne Überlappung.
9
+ - Benutzername im Header ist hell und lesbar.
@@ -1,24 +1,24 @@
1
- # NexoWatt EOS UI v17
2
-
3
- Basis: v16 Layoutstand.
4
-
5
- ## Änderungen
6
-
7
- - Header-Logo links optisch neu abgestimmt:
8
- - eigenes optimiertes Header-Logo `nexowatt-header-192.png`
9
- - Logo sitzt mittig im ovalen Header-Badge
10
- - dunkler Logo-Hintergrund bleibt als Schein erhalten
11
- - kein Mehrfach-/Geisterschein mehr
12
- - Horizontaler Navigationsbereich:
13
- - der Einklapp-Pfeil hat einen festen eigenen Slot
14
- - alte Drawer-Header-Inhalte werden dort ausgeblendet
15
- - native `Abmelden`-Navigationseinträge werden aus der EOS-Navigation entfernt, damit keine Überlappung mehr entsteht
16
- - Benutzername im Header bleibt hell lesbar.
17
- - Authentifizierung / automatische Abmeldung:
18
- - keine eigene Token-Löschung mehr im EOS-Overlay
19
- - kein eigener Logout-Button mehr
20
- - Session-Ablauf und automatische Abmeldung laufen über die originale Admin-Logik.
21
-
22
- ## Technischer Hinweis
23
-
24
- Das EOS-Overlay greift nur in Branding/Layout ein. Die native Session-Überwachung bleibt in `src-admin/src/App.tsx` und die Token-Erneuerung bleibt in `src-admin/src/login/Login.tsx`.
1
+ # NexoWatt EOS UI v17
2
+
3
+ Basis: v16 Layoutstand.
4
+
5
+ ## Änderungen
6
+
7
+ - Header-Logo links optisch neu abgestimmt:
8
+ - eigenes optimiertes Header-Logo `nexowatt-header-192.png`
9
+ - Logo sitzt mittig im ovalen Header-Badge
10
+ - dunkler Logo-Hintergrund bleibt als Schein erhalten
11
+ - kein Mehrfach-/Geisterschein mehr
12
+ - Horizontaler Navigationsbereich:
13
+ - der Einklapp-Pfeil hat einen festen eigenen Slot
14
+ - alte Drawer-Header-Inhalte werden dort ausgeblendet
15
+ - native `Abmelden`-Navigationseinträge werden aus der EOS-Navigation entfernt, damit keine Überlappung mehr entsteht
16
+ - Benutzername im Header bleibt hell lesbar.
17
+ - Authentifizierung / automatische Abmeldung:
18
+ - keine eigene Token-Löschung mehr im EOS-Overlay
19
+ - kein eigener Logout-Button mehr
20
+ - Session-Ablauf und automatische Abmeldung laufen über die originale Admin-Logik.
21
+
22
+ ## Technischer Hinweis
23
+
24
+ Das EOS-Overlay greift nur in Branding/Layout ein. Die native Session-Überwachung bleibt in `src-admin/src/App.tsx` und die Token-Erneuerung bleibt in `src-admin/src/login/Login.tsx`.
@@ -1,13 +1,13 @@
1
- # NexoWatt EOS UI v6 Fixes
2
-
3
- Diese Version korrigiert die gemeldeten Punkte aus dem Testlauf:
4
-
5
- - Die Login-Seite zeigt keinen Abmelden-Button mehr.
6
- - Login, Drawer, Topbar, Manifest, Favicon und Adapter-Icon verwenden wieder das originale NexoWatt/EOS Logo.
7
- - Das Login-Branding lautet sichtbar `NexoWatt EOS` und `Energy Operation System`.
8
- - Der zusätzliche Logout-Button wird nur im angemeldeten Admin-Shell angezeigt.
9
- - Der Logout-Fallback nutzt die Backend-Route `/logout` ohne kodierten Hash-/Origin-Parameter, damit nach erneutem Login keine 404-Weiterleitung entsteht.
10
- - Login-Weiterleitungen auf `/logout` oder `/login` werden zur Laufzeit bereinigt, um 404-/Redirect-Loops nach der Anmeldung zu vermeiden.
11
- - Die Asset-Prüfung für `adminWww/index.html`, `manifest.json`, Branding-CSS/JS und dynamische Assets wurde durchgeführt.
12
-
13
- Die technische Admin-Basis bleibt upstream-nah. Das Branding liegt weiterhin überwiegend in `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js`.
1
+ # NexoWatt EOS UI v6 Fixes
2
+
3
+ Diese Version korrigiert die gemeldeten Punkte aus dem Testlauf:
4
+
5
+ - Die Login-Seite zeigt keinen Abmelden-Button mehr.
6
+ - Login, Drawer, Topbar, Manifest, Favicon und Adapter-Icon verwenden wieder das originale NexoWatt/EOS Logo.
7
+ - Das Login-Branding lautet sichtbar `NexoWatt EOS` und `Energy Operation System`.
8
+ - Der zusätzliche Logout-Button wird nur im angemeldeten Admin-Shell angezeigt.
9
+ - Der Logout-Fallback nutzt die Backend-Route `/logout` ohne kodierten Hash-/Origin-Parameter, damit nach erneutem Login keine 404-Weiterleitung entsteht.
10
+ - Login-Weiterleitungen auf `/logout` oder `/login` werden zur Laufzeit bereinigt, um 404-/Redirect-Loops nach der Anmeldung zu vermeiden.
11
+ - Die Asset-Prüfung für `adminWww/index.html`, `manifest.json`, Branding-CSS/JS und dynamische Assets wurde durchgeführt.
12
+
13
+ Die technische Admin-Basis bleibt upstream-nah. Das Branding liegt weiterhin überwiegend in `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js`.
@@ -1,9 +1,9 @@
1
- # NexoWatt EOS UI v6
2
-
3
- Änderungen:
4
-
5
- - Login-Maske zeigt keinen zusätzlichen Abmelden-Button mehr.
6
- - Das alte grüne NexoWatt Logo wird überall verwendet: Login, Drawer, Topbar, Favicon-/Manifest-nahe Admin-Assets und Fallback-Icons.
7
- - `Energy Management System` bleibt entfernt; Login nutzt `NexoWatt EOS` und `Energy Operation System`.
8
- - Login-Weiterleitung wurde abgesichert, damit nach erfolgreicher Anmeldung nicht auf eine Route weitergeleitet wird, die einen 404 erzeugen kann.
9
- - Das Branding bleibt weiterhin als Overlay umgesetzt, damit die Upstream-Admin-Struktur möglichst updatefähig bleibt.
1
+ # NexoWatt EOS UI v6
2
+
3
+ Änderungen:
4
+
5
+ - Login-Maske zeigt keinen zusätzlichen Abmelden-Button mehr.
6
+ - Das alte grüne NexoWatt Logo wird überall verwendet: Login, Drawer, Topbar, Favicon-/Manifest-nahe Admin-Assets und Fallback-Icons.
7
+ - `Energy Management System` bleibt entfernt; Login nutzt `NexoWatt EOS` und `Energy Operation System`.
8
+ - Login-Weiterleitung wurde abgesichert, damit nach erfolgreicher Anmeldung nicht auf eine Route weitergeleitet wird, die einen 404 erzeugen kann.
9
+ - Das Branding bleibt weiterhin als Overlay umgesetzt, damit die Upstream-Admin-Struktur möglichst updatefähig bleibt.
@@ -1,10 +1,10 @@
1
- # NexoWatt EOS Admin UI v6
2
-
3
- Änderungen:
4
-
5
- - Login zeigt keinen Abmelden-Button mehr.
6
- - Temporäres Kompass-Icon wurde entfernt. Es wird überall das ursprüngliche NexoWatt-Logo verwendet.
7
- - Admin-/Adapter-Icons, PWA-Icons und Favicons wurden auf das NexoWatt-Logo vereinheitlicht.
8
- - Runtime-Branding beobachtet keine Attribute mehr und reduziert damit DOM-Arbeit auf großen Modul-/Adapterseiten.
9
- - Logout-URL bleibt relativ zur Admin-Basis, um 404-Probleme hinter Pfaden/Proxy-Setups zu vermeiden.
10
- - Die technischen Admin-Bundles bleiben unangetastet.
1
+ # NexoWatt EOS Admin UI v6
2
+
3
+ Änderungen:
4
+
5
+ - Login zeigt keinen Abmelden-Button mehr.
6
+ - Temporäres Kompass-Icon wurde entfernt. Es wird überall das ursprüngliche NexoWatt-Logo verwendet.
7
+ - Admin-/Adapter-Icons, PWA-Icons und Favicons wurden auf das NexoWatt-Logo vereinheitlicht.
8
+ - Runtime-Branding beobachtet keine Attribute mehr und reduziert damit DOM-Arbeit auf großen Modul-/Adapterseiten.
9
+ - Logout-URL bleibt relativ zur Admin-Basis, um 404-Probleme hinter Pfaden/Proxy-Setups zu vermeiden.
10
+ - Die technischen Admin-Bundles bleiben unangetastet.
@@ -1,18 +1,18 @@
1
- # NexoWatt EOS UI v7 – Restore v5 Design, Logo Scope Fix
2
-
3
- Diese Version basiert bewusst wieder auf dem v5-Designstand. Die v6-Änderungen an Drawer/AdapterTile wurden nicht übernommen, damit das vorherige Kachel-/Cockpit-Design erhalten bleibt.
4
-
5
- ## Korrekturen
6
-
7
- - Das markierte EOS-Symbol wurde durch das alte NexoWatt-Logo ersetzt.
8
- - `admin.svg`-Kopien wurden ebenfalls auf das alte NexoWatt-Logo gesetzt.
9
- - Das NexoWatt-Logo wird nur in echten Branding-Bereichen gesetzt: Login, Drawer/Header, EOS-Branding.
10
- - Adapter-/Dienst-/Instanzzeilen werden nicht mehr mit dem NexoWatt-Logo überschrieben.
11
- - `no-image.svg` ist wieder ein neutrales Modul-Platzhalter-Icon, damit fehlende Adapterbilder nicht als NexoWatt-Branding erscheinen.
12
- - Der Abmelden-Button wird auf der Anmeldemaske entfernt und zusätzlich per CSS ausgeblendet.
13
- - Login-Redirects auf `login` oder `logout` werden bereinigt, um 404-Schleifen nach der Anmeldung zu vermeiden.
14
- - Die technische OAuth-Client-ID im Source-Code ist wieder upstream-nah gesetzt.
15
-
16
- ## Wichtig
17
-
18
- Die React/Vite-Runtime-Bundles bleiben unverändert. Das Branding wird über `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js` aufgesetzt.
1
+ # NexoWatt EOS UI v7 – Restore v5 Design, Logo Scope Fix
2
+
3
+ Diese Version basiert bewusst wieder auf dem v5-Designstand. Die v6-Änderungen an Drawer/AdapterTile wurden nicht übernommen, damit das vorherige Kachel-/Cockpit-Design erhalten bleibt.
4
+
5
+ ## Korrekturen
6
+
7
+ - Das markierte EOS-Symbol wurde durch das alte NexoWatt-Logo ersetzt.
8
+ - `admin.svg`-Kopien wurden ebenfalls auf das alte NexoWatt-Logo gesetzt.
9
+ - Das NexoWatt-Logo wird nur in echten Branding-Bereichen gesetzt: Login, Drawer/Header, EOS-Branding.
10
+ - Adapter-/Dienst-/Instanzzeilen werden nicht mehr mit dem NexoWatt-Logo überschrieben.
11
+ - `no-image.svg` ist wieder ein neutrales Modul-Platzhalter-Icon, damit fehlende Adapterbilder nicht als NexoWatt-Branding erscheinen.
12
+ - Der Abmelden-Button wird auf der Anmeldemaske entfernt und zusätzlich per CSS ausgeblendet.
13
+ - Login-Redirects auf `login` oder `logout` werden bereinigt, um 404-Schleifen nach der Anmeldung zu vermeiden.
14
+ - Die technische OAuth-Client-ID im Source-Code ist wieder upstream-nah gesetzt.
15
+
16
+ ## Wichtig
17
+
18
+ Die React/Vite-Runtime-Bundles bleiben unverändert. Das Branding wird über `adminWww/css/eos-branding.css` und `adminWww/js/eos-branding.js` aufgesetzt.
package/io-package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "eos-admin",
4
- "version": "7.9.47",
4
+ "version": "7.9.48",
5
5
  "titleLang": {
6
6
  "en": "NexoWatt EOS Admin",
7
7
  "de": "NexoWatt EOS Admin",
@@ -18,6 +18,10 @@
18
18
  "connectionType": "local",
19
19
  "dataSource": "push",
20
20
  "news": {
21
+ "7.9.48": {
22
+ "en": "Role guard fix: formal admin/installer/end-user roles, end-user auto-routing away from the blank intro page, and role-filtered EOS navigation.",
23
+ "de": "Rollen-Fix: klare Admin-/Installateur-/Endkundenrollen, automatische Endkunden-Weiterleitung weg von der leeren Intro-Seite und rollenbasierte EOS-Navigation."
24
+ },
21
25
  "7.9.47": {
22
26
  "en": "Quiet delete guard fix: stale delete-lock repair runs once and skips protected core adapters, preventing repeated log entries while service deletion remains functional.",
23
27
  "de": "Leiser Delete-Guard-Fix: Reparatur alter Löschsperren läuft nur einmal und überspringt geschützte Core-Adapter, wodurch wiederholte Logeinträge verhindert werden; Dienste löschen bleibt funktionsfähig."
@@ -247,7 +251,7 @@
247
251
  "icon": "admin.svg",
248
252
  "messagebox": true,
249
253
  "enabled": true,
250
- "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.47/admin/admin.svg",
254
+ "extIcon": "https://unpkg.com/iobroker.eos-admin@7.9.48/admin/admin.svg",
251
255
  "keywords": [
252
256
  "NexoWatt",
253
257
  "EOS",
@@ -258,7 +262,7 @@
258
262
  "licensed"
259
263
  ],
260
264
  "compact": true,
261
- "readme": "https://unpkg.com/iobroker.eos-admin@7.9.47/README.md",
265
+ "readme": "https://unpkg.com/iobroker.eos-admin@7.9.48/README.md",
262
266
  "authors": [
263
267
  "bluefox <bluefox@ccu.io>",
264
268
  "hobbyquaker <hq@ccu.io>"
@@ -327,7 +331,7 @@
327
331
  "nondeletable": false,
328
332
  "allowAdapterUpdate": true,
329
333
  "allowAdapterDelete": false,
330
- "meta": "https://unpkg.com/iobroker.eos-admin@7.9.47/io-package.json",
334
+ "meta": "https://unpkg.com/iobroker.eos-admin@7.9.48/io-package.json",
331
335
  "npmPackage": "iobroker.eos-admin",
332
336
  "packetName": "iobroker.eos-admin"
333
337
  },
@@ -419,7 +423,26 @@
419
423
  "eosApplyAdminOnlyAclToProtectedAdapters": false,
420
424
  "eosAssistantEnabled": true,
421
425
  "eosAssistantAdminOnly": false,
422
- "eosStrictProtectedAdapterAcl": false
426
+ "eosStrictProtectedAdapterAcl": false,
427
+ "eosInstallerGroups": [
428
+ {
429
+ "group": "system.group.installateur",
430
+ "note": "Installateure / Service",
431
+ "enabled": true
432
+ }
433
+ ],
434
+ "eosEndUserGroups": [
435
+ {
436
+ "group": "system.group.endkunde",
437
+ "note": "Endkunden / Bediener",
438
+ "enabled": true
439
+ },
440
+ {
441
+ "group": "system.group.endkunden",
442
+ "note": "Endkunden / Bediener",
443
+ "enabled": true
444
+ }
445
+ ]
423
446
  },
424
447
  "notifications": [
425
448
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "iobroker.eos-admin",
3
3
  "description": "NexoWatt EOS Admin interface with reliable repository update mapping and technical log preservation.",
4
- "version": "7.9.47",
4
+ "version": "7.9.48",
5
5
  "contributors": [
6
6
  "bluefox <dogafox@gmail.com>",
7
7
  "apollon77",