feat: WebDAV sync + batch DnD + clean shutdown + center-modal UX bundle
- Sync (WebDAV, auto-merge): UUID + tombstones foundations (server + JS), THTTPClient bridge cmds (get/put/test), runSyncNow engine with pull/merge/push flow, Settings UI, pre-sync backup option. Test connection now treats 404 as OK (snapshot not yet created) and 401/ 403 as auth failure with dedicated toast. - Batch drag-drop: cards + table rows carry checked-set ids (CSV) when dragged from an active selection; folder + trash drop handlers parse and apply in batch via new moveEntriesToFolder helper that preserves TOTP / custom_fields / kind in the full PUT payload. - Clean shutdown: WM_QUERYENDSESSION / WM_ENDSESSION captured in the bridge message-only window; FormCloseQuery bypasses the tray-minimize intercept on system shutdown / restart / logoff so FireDAC closes the SQLite WAL cleanly instead of leaving -shm / -wal residue after a force-kill. - Center-mode modal: blur+dim backdrop via body::before pseudo-element in editor-position=center, swallows clicks below the panel so the existing outside-click handlers reliably dismiss the slideover / settings panel. - Batch bar state fixes: state.checked cleared before render in moveEntriesToFolder, emptyTrash, and per-card restoreEntry / permanentDelete / deleteEntry so the action bar disappears once the selection is fully processed. - Save-then-discard duplicate fix: soState reset to null before openSlideOver re-opens the freshly saved entry, otherwise the dirty check fired on the soState.id=null → newId switch and a Cancel left the form in new-entry mode (second Save → POST duplicate). - TEST_SYNC.md: end-to-end checklist for validating the WebDAV sync with 2 real instances. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+51
@@ -646,6 +646,57 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slideover-field" id="syncField" style="display:none">
|
||||
<div class="slideover-field-label">Sync (WebDAV)</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
|
||||
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).
|
||||
</p>
|
||||
<label style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||||
<input type="checkbox" id="settingSyncEnabled">
|
||||
<span>Enable sync</span>
|
||||
</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>
|
||||
<input type="text" id="syncUrl" class="so-input"
|
||||
placeholder="https://cloud.example.com/remote.php/dav/files/USER/PMServer/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>
|
||||
<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>
|
||||
<input type="password" id="syncPwd" class="so-input" style="width:100%">
|
||||
</label>
|
||||
</div>
|
||||
<label style="display:flex;align-items:center;gap:8px;margin:8px 0">
|
||||
<input type="checkbox" id="syncPreBackup">
|
||||
<span style="font-size:12px">Create a local backup before each sync</span>
|
||||
</label>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||||
Sync password ≠ master password. <b>Use the same one
|
||||
on every device</b> — that's how the encrypted snapshot
|
||||
round-trips. Stored DPAPI-protected locally.
|
||||
</p>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
||||
<button class="btn btn-ghost btn-sm" id="syncSetPwdBtn">Set sync password</button>
|
||||
<span id="syncPwdStatus" style="font-size:11px;color:var(--text-faint)">Not set.</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-top:6px">
|
||||
<button class="btn btn-ghost btn-sm" id="syncTestBtn">Test connection</button>
|
||||
<button class="btn btn-primary btn-sm" id="syncNowBtn">Sync now</button>
|
||||
<span id="syncLast" style="font-size:11px;color:var(--text-faint)"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slideover-field" id="autoBackupField" style="display:none">
|
||||
<div class="slideover-field-label">Auto-backup</div>
|
||||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||||
|
||||
Reference in New Issue
Block a user