Sync from main server - 2026-04-18 18:47:38
This commit is contained in:
@@ -24,28 +24,28 @@
|
||||
|
||||
<nav class="nav">
|
||||
<div class="nav-section-label">MONITOR</div>
|
||||
<a class="nav-item active" data-page="dashboard" href="#">
|
||||
<a class="nav-item {% if active_page == 'dashboard' %}active{% endif %}" href="{{ url_for('dashboard') }}">
|
||||
<i class="fas fa-gauge-high"></i><span>Dashboard</span>
|
||||
</a>
|
||||
<a class="nav-item" data-page="containers" href="#">
|
||||
<a class="nav-item {% if active_page == 'containers' %}active{% endif %}" href="{{ url_for('containers_page') }}">
|
||||
<i class="fas fa-cubes"></i><span>All Containers</span>
|
||||
<span class="nav-badge" id="nav-badge-containers">—</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section-label" style="margin-top:20px;">OPERATIONS</div>
|
||||
<a class="nav-item" data-page="restore" href="#">
|
||||
<a class="nav-item {% if active_page == 'restore' %}active{% endif %}" href="{{ url_for('restore_page') }}">
|
||||
<i class="fas fa-rotate-right"></i><span>Restore</span>
|
||||
</a>
|
||||
<a class="nav-item" data-page="backups" href="#">
|
||||
<a class="nav-item {% if active_page == 'backups' %}active{% endif %}" href="{{ url_for('backups_page') }}">
|
||||
<i class="fas fa-database"></i><span>Backups</span>
|
||||
</a>
|
||||
|
||||
<div class="nav-section-label" style="margin-top:20px;">ADMIN</div>
|
||||
<a class="nav-item" data-page="users" href="#">
|
||||
<a class="nav-item {% if active_page == 'users' %}active{% endif %}" href="{{ url_for('users_page') }}">
|
||||
<i class="fas fa-users-gear"></i><span>Users</span>
|
||||
<span class="nav-badge" id="nav-badge-users">—</span>
|
||||
</a>
|
||||
<a class="nav-item" data-page="settings" href="#">
|
||||
<a class="nav-item {% if active_page == 'settings' %}active{% endif %}" href="{{ url_for('settings_page') }}">
|
||||
<i class="fas fa-sliders"></i><span>Settings</span>
|
||||
</a>
|
||||
</nav>
|
||||
@@ -67,8 +67,8 @@
|
||||
<main class="main">
|
||||
<header class="topbar">
|
||||
<div class="topbar-left">
|
||||
<h1 class="page-title" id="page-title">Dashboard</h1>
|
||||
<span class="page-subtitle" id="page-subtitle">{{ main_server }}</span>
|
||||
<h1 class="page-title" id="page-title">{{ page_title or 'Dashboard' }}</h1>
|
||||
<span class="page-subtitle" id="page-subtitle">{{ page_subtitle or main_server }}</span>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<button class="icon-btn" onclick="refreshAll()" title="Refresh">
|
||||
@@ -84,7 +84,7 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/platform.js') }}?v=navbadges2"></script>
|
||||
<script>
|
||||
// ── Theme persistence ───────────────────────────────────────
|
||||
(function() {
|
||||
|
||||
65
platform/templates/pages/backups.html
Normal file
65
platform/templates/pages/backups.html
Normal file
@@ -0,0 +1,65 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-shield-halved"></i> Manual Backup</div>
|
||||
</div>
|
||||
<div style="padding:4px 0 12px;">
|
||||
<p style="color:var(--text2);font-size:13px;margin-bottom:14px;">Manually trigger a backup of all containers</p>
|
||||
<button class="btn btn-primary" onclick="runManualBackup()" id="manual-backup-btn">
|
||||
<i class="fas fa-play"></i> Run Backup Now
|
||||
</button>
|
||||
</div>
|
||||
<div id="manual-backup-wrapper" style="display:none; margin-top:16px;">
|
||||
<div class="card-header" style="margin-bottom:8px;">
|
||||
<div class="card-title" style="font-size:13px;"><i class="fas fa-terminal"></i> Backup Log</div>
|
||||
<span class="badge" id="manual-backup-badge" style="background:rgba(59,130,246,0.15);color:var(--accent2);">Running…</span>
|
||||
</div>
|
||||
<div id="manual-backup-log" class="log-console" style="max-height:240px;"></div>
|
||||
<div style="color:var(--text3);font-size:11px;margin-top:6px;font-family:var(--mono);" id="manual-backup-elapsed"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-database"></i> Available Backups</div>
|
||||
<button class="btn btn-ghost btn-sm" onclick="refreshBackupsList()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
||||
</div>
|
||||
<div class="two-col">
|
||||
<div>
|
||||
<div class="section-header"><div class="section-title">🖥️ MAIN SERVER</div><span class="card-meta">/root/backups/</span></div>
|
||||
<div class="backup-list" id="local-backup-list">
|
||||
{% for b in backups %}
|
||||
<div class="backup-item"><span class="backup-name">{{ b }}</span><div class="backup-actions"><button class="btn btn-ghost btn-sm btn-audit" onclick="auditBackup('local','{{ b }}',this)"><i class="fas fa-shield-check"></i> Audit</button><button class="btn btn-ghost btn-sm" onclick="quickRestore('local','{{ b }}')">↩ Restore</button><button class="btn btn-ghost btn-sm btn-delete-backup" onclick="deleteBackup('local','{{ b }}',this)"><i class="fas fa-trash"></i></button></div></div>
|
||||
{% else %}<div class="empty-state"><i class="fas fa-inbox"></i>No backups</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="section-header"><div class="section-title">💾 VM SERVER</div><span class="card-meta">/backups/main-server/</span></div>
|
||||
<div class="backup-list" id="vm-backup-list">
|
||||
{% for b in vm_backups %}
|
||||
<div class="backup-item"><span class="backup-name">{{ b }}</span><div class="backup-actions"><button class="btn btn-ghost btn-sm btn-audit" onclick="auditBackup('vm','{{ b }}',this)"><i class="fas fa-shield-check"></i> Audit</button><button class="btn btn-ghost btn-sm" onclick="quickRestore('vm','{{ b }}')">↩ Restore</button><button class="btn btn-ghost btn-sm btn-delete-backup" onclick="deleteBackup('vm','{{ b }}',this)"><i class="fas fa-trash"></i></button></div></div>
|
||||
{% else %}<div class="empty-state"><i class="fas fa-inbox"></i>No VM backups</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="audit-modal" class="modal-overlay" style="display:none;" onclick="closeAuditModal(event)">
|
||||
<div class="modal-box" onclick="event.stopPropagation()">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title"><i class="fas fa-shield-halved" style="color:var(--cyan);"></i> Backup Health Audit</div>
|
||||
<button class="modal-close" onclick="closeAuditModal()" title="Close">✕</button>
|
||||
</div>
|
||||
<div id="audit-modal-content"><div class="empty-state"><i class="fas fa-spinner fa-spin"></i> Running audit…</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:0;">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-clock-rotate-left"></i> Backup History</div>
|
||||
<button class="btn btn-ghost btn-sm" onclick="loadBackupLog()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
||||
</div>
|
||||
<div id="backup-history-list"><div class="empty-state"><i class="fas fa-spinner fa-spin"></i> Loading…</div></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
36
platform/templates/pages/containers.html
Normal file
36
platform/templates/pages/containers.html
Normal file
@@ -0,0 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-layer-group"></i> All Containers</div>
|
||||
<div style="display:flex;gap:8px;align-items:center;">
|
||||
<span class="card-meta" id="all-ctr-meta">—</span>
|
||||
<button class="btn btn-ghost btn-sm" onclick="toggleExtraColumns('all')" id="all-toggle-btn">
|
||||
<i class="fas fa-eye"></i> Show more
|
||||
</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick="loadAllContainers()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="ct-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NAME</th>
|
||||
<th>OWNER</th>
|
||||
<th>STATUS</th>
|
||||
<th>CPU</th>
|
||||
<th>MEMORY</th>
|
||||
<th>NET I/O</th>
|
||||
<th class="col-extra all-extra" style="display:none;">DISK I/O</th>
|
||||
<th class="col-extra all-extra" style="display:none;">IMAGE</th>
|
||||
<th class="col-extra all-extra" style="display:none;">PORTS</th>
|
||||
<th>ACTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="all-containers-body">
|
||||
<tr><td colspan="10"><div class="empty-state"><i class="fas fa-spinner fa-spin"></i> Loading…</div></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
108
platform/templates/pages/dashboard.html
Normal file
108
platform/templates/pages/dashboard.html
Normal file
@@ -0,0 +1,108 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% macro ctr_actions(name) %}
|
||||
<button class="ctr-action-btn restart" title="Restart" onclick="ctrAction('{{ name }}','restart',this)">
|
||||
<i class="fas fa-rotate-right"></i>
|
||||
</button>
|
||||
<button class="ctr-action-btn stop" title="Stop" onclick="ctrAction('{{ name }}','stop',this)">
|
||||
<i class="fas fa-stop"></i>
|
||||
</button>
|
||||
<button class="ctr-action-btn start" title="Start" onclick="ctrAction('{{ name }}','start',this)">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="metrics-row">
|
||||
<div class="metric-card cpu">
|
||||
<div class="metric-label">CPU USAGE</div>
|
||||
<div class="metric-value" id="m-cpu">{{ system.cpu_pct }}<span>%</span></div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-cpu" style="width:{{ system.cpu_pct }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card mem">
|
||||
<div class="metric-label">MEMORY</div>
|
||||
<div class="metric-value" id="m-mem" style="font-size:16px;">{{ system.memory }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-mem" style="width:{{ system.mem_pct }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card disk">
|
||||
<div class="metric-label">DISK /</div>
|
||||
<div class="metric-value" id="m-disk" style="font-size:16px;">{{ system.disk }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-disk" style="width:{{ system.disk_pct }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card load">
|
||||
<div class="metric-label">LOAD AVG</div>
|
||||
<div class="metric-value" id="m-load" style="font-size:16px;">{{ system.load }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-load" style="width:10%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-chart-line"></i> Overview</div>
|
||||
<span class="card-meta">Docker {{ system.docker_v }} · {{ main_server }}</span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-card"><div class="stat-number" id="stat-total">{{ containers|length }}</div><div class="stat-label">App Containers</div></div>
|
||||
<div class="stat-card"><div class="stat-number" id="stat-running">{{ running_count }}</div><div class="stat-label">Running</div></div>
|
||||
<div class="stat-card"><div class="stat-number" id="stat-users">{{ users|length }}</div><div class="stat-label">Linux Users</div></div>
|
||||
<div class="stat-card"><div class="stat-number" id="stat-local-bk">{{ backups|length }}</div><div class="stat-label">Local Backups</div></div>
|
||||
<div class="stat-card"><div class="stat-number" id="stat-vm-bk">{{ vm_backups|length }}</div><div class="stat-label">VM Backups</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-cubes"></i> App Containers</div>
|
||||
<div style="display:flex;align-items:center;gap:10px;">
|
||||
<span class="card-meta">Auto-refresh 15s</span>
|
||||
<button class="btn btn-ghost btn-sm" onclick="toggleExtraColumns('app')" id="app-toggle-btn">
|
||||
<i class="fas fa-eye"></i> Show more
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="ct-table" id="app-containers-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NAME</th>
|
||||
<th>STATUS</th>
|
||||
<th>CPU</th>
|
||||
<th>MEMORY</th>
|
||||
<th>NET I/O</th>
|
||||
<th class="col-extra app-extra" style="display:none;">DISK I/O</th>
|
||||
<th class="col-extra app-extra" style="display:none;">IMAGE</th>
|
||||
<th class="col-extra app-extra" style="display:none;">PORTS</th>
|
||||
<th>ACTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="app-containers-body">
|
||||
{% for c in containers %}
|
||||
<tr data-ctr="{{ c.name }}">
|
||||
<td class="ct-name">{{ c.name }}</td>
|
||||
<td class="ctr-status-cell" data-ctr="{{ c.name }}">
|
||||
{% if 'Up' in c.status %}
|
||||
<span class="badge badge-run">Running</span>
|
||||
{% else %}
|
||||
<span class="badge badge-stop">Stopped</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="stat-pct" data-ctr="{{ c.name }}" data-stat="cpu">—</span></td>
|
||||
<td>
|
||||
<div class="stat-bar-wrap">
|
||||
<div class="stat-bar-bg"><div class="stat-bar-fill" data-ctr="{{ c.name }}" data-stat="mem_bar" style="width:0%"></div></div>
|
||||
<span class="stat-pct" data-ctr="{{ c.name }}" data-stat="mem_pct">—</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="stat-pct" data-ctr="{{ c.name }}" data-stat="net" style="color:var(--cyan)">—</span></td>
|
||||
<td class="col-extra app-extra" style="display:none;"><span class="stat-pct" data-ctr="{{ c.name }}" data-stat="block" style="color:var(--yellow)">—</span></td>
|
||||
<td class="col-extra app-extra ct-image" style="display:none;">{{ c.image }}</td>
|
||||
<td class="col-extra app-extra ct-ports" style="display:none;">{{ c.ports or '—' }}</td>
|
||||
<td><div class="action-btns">{{ ctr_actions(c.name) }}</div></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="9"><div class="empty-state"><i class="fas fa-inbox"></i>No containers</div></td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
106
platform/templates/pages/restore.html
Normal file
106
platform/templates/pages/restore.html
Normal file
@@ -0,0 +1,106 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-rotate-right"></i> Restore Configuration</div>
|
||||
</div>
|
||||
|
||||
<div class="restore-form">
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">STEP 1 — SELECT BACKUP SOURCE</div>
|
||||
<div class="radio-group">
|
||||
<label class="radio-card">
|
||||
<input type="radio" name="backup_source" value="local" checked onchange="updateBackupList()">
|
||||
<div class="radio-body"><span class="radio-icon">🖥️</span><div><div class="radio-label">Main Server</div><div class="radio-desc"><code>/root/backups/</code></div></div></div>
|
||||
</label>
|
||||
<label class="radio-card">
|
||||
<input type="radio" name="backup_source" value="vm" onchange="updateBackupList()">
|
||||
<div class="radio-body"><span class="radio-icon">💾</span><div><div class="radio-label">VM Backup Server</div><div class="radio-desc"><code>/backups/main-server/</code></div></div></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:14px; max-width:500px;">
|
||||
<label class="form-label">BACKUP FILE</label>
|
||||
<select id="backup-file-select" class="form-input">
|
||||
<optgroup label="Main Server" id="local-options">
|
||||
{% for b in backups %}<option value="{{ b }}" data-source="local">{{ b }}</option>{% else %}<option disabled>No local backups</option>{% endfor %}
|
||||
</optgroup>
|
||||
<optgroup label="VM Backups" id="vm-options" style="display:none;">
|
||||
{% for b in vm_backups %}<option value="{{ b }}" data-source="vm">{{ b }}</option>{% else %}<option disabled>No VM backups</option>{% endfor %}
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">STEP 2 — SELECT RESTORE TARGET</div>
|
||||
<div class="radio-group">
|
||||
<label class="radio-card">
|
||||
<input type="radio" name="restore_target" value="local" checked onchange="toggleRemoteFields()">
|
||||
<div class="radio-body"><span class="radio-icon">🎯</span><div><div class="radio-label">Restore on This Server</div><div class="radio-desc" id="this-server-desc">Loading hostname…</div></div></div>
|
||||
</label>
|
||||
<label class="radio-card">
|
||||
<input type="radio" name="restore_target" value="remote" onchange="toggleRemoteFields()">
|
||||
<div class="radio-body"><span class="radio-icon">📡</span><div><div class="radio-label">External Machine</div><div class="radio-desc">via SSH — any IP</div></div></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="remote-fields" style="display:none; margin-top:16px; max-width:560px;">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">TARGET IP</label>
|
||||
<input type="text" id="remote-ip" class="form-input" placeholder="192.168.x.x or IP">
|
||||
</div>
|
||||
<div class="form-group" style="max-width:100px;">
|
||||
<label class="form-label">SSH PORT</label>
|
||||
<input type="text" id="remote-port" class="form-input" value="22">
|
||||
</div>
|
||||
<div class="form-group" style="max-width:120px;">
|
||||
<label class="form-label">SSH USER</label>
|
||||
<input type="text" id="remote-user" class="form-input" value="root">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:8px;">
|
||||
<label class="form-label">AUTHENTICATION</label>
|
||||
<div class="radio-group" style="gap:8px;">
|
||||
<label class="radio-card small">
|
||||
<input type="radio" name="auth_method" value="key" checked onchange="toggleAuthFields()">
|
||||
<div class="radio-body"><span>🔑</span><div class="radio-label">SSH Key</div></div>
|
||||
</label>
|
||||
<label class="radio-card small">
|
||||
<input type="radio" name="auth_method" value="password" onchange="toggleAuthFields()">
|
||||
<div class="radio-body"><span>🔒</span><div class="radio-label">Password</div></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="key-field" class="form-group" style="margin-top:8px;">
|
||||
<label class="form-label">SSH KEY PATH</label>
|
||||
<input type="text" id="ssh-key-path" class="form-input" value="/root/.ssh/contabo-key">
|
||||
</div>
|
||||
<div id="password-field" class="form-group" style="display:none; margin-top:8px;">
|
||||
<label class="form-label">SSH PASSWORD</label>
|
||||
<input type="password" id="ssh-password" class="form-input">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<button class="btn btn-danger btn-lg" onclick="launchRestore()" id="restore-btn">
|
||||
<i class="fas fa-play"></i> Start Restore
|
||||
</button>
|
||||
<p style="color:var(--text3); font-size:12px; margin-top:8px;">⚠ Healthy running containers are skipped.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="restore-log-wrapper" style="display:none; margin-top:20px;">
|
||||
<div class="card-header" style="margin-bottom:10px;">
|
||||
<div class="card-title"><i class="fas fa-terminal"></i> Restore Log</div>
|
||||
<span class="badge" id="restore-status-badge" style="background:rgba(59,130,246,0.15);color:var(--accent2);">Running…</span>
|
||||
</div>
|
||||
<div id="restore-log" class="log-console"></div>
|
||||
<div style="color:var(--text3);font-size:11px;margin-top:6px;font-family:var(--mono);" id="restore-elapsed"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.restorePrefill = {{ restore_prefill|tojson }};
|
||||
</script>
|
||||
{% endblock %}
|
||||
14
platform/templates/pages/settings.html
Normal file
14
platform/templates/pages/settings.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header"><div class="card-title"><i class="fas fa-sliders"></i> Platform Settings</div></div>
|
||||
<div style="max-width:460px; display:flex; flex-direction:column; gap:14px;">
|
||||
<div class="form-group"><label class="form-label">MAIN SERVER IP</label><input class="form-input" value="{{ main_server }}" readonly></div>
|
||||
<div class="form-group"><label class="form-label">PLATFORM HOST</label><input class="form-input" value="{{ 'Main Server' if running_on_main else 'VM (backup mode)' }}" readonly></div>
|
||||
<div class="form-group"><label class="form-label">VM BACKUP PATH</label><input class="form-input" value="/backups/main-server/" readonly></div>
|
||||
<div class="form-group"><label class="form-label">MAIN SERVER UPTIME</label><input class="form-input" id="settings-uptime" value="{{ system.uptime }}" readonly></div>
|
||||
<div class="form-group"><label class="form-label">DOCKER VERSION</label><input class="form-input" value="{{ system.docker_v }}" readonly></div>
|
||||
<button class="btn btn-ghost" onclick="refreshAll()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
44
platform/templates/pages/users.html
Normal file
44
platform/templates/pages/users.html
Normal file
@@ -0,0 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header"><div class="card-title"><i class="fas fa-user-plus"></i> Create New User</div></div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">USERNAME</label><input type="text" id="new-username" class="form-input" placeholder="e.g. secuser2"></div>
|
||||
<div class="form-group"><label class="form-label">PASSWORD (optional)</label><input type="password" id="new-password" class="form-input"></div>
|
||||
<div class="form-group"><label class="form-label">DISK SIZE MB (creates dedicated virtual disk)</label><input type="number" id="new-quota" class="form-input" placeholder="e.g. 10240 for 10GB"></div>
|
||||
<div class="form-group" style="justify-content:flex-end;">
|
||||
<label class="form-check" style="margin-bottom:8px;"><input type="checkbox" id="new-docker" checked><span>Setup rootless Docker</span></label>
|
||||
<button class="btn btn-primary" onclick="createUser()"><i class="fas fa-plus"></i> Create User</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="create-user-result" class="alert" style="margin-top:14px;"></div>
|
||||
<div id="create-user-log" class="log-console" style="display:none; margin-top:12px; max-height:200px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-users"></i> System Users</div>
|
||||
<button class="btn btn-ghost btn-sm" onclick="loadUsers()"><i class="fas fa-sync-alt"></i> Refresh</button>
|
||||
</div>
|
||||
<div class="users-grid" id="users-grid">
|
||||
{% for u in users %}
|
||||
<div class="user-card" onclick="loadUserContainers('{{ u.name }}')">
|
||||
<div class="user-card-top"><div class="user-avatar" style="background:#3b82f6">{{ u.name[0].upper() }}</div><div><div class="user-name">{{ u.name }}</div><div class="user-uid">uid {{ u.uid }}</div></div></div>
|
||||
<div class="user-tags">{% if u.has_docker %}<span class="user-tag docker"><i class="fab fa-docker"></i> docker</span>{% endif %}{% if u.linger %}<span class="user-tag linger">linger</span>{% endif %}{% if u.has_vdisk %}<span class="user-tag" style="background:rgba(34,211,238,0.12);color:var(--cyan);">💾 vdisk</span>{% endif %}</div>
|
||||
<div class="user-stats"><div class="user-stat">Disk: <strong>{{ u.disk_used }}</strong></div><div class="user-stat">Ctrs: <strong>{{ u.container_count }}</strong></div></div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state" style="grid-column:1/-1"><i class="fas fa-user-slash"></i>No non-system users</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" id="user-detail-panel" style="display:none;">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-cube"></i> <span id="user-detail-title">Containers</span></div>
|
||||
<div style="display:flex;gap:8px;"><button class="btn btn-ghost btn-sm" style="color:var(--red);" onclick="deleteUser()"><i class="fas fa-trash"></i> Delete</button><button class="btn btn-ghost btn-sm" onclick="document.getElementById('user-detail-panel').style.display='none'">✕</button></div>
|
||||
</div>
|
||||
<div style="overflow-x:auto;"><table class="ct-table"><thead><tr><th>NAME</th><th>STATUS</th><th>IMAGE</th><th>PORTS</th></tr></thead><tbody id="user-containers-body"></tbody></table></div>
|
||||
<div id="user-action-result" class="alert" style="margin-top:14px;"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user