D62: restore copies kept 30 days, listed with their date, deletable by hand (typed name, never a needed copy, operator event); D63: a test-build-only fault switch for the R-1256 H4/H5 live proofs — tests red first
gates / gates (push) Successful in 33s

This commit is contained in:
admin committed 2026-10-29 08:50:59 +01:00
1 parent a815b37fbd
commit e3fce09d96
24 files changed
+1992 -11

No files matched your search

@@ -0,0 +1,34 @@
package notify
import (
"encoding/json"
"strings"
"testing"
)
// D62 — restore_copy_deleted: info (the hub's vocabulary), the operator message only (no household sentence — the hub
// bars the type from customers too), and the details carry who, app, the restore's date and the size.
func TestD62_RestoreCopyDeletedEvent(t *testing.T) {
n := &Notifier{}
var et, sev, msg, cust string
var det []byte
n.pushFn = func(eventType, severity, message, customer string, details interface{}) {
et, sev, msg, cust = eventType, severity, message, customer
det, _ = json.Marshal(details)
}
n.NotifyRestoreCopyDeleted(RestoreCopyDeletedDetails{App: "vikunja", RestoreAt: "2026-10-29T08:00:00Z", SizeBytes: 4096,
Who: "the household (dashboard sign-in) from 192.168.0.5", Path: "/x/backups/restore-undo/vikunja/20261029T080000Z"})
if et != "restore_copy_deleted" || sev != "info" || cust != "" {
t.Fatalf("type=%q severity=%q customer=%q", et, sev, cust)
}
for _, want := range []string{"vikunja", "2026-10-29T08:00:00Z", "4096", "the household"} {
if !strings.Contains(msg, want) {
t.Fatalf("message %q lacks %q", msg, want)
}
}
for _, want := range []string{`"app":"vikunja"`, `"restore_at":"2026-10-29T08:00:00Z"`, `"size_bytes":4096`, `"who":"the household`} {
if !strings.Contains(string(det), want) {
t.Fatalf("details %s lack %s", det, want)
}
}
}
+19
View File
@@ -1585,6 +1585,25 @@ func (n *Notifier) NotifyAppDefinitionRefused(app, detail string) {
n.pushEventBoth("app_definition_refused", "warning", msg, "", AppDefinitionRefusedDetails{App: app, Refused: detail})
}
// RestoreCopyDeletedDetails is restore_copy_deleted's record: who deleted which app's copy, the restore it protected,
// and its size.
type RestoreCopyDeletedDetails struct {
App string `json:"app"`
RestoreAt string `json:"restore_at"` // RFC 3339, UTC
SizeBytes int64 `json:"size_bytes"`
Who string `json:"who"`
Path string `json:"path"`
}
// NotifyRestoreCopyDeleted (D62, operator 2026-10-29): the household deleted a restore copy with „Törlés most" before
// its 30 days. Info, operator-only on the hub (registered in both of its lists, hub D62): an audit line for support,
// never a household mail. Pinned by TestD62_RestoreCopyDeletedEvent.
func (n *Notifier) NotifyRestoreCopyDeleted(d RestoreCopyDeletedDetails) {
msg := fmt.Sprintf("%s: the restore copy from before the restore of %s (%d bytes) was deleted by hand — %s. That restore can no longer be undone from this box.",
d.App, d.RestoreAt, d.SizeBytes, d.Who)
n.pushEventBoth("restore_copy_deleted", "info", msg, "", d)
}
// healthSeverity is the event severity a health status would be sent at (R-647): the disabled path
// names what it drops, and "warn" is a health STATUS, not a severity the hub knows.
func healthSeverity(status string) string {