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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "mywebui",
4
- "version": "1.37.27",
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.js",
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.27",
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.js"
62
+ "webExtension.cjs"
63
63
  ],
64
64
  "engines": {
65
65
  "node": ">=18"
@@ -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
- if (this.userName && this.connection) {
501
- this.connection.sendTo('mywebui.0', 'grafanaLangSync', { username: this.userName, lang }).catch(() => {});
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;