Jun 28 - Update and polish UI/UX
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
control/panel/health.py
|
||||
-----------------------
|
||||
Superadmin health / monitoring dashboard (MT-8+).
|
||||
|
||||
GET /health — reads control plane data only (no per-tenant DB queries)
|
||||
so it stays fast regardless of tenant count.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from flask import Blueprint, render_template, session
|
||||
|
||||
from control.base import control_session
|
||||
from control.models import Plan, Tenant
|
||||
from control.tenant_migrate import chain_head
|
||||
from control.time_utils import now_eastern
|
||||
from .decorators import superadmin_required
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
bp = Blueprint('health', __name__, url_prefix='/health')
|
||||
|
||||
_BASE_DOMAIN = lambda: os.environ.get('TENANT_BASE_DOMAIN', 'jqc.app')
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
@superadmin_required
|
||||
def dashboard():
|
||||
try:
|
||||
head = chain_head()
|
||||
except Exception:
|
||||
head = None
|
||||
|
||||
with control_session() as s:
|
||||
plans = {p.id: p.name for p in s.query(Plan).all()}
|
||||
tenants = s.query(Tenant).order_by(Tenant.id).all()
|
||||
|
||||
now = now_eastern()
|
||||
rows = []
|
||||
for t in tenants:
|
||||
trial_days_left = None
|
||||
trial_expired = False
|
||||
if t.trial_ends_at:
|
||||
delta = (t.trial_ends_at - now).days
|
||||
trial_days_left = max(0, delta)
|
||||
trial_expired = now >= t.trial_ends_at
|
||||
|
||||
schema_ok = (t.alembic_head == head) if head else None
|
||||
|
||||
rows.append({
|
||||
'id': t.id,
|
||||
'slug': t.slug,
|
||||
'name': t.name,
|
||||
'status': t.status,
|
||||
'plan_name': plans.get(t.plan_id, '?'),
|
||||
'subscription_status': t.subscription_status,
|
||||
'trial_ends_at': t.trial_ends_at,
|
||||
'trial_days_left': trial_days_left,
|
||||
'trial_expired': trial_expired,
|
||||
'current_period_end': t.current_period_end,
|
||||
'has_stripe': bool(t.stripe_customer_id),
|
||||
'alembic_head': t.alembic_head,
|
||||
'schema_ok': schema_ok,
|
||||
'created_at': t.created_at,
|
||||
'suspended_at': t.suspended_at,
|
||||
})
|
||||
|
||||
# ── Aggregate stats ──────────────────────────────────────────────────────
|
||||
total = len(rows)
|
||||
active = sum(1 for r in rows if r['status'] == 'active')
|
||||
suspended = sum(1 for r in rows if r['status'] == 'suspended')
|
||||
schema_behind = sum(1 for r in rows if r['schema_ok'] is False)
|
||||
|
||||
sub_counts = {}
|
||||
for r in rows:
|
||||
key = r['subscription_status'] or 'none'
|
||||
sub_counts[key] = sub_counts.get(key, 0) + 1
|
||||
|
||||
trial_expiring_soon = sum(
|
||||
1 for r in rows
|
||||
if r['subscription_status'] == 'trial'
|
||||
and r['trial_days_left'] is not None
|
||||
and r['trial_days_left'] <= 3
|
||||
and not r['trial_expired']
|
||||
)
|
||||
trial_expired_count = sum(
|
||||
1 for r in rows
|
||||
if r['subscription_status'] == 'trial' and r['trial_expired']
|
||||
)
|
||||
|
||||
stats = {
|
||||
'total': total,
|
||||
'active': active,
|
||||
'suspended': suspended,
|
||||
'schema_behind': schema_behind,
|
||||
'sub_counts': sub_counts,
|
||||
'trial_expiring_soon': trial_expiring_soon,
|
||||
'trial_expired': trial_expired_count,
|
||||
}
|
||||
|
||||
return render_template(
|
||||
'panel/health.html',
|
||||
rows=rows,
|
||||
stats=stats,
|
||||
chain_head=head,
|
||||
sa_username=session.get('sa_username'),
|
||||
base_domain=_BASE_DOMAIN(),
|
||||
)
|
||||
Reference in New Issue
Block a user