iobroker.mywebui 1.37.27 → 1.37.29
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/io-package.json +16 -2
- package/package.json +2 -2
- package/webExtension.cjs +106 -0
- package/www/dist/frontend/common/IobrokerHandler.js +6 -3
- package/webExtension.js +0 -35
package/io-package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "mywebui",
|
|
4
|
-
"version": "1.37.
|
|
4
|
+
"version": "1.37.29",
|
|
5
5
|
"titleLang": {
|
|
6
6
|
"en": "mywebui",
|
|
7
7
|
"de": "mywebui",
|
|
@@ -29,6 +29,20 @@
|
|
|
29
29
|
"zh-cn": "使用万维网传送器的高锰用户接口"
|
|
30
30
|
},
|
|
31
31
|
"news": {
|
|
32
|
+
"1.37.29": {
|
|
33
|
+
"en": "security fix: lang sync via session-verified HTTP endpoint instead of sendTo — username no longer trusted from frontend",
|
|
34
|
+
"az": "təhlükəsizlik düzəlişi: dil sinxronizasiyası session-yoxlamalı HTTP endpoint ilə — username artıq frontend-dən alınmır",
|
|
35
|
+
"tr": "güvenlik düzeltmesi: dil senkronizasyonu session doğrulamalı HTTP endpoint ile — username artık frontend'den gelmiyor",
|
|
36
|
+
"ru": "исправление безопасности: синхронизация языка через HTTP endpoint с проверкой сессии — username больше не берётся из frontend",
|
|
37
|
+
"de": "Sicherheitsfix: Sprachsync über session-verifizierten HTTP-Endpoint — username kommt nicht mehr vom Frontend"
|
|
38
|
+
},
|
|
39
|
+
"1.37.28": {
|
|
40
|
+
"en": "fix: rename webExtension.js to .cjs to avoid ES module conflict",
|
|
41
|
+
"az": "düzəliş: ES module konflikti aradan qaldırmaq üçün webExtension.js → .cjs",
|
|
42
|
+
"tr": "düzeltme: ES module çakışmasını önlemek için webExtension.js → .cjs",
|
|
43
|
+
"ru": "исправление: переименован webExtension.js в .cjs для совместимости с ES module",
|
|
44
|
+
"de": "fix: webExtension.js in .cjs umbenannt wegen ES-Modul-Konflikt"
|
|
45
|
+
},
|
|
32
46
|
"1.37.27": {
|
|
33
47
|
"en": "fix: add native.webInstance='*' so iobroker.web loads webExtension.js",
|
|
34
48
|
"az": "düzəliş: native.webInstance='*' əlavə edildi ki iobroker.web webExtension.js-i yükləsin",
|
|
@@ -308,7 +322,7 @@
|
|
|
308
322
|
"onlyWWW": false,
|
|
309
323
|
"nogit": false,
|
|
310
324
|
"mode": "daemon",
|
|
311
|
-
"webExtension": "webExtension.
|
|
325
|
+
"webExtension": "webExtension.cjs",
|
|
312
326
|
"welcomeScreen": {
|
|
313
327
|
"link": "mywebui/index.html",
|
|
314
328
|
"name": "mywebui editor",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.mywebui",
|
|
3
|
-
"version": "1.37.
|
|
3
|
+
"version": "1.37.29",
|
|
4
4
|
"description": "ioBroker mywebui - Custom edited mywebui by gokturk413",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/backend/main.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"README.md",
|
|
60
60
|
"SCADA_SETUP.md",
|
|
61
61
|
"setup-scada-utils.js",
|
|
62
|
-
"webExtension.
|
|
62
|
+
"webExtension.cjs"
|
|
63
63
|
],
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": ">=18"
|
package/webExtension.cjs
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
|
|
6
|
+
function mapGrafanaLanguage(lang) {
|
|
7
|
+
switch (lang) {
|
|
8
|
+
case 'az': return 'az-AZ';
|
|
9
|
+
case 'ru': return 'ru-RU';
|
|
10
|
+
case 'en': return 'en-US';
|
|
11
|
+
case 'de': return 'de-DE';
|
|
12
|
+
case 'tr': return 'tr-TR';
|
|
13
|
+
default: return 'en-US';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function httpPatch(urlStr, headers, body) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
const parsed = new URL(urlStr);
|
|
20
|
+
const lib = parsed.protocol === 'https:' ? https : http;
|
|
21
|
+
const req = lib.request(parsed, { method: 'PATCH', headers }, (res) => {
|
|
22
|
+
let data = '';
|
|
23
|
+
res.on('data', c => data += c);
|
|
24
|
+
res.on('end', () => resolve({ status: res.statusCode, body: data }));
|
|
25
|
+
});
|
|
26
|
+
req.on('error', reject);
|
|
27
|
+
req.setTimeout(3000, () => { req.destroy(); reject(new Error('timeout')); });
|
|
28
|
+
req.write(body);
|
|
29
|
+
req.end();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class MywebuiWebExtension {
|
|
34
|
+
/**
|
|
35
|
+
* @param {import('http').Server} server
|
|
36
|
+
* @param {{secure: boolean, port: number}} settings
|
|
37
|
+
* @param {object} adapter - iobroker.web adapter instance
|
|
38
|
+
* @param {object} config - system.adapter.mywebui.0 object
|
|
39
|
+
* @param {import('express').Application} app
|
|
40
|
+
*/
|
|
41
|
+
constructor(server, settings, adapter, config, app) {
|
|
42
|
+
this._adapter = adapter;
|
|
43
|
+
const native = config?.native || {};
|
|
44
|
+
|
|
45
|
+
if (!native.grafanaEnabled) {
|
|
46
|
+
adapter.log.debug('[mywebui/WebExt] Grafana disabled — no endpoints registered');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const grafanaUrl = (native.grafanaUrl || 'http://localhost:3000').replace(/\/$/, '');
|
|
51
|
+
|
|
52
|
+
// ── SSO auth check (nginx auth_request) ──────────────────────────────
|
|
53
|
+
if (native.grafanaSsoEnabled) {
|
|
54
|
+
app.get('/mywebui-auth-check', (req, res) => {
|
|
55
|
+
const username = req.session?.passport?.user;
|
|
56
|
+
if (!username) { res.status(401).end(); return; }
|
|
57
|
+
res.setHeader('X-User', username);
|
|
58
|
+
res.status(200).end();
|
|
59
|
+
});
|
|
60
|
+
adapter.log.info('[mywebui/WebExt] /mywebui-auth-check registered for nginx SSO');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Language sync (session-verified, no username from frontend) ──────
|
|
64
|
+
if (native.grafanaLangSyncEnabled) {
|
|
65
|
+
app.post('/mywebui-grafana-lang', async (req, res) => {
|
|
66
|
+
const username = req.session?.passport?.user;
|
|
67
|
+
if (!username) { res.status(401).json({ error: 'not authenticated' }); return; }
|
|
68
|
+
|
|
69
|
+
let body = '';
|
|
70
|
+
req.on('data', c => body += c);
|
|
71
|
+
await new Promise(r => req.on('end', r));
|
|
72
|
+
|
|
73
|
+
let lang;
|
|
74
|
+
try { lang = JSON.parse(body).lang; } catch (e) { /**/ }
|
|
75
|
+
if (!lang) { res.status(400).json({ error: 'missing lang' }); return; }
|
|
76
|
+
|
|
77
|
+
const grafanaLang = mapGrafanaLanguage(lang);
|
|
78
|
+
const payload = JSON.stringify({ language: grafanaLang });
|
|
79
|
+
try {
|
|
80
|
+
const r = await httpPatch(
|
|
81
|
+
grafanaUrl + '/api/user/preferences',
|
|
82
|
+
{
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
85
|
+
'X-WEBAUTH-USER': username
|
|
86
|
+
},
|
|
87
|
+
payload
|
|
88
|
+
);
|
|
89
|
+
if (r.status === 200) {
|
|
90
|
+
adapter.log.debug(`[mywebui/WebExt] lang synced: ${username} → ${grafanaLang}`);
|
|
91
|
+
res.json({ success: true });
|
|
92
|
+
} else {
|
|
93
|
+
adapter.log.warn(`[mywebui/WebExt] Grafana ${r.status}: ${r.body}`);
|
|
94
|
+
res.status(502).json({ error: `Grafana HTTP ${r.status}` });
|
|
95
|
+
}
|
|
96
|
+
} catch (e) {
|
|
97
|
+
adapter.log.warn(`[mywebui/WebExt] lang sync error: ${e.message}`);
|
|
98
|
+
res.status(500).json({ error: e.message });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
adapter.log.info('[mywebui/WebExt] /mywebui-grafana-lang registered (session-verified)');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = MywebuiWebExtension;
|
|
@@ -497,9 +497,12 @@ export class IobrokerHandler {
|
|
|
497
497
|
localStorage.setItem('webui-language', lang);
|
|
498
498
|
this.languageChanged.emit(lang);
|
|
499
499
|
window.dispatchEvent(new CustomEvent('languageChanged', { detail: lang }));
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
500
|
+
fetch('/mywebui-grafana-lang', {
|
|
501
|
+
method: 'POST',
|
|
502
|
+
headers: { 'Content-Type': 'application/json' },
|
|
503
|
+
credentials: 'include',
|
|
504
|
+
body: JSON.stringify({ lang })
|
|
505
|
+
}).catch(() => {});
|
|
503
506
|
}
|
|
504
507
|
/** Translation helper - hər yerdən istifadə üçün
|
|
505
508
|
* iobrokerHandler.t('pid.op')
|
package/webExtension.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
class MywebuiWebExtension {
|
|
4
|
-
/**
|
|
5
|
-
* @param {import('http').Server} server
|
|
6
|
-
* @param {{secure: boolean, port: number}} settings
|
|
7
|
-
* @param {object} adapter - iobroker.web adapter instance
|
|
8
|
-
* @param {object} config - system.adapter.mywebui.0 object
|
|
9
|
-
* @param {import('express').Application} app
|
|
10
|
-
*/
|
|
11
|
-
constructor(server, settings, adapter, config, app) {
|
|
12
|
-
this._adapter = adapter;
|
|
13
|
-
|
|
14
|
-
const ssoEnabled = config?.native?.grafanaEnabled && config?.native?.grafanaSsoEnabled;
|
|
15
|
-
|
|
16
|
-
if (!ssoEnabled) {
|
|
17
|
-
adapter.log.debug('[mywebui/WebExt] SSO disabled — /mywebui-auth-check not registered');
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
app.get('/mywebui-auth-check', (req, res) => {
|
|
22
|
-
const username = req.session?.passport?.user;
|
|
23
|
-
if (!username) {
|
|
24
|
-
res.status(401).end();
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
res.setHeader('X-User', username);
|
|
28
|
-
res.status(200).end();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
adapter.log.info('[mywebui/WebExt] /mywebui-auth-check registered for nginx SSO');
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = MywebuiWebExtension;
|