bot_host / templates /admin_stats.html
ItsBounvy's picture
Upload 21 files
4272701 verified
Raw History Blame Contribute Delete
10.4 kB
{% extends "base.html" %}
{% block title %}{{ _("System stats") }}{% endblock %}
{% block content %}
<div class="page-header">
<div>
<h1>{{ _("System stats") }}</h1>
<div class="subtitle">{{ _("Host load and a per-user breakdown of bots and resource footprint.") }}</div>
</div>
<div style="display: flex; gap: 8px; align-items: center;">
<a href="/api/admin/stats" target="_blank" class="btn btn-sm"
style="background: var(--surface-2); color: var(--text-1);">{{ panel_icon('cpu')|safe }} {{ _("JSON API") }}</a>
<button type="button" class="btn btn-sm btn-primary" onclick="location.reload();">
πŸ”„ {{ _("Refresh") }}
</button>
</div>
</div>
<div class="stats-grid">
<div class="stat-card tone-primary">
<div class="stat-label">{{ panel_icon('users')|safe }} {{ _("Users") }}</div>
<div class="stat-value">{{ user_count }}</div>
<div class="stat-foot">{{ totals.bots }} {{ ngettext("bot", "bots", totals.bots, count=totals.bots) }} {{ _("total") }}</div>
</div>
<div class="stat-card tone-success">
<div class="stat-label">{{ panel_icon('check')|safe }} {{ _("Running bots") }}</div>
<div class="stat-value">{{ totals.running }}</div>
<div class="stat-foot">{{ _("out of") }} {{ totals.bots }}</div>
</div>
<div class="stat-card tone-accent">
<div class="stat-label">{{ panel_icon('cpu')|safe }} {{ _("Host CPU") }}</div>
<div class="stat-value">{{ host.cpu_percent }}%</div>
<div class="stat-foot">{{ host.cpu_count }} {{ ngettext("core", "cores", host.cpu_count, count=host.cpu_count) }}</div>
</div>
<div class="stat-card tone-warning">
<div class="stat-label">{{ panel_icon('db')|safe }} {{ _("Host RAM") }}</div>
<div class="stat-value">{{ host.ram_percent }}%</div>
<div class="stat-foot">{{ host.ram_used_mb }} / {{ host.ram_total_mb }} MB</div>
</div>
</div>
{# ======================== Bot status breakdown ======================== #}
{# Per-status counts across the whole fleet. Always shows every known
status (running / building / error / sleeping / pending / stopped /
starting) even when the count is 0 β€” keeps the column order stable
for ops monitoring. #}
<div class="card" style="margin-top: 22px;">
<div class="card-body form-section">
<div class="form-section-title">
<span class="ico">{{ panel_icon('bot')|safe }}</span>
{{ _("Bot fleet by status") }}
</div>
<div style="display: flex; flex-wrap: wrap; gap: 14px; margin-top: 6px;">
{% for key, label, tone in [
('running', _('Running'), 'tone-success'),
('building', _('Building'), 'tone-accent'),
('starting', _('Starting'), 'tone-accent'),
('sleeping', _('Sleeping'), 'tone-warning'),
('error', _('Error'), 'tone-error'),
('pending', _('Pending'), ''),
('stopped', _('Stopped'), ''),
] %}
{% set count = totals.bots_by_status.get(key, 0) %}
<div class="stat-card {{ tone }}" style="flex: 1 1 110px; min-width: 110px; max-width: 160px; padding: 12px 14px;">
<div class="stat-label" style="font-size: 12px;">{{ label }}</div>
<div class="stat-value" style="font-size: 22px;">{{ count }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
{# ======================== Host resource bars ======================== #}
<div class="card">
<div class="card-body form-section">
<div class="form-section-title">
<span class="ico">{{ panel_icon('cpu')|safe }}</span>
{{ _("Host machine load") }}
</div>
<div style="display: flex; flex-direction: column; gap: 16px; margin-top: 4px;">
<div>
<div style="display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2); margin-bottom: 4px;">
<span>{{ _("CPU β€”") }} {{ host.cpu_count }} {{ ngettext("core", "cores", host.cpu_count, count=host.cpu_count) }}</span>
<span>{{ host.cpu_percent }}%</span>
</div>
<div style="height: 8px; border-radius: 4px; background: var(--surface-2, rgba(255,255,255,.08)); overflow: hidden;">
<div style="height: 100%; width: {{ host.cpu_percent }}%; background: var(--primary); border-radius: 4px;"></div>
</div>
</div>
<div>
<div style="display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2); margin-bottom: 4px;">
<span>{{ _("RAM") }}</span>
<span>{{ host.ram_used_mb }} / {{ host.ram_total_mb }} MB ({{ host.ram_percent }}%)</span>
</div>
<div style="height: 8px; border-radius: 4px; background: var(--surface-2, rgba(255,255,255,.08)); overflow: hidden;">
<div style="height: 100%; width: {{ host.ram_percent }}%; background: var(--warning, #f59e0b); border-radius: 4px;"></div>
</div>
</div>
<div>
<div style="display: flex; justify-content: space-between; font-size: 12px; color: var(--text-2); margin-bottom: 4px;">
<span>{{ _("Disk") }} ({{ host.disk_total_mb }} MB {{ _("total") }})</span>
<span>{{ host.disk_used_mb }} MB ({{ host.disk_percent }}%)</span>
</div>
<div style="height: 8px; border-radius: 4px; background: var(--surface-2, rgba(255,255,255,.08)); overflow: hidden;">
<div style="height: 100%; width: {{ host.disk_percent }}%; background: var(--accent-2, #06b6d4); border-radius: 4px;"></div>
</div>
</div>
</div>
<p class="muted" style="font-size: 11px; margin: 14px 0 0 0;">
{{ _("This is the machine the panel process itself runs on. Bots deployed in") }} <code>legacy_space</code> {{ _("mode run on their own separate HF Space and aren't reflected in this host's CPU/RAM β€” only in the per-user table below (allocated quota, not live usage). Bots running in") }} <code>multitenant</code> {{ _("mode (subprocess inside this Space) do show live CPU/RAM in the \"live\" columns.") }}
</p>
</div>
</div>
{# ======================== Per-user table ======================== #}
<div class="card flush" style="margin-top: 22px;">
<div class="card-header" style="padding: 16px 18px 0 18px;">
<div>
<h2>{{ _("Per-user usage") }}</h2>
<div class="desc">{{ _("Bot counts and resource footprint by account.") }}</div>
</div>
</div>
<div class="table-wrap" style="border: 0; border-radius: 0;">
<table class="table">
<thead>
<tr>
<th>{{ _("User") }}</th>
<th>{{ _("Bots") }}</th>
<th>{{ _("Running") }}</th>
<th>{{ _("By status") }}</th>
<th>{{ _("Live CPU") }}<span class="muted" style="font-weight:400; font-size:11px;"> {{ _("(multitenant)") }}</span></th>
<th>{{ _("Live RAM") }}<span class="muted" style="font-weight:400; font-size:11px;"> {{ _("(multitenant)") }}</span></th>
<th>{{ _("Allocated CPU") }}</th>
<th>{{ _("Allocated RAM") }}</th>
<th>{{ _("Storage used") }}</th>
</tr>
</thead>
<tbody>
{% for row in rows %}
<tr>
<td>
<div style="display: flex; align-items: center; gap: 10px;">
<div class="avatar" style="width: 26px; height: 26px; border-radius: 50%; background: linear-gradient(135deg, var(--primary), var(--accent-2)); display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 11px; flex: 0 0 26px;">
{{ row.user.username[:2]|upper }}
</div>
<div style="font-weight: 600;">{{ row.user.username }}</div>
</div>
</td>
<td>{{ row.bots_total }}</td>
<td>
{% if row.bots_running %}
<span class="badge status-success"><span class="dot"></span> {{ row.bots_running }}</span>
{% else %}
<span class="muted">0</span>
{% endif %}
</td>
<td>
{# Compact status pills: 🟒3 πŸ”¨1 πŸ’€2 etc. #}
{% if row.bots_by_status %}
<div style="display: flex; flex-wrap: wrap; gap: 4px; font-size: 11px;">
{% for key in ['running', 'building', 'starting', 'sleeping', 'error', 'pending', 'stopped'] %}
{% set n = row.bots_by_status.get(key, 0) %}
{% if n > 0 %}
{% set icon = {'running':'🟒','building':'πŸ”¨','starting':'⏳','sleeping':'πŸ’€','error':'πŸ”΄','pending':'⏸','stopped':'⏹'}[key] %}
<span title="{{ key }}" style="background: var(--surface-2); padding: 2px 6px; border-radius: 4px;">{{ icon }}{{ n }}</span>
{% endif %}
{% endfor %}
</div>
{% else %}
<span class="muted">β€”</span>
{% endif %}
</td>
<td>{{ row.cpu_live_percent }}%</td>
<td>{{ row.ram_live_mb }} MB</td>
<td class="mono">{{ row.cpu_alloc_cores }} / {{ row.user.cpu_quota_cores }} {{ _("cores") }}</td>
<td class="mono">{{ row.ram_alloc_mb }} / {{ row.user.ram_quota_mb }} MB</td>
<td class="mono">{{ row.storage_used_mb }} / {{ row.user.storage_quota_mb }} MB</td>
</tr>
{% else %}
<tr><td colspan="9" class="muted" style="text-align:center; padding: 24px;">{{ _("No users yet.") }}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}