diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index fa14042..14a9805 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -1,5 +1,39 @@ # PROJECT_STATE - OTB Billing +## v2.0.0 - 2026-05-18 + +Current state: +- OTB Billing is running on outsidethedb. +- Service: otb_billing.service. +- App port: 5050. +- Project path: /home/def/otb_billing. +- Current version: v2.0.0. +- /health renders the standard health grid with Status, Database, Uptime, Load Average, Memory, Disk, Operations Bal, Treasury Bal, and Crypto Reconcile. +- Operations Bal wallet: 0x44f6c44C42e6ae0392E7289F032384C0d37F56D5. +- Treasury Bal wallet: 0xbe1fdc8c69f712d62cfcd3bf23f636de1dbd213f. +- Wallet coin names are clickable explorer links: + - USDC uses Arbiscan + - ETH uses Etherscan + - ETHO uses explorer.ethoprotocol.com + - EGAZ and ETI use explorer.etica-stats.org +- Crypto Reconcile card shows: + - timer status + - service status + - last run timestamp + - last result + - pending payment count + - confirmed today count + - stale pending count + - Reconcile Now button +- Crypto reconciliation worker: + - Timer: otb-billing-crypto-reconcile.timer + - Service: otb-billing-crypto-reconcile.service + - Script: /home/def/otb_billing/scripts/crypto_reconciliation_worker.py + - Timer cadence: every 15 minutes +- The reconcile service is one-shot; inactive between runs is normal when the timer is active. + +# PROJECT_STATE - OTB Billing + ## v1.4.0 - 2026-05-18 Current state: diff --git a/README.md b/README.md index c9b9a5c..ac38801 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,27 @@ +# OTB Billing - v2.0.0 + +Build date: 2026-05-18 + +## v2.0.0 changes + +- Promoted OTB Billing to v2.0.0. +- Added Last Run and Last Result fields to the Crypto Reconcile health card. +- Last Run is read from systemd one-shot service timestamps for otb-billing-crypto-reconcile.service. +- Confirmed Crypto Reconcile remains a normal card in the main /health grid. +- Existing health cards remain: + - Status + - Database + - Uptime + - Load Average + - Memory + - Disk + - Operations Bal + - Treasury Bal + - Crypto Reconcile +- Existing Operations Bal and Treasury Bal cards retain clickable explorer links for USDC, ETH, ETHO, EGAZ, and ETI. +- Reconcile Now button remains available on /health to manually start the crypto reconciliation worker. +- Footer/app version bumped from v1.4.0 to v2.0.0. + # OTB Billing - v1.4.0 Build date: 2026-05-18 diff --git a/VERSION b/VERSION index 0d0c52f..46b105a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v1.4.0 +v2.0.0 diff --git a/backend/health.py b/backend/health.py index 176a6c5..87cf477 100644 --- a/backend/health.py +++ b/backend/health.py @@ -490,6 +490,18 @@ def _try_payment_stats_from_db(app): pass +def _parse_systemctl_show(stdout): + parsed = {} + + for line in str(stdout or "").splitlines(): + if "=" not in line: + continue + key, value = line.split("=", 1) + parsed[key.strip()] = value.strip() + + return parsed + + def _crypto_reconcile_status(app): service_name = "otb-billing-crypto-reconcile.service" timer_name = "otb-billing-crypto-reconcile.timer" @@ -507,16 +519,33 @@ def _crypto_reconcile_status(app): timer_name, ]) - last_logs = _systemctl_value([ + service_show = _systemctl_value([ "show", service_name, "--property=ActiveEnterTimestamp", "--property=InactiveEnterTimestamp", "--property=ExecMainStatus", "--property=Result", + "--property=NRestarts", "--no-pager", ]) + parsed = _parse_systemctl_show(service_show["stdout"]) + + active_entered = parsed.get("ActiveEnterTimestamp") or "" + inactive_entered = parsed.get("InactiveEnterTimestamp") or "" + result = parsed.get("Result") or "unknown" + exec_status = parsed.get("ExecMainStatus") or "unknown" + + # For a oneshot timer service, InactiveEnterTimestamp is usually the useful + # "last completed" timestamp. Fall back to ActiveEnterTimestamp if needed. + last_run = inactive_entered + if not last_run or last_run.lower() in ("n/a", "never"): + last_run = active_entered + + if not last_run: + last_run = "unknown" + return { "service_name": service_name, "timer_name": timer_name, @@ -525,7 +554,10 @@ def _crypto_reconcile_status(app): "service_active": service_active["stdout"] or "unknown", "service_enabled": service_enabled["stdout"] or "unknown", "timer_line": timer_list["stdout"], - "service_details": last_logs["stdout"], + "service_details": service_show["stdout"], + "last_run": last_run, + "last_result": result, + "last_exit_status": exec_status, "payment_stats": _try_payment_stats_from_db(app), } diff --git a/templates/health.html b/templates/health.html index 7f25195..755278c 100644 --- a/templates/health.html +++ b/templates/health.html @@ -222,6 +222,8 @@ {% endif %}

Service: {{ health.crypto_reconcile.service_active }}

+

Last Run: {{ health.crypto_reconcile.last_run }}

+

Last Result: {{ health.crypto_reconcile.last_result }}

{% if health.crypto_reconcile.payment_stats and health.crypto_reconcile.payment_stats.available %}

Pending: {{ health.crypto_reconcile.payment_stats.pending }}