mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-04 03:42:26 +00:00
feat(sync): warn before overwriting a newer remote copy (conflict guard)
Saves push silently as before, but if the relay copy is NEWER than the one this device last pushed (another device changed it), the user is now asked to confirm before overwriting — preventing a silent multi-device clobber. No nag on normal saves. - js/services/settingsSyncFlags.js: track per-pubkey+kind last-pushed created_at (getSyncPushedAt/setSyncPushedAt). - js/services/syncConflict.js: pushWithConflictCheck — peek remote created_at (list-only, no decrypt), compare to last push, call confirmOverwrite only on a real conflict, then push strictly-newer than remote so the replace wins. - storage/walletSyncService push()/enable() route through it and accept a confirmOverwrite callback. - ProfileStorage/WalletController supply confirmOverwrite (window.confirm) and surface a "kept the newer copy" status when the user declines. tests/sync-conflict.test.mjs: no-remote→silent, newer→prompt(+decline blocks push), confirm→strictly-newer push, older→no prompt. Mutation-verified (ignoring the decline fails). Service-test twins gained exists(). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
c829a42a45
commit
dec5cf8fe1
9 files changed
+275
-14
No files matched your search
@@ -214,6 +214,16 @@ export class ProfileStorageController {
|
||||
this.updateStorageFormVisibility();
|
||||
}
|
||||
|
||||
confirmSyncOverwrite() {
|
||||
if (typeof window === "undefined" || typeof window.confirm !== "function") {
|
||||
return true;
|
||||
}
|
||||
return window.confirm(
|
||||
"A newer copy of your storage settings is on your account (changed on " +
|
||||
"another device). Overwrite it with this one?"
|
||||
);
|
||||
}
|
||||
|
||||
renderSyncSection(pubkey) {
|
||||
if (!(this.storageSyncSection instanceof HTMLElement)) {
|
||||
return;
|
||||
@@ -259,13 +269,20 @@ export class ProfileStorageController {
|
||||
try {
|
||||
if (enabled) {
|
||||
this.setSyncStatus("Encrypting and publishing…");
|
||||
const result = await storageSyncService.enable(pubkey);
|
||||
const result = await storageSyncService.enable(pubkey, {
|
||||
confirmOverwrite: () => this.confirmSyncOverwrite(),
|
||||
});
|
||||
if (result?.ok) {
|
||||
this.setSyncStatus(
|
||||
`Synced to ${result.accepted}/${result.total} relays.`,
|
||||
"success"
|
||||
);
|
||||
this.mainController.showSuccess("Storage settings synced (encrypted).");
|
||||
} else if (result?.conflict) {
|
||||
// User declined to overwrite a newer copy from another device.
|
||||
this.setSyncStatus(
|
||||
"Kept the newer copy on your account. Use Restore to pull it, or save again to overwrite.",
|
||||
);
|
||||
} else {
|
||||
// Roll the toggle back so it reflects reality.
|
||||
if (this.storageSyncToggle instanceof HTMLInputElement) {
|
||||
@@ -723,12 +740,19 @@ export class ProfileStorageController {
|
||||
this.setStorageFormStatus("Connection saved.", "success");
|
||||
this.mainController.showSuccess("Storage connection saved.");
|
||||
|
||||
// Keep the encrypted synced copy current if the user opted in.
|
||||
// Keep the encrypted synced copy current if the user opted in. Warn before
|
||||
// overwriting a newer copy changed on another device.
|
||||
if (storageSyncService.isEnabled(pubkey)) {
|
||||
try {
|
||||
const syncResult = await storageSyncService.push(pubkey);
|
||||
const syncResult = await storageSyncService.push(pubkey, {
|
||||
confirmOverwrite: () => this.confirmSyncOverwrite(),
|
||||
});
|
||||
if (syncResult?.ok) {
|
||||
this.setSyncStatus("Synced copy updated.", "success");
|
||||
} else if (syncResult?.conflict) {
|
||||
this.setSyncStatus(
|
||||
"Synced copy on your account is newer — not overwritten.",
|
||||
);
|
||||
}
|
||||
} catch (syncError) {
|
||||
devLogger.warn("[ProfileModal] Storage re-sync after save failed:", syncError);
|
||||
|
||||
Reference in new issue
Block a user