saltcorn-samba 0.4.2 → 0.4.3
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/CHANGELOG.md +36 -0
- package/index.js +99 -0
- package/package.json +1 -1
- package/smb-client.js +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to `saltcorn-samba` are documented here.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.4.3] – 2026-07-05
|
|
8
|
+
|
|
9
|
+
### Fixed – **Unklare Meldung bei nicht-existierendem Basispfad**
|
|
10
|
+
|
|
11
|
+
Wenn ein Basispfad angegeben wurde, der auf dem Server nicht existiert
|
|
12
|
+
(oder für den angemeldeten Benutzer nicht sichtbar ist), meldete die
|
|
13
|
+
Test-Route bisher nur die rohe Server-Antwort:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
CREATE failed: STATUS_OBJECT_NAME_NOT_FOUND (ENOENT)
|
|
17
|
+
CREATE failed: STATUS_OBJECT_PATH_NOT_FOUND (ENOENT)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Daraus konnte der Benutzer nicht erkennen, ob es sich um einen Tippfehler,
|
|
21
|
+
um einen Klein-/Großschreibungs-Konflikt (Samba mit `case sensitive = yes`)
|
|
22
|
+
oder um eine Berechtigung handelt.
|
|
23
|
+
|
|
24
|
+
**Neu:**
|
|
25
|
+
|
|
26
|
+
1. Die Test-Route prüft den Basispfad jetzt zuerst mit `stat()`, bevor sie
|
|
27
|
+
`readdir()` versucht. Fehlt der Ordner, wird eine deutsche Meldung
|
|
28
|
+
zurückgegeben, die den betroffenen Pfad, die Freigabe und typische
|
|
29
|
+
Ursachen (Schreibweise, Groß-/Kleinschreibung, Zugriffsrechte) nennt.
|
|
30
|
+
2. Zusätzlich liefert die Route ein `diagnostics`-Objekt zurück, das
|
|
31
|
+
den fehlenden Segmentnamen, den übergeordneten Pfad und — sofern der
|
|
32
|
+
übergeordnete Ordner auflistbar ist — dessen tatsächliche Einträge
|
|
33
|
+
enthält. Das Test-UI hebt ähnlich geschriebene Nachbareinträge hervor,
|
|
34
|
+
damit Tippfehler oder Case-Mismatch sofort sichtbar werden.
|
|
35
|
+
3. Der `smb-client.js`-`readdir()`-Wrapper mappt `OBJECT_NAME_NOT_FOUND`
|
|
36
|
+
und `OBJECT_PATH_NOT_FOUND` ebenfalls auf eine deutsche Meldung, damit
|
|
37
|
+
auch der File-Manager (außerhalb der Test-Route) verständlich über
|
|
38
|
+
fehlende Ordner informiert.
|
|
39
|
+
|
|
40
|
+
**Kein Config-Migrationsschritt nötig.** Wer die neuen Diagnose-Boxen
|
|
41
|
+
sehen will, muss lediglich `pv-host/saltcorn-samba@0.4.3` einspielen.
|
|
42
|
+
|
|
7
43
|
## [0.4.2] – 2026-07-05
|
|
8
44
|
|
|
9
45
|
### Fixed – **Dropdown-Felder zeigen `[object Object]` statt Optionen**
|
package/index.js
CHANGED
|
@@ -184,6 +184,40 @@ window.sambaTestConn = async function(btn) {
|
|
|
184
184
|
'Fehler: <code>' + String(data && data.error || 'Unbekannt').replace(/[<>&]/g,'?') + '</code>' +
|
|
185
185
|
(data && data.code ? ' <span class="text-muted">(' + data.code + ')</span>' : '') + '<br>' +
|
|
186
186
|
(data && data.hint ? '<div style="margin-top:.4rem"><b>Hinweis:</b> ' + String(data.hint).replace(/[<>&]/g,'?') + '</div>' : '') +
|
|
187
|
+
((function(){
|
|
188
|
+
var d = data && data.diagnostics;
|
|
189
|
+
if (!d) return '';
|
|
190
|
+
var esc = function(s){ return String(s==null?'':s).replace(/[<>&]/g,function(c){return {'<':'<','>':'>','&':'&'}[c];}); };
|
|
191
|
+
var lc = String(d.missing_segment||'').toLowerCase();
|
|
192
|
+
var sibs = Array.isArray(d.siblings) ? d.siblings : [];
|
|
193
|
+
var similar = sibs.filter(function(s){
|
|
194
|
+
var n = String(s.name||'').toLowerCase();
|
|
195
|
+
if (!n || !lc) return false;
|
|
196
|
+
if (n === lc) return true;
|
|
197
|
+
if (n.indexOf(lc) !== -1 || lc.indexOf(n) !== -1) return true;
|
|
198
|
+
// simple Levenshtein-1 heuristic: same length ±1 and share prefix
|
|
199
|
+
return Math.abs(n.length - lc.length) <= 1 && n.substring(0, Math.min(3, n.length)) === lc.substring(0, Math.min(3, lc.length));
|
|
200
|
+
});
|
|
201
|
+
var box = '<div style="margin-top:.4rem;padding:.4rem .6rem;background:#f8d7da;border:1px solid #f5c2c7;border-radius:.25rem">';
|
|
202
|
+
box += '<b>Diagnose:</b> Der Server meldet, dass \u201e<code>' + esc(d.missing_segment) + '</code>\u201c im Ordner \u201e<code>' + esc(d.parent_path) + '</code>\u201c nicht existiert.';
|
|
203
|
+
if (!d.parent_listable) {
|
|
204
|
+
box += '<br><span class="text-muted">(Der übergeordnete Ordner konnte nicht aufgelistet werden — Share-Root-Auflistung ist mit smb3-client aktuell blockiert.)</span>';
|
|
205
|
+
} else if (sibs.length === 0) {
|
|
206
|
+
box += '<br>Der übergeordnete Ordner ist leer.';
|
|
207
|
+
} else {
|
|
208
|
+
if (similar.length) {
|
|
209
|
+
box += '<br><b>Ähnliche Einträge, die tatsächlich existieren:</b><ul style="margin:.3rem 0 .3rem 1rem">';
|
|
210
|
+
similar.slice(0, 10).forEach(function(s){ box += '<li>' + (s.isDirectory?'📁 ':'📄 ') + '<code>' + esc(s.name) + '</code></li>'; });
|
|
211
|
+
box += '</ul>';
|
|
212
|
+
}
|
|
213
|
+
box += '<details style="margin-top:.3rem"><summary>Alle Einträge im Ordner \u201e' + esc(d.parent_path) + '\u201c anzeigen (' + sibs.length + ')</summary><ul style="margin:.3rem 0 0 1rem">';
|
|
214
|
+
sibs.slice(0, 100).forEach(function(s){ box += '<li>' + (s.isDirectory?'📁 ':'📄 ') + '<code>' + esc(s.name) + '</code></li>'; });
|
|
215
|
+
if (sibs.length > 100) box += '<li><i>… (' + (sibs.length - 100) + ' weitere)</i></li>';
|
|
216
|
+
box += '</ul></details>';
|
|
217
|
+
}
|
|
218
|
+
box += '</div>';
|
|
219
|
+
return box;
|
|
220
|
+
})()) +
|
|
187
221
|
(a.server ? (
|
|
188
222
|
'<details style="margin-top:.4rem"><summary>Versuchte Verbindungsdaten</summary>' +
|
|
189
223
|
'<table class="table table-sm" style="margin-top:.4rem">' +
|
|
@@ -797,6 +831,70 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
797
831
|
// (TCP + Negotiate + Session + TREE_CONNECT + Auth) works, which
|
|
798
832
|
// is all the connection test actually promises.
|
|
799
833
|
const rel = testCfg.base_path ? sanitizeRelativePath(testCfg.base_path) : "";
|
|
834
|
+
// When a base_path is set, verify it exists and is a directory
|
|
835
|
+
// BEFORE trying to enumerate it. This turns the opaque
|
|
836
|
+
// "CREATE failed: STATUS_OBJECT_NAME_NOT_FOUND" into a clear
|
|
837
|
+
// "Basispfad existiert nicht" hint the user can act on.
|
|
838
|
+
if (rel) {
|
|
839
|
+
let st;
|
|
840
|
+
try {
|
|
841
|
+
st = await client.stat(rel);
|
|
842
|
+
} catch (statErr) {
|
|
843
|
+
const smsg = String((statErr && statErr.message) || statErr || "");
|
|
844
|
+
if (/OBJECT_NAME_NOT_FOUND|OBJECT_PATH_NOT_FOUND|ENOENT|STATUS_NO_SUCH_FILE/i.test(smsg)) {
|
|
845
|
+
// Try to list the parent directory so we can *show* the
|
|
846
|
+
// user what the server actually reports at that level.
|
|
847
|
+
// This helps distinguish typo vs. case-mismatch vs.
|
|
848
|
+
// permission-hidden entry. If the parent is the share
|
|
849
|
+
// root we can't list it (known smb3-client / Samba bug),
|
|
850
|
+
// so we just skip the sibling probe in that case.
|
|
851
|
+
const parts = rel.split("/").filter(Boolean);
|
|
852
|
+
const missing = parts[parts.length - 1];
|
|
853
|
+
const parent = parts.slice(0, -1).join("/");
|
|
854
|
+
let siblings = null;
|
|
855
|
+
if (parent) {
|
|
856
|
+
try {
|
|
857
|
+
const listing = await client.readdir(parent);
|
|
858
|
+
siblings = Array.isArray(listing)
|
|
859
|
+
? listing.map((d) => ({
|
|
860
|
+
name: d && (d.name || d),
|
|
861
|
+
isDirectory: !!(d && (d.isDirectory === true || (typeof d.isDirectory === "function" && d.isDirectory()))),
|
|
862
|
+
}))
|
|
863
|
+
: null;
|
|
864
|
+
} catch (_) {
|
|
865
|
+
siblings = null;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
const hint =
|
|
869
|
+
"Der Basispfad \u201e" + rel + "\u201c existiert auf der " +
|
|
870
|
+
"Freigabe \u201e" + testCfg.share + "\u201c nicht " +
|
|
871
|
+
"(oder ist f\u00fcr den angemeldeten Benutzer nicht " +
|
|
872
|
+
"sichtbar). Bitte Schreibweise, Gro\u00df-/Kleinschreibung " +
|
|
873
|
+
"und Zugriffsrechte pr\u00fcfen. Der Basispfad ist relativ " +
|
|
874
|
+
"zur Freigabe \u2014 also z.\u202fB. \u201eprojekte/2026\u201c, " +
|
|
875
|
+
"nicht \u201e/mnt/\u2026\u201c.";
|
|
876
|
+
const e = new Error(hint);
|
|
877
|
+
e.cause = statErr;
|
|
878
|
+
e.code = "BASE_PATH_NOT_FOUND";
|
|
879
|
+
e.diagnostics = {
|
|
880
|
+
missing_segment: missing,
|
|
881
|
+
parent_path: parent || "(Share-Root)",
|
|
882
|
+
parent_listable: siblings !== null,
|
|
883
|
+
siblings: siblings,
|
|
884
|
+
};
|
|
885
|
+
throw e;
|
|
886
|
+
}
|
|
887
|
+
throw statErr;
|
|
888
|
+
}
|
|
889
|
+
if (st && st.isDirectory === false && st.isFile === true) {
|
|
890
|
+
const e = new Error(
|
|
891
|
+
"Der Basispfad \u201e" + rel + "\u201c ist eine Datei, kein " +
|
|
892
|
+
"Verzeichnis. Bitte tragen Sie einen Ordnernamen ein."
|
|
893
|
+
);
|
|
894
|
+
e.code = "BASE_PATH_NOT_A_DIR";
|
|
895
|
+
throw e;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
800
898
|
try {
|
|
801
899
|
return await client.readdir(rel);
|
|
802
900
|
} catch (err) {
|
|
@@ -890,6 +988,7 @@ code{background:#f4f4f4;padding:2px 6px;border-radius:3px;word-break:break-all}<
|
|
|
890
988
|
error: msg,
|
|
891
989
|
code,
|
|
892
990
|
hint,
|
|
991
|
+
diagnostics: (err && err.diagnostics) || undefined,
|
|
893
992
|
attempted: {
|
|
894
993
|
server: testCfg.server,
|
|
895
994
|
share: testCfg.share,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saltcorn-samba",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Saltcorn plugin: browse, upload, rename and delete files on a Samba/CIFS share via SMB 3.1.1 (AES-CMAC signing, optional encryption). File-manager view, directory tree, inline PDF viewer, external-app open (smb://).",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
package/smb-client.js
CHANGED
|
@@ -304,6 +304,28 @@ async function buildClient(config) {
|
|
|
304
304
|
e.cause = err;
|
|
305
305
|
throw e;
|
|
306
306
|
}
|
|
307
|
+
// A missing directory (either the base_path itself or a
|
|
308
|
+
// sub-directory the caller asked to list) surfaces as CREATE
|
|
309
|
+
// failing with STATUS_OBJECT_NAME_NOT_FOUND / OBJECT_PATH_NOT_FOUND
|
|
310
|
+
// (both reported by smb3-client with an ENOENT tail). The raw
|
|
311
|
+
// "CREATE failed: STATUS_OBJECT_NAME_NOT_FOUND (ENOENT)" is not
|
|
312
|
+
// actionable for end users, so rewrap it into a German hint that
|
|
313
|
+
// names the actual missing path.
|
|
314
|
+
const isMissing =
|
|
315
|
+
/OBJECT_NAME_NOT_FOUND|OBJECT_PATH_NOT_FOUND|ENOENT|STATUS_NO_SUCH_FILE/i.test(msg);
|
|
316
|
+
if (isMissing) {
|
|
317
|
+
const shown = full.replace(/^[^/]+\/?/, "") || "(Share-Root)";
|
|
318
|
+
const e = new Error(
|
|
319
|
+
"Der Pfad „" + shown + "“ existiert auf der Freigabe „" +
|
|
320
|
+
shareName + "“ nicht (oder ist für den angemeldeten Benutzer " +
|
|
321
|
+
"nicht sichtbar). Bitte Schreibweise, Groß-/Kleinschreibung " +
|
|
322
|
+
"und Zugriffsrechte prüfen. Ursprüngliche Server-Antwort: " +
|
|
323
|
+
(msg || "CREATE failed")
|
|
324
|
+
);
|
|
325
|
+
e.cause = err;
|
|
326
|
+
e.code = "ENOENT";
|
|
327
|
+
throw e;
|
|
328
|
+
}
|
|
307
329
|
throw err;
|
|
308
330
|
}
|
|
309
331
|
// Parallel enrichment. Bounded to a reasonable concurrency to avoid
|