iobroker.eos-admin 7.9.97 → 7.9.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL_TEST_V7.9.98.md +39 -0
- package/MERGE_UPDATE_README_V7.9.98.md +29 -0
- package/NEXOWATT_EOS_BUILD_INFO.json +20 -20
- package/PUBLISH_STABLE_V7.9.98.md +33 -0
- package/README.md +16 -6
- package/README_STABILITY_V7.9.98.md +22 -0
- package/RELEASE_ACCEPTANCE_V7.9.98.md +48 -0
- package/RELEASE_NOTES_V7.9.98.md +32 -0
- package/adminWww/assets/bootstrap-COulQZax-v84.js +2 -2
- package/adminWww/css/eos-ems-overview.css +1 -1
- package/adminWww/css/nexowatt-native-shell.css +117 -0
- package/adminWww/index.html +15 -15
- package/adminWww/js/eos-account-management.js +26 -13
- package/adminWww/js/eos-ems-overview.js +1 -1
- package/adminWww/js/eos-role-bootstrap.js +7 -7
- package/adminWww/js/eos-role-ui.js +1 -1
- package/adminWww/js/nexowatt-native-shell.js +1 -1
- package/adminWww/static/js/nexowatt-stable-login.js +3 -3
- package/adminWww/static/js/{nexowatt-stable-v97.js → nexowatt-stable-v98.js} +2 -2
- package/build/lib/web.js +112 -79
- package/build/main.js +1 -1
- package/io-package.json +10 -5
- package/package.json +14 -12
- package/tools/nexowatt-account-management-selftest.cjs +16 -57
- package/tools/nexowatt-account-password-flow-selftest.cjs +186 -0
- package/tools/nexowatt-login-layout-selftest.cjs +8 -8
- package/tools/nexowatt-merge-update-selftest.cjs +1 -1
- package/tools/nexowatt-modern-ui-selftest.cjs +2 -2
- package/tools/nexowatt-password-write-selftest.cjs +48 -0
- package/tools/nexowatt-runtime-cleanup-selftest.cjs +5 -5
- package/tools/nexowatt-stable-v98-selftest.cjs +14 -0
- package/tools/nexowatt-sync-release-version.cjs +42 -1
- package/tools/nexowatt-ui-overview-runtime-selftest.cjs +1 -1
- package/tools/nexowatt-validate-package.cjs +20 -11
- package/tools/nexowatt-version-sync-selftest.cjs +18 -1
- package/INSTALL_TEST_V7.9.97.md +0 -11
- package/MERGE_UPDATE_README_V7.9.97.md +0 -5
- package/PUBLISH_STABLE_V7.9.97.md +0 -15
- package/README_STABILITY_V7.9.97.md +0 -18
- package/RELEASE_ACCEPTANCE_V7.9.97.md +0 -11
- package/RELEASE_NOTES_V7.9.97.md +0 -19
- package/tools/nexowatt-stable-v97-selftest.cjs +0 -16
|
@@ -0,0 +1,186 @@
|
|
|
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
|
+
const vm = require('node:vm');
|
|
8
|
+
|
|
9
|
+
const root = path.resolve(__dirname, '..');
|
|
10
|
+
const built = fs.readFileSync(path.join(root, 'build/lib/web.js'), 'utf8');
|
|
11
|
+
const accountUi = fs.readFileSync(path.join(root, 'adminWww/js/eos-account-management.js'), 'utf8');
|
|
12
|
+
const firstLoginUi = fs.readFileSync(path.join(root, 'adminWww/js/eos-role-bootstrap.js'), 'utf8');
|
|
13
|
+
|
|
14
|
+
function methodSlice(startMarker, endMarker) {
|
|
15
|
+
const start = built.indexOf(startMarker);
|
|
16
|
+
const end = built.indexOf(endMarker, start + startMarker.length);
|
|
17
|
+
assert(start >= 0 && end > start, `method block not found: ${startMarker}`);
|
|
18
|
+
return built.slice(start, end);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const methods = [
|
|
22
|
+
methodSlice(' normalizeEosUserId(value) {', ' normalizeEosAdapterName(value) {'),
|
|
23
|
+
methodSlice(' async readEosCurrentUser(req) {', ' getEosConfiguredRoleGroups(...keys) {'),
|
|
24
|
+
methodSlice(' async getEosRequestAccess(req) {', ' getEosRoleCapabilities(role) {'),
|
|
25
|
+
methodSlice(' validateEosFirstLoginPassword(password, passwordRepeat, userId) {', ' getEosPasswordUserName(userId) {'),
|
|
26
|
+
methodSlice(' getEosPasswordUserName(userId) {', ' async destroyEosRequestSessions(req) {'),
|
|
27
|
+
methodSlice(' async saveEosFirstLoginPassword(req, res) {', ' isEosPasswordlessFirstLoginEnabled() {'),
|
|
28
|
+
methodSlice(' async resetEosAccountPassword(req, res) {', ' async getEosHistoryInstances() {'),
|
|
29
|
+
methodSlice(' isEosSameOriginWrite(req) {', ' async saveEosBasicSettings(req, res) {'),
|
|
30
|
+
].join('\n');
|
|
31
|
+
|
|
32
|
+
const sandbox = { module: { exports: null }, URL, Set, console, EOS_PASSWORD_SERVICE_USER: 'system.user.admin' };
|
|
33
|
+
vm.runInNewContext(`module.exports = class Harness {\n${methods}\n}`, sandbox);
|
|
34
|
+
const Harness = sandbox.module.exports;
|
|
35
|
+
|
|
36
|
+
function responseMock() {
|
|
37
|
+
return {
|
|
38
|
+
statusCode: 200,
|
|
39
|
+
payload: null,
|
|
40
|
+
headers: {},
|
|
41
|
+
clearedCookies: [],
|
|
42
|
+
setHeader(name, value) { this.headers[String(name).toLowerCase()] = value; },
|
|
43
|
+
status(code) { this.statusCode = code; return this; },
|
|
44
|
+
json(value) { this.payload = value; return this; },
|
|
45
|
+
clearCookie(name) { this.clearedCookies.push(name); },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function requestMock(body = {}, headers = {}) {
|
|
50
|
+
return {
|
|
51
|
+
body,
|
|
52
|
+
headers: {
|
|
53
|
+
host: '192.168.10.124:8081',
|
|
54
|
+
origin: 'http://192.168.10.124:8081',
|
|
55
|
+
'sec-fetch-site': 'same-origin',
|
|
56
|
+
...headers,
|
|
57
|
+
},
|
|
58
|
+
query: {},
|
|
59
|
+
socket: { remoteAddress: '192.168.10.50' },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function resetScenario(requesterRole, targetRole, targetGroups) {
|
|
64
|
+
const harness = new Harness();
|
|
65
|
+
const writes = [];
|
|
66
|
+
const metadata = {};
|
|
67
|
+
harness.getEosRequestAccess = async () => ({ userId: requesterRole === 'admin' ? 'system.user.admin' : 'system.user.installer', role: requesterRole });
|
|
68
|
+
harness.normalizeEosUserId = value => String(value || '').startsWith('system.user.') ? String(value) : `system.user.${value}`;
|
|
69
|
+
harness.getEosAccessForUserId = async () => ({ role: targetRole, groups: targetGroups });
|
|
70
|
+
harness.getEosInstallerGroups = () => ['system.group.installer', 'system.group.installateur'];
|
|
71
|
+
harness.getEosEndUserGroups = () => ['system.group.endkunde', 'system.group.user'];
|
|
72
|
+
harness.adapter = {
|
|
73
|
+
setPasswordAsync: async (user, password) => writes.push([`system.user.${user}`, password]),
|
|
74
|
+
checkPasswordAsync: async (user, password) => [password === 'nexowatt', `system.user.${user}`],
|
|
75
|
+
getForeignObjectAsync: async id => ({ _id: id, common: { enabled: true, password: '$hash' }, native: { keep: true, nexowattEosAccount: {} } }),
|
|
76
|
+
extendForeignObjectAsync: async (id, patch) => {
|
|
77
|
+
metadata.id = id;
|
|
78
|
+
metadata.native = patch.native;
|
|
79
|
+
},
|
|
80
|
+
log: { warn: () => undefined, info: () => undefined },
|
|
81
|
+
};
|
|
82
|
+
harness.invalidateEosPasswordClaimsForUser = () => undefined;
|
|
83
|
+
const res = responseMock();
|
|
84
|
+
await harness.resetEosAccountPassword(
|
|
85
|
+
requestMock({ user: targetRole === 'installer' ? 'system.user.installer-target' : 'system.user.user' }, { 'x-nexowatt-eos-account-reset': '1' }),
|
|
86
|
+
res,
|
|
87
|
+
);
|
|
88
|
+
return { res, writes, metadata };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
(async () => {
|
|
92
|
+
// Authenticated request identities are normalized to the real ioBroker user object id.
|
|
93
|
+
const accessHarness = new Harness();
|
|
94
|
+
accessHarness.getEosAccessForUserId = async userId => ({ userId, role: userId === 'system.user.admin' ? 'admin' : 'enduser' });
|
|
95
|
+
const directAdmin = await accessHarness.getEosRequestAccess({ user: 'admin' });
|
|
96
|
+
assert.equal(directAdmin.userId, 'system.user.admin');
|
|
97
|
+
assert.equal(directAdmin.role, 'admin');
|
|
98
|
+
const objectAdmin = await accessHarness.getEosRequestAccess({ user: { id: 'system.user.admin' } });
|
|
99
|
+
assert.equal(objectAdmin.userId, 'system.user.admin');
|
|
100
|
+
assert.equal(objectAdmin.role, 'admin');
|
|
101
|
+
|
|
102
|
+
// Admin/Service can reset an explicitly managed End User and Installer account.
|
|
103
|
+
const adminEndUser = await resetScenario('admin', 'enduser', ['system.group.endkunde']);
|
|
104
|
+
assert.equal(adminEndUser.res.statusCode, 200);
|
|
105
|
+
assert.deepEqual(adminEndUser.writes, [['system.user.user', 'nexowatt']]);
|
|
106
|
+
assert.equal(adminEndUser.metadata.native.nexowattEosAccount.forcePasswordChange, true);
|
|
107
|
+
assert.equal(adminEndUser.metadata.native.nexowattEosAccount.passwordResetMode, 'initial-password');
|
|
108
|
+
assert.equal(adminEndUser.metadata.native.nexowattPasswordChangeRequired, true);
|
|
109
|
+
|
|
110
|
+
const adminInstaller = await resetScenario('admin', 'installer', ['system.group.installer']);
|
|
111
|
+
assert.equal(adminInstaller.res.statusCode, 200);
|
|
112
|
+
assert.deepEqual(adminInstaller.writes, [['system.user.installer-target', 'nexowatt']]);
|
|
113
|
+
|
|
114
|
+
// Installer may never reset another installer account.
|
|
115
|
+
const installerDenied = await resetScenario('installer', 'installer', ['system.group.installer']);
|
|
116
|
+
assert.equal(installerDenied.res.statusCode, 403);
|
|
117
|
+
assert.equal(installerDenied.res.payload.error, 'permissionError');
|
|
118
|
+
assert.equal(installerDenied.writes.length, 0);
|
|
119
|
+
|
|
120
|
+
// First-login password save must write the real account and clear every force-change flag.
|
|
121
|
+
const harness = new Harness();
|
|
122
|
+
const passwordWrites = [];
|
|
123
|
+
let metadata;
|
|
124
|
+
let sessionsDestroyed = false;
|
|
125
|
+
harness.getEosRequestAccess = async () => ({ userId: 'system.user.user', role: 'enduser' });
|
|
126
|
+
harness.getEosFirstLoginPasswordState = async () => ({ required: true, initialized: false, minLength: 10, version: 1 });
|
|
127
|
+
harness.getEosFirstLoginPasswordMinLength = () => 10;
|
|
128
|
+
harness.destroyEosRequestSessions = async () => { sessionsDestroyed = true; };
|
|
129
|
+
harness.adapter = {
|
|
130
|
+
setPasswordAsync: async (user, password) => passwordWrites.push([`system.user.${user}`, password]),
|
|
131
|
+
checkPasswordAsync: async (user, password) => [password === 'NexoWatt2025!', `system.user.${user}`],
|
|
132
|
+
getForeignObjectAsync: async id => ({ _id: id, common: { enabled: true, password: '$hash' }, native: { nexowattEosAccount: {} } }),
|
|
133
|
+
extendForeignObjectAsync: async (id, patch) => { metadata = { id, native: patch.native }; },
|
|
134
|
+
log: { info: () => undefined },
|
|
135
|
+
};
|
|
136
|
+
const acceptedPassword = harness.validateEosFirstLoginPassword(
|
|
137
|
+
'NexoWatt2025!',
|
|
138
|
+
'NexoWatt2025!',
|
|
139
|
+
'system.user.user',
|
|
140
|
+
);
|
|
141
|
+
assert.equal(acceptedPassword.valid, true);
|
|
142
|
+
assert.equal(acceptedPassword.password, 'NexoWatt2025!');
|
|
143
|
+
const saveRes = responseMock();
|
|
144
|
+
await harness.saveEosFirstLoginPassword(
|
|
145
|
+
requestMock(
|
|
146
|
+
{ password: 'NexoWatt2025!', passwordRepeat: 'NexoWatt2025!' },
|
|
147
|
+
{ 'x-nexowatt-eos-first-login': '1' },
|
|
148
|
+
),
|
|
149
|
+
saveRes,
|
|
150
|
+
);
|
|
151
|
+
assert.equal(saveRes.statusCode, 200);
|
|
152
|
+
assert.equal(saveRes.payload.success, true);
|
|
153
|
+
assert.equal(saveRes.payload.logoutRequired, true);
|
|
154
|
+
assert.deepEqual(passwordWrites, [['system.user.user', 'NexoWatt2025!']]);
|
|
155
|
+
assert.equal(metadata.native.nexowattEosAccount.passwordInitialized, true);
|
|
156
|
+
assert.equal(metadata.native.nexowattEosAccount.forcePasswordChange, false);
|
|
157
|
+
assert.equal(metadata.native.nexowattPasswordChangeRequired, false);
|
|
158
|
+
assert.equal(metadata.native.eosFirstLoginRequired, false);
|
|
159
|
+
assert.equal(sessionsDestroyed, true);
|
|
160
|
+
assert.deepEqual(saveRes.clearedCookies.sort(), ['access_token', 'connect.sid', 'refresh_token'].sort());
|
|
161
|
+
|
|
162
|
+
// Same-origin logic must support direct access and a reverse proxy without accepting cross-site writes.
|
|
163
|
+
const originHarness = new Harness();
|
|
164
|
+
assert.equal(originHarness.isEosSameOriginWrite(requestMock()), true);
|
|
165
|
+
assert.equal(originHarness.isEosSameOriginWrite(requestMock({}, {
|
|
166
|
+
host: '127.0.0.1:8081',
|
|
167
|
+
origin: 'https://eos.example.test',
|
|
168
|
+
'x-forwarded-host': 'eos.example.test',
|
|
169
|
+
'sec-fetch-site': 'same-origin',
|
|
170
|
+
})), true);
|
|
171
|
+
assert.equal(originHarness.isEosSameOriginWrite(requestMock({}, {
|
|
172
|
+
origin: 'https://attacker.example',
|
|
173
|
+
'sec-fetch-site': 'cross-site',
|
|
174
|
+
})), false);
|
|
175
|
+
|
|
176
|
+
// Browser writes must preserve the authenticated session cookie.
|
|
177
|
+
assert.match(accountUi, /nexowatt\/account\/reset/);
|
|
178
|
+
assert.match(accountUi, /method:\s*'POST',\s*credentials:\s*'include'/);
|
|
179
|
+
assert.match(firstLoginUi, /nexowatt\/account\/first-password/);
|
|
180
|
+
assert.match(firstLoginUi, /method:\s*'POST',\s*credentials:\s*'include'/);
|
|
181
|
+
|
|
182
|
+
console.log('[NexoWatt EOS account password flow] OK: Admin/Installer permissions, initial password reset, personal password save, session and origin checks verified.');
|
|
183
|
+
})().catch(error => {
|
|
184
|
+
console.error(error && error.stack ? error.stack : error);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
const fs=require('node:fs');const path=require('node:path');const root=path.resolve(__dirname,'..');
|
|
4
4
|
const read=rel=>fs.readFileSync(path.join(root,rel),'utf8');let bad=false;const fail=msg=>{console.error(`[NexoWatt EOS login layout] ${msg}`);bad=true;};
|
|
5
|
-
const css=read('adminWww/css/nexowatt-native-shell.css');const sourceCss=read('src-admin/public/css/nexowatt-native-shell.css');const login=read('src-admin/src/login/Login.tsx');const stableLogin=read('adminWww/static/js/nexowatt-stable-login.js');const stable=read('adminWww/static/js/nexowatt-stable-
|
|
5
|
+
const css=read('adminWww/css/nexowatt-native-shell.css');const sourceCss=read('src-admin/public/css/nexowatt-native-shell.css');const login=read('src-admin/src/login/Login.tsx');const stableLogin=read('adminWww/static/js/nexowatt-stable-login.js');const stable=read('adminWww/static/js/nexowatt-stable-v98.js');const index=read('adminWww/index.html');const info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json'));const bootstrap=read('adminWww/assets/bootstrap-COulQZax-v84.js');
|
|
6
6
|
if(css!==sourceCss)fail('login CSS source/build drift');
|
|
7
|
-
for(const marker of ['
|
|
8
|
-
if(
|
|
9
|
-
if(!
|
|
10
|
-
if(/
|
|
11
|
-
if(/width:min\(460px|
|
|
12
|
-
if(
|
|
13
|
-
if(!index.includes(`nexowatt-native-shell.css?v=${info.shellCacheTag}`)||!index.includes('nexowatt-stable-
|
|
7
|
+
for(const marker of ['NexoWatt EOS 7.9.98 – stable scrolling','html.eos-app body','overflow: hidden !important','overflow-y: auto !important','width: min(456px','width: min(400px','min-height: 0 !important','overflow: visible !important','.MuiInputLabel-root { display: none !important; }','.MuiOutlinedInput-input::placeholder','margin-top: 28px !important'])if(!css.includes(marker))fail(`login marker missing: ${marker}`);
|
|
8
|
+
if(!login.includes("minHeight: 0")||!login.includes("maxWidth: 400")||!login.includes('placeholder={`${I18n.t(\'enterLogin\')} *`}')||!login.includes('placeholder={`${I18n.t(\'enterPassword\')} *`}'))fail('native Login source does not use clean v98 placeholders');
|
|
9
|
+
if(!bootstrap.includes('placeholder:')||!bootstrap.includes('I18n.t("enterLogin")+" *"')||!bootstrap.includes('I18n.t("enterPassword")+" *"'))fail('productive login bundle does not use placeholders');
|
|
10
|
+
if(/label:.*I18n\.t\("enterLogin"\)|label:.*I18n\.t\("enterPassword"\)/.test(bootstrap.slice(1335000,1340500)))fail('productive login bundle still uses floating labels');
|
|
11
|
+
if(/data-nw-login-card|data-nw-login-shell|min-height:590px|width:min\(460px|createElement\(['"]style/.test(stableLogin))fail('stable-login runtime mutates geometry');
|
|
12
|
+
if(/width:min\(460px|min-height:540px|createElement\(['"]style/.test(stable))fail('stable v98 runtime injects geometry');
|
|
13
|
+
if(!index.includes(`nexowatt-native-shell.css?v=${info.shellCacheTag}`)||!index.includes('nexowatt-stable-v98.js?v=98'))fail('v98 login assets not active');
|
|
14
14
|
if(bad)process.exit(1);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.9.
|
|
19
|
+
'MERGE_UPDATE_README_V7.9.98.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',
|
|
@@ -19,7 +19,7 @@ for (const marker of [
|
|
|
19
19
|
'--nx-shadow-soft', '--nx-shadow-float', 'backdrop-filter: blur', '#app-paper', '.MuiDialog-paper',
|
|
20
20
|
'.MuiButton-root', '.MuiTableRow-root', ':focus-visible', 'border-radius: 24px', 'linear-gradient',
|
|
21
21
|
'@media (max-width: 720px)', '.eos-activation-steps', '.eos-account-row:hover',
|
|
22
|
-
'main.MuiPaper-root::before', 'width: min(
|
|
22
|
+
'main.MuiPaper-root::before', 'width: min(400px, calc(100vw - 48px)) !important', '.eos-assist-header-root', '.eos-overview-hero', '.eos-overview-grid',
|
|
23
23
|
'.eos-account-management-entry', 'pointer-events: auto !important', 'touch-action: manipulation', 'overflow: visible',
|
|
24
24
|
]) if (!css.includes(marker)) fail(`modern styling marker missing: ${marker}`);
|
|
25
25
|
if (sourceCss !== css) fail('modern CSS source/build drift');
|
|
@@ -29,7 +29,7 @@ for (const marker of ['eos-account-management.js','nexowatt-native-shell.css','e
|
|
|
29
29
|
for (const marker of ['NexoWatt EOS', 'Zugänge & Rollen', 'Endkunden-Zugänge', 'ensureEntrySurface']) {
|
|
30
30
|
if (!accountUi.includes(marker)) fail(`modern account surface missing label: ${marker}`);
|
|
31
31
|
}
|
|
32
|
-
for (const marker of ['installIntegratedFirstLogin','
|
|
32
|
+
for (const marker of ['installIntegratedFirstLogin','v98 compact normal-login first activation','eos-passwordless-card','Idempotent writes are essential here']) {
|
|
33
33
|
if (!bootstrap.includes(marker)) fail(`integrated first-activation surface missing: ${marker}`);
|
|
34
34
|
}
|
|
35
35
|
if (/data-eos-account=|selector\.innerHTML/.test(bootstrap)) fail('compact login still injects role buttons or a second panel');
|
|
@@ -0,0 +1,48 @@
|
|
|
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 vm = require('node:vm');
|
|
7
|
+
const root = path.resolve(__dirname, '..');
|
|
8
|
+
const built = fs.readFileSync(path.join(root, 'build/lib/web.js'), 'utf8');
|
|
9
|
+
const source = fs.readFileSync(path.join(root, 'src/lib/web.ts'), 'utf8');
|
|
10
|
+
const begin = built.indexOf(' getEosPasswordUserName(userId) {');
|
|
11
|
+
const end = built.indexOf(' async destroyEosRequestSessions', begin);
|
|
12
|
+
assert(begin >= 0 && end > begin, 'password methods not found in built backend');
|
|
13
|
+
const methods = built.slice(begin, end);
|
|
14
|
+
const sandbox = { module: { exports: null }, EOS_PASSWORD_SERVICE_USER: 'system.user.admin' };
|
|
15
|
+
vm.runInNewContext(`module.exports = class Harness {\nconstructor(adapter){this.adapter=adapter;}\n${methods}\n}`, sandbox);
|
|
16
|
+
const Harness = sandbox.module.exports;
|
|
17
|
+
(async () => {
|
|
18
|
+
const calls = [];
|
|
19
|
+
const userObject = { _id: 'system.user.user', common: { password: '$2b$hash' }, native: { keep: true } };
|
|
20
|
+
const adapter = {
|
|
21
|
+
setPasswordAsync: async (user, password, options) => calls.push(['set', user, password, options]),
|
|
22
|
+
checkPasswordAsync: async (user, password, options) => { calls.push(['check', user, password, options]); return [true, `system.user.${user}`]; },
|
|
23
|
+
getForeignObjectAsync: async id => { calls.push(['get', id]); return JSON.parse(JSON.stringify(userObject)); },
|
|
24
|
+
extendForeignObjectAsync: async (id, patch, options) => calls.push(['extend', id, patch, options]),
|
|
25
|
+
setForeignObjectAsync: async () => { throw new Error('full user object replacement must not be used'); },
|
|
26
|
+
};
|
|
27
|
+
const harness = new Harness(adapter);
|
|
28
|
+
await harness.setEosUserPassword('system.user.user', 'NexoWatt2025!');
|
|
29
|
+
assert.deepEqual(calls[0].slice(0, 3), ['set', 'user', 'NexoWatt2025!']);
|
|
30
|
+
assert.deepEqual(calls[1].slice(0, 3), ['check', 'user', 'NexoWatt2025!']);
|
|
31
|
+
await harness.updateEosAccountMetadata('system.user.user', (native, account) => {
|
|
32
|
+
account.passwordInitialized = true;
|
|
33
|
+
native.nexowattPasswordChangeRequired = false;
|
|
34
|
+
});
|
|
35
|
+
const extend = calls.find(row => row[0] === 'extend');
|
|
36
|
+
assert(extend, 'native metadata was not extended');
|
|
37
|
+
assert.equal(extend[1], 'system.user.user');
|
|
38
|
+
assert.equal(extend[2].native.keep, true);
|
|
39
|
+
assert.equal(extend[2].native.nexowattEosAccount.passwordInitialized, true);
|
|
40
|
+
assert(!Object.prototype.hasOwnProperty.call(extend[2], 'common'), 'password/common must not be overwritten by metadata update');
|
|
41
|
+
for (const code of [source, built]) {
|
|
42
|
+
assert.match(code, /setPasswordAsync\(userName, password/);
|
|
43
|
+
assert.match(code, /checkPasswordAsync\(userName, password/);
|
|
44
|
+
assert.match(code, /extendForeignObjectAsync/);
|
|
45
|
+
assert.match(code, /passwordVerificationFailed/);
|
|
46
|
+
}
|
|
47
|
+
console.log('[NexoWatt EOS password write] OK: real account name, verified password API and metadata-only update');
|
|
48
|
+
})().catch(error => { console.error(error.stack || error); process.exit(1); });
|
|
@@ -15,15 +15,15 @@ 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.writeFileSync(path.join(temp, 'NEXOWATT_EOS_BUILD_INFO.json'), JSON.stringify({ runtimeEntry: 'v82', shellCacheVersion:
|
|
18
|
+
fs.writeFileSync(path.join(temp, 'NEXOWATT_EOS_BUILD_INFO.json'), JSON.stringify({ runtimeEntry: 'v82', shellCacheVersion: 98 }));
|
|
19
19
|
|
|
20
20
|
const keep = [
|
|
21
21
|
'adminWww/assets/bootstrap-current-v82.js',
|
|
22
22
|
'adminWww/assets/Objects-current-v82.js',
|
|
23
23
|
'adminWww/remoteEntry-v82.js',
|
|
24
24
|
'adminWww/remoteEntry.js',
|
|
25
|
-
'adminWww/static/js/nexowatt-stable-
|
|
26
|
-
'src-admin/public/static/js/nexowatt-stable-
|
|
25
|
+
'adminWww/static/js/nexowatt-stable-v98.js',
|
|
26
|
+
'src-admin/public/static/js/nexowatt-stable-v98.js',
|
|
27
27
|
'adminWww/static/js/nexowatt-stable-login.js',
|
|
28
28
|
];
|
|
29
29
|
const remove = [
|
|
@@ -34,7 +34,7 @@ try {
|
|
|
34
34
|
'adminWww/js/eos-branding.js',
|
|
35
35
|
'adminWww/css/eos-branding.css',
|
|
36
36
|
'src-admin/public/js/eos-security-ui.js',
|
|
37
|
-
'adminWww/static/js/nexowatt-stable-
|
|
37
|
+
'adminWww/static/js/nexowatt-stable-v97.js',
|
|
38
38
|
'src-admin/public/static/js/nexowatt-stable-v95.js',
|
|
39
39
|
];
|
|
40
40
|
for (const rel of [...keep, ...remove]) {
|
|
@@ -45,7 +45,7 @@ try {
|
|
|
45
45
|
|
|
46
46
|
const result = cleanLegacyRuntime({ root: temp, quiet: true, silent: true });
|
|
47
47
|
assert.strictEqual(result.activeRuntime, 82);
|
|
48
|
-
assert.strictEqual(result.activeShell,
|
|
48
|
+
assert.strictEqual(result.activeShell, 98);
|
|
49
49
|
assert.strictEqual(result.removed.length, remove.length);
|
|
50
50
|
for (const rel of keep) assert.ok(fs.existsSync(path.join(temp, rel)), `current runtime removed: ${rel}`);
|
|
51
51
|
for (const rel of remove) assert.ok(!fs.existsSync(path.join(temp, rel)), `legacy file remained: ${rel}`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
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 must=(ok,msg)=>{if(!ok){console.error(`[NexoWatt EOS stable v98] ${msg}`);process.exit(1)}};
|
|
4
|
+
const pkg=JSON.parse(read('package.json')),io=JSON.parse(read('io-package.json')),info=JSON.parse(read('NEXOWATT_EOS_BUILD_INFO.json')),css=read('adminWww/css/nexowatt-native-shell.css'),stable=read('adminWww/static/js/nexowatt-stable-v98.js'),login=read('adminWww/static/js/nexowatt-stable-login.js'),index=read('adminWww/index.html'),web=read('src/lib/web.ts'),built=read('build/lib/web.js'),account=read('adminWww/js/eos-account-management.js'),bootstrap=read('adminWww/assets/bootstrap-COulQZax-v84.js'),ems=read('adminWww/js/eos-ems-overview.js');
|
|
5
|
+
must(pkg.version==='7.9.98'&&io.version==='7.9.98'&&io.common.version==='7.9.98'&&info.version==='7.9.98','version drift');
|
|
6
|
+
must(info.shellCacheVersion===98&&info.shellCacheTag==='98','shell cache not v98');
|
|
7
|
+
must(css.includes('overflow-y: auto !important')&&css.includes('scrollbar-color: rgba(0, 229, 138'),'cockpit vertical scrollbar missing');
|
|
8
|
+
must(css.includes('.MuiInputLabel-root { display: none !important; }')&&bootstrap.includes('I18n.t("enterLogin")+" *"'),'clean login field contract missing');
|
|
9
|
+
must(stable.includes("VERSION = 'v98-scroll-login-password-write'")&&!login.includes('data-nw-login-card'),'stable runtime incorrect');
|
|
10
|
+
must(index.includes('nexowatt-stable-v98.js?v=98')&&index.includes('nexowatt-native-shell.css?v=98'),'v98 assets not loaded');
|
|
11
|
+
for(const code of [web,built])for(const marker of ['getEosPasswordUserName','setPasswordAsync(userName, password','checkPasswordAsync(userName, password','passwordVerificationFailed','updateEosAccountMetadata','extendForeignObjectAsync'])must(code.includes(marker),`password marker missing: ${marker}`);
|
|
12
|
+
must(account.includes('Startpasswort „nexowatt“')&&account.includes('credentials: \'include\''),'reset UI does not describe/use stable initial password flow');
|
|
13
|
+
must(ems.includes("REQUIRED_UI_VERSION = '0.8.198'")&&ems.includes('readUiAdapterInfo'),'working EMS diagnosis was lost');
|
|
14
|
+
console.log('[NexoWatt EOS stable v98] OK');
|
|
@@ -19,6 +19,14 @@ function setValue(changes, label, object, key, value) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function setJsonValue(changes, label, object, key, value) {
|
|
23
|
+
const previous = object[key];
|
|
24
|
+
if (JSON.stringify(previous) !== JSON.stringify(value)) {
|
|
25
|
+
changes.push({ label, previous, value });
|
|
26
|
+
object[key] = value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
function syncReleaseVersion(rootDir) {
|
|
23
31
|
const root = path.resolve(rootDir);
|
|
24
32
|
const packageFile = path.join(root, 'package.json');
|
|
@@ -62,7 +70,40 @@ function syncReleaseVersion(rootDir) {
|
|
|
62
70
|
update(json) {
|
|
63
71
|
setValue(changes, 'package-lock.json version', json, 'version', version);
|
|
64
72
|
if (json.packages?.['']) {
|
|
65
|
-
|
|
73
|
+
const rootPackage = json.packages[''];
|
|
74
|
+
setValue(changes, 'package-lock.json packages[""] version', rootPackage, 'version', version);
|
|
75
|
+
// A merge archive can update package.json while leaving stale release scripts,
|
|
76
|
+
// files and policy metadata in package-lock.json. npm reads the lock root during
|
|
77
|
+
// packaging, so keep every release-critical root field canonical as well.
|
|
78
|
+
for (const key of [
|
|
79
|
+
'name',
|
|
80
|
+
'description',
|
|
81
|
+
'license',
|
|
82
|
+
'author',
|
|
83
|
+
'private',
|
|
84
|
+
'engines',
|
|
85
|
+
'main',
|
|
86
|
+
'files',
|
|
87
|
+
'scripts',
|
|
88
|
+
'dependencies',
|
|
89
|
+
'devDependencies',
|
|
90
|
+
'overrides',
|
|
91
|
+
'keywords',
|
|
92
|
+
'publishConfig',
|
|
93
|
+
'nexowattReleasePolicy',
|
|
94
|
+
'publisher',
|
|
95
|
+
'copyright',
|
|
96
|
+
]) {
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(pkg, key)) {
|
|
98
|
+
setJsonValue(
|
|
99
|
+
changes,
|
|
100
|
+
`package-lock.json packages[""] ${key}`,
|
|
101
|
+
rootPackage,
|
|
102
|
+
key,
|
|
103
|
+
pkg[key],
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
66
107
|
}
|
|
67
108
|
},
|
|
68
109
|
},
|
|
@@ -5,7 +5,7 @@ const runtime=read('adminWww/js/eos-ems-overview.js');const source=read('src-adm
|
|
|
5
5
|
if(runtime!==source)fail('runtime source/build drift');
|
|
6
6
|
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}`);
|
|
7
7
|
for(const forbidden of ['setForeignState','setState(','sendToHost','writeFile','extendObject'])if(runtime.includes(forbidden))fail(`read-only runtime contains ${forbidden}`);
|
|
8
|
-
for(const marker of ['eos-ems-overview.css?v=
|
|
8
|
+
for(const marker of ['eos-ems-overview.css?v=98','eos-ems-overview.js?v=98','nexowatt-stable-v98.js?v=98'])if(!index.includes(marker))fail(`index marker missing: ${marker}`);
|
|
9
9
|
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');
|
|
10
10
|
if(role.includes('overview.innerHTML = `')||!role.includes('never cover the real Admin Intro'))fail('obsolete role overview still active');
|
|
11
11
|
if(!shell.includes("label: 'Installateur'")||!shell.includes("label: 'Endkunde'")||!shell.includes('eosNativeReactOverview'))fail('native overview role bridge incomplete');
|
|
@@ -41,16 +41,23 @@ if (pkg.scripts['test:eos-version-sync'] !== 'node tools/nexowatt-version-sync-s
|
|
|
41
41
|
if (pkg.scripts['verify:eos-merge'] !== 'node tools/nexowatt-merge-update-selftest.cjs') fail('merge update selftest script is missing or incorrect');
|
|
42
42
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-version-sync-selftest.cjs')) fail('version sync selftest is not part of check:eos-stability');
|
|
43
43
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-merge-update-selftest.cjs')) fail('merge update selftest is not part of check:eos-stability');
|
|
44
|
-
if (pkg.scripts['check:eos-stable-
|
|
44
|
+
if (pkg.scripts['check:eos-stable-v98'] !== 'node tools/nexowatt-stable-v98-selftest.cjs') fail('stable v98 selftest script is missing or incorrect');
|
|
45
45
|
if (!pkg.scripts.prepublishOnly?.startsWith('npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run check:eos-publish-channel')) fail('prepublishOnly must synchronize versions and normalize release defaults before the publish channel guard');
|
|
46
46
|
if (pkg.scripts['precheck:eos-package'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-package must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
47
47
|
if (pkg.scripts['precheck:eos-stability'] !== 'npm run sync:eos-version && npm run prepare:eos-release-defaults && npm run clean:eos-runtime') fail('precheck:eos-stability must synchronize versions, normalize release defaults and clean stale runtime files');
|
|
48
48
|
if (pkg.scripts['test:eos-publish-channel'] !== 'node tools/nexowatt-publish-channel-selftest.cjs') fail('publish channel selftest script is missing');
|
|
49
49
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-publish-channel-selftest.cjs')) fail('stability check must execute the publish channel selftest');
|
|
50
50
|
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-release-defaults-selftest.cjs')) fail('stability check must execute the release-default selftest');
|
|
51
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-password-write-selftest.cjs')) fail('password-write selftest is not part of check:eos-stability');
|
|
52
|
+
if (!pkg.scripts['check:eos-stability']?.includes('nexowatt-account-password-flow-selftest.cjs')) fail('account-password flow selftest is not part of check:eos-stability');
|
|
51
53
|
if (io.native?.auth !== true) fail('fresh sales installations must default to authenticated access');
|
|
52
54
|
if (io.native?.eosHideLegacyBackupFromNonAdmins !== true) fail('internal BackItUp reserve must default to Admin/Service visibility');
|
|
53
55
|
if (io.native?.nexowattHideLegacyBackupFromNonAdmins !== true) fail('NexoWatt backup visibility compatibility flag must default to true');
|
|
56
|
+
for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
|
|
57
|
+
if (JSON.stringify(pkgLock.packages?.['']?.[key]) !== JSON.stringify(pkg[key])) {
|
|
58
|
+
fail(`package-lock root ${key} must match package.json`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
54
61
|
for (const [label, value] of [
|
|
55
62
|
['io-package common.version', io.common?.version],
|
|
56
63
|
['io-package top-level version', io.version],
|
|
@@ -110,14 +117,14 @@ for (const file of [
|
|
|
110
117
|
'LICENSE',
|
|
111
118
|
'NEXOWATT_PROPRIETARY_LICENSE.md',
|
|
112
119
|
'THIRD_PARTY_NOTICES.md',
|
|
113
|
-
'README_STABILITY_V7.9.
|
|
114
|
-
'RELEASE_NOTES_V7.9.
|
|
115
|
-
'PUBLISH_STABLE_V7.9.
|
|
116
|
-
'INSTALL_TEST_V7.9.
|
|
117
|
-
'RELEASE_ACCEPTANCE_V7.9.
|
|
120
|
+
'README_STABILITY_V7.9.98.md',
|
|
121
|
+
'RELEASE_NOTES_V7.9.98.md',
|
|
122
|
+
'PUBLISH_STABLE_V7.9.98.md',
|
|
123
|
+
'INSTALL_TEST_V7.9.98.md',
|
|
124
|
+
'RELEASE_ACCEPTANCE_V7.9.98.md',
|
|
118
125
|
'MERGE_UPDATE.ps1',
|
|
119
126
|
'MERGE_UPDATE.cmd',
|
|
120
|
-
'MERGE_UPDATE_README_V7.9.
|
|
127
|
+
'MERGE_UPDATE_README_V7.9.98.md',
|
|
121
128
|
'tools/nexowatt-patch-built-frontend.cjs',
|
|
122
129
|
'tools/nexowatt-native-shell-selftest.cjs',
|
|
123
130
|
'tools/nexowatt-clean-legacy-runtime.cjs',
|
|
@@ -135,12 +142,14 @@ for (const file of [
|
|
|
135
142
|
'tools/nexowatt-sync-release-version.cjs',
|
|
136
143
|
'tools/nexowatt-version-sync-selftest.cjs',
|
|
137
144
|
'tools/nexowatt-merge-update-selftest.cjs',
|
|
138
|
-
'tools/nexowatt-stable-
|
|
145
|
+
'tools/nexowatt-stable-v98-selftest.cjs',
|
|
146
|
+
'tools/nexowatt-password-write-selftest.cjs',
|
|
147
|
+
'tools/nexowatt-account-password-flow-selftest.cjs',
|
|
139
148
|
'tools/nexowatt-ems-overview-selftest.cjs',
|
|
140
149
|
'tools/nexowatt-ui-overview-runtime-selftest.cjs',
|
|
141
150
|
'adminWww/js/eos-ems-overview.js',
|
|
142
151
|
'adminWww/css/eos-ems-overview.css',
|
|
143
|
-
'adminWww/static/js/nexowatt-stable-
|
|
152
|
+
'adminWww/static/js/nexowatt-stable-v98.js',
|
|
144
153
|
'src-admin/src/components/Intro/NexoWattEmsOverview.tsx',
|
|
145
154
|
'tools/nexowatt-publish-channel-guard.cjs',
|
|
146
155
|
'tools/nexowatt-publish-channel-selftest.cjs',
|
|
@@ -183,7 +192,7 @@ if (!index.includes(`eos-account-management.js?v=${shellTag}`)) fail('account ma
|
|
|
183
192
|
if (!index.includes(`eos-assistant.js?v=${shellTag}`)) fail('EOS Assist cache key mismatch');
|
|
184
193
|
if (!index.includes(`eos-ems-overview.js?v=${shellTag}`)) fail('EMS overview cache key mismatch');
|
|
185
194
|
if (!index.includes(`eos-ems-overview.css?v=${shellTag}`)) fail('EMS overview CSS cache key mismatch');
|
|
186
|
-
if (!index.includes(`nexowatt-stable-
|
|
195
|
+
if (!index.includes(`nexowatt-stable-v98.js?v=${shellTag}`)) fail('stable v98 cache key mismatch');
|
|
187
196
|
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');
|
|
188
197
|
if (!index.includes('class="eos-native-shell"')) fail('native NexoWatt shell class missing');
|
|
189
198
|
for (const legacy of ['eos-branding.js', 'eos-security-ui.js', 'eos-console-quiet.js', 'eos-objects-state-tools.js']) {
|
|
@@ -232,7 +241,7 @@ if (accountManagement.includes("launcher.className = 'eos-account-management-lau
|
|
|
232
241
|
if (accountManagement.includes('new MutationObserver')) fail('account-management runtime adds a second broad DOM observer');
|
|
233
242
|
if (read('src-admin/public/js/eos-account-management.js') !== accountManagement) fail('account-management source/build drift');
|
|
234
243
|
const roleBootstrap = read('adminWww/js/eos-role-bootstrap.js');
|
|
235
|
-
if (!roleBootstrap.includes('installIntegratedFirstLogin') || !roleBootstrap.includes('
|
|
244
|
+
if (!roleBootstrap.includes('installIntegratedFirstLogin') || !roleBootstrap.includes('v98 compact normal-login first activation')) fail('compact integrated first-login is incomplete');
|
|
236
245
|
if (/data-eos-account=|selector\.innerHTML/.test(roleBootstrap)) fail('login role buttons must not enlarge the normal login card');
|
|
237
246
|
if (!roleBootstrap.includes('nexowatt/account/passwordless-status') || !roleBootstrap.includes('eligibility.allowed')) fail('server-checked first-login eligibility is incomplete');
|
|
238
247
|
if (roleBootstrap.includes('installPasswordlessFirstLoginLauncher')) fail('old first-login launcher remains active');
|
|
@@ -24,7 +24,19 @@ try {
|
|
|
24
24
|
const currentPkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
25
25
|
const version = currentPkg.version;
|
|
26
26
|
writeJson('package.json', currentPkg);
|
|
27
|
-
writeJson('package-lock.json', {
|
|
27
|
+
writeJson('package-lock.json', {
|
|
28
|
+
name: currentPkg.name,
|
|
29
|
+
version: '7.9.92',
|
|
30
|
+
packages: {
|
|
31
|
+
'': {
|
|
32
|
+
version: '7.9.92',
|
|
33
|
+
description: 'stale description',
|
|
34
|
+
files: ['README_STABILITY_V7.9.92.md'],
|
|
35
|
+
scripts: { 'check:eos-stable-v92': 'node stale.js' },
|
|
36
|
+
nexowattReleasePolicy: { distTag: 'latest', purpose: 'stale 7.9.92 policy' },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
28
40
|
writeJson('io-package.json', {
|
|
29
41
|
version: '7.9.92',
|
|
30
42
|
common: {
|
|
@@ -75,6 +87,11 @@ try {
|
|
|
75
87
|
]) {
|
|
76
88
|
if (actual !== version) fail(`${label} was not synchronized: ${actual}`);
|
|
77
89
|
}
|
|
90
|
+
for (const key of ['description', 'files', 'scripts', 'nexowattReleasePolicy']) {
|
|
91
|
+
if (JSON.stringify(lock.packages[''][key]) !== JSON.stringify(currentPkg[key])) {
|
|
92
|
+
fail(`package-lock root ${key} was not synchronized`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
78
95
|
if (io.common.meta !== `${expectedBase}/io-package.json`) fail('io-package URL was not synchronized');
|
|
79
96
|
if (entry.meta !== `${expectedBase}/io-package.json`) fail('repository entry URL was not synchronized');
|
|
80
97
|
if (!fs.existsSync(path.join(temp, build.repositoryEntry))) fail('current repository entry was not created');
|
package/INSTALL_TEST_V7.9.97.md
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Installationstest 7.9.97
|
|
2
|
-
|
|
3
|
-
```bash
|
|
4
|
-
cd /opt/iobroker
|
|
5
|
-
iobroker stop eos-admin.0
|
|
6
|
-
npm install --no-save --omit=dev iobroker.eos-admin@7.9.97
|
|
7
|
-
iobroker upload eos-admin
|
|
8
|
-
iobroker restart eos-admin.0
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Danach Browser mit `Strg + F5` vollständig neu laden. Für EMS-Livedaten muss `iobroker.nexowatt-ui` mindestens `0.8.198` installiert und gestartet sein.
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.9.97 – Merge-Update
|
|
2
|
-
|
|
3
|
-
Die flache Merge-ZIP direkt in den vorhandenen Repository-Ordner entpacken und anschließend `MERGE_UPDATE.cmd` ausführen.
|
|
4
|
-
|
|
5
|
-
Enthalten sind alle Änderungen aus 7.9.96 einschließlich nativer Rollenübersicht, persönlicher Erstkennwortvergabe und EMS-Diagnose sowie die Korrekturen für Login-Geometrie und produktive AdminConnection.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Stable-Veröffentlichung 7.9.97
|
|
2
|
-
|
|
3
|
-
```powershell
|
|
4
|
-
npm login
|
|
5
|
-
npm whoami
|
|
6
|
-
npm run check:eos-release
|
|
7
|
-
npm publish --dry-run --tag latest
|
|
8
|
-
npm publish --tag latest
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Das geprüfte Tarball kann alternativ direkt veröffentlicht werden:
|
|
12
|
-
|
|
13
|
-
```powershell
|
|
14
|
-
npm publish .\iobroker.eos-admin-7.9.97.tgz --tag latest
|
|
15
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# NexoWatt EOS Admin 7.9.97 – Stable
|
|
2
|
-
|
|
3
|
-
Diese Version behebt die sichtbaren Regressionen aus 7.9.96 und ist für den anschließenden Feldtest als Verkaufsfassung vorbereitet.
|
|
4
|
-
|
|
5
|
-
## Korrigiert
|
|
6
|
-
|
|
7
|
-
- Ordentliche NexoWatt-Anmeldekarte im bewährten Verhältnis, nur moderat größer.
|
|
8
|
-
- Keine innere horizontale oder vertikale Scrollleiste – auch nicht bei „Falsches Passwort“ oder der persönlichen Erstanmeldung.
|
|
9
|
-
- Keine doppelte Karten-Geometrie auf einem inneren Formular-/Grid-Container.
|
|
10
|
-
- Native Übersicht für Admin, Installateur und Endkunde.
|
|
11
|
-
- Read-only EMS-Live-Diagnose über die echte authentifizierte AdminConnection.
|
|
12
|
-
- Konkrete Hinweise bei fehlendem UI-Adapter, zu alter UI-Version, fehlendem Diagnosevertrag oder fehlender Leseberechtigung.
|
|
13
|
-
|
|
14
|
-
## Kompatibilität
|
|
15
|
-
|
|
16
|
-
Für vollständige EMS-Livedaten wird `iobroker.nexowatt-ui` ab `0.8.198` benötigt. Der Diagnosevertrag liegt unter `nexowatt-ui.*.info.adminOverview.*` und besitzt keine Schreibhoheit.
|
|
17
|
-
|
|
18
|
-
Erstkennwort `nexowatt`, verpflichtende persönliche Kennwortvergabe, Rollenmodell, geschützte EOS-Adapter, interne Notfallreserven und Standardport `8081` bleiben unverändert.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Release-Abnahme 7.9.97
|
|
2
|
-
|
|
3
|
-
- [ ] Login-Karte ist ordentlich proportioniert und nur moderat größer als vor 7.9.96.
|
|
4
|
-
- [ ] Normale Anmeldung, falsches Passwort und persönliche Erstkennwortvergabe erzeugen keine innere Scrollleiste.
|
|
5
|
-
- [ ] Erstkennwort `nexowatt` führt zur verpflichtenden persönlichen Kennwortvergabe.
|
|
6
|
-
- [ ] Native Adapterkarten sind bei Admin, Installateur und Endkunde sichtbar, soweit die Rolle sie freigibt.
|
|
7
|
-
- [ ] NexoWatt-UI-Karte ist in der Übersicht vorhanden.
|
|
8
|
-
- [ ] EMS-Kachel zeigt nach Verbindungsaufbau Livewerte aus `nexowatt-ui.*.info.adminOverview.*`.
|
|
9
|
-
- [ ] UI-Version unter `0.8.198` wird verständlich gemeldet.
|
|
10
|
-
- [ ] Die EMS-Kachel enthält keinen Schreibpfad.
|
|
11
|
-
- [ ] `npm run check:eos-release` und `npm publish --dry-run --tag latest` sind vollständig grün.
|
package/RELEASE_NOTES_V7.9.97.md
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# Release Notes 7.9.97
|
|
2
|
-
|
|
3
|
-
## Anmeldung
|
|
4
|
-
|
|
5
|
-
- Rückkehr zur bewährten kompakten Kartenstruktur.
|
|
6
|
-
- Außenrahmen und Formular sind nur ungefähr sieben Prozent größer als der frühere stabile Stand.
|
|
7
|
-
- Meldungen wachsen innerhalb der Karte, ohne Scrollleisten oder überlange Eingabefelder.
|
|
8
|
-
- Laufzeitskripte verändern keine Breite, Höhe oder Padding der Karte mehr.
|
|
9
|
-
|
|
10
|
-
## Übersicht und EMS
|
|
11
|
-
|
|
12
|
-
- Admin, Installateur und Endkunde nutzen dieselbe native Intro-Kartenkomponente.
|
|
13
|
-
- Die NexoWatt-UI-Karte bleibt für freigegebene Rollen sichtbar.
|
|
14
|
-
- Die EMS-Live-Diagnose übernimmt die produktive EOS-Admin-Verbindung und liest ausschließlich `info.adminOverview.*`.
|
|
15
|
-
- Mindestversion für den vollständigen Publisher: NexoWatt UI `0.8.198`.
|
|
16
|
-
|
|
17
|
-
## Sicherheit
|
|
18
|
-
|
|
19
|
-
Keine Änderung an Geräte-/Setpoint-Schreibwegen. Die Diagnose bleibt vollständig read-only.
|