From 112d72471a76c914f55a95065a0c290627777edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?He=CC=84sperus?= Date: Wed, 12 Aug 2026 15:37:57 +0800 Subject: [PATCH] fix(webdav): handle RFC 9110 weak etag --- .gitignore | 1 + modules.json | 2 +- packages/webdav/src/webdav/fs.ts | 7 ++++++- packages/webdav/test/fs-webdav.test.ts | 4 ++-- skills/debug-module/SKILL.md | 21 +++++++++++++++++++++ 5 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 skills/debug-module/SKILL.md diff --git a/.gitignore b/.gitignore index 34517f20..b36980bd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ test-files .ignore .obsidian .trash +.agents .obsidianignore Sync Engine Logs .vscode diff --git a/modules.json b/modules.json index 833e8226..7c476e0f 100644 --- a/modules.json +++ b/modules.json @@ -2,7 +2,7 @@ { "id": "webdav", "name": "WebDAV", - "version": "0.1.8", + "version": "0.1.9", "description": "WebDAV backend support.", "icon": "server", "main": "https://sync.consensia.cc/modules/webdav.js", diff --git a/packages/webdav/src/webdav/fs.ts b/packages/webdav/src/webdav/fs.ts index 74dacc83..d0af9046 100644 --- a/packages/webdav/src/webdav/fs.ts +++ b/packages/webdav/src/webdav/fs.ts @@ -130,7 +130,12 @@ function toStat(endpoint: string, { propstat, href }: WebDAVResponseItem): Stat const mtime = new Date(getDavText(validPropstat.prop.getlastmodified) ?? '').valueOf(); const size = Number.parseInt(getDavText(validPropstat.prop.getcontentlength) ?? '0', 10); - const uid = getDavText(validPropstat.prop.getetag) ?? `${mtime}~${size}`; + + // https://www.rfc-editor.org/rfc/rfc9110.html#section-8.8.3 + // https://github.com/hesprs/sync-engine/issues/225 + let etag = getDavText(validPropstat.prop.getetag); + if (etag?.startsWith('W/')) etag = etag.slice(2); + const uid = etag ?? `${mtime}~${size}`; return { isDir: false, key, mtime, size, uid }; } diff --git a/packages/webdav/test/fs-webdav.test.ts b/packages/webdav/test/fs-webdav.test.ts index fd3a6026..f9381bc1 100644 --- a/packages/webdav/test/fs-webdav.test.ts +++ b/packages/webdav/test/fs-webdav.test.ts @@ -141,7 +141,7 @@ test('stat parses dav fields and prefers etag for uid', async () => { propstat: { prop: { getcontentlength: { '#text': '12' }, - getetag: 'etag-123', + getetag: 'W/"etag-123"', getlastmodified: { '#text': 'Mon, 01 Jan 2024 00:00:00 GMT' }, resourcetype: {}, }, @@ -164,7 +164,7 @@ test('stat parses dav fields and prefers etag for uid', async () => { key: 'Notes/file.md', mtime: sharedDate, size: 12, - uid: 'etag-123', + uid: '"etag-123"', }); }); diff --git a/skills/debug-module/SKILL.md b/skills/debug-module/SKILL.md new file mode 100644 index 00000000..7369bb1c --- /dev/null +++ b/skills/debug-module/SKILL.md @@ -0,0 +1,21 @@ +--- +name: debug-module +description: Write a temporary Sync Engine module for debugging. Use when encountering unreasonable bugs, don't use when the bug can be identified by inspecting code. +--- + +Sync Engine serves for various services, many bugs need deeper investigation that Sync Engine's built-in logs are not suffice. Temporary debug modules allow you to gather more info to facilitate the analysis. + +When writing a debug module, you need to produce a plain, self-contained JS ESM file at repo root, containing a simple Sync Engine module. Read repo docs on how to develop a module before writing. Useful patterns: + +- Register a request middleware to log raw request raw request and response. +- Register a filesystem wrapper to trace the files. +- Subscribe to Sync Engine events and (execute code to) gather information when event fires. + +You must: + +- Obfuscate any privacy-sensitive info logged in your module, including hashing filenames and contents instead of logging raw content, and strip off auth header and URL in logged requests. File mtime, size, and UID are safe to disclose without obfuscation. +- When logging, dispatch Sync Engine events `logGeneral` or `logSync` directly, do not create a separate logging and export logs path. +- Simplify the module to bare minimum, don't gather information that has no value, focus on the most valuable and distinguishing info, don't do over-abstraction. The module is throwaway. +- Only perform syntax checking, no need linting or formatting. + +After writing the module, you need to give clear instruction on what to perform after loading the module.