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:
@@ -454,6 +454,53 @@ hotkeys autofill, etc. **Device-only** (localStorage seulement) :
|
||||
`quickUnlockEnabled` (DPAPI lié au compte Windows), `autofillEnabled`
|
||||
(toggle hotkey Win32), `rememberedUsername` (auth screen autofill local).
|
||||
|
||||
## Sync (WebDAV, auto-merge)
|
||||
|
||||
Multi-device sync via a user-hosted WebDAV server (Nextcloud, ownCloud,
|
||||
Apache mod_dav). Auto-merge strategy: last-write-wins per entry on
|
||||
`updated_at`, tombstones propagate hard-deletes. No conflict UI — solo
|
||||
personal use rarely produces simultaneous edits.
|
||||
|
||||
Foundations :
|
||||
- `vault_entries.uuid` (TEXT, indexed) — stable cross-device identity.
|
||||
Migration backfills existing rows via `hex(randomblob)` → RFC 4122 v4.
|
||||
- `entry_tombstones (user_id, uuid, deleted_at)` — UNIQUE(user_id, uuid),
|
||||
written on hard-delete (`DELETE permanent=1`, trash empty, auto-purge).
|
||||
- GET `/entries` returns uuid ; POST/bulk-import accept it (mint fresh
|
||||
if absent) ; PUT keeps it immutable.
|
||||
- GET `/entries/tombstones` lists local tombstones.
|
||||
- POST `/entries/tombstones {uuids:[...]}` adds tombstones + hard-deletes
|
||||
any local rows matching those uuids (idempotent via INSERT OR IGNORE).
|
||||
|
||||
Transport ([UMainForm.pas](delphi-backend/UMainForm.pas)) :
|
||||
- `cmd://webdav/get|put|test?reqId=&url=&user=&pwd=[&data=]` → async via
|
||||
`THTTPClient` (WinHTTP under the hood, no OpenSSL DLLs required).
|
||||
Basic auth, 10s connect / 30s response timeout. Callback
|
||||
`Bridge.onWebdavResult(reqId, status, payload)`.
|
||||
|
||||
Settings : all config in DPAPI prefs (`syncEnabled`, `syncUrl`,
|
||||
`syncUser`, `syncPwd`, `syncEncPwd`, `syncPreBackup`, `syncLast`).
|
||||
**`syncEncPwd` MUST be the same on every device** — it's the secret
|
||||
that encrypts the WebDAV-stored snapshot. User sets it once per
|
||||
device, never transmitted.
|
||||
|
||||
`runSyncNow()` flow :
|
||||
1. (Optional) Write `vault-presync-yyyymmdd-HHMMSS.json` to the
|
||||
auto-backup folder if enabled.
|
||||
2. `webdav/get` → 404 = first sync, treat as empty remote.
|
||||
3. Decrypt with `syncEncPwd` (reuses `encryptExportPayload` container).
|
||||
4. POST remote tombstones → server hard-deletes local matches.
|
||||
5. Folders : add missing ones additively (don't touch existing).
|
||||
6. Entries : for each remote uuid → not in local = POST keeping uuid +
|
||||
restore attachments ; both sides have it = compare `updated_at`,
|
||||
PUT if remote newer.
|
||||
7. `loadEntries()` + `buildSyncSnapshot()` for the post-merge state.
|
||||
8. `webdav/put` push the merged snapshot.
|
||||
9. Toast `X added · Y updated · Z deleted`.
|
||||
|
||||
Sensitive actions (export, change master pw, recovery code…) still
|
||||
require master pw via `askReauth` — sync never substitutes.
|
||||
|
||||
## PIN unlock
|
||||
|
||||
Optional shortcut unlock with a 4–12 digit PIN, complementary to Quick
|
||||
|
||||
Reference in New Issue
Block a user