billing frontend for mariadb. setup as otb_billing for outsidethebox.top accounting. also involved with outsidethedb
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

264 lines
9.3 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Health - OTB Billing</title>
<link rel="stylesheet" href="/static/css/style.css">
<style>
.page-wrap {
padding: 1.5rem;
}
.page-header h1 {
margin-bottom: 0.35rem;
}
.page-header p {
margin-top: 0;
opacity: 0.9;
}
.health-links {
margin: 1rem 0 1.25rem 0;
}
.health-links a {
margin-right: 1rem;
text-decoration: underline;
}
.health-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.health-card {
border: 1px solid rgba(255,255,255,0.16);
border-radius: 14px;
padding: 1rem 1.1rem;
background: rgba(255,255,255,0.03);
box-shadow: 0 6px 18px rgba(0,0,0,0.16);
}
.health-card h3 {
margin-top: 0;
margin-bottom: 0.85rem;
}
.health-card a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
.ok {
color: #69db7c;
font-weight: 700;
}
.warn {
color: #ffd43b;
font-weight: 700;
}
.bad {
color: #ff8787;
font-weight: 700;
}
.health-action-button {
margin-top: 0.65rem;
padding: 0.45rem 0.7rem;
border: 1px solid rgba(255,255,255,0.25);
border-radius: 0.45rem;
background: rgba(255,255,255,0.08);
color: inherit;
cursor: pointer;
}
.health-action-button:hover {
background: rgba(255,255,255,0.14);
}
.health-note {
margin-top: 0.55rem;
opacity: 0.9;
}
.health-card p {
margin: 0.45rem 0;
}
.status-good {
color: #63d471;
font-weight: 700;
}
.status-bad {
color: #ff7b7b;
font-weight: 700;
}
.monoish {
word-break: break-word;
}
</style>
<link rel="icon" type="image/png" href="/static/favicon.png">
</head>
<body>
<div class="page-wrap">
<div class="page-header">
<h1>System Health</h1>
<p>Application and server status for OTB Billing.</p>
</div>
<div class="health-links">
<a href="/">Home</a>
<a href="/health.json">Raw JSON</a>
</div>
<div class="health-grid">
<div class="health-card">
<h3>Status</h3>
{% if health.status == "ok" %}
<p class="status-good">Healthy</p>
{% else %}
<p class="status-bad">Degraded</p>
{% endif %}
<p><strong>App:</strong> {{ health.app_name }}</p>
<p><strong>Host:</strong> {{ health.hostname }}</p>
<p class="monoish"><strong>Toronto Time:</strong> {{ health.server_time_toronto }}</p>
<p class="monoish"><strong>UTC Time:</strong> {{ health.server_time_utc }}</p>
</div>
<div class="health-card">
<h3>Database</h3>
{% if health.database.ok %}
<p class="status-good">Connected</p>
{% else %}
<p class="status-bad">Connection Error</p>
{% endif %}
<p class="monoish"><strong>Error:</strong> {{ health.database.error or "None" }}</p>
</div>
<div class="health-card">
<h3>Uptime</h3>
<p><strong>Application:</strong> {{ health.app_uptime_human }}</p>
<p><strong>Server:</strong> {{ health.server_uptime_human }}</p>
</div>
<div class="health-card">
<h3>Load Average</h3>
<p><strong>1 min:</strong> {{ health.load_average["1m"] }}</p>
<p><strong>5 min:</strong> {{ health.load_average["5m"] }}</p>
<p><strong>15 min:</strong> {{ health.load_average["15m"] }}</p>
</div>
<div class="health-card">
<h3>Memory</h3>
<p><strong>Total:</strong> {{ health.memory.total_mb }} MB</p>
<p><strong>Available:</strong> {{ health.memory.available_mb }} MB</p>
<p><strong>Used:</strong> {{ health.memory.used_mb }} MB</p>
<p><strong>Used %:</strong> {{ health.memory.used_percent }}%</p>
</div>
<div class="health-card">
<h3>Disk /</h3>
<p><strong>Total:</strong> {{ health.disk_root.total_gb }} GB</p>
<p><strong>Used:</strong> {{ health.disk_root.used_gb }} GB</p>
<p><strong>Free:</strong> {{ health.disk_root.free_gb }} GB</p>
<p><strong>Used %:</strong> {{ health.disk_root.used_percent }}%</p>
</div>
<div class="health-card">
<h3>Operations Bal</h3>
{% if health.operations_balances %}
{% for asset in health.operations_balances.assets %}
<p>
<strong>{% if asset.explorer_url %}<a href="{{ asset.explorer_url }}" target="_blank" rel="noopener">{{ asset.coin }}</a>{% else %}{{ asset.coin }}{% endif %}:</strong>
{% if asset.ok %}
{{ asset.balance }}
{% else %}
error
{% endif %}
</p>
{% endfor %}
<p class="monoish"><strong>Wallet:</strong> {{ health.operations_balances.wallet }}</p>
{% else %}
<p>Not available</p>
{% endif %}
</div>
<div class="health-card">
<h3>Treasury Bal</h3>
{% if health.treasury_balances %}
{% for asset in health.treasury_balances.assets %}
<p>
<strong>{% if asset.explorer_url %}<a href="{{ asset.explorer_url }}" target="_blank" rel="noopener">{{ asset.coin }}</a>{% else %}{{ asset.coin }}{% endif %}:</strong>
{% if asset.ok %}
{{ asset.balance }}
{% else %}
error
{% endif %}
</p>
{% endfor %}
<p class="monoish"><strong>Wallet:</strong> {{ health.treasury_balances.wallet }}</p>
{% else %}
<p>Not available</p>
{% endif %}
</div>
<div class="health-card">
<h3>Crypto Reconcile</h3>
{% if health.crypto_reconcile %}
<p><strong>Timer:</strong>
{% if health.crypto_reconcile.timer_active == "active" %}
<span class="ok">{{ health.crypto_reconcile.timer_active }}</span>
{% else %}
<span class="warn">{{ health.crypto_reconcile.timer_active }}</span>
{% endif %}
</p>
<p><strong>Service:</strong> {{ health.crypto_reconcile.service_active }}</p>
<p class="monoish"><strong>Last Run:</strong> {{ health.crypto_reconcile.last_run }}</p>
<p><strong>Last Result:</strong> {{ health.crypto_reconcile.last_result }}</p>
{% if health.crypto_reconcile.payment_stats and health.crypto_reconcile.payment_stats.available %}
<p><strong>Pending:</strong> {{ health.crypto_reconcile.payment_stats.pending }}</p>
<p><strong>Confirmed Today:</strong> {{ health.crypto_reconcile.payment_stats.confirmed_today }}</p>
<p><strong>Stale Pending:</strong>
{% if health.crypto_reconcile.payment_stats.stale_pending and health.crypto_reconcile.payment_stats.stale_pending > 0 %}
<span class="warn">{{ health.crypto_reconcile.payment_stats.stale_pending }}</span>
{% else %}
{{ health.crypto_reconcile.payment_stats.stale_pending }}
{% endif %}
</p>
{% elif health.crypto_reconcile.payment_stats %}
<p><strong>Stats:</strong> <span class="warn">unavailable</span></p>
<p class="monoish"><strong>Error:</strong> {{ health.crypto_reconcile.payment_stats.error }}</p>
{% endif %}
<form method="post" action="/health/reconcile-now">
<button class="health-action-button" type="submit">Reconcile Now</button>
</form>
{% if request.args.get("reconcile") == "started" %}
<p class="health-note ok">Manual reconcile started.</p>
{% elif request.args.get("reconcile") == "failed" %}
<p class="health-note bad">Manual reconcile failed to start.</p>
{% elif request.args.get("reconcile") == "missing-worker" %}
<p class="health-note bad">Worker script missing.</p>
{% endif %}
{% else %}
<p>Not available</p>
{% endif %}
</div>
</div>
</div>
{% include "footer.html" %}
</body>
</html>