iobroker.script-restore 0.0.10 → 0.0.11
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/README.de.md +148 -0
- package/README.md +8 -23
- package/admin/tab_m.html +157 -3
- package/build/main.js +67 -0
- package/build/main.js.map +2 -2
- package/io-package.json +15 -15
- package/package.json +3 -3
- package/admin/words.js +0 -444
package/README.de.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# ioBroker.script-restore
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/iobroker.script-restore)
|
|
6
|
+
[](https://www.npmjs.com/package/iobroker.script-restore)
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
[](https://nodei.co/npm/iobroker.script-restore/)
|
|
10
|
+
|
|
11
|
+
**Tests:** 
|
|
12
|
+
|
|
13
|
+
## script-restore Adapter für ioBroker
|
|
14
|
+
|
|
15
|
+
Einzelne Skripte aus ioBroker-Backup-Archiven durchsuchen und wiederherstellen — ohne das gesamte Backup einspielen zu müssen.
|
|
16
|
+
|
|
17
|
+
## Beschreibung
|
|
18
|
+
|
|
19
|
+
Der script-restore Adapter fügt dem ioBroker-Admin-Interface einen Tab hinzu, über den Backup-Archive geöffnet und alle enthaltenen JavaScript-, TypeScript-, Blockly- und Rules-Skripte durchsucht werden können. Der Quellcode jedes Skripts kann einzeln angezeigt, heruntergeladen oder kopiert werden.
|
|
20
|
+
|
|
21
|
+
Das Archiv wird vollständig im Browser geparst — beim Durchsuchen werden keine Dateien auf die Festplatte geschrieben.
|
|
22
|
+
|
|
23
|
+
## Funktionen
|
|
24
|
+
|
|
25
|
+
- Backup-Archive direkt im ioBroker-Admin-Tab durchsuchen
|
|
26
|
+
- Lokale Backup-Dateien aus dem Backup-Verzeichnis laden (Standard: `/opt/iobroker/backups`)
|
|
27
|
+
- Archivdateien direkt vom Computer hochladen
|
|
28
|
+
- Unterstützte Formate: `.tar.gz`, `.tar`, `.json`, `.jsonl`
|
|
29
|
+
- Baumansicht aller Skripte nach Ordner sortiert
|
|
30
|
+
- Skripte nach Typ filtern: JS, TypeScript, Blockly, Rules
|
|
31
|
+
- Volltextsuche über Skriptnamen, Pfade und Quellcode
|
|
32
|
+
- Quellcode anzeigen (JS/TS/Blockly/Rules)
|
|
33
|
+
- Quellcode in die Zwischenablage kopieren oder als Datei herunterladen
|
|
34
|
+
- Vollständig browserbasiertes Parsen — kein Server-Roundtrip bei Uploads
|
|
35
|
+
- Mehrere Skripte mit Strg+Klick auswählen und als ZIP herunterladen
|
|
36
|
+
- Optionale Quellen: Lokal, FTP, SMB, HTTP, SFTP, WebDAV
|
|
37
|
+
- **Skripte direkt in ioBroker laden** mit konfigurierbarem Suffix (Standard: `_rcvr`) — bestehende Skripte werden nie überschrieben
|
|
38
|
+
|
|
39
|
+
## Konfiguration
|
|
40
|
+
|
|
41
|
+
| Einstellung | Beschreibung | Standard |
|
|
42
|
+
|-------------|--------------|----------|
|
|
43
|
+
| Backup-Pfad | Verzeichnis mit ioBroker-Backup-Dateien | `/opt/iobroker/backups` |
|
|
44
|
+
|
|
45
|
+
## Verwendung
|
|
46
|
+
|
|
47
|
+
### Lokale Backup-Datei laden
|
|
48
|
+
|
|
49
|
+
1. Den Tab **Script Restore** im ioBroker-Admin öffnen
|
|
50
|
+
2. Auf das Dropdown **Lokale Dateien** klicken
|
|
51
|
+
3. Eine Backup-Datei aus der Liste auswählen — die Skripte werden automatisch geladen
|
|
52
|
+
|
|
53
|
+
### Backup-Datei hochladen
|
|
54
|
+
|
|
55
|
+
1. Den Tab **Script Restore** im ioBroker-Admin öffnen
|
|
56
|
+
2. Auf **Archiv hochladen** klicken und eine Datei vom Computer auswählen
|
|
57
|
+
3. Das Archiv wird im Browser geparst und alle Skripte werden angezeigt
|
|
58
|
+
|
|
59
|
+
### Skripte ansehen und herunterladen
|
|
60
|
+
|
|
61
|
+
- Ein Skript im Baum anklicken, um den Quellcode anzuzeigen
|
|
62
|
+
- **Kopieren**-Schaltfläche nutzen, um den Quellcode in die Zwischenablage zu kopieren
|
|
63
|
+
- **Herunterladen**-Schaltfläche nutzen, um das Skript als Datei zu speichern
|
|
64
|
+
|
|
65
|
+
## Unterstützte Backup-Formate
|
|
66
|
+
|
|
67
|
+
| Format | Beschreibung |
|
|
68
|
+
|--------|--------------|
|
|
69
|
+
| `.tar.gz` | Standard-ioBroker-Backup (`iobroker_YYYY-MM-DD-HH-mm_SS_backupiobroker.tar.gz`) |
|
|
70
|
+
| `.tar` | Unkomprimiertes Tar-Archiv |
|
|
71
|
+
| `.json` | JavaScript-Adapter Skript-Export |
|
|
72
|
+
| `.jsonl` | ioBroker-Objekte-Export (JSON Lines) |
|
|
73
|
+
|
|
74
|
+
## Changelog
|
|
75
|
+
|
|
76
|
+
<!--
|
|
77
|
+
Placeholder for the next version (at the beginning of the line):
|
|
78
|
+
### **WORK IN PROGRESS**
|
|
79
|
+
-->
|
|
80
|
+
### **WORK IN PROGRESS**
|
|
81
|
+
* (ipod86) Typ-Filter (JS/TS/Blockly/Rules) in der Skript-Sidebar hinzugefügt
|
|
82
|
+
* (ipod86) Direktes Laden in ioBroker mit Suffix-Eingabe und Bestätigungs-Modal hinzugefügt
|
|
83
|
+
* (ipod86) Veraltete admin/words.js und .prettierignore entfernt
|
|
84
|
+
|
|
85
|
+
### 0.0.10 (2026-04-08)
|
|
86
|
+
* (ipod86) jsonConfig: responsive Größen lg/xl für backupPath korrigiert (E5509)
|
|
87
|
+
* (ipod86) News-Einträge auf 7 begrenzt (W1032)
|
|
88
|
+
* (ipod86) Dependabot npm cooldown von 7 Tagen hinzugefügt (W8915)
|
|
89
|
+
|
|
90
|
+
### 0.0.9 (2026-04-08)
|
|
91
|
+
* (ipod86) jsonConfig: responsive Größenattribute ergänzt (E5507)
|
|
92
|
+
* (ipod86) i18n-Übersetzungsdateien hinzugefügt (W5022)
|
|
93
|
+
* (ipod86) veraltete index_m.html und style.css entfernt (W5047)
|
|
94
|
+
* (ipod86) ungültiges copyToField-Attribut entfernt (W5512)
|
|
95
|
+
|
|
96
|
+
### 0.0.8 (2026-04-08)
|
|
97
|
+
* (ipod86) Einstellungs-UI zu jsonConfig (admin 5+) migriert — behebt S5022
|
|
98
|
+
* (ipod86) `node:fs` statt `fs` verwendet — behebt S5043
|
|
99
|
+
* (ipod86) Dependabot-Zeitplan von monatlich auf wöchentlich geändert — behebt S8906
|
|
100
|
+
* (ipod86) Automerge-Workflow in automerge-dependabot.yml umbenannt — behebt S8911
|
|
101
|
+
|
|
102
|
+
### 0.0.7 (2026-04-08)
|
|
103
|
+
* (ipod86) HTTP-URL-Laden ohne Protokoll-Präfix korrigiert (https:// wird automatisch ergänzt)
|
|
104
|
+
* (ipod86) localStorage-Speicherung des zuletzt geladenen Backups entfernt
|
|
105
|
+
|
|
106
|
+
### 0.0.6 (2026-04-08)
|
|
107
|
+
* (ipod86) HTTP, SFTP und WebDAV als optionale Backup-Quellen hinzugefügt
|
|
108
|
+
* (ipod86) Mehrfachauswahl von Skripten mit Strg+Klick und ZIP-Download
|
|
109
|
+
* (ipod86) Zuletzt geladenes Backup im Browser merken (localStorage)
|
|
110
|
+
* (ipod86) Lokalen Backup-Pfad vom backitup-Adapter automatisch erkennen
|
|
111
|
+
|
|
112
|
+
### 0.0.5 (2026-04-08)
|
|
113
|
+
* (ipod86) FTP und SMB als optionale Backup-Quellen mit Verbindungstest hinzugefügt
|
|
114
|
+
* (ipod86) Lokale Backup-Quelle optional gemacht (in Einstellungen aktivierbar)
|
|
115
|
+
* (ipod86) SMB-Versionshinweis (nur SMB2) in den Einstellungen ergänzt
|
|
116
|
+
|
|
117
|
+
### 0.0.4 (2026-04-06)
|
|
118
|
+
* (ipod86) Dunkles-Theme-Erkennung verbessert: Live-Umschaltung via MutationObserver und Storage-Events
|
|
119
|
+
|
|
120
|
+
### 0.0.3 (2026-04-06)
|
|
121
|
+
* (ipod86) Dunkles Theme für Admin-Tab-UI hinzugefügt
|
|
122
|
+
|
|
123
|
+
### 0.0.1 (2026-04-06)
|
|
124
|
+
* (ipod86) Erstveröffentlichung
|
|
125
|
+
|
|
126
|
+
## Lizenz
|
|
127
|
+
|
|
128
|
+
MIT License
|
|
129
|
+
|
|
130
|
+
Copyright (c) 2026 ipod86 <david@graef.email>
|
|
131
|
+
|
|
132
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
133
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
134
|
+
in the Software without restriction, including without limitation the rights
|
|
135
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
136
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
137
|
+
furnished to do so, subject to the following conditions:
|
|
138
|
+
|
|
139
|
+
The above copyright notice and this permission notice shall be included in all
|
|
140
|
+
copies or substantial portions of the Software.
|
|
141
|
+
|
|
142
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
143
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
144
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
145
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
146
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
147
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
148
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -20,8 +20,6 @@ The script-restore adapter adds a tab to the ioBroker admin interface that lets
|
|
|
20
20
|
|
|
21
21
|
The archive is parsed entirely in the browser — no files are written to disk during browsing.
|
|
22
22
|
|
|
23
|
-
**Note:** This adapter does not automatically restore scripts into ioBroker. It only allows you to view and download them so you can manually re-import them via the JavaScript adapter.
|
|
24
|
-
|
|
25
23
|
## Features
|
|
26
24
|
|
|
27
25
|
- Browse backup archives directly from the ioBroker admin tab
|
|
@@ -29,10 +27,12 @@ The archive is parsed entirely in the browser — no files are written to disk d
|
|
|
29
27
|
- Upload archive files directly from your computer
|
|
30
28
|
- Supported formats: `.tar.gz`, `.tar`, `.json`, `.jsonl`
|
|
31
29
|
- Tree view of all scripts organized by folder
|
|
32
|
-
-
|
|
30
|
+
- Filter scripts by type: JS, TypeScript, Blockly, Rules
|
|
31
|
+
- Full-text search across script names, paths and source code
|
|
33
32
|
- View source code (JS/TS/Blockly/Rules)
|
|
34
33
|
- Copy source code to clipboard or download as file
|
|
35
34
|
- Fully browser-based parsing — no server roundtrip for uploads
|
|
35
|
+
- **Restore scripts directly into ioBroker** with a configurable suffix (default: `_rcvr`) — existing scripts are never overwritten
|
|
36
36
|
|
|
37
37
|
## Configuration
|
|
38
38
|
|
|
@@ -75,6 +75,11 @@ The archive is parsed entirely in the browser — no files are written to disk d
|
|
|
75
75
|
Placeholder for the next version (at the beginning of the line):
|
|
76
76
|
### **WORK IN PROGRESS**
|
|
77
77
|
-->
|
|
78
|
+
### 0.0.11 (2026-04-13)
|
|
79
|
+
* (ipod86) add type filter (JS/TS/Blockly/Rules) in script sidebar
|
|
80
|
+
* (ipod86) add direct restore into ioBroker with suffix input and confirm modal
|
|
81
|
+
* (ipod86) remove obsolete admin/words.js and .prettierignore
|
|
82
|
+
|
|
78
83
|
### 0.0.10 (2026-04-08)
|
|
79
84
|
* (ipod86) fix jsonConfig responsive sizes lg/xl for backupPath (E5509)
|
|
80
85
|
* (ipod86) trim news entries to 7 (W1032)
|
|
@@ -96,26 +101,6 @@ The archive is parsed entirely in the browser — no files are written to disk d
|
|
|
96
101
|
* (ipod86) fix HTTP URL loading without protocol prefix (auto-prepend https://)
|
|
97
102
|
* (ipod86) remove localStorage persistence for last loaded backup
|
|
98
103
|
|
|
99
|
-
### 0.0.6 (2026-04-08)
|
|
100
|
-
* (ipod86) add HTTP, SFTP and WebDAV as optional backup sources
|
|
101
|
-
* (ipod86) multi-select scripts with Ctrl+click and download as ZIP
|
|
102
|
-
* (ipod86) remember last loaded backup in browser (localStorage)
|
|
103
|
-
* (ipod86) auto-detect local backup path from backitup adapter
|
|
104
|
-
|
|
105
|
-
### 0.0.5 (2026-04-08)
|
|
106
|
-
* (ipod86) add FTP and SMB as optional backup sources with connection test button
|
|
107
|
-
* (ipod86) make local backup source optional (enable/disable in settings)
|
|
108
|
-
* (ipod86) add SMB version info (SMB2 only) in settings
|
|
109
|
-
|
|
110
|
-
### 0.0.4 (2026-04-06)
|
|
111
|
-
* (ipod86) improve dark theme detection: live switching via MutationObserver and storage events
|
|
112
|
-
|
|
113
|
-
### 0.0.3 (2026-04-06)
|
|
114
|
-
* (ipod86) add dark theme support for admin tab UI
|
|
115
|
-
|
|
116
|
-
### 0.0.1 (2026-04-06)
|
|
117
|
-
* (ipod86) initial release
|
|
118
|
-
|
|
119
104
|
## License
|
|
120
105
|
MIT License
|
|
121
106
|
|
package/admin/tab_m.html
CHANGED
|
@@ -297,6 +297,66 @@
|
|
|
297
297
|
body.dark .progress-inner { background: rgba(26,26,46,0.95); }
|
|
298
298
|
body.dark #loaderText { color: #c0c0d0; }
|
|
299
299
|
|
|
300
|
+
/* Type Filter Bar */
|
|
301
|
+
.type-filter-bar {
|
|
302
|
+
display: flex; gap: 4px; padding: 5px 8px;
|
|
303
|
+
border-bottom: 1px solid var(--border); flex-shrink: 0; flex-wrap: wrap;
|
|
304
|
+
}
|
|
305
|
+
.btn-type {
|
|
306
|
+
background: transparent; border: 1px solid #ced4da; border-radius: 3px;
|
|
307
|
+
padding: 2px 8px; cursor: pointer; font-size: 0.8rem; color: #495057;
|
|
308
|
+
transition: 0.15s; font-family: var(--font-main);
|
|
309
|
+
}
|
|
310
|
+
.btn-type:hover { background: #f0f0f0; }
|
|
311
|
+
.btn-type.active { background: #e9ecef; border-color: #999; font-weight: 600; }
|
|
312
|
+
.btn-type .type-badge { margin: 0; pointer-events: none; }
|
|
313
|
+
.restore-suffix-input {
|
|
314
|
+
width: 70px; padding: 2px 6px; border: 1px solid #555;
|
|
315
|
+
background: #1e1e1e; color: #d4d4d4; border-radius: 3px; font-size: 0.85rem;
|
|
316
|
+
font-family: var(--font-main);
|
|
317
|
+
}
|
|
318
|
+
body.dark .type-filter-bar { border-bottom-color: #3a3a5c; }
|
|
319
|
+
body.dark .btn-type { color: #c0c0d0; border-color: #4a4a6a; }
|
|
320
|
+
body.dark .btn-type:hover { background: #3a3a5c; }
|
|
321
|
+
body.dark .btn-type.active { background: #3a3a5c; border-color: #7070a0; }
|
|
322
|
+
|
|
323
|
+
/* Restore Modal */
|
|
324
|
+
.restore-modal-overlay {
|
|
325
|
+
display: none; position: fixed; inset: 0;
|
|
326
|
+
background: rgba(0,0,0,0.5); z-index: 10000;
|
|
327
|
+
align-items: center; justify-content: center;
|
|
328
|
+
}
|
|
329
|
+
.restore-modal-overlay.open { display: flex; }
|
|
330
|
+
.restore-modal {
|
|
331
|
+
background: white; border-radius: 8px; padding: 24px;
|
|
332
|
+
min-width: 360px; max-width: 520px; width: 90%;
|
|
333
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.25); font-family: var(--font-main);
|
|
334
|
+
}
|
|
335
|
+
.restore-modal h3 { margin: 0 0 16px; font-size: 1.1rem; color: #111; }
|
|
336
|
+
.restore-modal-meta { background: #f8f9fa; border-radius: 4px; padding: 10px 12px; margin-bottom: 16px; font-size: 0.85rem; }
|
|
337
|
+
.restore-modal-meta div { color: #555; margin-bottom: 4px; }
|
|
338
|
+
.restore-modal-meta div:last-child { margin-bottom: 0; }
|
|
339
|
+
.restore-modal-meta strong { color: #111; }
|
|
340
|
+
.restore-modal label { display: block; font-size: 0.85rem; color: #555; margin-bottom: 6px; }
|
|
341
|
+
.restore-modal input[type=text] {
|
|
342
|
+
width: 100%; padding: 8px 10px; border: 1px solid #ced4da; border-radius: 4px;
|
|
343
|
+
font-size: 0.95rem; font-family: var(--font-main); box-sizing: border-box;
|
|
344
|
+
}
|
|
345
|
+
.restore-modal input[type=text]:focus { outline: none; border-color: var(--primary); }
|
|
346
|
+
.restore-modal-preview {
|
|
347
|
+
margin-top: 10px; font-size: 0.8rem; color: #6c757d;
|
|
348
|
+
font-family: 'Consolas', monospace; word-break: break-all;
|
|
349
|
+
}
|
|
350
|
+
.restore-modal-footer { display: flex; gap: 8px; justify-content: flex-end; margin-top: 20px; }
|
|
351
|
+
body.dark .restore-modal { background: #252537; }
|
|
352
|
+
body.dark .restore-modal h3 { color: #e0e0e0; }
|
|
353
|
+
body.dark .restore-modal-meta { background: #1a1a2e; }
|
|
354
|
+
body.dark .restore-modal-meta div { color: #aaa; }
|
|
355
|
+
body.dark .restore-modal-meta strong { color: #e0e0e0; }
|
|
356
|
+
body.dark .restore-modal label { color: #aaa; }
|
|
357
|
+
body.dark .restore-modal input[type=text] { background: #1a1a2e; border-color: #4a4a6a; color: #e0e0e0; }
|
|
358
|
+
body.dark .restore-modal-preview { color: #7a7aaa; }
|
|
359
|
+
|
|
300
360
|
/* Responsive */
|
|
301
361
|
@media (max-width: 768px) {
|
|
302
362
|
.toolbar { padding: 6px 8px; }
|
|
@@ -316,6 +376,26 @@
|
|
|
316
376
|
<div id="loaderText">Lade Backup...</div>
|
|
317
377
|
</div>
|
|
318
378
|
|
|
379
|
+
<!-- Restore Modal -->
|
|
380
|
+
<div class="restore-modal-overlay" id="restoreModalOverlay" onclick="closeRestoreModal(event)">
|
|
381
|
+
<div class="restore-modal" onclick="event.stopPropagation()">
|
|
382
|
+
<h3>In ioBroker laden</h3>
|
|
383
|
+
<div class="restore-modal-meta">
|
|
384
|
+
<div>Skript: <strong id="rmScriptName"></strong></div>
|
|
385
|
+
<div>Pfad: <strong id="rmScriptPath"></strong></div>
|
|
386
|
+
<div>Typ: <strong id="rmScriptType"></strong></div>
|
|
387
|
+
</div>
|
|
388
|
+
<label for="rmSuffix">Suffix (wird an den Skriptnamen angehängt)</label>
|
|
389
|
+
<input type="text" id="rmSuffix" value="_rcvr" oninput="updateRestorePreview()">
|
|
390
|
+
<div class="restore-modal-preview" id="rmPreview"></div>
|
|
391
|
+
<div class="restore-modal-footer">
|
|
392
|
+
<span id="rmMsg" style="flex:1;font-size:0.85rem;align-self:center;"></span>
|
|
393
|
+
<button class="btn btn-outline" onclick="closeRestoreModal()">Abbrechen</button>
|
|
394
|
+
<button class="btn btn-outline-success" id="rmConfirmBtn" onclick="confirmRestoreScript()">Laden</button>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
319
399
|
<div class="toolbar">
|
|
320
400
|
<div class="toolbar-left">
|
|
321
401
|
<label class="file-input-label">
|
|
@@ -367,6 +447,13 @@
|
|
|
367
447
|
<input type="text" id="q" placeholder="Suche in Namen, Ordner & Code...">
|
|
368
448
|
<button class="btn-icon" id="expandToggleBtn" onclick="toggleExpandAll()" title="Alle Ordner aufklappen">📂</button>
|
|
369
449
|
</div>
|
|
450
|
+
<div class="type-filter-bar">
|
|
451
|
+
<button class="btn-type active" onclick="setTypeFilter(this, '')">Alle</button>
|
|
452
|
+
<button class="btn-type" onclick="setTypeFilter(this, 'JS')"><span class="type-badge badge-JS">JS</span></button>
|
|
453
|
+
<button class="btn-type" onclick="setTypeFilter(this, 'TypeScript')"><span class="type-badge badge-TypeScript">TS</span></button>
|
|
454
|
+
<button class="btn-type" onclick="setTypeFilter(this, 'Blockly')"><span class="type-badge badge-Blockly">Blockly</span></button>
|
|
455
|
+
<button class="btn-type" onclick="setTypeFilter(this, 'Rules')"><span class="type-badge badge-Rules">Rules</span></button>
|
|
456
|
+
</div>
|
|
370
457
|
<div id="list" class="script-list"></div>
|
|
371
458
|
</div>
|
|
372
459
|
|
|
@@ -376,7 +463,8 @@
|
|
|
376
463
|
<div class="action-bar" id="actionBar">
|
|
377
464
|
<div class="action-bar-inner">
|
|
378
465
|
<div class="btn-group" id="viewSwitcher"></div>
|
|
379
|
-
<div style="display:flex;gap:
|
|
466
|
+
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
|
467
|
+
<button onclick="openRestoreModal()" class="btn btn-outline-success" id="restoreBtn">In ioBroker laden</button>
|
|
380
468
|
<button onclick="copyCode(this)" class="btn btn-outline-light">Code Kopieren</button>
|
|
381
469
|
<button onclick="downloadActive()" class="btn btn-primary" id="dlBtn">Download</button>
|
|
382
470
|
</div>
|
|
@@ -578,6 +666,7 @@
|
|
|
578
666
|
let cur = { index: -1 };
|
|
579
667
|
const openFolders = new Set();
|
|
580
668
|
let isAllExpanded = false;
|
|
669
|
+
let activeTypeFilter = '';
|
|
581
670
|
|
|
582
671
|
function escapeHTML(str) {
|
|
583
672
|
if (!str) return '';
|
|
@@ -1023,13 +1112,15 @@
|
|
|
1023
1112
|
const toggleBtn = document.getElementById('expandToggleBtn');
|
|
1024
1113
|
const tree = buildTree(scriptsData);
|
|
1025
1114
|
|
|
1026
|
-
if (q) {
|
|
1115
|
+
if (q || activeTypeFilter) {
|
|
1027
1116
|
toggleBtn.style.display = 'none';
|
|
1028
1117
|
const filter = (node, path) => {
|
|
1029
1118
|
if (!node.isDir) {
|
|
1030
|
-
|
|
1119
|
+
const typeOk = !activeTypeFilter || node.script.type === activeTypeFilter;
|
|
1120
|
+
const qOk = !q || node.script.name.toLowerCase().includes(q) ||
|
|
1031
1121
|
node.script.path.toLowerCase().includes(q) ||
|
|
1032
1122
|
(node.script.source && node.script.source.toLowerCase().includes(q));
|
|
1123
|
+
return typeOk && qOk;
|
|
1033
1124
|
}
|
|
1034
1125
|
let has = false;
|
|
1035
1126
|
for (const k in node.children) {
|
|
@@ -1144,6 +1235,69 @@
|
|
|
1144
1235
|
}
|
|
1145
1236
|
|
|
1146
1237
|
document.getElementById('q').addEventListener('keyup', renderList);
|
|
1238
|
+
|
|
1239
|
+
// === Type Filter ===
|
|
1240
|
+
function setTypeFilter(btn, type) {
|
|
1241
|
+
activeTypeFilter = type;
|
|
1242
|
+
document.querySelectorAll('.btn-type').forEach(b => b.classList.remove('active'));
|
|
1243
|
+
btn.classList.add('active');
|
|
1244
|
+
renderList();
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// === Restore Modal ===
|
|
1248
|
+
function openRestoreModal() {
|
|
1249
|
+
if (cur.index < 0) return;
|
|
1250
|
+
const s = scriptsData[cur.index];
|
|
1251
|
+
document.getElementById('rmScriptName').textContent = s.name;
|
|
1252
|
+
document.getElementById('rmScriptPath').textContent = s.path;
|
|
1253
|
+
document.getElementById('rmScriptType').textContent = s.type;
|
|
1254
|
+
document.getElementById('rmSuffix').value = '_rcvr';
|
|
1255
|
+
document.getElementById('rmMsg').textContent = '';
|
|
1256
|
+
document.getElementById('rmMsg').style.color = '';
|
|
1257
|
+
document.getElementById('rmConfirmBtn').disabled = false;
|
|
1258
|
+
updateRestorePreview();
|
|
1259
|
+
document.getElementById('restoreModalOverlay').classList.add('open');
|
|
1260
|
+
setTimeout(() => document.getElementById('rmSuffix').focus(), 50);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function closeRestoreModal(e) {
|
|
1264
|
+
if (e && e.target !== document.getElementById('restoreModalOverlay')) return;
|
|
1265
|
+
document.getElementById('restoreModalOverlay').classList.remove('open');
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
function updateRestorePreview() {
|
|
1269
|
+
if (cur.index < 0) return;
|
|
1270
|
+
const s = scriptsData[cur.index];
|
|
1271
|
+
const suffix = document.getElementById('rmSuffix').value.trim();
|
|
1272
|
+
const parts = s.path.split('.');
|
|
1273
|
+
parts[parts.length - 1] = parts[parts.length - 1] + suffix;
|
|
1274
|
+
document.getElementById('rmPreview').textContent = '→ script.js.' + parts.join('.');
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
function confirmRestoreScript() {
|
|
1278
|
+
if (cur.index < 0) return;
|
|
1279
|
+
const s = scriptsData[cur.index];
|
|
1280
|
+
const suffix = document.getElementById('rmSuffix').value.trim() || '_rcvr';
|
|
1281
|
+
const msgEl = document.getElementById('rmMsg');
|
|
1282
|
+
const confirmBtn = document.getElementById('rmConfirmBtn');
|
|
1283
|
+
msgEl.style.color = '#aaa';
|
|
1284
|
+
msgEl.textContent = '⏳ Laden...';
|
|
1285
|
+
confirmBtn.disabled = true;
|
|
1286
|
+
sendTo('restoreScript', { path: s.path, name: s.name, type: s.type, source: s.source, suffix }, function(result) {
|
|
1287
|
+
confirmBtn.disabled = false;
|
|
1288
|
+
if (result && result.error) {
|
|
1289
|
+
msgEl.style.color = '#dc3545';
|
|
1290
|
+
msgEl.textContent = '✗ ' + result.error;
|
|
1291
|
+
} else if (result && result.success) {
|
|
1292
|
+
msgEl.style.color = '#198754';
|
|
1293
|
+
msgEl.textContent = '✓ Geladen!';
|
|
1294
|
+
setTimeout(() => document.getElementById('restoreModalOverlay').classList.remove('open'), 1200);
|
|
1295
|
+
} else {
|
|
1296
|
+
msgEl.style.color = '#dc3545';
|
|
1297
|
+
msgEl.textContent = '✗ Unbekannter Fehler';
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1147
1301
|
</script>
|
|
1148
1302
|
</body>
|
|
1149
1303
|
</html>
|
package/build/main.js
CHANGED
|
@@ -122,6 +122,9 @@ class ScriptRestore extends utils.Adapter {
|
|
|
122
122
|
case "parseSmbFile":
|
|
123
123
|
await this.handleParseSmbFile(obj);
|
|
124
124
|
break;
|
|
125
|
+
case "restoreScript":
|
|
126
|
+
await this.handleRestoreScript(obj);
|
|
127
|
+
break;
|
|
125
128
|
default:
|
|
126
129
|
this.sendTo(obj.from, obj.command, { error: "Unknown command" }, obj.callback);
|
|
127
130
|
}
|
|
@@ -685,6 +688,70 @@ class ScriptRestore extends utils.Adapter {
|
|
|
685
688
|
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
686
689
|
}
|
|
687
690
|
}
|
|
691
|
+
// ─── Restore to ioBroker ─────────────────────────────────────────────────
|
|
692
|
+
async handleRestoreScript(obj) {
|
|
693
|
+
const msg = obj.message;
|
|
694
|
+
const suffix = msg.suffix || "_rcvr";
|
|
695
|
+
const parts = msg.path.split(".");
|
|
696
|
+
parts[parts.length - 1] = parts[parts.length - 1] + suffix;
|
|
697
|
+
const newScriptPath = parts.join(".");
|
|
698
|
+
const newId = `script.js.${newScriptPath}`;
|
|
699
|
+
const newName = msg.name + suffix;
|
|
700
|
+
let existing;
|
|
701
|
+
try {
|
|
702
|
+
existing = await this.getForeignObjectAsync(newId);
|
|
703
|
+
} catch {
|
|
704
|
+
existing = null;
|
|
705
|
+
}
|
|
706
|
+
if (existing) {
|
|
707
|
+
this.sendTo(obj.from, obj.command, { error: `Skript existiert bereits: ${newId}` }, obj.callback);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const engineTypeMap = {
|
|
711
|
+
TypeScript: "TypeScript/ts",
|
|
712
|
+
Blockly: "Blockly",
|
|
713
|
+
Rules: "Rules",
|
|
714
|
+
JS: "JavaScript/js"
|
|
715
|
+
};
|
|
716
|
+
const engineType = engineTypeMap[msg.type] || "JavaScript/js";
|
|
717
|
+
try {
|
|
718
|
+
await this.ensureScriptFolders(newId);
|
|
719
|
+
await this.setForeignObjectAsync(newId, {
|
|
720
|
+
type: "script",
|
|
721
|
+
common: {
|
|
722
|
+
name: newName,
|
|
723
|
+
engineType,
|
|
724
|
+
engine: "system.adapter.javascript.0",
|
|
725
|
+
source: msg.source || "",
|
|
726
|
+
enabled: false,
|
|
727
|
+
debug: false,
|
|
728
|
+
verbose: false
|
|
729
|
+
},
|
|
730
|
+
native: {}
|
|
731
|
+
});
|
|
732
|
+
this.log.info(`Script restored: ${newId}`);
|
|
733
|
+
this.sendTo(obj.from, obj.command, { success: true, id: newId }, obj.callback);
|
|
734
|
+
} catch (e) {
|
|
735
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
async ensureScriptFolders(scriptId) {
|
|
739
|
+
const parts = scriptId.split(".");
|
|
740
|
+
for (let i = 2; i < parts.length - 1; i++) {
|
|
741
|
+
const folderId = parts.slice(0, i + 1).join(".");
|
|
742
|
+
try {
|
|
743
|
+
const existing = await this.getForeignObjectAsync(folderId);
|
|
744
|
+
if (!existing) {
|
|
745
|
+
await this.setForeignObjectAsync(folderId, {
|
|
746
|
+
type: "folder",
|
|
747
|
+
common: { name: parts[i] },
|
|
748
|
+
native: {}
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
} catch {
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
688
755
|
}
|
|
689
756
|
if (require.main !== module) {
|
|
690
757
|
module.exports = (options) => new ScriptRestore(options);
|
package/build/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["/*\n * ioBroker Script Restore Adapter\n * Restore ioBroker scripts from backup archives\n * Copyright (c) 2024 ipod86 <david@graef.email>\n * MIT License\n */\n\nimport type { Dirent } from \"node:fs\";\nimport * as utils from \"@iobroker/adapter-core\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as ftp from \"basic-ftp\";\nimport { Writable } from \"node:stream\";\nimport * as https from \"node:https\";\nimport * as http from \"node:http\";\nimport SftpClient from \"ssh2-sftp-client\";\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst SMB2 = require(\"@marsaud/smb2\");\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: string;\n}\n\nclass ScriptRestore extends utils.Adapter {\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: \"script-restore\",\n\t\t});\n\t\tthis.on(\"ready\", this.onReady.bind(this));\n\t\tthis.on(\"message\", this.onMessage.bind(this));\n\t\tthis.on(\"unload\", this.onUnload.bind(this));\n\t}\n\n\tprivate onReady(): void {\n\t\tthis.log.info(`Script Restore ready. Backup path: ${this.config.backupPath || \"/opt/iobroker/backups\"}`);\n\t}\n\n\tprivate onUnload(callback: () => void): void {\n\t\tcallback();\n\t}\n\n\tprivate async onMessage(obj: ioBroker.Message): Promise<void> {\n\t\tif (!obj.callback) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tswitch (obj.command) {\n\t\t\t\tcase \"listLocalFiles\":\n\t\t\t\t\tawait this.handleListLocalFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseLocalFile\":\n\t\t\t\t\tawait this.handleParseLocalFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseUploadedFile\":\n\t\t\t\t\tawait this.handleParseUploadedFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"getSourceConfig\":\n\t\t\t\t\tthis.sendTo(\n\t\t\t\t\t\tobj.from,\n\t\t\t\t\t\tobj.command,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlocalEnabled: this.config.localEnabled !== false,\n\t\t\t\t\t\t\tftpEnabled: !!this.config.ftpEnabled,\n\t\t\t\t\t\t\tsmbEnabled: !!this.config.smbEnabled,\n\t\t\t\t\t\t\thttpEnabled: !!this.config.httpEnabled,\n\t\t\t\t\t\t\tsftpEnabled: !!this.config.sftpEnabled,\n\t\t\t\t\t\t\twebdavEnabled: !!this.config.webdavEnabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tobj.callback,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"suggestBackupPath\":\n\t\t\t\t\tawait this.handleSuggestBackupPath(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseHttpUrl\":\n\t\t\t\t\tawait this.handleParseHttpUrl(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSftp\":\n\t\t\t\t\tawait this.handleTestSftp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSftpFiles\":\n\t\t\t\t\tawait this.handleListSftpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSftpFile\":\n\t\t\t\t\tawait this.handleParseSftpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testWebdav\":\n\t\t\t\t\tawait this.handleTestWebdav(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listWebdavFiles\":\n\t\t\t\t\tawait this.handleListWebdavFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseWebdavFile\":\n\t\t\t\t\tawait this.handleParseWebdavFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testFtp\":\n\t\t\t\t\tawait this.handleTestFtp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSmb\":\n\t\t\t\t\tawait this.handleTestSmb(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listFtpFiles\":\n\t\t\t\t\tawait this.handleListFtpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseFtpFile\":\n\t\t\t\t\tawait this.handleParseFtpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSmbFiles\":\n\t\t\t\t\tawait this.handleListSmbFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSmbFile\":\n\t\t\t\t\tawait this.handleParseSmbFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.log.error(`Error handling ${obj.command}: ${(e as Error).message}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Local \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\ttry {\n\t\t\tconst rawEntries = await fs.readdir(backupPath, { withFileTypes: true, encoding: \"utf8\" });\n\t\t\tconst entries = rawEntries as unknown as Dirent[];\n\t\t\tconst files = entries\n\t\t\t\t.filter(e => {\n\t\t\t\t\tconst n = String(e.name);\n\t\t\t\t\treturn (\n\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(e => String(e.name))\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: backupPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{ error: `Verzeichnis nicht lesbar: ${(e as Error).message}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleParseLocalFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst filepath = path.join(backupPath, filename);\n\t\ttry {\n\t\t\tconst buf = await fs.readFile(filepath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseUploadedFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { name: string; data: string };\n\t\ttry {\n\t\t\tconst buf = Buffer.from(msg.data, \"base64\");\n\t\t\tconst scripts = await this.parseBuffer(buf, msg.name);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Tests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestFtp(obj: ioBroker.Message): Promise<void> {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\ttry {\n\t\t\tawait client.access({\n\t\t\t\thost: this.config.ftpHost,\n\t\t\t\tport: this.config.ftpPort || 21,\n\t\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\t\tsecure: this.config.ftpSecure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(this.config.ftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === ftp.FileType.File).length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.ftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleTestSmb(obj: ioBroker.Message): Promise<void> {\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, this.config.smbPath || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}${this.config.smbPath ? `\\\\${this.config.smbPath}` : \"\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 FTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createFtpClient(): ftp.Client {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\treturn client;\n\t}\n\n\tprivate async ftpConnect(client: ftp.Client): Promise<void> {\n\t\tawait client.access({\n\t\t\thost: this.config.ftpHost,\n\t\t\tport: this.config.ftpPort || 21,\n\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\tsecure: this.config.ftpSecure || false,\n\t\t});\n\t}\n\n\tprivate async handleListFtpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst remotePath = this.config.ftpPath || \"/\";\n\t\t\tconst list = await client.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(item => {\n\t\t\t\t\tconst n = item.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\titem.type === ftp.FileType.File &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(item => item.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleParseFtpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.ftpPath || \"/\", filename);\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst chunks: Buffer[] = [];\n\t\t\tconst writable = new Writable({\n\t\t\t\twrite(chunk, _enc, cb) {\n\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\n\t\t\t\t\tcb();\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait client.downloadTo(writable, remotePath);\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SMB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createSmbClient(): typeof SMB2 {\n\t\treturn new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t}\n\n\tprivate smbReaddir(smb: typeof SMB2, dirPath: string): Promise<string[]> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readdir(dirPath, (err: Error | null, files: string[]) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(files);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate smbReadFile(smb: typeof SMB2, filePath: string): Promise<Buffer> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readFile(filePath, (err: Error | null, data: Buffer) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async handleListSmbFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst smbPath = this.config.smbPath || \"\";\n\t\t\tconst entries = await this.smbReaddir(smb, smbPath);\n\t\t\tconst files = entries\n\t\t\t\t.filter(n => {\n\t\t\t\t\treturn (\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: smbPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\tprivate async handleParseSmbFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst smbPath = this.config.smbPath || \"\";\n\t\tconst filePath = smbPath ? `${smbPath}\\\\${filename}` : filename;\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst buf = await this.smbReadFile(smb, filePath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async parseBuffer(buf: Buffer, filename: string): Promise<ScriptEntry[]> {\n\t\tconst name = filename.toLowerCase();\n\t\tif (name.endsWith(\".tar.gz\") || name.endsWith(\".tgz\") || name.endsWith(\".tar\")) {\n\t\t\treturn this.parseTarArchive(buf, name.endsWith(\".tar\") && !name.endsWith(\".tar.gz\"));\n\t\t}\n\t\treturn this.parseJsonContent(buf.toString(\"utf8\"), filename);\n\t}\n\n\tprivate async parseTarArchive(buf: Buffer, isPlainTar: boolean): Promise<ScriptEntry[]> {\n\t\tconst tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"script-restore-\"));\n\t\tconst tmpFile = path.join(tmpDir, `archive.tar${isPlainTar ? \"\" : \".gz\"}`);\n\t\ttry {\n\t\t\tawait fs.writeFile(tmpFile, buf);\n\n\t\t\tconst extractFlag = isPlainTar ? \"-xf\" : \"-xzf\";\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" --wildcards` +\n\t\t\t\t\t\t` \"*/objects.jsonl\" \"*/objects.json\" \"*/scripts.json\" \"*/script.json\"` +\n\t\t\t\t\t\t` 2>/dev/null`,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait execAsync(`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" 2>/dev/null`).catch(() => {});\n\t\t\t}\n\n\t\t\tconst targets = [\"objects.jsonl\", \"objects.json\", \"scripts.json\", \"script.json\"];\n\t\t\tconst found = await this.findFile(tmpDir, targets);\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json, script.json)\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(found, \"utf8\");\n\t\t\treturn this.parseJsonContent(content, path.basename(found));\n\t\t} finally {\n\t\t\tawait fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async findFile(dir: string, names: string[]): Promise<string | null> {\n\t\tconst walk = async (d: string): Promise<string | null> => {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = (await fs.readdir(d, { withFileTypes: true, encoding: \"utf8\" })) as unknown as Dirent[];\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (const e of entries) {\n\t\t\t\tconst p = path.join(d, String(e.name));\n\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\tconst found = await walk(p);\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\treturn found;\n\t\t\t\t\t}\n\t\t\t\t} else if (names.includes(String(e.name))) {\n\t\t\t\t\treturn p;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\treturn walk(dir);\n\t}\n\n\tprivate parseJsonContent(content: string, filename: string): ScriptEntry[] {\n\t\tconst scripts: ScriptEntry[] = [];\n\t\tconst trimmed = content.trimStart();\n\n\t\tconst isJsonl =\n\t\t\tfilename.endsWith(\".jsonl\") ||\n\t\t\t(trimmed.startsWith(\"{\") && !trimmed.startsWith('{\\n \"') && trimmed.includes(\"\\n{\"));\n\n\t\tif (isJsonl) {\n\t\t\tfor (const line of content.split(\"\\n\")) {\n\t\t\t\tconst l = line.trim();\n\t\t\t\tif (!l) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst item = JSON.parse(l) as Record<string, unknown>;\n\t\t\t\t\tthis.processItem(\n\t\t\t\t\t\t(item._id || item.id) as string,\n\t\t\t\t\t\t(item.value || item.doc || item) as Record<string, unknown>,\n\t\t\t\t\t\tscripts,\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// skip invalid lines\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = JSON.parse(content) as Record<string, unknown>;\n\t\t\tfor (const [k, v] of Object.entries(data)) {\n\t\t\t\tthis.processItem(k, v as Record<string, unknown>, scripts);\n\t\t\t}\n\t\t}\n\n\t\treturn scripts.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n\t}\n\n\tprivate processItem(key: string, val: unknown, scripts: ScriptEntry[]): void {\n\t\tif (!key || typeof val !== \"object\" || val === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst v = val as Record<string, unknown>;\n\n\t\tif ([\"channel\", \"device\", \"folder\", \"meta\"].includes(v.type as string)) {\n\t\t\treturn;\n\t\t}\n\t\tif (v.type !== \"script\" && !key.startsWith(\"script.js.\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = v.common as Record<string, unknown> | undefined;\n\t\tif (!c || (c.engineType === undefined && c.source === undefined)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawEngineType = typeof c.engineType === \"string\" ? c.engineType : \"JS\";\n\t\tconst engineType = rawEngineType.toLowerCase();\n\t\tlet stype: string;\n\t\tif (engineType.includes(\"ts\") || engineType.includes(\"typescript\")) {\n\t\t\tstype = \"TypeScript\";\n\t\t} else if (engineType.includes(\"blockly\")) {\n\t\t\tstype = \"Blockly\";\n\t\t} else if (engineType.includes(\"rules\")) {\n\t\t\tstype = \"Rules\";\n\t\t} else {\n\t\t\tstype = \"JS\";\n\t\t}\n\n\t\tlet name: string;\n\t\tconst nameObj = c.name;\n\t\tif (typeof nameObj === \"object\" && nameObj !== null) {\n\t\t\tconst n = nameObj as Record<string, string>;\n\t\t\tname = n.de || n.en || Object.values(n)[0] || key.split(\".\").pop() || key;\n\t\t} else {\n\t\t\tname = typeof nameObj === \"string\" && nameObj ? nameObj : (key.split(\".\").pop() ?? key);\n\t\t}\n\n\t\tconst scriptPath = key.startsWith(\"script.js.\") ? key.slice(10) : key;\n\n\t\tscripts.push({\n\t\t\tname,\n\t\t\tpath: scriptPath,\n\t\t\ttype: stype,\n\t\t\tsource: typeof c.source === \"string\" ? c.source : \"\",\n\t\t});\n\t}\n\n\t// \u2500\u2500\u2500 Suggest backup path \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleSuggestBackupPath(obj: ioBroker.Message): Promise<void> {\n\t\tconst candidates = [\"/opt/iobroker/backups\", \"/root/backups\"];\n\t\t// Check if backupPC or iobroker-backup adapter is configured\n\t\ttry {\n\t\t\tconst backupObj = (await this.getForeignObjectAsync(\"system.adapter.backitup.0\")) as ioBroker.Object | null;\n\t\t\tif (backupObj?.native?.defaultFolder) {\n\t\t\t\tcandidates.unshift(backupObj.native.defaultFolder as string);\n\t\t\t}\n\t\t} catch {\n\t\t\t// adapter not installed\n\t\t}\n\t\tfor (const p of candidates) {\n\t\t\ttry {\n\t\t\t\tawait fs.access(p);\n\t\t\t\tthis.sendTo(obj.from, obj.command, p, obj.callback);\n\t\t\t\treturn;\n\t\t\t} catch {\n\t\t\t\t// not accessible\n\t\t\t}\n\t\t}\n\t\tthis.sendTo(obj.from, obj.command, \"\", obj.callback);\n\t}\n\n\t// \u2500\u2500\u2500 HTTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate downloadUrl(urlRaw: string): Promise<Buffer> {\n\t\tconst url = urlRaw.startsWith(\"http://\") || urlRaw.startsWith(\"https://\") ? urlRaw : `https://${urlRaw}`;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst mod = url.startsWith(\"https\") ? https : http;\n\t\t\tmod.get(url, res => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tres.on(\"data\", (c: Buffer) => chunks.push(c));\n\t\t\t\tres.on(\"end\", () => resolve(Buffer.concat(chunks)));\n\t\t\t\tres.on(\"error\", reject);\n\t\t\t}).on(\"error\", reject);\n\t\t});\n\t}\n\n\tprivate async handleParseHttpUrl(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.httpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"HTTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { url: string };\n\t\tconst filename = msg.url.split(\"/\").pop() || \"backup\";\n\t\ttry {\n\t\t\tconst buf = await this.downloadUrl(msg.url);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SFTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestSftp(obj: ioBroker.Message): Promise<void> {\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst list = await sftp.list(this.config.sftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === \"-\").length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.sftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleListSftpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.sftpPath || \"/\";\n\t\t\tconst list = await sftp.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(i => {\n\t\t\t\t\tconst n = i.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"-\" &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(i => i.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleParseSftpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.sftpPath || \"/\", filename);\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst buf = (await sftp.get(remotePath)) as Buffer;\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 WebDAV \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestWebdav(obj: ioBroker.Message): Promise<void> {\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst list = await client.getDirectoryContents(this.config.webdavPath || \"/\");\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: unknown[] }).data;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${arr.length} Eintr\u00E4ge in: ${this.config.webdavPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleListWebdavFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.webdavPath || \"/\";\n\t\t\tconst list = await client.getDirectoryContents(remotePath);\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: { basename: string; type: string }[] }).data;\n\t\t\tconst files = arr\n\t\t\t\t.filter((i: { basename: string; type: string }) => {\n\t\t\t\t\tconst n = i.basename;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"file\" &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map((i: { basename: string }) => i.basename)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseWebdavFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = (this.config.webdavPath ? `${this.config.webdavPath}/` : \"/\") + filename;\n\t\t\tconst buf = Buffer.from((await client.getFileContents(remotePath)) as ArrayBuffer);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n}\n\nif (require.main !== module) {\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ScriptRestore(options);\n} else {\n\t(() => new ScriptRestore())();\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/*\n * ioBroker Script Restore Adapter\n * Restore ioBroker scripts from backup archives\n * Copyright (c) 2024 ipod86 <david@graef.email>\n * MIT License\n */\n\nimport type { Dirent } from \"node:fs\";\nimport * as utils from \"@iobroker/adapter-core\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as ftp from \"basic-ftp\";\nimport { Writable } from \"node:stream\";\nimport * as https from \"node:https\";\nimport * as http from \"node:http\";\nimport SftpClient from \"ssh2-sftp-client\";\n\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst SMB2 = require(\"@marsaud/smb2\");\n\nconst execAsync = promisify(exec);\n\ninterface ScriptEntry {\n\tname: string;\n\tpath: string;\n\ttype: string;\n\tsource: string;\n}\n\nclass ScriptRestore extends utils.Adapter {\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: \"script-restore\",\n\t\t});\n\t\tthis.on(\"ready\", this.onReady.bind(this));\n\t\tthis.on(\"message\", this.onMessage.bind(this));\n\t\tthis.on(\"unload\", this.onUnload.bind(this));\n\t}\n\n\tprivate onReady(): void {\n\t\tthis.log.info(`Script Restore ready. Backup path: ${this.config.backupPath || \"/opt/iobroker/backups\"}`);\n\t}\n\n\tprivate onUnload(callback: () => void): void {\n\t\tcallback();\n\t}\n\n\tprivate async onMessage(obj: ioBroker.Message): Promise<void> {\n\t\tif (!obj.callback) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tswitch (obj.command) {\n\t\t\t\tcase \"listLocalFiles\":\n\t\t\t\t\tawait this.handleListLocalFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseLocalFile\":\n\t\t\t\t\tawait this.handleParseLocalFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseUploadedFile\":\n\t\t\t\t\tawait this.handleParseUploadedFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"getSourceConfig\":\n\t\t\t\t\tthis.sendTo(\n\t\t\t\t\t\tobj.from,\n\t\t\t\t\t\tobj.command,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlocalEnabled: this.config.localEnabled !== false,\n\t\t\t\t\t\t\tftpEnabled: !!this.config.ftpEnabled,\n\t\t\t\t\t\t\tsmbEnabled: !!this.config.smbEnabled,\n\t\t\t\t\t\t\thttpEnabled: !!this.config.httpEnabled,\n\t\t\t\t\t\t\tsftpEnabled: !!this.config.sftpEnabled,\n\t\t\t\t\t\t\twebdavEnabled: !!this.config.webdavEnabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tobj.callback,\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"suggestBackupPath\":\n\t\t\t\t\tawait this.handleSuggestBackupPath(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseHttpUrl\":\n\t\t\t\t\tawait this.handleParseHttpUrl(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSftp\":\n\t\t\t\t\tawait this.handleTestSftp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSftpFiles\":\n\t\t\t\t\tawait this.handleListSftpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSftpFile\":\n\t\t\t\t\tawait this.handleParseSftpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testWebdav\":\n\t\t\t\t\tawait this.handleTestWebdav(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listWebdavFiles\":\n\t\t\t\t\tawait this.handleListWebdavFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseWebdavFile\":\n\t\t\t\t\tawait this.handleParseWebdavFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testFtp\":\n\t\t\t\t\tawait this.handleTestFtp(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"testSmb\":\n\t\t\t\t\tawait this.handleTestSmb(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listFtpFiles\":\n\t\t\t\t\tawait this.handleListFtpFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseFtpFile\":\n\t\t\t\t\tawait this.handleParseFtpFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"listSmbFiles\":\n\t\t\t\t\tawait this.handleListSmbFiles(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"parseSmbFile\":\n\t\t\t\t\tawait this.handleParseSmbFile(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"restoreScript\":\n\t\t\t\t\tawait this.handleRestoreScript(obj);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Unknown command\" }, obj.callback);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.log.error(`Error handling ${obj.command}: ${(e as Error).message}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Local \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleListLocalFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\ttry {\n\t\t\tconst rawEntries = await fs.readdir(backupPath, { withFileTypes: true, encoding: \"utf8\" });\n\t\t\tconst entries = rawEntries as unknown as Dirent[];\n\t\t\tconst files = entries\n\t\t\t\t.filter(e => {\n\t\t\t\t\tconst n = String(e.name);\n\t\t\t\t\treturn (\n\t\t\t\t\t\te.isFile() &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(e => String(e.name))\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: backupPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{ error: `Verzeichnis nicht lesbar: ${(e as Error).message}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async handleParseLocalFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (this.config.localEnabled === false) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"Local source not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst backupPath = this.config.backupPath || \"/opt/iobroker/backups\";\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst filepath = path.join(backupPath, filename);\n\t\ttry {\n\t\t\tconst buf = await fs.readFile(filepath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseUploadedFile(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { name: string; data: string };\n\t\ttry {\n\t\t\tconst buf = Buffer.from(msg.data, \"base64\");\n\t\t\tconst scripts = await this.parseBuffer(buf, msg.name);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Tests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestFtp(obj: ioBroker.Message): Promise<void> {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\ttry {\n\t\t\tawait client.access({\n\t\t\t\thost: this.config.ftpHost,\n\t\t\t\tport: this.config.ftpPort || 21,\n\t\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\t\tsecure: this.config.ftpSecure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(this.config.ftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === ftp.FileType.File).length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.ftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleTestSmb(obj: ioBroker.Message): Promise<void> {\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, this.config.smbPath || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}${this.config.smbPath ? `\\\\${this.config.smbPath}` : \"\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 FTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createFtpClient(): ftp.Client {\n\t\tconst client = new ftp.Client();\n\t\tclient.ftp.verbose = false;\n\t\treturn client;\n\t}\n\n\tprivate async ftpConnect(client: ftp.Client): Promise<void> {\n\t\tawait client.access({\n\t\t\thost: this.config.ftpHost,\n\t\t\tport: this.config.ftpPort || 21,\n\t\t\tuser: this.config.ftpUser || \"anonymous\",\n\t\t\tpassword: this.config.ftpPassword || \"\",\n\t\t\tsecure: this.config.ftpSecure || false,\n\t\t});\n\t}\n\n\tprivate async handleListFtpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst remotePath = this.config.ftpPath || \"/\";\n\t\t\tconst list = await client.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(item => {\n\t\t\t\t\tconst n = item.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\titem.type === ftp.FileType.File &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(item => item.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\tprivate async handleParseFtpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.ftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"FTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.ftpPath || \"/\", filename);\n\t\tconst client = this.createFtpClient();\n\t\ttry {\n\t\t\tawait this.ftpConnect(client);\n\t\t\tconst chunks: Buffer[] = [];\n\t\t\tconst writable = new Writable({\n\t\t\t\twrite(chunk, _enc, cb) {\n\t\t\t\t\tchunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as string));\n\t\t\t\t\tcb();\n\t\t\t\t},\n\t\t\t});\n\t\t\tawait client.downloadTo(writable, remotePath);\n\t\t\tconst buf = Buffer.concat(chunks);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tclient.close();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SMB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate createSmbClient(): typeof SMB2 {\n\t\treturn new SMB2({\n\t\t\tshare: `\\\\\\\\${this.config.smbHost}\\\\${this.config.smbShare}`,\n\t\t\tusername: this.config.smbUser || \"\",\n\t\t\tpassword: this.config.smbPassword || \"\",\n\t\t\tdomain: this.config.smbDomain || \"\",\n\t\t});\n\t}\n\n\tprivate smbReaddir(smb: typeof SMB2, dirPath: string): Promise<string[]> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readdir(dirPath, (err: Error | null, files: string[]) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(files);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate smbReadFile(smb: typeof SMB2, filePath: string): Promise<Buffer> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tsmb.readFile(filePath, (err: Error | null, data: Buffer) => {\n\t\t\t\tif (err) {\n\t\t\t\t\treject(err);\n\t\t\t\t} else {\n\t\t\t\t\tresolve(data);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate async handleListSmbFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst smbPath = this.config.smbPath || \"\";\n\t\t\tconst entries = await this.smbReaddir(smb, smbPath);\n\t\t\tconst files = entries\n\t\t\t\t.filter(n => {\n\t\t\t\t\treturn (\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: smbPath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\tprivate async handleParseSmbFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.smbEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SMB not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst smbPath = this.config.smbPath || \"\";\n\t\tconst filePath = smbPath ? `${smbPath}\\\\${filename}` : filename;\n\t\tconst smb = this.createSmbClient();\n\t\ttry {\n\t\t\tconst buf = await this.smbReadFile(smb, filePath);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tsmb.disconnect();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async parseBuffer(buf: Buffer, filename: string): Promise<ScriptEntry[]> {\n\t\tconst name = filename.toLowerCase();\n\t\tif (name.endsWith(\".tar.gz\") || name.endsWith(\".tgz\") || name.endsWith(\".tar\")) {\n\t\t\treturn this.parseTarArchive(buf, name.endsWith(\".tar\") && !name.endsWith(\".tar.gz\"));\n\t\t}\n\t\treturn this.parseJsonContent(buf.toString(\"utf8\"), filename);\n\t}\n\n\tprivate async parseTarArchive(buf: Buffer, isPlainTar: boolean): Promise<ScriptEntry[]> {\n\t\tconst tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"script-restore-\"));\n\t\tconst tmpFile = path.join(tmpDir, `archive.tar${isPlainTar ? \"\" : \".gz\"}`);\n\t\ttry {\n\t\t\tawait fs.writeFile(tmpFile, buf);\n\n\t\t\tconst extractFlag = isPlainTar ? \"-xf\" : \"-xzf\";\n\t\t\ttry {\n\t\t\t\tawait execAsync(\n\t\t\t\t\t`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" --wildcards` +\n\t\t\t\t\t\t` \"*/objects.jsonl\" \"*/objects.json\" \"*/scripts.json\" \"*/script.json\"` +\n\t\t\t\t\t\t` 2>/dev/null`,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\tawait execAsync(`tar ${extractFlag} \"${tmpFile}\" -C \"${tmpDir}\" 2>/dev/null`).catch(() => {});\n\t\t\t}\n\n\t\t\tconst targets = [\"objects.jsonl\", \"objects.json\", \"scripts.json\", \"script.json\"];\n\t\t\tconst found = await this.findFile(tmpDir, targets);\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"Keine passende Datei im Archiv gefunden (objects.json, objects.jsonl, scripts.json, script.json)\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst content = await fs.readFile(found, \"utf8\");\n\t\t\treturn this.parseJsonContent(content, path.basename(found));\n\t\t} finally {\n\t\t\tawait fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n\t\t}\n\t}\n\n\tprivate async findFile(dir: string, names: string[]): Promise<string | null> {\n\t\tconst walk = async (d: string): Promise<string | null> => {\n\t\t\tlet entries: Dirent[];\n\t\t\ttry {\n\t\t\t\tentries = (await fs.readdir(d, { withFileTypes: true, encoding: \"utf8\" })) as unknown as Dirent[];\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (const e of entries) {\n\t\t\t\tconst p = path.join(d, String(e.name));\n\t\t\t\tif (e.isDirectory()) {\n\t\t\t\t\tconst found = await walk(p);\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\treturn found;\n\t\t\t\t\t}\n\t\t\t\t} else if (names.includes(String(e.name))) {\n\t\t\t\t\treturn p;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t};\n\t\treturn walk(dir);\n\t}\n\n\tprivate parseJsonContent(content: string, filename: string): ScriptEntry[] {\n\t\tconst scripts: ScriptEntry[] = [];\n\t\tconst trimmed = content.trimStart();\n\n\t\tconst isJsonl =\n\t\t\tfilename.endsWith(\".jsonl\") ||\n\t\t\t(trimmed.startsWith(\"{\") && !trimmed.startsWith('{\\n \"') && trimmed.includes(\"\\n{\"));\n\n\t\tif (isJsonl) {\n\t\t\tfor (const line of content.split(\"\\n\")) {\n\t\t\t\tconst l = line.trim();\n\t\t\t\tif (!l) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst item = JSON.parse(l) as Record<string, unknown>;\n\t\t\t\t\tthis.processItem(\n\t\t\t\t\t\t(item._id || item.id) as string,\n\t\t\t\t\t\t(item.value || item.doc || item) as Record<string, unknown>,\n\t\t\t\t\t\tscripts,\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// skip invalid lines\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst data = JSON.parse(content) as Record<string, unknown>;\n\t\t\tfor (const [k, v] of Object.entries(data)) {\n\t\t\t\tthis.processItem(k, v as Record<string, unknown>, scripts);\n\t\t\t}\n\t\t}\n\n\t\treturn scripts.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));\n\t}\n\n\tprivate processItem(key: string, val: unknown, scripts: ScriptEntry[]): void {\n\t\tif (!key || typeof val !== \"object\" || val === null) {\n\t\t\treturn;\n\t\t}\n\t\tconst v = val as Record<string, unknown>;\n\n\t\tif ([\"channel\", \"device\", \"folder\", \"meta\"].includes(v.type as string)) {\n\t\t\treturn;\n\t\t}\n\t\tif (v.type !== \"script\" && !key.startsWith(\"script.js.\")) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst c = v.common as Record<string, unknown> | undefined;\n\t\tif (!c || (c.engineType === undefined && c.source === undefined)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst rawEngineType = typeof c.engineType === \"string\" ? c.engineType : \"JS\";\n\t\tconst engineType = rawEngineType.toLowerCase();\n\t\tlet stype: string;\n\t\tif (engineType.includes(\"ts\") || engineType.includes(\"typescript\")) {\n\t\t\tstype = \"TypeScript\";\n\t\t} else if (engineType.includes(\"blockly\")) {\n\t\t\tstype = \"Blockly\";\n\t\t} else if (engineType.includes(\"rules\")) {\n\t\t\tstype = \"Rules\";\n\t\t} else {\n\t\t\tstype = \"JS\";\n\t\t}\n\n\t\tlet name: string;\n\t\tconst nameObj = c.name;\n\t\tif (typeof nameObj === \"object\" && nameObj !== null) {\n\t\t\tconst n = nameObj as Record<string, string>;\n\t\t\tname = n.de || n.en || Object.values(n)[0] || key.split(\".\").pop() || key;\n\t\t} else {\n\t\t\tname = typeof nameObj === \"string\" && nameObj ? nameObj : (key.split(\".\").pop() ?? key);\n\t\t}\n\n\t\tconst scriptPath = key.startsWith(\"script.js.\") ? key.slice(10) : key;\n\n\t\tscripts.push({\n\t\t\tname,\n\t\t\tpath: scriptPath,\n\t\t\ttype: stype,\n\t\t\tsource: typeof c.source === \"string\" ? c.source : \"\",\n\t\t});\n\t}\n\n\t// \u2500\u2500\u2500 Suggest backup path \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleSuggestBackupPath(obj: ioBroker.Message): Promise<void> {\n\t\tconst candidates = [\"/opt/iobroker/backups\", \"/root/backups\"];\n\t\t// Check if backupPC or iobroker-backup adapter is configured\n\t\ttry {\n\t\t\tconst backupObj = (await this.getForeignObjectAsync(\"system.adapter.backitup.0\")) as ioBroker.Object | null;\n\t\t\tif (backupObj?.native?.defaultFolder) {\n\t\t\t\tcandidates.unshift(backupObj.native.defaultFolder as string);\n\t\t\t}\n\t\t} catch {\n\t\t\t// adapter not installed\n\t\t}\n\t\tfor (const p of candidates) {\n\t\t\ttry {\n\t\t\t\tawait fs.access(p);\n\t\t\t\tthis.sendTo(obj.from, obj.command, p, obj.callback);\n\t\t\t\treturn;\n\t\t\t} catch {\n\t\t\t\t// not accessible\n\t\t\t}\n\t\t}\n\t\tthis.sendTo(obj.from, obj.command, \"\", obj.callback);\n\t}\n\n\t// \u2500\u2500\u2500 HTTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate downloadUrl(urlRaw: string): Promise<Buffer> {\n\t\tconst url = urlRaw.startsWith(\"http://\") || urlRaw.startsWith(\"https://\") ? urlRaw : `https://${urlRaw}`;\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst mod = url.startsWith(\"https\") ? https : http;\n\t\t\tmod.get(url, res => {\n\t\t\t\tif (res.statusCode !== 200) {\n\t\t\t\t\treject(new Error(`HTTP ${res.statusCode}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tres.on(\"data\", (c: Buffer) => chunks.push(c));\n\t\t\t\tres.on(\"end\", () => resolve(Buffer.concat(chunks)));\n\t\t\t\tres.on(\"error\", reject);\n\t\t\t}).on(\"error\", reject);\n\t\t});\n\t}\n\n\tprivate async handleParseHttpUrl(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.httpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"HTTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { url: string };\n\t\tconst filename = msg.url.split(\"/\").pop() || \"backup\";\n\t\ttry {\n\t\t\tconst buf = await this.downloadUrl(msg.url);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 SFTP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestSftp(obj: ioBroker.Message): Promise<void> {\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst list = await sftp.list(this.config.sftpPath || \"/\");\n\t\t\tconst count = list.filter(i => i.type === \"-\").length;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${count} Datei(en) in: ${this.config.sftpPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleListSftpFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.sftpPath || \"/\";\n\t\t\tconst list = await sftp.list(remotePath);\n\t\t\tconst files = list\n\t\t\t\t.filter(i => {\n\t\t\t\t\tconst n = i.name;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"-\" &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map(i => i.name)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\tprivate async handleParseSftpFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.sftpEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"SFTP not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\tconst remotePath = path.posix.join(this.config.sftpPath || \"/\", filename);\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({\n\t\t\t\thost: this.config.sftpHost,\n\t\t\t\tport: this.config.sftpPort || 22,\n\t\t\t\tusername: this.config.sftpUser,\n\t\t\t\tpassword: this.config.sftpPassword,\n\t\t\t});\n\t\t\tconst buf = (await sftp.get(remotePath)) as Buffer;\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t} finally {\n\t\t\tawait sftp.end();\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 WebDAV \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleTestWebdav(obj: ioBroker.Message): Promise<void> {\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst list = await client.getDirectoryContents(this.config.webdavPath || \"/\");\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: unknown[] }).data;\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t`\u2713 Verbunden! ${arr.length} Eintr\u00E4ge in: ${this.config.webdavPath || \"/\"}`,\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleListWebdavFiles(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = this.config.webdavPath || \"/\";\n\t\t\tconst list = await client.getDirectoryContents(remotePath);\n\t\t\tconst arr = Array.isArray(list) ? list : (list as { data: { basename: string; type: string }[] }).data;\n\t\t\tconst files = arr\n\t\t\t\t.filter((i: { basename: string; type: string }) => {\n\t\t\t\t\tconst n = i.basename;\n\t\t\t\t\treturn (\n\t\t\t\t\t\ti.type === \"file\" &&\n\t\t\t\t\t\t(n.startsWith(\"iobroker\") || n.startsWith(\"javascript\")) &&\n\t\t\t\t\t\t(n.endsWith(\".tar.gz\") || n.endsWith(\".tar\") || n.endsWith(\".json\") || n.endsWith(\".jsonl\"))\n\t\t\t\t\t);\n\t\t\t\t})\n\t\t\t\t.map((i: { basename: string }) => i.basename)\n\t\t\t\t.sort()\n\t\t\t\t.reverse();\n\t\t\tthis.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async handleParseWebdavFile(obj: ioBroker.Message): Promise<void> {\n\t\tif (!this.config.webdavEnabled) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: \"WebDAV not enabled\" }, obj.callback);\n\t\t\treturn;\n\t\t}\n\t\tconst msg = obj.message as { filename: string };\n\t\tconst filename = path.basename(msg.filename);\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(this.config.webdavUrl, {\n\t\t\t\tusername: this.config.webdavUser,\n\t\t\t\tpassword: this.config.webdavPassword,\n\t\t\t});\n\t\t\tconst remotePath = (this.config.webdavPath ? `${this.config.webdavPath}/` : \"/\") + filename;\n\t\t\tconst buf = Buffer.from((await client.getFileContents(remotePath)) as ArrayBuffer);\n\t\t\tconst scripts = await this.parseBuffer(buf, filename);\n\t\t\tthis.sendTo(obj.from, obj.command, { scripts }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\t// \u2500\u2500\u2500 Restore to ioBroker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\tprivate async handleRestoreScript(obj: ioBroker.Message): Promise<void> {\n\t\tconst msg = obj.message as { path: string; name: string; type: string; source: string; suffix: string };\n\t\tconst suffix = msg.suffix || \"_rcvr\";\n\n\t\t// Append suffix to the last path segment (same logic as scriptRecovery.js)\n\t\tconst parts = msg.path.split(\".\");\n\t\tparts[parts.length - 1] = parts[parts.length - 1] + suffix;\n\t\tconst newScriptPath = parts.join(\".\");\n\t\tconst newId = `script.js.${newScriptPath}`;\n\t\tconst newName = msg.name + suffix;\n\n\t\t// Do not overwrite existing scripts\n\t\tlet existing: ioBroker.Object | null | undefined;\n\t\ttry {\n\t\t\texisting = await this.getForeignObjectAsync(newId);\n\t\t} catch {\n\t\t\texisting = null;\n\t\t}\n\t\tif (existing) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: `Skript existiert bereits: ${newId}` }, obj.callback);\n\t\t\treturn;\n\t\t}\n\n\t\tconst engineTypeMap: Record<string, string> = {\n\t\t\tTypeScript: \"TypeScript/ts\",\n\t\t\tBlockly: \"Blockly\",\n\t\t\tRules: \"Rules\",\n\t\t\tJS: \"JavaScript/js\",\n\t\t};\n\t\tconst engineType = engineTypeMap[msg.type] || \"JavaScript/js\";\n\n\t\ttry {\n\t\t\tawait this.ensureScriptFolders(newId);\n\t\t\tawait this.setForeignObjectAsync(newId, {\n\t\t\t\ttype: \"script\",\n\t\t\t\tcommon: {\n\t\t\t\t\tname: newName,\n\t\t\t\t\tengineType,\n\t\t\t\t\tengine: \"system.adapter.javascript.0\",\n\t\t\t\t\tsource: msg.source || \"\",\n\t\t\t\t\tenabled: false,\n\t\t\t\t\tdebug: false,\n\t\t\t\t\tverbose: false,\n\t\t\t\t} as unknown as ioBroker.ScriptCommon,\n\t\t\t\tnative: {},\n\t\t\t});\n\t\t\tthis.log.info(`Script restored: ${newId}`);\n\t\t\tthis.sendTo(obj.from, obj.command, { success: true, id: newId }, obj.callback);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { error: (e as Error).message }, obj.callback);\n\t\t}\n\t}\n\n\tprivate async ensureScriptFolders(scriptId: string): Promise<void> {\n\t\t// scriptId = \"script.js.folderA.folderB.scriptName\"\n\t\t// Create folder objects for each intermediate path segment\n\t\tconst parts = scriptId.split(\".\");\n\t\tfor (let i = 2; i < parts.length - 1; i++) {\n\t\t\tconst folderId = parts.slice(0, i + 1).join(\".\");\n\t\t\ttry {\n\t\t\t\tconst existing = await this.getForeignObjectAsync(folderId);\n\t\t\t\tif (!existing) {\n\t\t\t\t\tawait this.setForeignObjectAsync(folderId, {\n\t\t\t\t\t\ttype: \"folder\",\n\t\t\t\t\t\tcommon: { name: parts[i] } as ioBroker.ObjectCommon,\n\t\t\t\t\t\tnative: {},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// ignore individual folder creation errors\n\t\t\t}\n\t\t}\n\t}\n}\n\nif (require.main !== module) {\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new ScriptRestore(options);\n} else {\n\t(() => new ScriptRestore())();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["SftpClient"]
|
|
7
7
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "script-restore",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.11",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.0.11": {
|
|
7
|
+
"en": "add type filter (JS/TS/Blockly/Rules) in script sidebar\nadd direct restore into ioBroker with suffix input and confirm modal\nremove obsolete admin/words.js and .prettierignore",
|
|
8
|
+
"de": "typfilter (JS/TS/Blockly/Rules) in Skript-Seitenleiste hinzufügen\ndirekte Wiederherstellung in ioBroker mit Suffix-Eingang und bestätigen\nentfernen obsolete admin/words.js und .prettierignore",
|
|
9
|
+
"ru": "добавить фильтр типа (JS/TS/Blockly/Rules) на боковой панели сценария\nдобавить прямое восстановление в ioBroker с вводом суффикса и подтвердить модаль\nудалить устаревшие admin/words.js и .prettierignore",
|
|
10
|
+
"pt": "adicionar filtro do tipo (JS/TS/Blockly/Regras) na barra lateral do script\nadicionar restauração direta no ioBroker com entrada sufixo e confirmar modal\nremover o obsoleto admin/words.js e .prettierignore",
|
|
11
|
+
"nl": "typefilter (JS/TS/Blockly/Rules) toevoegen in script zijbalk\ndirect herstellen toevoegen aan ioBroker met achtervoegsel ingang en bevestigen modal\nverouderde admin/words.js en .prettierignore verwijderen",
|
|
12
|
+
"fr": "ajouter un filtre de type (JS/TS/Blockly/Rules) dans la barre latérale du script\najouter la restauration directe dans ioBroker avec suffixe entrée et confirmer modal\nsupprimer admin/words.js obsolète et .prettierignore",
|
|
13
|
+
"it": "aggiungere il filtro di tipo (JS/TS/Blockly/Rules) nella barra laterale dello script\naggiungere il ripristino diretto in ioBroker con ingresso suffisso e confermare modal\nrimuovere obsoleti admin/words.js e .prettierignore",
|
|
14
|
+
"es": "añadir filtro tipo (JS/TS/Blockly/Rules) en barra lateral de script\nañadir la restauración directa en ioBroker con entrada de sufijo y confirmar modal\neliminar obsoletos admin/words.js y .prettierignore",
|
|
15
|
+
"pl": "dodaj filtr typu (JS / TS / Blockly / Rules) w pasku bocznym skryptu\ndodaj bezpośrednie przywracanie do joBrokera z wejściem przyrostowym i potwierdź modal\nusunąć przestarzałe admin / words.js i .prettierignore",
|
|
16
|
+
"uk": "додати фільтр типу (JS/TS/Blockly/Rules) в бічній панелі скриптів\nдодати прямий відновлення в ioBroker з входом Suffix і підтвердити модаль\nвидалити obsolete admin/words.js і .prettierignore",
|
|
17
|
+
"zh-cn": "在脚本边栏中添加类型过滤器( JS/ TS/ Blockly/ Rules)\n在 ioBroker 中添加带有后缀输入的直接还原并确认模式\n删除过时的管理员/名词.js和. pretierignore"
|
|
18
|
+
},
|
|
6
19
|
"0.0.10": {
|
|
7
20
|
"en": "Fix jsonConfig responsive sizes (E5509); trim news to 7 entries; add Dependabot cooldown",
|
|
8
21
|
"de": "jsonConfig Responsive-Größen korrigiert (E5509); News auf 7 Einträge reduziert; Dependabot-Cooldown ergänzt",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "Dodano FTP/SMB jako opcjonalne źródła kopii zapasowych z przyciskiem testowym; lokalne źródło opcjonalne",
|
|
81
94
|
"uk": "Додано FTP/SMB як опціональні джерела резервних копій з кнопкою тестування; локальне джерело опціональне",
|
|
82
95
|
"zh-cn": "添加 FTP/SMB 作为可选备份来源(含测试按钮);本地来源可选"
|
|
83
|
-
},
|
|
84
|
-
"0.0.4": {
|
|
85
|
-
"en": "Improve dark theme detection: live switching via MutationObserver and storage events",
|
|
86
|
-
"de": "Dark-Theme-Erkennung verbessert: Live-Wechsel per MutationObserver und Storage-Events",
|
|
87
|
-
"ru": "Улучшено определение тёмной темы: переключение в реальном времени через MutationObserver и storage-события",
|
|
88
|
-
"pt": "Melhoria na detecção do tema escuro: troca em tempo real via MutationObserver e eventos de armazenamento",
|
|
89
|
-
"nl": "Verbeterde detectie van donker thema: live wisseling via MutationObserver en opslag-events",
|
|
90
|
-
"fr": "Amélioration de la détection du thème sombre : changement en direct via MutationObserver et événements de stockage",
|
|
91
|
-
"it": "Migliorata la rilevazione del tema scuro: cambio in tempo reale tramite MutationObserver ed eventi di archiviazione",
|
|
92
|
-
"es": "Mejora en la detección del tema oscuro: cambio en vivo mediante MutationObserver y eventos de almacenamiento",
|
|
93
|
-
"pl": "Ulepszone wykrywanie ciemnego motywu: przełączanie na żywo przez MutationObserver i zdarzenia storage",
|
|
94
|
-
"uk": "Покращено визначення темної теми: перемикання в реальному часі через MutationObserver та події сховища",
|
|
95
|
-
"zh-cn": "改进暗色主题检测:通过 MutationObserver 和存储事件实现实时切换"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -222,4 +222,4 @@
|
|
|
222
222
|
],
|
|
223
223
|
"objects": [],
|
|
224
224
|
"instanceObjects": []
|
|
225
|
-
}
|
|
225
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.script-restore",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Restore ioBroker scripts from backup archives",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ipod86",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@iobroker/adapter-core": "^3.3.2",
|
|
29
29
|
"@marsaud/smb2": "^0.18.0",
|
|
30
|
-
"basic-ftp": "^5.2.
|
|
30
|
+
"basic-ftp": "^5.2.2",
|
|
31
31
|
"ssh2-sftp-client": "^12.1.1",
|
|
32
32
|
"webdav": "^5.9.0"
|
|
33
33
|
},
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@iobroker/testing": "^5.2.2",
|
|
42
42
|
"@tsconfig/node18": "^18.2.6",
|
|
43
43
|
"@types/iobroker": "npm:@iobroker/types@^7.1.0",
|
|
44
|
-
"@types/node": "^25.
|
|
44
|
+
"@types/node": "^25.6.0",
|
|
45
45
|
"@types/ssh2-sftp-client": "^9.0.6",
|
|
46
46
|
"rimraf": "^6.1.3",
|
|
47
47
|
"source-map-support": "^0.5.21",
|
package/admin/words.js
DELETED
|
@@ -1,444 +0,0 @@
|
|
|
1
|
-
/* eslint no-unused-vars: off */
|
|
2
|
-
/* eslint no-global-assign: off */
|
|
3
|
-
/* global systemDictionary */
|
|
4
|
-
"use strict";
|
|
5
|
-
|
|
6
|
-
systemDictionary = {
|
|
7
|
-
"script-restore adapter settings": {
|
|
8
|
-
en: "Adapter settings for script-restore",
|
|
9
|
-
de: "Adaptereinstellungen für script-restore",
|
|
10
|
-
ru: "Настройки адаптера script-restore",
|
|
11
|
-
pt: "Configurações do adaptador script-restore",
|
|
12
|
-
nl: "Adapterinstellingen voor script-restore",
|
|
13
|
-
fr: "Paramètres de l'adaptateur script-restore",
|
|
14
|
-
it: "Impostazioni adattatore script-restore",
|
|
15
|
-
es: "Configuración del adaptador script-restore",
|
|
16
|
-
pl: "Ustawienia adaptera script-restore",
|
|
17
|
-
uk: "Налаштування адаптера script-restore",
|
|
18
|
-
"zh-cn": "script-restore 适配器设置",
|
|
19
|
-
},
|
|
20
|
-
"backupPath": {
|
|
21
|
-
en: "Backup path",
|
|
22
|
-
de: "Backup-Pfad",
|
|
23
|
-
ru: "Путь к резервным копиям",
|
|
24
|
-
pt: "Caminho de backup",
|
|
25
|
-
nl: "Back-uppad",
|
|
26
|
-
fr: "Chemin de sauvegarde",
|
|
27
|
-
it: "Percorso di backup",
|
|
28
|
-
es: "Ruta de copia de seguridad",
|
|
29
|
-
pl: "Ścieżka kopii zapasowej",
|
|
30
|
-
uk: "Шлях до резервних копій",
|
|
31
|
-
"zh-cn": "备份路径",
|
|
32
|
-
},
|
|
33
|
-
"backupPathHint": {
|
|
34
|
-
en: "Directory where ioBroker backup files are stored",
|
|
35
|
-
de: "Verzeichnis, in dem ioBroker-Backup-Dateien gespeichert sind",
|
|
36
|
-
ru: "Каталог, в котором хранятся резервные файлы ioBroker",
|
|
37
|
-
pt: "Diretório onde os arquivos de backup do ioBroker são armazenados",
|
|
38
|
-
nl: "Map waar ioBroker-back-upbestanden worden opgeslagen",
|
|
39
|
-
fr: "Répertoire où sont stockés les fichiers de sauvegarde ioBroker",
|
|
40
|
-
it: "Directory in cui vengono archiviati i file di backup di ioBroker",
|
|
41
|
-
es: "Directorio donde se almacenan los archivos de copia de seguridad de ioBroker",
|
|
42
|
-
pl: "Katalog, w którym przechowywane są pliki kopii zapasowych ioBroker",
|
|
43
|
-
uk: "Каталог, де зберігаються файли резервних копій ioBroker",
|
|
44
|
-
"zh-cn": "存储 ioBroker 备份文件的目录",
|
|
45
|
-
},
|
|
46
|
-
"localEnabled": {
|
|
47
|
-
en: "Enable local source",
|
|
48
|
-
de: "Lokale Quelle aktivieren",
|
|
49
|
-
ru: "Включить локальный источник",
|
|
50
|
-
pt: "Ativar fonte local",
|
|
51
|
-
nl: "Lokale bron inschakelen",
|
|
52
|
-
fr: "Activer la source locale",
|
|
53
|
-
it: "Abilita sorgente locale",
|
|
54
|
-
es: "Activar fuente local",
|
|
55
|
-
pl: "Włącz źródło lokalne",
|
|
56
|
-
uk: "Увімкнути локальне джерело",
|
|
57
|
-
"zh-cn": "启用本地来源",
|
|
58
|
-
},
|
|
59
|
-
"localSection": {
|
|
60
|
-
en: "Local backup path",
|
|
61
|
-
de: "Lokaler Backup-Pfad",
|
|
62
|
-
ru: "Локальный путь к резервным копиям",
|
|
63
|
-
pt: "Caminho de backup local",
|
|
64
|
-
nl: "Lokaal back-uppad",
|
|
65
|
-
fr: "Chemin de sauvegarde local",
|
|
66
|
-
it: "Percorso di backup locale",
|
|
67
|
-
es: "Ruta de copia de seguridad local",
|
|
68
|
-
pl: "Lokalna ścieżka kopii zapasowej",
|
|
69
|
-
uk: "Локальний шлях до резервних копій",
|
|
70
|
-
"zh-cn": "本地备份路径",
|
|
71
|
-
},
|
|
72
|
-
"testConnection": {
|
|
73
|
-
en: "Test connection",
|
|
74
|
-
de: "Verbindung testen",
|
|
75
|
-
ru: "Проверить подключение",
|
|
76
|
-
pt: "Testar ligação",
|
|
77
|
-
nl: "Verbinding testen",
|
|
78
|
-
fr: "Tester la connexion",
|
|
79
|
-
it: "Testa connessione",
|
|
80
|
-
es: "Probar conexión",
|
|
81
|
-
pl: "Testuj połączenie",
|
|
82
|
-
uk: "Перевірити з'єднання",
|
|
83
|
-
"zh-cn": "测试连接",
|
|
84
|
-
},
|
|
85
|
-
"smbVersionNote": {
|
|
86
|
-
en: "Note: only SMB2 is supported (not SMB1 or SMB3). Enable SMB2 on your NAS/server if needed.",
|
|
87
|
-
de: "Hinweis: Nur SMB2 wird unterstützt (nicht SMB1 oder SMB3). SMB2 am NAS/Server ggf. aktivieren.",
|
|
88
|
-
ru: "Примечание: поддерживается только SMB2 (не SMB1 и не SMB3).",
|
|
89
|
-
pt: "Nota: apenas SMB2 é suportado (não SMB1 nem SMB3).",
|
|
90
|
-
nl: "Let op: alleen SMB2 wordt ondersteund (niet SMB1 of SMB3).",
|
|
91
|
-
fr: "Remarque : seul SMB2 est pris en charge (pas SMB1 ni SMB3).",
|
|
92
|
-
it: "Nota: è supportato solo SMB2 (non SMB1 o SMB3).",
|
|
93
|
-
es: "Nota: solo se admite SMB2 (no SMB1 ni SMB3).",
|
|
94
|
-
pl: "Uwaga: obsługiwany jest tylko SMB2 (nie SMB1 ani SMB3).",
|
|
95
|
-
uk: "Примітка: підтримується лише SMB2 (не SMB1 і не SMB3).",
|
|
96
|
-
"zh-cn": "注意:仅支持 SMB2(不支持 SMB1 或 SMB3)。",
|
|
97
|
-
},
|
|
98
|
-
"ftpSection": {
|
|
99
|
-
en: "FTP / FTPS",
|
|
100
|
-
de: "FTP / FTPS",
|
|
101
|
-
ru: "FTP / FTPS",
|
|
102
|
-
pt: "FTP / FTPS",
|
|
103
|
-
nl: "FTP / FTPS",
|
|
104
|
-
fr: "FTP / FTPS",
|
|
105
|
-
it: "FTP / FTPS",
|
|
106
|
-
es: "FTP / FTPS",
|
|
107
|
-
pl: "FTP / FTPS",
|
|
108
|
-
uk: "FTP / FTPS",
|
|
109
|
-
"zh-cn": "FTP / FTPS",
|
|
110
|
-
},
|
|
111
|
-
"ftpEnabled": {
|
|
112
|
-
en: "Enable FTP source",
|
|
113
|
-
de: "FTP-Quelle aktivieren",
|
|
114
|
-
ru: "Включить источник FTP",
|
|
115
|
-
pt: "Ativar fonte FTP",
|
|
116
|
-
nl: "FTP-bron inschakelen",
|
|
117
|
-
fr: "Activer la source FTP",
|
|
118
|
-
it: "Abilita sorgente FTP",
|
|
119
|
-
es: "Activar fuente FTP",
|
|
120
|
-
pl: "Włącz źródło FTP",
|
|
121
|
-
uk: "Увімкнути джерело FTP",
|
|
122
|
-
"zh-cn": "启用 FTP 来源",
|
|
123
|
-
},
|
|
124
|
-
"ftpHost": {
|
|
125
|
-
en: "Host",
|
|
126
|
-
de: "Host",
|
|
127
|
-
ru: "Хост",
|
|
128
|
-
pt: "Host",
|
|
129
|
-
nl: "Host",
|
|
130
|
-
fr: "Hôte",
|
|
131
|
-
it: "Host",
|
|
132
|
-
es: "Host",
|
|
133
|
-
pl: "Host",
|
|
134
|
-
uk: "Хост",
|
|
135
|
-
"zh-cn": "主机",
|
|
136
|
-
},
|
|
137
|
-
"ftpPort": {
|
|
138
|
-
en: "Port",
|
|
139
|
-
de: "Port",
|
|
140
|
-
ru: "Порт",
|
|
141
|
-
pt: "Porta",
|
|
142
|
-
nl: "Poort",
|
|
143
|
-
fr: "Port",
|
|
144
|
-
it: "Porta",
|
|
145
|
-
es: "Puerto",
|
|
146
|
-
pl: "Port",
|
|
147
|
-
uk: "Порт",
|
|
148
|
-
"zh-cn": "端口",
|
|
149
|
-
},
|
|
150
|
-
"ftpUser": {
|
|
151
|
-
en: "Username",
|
|
152
|
-
de: "Benutzername",
|
|
153
|
-
ru: "Имя пользователя",
|
|
154
|
-
pt: "Utilizador",
|
|
155
|
-
nl: "Gebruikersnaam",
|
|
156
|
-
fr: "Nom d'utilisateur",
|
|
157
|
-
it: "Nome utente",
|
|
158
|
-
es: "Usuario",
|
|
159
|
-
pl: "Nazwa użytkownika",
|
|
160
|
-
uk: "Ім'я користувача",
|
|
161
|
-
"zh-cn": "用户名",
|
|
162
|
-
},
|
|
163
|
-
"ftpPassword": {
|
|
164
|
-
en: "Password",
|
|
165
|
-
de: "Passwort",
|
|
166
|
-
ru: "Пароль",
|
|
167
|
-
pt: "Senha",
|
|
168
|
-
nl: "Wachtwoord",
|
|
169
|
-
fr: "Mot de passe",
|
|
170
|
-
it: "Password",
|
|
171
|
-
es: "Contraseña",
|
|
172
|
-
pl: "Hasło",
|
|
173
|
-
uk: "Пароль",
|
|
174
|
-
"zh-cn": "密码",
|
|
175
|
-
},
|
|
176
|
-
"ftpPath": {
|
|
177
|
-
en: "Remote path",
|
|
178
|
-
de: "Remote-Pfad",
|
|
179
|
-
ru: "Удалённый путь",
|
|
180
|
-
pt: "Caminho remoto",
|
|
181
|
-
nl: "Extern pad",
|
|
182
|
-
fr: "Chemin distant",
|
|
183
|
-
it: "Percorso remoto",
|
|
184
|
-
es: "Ruta remota",
|
|
185
|
-
pl: "Ścieżka zdalna",
|
|
186
|
-
uk: "Віддалений шлях",
|
|
187
|
-
"zh-cn": "远程路径",
|
|
188
|
-
},
|
|
189
|
-
"ftpSecure": {
|
|
190
|
-
en: "Use FTPS (SSL/TLS)",
|
|
191
|
-
de: "FTPS verwenden (SSL/TLS)",
|
|
192
|
-
ru: "Использовать FTPS (SSL/TLS)",
|
|
193
|
-
pt: "Usar FTPS (SSL/TLS)",
|
|
194
|
-
nl: "FTPS gebruiken (SSL/TLS)",
|
|
195
|
-
fr: "Utiliser FTPS (SSL/TLS)",
|
|
196
|
-
it: "Usa FTPS (SSL/TLS)",
|
|
197
|
-
es: "Usar FTPS (SSL/TLS)",
|
|
198
|
-
pl: "Użyj FTPS (SSL/TLS)",
|
|
199
|
-
uk: "Використовувати FTPS (SSL/TLS)",
|
|
200
|
-
"zh-cn": "使用 FTPS (SSL/TLS)",
|
|
201
|
-
},
|
|
202
|
-
"smbSection": {
|
|
203
|
-
en: "SMB (Windows Share / NAS)",
|
|
204
|
-
de: "SMB (Windows-Freigabe / NAS)",
|
|
205
|
-
ru: "SMB (общий ресурс Windows / NAS)",
|
|
206
|
-
pt: "SMB (partilha Windows / NAS)",
|
|
207
|
-
nl: "SMB (Windows-share / NAS)",
|
|
208
|
-
fr: "SMB (partage Windows / NAS)",
|
|
209
|
-
it: "SMB (condivisione Windows / NAS)",
|
|
210
|
-
es: "SMB (recurso compartido Windows / NAS)",
|
|
211
|
-
pl: "SMB (udział Windows / NAS)",
|
|
212
|
-
uk: "SMB (мережева папка Windows / NAS)",
|
|
213
|
-
"zh-cn": "SMB(Windows 共享 / NAS)",
|
|
214
|
-
},
|
|
215
|
-
"smbEnabled": {
|
|
216
|
-
en: "Enable SMB source",
|
|
217
|
-
de: "SMB-Quelle aktivieren",
|
|
218
|
-
ru: "Включить источник SMB",
|
|
219
|
-
pt: "Ativar fonte SMB",
|
|
220
|
-
nl: "SMB-bron inschakelen",
|
|
221
|
-
fr: "Activer la source SMB",
|
|
222
|
-
it: "Abilita sorgente SMB",
|
|
223
|
-
es: "Activar fuente SMB",
|
|
224
|
-
pl: "Włącz źródło SMB",
|
|
225
|
-
uk: "Увімкнути джерело SMB",
|
|
226
|
-
"zh-cn": "启用 SMB 来源",
|
|
227
|
-
},
|
|
228
|
-
"smbHost": {
|
|
229
|
-
en: "Host / IP",
|
|
230
|
-
de: "Host / IP",
|
|
231
|
-
ru: "Хост / IP",
|
|
232
|
-
pt: "Host / IP",
|
|
233
|
-
nl: "Host / IP",
|
|
234
|
-
fr: "Hôte / IP",
|
|
235
|
-
it: "Host / IP",
|
|
236
|
-
es: "Host / IP",
|
|
237
|
-
pl: "Host / IP",
|
|
238
|
-
uk: "Хост / IP",
|
|
239
|
-
"zh-cn": "主机 / IP",
|
|
240
|
-
},
|
|
241
|
-
"smbShare": {
|
|
242
|
-
en: "Share name",
|
|
243
|
-
de: "Freigabename",
|
|
244
|
-
ru: "Имя ресурса",
|
|
245
|
-
pt: "Nome de partilha",
|
|
246
|
-
nl: "Sharenaam",
|
|
247
|
-
fr: "Nom du partage",
|
|
248
|
-
it: "Nome condivisione",
|
|
249
|
-
es: "Nombre del recurso",
|
|
250
|
-
pl: "Nazwa udziału",
|
|
251
|
-
uk: "Ім'я ресурсу",
|
|
252
|
-
"zh-cn": "共享名称",
|
|
253
|
-
},
|
|
254
|
-
"smbPath": {
|
|
255
|
-
en: "Path within share (optional)",
|
|
256
|
-
de: "Pfad innerhalb der Freigabe (optional)",
|
|
257
|
-
ru: "Путь внутри ресурса (необязательно)",
|
|
258
|
-
pt: "Caminho na partilha (opcional)",
|
|
259
|
-
nl: "Pad binnen share (optioneel)",
|
|
260
|
-
fr: "Chemin dans le partage (optionnel)",
|
|
261
|
-
it: "Percorso nella condivisione (opzionale)",
|
|
262
|
-
es: "Ruta dentro del recurso (opcional)",
|
|
263
|
-
pl: "Ścieżka w udziale (opcjonalnie)",
|
|
264
|
-
uk: "Шлях всередині ресурсу (необов'язково)",
|
|
265
|
-
"zh-cn": "共享内路径(可选)",
|
|
266
|
-
},
|
|
267
|
-
"smbUser": {
|
|
268
|
-
en: "Username",
|
|
269
|
-
de: "Benutzername",
|
|
270
|
-
ru: "Имя пользователя",
|
|
271
|
-
pt: "Utilizador",
|
|
272
|
-
nl: "Gebruikersnaam",
|
|
273
|
-
fr: "Nom d'utilisateur",
|
|
274
|
-
it: "Nome utente",
|
|
275
|
-
es: "Usuario",
|
|
276
|
-
pl: "Nazwa użytkownika",
|
|
277
|
-
uk: "Ім'я користувача",
|
|
278
|
-
"zh-cn": "用户名",
|
|
279
|
-
},
|
|
280
|
-
"smbPassword": {
|
|
281
|
-
en: "Password",
|
|
282
|
-
de: "Passwort",
|
|
283
|
-
ru: "Пароль",
|
|
284
|
-
pt: "Senha",
|
|
285
|
-
nl: "Wachtwoord",
|
|
286
|
-
fr: "Mot de passe",
|
|
287
|
-
it: "Password",
|
|
288
|
-
es: "Contraseña",
|
|
289
|
-
pl: "Hasło",
|
|
290
|
-
uk: "Пароль",
|
|
291
|
-
"zh-cn": "密码",
|
|
292
|
-
},
|
|
293
|
-
"smbDomain": {
|
|
294
|
-
en: "Domain (optional)",
|
|
295
|
-
de: "Domäne (optional)",
|
|
296
|
-
ru: "Домен (необязательно)",
|
|
297
|
-
pt: "Domínio (opcional)",
|
|
298
|
-
nl: "Domein (optioneel)",
|
|
299
|
-
fr: "Domaine (optionnel)",
|
|
300
|
-
it: "Dominio (opzionale)",
|
|
301
|
-
es: "Dominio (opcional)",
|
|
302
|
-
pl: "Domena (opcjonalnie)",
|
|
303
|
-
uk: "Домен (необов'язково)",
|
|
304
|
-
"zh-cn": "域(可选)",
|
|
305
|
-
},
|
|
306
|
-
"httpSection": {
|
|
307
|
-
en: "HTTP / HTTPS (direct URL)",
|
|
308
|
-
de: "HTTP / HTTPS (direkte URL)",
|
|
309
|
-
ru: "HTTP / HTTPS (прямой URL)",
|
|
310
|
-
pt: "HTTP / HTTPS (URL direto)",
|
|
311
|
-
nl: "HTTP / HTTPS (directe URL)",
|
|
312
|
-
fr: "HTTP / HTTPS (URL directe)",
|
|
313
|
-
it: "HTTP / HTTPS (URL diretto)",
|
|
314
|
-
es: "HTTP / HTTPS (URL directa)",
|
|
315
|
-
pl: "HTTP / HTTPS (bezpośredni URL)",
|
|
316
|
-
uk: "HTTP / HTTPS (пряме посилання)",
|
|
317
|
-
"zh-cn": "HTTP / HTTPS(直接 URL)",
|
|
318
|
-
},
|
|
319
|
-
"httpEnabled": {
|
|
320
|
-
en: "Enable HTTP source",
|
|
321
|
-
de: "HTTP-Quelle aktivieren",
|
|
322
|
-
ru: "Включить источник HTTP",
|
|
323
|
-
pt: "Ativar fonte HTTP",
|
|
324
|
-
nl: "HTTP-bron inschakelen",
|
|
325
|
-
fr: "Activer la source HTTP",
|
|
326
|
-
it: "Abilita sorgente HTTP",
|
|
327
|
-
es: "Activar fuente HTTP",
|
|
328
|
-
pl: "Włącz źródło HTTP",
|
|
329
|
-
uk: "Увімкнути джерело HTTP",
|
|
330
|
-
"zh-cn": "启用 HTTP 来源",
|
|
331
|
-
},
|
|
332
|
-
"httpNote": {
|
|
333
|
-
en: "Directly load a backup file from a URL in the Script Restore tab.",
|
|
334
|
-
de: "Im Script-Restore-Tab direkt eine Backup-URL eingeben und laden.",
|
|
335
|
-
ru: "В вкладке напрямую введите URL резервной копии.",
|
|
336
|
-
pt: "No separador, insira diretamente um URL de backup.",
|
|
337
|
-
nl: "In het tabblad een backup-URL direct invoeren en laden.",
|
|
338
|
-
fr: "Dans l'onglet, entrez directement une URL de sauvegarde.",
|
|
339
|
-
it: "Nella scheda, inserire direttamente un URL di backup.",
|
|
340
|
-
es: "En la pestaña, introducir directamente una URL de copia de seguridad.",
|
|
341
|
-
pl: "W zakładce wpisz bezpośrednio adres URL kopii zapasowej.",
|
|
342
|
-
uk: "На вкладці введіть URL резервної копії безпосередньо.",
|
|
343
|
-
"zh-cn": "在选项卡中直接输入备份文件的 URL。",
|
|
344
|
-
},
|
|
345
|
-
"sftpSection": {
|
|
346
|
-
en: "SFTP (SSH File Transfer)",
|
|
347
|
-
de: "SFTP (SSH-Dateiübertragung)",
|
|
348
|
-
ru: "SFTP (передача файлов по SSH)",
|
|
349
|
-
pt: "SFTP (transferência de ficheiros SSH)",
|
|
350
|
-
nl: "SFTP (SSH bestandsoverdracht)",
|
|
351
|
-
fr: "SFTP (transfert de fichiers SSH)",
|
|
352
|
-
it: "SFTP (trasferimento file SSH)",
|
|
353
|
-
es: "SFTP (transferencia de archivos SSH)",
|
|
354
|
-
pl: "SFTP (przesyłanie plików SSH)",
|
|
355
|
-
uk: "SFTP (передача файлів SSH)",
|
|
356
|
-
"zh-cn": "SFTP(SSH 文件传输)",
|
|
357
|
-
},
|
|
358
|
-
"sftpEnabled": {
|
|
359
|
-
en: "Enable SFTP source",
|
|
360
|
-
de: "SFTP-Quelle aktivieren",
|
|
361
|
-
ru: "Включить источник SFTP",
|
|
362
|
-
pt: "Ativar fonte SFTP",
|
|
363
|
-
nl: "SFTP-bron inschakelen",
|
|
364
|
-
fr: "Activer la source SFTP",
|
|
365
|
-
it: "Abilita sorgente SFTP",
|
|
366
|
-
es: "Activar fuente SFTP",
|
|
367
|
-
pl: "Włącz źródło SFTP",
|
|
368
|
-
uk: "Увімкнути джерело SFTP",
|
|
369
|
-
"zh-cn": "启用 SFTP 来源",
|
|
370
|
-
},
|
|
371
|
-
"sftpHost": { en: "Host", de: "Host", ru: "Хост", pt: "Host", nl: "Host", fr: "Hôte", it: "Host", es: "Host", pl: "Host", uk: "Хост", "zh-cn": "主机" },
|
|
372
|
-
"sftpPort": { en: "Port", de: "Port", ru: "Порт", pt: "Porta", nl: "Poort", fr: "Port", it: "Porta", es: "Puerto", pl: "Port", uk: "Порт", "zh-cn": "端口" },
|
|
373
|
-
"sftpUser": { en: "Username", de: "Benutzername", ru: "Пользователь", pt: "Utilizador", nl: "Gebruikersnaam", fr: "Nom d'utilisateur", it: "Nome utente", es: "Usuario", pl: "Nazwa użytkownika", uk: "Ім'я користувача", "zh-cn": "用户名" },
|
|
374
|
-
"sftpPassword": { en: "Password", de: "Passwort", ru: "Пароль", pt: "Senha", nl: "Wachtwoord", fr: "Mot de passe", it: "Password", es: "Contraseña", pl: "Hasło", uk: "Пароль", "zh-cn": "密码" },
|
|
375
|
-
"sftpPath": { en: "Remote path", de: "Remote-Pfad", ru: "Удалённый путь", pt: "Caminho remoto", nl: "Extern pad", fr: "Chemin distant", it: "Percorso remoto", es: "Ruta remota", pl: "Ścieżka zdalna", uk: "Віддалений шлях", "zh-cn": "远程路径" },
|
|
376
|
-
"webdavSection": {
|
|
377
|
-
en: "WebDAV (Nextcloud, Owncloud, …)",
|
|
378
|
-
de: "WebDAV (Nextcloud, Owncloud, …)",
|
|
379
|
-
ru: "WebDAV (Nextcloud, Owncloud, …)",
|
|
380
|
-
pt: "WebDAV (Nextcloud, Owncloud, …)",
|
|
381
|
-
nl: "WebDAV (Nextcloud, Owncloud, …)",
|
|
382
|
-
fr: "WebDAV (Nextcloud, Owncloud, …)",
|
|
383
|
-
it: "WebDAV (Nextcloud, Owncloud, …)",
|
|
384
|
-
es: "WebDAV (Nextcloud, Owncloud, …)",
|
|
385
|
-
pl: "WebDAV (Nextcloud, Owncloud, …)",
|
|
386
|
-
uk: "WebDAV (Nextcloud, Owncloud, …)",
|
|
387
|
-
"zh-cn": "WebDAV(Nextcloud、Owncloud 等)",
|
|
388
|
-
},
|
|
389
|
-
"webdavEnabled": {
|
|
390
|
-
en: "Enable WebDAV source",
|
|
391
|
-
de: "WebDAV-Quelle aktivieren",
|
|
392
|
-
ru: "Включить источник WebDAV",
|
|
393
|
-
pt: "Ativar fonte WebDAV",
|
|
394
|
-
nl: "WebDAV-bron inschakelen",
|
|
395
|
-
fr: "Activer la source WebDAV",
|
|
396
|
-
it: "Abilita sorgente WebDAV",
|
|
397
|
-
es: "Activar fuente WebDAV",
|
|
398
|
-
pl: "Włącz źródło WebDAV",
|
|
399
|
-
uk: "Увімкнути джерело WebDAV",
|
|
400
|
-
"zh-cn": "启用 WebDAV 来源",
|
|
401
|
-
},
|
|
402
|
-
"webdavUrl": {
|
|
403
|
-
en: "Server URL (e.g. https://cloud.example.com/remote.php/webdav)",
|
|
404
|
-
de: "Server-URL (z.B. https://cloud.example.com/remote.php/webdav)",
|
|
405
|
-
ru: "URL сервера",
|
|
406
|
-
pt: "URL do servidor",
|
|
407
|
-
nl: "Server-URL",
|
|
408
|
-
fr: "URL du serveur",
|
|
409
|
-
it: "URL del server",
|
|
410
|
-
es: "URL del servidor",
|
|
411
|
-
pl: "URL serwera",
|
|
412
|
-
uk: "URL сервера",
|
|
413
|
-
"zh-cn": "服务器 URL",
|
|
414
|
-
},
|
|
415
|
-
"webdavUser": { en: "Username", de: "Benutzername", ru: "Пользователь", pt: "Utilizador", nl: "Gebruikersnaam", fr: "Nom d'utilisateur", it: "Nome utente", es: "Usuario", pl: "Nazwa użytkownika", uk: "Ім'я користувача", "zh-cn": "用户名" },
|
|
416
|
-
"webdavPassword": { en: "Password", de: "Passwort", ru: "Пароль", pt: "Senha", nl: "Wachtwoord", fr: "Mot de passe", it: "Password", es: "Contraseña", pl: "Hasło", uk: "Пароль", "zh-cn": "密码" },
|
|
417
|
-
"webdavPath": { en: "Path within WebDAV", de: "Pfad im WebDAV", ru: "Путь в WebDAV", pt: "Caminho no WebDAV", nl: "Pad in WebDAV", fr: "Chemin dans WebDAV", it: "Percorso in WebDAV", es: "Ruta en WebDAV", pl: "Ścieżka w WebDAV", uk: "Шлях у WebDAV", "zh-cn": "WebDAV 内路径" },
|
|
418
|
-
"suggestPath": {
|
|
419
|
-
en: "Auto-detect",
|
|
420
|
-
de: "Auto-erkennen",
|
|
421
|
-
ru: "Авто-определение",
|
|
422
|
-
pt: "Auto-detetar",
|
|
423
|
-
nl: "Auto-detectie",
|
|
424
|
-
fr: "Détection auto",
|
|
425
|
-
it: "Rilevamento auto",
|
|
426
|
-
es: "Auto-detección",
|
|
427
|
-
pl: "Automatyczne wykrycie",
|
|
428
|
-
uk: "Авто-визначення",
|
|
429
|
-
"zh-cn": "自动检测",
|
|
430
|
-
},
|
|
431
|
-
"downloadZip": {
|
|
432
|
-
en: "Download as ZIP",
|
|
433
|
-
de: "Als ZIP herunterladen",
|
|
434
|
-
ru: "Скачать как ZIP",
|
|
435
|
-
pt: "Baixar como ZIP",
|
|
436
|
-
nl: "Downloaden als ZIP",
|
|
437
|
-
fr: "Télécharger en ZIP",
|
|
438
|
-
it: "Scarica come ZIP",
|
|
439
|
-
es: "Descargar como ZIP",
|
|
440
|
-
pl: "Pobierz jako ZIP",
|
|
441
|
-
uk: "Завантажити як ZIP",
|
|
442
|
-
"zh-cn": "下载为 ZIP",
|
|
443
|
-
},
|
|
444
|
-
};
|