- Fix Docker container CPU % (multiply by num_cpus) - Add missing Disk usage card to system status grid - Replace deprecated @app.on_event with lifespan context manager - Move `import requests` to module level - Remove Google Fonts CDN dependency from CSS - Update naming doc URL to real raw Gitea link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
219 lines
11 KiB
HTML
219 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>RedUnits Control Panel</title>
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
<!-- Alpine.js served locally (no CDN dependency) -->
|
|
<script defer src="/static/js/alpine.min.js"></script>
|
|
<script src="/static/js/marked.min.js"></script>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container" x-data="dashboard()" x-init="init()">
|
|
|
|
<!-- Toast notification -->
|
|
<div class="toast" x-show="actionMessage" x-transition :class="actionError ? 'toast-error' : 'toast-success'"
|
|
style="display: none;">
|
|
<span x-text="actionMessage"></span>
|
|
</div>
|
|
|
|
<header>
|
|
<div class="header-left">
|
|
<div class="logo-dot"></div>
|
|
<h1>RedUnits Control Panel</h1>
|
|
</div>
|
|
<div class="header-right">
|
|
<!-- Live WS indicator -->
|
|
<div class="indicator-group" :class="wsConnected ? 'ws-live' : 'ws-offline'">
|
|
<span class="ws-dot"></span>
|
|
<span x-text="wsConnected ? 'live' : 'reconnecting...'"></span>
|
|
</div>
|
|
<div class="last-update" x-text="lastUpdate"></div>
|
|
<button @click="refresh()" class="refresh-btn" :disabled="loading">
|
|
<span x-show="!loading">↻ refresh</span>
|
|
<span x-show="loading">⏳...</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- SYSTEM STATUS SECTION -->
|
|
<section class="section">
|
|
<h2 class="section-title">SYSTEM STATUS</h2>
|
|
<div class="system-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-label">CPU usage</div>
|
|
<div class="stat-value" x-text="system.cpu.percent + '%'"></div>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" :style="`width: ${system.cpu.percent}%`"
|
|
:class="getProgressColor(system.cpu.percent)"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-label">Memory usage</div>
|
|
<div class="stat-value" x-text="system.memory.percent + '%'"></div>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" :style="`width: ${system.memory.percent}%`"
|
|
:class="getProgressColor(system.memory.percent)"></div>
|
|
</div>
|
|
<div class="stat-detail" x-text="`${system.memory.used_gb} GB / ${system.memory.total_gb} GB`">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-label">Disk usage</div>
|
|
<div class="stat-value" x-text="system.disk.percent + '%'"></div>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" :style="`width: ${system.disk.percent}%`"
|
|
:class="getProgressColor(system.disk.percent)"></div>
|
|
</div>
|
|
<div class="stat-detail" x-text="`${system.disk.used_gb} GB / ${system.disk.total_gb} GB`"></div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-label">Docker containers</div>
|
|
<div class="stat-value" x-text="`${system.containers.running} / ${system.containers.total}`"></div>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill"
|
|
:style="`width: ${system.containers.total > 0 ? (system.containers.running / system.containers.total * 100) : 0}%`"
|
|
:class="system.containers.running === system.containers.total ? 'green' : 'red'"></div>
|
|
</div>
|
|
<div class="stat-detail">
|
|
<span x-show="system.containers.running < system.containers.total"
|
|
x-text="`${system.containers.total - system.containers.running} container${(system.containers.total - system.containers.running) > 1 ? 's' : ''} offline`"></span>
|
|
<span x-show="system.containers.running === system.containers.total">All containers
|
|
running</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-label">System uptime</div>
|
|
<div class="stat-value" x-text="system.uptime.days + ' days'"></div>
|
|
<div class="stat-status" :class="allServicesOnline() ? 'status-ok' : 'status-warn'">
|
|
<span x-show="allServicesOnline()">✓ All services operational</span>
|
|
<span x-show="!allServicesOnline()" style="display: flex; align-items: center; gap: 6px;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
<line x1="12" y1="8" x2="12" y2="12"></line>
|
|
<line x1="12" y1="16" x2="12.01" y2="16"></line>
|
|
</svg>
|
|
Some services offline
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- SERVICES SECTION -->
|
|
<section class="section">
|
|
<h2 class="section-title">SERVICES</h2>
|
|
<div class="services-grid">
|
|
<template x-for="service in services" :key="service.id">
|
|
<div class="service-card" :class="!service.status.online ? 'card-offline' : ''">
|
|
<div class="service-header">
|
|
<div class="service-name">
|
|
<span class="service-icon" x-text="service.icon"></span>
|
|
<span x-text="service.name"></span>
|
|
</div>
|
|
<div class="status-badge"
|
|
:class="service.status.online ? 'status-online' : 'status-offline'">
|
|
<span x-text="service.status.online ? 'online' : 'offline'"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="service-description" x-text="service.description"></div>
|
|
<div class="service-url" x-text="service.url"></div>
|
|
|
|
<!-- Online State Info -->
|
|
<template x-if="service.status.online">
|
|
<div class="service-metrics">
|
|
<div class="metric">
|
|
<span class="metric-label">CPU</span>
|
|
<span class="metric-value"><span x-text="service.status.cpu_percent"></span>%</span>
|
|
</div>
|
|
<div class="metric">
|
|
<span class="metric-label">RAM</span>
|
|
<span class="metric-value">
|
|
<span x-text="service.status.memory_mb"></span> <span
|
|
class="metric-unit">MB</span>
|
|
</span>
|
|
</div>
|
|
<div class="metric">
|
|
<span class="metric-label">Uptime</span>
|
|
<span class="metric-value" x-text="service.status.uptime"></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Offline State Info -->
|
|
<template x-if="!service.status.online">
|
|
<div class="offline-message">
|
|
Container is not running
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Actions for Online -->
|
|
<div class="service-actions" x-show="service.status.online" style="display: none;">
|
|
<a :href="service.url" target="_blank" class="btn btn-primary">Open</a>
|
|
<button class="btn btn-icon" @click="restartService(service.id)" title="Restart container">
|
|
↻
|
|
</button>
|
|
<button class="btn btn-outline" @click="stopService(service.id)" title="Stop container">
|
|
Stop
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Actions for Offline -->
|
|
<div class="service-actions-center" x-show="!service.status.online" style="display: none;">
|
|
<button class="btn btn-outline btn-full" @click="restartService(service.id)">
|
|
↻ Restart service
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- DOCUMENTS SECTION -->
|
|
<section class="section" x-show="documents.length > 0" style="display: none;">
|
|
<h2 class="section-title">QUICK LINKS & DOCS</h2>
|
|
<div class="docs-list">
|
|
<template x-for="doc in documents" :key="doc.id">
|
|
<div class="doc-item" @click="openDocument(doc)">
|
|
<span class="doc-icon">📄</span>
|
|
<div class="doc-info">
|
|
<div class="doc-title" x-text="doc.title"></div>
|
|
<div class="doc-desc" x-text="doc.description"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- DOCUMENT MODAL -->
|
|
<div class="modal-overlay" x-show="showDocModal" style="display: none;">
|
|
<div class="modal-content" @click.away="closeDocument()">
|
|
<div class="modal-header">
|
|
<h3 x-text="docTitle"></h3>
|
|
<button class="modal-close" @click="closeDocument()">✕</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div x-show="docLoading" class="doc-loading">⏳ Loading document...</div>
|
|
<div class="markdown-body" x-html="docContent" x-show="!docLoading"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer>
|
|
<p>RedUnits Control Panel v1.3</p>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="/static/js/app.js"></script>
|
|
</body>
|
|
|
|
</html> |