iobroker.script-restore 0.0.5 → 0.0.6
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.md +6 -0
- package/admin/index_m.html +152 -8
- package/admin/tab_m.html +105 -10
- package/admin/words.js +138 -0
- package/build/main.js +216 -1
- package/build/main.js.map +3 -3
- package/io-package.json +39 -2
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -75,6 +75,12 @@ 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.6 (2026-04-08)
|
|
79
|
+
* (ipod86) add HTTP, SFTP and WebDAV as optional backup sources
|
|
80
|
+
* (ipod86) multi-select scripts with Ctrl+click and download as ZIP
|
|
81
|
+
* (ipod86) remember last loaded backup in browser (localStorage)
|
|
82
|
+
* (ipod86) auto-detect local backup path from backitup adapter
|
|
83
|
+
|
|
78
84
|
### 0.0.5 (2026-04-08)
|
|
79
85
|
* (ipod86) add FTP and SMB as optional backup sources with connection test button
|
|
80
86
|
* (ipod86) make local backup source optional (enable/disable in settings)
|
package/admin/index_m.html
CHANGED
|
@@ -59,15 +59,28 @@
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function updateSections() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
$('#
|
|
66
|
-
$('#
|
|
67
|
-
$('#
|
|
62
|
+
$('#localDetails').toggle($('#localEnabled').prop('checked'));
|
|
63
|
+
$('#ftpDetails').toggle($('#ftpEnabled').prop('checked'));
|
|
64
|
+
$('#smbDetails').toggle($('#smbEnabled').prop('checked'));
|
|
65
|
+
$('#httpDetails').toggle($('#httpEnabled').prop('checked'));
|
|
66
|
+
$('#sftpDetails').toggle($('#sftpEnabled').prop('checked'));
|
|
67
|
+
$('#webdavDetails').toggle($('#webdavEnabled').prop('checked'));
|
|
68
68
|
if (M) M.updateTextFields();
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function suggestBackupPath() {
|
|
72
|
+
var btn = document.getElementById('suggestBtn');
|
|
73
|
+
btn.disabled = true;
|
|
74
|
+
var inst = 'script-restore.' + getAdapterInstance();
|
|
75
|
+
socket.emit('sendTo', inst, 'suggestBackupPath', {}, function (result) {
|
|
76
|
+
btn.disabled = false;
|
|
77
|
+
if (result && result.path) {
|
|
78
|
+
$('#backupPath').val(result.path).trigger('change');
|
|
79
|
+
if (M) M.updateTextFields();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
function getAdapterInstance() {
|
|
72
85
|
var params = new URLSearchParams(window.location.search);
|
|
73
86
|
var id = params.get('id') || '';
|
|
@@ -92,7 +105,7 @@
|
|
|
92
105
|
path: $('#ftpPath').val() || '/',
|
|
93
106
|
secure: $('#ftpSecure').prop('checked'),
|
|
94
107
|
};
|
|
95
|
-
} else {
|
|
108
|
+
} else if (type === 'smb') {
|
|
96
109
|
data = {
|
|
97
110
|
host: $('#smbHost').val(),
|
|
98
111
|
share: $('#smbShare').val(),
|
|
@@ -101,10 +114,26 @@
|
|
|
101
114
|
password: $('#smbPassword').val(),
|
|
102
115
|
domain: $('#smbDomain').val(),
|
|
103
116
|
};
|
|
117
|
+
} else if (type === 'sftp') {
|
|
118
|
+
data = {
|
|
119
|
+
host: $('#sftpHost').val(),
|
|
120
|
+
port: parseInt($('#sftpPort').val(), 10) || 22,
|
|
121
|
+
user: $('#sftpUser').val(),
|
|
122
|
+
password: $('#sftpPassword').val(),
|
|
123
|
+
path: $('#sftpPath').val() || '/',
|
|
124
|
+
};
|
|
125
|
+
} else {
|
|
126
|
+
data = {
|
|
127
|
+
url: $('#webdavUrl').val(),
|
|
128
|
+
user: $('#webdavUser').val(),
|
|
129
|
+
password: $('#webdavPassword').val(),
|
|
130
|
+
path: $('#webdavPath').val() || '/',
|
|
131
|
+
};
|
|
104
132
|
}
|
|
105
133
|
|
|
134
|
+
var cmdMap = { ftp: 'testFtp', smb: 'testSmb', sftp: 'testSftp', webdav: 'testWebdav' };
|
|
106
135
|
var inst = 'script-restore.' + getAdapterInstance();
|
|
107
|
-
var cmd = type
|
|
136
|
+
var cmd = cmdMap[type] || ('test' + type.charAt(0).toUpperCase() + type.slice(1));
|
|
108
137
|
socket.emit('sendTo', inst, cmd, data, function (result) {
|
|
109
138
|
btn.disabled = false;
|
|
110
139
|
btn.textContent = _('testConnection');
|
|
@@ -151,6 +180,11 @@
|
|
|
151
180
|
<label for="backupPath" class="translate">backupPath</label>
|
|
152
181
|
<span class="translate helper-text">backupPathHint</span>
|
|
153
182
|
</div>
|
|
183
|
+
<div class="col s12 m2 l2" style="padding-top: 20px;">
|
|
184
|
+
<button id="suggestBtn" class="btn btn-small waves-effect waves-light" onclick="suggestBackupPath()" type="button">
|
|
185
|
+
<span class="translate">suggestPath</span>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
154
188
|
</div>
|
|
155
189
|
</div>
|
|
156
190
|
|
|
@@ -261,6 +295,116 @@
|
|
|
261
295
|
</div>
|
|
262
296
|
</div>
|
|
263
297
|
|
|
298
|
+
<!-- HTTP Section -->
|
|
299
|
+
<div class="row" style="margin-top: 24px;">
|
|
300
|
+
<div class="col s12">
|
|
301
|
+
<h6 class="translate" style="font-weight:600; border-bottom: 1px solid #ccc; padding-bottom: 4px;">httpSection</h6>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
<div class="row">
|
|
305
|
+
<div class="col s12">
|
|
306
|
+
<input class="value filled-in" id="httpEnabled" type="checkbox">
|
|
307
|
+
<label for="httpEnabled" class="translate">httpEnabled</label>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
<div id="httpDetails" style="display:none;">
|
|
311
|
+
<div class="row">
|
|
312
|
+
<div class="col s12">
|
|
313
|
+
<span class="translate helper-text" style="font-size:0.85rem; color:#555;">httpNote</span>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<!-- SFTP Section -->
|
|
319
|
+
<div class="row" style="margin-top: 24px;">
|
|
320
|
+
<div class="col s12">
|
|
321
|
+
<h6 class="translate" style="font-weight:600; border-bottom: 1px solid #ccc; padding-bottom: 4px;">sftpSection</h6>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="row">
|
|
325
|
+
<div class="col s12">
|
|
326
|
+
<input class="value filled-in" id="sftpEnabled" type="checkbox">
|
|
327
|
+
<label for="sftpEnabled" class="translate">sftpEnabled</label>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
<div id="sftpDetails" style="display:none;">
|
|
331
|
+
<div class="row">
|
|
332
|
+
<div class="input-field col s12 m5 l4">
|
|
333
|
+
<input class="value" id="sftpHost" type="text" placeholder="192.168.1.100">
|
|
334
|
+
<label for="sftpHost" class="translate">sftpHost</label>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="input-field col s6 m2 l1">
|
|
337
|
+
<input class="value" id="sftpPort" type="number" placeholder="22" min="1" max="65535">
|
|
338
|
+
<label for="sftpPort" class="translate">sftpPort</label>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="row">
|
|
342
|
+
<div class="input-field col s12 m4 l3">
|
|
343
|
+
<input class="value" id="sftpUser" type="text" placeholder="">
|
|
344
|
+
<label for="sftpUser" class="translate">sftpUser</label>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="input-field col s12 m4 l3">
|
|
347
|
+
<input class="value" id="sftpPassword" type="password" placeholder="">
|
|
348
|
+
<label for="sftpPassword" class="translate">sftpPassword</label>
|
|
349
|
+
</div>
|
|
350
|
+
<div class="input-field col s12 m4 l3">
|
|
351
|
+
<input class="value" id="sftpPath" type="text" placeholder="/">
|
|
352
|
+
<label for="sftpPath" class="translate">sftpPath</label>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
<div class="row" style="margin-top: 4px;">
|
|
356
|
+
<div class="col s12">
|
|
357
|
+
<button id="sftpTestBtn" class="btn btn-small waves-effect waves-light" onclick="testConnection('sftp')" type="button">
|
|
358
|
+
<span class="translate">testConnection</span>
|
|
359
|
+
</button>
|
|
360
|
+
<span id="sftpTestResult" style="display:none; margin-left:12px; font-size:0.9rem; font-weight:500;"></span>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<!-- WebDAV Section -->
|
|
366
|
+
<div class="row" style="margin-top: 24px;">
|
|
367
|
+
<div class="col s12">
|
|
368
|
+
<h6 class="translate" style="font-weight:600; border-bottom: 1px solid #ccc; padding-bottom: 4px;">webdavSection</h6>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
<div class="row">
|
|
372
|
+
<div class="col s12">
|
|
373
|
+
<input class="value filled-in" id="webdavEnabled" type="checkbox">
|
|
374
|
+
<label for="webdavEnabled" class="translate">webdavEnabled</label>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
<div id="webdavDetails" style="display:none;">
|
|
378
|
+
<div class="row">
|
|
379
|
+
<div class="input-field col s12 m8 l6">
|
|
380
|
+
<input class="value" id="webdavUrl" type="text" placeholder="https://cloud.example.com/remote.php/webdav">
|
|
381
|
+
<label for="webdavUrl" class="translate">webdavUrl</label>
|
|
382
|
+
</div>
|
|
383
|
+
</div>
|
|
384
|
+
<div class="row">
|
|
385
|
+
<div class="input-field col s12 m4 l3">
|
|
386
|
+
<input class="value" id="webdavUser" type="text" placeholder="">
|
|
387
|
+
<label for="webdavUser" class="translate">webdavUser</label>
|
|
388
|
+
</div>
|
|
389
|
+
<div class="input-field col s12 m4 l3">
|
|
390
|
+
<input class="value" id="webdavPassword" type="password" placeholder="">
|
|
391
|
+
<label for="webdavPassword" class="translate">webdavPassword</label>
|
|
392
|
+
</div>
|
|
393
|
+
<div class="input-field col s12 m4 l3">
|
|
394
|
+
<input class="value" id="webdavPath" type="text" placeholder="/">
|
|
395
|
+
<label for="webdavPath" class="translate">webdavPath</label>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="row" style="margin-top: 4px;">
|
|
399
|
+
<div class="col s12">
|
|
400
|
+
<button id="webdavTestBtn" class="btn btn-small waves-effect waves-light" onclick="testConnection('webdav')" type="button">
|
|
401
|
+
<span class="translate">testConnection</span>
|
|
402
|
+
</button>
|
|
403
|
+
<span id="webdavTestResult" style="display:none; margin-left:12px; font-size:0.9rem; font-weight:500;"></span>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
|
|
264
408
|
</div>
|
|
265
409
|
|
|
266
410
|
</body>
|
package/admin/tab_m.html
CHANGED
|
@@ -148,6 +148,7 @@
|
|
|
148
148
|
|
|
149
149
|
.script-item:hover { background-color: #f0f7ff; }
|
|
150
150
|
.script-item.active { background-color: #e7f1ff; border-left: 3px solid var(--primary); padding-left: calc(12px - 3px); }
|
|
151
|
+
.script-item.selected { background-color: #fff3cd; border-left: 3px solid #ffc107; padding-left: calc(12px - 3px); }
|
|
151
152
|
.tree-script.active { padding-left: calc(32px - 3px); }
|
|
152
153
|
.tree-children .tree-children .tree-script.active { padding-left: calc(50px - 3px); }
|
|
153
154
|
.tree-children .tree-children .tree-children .tree-script.active { padding-left: calc(68px - 3px); }
|
|
@@ -339,6 +340,23 @@
|
|
|
339
340
|
</button>
|
|
340
341
|
<div class="dropdown-menu" id="smbMenu"></div>
|
|
341
342
|
</div>
|
|
343
|
+
<div class="dropdown-wrapper" id="sftpDropdown" style="display:none;">
|
|
344
|
+
<button class="btn btn-outline" onclick="toggleDropdown('sftp')">
|
|
345
|
+
🔒 SFTP Backups ▾
|
|
346
|
+
</button>
|
|
347
|
+
<div class="dropdown-menu" id="sftpMenu"></div>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="dropdown-wrapper" id="webdavDropdown" style="display:none;">
|
|
350
|
+
<button class="btn btn-outline" onclick="toggleDropdown('webdav')">
|
|
351
|
+
☁️ WebDAV Backups ▾
|
|
352
|
+
</button>
|
|
353
|
+
<div class="dropdown-menu" id="webdavMenu"></div>
|
|
354
|
+
</div>
|
|
355
|
+
<div id="httpInputWrapper" style="display:none; display:flex; align-items:center; gap:6px;">
|
|
356
|
+
<input type="text" id="httpUrlInput" placeholder="https://..." style="padding:0.35rem 0.6rem; border:1px solid #ced4da; border-radius:4px; font-size:0.875rem; min-width:260px; font-family:inherit;">
|
|
357
|
+
<button class="btn btn-outline" onclick="loadHttpUrl()">🌐 URL laden</button>
|
|
358
|
+
</div>
|
|
359
|
+
<button id="zipBtn" class="btn btn-outline" onclick="downloadZip()" style="display:none;">📦 ZIP</button>
|
|
342
360
|
<span class="status-msg" id="statusMsg">Backup laden oder Quelle wählen</span>
|
|
343
361
|
</div>
|
|
344
362
|
</div>
|
|
@@ -687,7 +705,7 @@
|
|
|
687
705
|
const scripts = parseJsonContent(reader.result, file.name);
|
|
688
706
|
hideLoader();
|
|
689
707
|
loadScripts(scripts);
|
|
690
|
-
setStatus(scripts.length + ' Skripte geladen aus: ' + file.name, 'success');
|
|
708
|
+
saveLastBackup('Upload', file.name); setStatus(scripts.length + ' Skripte geladen aus: ' + file.name, 'success');
|
|
691
709
|
} catch(e) {
|
|
692
710
|
hideLoader();
|
|
693
711
|
setStatus('Fehler beim Parsen: ' + e.message, 'error');
|
|
@@ -710,7 +728,7 @@
|
|
|
710
728
|
const scripts = await parseArchiveInBrowser(archiveReader.result, file.name); // result is ArrayBuffer
|
|
711
729
|
hideLoader();
|
|
712
730
|
loadScripts(scripts);
|
|
713
|
-
setStatus(scripts.length + ' Skripte geladen aus: ' + file.name, 'success');
|
|
731
|
+
saveLastBackup('Upload', file.name); setStatus(scripts.length + ' Skripte geladen aus: ' + file.name, 'success');
|
|
714
732
|
} catch(e) {
|
|
715
733
|
hideLoader();
|
|
716
734
|
setStatus('Fehler: ' + e.message, 'error');
|
|
@@ -724,6 +742,19 @@
|
|
|
724
742
|
this.value = '';
|
|
725
743
|
});
|
|
726
744
|
|
|
745
|
+
// === localStorage: last loaded backup ===
|
|
746
|
+
const LS_KEY = 'scriptRestore_lastBackup';
|
|
747
|
+
function saveLastBackup(source, label) {
|
|
748
|
+
try { localStorage.setItem(LS_KEY, JSON.stringify({ source, label })); } catch {}
|
|
749
|
+
}
|
|
750
|
+
function restoreLastBackup() {
|
|
751
|
+
try {
|
|
752
|
+
const d = JSON.parse(localStorage.getItem(LS_KEY) || 'null');
|
|
753
|
+
if (d) setStatus('Zuletzt geladen: [' + escapeHTML(d.source) + '] ' + escapeHTML(d.label), '');
|
|
754
|
+
} catch {}
|
|
755
|
+
}
|
|
756
|
+
restoreLastBackup();
|
|
757
|
+
|
|
727
758
|
// === Source Config ===
|
|
728
759
|
function loadSourceConfig(attempt) {
|
|
729
760
|
attempt = attempt || 0;
|
|
@@ -735,16 +766,21 @@
|
|
|
735
766
|
if (result.localEnabled === false) document.getElementById('localDropdown').style.display = 'none';
|
|
736
767
|
if (result.ftpEnabled) document.getElementById('ftpDropdown').style.display = '';
|
|
737
768
|
if (result.smbEnabled) document.getElementById('smbDropdown').style.display = '';
|
|
769
|
+
if (result.sftpEnabled) document.getElementById('sftpDropdown').style.display = '';
|
|
770
|
+
if (result.webdavEnabled) document.getElementById('webdavDropdown').style.display = '';
|
|
771
|
+
if (result.httpEnabled) document.getElementById('httpInputWrapper').style.display = 'flex';
|
|
738
772
|
});
|
|
739
773
|
}
|
|
740
774
|
setTimeout(function() { loadSourceConfig(0); }, 300);
|
|
741
775
|
|
|
742
|
-
// === Dropdowns (local / ftp / smb) ===
|
|
743
|
-
const dropdownState = { local: false, ftp: false, smb: false };
|
|
776
|
+
// === Dropdowns (local / ftp / smb / sftp / webdav) ===
|
|
777
|
+
const dropdownState = { local: false, ftp: false, smb: false, sftp: false, webdav: false };
|
|
744
778
|
const dropdownConfig = {
|
|
745
|
-
local:
|
|
746
|
-
ftp:
|
|
747
|
-
smb:
|
|
779
|
+
local: { listCmd: 'listLocalFiles', parseCmd: 'parseLocalFile', menuId: 'localMenu', wrapperId: 'localDropdown' },
|
|
780
|
+
ftp: { listCmd: 'listFtpFiles', parseCmd: 'parseFtpFile', menuId: 'ftpMenu', wrapperId: 'ftpDropdown' },
|
|
781
|
+
smb: { listCmd: 'listSmbFiles', parseCmd: 'parseSmbFile', menuId: 'smbMenu', wrapperId: 'smbDropdown' },
|
|
782
|
+
sftp: { listCmd: 'listSftpFiles', parseCmd: 'parseSftpFile', menuId: 'sftpMenu', wrapperId: 'sftpDropdown' },
|
|
783
|
+
webdav: { listCmd: 'listWebdavFiles', parseCmd: 'parseWebdavFile', menuId: 'webdavMenu', wrapperId: 'webdavDropdown' },
|
|
748
784
|
};
|
|
749
785
|
|
|
750
786
|
function toggleDropdown(src) {
|
|
@@ -810,6 +846,7 @@
|
|
|
810
846
|
setStatus('Fehler: ' + result.error, 'error');
|
|
811
847
|
} else if (result && result.scripts) {
|
|
812
848
|
loadScripts(result.scripts);
|
|
849
|
+
saveLastBackup(src.toUpperCase(), filename);
|
|
813
850
|
setStatus(result.scripts.length + ' Skripte geladen aus: ' + filename, 'success');
|
|
814
851
|
} else {
|
|
815
852
|
setStatus('Keine Skripte gefunden.', 'error');
|
|
@@ -817,9 +854,61 @@
|
|
|
817
854
|
});
|
|
818
855
|
}
|
|
819
856
|
|
|
857
|
+
// === HTTP URL ===
|
|
858
|
+
function loadHttpUrl() {
|
|
859
|
+
const url = document.getElementById('httpUrlInput').value.trim();
|
|
860
|
+
if (!url) return;
|
|
861
|
+
const filename = url.split('/').pop() || 'backup';
|
|
862
|
+
showLoaderSpinner('Lade URL...');
|
|
863
|
+
sendTo('parseHttpUrl', { url }, function(result) {
|
|
864
|
+
hideLoader();
|
|
865
|
+
if (result && result.error) {
|
|
866
|
+
setStatus('Fehler: ' + result.error, 'error');
|
|
867
|
+
} else if (result && result.scripts) {
|
|
868
|
+
loadScripts(result.scripts);
|
|
869
|
+
saveLastBackup('HTTP', filename);
|
|
870
|
+
setStatus(result.scripts.length + ' Skripte geladen von URL', 'success');
|
|
871
|
+
} else {
|
|
872
|
+
setStatus('Keine Skripte gefunden.', 'error');
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// === Multi-Select & ZIP ===
|
|
878
|
+
let selectedIndices = new Set();
|
|
879
|
+
|
|
880
|
+
function toggleSelect(idx, el) {
|
|
881
|
+
if (selectedIndices.has(idx)) {
|
|
882
|
+
selectedIndices.delete(idx);
|
|
883
|
+
el.classList.remove('selected');
|
|
884
|
+
} else {
|
|
885
|
+
selectedIndices.add(idx);
|
|
886
|
+
el.classList.add('selected');
|
|
887
|
+
}
|
|
888
|
+
document.getElementById('zipBtn').style.display = selectedIndices.size > 1 ? '' : 'none';
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
async function downloadZip() {
|
|
892
|
+
if (selectedIndices.size < 2) return;
|
|
893
|
+
const { default: JSZip } = await import('https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js');
|
|
894
|
+
const zip = new JSZip();
|
|
895
|
+
selectedIndices.forEach(idx => {
|
|
896
|
+
const s = scriptsData[idx];
|
|
897
|
+
const ext = s.type === 'TypeScript' ? '.ts' : '.js';
|
|
898
|
+
zip.file(s.path.replace(/\./g, '/') + ext, s.source || '');
|
|
899
|
+
});
|
|
900
|
+
const blob = await zip.generateAsync({ type: 'blob' });
|
|
901
|
+
const a = document.createElement('a');
|
|
902
|
+
a.href = URL.createObjectURL(blob);
|
|
903
|
+
a.download = 'scripts.zip';
|
|
904
|
+
a.click();
|
|
905
|
+
}
|
|
906
|
+
|
|
820
907
|
function loadScripts(scripts) {
|
|
821
908
|
scriptsData = scripts;
|
|
822
909
|
cur = { index: -1 };
|
|
910
|
+
selectedIndices.clear();
|
|
911
|
+
document.getElementById('zipBtn').style.display = 'none';
|
|
823
912
|
openFolders.clear();
|
|
824
913
|
isAllExpanded = false;
|
|
825
914
|
document.getElementById('expandToggleBtn').innerHTML = '📂';
|
|
@@ -827,7 +916,7 @@
|
|
|
827
916
|
document.getElementById('actionBar').style.display = 'none';
|
|
828
917
|
document.getElementById('codeContainer').className = 'code-empty';
|
|
829
918
|
document.getElementById('codeContainer').innerHTML = scripts.length > 0
|
|
830
|
-
? '// Skript im Baum links auswählen
|
|
919
|
+
? '// Skript im Baum links auswählen… oder mehrere mit Strg+Klick für ZIP'
|
|
831
920
|
: '<span style="color:#dc3545">Keine Skripte in diesem Backup gefunden.</span>';
|
|
832
921
|
}
|
|
833
922
|
|
|
@@ -924,9 +1013,15 @@
|
|
|
924
1013
|
function createScriptNode(s, idx) {
|
|
925
1014
|
const badgeText = s.type === 'TypeScript' ? 'TS' : (s.type === 'Blockly' ? 'Blockly' : (s.type === 'Rules' ? 'RULES' : 'JS'));
|
|
926
1015
|
const div = document.createElement('div');
|
|
927
|
-
div.className = 'script-item' + (cur.index === idx ? ' active' : '');
|
|
1016
|
+
div.className = 'script-item' + (cur.index === idx ? ' active' : '') + (selectedIndices.has(idx) ? ' selected' : '');
|
|
928
1017
|
div.dataset.index = idx;
|
|
929
|
-
div.onclick = () =>
|
|
1018
|
+
div.onclick = (e) => {
|
|
1019
|
+
if (e.ctrlKey || e.metaKey) {
|
|
1020
|
+
toggleSelect(idx, div);
|
|
1021
|
+
} else {
|
|
1022
|
+
selectScript(idx);
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
930
1025
|
div.innerHTML = '<div class="script-name" title="' + escapeHTML(s.path) + '">📄 ' + escapeHTML(s.name) + '</div>' +
|
|
931
1026
|
'<span class="type-badge badge-' + s.type + '">' + badgeText + '</span>';
|
|
932
1027
|
return div;
|
package/admin/words.js
CHANGED
|
@@ -303,4 +303,142 @@ systemDictionary = {
|
|
|
303
303
|
uk: "Домен (необов'язково)",
|
|
304
304
|
"zh-cn": "域(可选)",
|
|
305
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
|
+
},
|
|
306
444
|
};
|
package/build/main.js
CHANGED
|
@@ -29,6 +29,9 @@ var import_node_child_process = require("node:child_process");
|
|
|
29
29
|
var import_node_util = require("node:util");
|
|
30
30
|
var ftp = __toESM(require("basic-ftp"));
|
|
31
31
|
var import_node_stream = require("node:stream");
|
|
32
|
+
var https = __toESM(require("node:https"));
|
|
33
|
+
var http = __toESM(require("node:http"));
|
|
34
|
+
var import_ssh2_sftp_client = __toESM(require("ssh2-sftp-client"));
|
|
32
35
|
const SMB2 = require("@marsaud/smb2");
|
|
33
36
|
const execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
|
|
34
37
|
class ScriptRestore extends utils.Adapter {
|
|
@@ -69,11 +72,38 @@ class ScriptRestore extends utils.Adapter {
|
|
|
69
72
|
{
|
|
70
73
|
localEnabled: this.config.localEnabled !== false,
|
|
71
74
|
ftpEnabled: !!this.config.ftpEnabled,
|
|
72
|
-
smbEnabled: !!this.config.smbEnabled
|
|
75
|
+
smbEnabled: !!this.config.smbEnabled,
|
|
76
|
+
httpEnabled: !!this.config.httpEnabled,
|
|
77
|
+
sftpEnabled: !!this.config.sftpEnabled,
|
|
78
|
+
webdavEnabled: !!this.config.webdavEnabled
|
|
73
79
|
},
|
|
74
80
|
obj.callback
|
|
75
81
|
);
|
|
76
82
|
break;
|
|
83
|
+
case "suggestBackupPath":
|
|
84
|
+
await this.handleSuggestBackupPath(obj);
|
|
85
|
+
break;
|
|
86
|
+
case "parseHttpUrl":
|
|
87
|
+
await this.handleParseHttpUrl(obj);
|
|
88
|
+
break;
|
|
89
|
+
case "testSftp":
|
|
90
|
+
await this.handleTestSftp(obj);
|
|
91
|
+
break;
|
|
92
|
+
case "listSftpFiles":
|
|
93
|
+
await this.handleListSftpFiles(obj);
|
|
94
|
+
break;
|
|
95
|
+
case "parseSftpFile":
|
|
96
|
+
await this.handleParseSftpFile(obj);
|
|
97
|
+
break;
|
|
98
|
+
case "testWebdav":
|
|
99
|
+
await this.handleTestWebdav(obj);
|
|
100
|
+
break;
|
|
101
|
+
case "listWebdavFiles":
|
|
102
|
+
await this.handleListWebdavFiles(obj);
|
|
103
|
+
break;
|
|
104
|
+
case "parseWebdavFile":
|
|
105
|
+
await this.handleParseWebdavFile(obj);
|
|
106
|
+
break;
|
|
77
107
|
case "testFtp":
|
|
78
108
|
await this.handleTestFtp(obj);
|
|
79
109
|
break;
|
|
@@ -468,6 +498,191 @@ class ScriptRestore extends utils.Adapter {
|
|
|
468
498
|
source: typeof c.source === "string" ? c.source : ""
|
|
469
499
|
});
|
|
470
500
|
}
|
|
501
|
+
// ─── Suggest backup path ─────────────────────────────────────────────────
|
|
502
|
+
async handleSuggestBackupPath(obj) {
|
|
503
|
+
var _a;
|
|
504
|
+
const candidates = ["/opt/iobroker/backups", "/root/backups"];
|
|
505
|
+
try {
|
|
506
|
+
const backupObj = await this.getForeignObjectAsync("system.adapter.backitup.0");
|
|
507
|
+
if ((_a = backupObj == null ? void 0 : backupObj.native) == null ? void 0 : _a.defaultFolder) {
|
|
508
|
+
candidates.unshift(backupObj.native.defaultFolder);
|
|
509
|
+
}
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
for (const p of candidates) {
|
|
513
|
+
try {
|
|
514
|
+
await fs.access(p);
|
|
515
|
+
this.sendTo(obj.from, obj.command, { path: p }, obj.callback);
|
|
516
|
+
return;
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
this.sendTo(obj.from, obj.command, { path: null }, obj.callback);
|
|
521
|
+
}
|
|
522
|
+
// ─── HTTP ────────────────────────────────────────────────────────────────
|
|
523
|
+
downloadUrl(url) {
|
|
524
|
+
return new Promise((resolve, reject) => {
|
|
525
|
+
const mod = url.startsWith("https") ? https : http;
|
|
526
|
+
mod.get(url, (res) => {
|
|
527
|
+
if (res.statusCode !== 200) {
|
|
528
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const chunks = [];
|
|
532
|
+
res.on("data", (c) => chunks.push(c));
|
|
533
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
534
|
+
res.on("error", reject);
|
|
535
|
+
}).on("error", reject);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
async handleParseHttpUrl(obj) {
|
|
539
|
+
if (!this.config.httpEnabled) {
|
|
540
|
+
this.sendTo(obj.from, obj.command, { error: "HTTP not enabled" }, obj.callback);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const msg = obj.message;
|
|
544
|
+
const filename = msg.url.split("/").pop() || "backup";
|
|
545
|
+
try {
|
|
546
|
+
const buf = await this.downloadUrl(msg.url);
|
|
547
|
+
const scripts = await this.parseBuffer(buf, filename);
|
|
548
|
+
this.sendTo(obj.from, obj.command, { scripts }, obj.callback);
|
|
549
|
+
} catch (e) {
|
|
550
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
// ─── SFTP ────────────────────────────────────────────────────────────────
|
|
554
|
+
async handleTestSftp(obj) {
|
|
555
|
+
const msg = obj.message;
|
|
556
|
+
const sftp = new import_ssh2_sftp_client.default();
|
|
557
|
+
try {
|
|
558
|
+
await sftp.connect({ host: msg.host, port: msg.port || 22, username: msg.user, password: msg.password });
|
|
559
|
+
const list = await sftp.list(msg.path || "/");
|
|
560
|
+
const count = list.filter((i) => i.type === "-").length;
|
|
561
|
+
this.sendTo(
|
|
562
|
+
obj.from,
|
|
563
|
+
obj.command,
|
|
564
|
+
{ success: true, message: `Verbunden! ${count} Datei(en) in: ${msg.path || "/"}` },
|
|
565
|
+
obj.callback
|
|
566
|
+
);
|
|
567
|
+
} catch (e) {
|
|
568
|
+
this.sendTo(obj.from, obj.command, { success: false, message: e.message }, obj.callback);
|
|
569
|
+
} finally {
|
|
570
|
+
await sftp.end();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async handleListSftpFiles(obj) {
|
|
574
|
+
if (!this.config.sftpEnabled) {
|
|
575
|
+
this.sendTo(obj.from, obj.command, { error: "SFTP not enabled" }, obj.callback);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
const sftp = new import_ssh2_sftp_client.default();
|
|
579
|
+
try {
|
|
580
|
+
await sftp.connect({
|
|
581
|
+
host: this.config.sftpHost,
|
|
582
|
+
port: this.config.sftpPort || 22,
|
|
583
|
+
username: this.config.sftpUser,
|
|
584
|
+
password: this.config.sftpPassword
|
|
585
|
+
});
|
|
586
|
+
const remotePath = this.config.sftpPath || "/";
|
|
587
|
+
const list = await sftp.list(remotePath);
|
|
588
|
+
const files = list.filter((i) => {
|
|
589
|
+
const n = i.name;
|
|
590
|
+
return i.type === "-" && (n.startsWith("iobroker") || n.startsWith("javascript")) && (n.endsWith(".tar.gz") || n.endsWith(".tar") || n.endsWith(".json") || n.endsWith(".jsonl"));
|
|
591
|
+
}).map((i) => i.name).sort().reverse();
|
|
592
|
+
this.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
595
|
+
} finally {
|
|
596
|
+
await sftp.end();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async handleParseSftpFile(obj) {
|
|
600
|
+
if (!this.config.sftpEnabled) {
|
|
601
|
+
this.sendTo(obj.from, obj.command, { error: "SFTP not enabled" }, obj.callback);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const msg = obj.message;
|
|
605
|
+
const filename = path.basename(msg.filename);
|
|
606
|
+
const remotePath = path.posix.join(this.config.sftpPath || "/", filename);
|
|
607
|
+
const sftp = new import_ssh2_sftp_client.default();
|
|
608
|
+
try {
|
|
609
|
+
await sftp.connect({
|
|
610
|
+
host: this.config.sftpHost,
|
|
611
|
+
port: this.config.sftpPort || 22,
|
|
612
|
+
username: this.config.sftpUser,
|
|
613
|
+
password: this.config.sftpPassword
|
|
614
|
+
});
|
|
615
|
+
const buf = await sftp.get(remotePath);
|
|
616
|
+
const scripts = await this.parseBuffer(buf, filename);
|
|
617
|
+
this.sendTo(obj.from, obj.command, { scripts }, obj.callback);
|
|
618
|
+
} catch (e) {
|
|
619
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
620
|
+
} finally {
|
|
621
|
+
await sftp.end();
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
// ─── WebDAV ──────────────────────────────────────────────────────────────
|
|
625
|
+
async handleTestWebdav(obj) {
|
|
626
|
+
const msg = obj.message;
|
|
627
|
+
try {
|
|
628
|
+
const { createClient: createWebdavClient } = await Promise.resolve().then(() => __toESM(require("webdav")));
|
|
629
|
+
const client = createWebdavClient(msg.url, { username: msg.user, password: msg.password });
|
|
630
|
+
const list = await client.getDirectoryContents(msg.path || "/");
|
|
631
|
+
const arr = Array.isArray(list) ? list : list.data;
|
|
632
|
+
this.sendTo(
|
|
633
|
+
obj.from,
|
|
634
|
+
obj.command,
|
|
635
|
+
{ success: true, message: `Verbunden! ${arr.length} Eintr\xE4ge in: ${msg.path || "/"}` },
|
|
636
|
+
obj.callback
|
|
637
|
+
);
|
|
638
|
+
} catch (e) {
|
|
639
|
+
this.sendTo(obj.from, obj.command, { success: false, message: e.message }, obj.callback);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async handleListWebdavFiles(obj) {
|
|
643
|
+
if (!this.config.webdavEnabled) {
|
|
644
|
+
this.sendTo(obj.from, obj.command, { error: "WebDAV not enabled" }, obj.callback);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
const { createClient: createWebdavClient } = await Promise.resolve().then(() => __toESM(require("webdav")));
|
|
649
|
+
const client = createWebdavClient(this.config.webdavUrl, {
|
|
650
|
+
username: this.config.webdavUser,
|
|
651
|
+
password: this.config.webdavPassword
|
|
652
|
+
});
|
|
653
|
+
const remotePath = this.config.webdavPath || "/";
|
|
654
|
+
const list = await client.getDirectoryContents(remotePath);
|
|
655
|
+
const arr = Array.isArray(list) ? list : list.data;
|
|
656
|
+
const files = arr.filter((i) => {
|
|
657
|
+
const n = i.basename;
|
|
658
|
+
return i.type === "file" && (n.startsWith("iobroker") || n.startsWith("javascript")) && (n.endsWith(".tar.gz") || n.endsWith(".tar") || n.endsWith(".json") || n.endsWith(".jsonl"));
|
|
659
|
+
}).map((i) => i.basename).sort().reverse();
|
|
660
|
+
this.sendTo(obj.from, obj.command, { files, path: remotePath }, obj.callback);
|
|
661
|
+
} catch (e) {
|
|
662
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async handleParseWebdavFile(obj) {
|
|
666
|
+
if (!this.config.webdavEnabled) {
|
|
667
|
+
this.sendTo(obj.from, obj.command, { error: "WebDAV not enabled" }, obj.callback);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const msg = obj.message;
|
|
671
|
+
const filename = path.basename(msg.filename);
|
|
672
|
+
try {
|
|
673
|
+
const { createClient: createWebdavClient } = await Promise.resolve().then(() => __toESM(require("webdav")));
|
|
674
|
+
const client = createWebdavClient(this.config.webdavUrl, {
|
|
675
|
+
username: this.config.webdavUser,
|
|
676
|
+
password: this.config.webdavPassword
|
|
677
|
+
});
|
|
678
|
+
const remotePath = (this.config.webdavPath ? `${this.config.webdavPath}/` : "/") + filename;
|
|
679
|
+
const buf = Buffer.from(await client.getFileContents(remotePath));
|
|
680
|
+
const scripts = await this.parseBuffer(buf, filename);
|
|
681
|
+
this.sendTo(obj.from, obj.command, { scripts }, obj.callback);
|
|
682
|
+
} catch (e) {
|
|
683
|
+
this.sendTo(obj.from, obj.command, { error: e.message }, obj.callback);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
471
686
|
}
|
|
472
687
|
if (require.main !== module) {
|
|
473
688
|
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 \"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\";\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},\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 \"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 msg = obj.message as {\n\t\t\thost: string;\n\t\t\tport: number;\n\t\t\tuser: string;\n\t\t\tpassword: string;\n\t\t\tpath: string;\n\t\t\tsecure: boolean;\n\t\t};\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: msg.host,\n\t\t\t\tport: msg.port || 21,\n\t\t\t\tuser: msg.user || \"anonymous\",\n\t\t\t\tpassword: msg.password || \"\",\n\t\t\t\tsecure: msg.secure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(msg.path || \"/\");\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{ success: true, message: `Verbunden! ${count} Datei(en) gefunden in: ${msg.path || \"/\"}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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 msg = obj.message as {\n\t\t\thost: string;\n\t\t\tshare: string;\n\t\t\tpath: string;\n\t\t\tuser: string;\n\t\t\tpassword: string;\n\t\t\tdomain: string;\n\t\t};\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${msg.host}\\\\${msg.share}`,\n\t\t\tusername: msg.user || \"\",\n\t\t\tpassword: msg.password || \"\",\n\t\t\tdomain: msg.domain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, msg.path || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${msg.host}\\\\${msg.share}${msg.path ? `\\\\${msg.path}` : \"\"}`,\n\t\t\t\t},\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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\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": ";;;;;;;;;;;;;;;;;;;;;;;AAQA,YAAuB;AACvB,SAAoB;AACpB,WAAsB;AACtB,SAAoB;AACpB,gCAAqB;AACrB,uBAA0B;AAC1B,UAAqB;AACrB,yBAAyB;AAGzB,MAAM,OAAO,QAAQ,eAAe;AAEpC,MAAM,gBAAY,4BAAU,8BAAI;AAShC,MAAM,sBAAsB,MAAM,QAAQ;AAAA,EAClC,YAAY,UAAyC,CAAC,GAAG;AAC/D,UAAM;AAAA,MACL,GAAG;AAAA,MACH,MAAM;AAAA,IACP,CAAC;AACD,SAAK,GAAG,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AACxC,SAAK,GAAG,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAC5C,SAAK,GAAG,UAAU,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,EAC3C;AAAA,EAEQ,UAAgB;AACvB,SAAK,IAAI,KAAK,sCAAsC,KAAK,OAAO,cAAc,uBAAuB,EAAE;AAAA,EACxG;AAAA,EAEQ,SAAS,UAA4B;AAC5C,aAAS;AAAA,EACV;AAAA,EAEA,MAAc,UAAU,KAAsC;AAC7D,QAAI,CAAC,IAAI,UAAU;AAClB;AAAA,IACD;AAEA,QAAI;AACH,cAAQ,IAAI,SAAS;AAAA,QACpB,KAAK;AACJ,gBAAM,KAAK,qBAAqB,GAAG;AACnC;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,qBAAqB,GAAG;AACnC;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,wBAAwB,GAAG;AACtC;AAAA,QACD,KAAK;AACJ,eAAK;AAAA,YACJ,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ;AAAA,cACC,cAAc,KAAK,OAAO,iBAAiB;AAAA,cAC3C,YAAY,CAAC,CAAC,KAAK,OAAO;AAAA,cAC1B,YAAY,CAAC,CAAC,KAAK,OAAO;AAAA,YAC3B;AAAA,YACA,IAAI;AAAA,UACL;AACA;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,cAAc,GAAG;AAC5B;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,cAAc,GAAG;AAC5B;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,mBAAmB,GAAG;AACjC;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,mBAAmB,GAAG;AACjC;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,mBAAmB,GAAG;AACjC;AAAA,QACD,KAAK;AACJ,gBAAM,KAAK,mBAAmB,GAAG;AACjC;AAAA,QACD;AACC,eAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,kBAAkB,GAAG,IAAI,QAAQ;AAAA,MAC/E;AAAA,IACD,SAAS,GAAG;AACX,WAAK,IAAI,MAAM,kBAAkB,IAAI,OAAO,KAAM,EAAY,OAAO,EAAE;AACvE,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF;AAAA,EACD;AAAA;AAAA,EAIA,MAAc,qBAAqB,KAAsC;AACxE,QAAI,KAAK,OAAO,iBAAiB,OAAO;AACvC,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,2BAA2B,GAAG,IAAI,QAAQ;AACtF;AAAA,IACD;AACA,UAAM,aAAa,KAAK,OAAO,cAAc;AAC7C,QAAI;AACH,YAAM,aAAa,MAAM,GAAG,QAAQ,YAAY,EAAE,eAAe,MAAM,UAAU,OAAO,CAAC;AACzF,YAAM,UAAU;AAChB,YAAM,QAAQ,QACZ,OAAO,OAAK;AACZ,cAAM,IAAI,OAAO,EAAE,IAAI;AACvB,eACC,EAAE,OAAO,MACR,EAAE,WAAW,UAAU,KAAK,EAAE,WAAW,YAAY,OACrD,EAAE,SAAS,SAAS,KAAK,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,OAAO,KAAK,EAAE,SAAS,QAAQ;AAAA,MAE5F,CAAC,EACA,IAAI,OAAK,OAAO,EAAE,IAAI,CAAC,EACvB,KAAK,EACL,QAAQ;AACV,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,MAAM,WAAW,GAAG,IAAI,QAAQ;AAAA,IAC7E,SAAS,GAAG;AACX,WAAK;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,EAAE,OAAO,6BAA8B,EAAY,OAAO,GAAG;AAAA,QAC7D,IAAI;AAAA,MACL;AAAA,IACD;AAAA,EACD;AAAA,EAEA,MAAc,qBAAqB,KAAsC;AACxE,QAAI,KAAK,OAAO,iBAAiB,OAAO;AACvC,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,2BAA2B,GAAG,IAAI,QAAQ;AACtF;AAAA,IACD;AACA,UAAM,aAAa,KAAK,OAAO,cAAc;AAC7C,UAAM,MAAM,IAAI;AAChB,UAAM,WAAW,KAAK,SAAS,IAAI,QAAQ;AAC3C,UAAM,WAAW,KAAK,KAAK,YAAY,QAAQ;AAC/C,QAAI;AACH,YAAM,MAAM,MAAM,GAAG,SAAS,QAAQ;AACtC,YAAM,UAAU,MAAM,KAAK,YAAY,KAAK,QAAQ;AACpD,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,QAAQ,GAAG,IAAI,QAAQ;AAAA,IAC7D,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF;AAAA,EACD;AAAA,EAEA,MAAc,wBAAwB,KAAsC;AAC3E,UAAM,MAAM,IAAI;AAChB,QAAI;AACH,YAAM,MAAM,OAAO,KAAK,IAAI,MAAM,QAAQ;AAC1C,YAAM,UAAU,MAAM,KAAK,YAAY,KAAK,IAAI,IAAI;AACpD,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,QAAQ,GAAG,IAAI,QAAQ;AAAA,IAC7D,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF;AAAA,EACD;AAAA;AAAA,EAIA,MAAc,cAAc,KAAsC;AACjE,UAAM,MAAM,IAAI;AAQhB,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,WAAO,IAAI,UAAU;AACrB,QAAI;AACH,YAAM,OAAO,OAAO;AAAA,QACnB,MAAM,IAAI;AAAA,QACV,MAAM,IAAI,QAAQ;AAAA,QAClB,MAAM,IAAI,QAAQ;AAAA,QAClB,UAAU,IAAI,YAAY;AAAA,QAC1B,QAAQ,IAAI,UAAU;AAAA,MACvB,CAAC;AACD,YAAM,OAAO,MAAM,OAAO,KAAK,IAAI,QAAQ,GAAG;AAC9C,YAAM,QAAQ,KAAK,OAAO,OAAK,EAAE,SAAS,IAAI,SAAS,IAAI,EAAE;AAC7D,WAAK;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,EAAE,SAAS,MAAM,SAAS,cAAc,KAAK,2BAA2B,IAAI,QAAQ,GAAG,GAAG;AAAA,QAC1F,IAAI;AAAA,MACL;AAAA,IACD,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,SAAS,OAAO,SAAU,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACnG,UAAE;AACD,aAAO,MAAM;AAAA,IACd;AAAA,EACD;AAAA,EAEA,MAAc,cAAc,KAAsC;AACjE,UAAM,MAAM,IAAI;AAQhB,UAAM,MAAM,IAAI,KAAK;AAAA,MACpB,OAAO,OAAO,IAAI,IAAI,KAAK,IAAI,KAAK;AAAA,MACpC,UAAU,IAAI,QAAQ;AAAA,MACtB,UAAU,IAAI,YAAY;AAAA,MAC1B,QAAQ,IAAI,UAAU;AAAA,IACvB,CAAC;AACD,QAAI;AACH,YAAM,QAAQ,MAAM,KAAK,WAAW,KAAK,IAAI,QAAQ,EAAE;AACvD,WAAK;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ;AAAA,UACC,SAAS;AAAA,UACT,SAAS,cAAc,MAAM,MAAM,wBAAqB,IAAI,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,IAAI,KAAK,EAAE;AAAA,QACjH;AAAA,QACA,IAAI;AAAA,MACL;AAAA,IACD,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,SAAS,OAAO,SAAU,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACnG,UAAE;AACD,UAAI,WAAW;AAAA,IAChB;AAAA,EACD;AAAA;AAAA,EAIQ,kBAA8B;AACrC,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,WAAO,IAAI,UAAU;AACrB,WAAO;AAAA,EACR;AAAA,EAEA,MAAc,WAAW,QAAmC;AAC3D,UAAM,OAAO,OAAO;AAAA,MACnB,MAAM,KAAK,OAAO;AAAA,MAClB,MAAM,KAAK,OAAO,WAAW;AAAA,MAC7B,MAAM,KAAK,OAAO,WAAW;AAAA,MAC7B,UAAU,KAAK,OAAO,eAAe;AAAA,MACrC,QAAQ,KAAK,OAAO,aAAa;AAAA,IAClC,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,mBAAmB,KAAsC;AACtE,QAAI,CAAC,KAAK,OAAO,YAAY;AAC5B,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,kBAAkB,GAAG,IAAI,QAAQ;AAC7E;AAAA,IACD;AACA,UAAM,SAAS,KAAK,gBAAgB;AACpC,QAAI;AACH,YAAM,KAAK,WAAW,MAAM;AAC5B,YAAM,aAAa,KAAK,OAAO,WAAW;AAC1C,YAAM,OAAO,MAAM,OAAO,KAAK,UAAU;AACzC,YAAM,QAAQ,KACZ,OAAO,UAAQ;AACf,cAAM,IAAI,KAAK;AACf,eACC,KAAK,SAAS,IAAI,SAAS,SAC1B,EAAE,WAAW,UAAU,KAAK,EAAE,WAAW,YAAY,OACrD,EAAE,SAAS,SAAS,KAAK,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,OAAO,KAAK,EAAE,SAAS,QAAQ;AAAA,MAE5F,CAAC,EACA,IAAI,UAAQ,KAAK,IAAI,EACrB,KAAK,EACL,QAAQ;AACV,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,MAAM,WAAW,GAAG,IAAI,QAAQ;AAAA,IAC7E,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF,UAAE;AACD,aAAO,MAAM;AAAA,IACd;AAAA,EACD;AAAA,EAEA,MAAc,mBAAmB,KAAsC;AACtE,QAAI,CAAC,KAAK,OAAO,YAAY;AAC5B,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,kBAAkB,GAAG,IAAI,QAAQ;AAC7E;AAAA,IACD;AACA,UAAM,MAAM,IAAI;AAChB,UAAM,WAAW,KAAK,SAAS,IAAI,QAAQ;AAC3C,UAAM,aAAa,KAAK,MAAM,KAAK,KAAK,OAAO,WAAW,KAAK,QAAQ;AACvE,UAAM,SAAS,KAAK,gBAAgB;AACpC,QAAI;AACH,YAAM,KAAK,WAAW,MAAM;AAC5B,YAAM,SAAmB,CAAC;AAC1B,YAAM,WAAW,IAAI,4BAAS;AAAA,QAC7B,MAAM,OAAO,MAAM,IAAI;AACtB,iBAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAe,CAAC;AACzE,aAAG;AAAA,QACJ;AAAA,MACD,CAAC;AACD,YAAM,OAAO,WAAW,UAAU,UAAU;AAC5C,YAAM,MAAM,OAAO,OAAO,MAAM;AAChC,YAAM,UAAU,MAAM,KAAK,YAAY,KAAK,QAAQ;AACpD,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,QAAQ,GAAG,IAAI,QAAQ;AAAA,IAC7D,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF,UAAE;AACD,aAAO,MAAM;AAAA,IACd;AAAA,EACD;AAAA;AAAA,EAIQ,kBAA+B;AACtC,WAAO,IAAI,KAAK;AAAA,MACf,OAAO,OAAO,KAAK,OAAO,OAAO,KAAK,KAAK,OAAO,QAAQ;AAAA,MAC1D,UAAU,KAAK,OAAO,WAAW;AAAA,MACjC,UAAU,KAAK,OAAO,eAAe;AAAA,MACrC,QAAQ,KAAK,OAAO,aAAa;AAAA,IAClC,CAAC;AAAA,EACF;AAAA,EAEQ,WAAW,KAAkB,SAAoC;AACxE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,UAAI,QAAQ,SAAS,CAAC,KAAmB,UAAoB;AAC5D,YAAI,KAAK;AACR,iBAAO,GAAG;AAAA,QACX,OAAO;AACN,kBAAQ,KAAK;AAAA,QACd;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEQ,YAAY,KAAkB,UAAmC;AACxE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACvC,UAAI,SAAS,UAAU,CAAC,KAAmB,SAAiB;AAC3D,YAAI,KAAK;AACR,iBAAO,GAAG;AAAA,QACX,OAAO;AACN,kBAAQ,IAAI;AAAA,QACb;AAAA,MACD,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AAAA,EAEA,MAAc,mBAAmB,KAAsC;AACtE,QAAI,CAAC,KAAK,OAAO,YAAY;AAC5B,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,kBAAkB,GAAG,IAAI,QAAQ;AAC7E;AAAA,IACD;AACA,UAAM,MAAM,KAAK,gBAAgB;AACjC,QAAI;AACH,YAAM,UAAU,KAAK,OAAO,WAAW;AACvC,YAAM,UAAU,MAAM,KAAK,WAAW,KAAK,OAAO;AAClD,YAAM,QAAQ,QACZ,OAAO,OAAK;AACZ,gBACE,EAAE,WAAW,UAAU,KAAK,EAAE,WAAW,YAAY,OACrD,EAAE,SAAS,SAAS,KAAK,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,OAAO,KAAK,EAAE,SAAS,QAAQ;AAAA,MAE5F,CAAC,EACA,KAAK,EACL,QAAQ;AACV,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,MAAM,QAAQ,GAAG,IAAI,QAAQ;AAAA,IAC1E,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF,UAAE;AACD,UAAI,WAAW;AAAA,IAChB;AAAA,EACD;AAAA,EAEA,MAAc,mBAAmB,KAAsC;AACtE,QAAI,CAAC,KAAK,OAAO,YAAY;AAC5B,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAO,kBAAkB,GAAG,IAAI,QAAQ;AAC7E;AAAA,IACD;AACA,UAAM,MAAM,IAAI;AAChB,UAAM,WAAW,KAAK,SAAS,IAAI,QAAQ;AAC3C,UAAM,UAAU,KAAK,OAAO,WAAW;AACvC,UAAM,WAAW,UAAU,GAAG,OAAO,KAAK,QAAQ,KAAK;AACvD,UAAM,MAAM,KAAK,gBAAgB;AACjC,QAAI;AACH,YAAM,MAAM,MAAM,KAAK,YAAY,KAAK,QAAQ;AAChD,YAAM,UAAU,MAAM,KAAK,YAAY,KAAK,QAAQ;AACpD,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,QAAQ,GAAG,IAAI,QAAQ;AAAA,IAC7D,SAAS,GAAG;AACX,WAAK,OAAO,IAAI,MAAM,IAAI,SAAS,EAAE,OAAQ,EAAY,QAAQ,GAAG,IAAI,QAAQ;AAAA,IACjF,UAAE;AACD,UAAI,WAAW;AAAA,IAChB;AAAA,EACD;AAAA;AAAA,EAIA,MAAc,YAAY,KAAa,UAA0C;AAChF,UAAM,OAAO,SAAS,YAAY;AAClC,QAAI,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,MAAM,KAAK,KAAK,SAAS,MAAM,GAAG;AAC/E,aAAO,KAAK,gBAAgB,KAAK,KAAK,SAAS,MAAM,KAAK,CAAC,KAAK,SAAS,SAAS,CAAC;AAAA,IACpF;AACA,WAAO,KAAK,iBAAiB,IAAI,SAAS,MAAM,GAAG,QAAQ;AAAA,EAC5D;AAAA,EAEA,MAAc,gBAAgB,KAAa,YAA6C;AACvF,UAAM,SAAS,MAAM,GAAG,QAAQ,KAAK,KAAK,GAAG,OAAO,GAAG,iBAAiB,CAAC;AACzE,UAAM,UAAU,KAAK,KAAK,QAAQ,cAAc,aAAa,KAAK,KAAK,EAAE;AACzE,QAAI;AACH,YAAM,GAAG,UAAU,SAAS,GAAG;AAE/B,YAAM,cAAc,aAAa,QAAQ;AACzC,UAAI;AACH,cAAM;AAAA,UACL,OAAO,WAAW,KAAK,OAAO,SAAS,MAAM;AAAA,QAG9C;AAAA,MACD,QAAQ;AACP,cAAM,UAAU,OAAO,WAAW,KAAK,OAAO,SAAS,MAAM,eAAe,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC7F;AAEA,YAAM,UAAU,CAAC,iBAAiB,gBAAgB,gBAAgB,aAAa;AAC/E,YAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ,OAAO;AACjD,UAAI,CAAC,OAAO;AACX,cAAM,IAAI;AAAA,UACT;AAAA,QACD;AAAA,MACD;AAEA,YAAM,UAAU,MAAM,GAAG,SAAS,OAAO,MAAM;AAC/C,aAAO,KAAK,iBAAiB,SAAS,KAAK,SAAS,KAAK,CAAC;AAAA,IAC3D,UAAE;AACD,YAAM,GAAG,GAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACrE;AAAA,EACD;AAAA,EAEA,MAAc,SAAS,KAAa,OAAyC;AAC5E,UAAM,OAAO,OAAO,MAAsC;AACzD,UAAI;AACJ,UAAI;AACH,kBAAW,MAAM,GAAG,QAAQ,GAAG,EAAE,eAAe,MAAM,UAAU,OAAO,CAAC;AAAA,MACzE,QAAQ;AACP,eAAO;AAAA,MACR;AACA,iBAAW,KAAK,SAAS;AACxB,cAAM,IAAI,KAAK,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC;AACrC,YAAI,EAAE,YAAY,GAAG;AACpB,gBAAM,QAAQ,MAAM,KAAK,CAAC;AAC1B,cAAI,OAAO;AACV,mBAAO;AAAA,UACR;AAAA,QACD,WAAW,MAAM,SAAS,OAAO,EAAE,IAAI,CAAC,GAAG;AAC1C,iBAAO;AAAA,QACR;AAAA,MACD;AACA,aAAO;AAAA,IACR;AACA,WAAO,KAAK,GAAG;AAAA,EAChB;AAAA,EAEQ,iBAAiB,SAAiB,UAAiC;AAC1E,UAAM,UAAyB,CAAC;AAChC,UAAM,UAAU,QAAQ,UAAU;AAElC,UAAM,UACL,SAAS,SAAS,QAAQ,KACzB,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,WAAW,QAAQ,KAAK,QAAQ,SAAS,KAAK;AAEpF,QAAI,SAAS;AACZ,iBAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACvC,cAAM,IAAI,KAAK,KAAK;AACpB,YAAI,CAAC,GAAG;AACP;AAAA,QACD;AACA,YAAI;AACH,gBAAM,OAAO,KAAK,MAAM,CAAC;AACzB,eAAK;AAAA,YACH,KAAK,OAAO,KAAK;AAAA,YACjB,KAAK,SAAS,KAAK,OAAO;AAAA,YAC3B;AAAA,UACD;AAAA,QACD,QAAQ;AAAA,QAER;AAAA,MACD;AAAA,IACD,OAAO;AACN,YAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC1C,aAAK,YAAY,GAAG,GAA8B,OAAO;AAAA,MAC1D;AAAA,IACD;AAEA,WAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,YAAY,EAAE,cAAc,EAAE,KAAK,YAAY,CAAC,CAAC;AAAA,EACvF;AAAA,EAEQ,YAAY,KAAa,KAAc,SAA8B;AA/e9E;AAgfE,QAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,QAAQ,MAAM;AACpD;AAAA,IACD;AACA,UAAM,IAAI;AAEV,QAAI,CAAC,WAAW,UAAU,UAAU,MAAM,EAAE,SAAS,EAAE,IAAc,GAAG;AACvE;AAAA,IACD;AACA,QAAI,EAAE,SAAS,YAAY,CAAC,IAAI,WAAW,YAAY,GAAG;AACzD;AAAA,IACD;AAEA,UAAM,IAAI,EAAE;AACZ,QAAI,CAAC,KAAM,EAAE,eAAe,UAAa,EAAE,WAAW,QAAY;AACjE;AAAA,IACD;AAEA,UAAM,gBAAgB,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;AACxE,UAAM,aAAa,cAAc,YAAY;AAC7C,QAAI;AACJ,QAAI,WAAW,SAAS,IAAI,KAAK,WAAW,SAAS,YAAY,GAAG;AACnE,cAAQ;AAAA,IACT,WAAW,WAAW,SAAS,SAAS,GAAG;AAC1C,cAAQ;AAAA,IACT,WAAW,WAAW,SAAS,OAAO,GAAG;AACxC,cAAQ;AAAA,IACT,OAAO;AACN,cAAQ;AAAA,IACT;AAEA,QAAI;AACJ,UAAM,UAAU,EAAE;AAClB,QAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACpD,YAAM,IAAI;AACV,aAAO,EAAE,MAAM,EAAE,MAAM,OAAO,OAAO,CAAC,EAAE,CAAC,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,KAAK;AAAA,IACvE,OAAO;AACN,aAAO,OAAO,YAAY,YAAY,UAAU,WAAW,SAAI,MAAM,GAAG,EAAE,IAAI,MAAnB,YAAwB;AAAA,IACpF;AAEA,UAAM,aAAa,IAAI,WAAW,YAAY,IAAI,IAAI,MAAM,EAAE,IAAI;AAElE,YAAQ,KAAK;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AAAA,IACnD,CAAC;AAAA,EACF;AACD;AAEA,IAAI,QAAQ,SAAS,QAAQ;AAC5B,SAAO,UAAU,CAAC,YAAuD,IAAI,cAAc,OAAO;AACnG,OAAO;AACN,GAAC,MAAM,IAAI,cAAc,GAAG;AAC7B;",
|
|
6
|
-
"names": []
|
|
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 \"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 msg = obj.message as {\n\t\t\thost: string;\n\t\t\tport: number;\n\t\t\tuser: string;\n\t\t\tpassword: string;\n\t\t\tpath: string;\n\t\t\tsecure: boolean;\n\t\t};\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: msg.host,\n\t\t\t\tport: msg.port || 21,\n\t\t\t\tuser: msg.user || \"anonymous\",\n\t\t\t\tpassword: msg.password || \"\",\n\t\t\t\tsecure: msg.secure || false,\n\t\t\t});\n\t\t\tconst list = await client.list(msg.path || \"/\");\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{ success: true, message: `Verbunden! ${count} Datei(en) gefunden in: ${msg.path || \"/\"}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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 msg = obj.message as {\n\t\t\thost: string;\n\t\t\tshare: string;\n\t\t\tpath: string;\n\t\t\tuser: string;\n\t\t\tpassword: string;\n\t\t\tdomain: string;\n\t\t};\n\t\tconst smb = new SMB2({\n\t\t\tshare: `\\\\\\\\${msg.host}\\\\${msg.share}`,\n\t\t\tusername: msg.user || \"\",\n\t\t\tpassword: msg.password || \"\",\n\t\t\tdomain: msg.domain || \"\",\n\t\t});\n\t\ttry {\n\t\t\tconst files = await this.smbReaddir(smb, msg.path || \"\");\n\t\t\tthis.sendTo(\n\t\t\t\tobj.from,\n\t\t\t\tobj.command,\n\t\t\t\t{\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tmessage: `Verbunden! ${files.length} Eintr\u00E4ge in: \\\\\\\\${msg.host}\\\\${msg.share}${msg.path ? `\\\\${msg.path}` : \"\"}`,\n\t\t\t\t},\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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, { path: 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, { path: null }, 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(url: string): Promise<Buffer> {\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 msg = obj.message as { host: string; port: number; user: string; password: string; path: string };\n\t\tconst sftp = new SftpClient();\n\t\ttry {\n\t\t\tawait sftp.connect({ host: msg.host, port: msg.port || 22, username: msg.user, password: msg.password });\n\t\t\tconst list = await sftp.list(msg.path || \"/\");\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{ success: true, message: `Verbunden! ${count} Datei(en) in: ${msg.path || \"/\"}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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\tconst msg = obj.message as { url: string; user: string; password: string; path: string };\n\t\ttry {\n\t\t\tconst { createClient: createWebdavClient } = await import(\"webdav\");\n\t\t\tconst client = createWebdavClient(msg.url, { username: msg.user, password: msg.password });\n\t\t\tconst list = await client.getDirectoryContents(msg.path || \"/\");\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{ success: true, message: `Verbunden! ${arr.length} Eintr\u00E4ge in: ${msg.path || \"/\"}` },\n\t\t\t\tobj.callback,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tthis.sendTo(obj.from, obj.command, { success: false, message: (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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",
|
|
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.6",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.0.6": {
|
|
7
|
+
"en": "Add HTTP/SFTP/WebDAV backup sources; multi-select scripts with Ctrl+click and ZIP download; remember last loaded backup; auto-detect local backup path",
|
|
8
|
+
"de": "HTTP/SFTP/WebDAV als Backup-Quellen; Mehrfachauswahl mit Strg+Klick und ZIP-Download; letztes Backup merken; lokalen Backup-Pfad auto-erkennen",
|
|
9
|
+
"ru": "Добавлены источники HTTP/SFTP/WebDAV; множественный выбор с Ctrl+клик и ZIP-загрузка; запоминание последнего бэкапа; авто-определение пути",
|
|
10
|
+
"pt": "Fontes HTTP/SFTP/WebDAV; seleção múltipla com Ctrl+clique e download ZIP; memorizar último backup; deteção automática do caminho",
|
|
11
|
+
"nl": "HTTP/SFTP/WebDAV bronnen; meervoudige selectie met Ctrl+klik en ZIP-download; laatste backup onthouden; automatische paddetectie",
|
|
12
|
+
"fr": "Sources HTTP/SFTP/WebDAV; sélection multiple avec Ctrl+clic et téléchargement ZIP; mémoriser le dernier backup; détection auto du chemin",
|
|
13
|
+
"it": "Sorgenti HTTP/SFTP/WebDAV; selezione multipla con Ctrl+clic e download ZIP; ricordare ultimo backup; rilevamento automatico percorso",
|
|
14
|
+
"es": "Fuentes HTTP/SFTP/WebDAV; selección múltiple con Ctrl+clic y descarga ZIP; recordar último backup; detección automática de ruta",
|
|
15
|
+
"pl": "Źródła HTTP/SFTP/WebDAV; wielokrotny wybór Ctrl+klik i pobieranie ZIP; zapamiętywanie ostatniego backupu; auto-wykrywanie ścieżki",
|
|
16
|
+
"uk": "Джерела HTTP/SFTP/WebDAV; множинний вибір Ctrl+клік та ZIP-завантаження; запам'ятовування останнього бекапу; авто-визначення шляху",
|
|
17
|
+
"zh-cn": "添加 HTTP/SFTP/WebDAV 备份来源;Ctrl+单击多选及 ZIP 下载;记住最后加载的备份;自动检测本地备份路径"
|
|
18
|
+
},
|
|
6
19
|
"0.0.5": {
|
|
7
20
|
"en": "Add FTP/SMB as optional backup sources with test button; make local source optional; configurable Beaufort threshold",
|
|
8
21
|
"de": "FTP/SMB als optionale Backup-Quellen mit Testbutton; lokale Quelle optional schaltbar; SMB-Versionshinweis",
|
|
@@ -155,8 +168,32 @@
|
|
|
155
168
|
"smbPath": "",
|
|
156
169
|
"smbUser": "",
|
|
157
170
|
"smbPassword": "",
|
|
158
|
-
"smbDomain": ""
|
|
171
|
+
"smbDomain": "",
|
|
172
|
+
"httpEnabled": false,
|
|
173
|
+
"sftpEnabled": false,
|
|
174
|
+
"sftpHost": "",
|
|
175
|
+
"sftpPort": 22,
|
|
176
|
+
"sftpUser": "",
|
|
177
|
+
"sftpPassword": "",
|
|
178
|
+
"sftpPath": "/",
|
|
179
|
+
"webdavEnabled": false,
|
|
180
|
+
"webdavUrl": "",
|
|
181
|
+
"webdavUser": "",
|
|
182
|
+
"webdavPassword": "",
|
|
183
|
+
"webdavPath": "/"
|
|
159
184
|
},
|
|
185
|
+
"protectedNative": [
|
|
186
|
+
"ftpPassword",
|
|
187
|
+
"smbPassword",
|
|
188
|
+
"sftpPassword",
|
|
189
|
+
"webdavPassword"
|
|
190
|
+
],
|
|
191
|
+
"encryptedNative": [
|
|
192
|
+
"ftpPassword",
|
|
193
|
+
"smbPassword",
|
|
194
|
+
"sftpPassword",
|
|
195
|
+
"webdavPassword"
|
|
196
|
+
],
|
|
160
197
|
"objects": [],
|
|
161
198
|
"instanceObjects": []
|
|
162
199
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.script-restore",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Restore ioBroker scripts from backup archives",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ipod86",
|
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@iobroker/adapter-core": "^3.3.2",
|
|
29
29
|
"@marsaud/smb2": "^0.18.0",
|
|
30
|
-
"basic-ftp": "^5.2.0"
|
|
30
|
+
"basic-ftp": "^5.2.0",
|
|
31
|
+
"ssh2-sftp-client": "^12.1.1",
|
|
32
|
+
"webdav": "^5.9.0"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
35
|
"@alcalzone/release-script": "^5.1.1",
|
|
@@ -40,6 +42,7 @@
|
|
|
40
42
|
"@tsconfig/node18": "^18.2.6",
|
|
41
43
|
"@types/iobroker": "npm:@iobroker/types@^7.1.0",
|
|
42
44
|
"@types/node": "^25.5.2",
|
|
45
|
+
"@types/ssh2-sftp-client": "^9.0.6",
|
|
43
46
|
"rimraf": "^6.1.3",
|
|
44
47
|
"source-map-support": "^0.5.21",
|
|
45
48
|
"ts-node": "^10.9.2",
|