""" 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(), )