Release v0.6.26: OpenBao settings synchronization with per-setting conflicts

This commit is contained in:
2026-11-02 00:37:57 +02:00
parent 908985d775
commit 22edece7cf
24 changed files with 1442 additions and 29 deletions
+108
View File
@@ -0,0 +1,108 @@
#include "ui_sync_conflicts.inl"
struct SyncView { HWND parent; App *app; };
SyncConfig syncFields(HWND w)
{
SyncConfig c;
c.enabled = checked(w, 601);
c.server = value(w, 602);
while (!c.server.empty() && c.server.back() == L'/') c.server.pop_back();
c.username = lower(value(w, 603)); c.mount = value(w, 605);
c.path = value(w, 606); c.authMount = value(w, 607);
return c;
}
LRESULT CALLBACK syncProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
{
if (msg == WM_DPICHANGED) { resizeForDpi(w, wp, lp); return 0; }
auto *v = reinterpret_cast<SyncView *>(GetWindowLongPtrW(w, GWLP_USERDATA));
if (!v) return DefWindowProcW(w, msg, wp, lp);
try
{
if (msg == WM_TIMER)
{
value(w, 608, v->app->syncCommand());
return 0;
}
if (msg == WM_COMMAND)
{
int id = LOWORD(wp);
if (id == 612) { showSyncConflicts(w, *v->app); return 0; }
if (id == IDCANCEL) { DestroyWindow(w); return 0; }
if (id == 609)
{
auto c = loadSyncConfig(v->app->data);
c.enabled = false;
saveSyncConfig(v->app->data, c);
forgetSyncPassword(c);
check(w, 601, false);
value(w, 604, L"");
value(w, 608, L"Disabled. Saved password removed. An in-flight server request may still finish.");
KillTimer(w, 1);
return 0;
}
if (id == IDOK || id == 610 || id == 611)
{
if (id != IDOK && MessageBoxW(w, id == 610 ?
L"Replace this PC's shared settings with the remote profile? A local backup will be saved. Unsaved edits in Settings are not included." :
L"Replace the remote profile with this PC's saved settings? OpenBao retains older versions according to its retention policy. Unsaved edits are not included.",
L"Resolve synchronization", MB_YESNO | MB_ICONWARNING) != IDYES) return 0;
auto c = syncFields(w);
if (id != IDOK && !c.enabled) throw std::runtime_error("Enable synchronization first.");
if (c.enabled) validateSyncConfig(c);
auto password = value(w, 604);
try { if (!password.empty()) saveSyncPassword(c, password); }
catch (...) { SecureZeroMemory(password.data(), password.size() * sizeof(wchar_t)); throw; }
SecureZeroMemory(password.data(), password.size() * sizeof(wchar_t));
value(w, 604, L"");
saveSyncConfig(v->app->data, c);
value(w, 608, v->app->syncCommand(id == 610 ? 2 : id == 611 ? 3 : 1));
SetTimer(w, 1, 1000, nullptr);
return 0;
}
}
}
catch (const std::exception &e)
{
// Never display parser exceptions containing connection file contents.
value(w, 608, L"Unable to complete sync action. Check connection fields and saved configuration.");
if (dynamic_cast<const std::runtime_error *>(&e))
value(w, 608, wide(e.what()));
return 0;
}
if (msg == WM_CLOSE) { DestroyWindow(w); return 0; }
if (msg == WM_NCDESTROY)
{
KillTimer(w, 1);
EnableWindow(v->parent, TRUE); SetForegroundWindow(v->parent);
disposeFont(w); delete v; SetWindowLongPtrW(w, GWLP_USERDATA, 0);
}
return DefWindowProcW(w, msg, wp, lp);
}
void showSyncOptions(HWND parent, App &app)
{
auto c = loadSyncConfig(app.data);
HWND w = makeWindow(app, L"KSwitcher.Sync", L"Settings synchronization - OpenBao", syncProc, 660, 640);
SetWindowLongPtrW(w, GWLP_HWNDPARENT, (LONG_PTR)parent);
SetWindowLongPtrW(w, GWLP_USERDATA, (LONG_PTR)new SyncView{parent, &app});
checkbox(w, L"Enable synchronization with your OpenBao (KV v2)", 601, 18, 18, 620);
check(w, 601, c.enabled);
label(w, L"Server (HTTPS)", 18, 60, 160); edit(w, 602, 190, 56, 450); value(w, 602, c.server);
label(w, L"Username", 18, 100, 160); edit(w, 603, 190, 96, 450); value(w, 603, c.username);
label(w, L"Password", 18, 140, 160); edit(w, 604, 190, 136, 450, 25, ES_AUTOHSCROLL | ES_PASSWORD);
label(w, L"Leave password blank to use the saved Windows credential.", 190, 166, 450);
label(w, L"KV v2 mount", 18, 210, 160); edit(w, 605, 190, 206, 450); value(w, 605, c.mount);
label(w, L"Profile path", 18, 250, 160); edit(w, 606, 190, 246, 450); value(w, 606, c.path);
label(w, L"Login mount", 18, 290, 160); edit(w, 607, 190, 286, 450); value(w, 607, c.authMount);
label(w, L"Sync uses saved settings. Close and reopen Settings after downloading changes.", 18, 330, 622);
control(w, L"STATIC", L"", 0, 608, 18, 362, 622, 76);
value(w, 608, app.syncCommand());
button(w, L"Save / Sign in / Sync", IDOK, 18, 452, 200);
button(w, L"Download remote", 610, 230, 452, 195);
button(w, L"Upload local", 611, 437, 452, 203);
button(w, L"Resolve individual conflicts...", 612, 18, 498, 300);
label(w, L"Background check: every 10 minutes", 330, 502, 310);
button(w, L"Disconnect / forget password", 609, 18, 550, 290);
button(w, L"Close", IDCANCEL, 540, 550, 100);
EnableWindow(parent, FALSE);
SetTimer(w, 1, 1000, nullptr);
showRequestedWindow(w); SetForegroundWindow(w);
}