aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/webdav.js
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-10-02 21:25:46 +0200
committerAgando <roduccomm@gmail.com>2026-10-02 21:25:46 +0200
commit6a0cb8cc84ad2cf9bf17d008823d07e5958c6e0a (patch)
tree05994acc3e40422157469d06c0108f7b8c136d03 /modules/ext/webdav.js
parent9a81c96b5a3941898f99665335ad679ff2105666 (diff)
newOS4 v4.6.18: nginx dav trailing-slash semantics
Discovered by dav-health-check.sh E2E (10/10 pass now): - nginx dav module REQUIRES trailing slash on MKCOL (no-slash → 409) and on directory DELETE (no-slash → 409/404); existing dir + slash MKCOL → 405 (the real 'already exists' signal) - _normalizeRemotePath now PRESERVES trailing slashes (collection ops) - _ensureDir MKCOLs with trailing slash - _deleteFile retries with slash on 409/404 (collection delete) - health-check expectations corrected (405 = existing, slash required)
Diffstat (limited to 'modules/ext/webdav.js')
-rw-r--r--modules/ext/webdav.js24
1 files changed, 20 insertions, 4 deletions
diff --git a/modules/ext/webdav.js b/modules/ext/webdav.js
index 5bcc4ee..e989ac9 100644
--- a/modules/ext/webdav.js
+++ b/modules/ext/webdav.js
@@ -194,14 +194,19 @@ class WebDAVModule {
}
p = p.split('?')[0].split('#')[0];
if (!p.startsWith('/')) p = '/' + p;
- // Collapse duplicate slashes and resolve '..' / '.' segments
+ // Collapse duplicate slashes and resolve '..' / '.' segments.
+ // PRESERVE a trailing slash — nginx's dav module needs it for
+ // collection operations (MKCOL/DELETE on directories).
+ const trailing = /\/+$/.test(p);
const out = [];
for (const seg of p.split('/')) {
if (seg === '' || seg === '.') continue;
if (seg === '..') { out.pop(); continue; }
out.push(seg);
}
- return '/' + out.join('/');
+ let res = '/' + out.join('/');
+ if (trailing && res !== '/') res += '/';
+ return res;
}
_absoluteUrl(remotePath) {
@@ -414,7 +419,10 @@ class WebDAVModule {
for (let i = deepest; i < segments.length; i++) {
current += '/' + segments[i];
try {
- const attempt = await this._webdavRequest('MKCOL', current);
+ // nginx dav requires a TRAILING SLASH on MKCOL for the
+ // collection itself (no-slash → 409 'conflict'). Verified
+ // live 2026-10-02: no-slash 409, with-slash 201.
+ const attempt = await this._webdavRequest('MKCOL', current + '/');
if (attempt.ok || attempt.status === 405 || attempt.status === 301 || attempt.status === 302) created = true;
} catch (e) { /* ignore — component may exist */ }
}
@@ -422,8 +430,16 @@ class WebDAVModule {
}
async _deleteFile(remotePath) {
+ const p = this._normalizeRemotePath(remotePath);
const resp = await this._webdavRequest('DELETE', remotePath);
- return resp.ok || resp.status === 204 || resp.status === 404;
+ if (resp.ok || resp.status === 204 || resp.status === 404) return true;
+ // nginx dav needs a trailing slash to delete a COLLECTION; a no-slash
+ // DELETE on a directory returns 409/404. Retry with slash.
+ if ((resp.status === 409 || resp.status === 404) && !p.endsWith('/')) {
+ const retry = await this._webdavRequest('DELETE', p + '/');
+ return retry.ok || retry.status === 204 || retry.status === 404;
+ }
+ return false;
}
async _cleanupRetention() {