#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" #include "tui.h" #include "formula.h" #include "util.h" #include "version.h" #include "yyjson.h" #include "ui.h" 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); } 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, 3, 0, &last_sel); if (sel < 0) return; if (sel == 0) { 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, a->org, "{\"limit\":200}"); if (!resp || !client_ok(resp)) { show_error("Revision", resp); free(resp); break; } struct buf text; buf_init(&text); size_t n = jarr_size(resp, "result.items"); for (size_t i = 0; i < n; i++) { char path[64], line[512]; snprintf(path, sizeof path, "result.items.%zu.at", i); char *at = jstr_dup(resp, path); snprintf(path, sizeof path, "result.items.%zu.action", i); char *action = jstr_dup(resp, path); snprintf(path, sizeof path, "result.items.%zu.result_code", i); char *code = jstr_dup(resp, path); snprintf(path, sizeof path, "result.items.%zu.actor_user_id", i); int64_t actor = jint_val(resp, path, 0); snprintf(line, sizeof line, "%s anv %lld %-22s %s\n", at ? at : "", (long long)actor, action ? action : "", code ? code : ""); buf_append(&text, line, strlen(line)); free(at); free(action); free(code); } buf_append(&text, "\0", 1); int again = tui_pager("Behandlingshistorik", (char *)text.p, NULL); buf_free(&text); free(resp); if (!again) break; } } } }