feat(desktop): sync to a private Gitea repo, chosen by the URL
Same discriminant as the Android client - "/api/v1/repos/" in the URL - so one address tells both ends which transport to speak, and nothing new appears in Settings beyond a placeholder and a warning. The bridge stays shared. Three things differ, and they are confined: the header is "token <PAT>" rather than Basic (Gitea answers a silent 401 to the other), a PUT carries a JSON envelope with the file base64-encoded inside it, and the connection test uses GET because the contents API does not answer HEAD. The sha does the ETag's whole job. It comes back from the pull, rides in the PUT body instead of an If-Match, and a stale one earns a 409 - handled exactly like WebDAV's 412, by re-running pull->merge->push so the other device's changes are folded in rather than clobbered. On a first push `sha` is OMITTED rather than sent empty: Gitea reads its presence as "replace that exact version". `branch` is omitted too, so the repo's default is used instead of a name we would be guessing. A failing PUT now reads its response body: Gitea explains a 409 or a 422 there, and without it the error was just a number. The test button checks the SHAPE of what came back, not only the status. A URL one path segment off answers 200 with valid JSON that is not a file - which would otherwise surface much later as "wrong sync password" and send you looking on the wrong side. Four tests cover that parsing, including the line-wrapped base64 Gitea sends and the array an accidental directory URL returns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+15
-6
@@ -756,10 +756,12 @@
|
||||
</div>
|
||||
|
||||
<div class="slideover-field" id="syncField" style="display:none">
|
||||
<div class="slideover-field-label">Sync (WebDAV)</div>
|
||||
<div class="slideover-field-label">Sync (WebDAV or Gitea)</div>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.5">
|
||||
Bidirectional sync with a WebDAV server (Nextcloud,
|
||||
ownCloud, Apache mod_dav). Auto-merge by entry
|
||||
ownCloud, Apache mod_dav) <b>or a private Gitea
|
||||
repository</b> — paste a contents-API URL and it switches
|
||||
transport on its own. Auto-merge by entry
|
||||
timestamp. <b>Master password is never sent to the
|
||||
remote</b> — the snapshot is encrypted with a separate
|
||||
sync password you set once per device (same on each).
|
||||
@@ -770,21 +772,28 @@
|
||||
</label>
|
||||
<div id="syncConfig" style="display:none;border-left:2px solid var(--border);padding-left:10px;margin-left:4px">
|
||||
<div style="margin-bottom:6px">
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">WebDAV URL (full path to the snapshot file)</div>
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">URL — full path to the snapshot file, or a Gitea contents path</div>
|
||||
<input type="text" id="syncUrl" class="so-input"
|
||||
placeholder="https://cloud.example.com/remote.php/dav/files/USER/PMServer/vault-sync.json"
|
||||
placeholder="https://git.example.com/api/v1/repos/OWNER/REPO/contents/vault-sync.json"
|
||||
style="width:100%;font-size:12px">
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;margin-bottom:6px">
|
||||
<label style="flex:1">
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Username</div>
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Username <span style="opacity:.7">(WebDAV only)</span></div>
|
||||
<input type="text" id="syncUser" class="so-input" style="width:100%">
|
||||
</label>
|
||||
<label style="flex:1">
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Password / app token</div>
|
||||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Password / token</div>
|
||||
<input type="password" id="syncPwd" class="so-input" style="width:100%">
|
||||
</label>
|
||||
</div>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.5">
|
||||
On Gitea the username is ignored and the token needs
|
||||
<b>write:repository</b> — a read-only one can pull but
|
||||
never push. Each sync is a commit, and Git history is
|
||||
kept: an old sync password stays attackable in earlier
|
||||
versions, so prune the history if you rotate it.
|
||||
</p>
|
||||
<div style="margin-bottom:8px">
|
||||
<button class="btn btn-ghost btn-sm" id="syncTestBtn">Test connection</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user