aboutsummaryrefslogtreecommitdiff
path: root/clients
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-20 09:20:39 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-20 09:20:39 +0200
commit5b27bb0950fd03d29c223b5960faff89baf37970 (patch)
tree5cc99420a4fc1365fdb8bf4bbec8237fa306fd4b /clients
parenta61cfeefb0599e74fc9189b189b16dd0b6d36f0f (diff)
downloadbokf-5b27bb0950fd03d29c223b5960faff89baf37970.tar.gz
bokf-5b27bb0950fd03d29c223b5960faff89baf37970.zip
audit: verify voucher chains and attachment hashes
Diffstat (limited to 'clients')
-rw-r--r--clients/bokftui.c70
1 files changed, 56 insertions, 14 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index f7500bf..7c31ccb 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -3307,31 +3307,73 @@ static void inbox_screen(struct app *a)
}
}
+static void audit_verify_run(struct app *a, int full)
+{
+ char *resp = client_rpc(&a->conn, "audit.verify", a->session, a->org,
+ full ? "{\"full\":true}" : "{}");
+ if (!resp || !client_ok(resp)) {
+ show_error("Revision", resp);
+ free(resp);
+ return;
+ }
+ int64_t checked = jint_val(resp, "result.checked", 0);
+ int64_t vouchers = jint_val(resp, "result.vouchers_checked", 0);
+ int is_ok = jbool_val(resp, "result.ok", 0);
+ if (is_ok) {
+ if (full)
+ tui_message("Hashkedjor",
+ "Kedjorna är intakta.\n%lld verifikat och %lld "
+ "händelser kontrollerade.\n%lld underlag "
+ "hash-kontrollerade.",
+ (long long)vouchers, (long long)checked,
+ (long long)jint_val(resp, "result.attachments_checked",
+ 0));
+ else
+ tui_message("Hashkedjor",
+ "Kedjorna är intakta.\n%lld verifikat och %lld "
+ "händelser kontrollerade.",
+ (long long)vouchers, (long long)checked);
+ } else {
+ int64_t bad_v = jint_val(resp, "result.first_bad_voucher_id", 0);
+ int64_t bad_a = jint_val(resp, "result.first_bad_seq", 0);
+ int64_t unbal = jint_val(resp, "result.first_unbalanced_voucher_id", 0);
+ int64_t bad_att =
+ jint_val(resp, "result.first_bad_attachment_id", 0);
+ char detail[320] = "";
+ if (bad_v)
+ snprintf(detail + strlen(detail), sizeof detail - strlen(detail),
+ "Första trasiga verifikat: %lld\n", (long long)bad_v);
+ if (bad_a)
+ snprintf(detail + strlen(detail), sizeof detail - strlen(detail),
+ "Första trasiga händelse: %lld\n", (long long)bad_a);
+ if (unbal)
+ snprintf(detail + strlen(detail), sizeof detail - strlen(detail),
+ "Första obalanserade verifikat: %lld\n",
+ (long long)unbal);
+ if (bad_att)
+ snprintf(detail + strlen(detail), sizeof detail - strlen(detail),
+ "Första trasiga underlag: %lld\n", (long long)bad_att);
+ tui_message("Hashkedjor", "KEDJORNA ÄR BRUTNA!\n%s", detail);
+ }
+ free(resp);
+}
+
static void audit_screen(struct app *a)
{
static const char *const audit_items[] = { "Verifiera hashkedjan",
+ "Verifiera allt (inkl. underlag)",
"Senaste händelser" };
static int last_sel = 0;
for (;;) {
if (g_quit)
return;
- int sel = tui_menu("Revision", audit_items, 2, 0, &last_sel);
+ int sel = tui_menu("Revision", audit_items, 3, 0, &last_sel);
if (sel < 0)
return;
if (sel == 0) {
- char *resp =
- client_rpc(&a->conn, "audit.verify", a->session, a->org, "{}");
- if (!resp || !client_ok(resp)) {
- show_error("Revision", resp);
- free(resp);
- continue;
- }
- int64_t checked = jint_val(resp, "result.checked", 0);
- int is_ok = jbool_val(resp, "result.ok", 0);
- tui_message("Hashkedja", "%s\n%d händelser kontrollerade.",
- is_ok ? "Kedjan är intakt." : "KEDJAN ÄR BRUTEN!",
- (int)checked);
- free(resp);
+ audit_verify_run(a, 0);
+ } else if (sel == 1) {
+ audit_verify_run(a, 1);
} else {
for (;;) {
char *resp = client_rpc(&a->conn, "audit.list", a->session,