Manage repositories, channels, and settings from the TUI #1

Merged
kleb merged 7 commits from feature/tui-config-management into main 2026-07-14 13:45:51 +02:00
Owner

Summary

Extends the daemon's overlay mechanism (previously jobs/schedules only) to
repositories, notification channels, and service/maintenance settings, so
the TUI can create, edit, and delete all configuration over gRPC instead of
requiring hand-edits to kbackup.yaml. All edits persist to the daemon's
overrides.yaml and survive restart; the base config file is never rewritten.

Changes

  • Contrast: readable status bar (was black-on-near-black), brighter borders
    and body text. Dark-first preserved.
  • Repo form focus fix: the form dropped every keystroke on all fields except
    Name (textinputs were never Focus()ed); fixed via applyFocus().
  • Repositories: RepoService.Upsert/Delete/GetRepository; full-schema form
    (local/s3/sftp) with a bubbles/filepicker folder browser; in-place edit with
    three-state secret handling (keep blank / replace / ctrl+x clear); live-applied
    through a lock-guarded registry.
  • Notification channels: NotifyService.Upsert/Delete with a new/edit/delete
    form; live-applied via a mutex-guarded notify.Dispatcher.SetChannels.
  • Settings: new SettingsService + Settings tab. Maintenance windows and
    prune/scrub tunables apply live; log level / max jobs persist for restart;
    state_dir/ipc_endpoint stay base-owned (never repointed by the overlay).
  • Safety: deletes of a repo/channel still referenced by a job are refused
    (would otherwise fail validation and brick daemon startup). Upserts persist
    with rollback so disk and memory never disagree. Switching sftp auth
    (password<->key) force-clears the inapplicable secret so a stale credential is
    never resurrected.

Testing

go build ./..., go vet ./..., gofmt -l clean. New tests: overlay merges
(repos/channels/service singleton), gRPC persistence round-trips across restart,
repo focus/validation/secret-clear, and SetChannels under -race. Per project
policy, no host-mutating (VHD/DISM/VSS/mount) tests were run.

## Summary Extends the daemon's overlay mechanism (previously jobs/schedules only) to **repositories, notification channels, and service/maintenance settings**, so the TUI can create, edit, and delete all configuration over gRPC instead of requiring hand-edits to `kbackup.yaml`. All edits persist to the daemon's `overrides.yaml` and survive restart; the base config file is never rewritten. ## Changes - **Contrast:** readable status bar (was black-on-near-black), brighter borders and body text. Dark-first preserved. - **Repo form focus fix:** the form dropped every keystroke on all fields except Name (textinputs were never `Focus()`ed); fixed via `applyFocus()`. - **Repositories:** `RepoService.Upsert/Delete/GetRepository`; full-schema form (local/s3/sftp) with a `bubbles/filepicker` folder browser; in-place edit with three-state secret handling (keep blank / replace / `ctrl+x` clear); live-applied through a lock-guarded registry. - **Notification channels:** `NotifyService.Upsert/Delete` with a new/edit/delete form; live-applied via a mutex-guarded `notify.Dispatcher.SetChannels`. - **Settings:** new `SettingsService` + Settings tab. Maintenance windows and prune/scrub tunables apply live; log level / max jobs persist for restart; `state_dir`/`ipc_endpoint` stay base-owned (never repointed by the overlay). - **Safety:** deletes of a repo/channel still referenced by a job are refused (would otherwise fail validation and brick daemon startup). Upserts persist with rollback so disk and memory never disagree. Switching sftp auth (password<->key) force-clears the inapplicable secret so a stale credential is never resurrected. ## Testing `go build ./...`, `go vet ./...`, `gofmt -l` clean. New tests: overlay merges (repos/channels/service singleton), gRPC persistence round-trips across restart, repo focus/validation/secret-clear, and `SetChannels` under `-race`. Per project policy, no host-mutating (VHD/DISM/VSS/mount) tests were run.
Extend the daemon's overlay mechanism (previously jobs/schedules only) to
repositories, notification channels, and service/maintenance settings, so the
TUI can create, edit, and delete all config over gRPC instead of requiring
hand-edits to kbackup.yaml.

- Repos: RepoService.Upsert/Delete/GetRepository, full-schema form with a
  filesystem folder browser, in-place edit with three-state secret handling
  (keep/replace/clear via ctrl+x), live-applied through a lock-guarded registry.
- Channels: NotifyService.Upsert/Delete with a new/edit/delete form; live
  applied via a mutex-guarded notify.Dispatcher.SetChannels.
- Settings: new SettingsService + Settings tab; maintenance windows and
  prune/scrub tunables apply live, log level/max jobs persist for restart,
  state_dir/ipc_endpoint stay base-owned.
- Fix TUI focus bug that dropped keystrokes in the repo form; raise TUI
  contrast (readable status bar, brighter borders/text).
- Referential guards prevent deleting a repo/channel still used by a job.
- Tests: overlay merges, gRPC persistence round-trips, focus/validation/clear.
Collaborator

kReview review

Verdict: no findings

No findings to address in the reviewed diff.

No meaningful correctness, security, or persistence defects are provable from the visible diff and supplied context.

Reviewed by kReview at db21dd21b8. This comment is conservative and based only on the PR diff and metadata.

Est. cost ~$4.65 total (775.9k in / 29.6k out) · this run ~$0.73 (123.6k in / 4.4k out) / gpt-5.6-sol.

<!-- codex-forgejo-review --> <!-- codex-forgejo-review-head:db21dd21b897b83244377290fac18c070561e16e --> ## kReview review **Verdict:** no findings No findings to address in the reviewed diff. No meaningful correctness, security, or persistence defects are provable from the visible diff and supplied context. _Reviewed by kReview at `db21dd21b8`. This comment is conservative and based only on the PR diff and metadata._ _Est. cost ~$4.65 total (775.9k in / 29.6k out) · this run ~$0.73 (123.6k in / 4.4k out) / gpt-5.6-sol._ <!-- codex-forgejo-review-state:eyJoZWFkU2hhIjoiZGIyMWRkMjFiODk3YjgzMjQ0Mzc3MjkwZmFjMThjMDcwNTYxZTE2ZSIsInN1bW1hcnkiOiJObyBtZWFuaW5nZnVsIGNvcnJlY3RuZXNzLCBzZWN1cml0eSwgb3IgcGVyc2lzdGVuY2UgZGVmZWN0cyBhcmUgcHJvdmFibGUgZnJvbSB0aGUgdmlzaWJsZSBkaWZmIGFuZCBzdXBwbGllZCBjb250ZXh0LiIsImZpbmRpbmdzIjpbXSwiY3VtdWxhdGl2ZUNvc3QiOnsidXNkIjo0LjY1MTQ2NywiaW5wdXRUb2tlbnMiOjc3NTk0Nywib3V0cHV0VG9rZW5zIjoyOTYyMiwibW9kZWwiOiJncHQtNS42LXNvbCJ9fQ== -->
- notify: on a channel edit, keep the stored url/params only when the type is
  unchanged; a type switch (e.g. to eventlog) clears them so a stale,
  secret-bearing url/param is never retained or resurrected. Also preserve an
  absent webhook secret param on same-type edits (was dropped).
- overlay: build each mutation on a cloned overlay and commit it to memory only
  after SaveOverlay succeeds, so a failed save can no longer leave a rejected
  edit for a later successful save to persist.
- Tests for the channel merge semantics and the failed-save-does-not-mutate
  guard.
- service: add a service-level configMu held across the full persist+apply of
  every repository, channel, and settings mutation, so two concurrent RPCs on
  the same subsystem cannot leave the overlay and the running daemon disagreeing.
- channel form: add ctrl+x to explicitly clear an existing webhook secret,
  sending an unambiguous empty-secret params map that the same-type merge will
  not overwrite with the stored value (blank still means keep).
- Tests for the explicit-clear merge pass-through and the form clear/keep/replace
  behavior.
Renaming during an edit saves a new entry, so the daemon has no stored secret
or url to merge under the new name. Track the original name in both forms and
require the secret/url again once the name changes (a same-name edit still
keeps the redacted value blank). Prevents an edit the TUI presented as valid
from being rejected by the daemon or silently losing a required value.
Same-name edits could still submit a blank secret/url when the credential
context changed: the daemon only merges a redacted secret when the backend and
sftp auth method (repos) or channel type (channels) also match. Track the
original backend/auth (repo form) and type (channel form) and require the
secret/url again when any of them changes, matching what the server can merge.
Repository and channel edits applied the live change before the overlay write,
so a concurrent send/open could observe an edit that then failed to persist.

- repos: fold the overlay write into an atomic applyPersistedUpsert/Delete that
  holds rr.mu across the open-check, persist, and apply, so no Open can slip in
  between and a failed write leaves neither disk nor memory changed (removes the
  rollback that could deadlock on a now-open repo).
- channels: persist to the overlay before swapping the dispatcher's channel set,
  so a concurrent notification is never delivered to an unpersisted edit.
- Tests for persist-before-apply and the open-repo refusal.
settingsRegistry.update replaced the shared cfg.Service under its own lock,
racing with unsynchronized runtime readers of the same struct (notably
repoRegistry.cacheDir reading StateDir during an Open). Keep the effective
settings in the registry's own copy instead and never touch cfg.Service at
runtime; the live-applicable subset is already pushed straight to the dispatcher
and maintenance scheduler, so cfg.Service never needs to change. Adds a
race-detector test.
kleb merged commit 09e2b26a87 into main 2026-07-14 13:45:51 +02:00
kleb deleted branch feature/tui-config-management 2026-07-14 13:45:51 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kleb/kBackup!1
No description provided.