- Add HTTPSConfig model for managing HTTPS settings - Add admin routes for HTTPS configuration management - Add beautiful admin template for HTTPS configuration - Add database migration for https_config table - Add CLI utility for HTTPS management - Add setup script for automated configuration - Add Caddy configuration generator and manager - Add comprehensive documentation (3 guides) - Add HTTPS Configuration card to admin dashboard - Implement input validation and security features - Add admin-only access control with audit trail - Add real-time configuration preview - Integrate with existing Caddy reverse proxy Features: - Enable/disable HTTPS from web interface - Configure domain, hostname, IP address, port - Automatic SSL certificate management via Let's Encrypt - Real-time Caddyfile generation and reload - Full audit trail with admin username and timestamps - Support for HTTPS and HTTP fallback access points - Beautiful, mobile-responsive UI Modified files: - app/models/__init__.py (added HTTPSConfig import) - app/blueprints/admin.py (added HTTPS routes) - app/templates/admin/admin.html (added HTTPS card) - docker-compose.yml (added Caddyfile mount and admin port) New files: - app/models/https_config.py - app/blueprints/https_config.html - app/utils/caddy_manager.py - https_manager.py - setup_https.sh - migrations/add_https_config_table.py - migrations/add_email_to_https_config.py - HTTPS_STATUS.txt - Documentation files (3 markdown guides)
196 lines
4.8 KiB
HTML
Executable File
196 lines
4.8 KiB
HTML
Executable File
{% extends "base.html" %}
|
||
|
||
{% block title %}Players - DigiServer v2{% endblock %}
|
||
|
||
{% block content %}
|
||
<style>
|
||
body.dark-mode h1 {
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.players-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
}
|
||
|
||
.players-table thead tr {
|
||
background: #f8f9fa;
|
||
text-align: left;
|
||
}
|
||
|
||
body.dark-mode .players-table thead tr {
|
||
background: #1a202c;
|
||
}
|
||
|
||
.players-table th {
|
||
padding: 12px;
|
||
border-bottom: 2px solid #dee2e6;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
}
|
||
|
||
body.dark-mode .players-table th {
|
||
border-bottom-color: #4a5568;
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.players-table tbody tr {
|
||
border-bottom: 1px solid #dee2e6;
|
||
}
|
||
|
||
body.dark-mode .players-table tbody tr {
|
||
border-bottom-color: #4a5568;
|
||
}
|
||
|
||
.players-table tbody tr:hover {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
body.dark-mode .players-table tbody tr:hover {
|
||
background: #2d3748;
|
||
}
|
||
|
||
.players-table td {
|
||
padding: 12px;
|
||
color: #2d3748;
|
||
}
|
||
|
||
body.dark-mode .players-table td {
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.players-table td strong {
|
||
color: #2d3748;
|
||
}
|
||
|
||
body.dark-mode .players-table td strong {
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.players-table code {
|
||
background: #f8f9fa;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
font-size: 0.9em;
|
||
}
|
||
|
||
body.dark-mode .players-table code {
|
||
background: #1a202c;
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 3px 8px;
|
||
border-radius: 3px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: white;
|
||
}
|
||
|
||
.status-badge.online {
|
||
background: #28a745;
|
||
}
|
||
|
||
.status-badge.offline {
|
||
background: #6c757d;
|
||
}
|
||
|
||
.text-muted {
|
||
color: #6c757d;
|
||
}
|
||
|
||
body.dark-mode .text-muted {
|
||
color: #718096;
|
||
}
|
||
|
||
.info-box {
|
||
background: #d1ecf1;
|
||
border: 1px solid #bee5eb;
|
||
color: #0c5460;
|
||
padding: 15px;
|
||
border-radius: 5px;
|
||
}
|
||
|
||
body.dark-mode .info-box {
|
||
background: #1a365d;
|
||
border-color: #2c5282;
|
||
color: #90cdf4;
|
||
}
|
||
|
||
.info-box a {
|
||
color: #0c5460;
|
||
text-decoration: underline;
|
||
}
|
||
|
||
body.dark-mode .info-box a {
|
||
color: #90cdf4;
|
||
}
|
||
</style>
|
||
<div class="container">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||
<h1>Players</h1>
|
||
<a href="{{ url_for('players.add_player') }}" class="btn btn-success">+ Add New Player</a>
|
||
</div>
|
||
|
||
{% if players %}
|
||
<div class="card">
|
||
<table class="players-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Hostname</th>
|
||
<th>Location</th>
|
||
<th>Orientation</th>
|
||
<th>Status</th>
|
||
<th>Last Seen</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for player in players %}
|
||
<tr>
|
||
<td>
|
||
<strong>{{ player.name }}</strong>
|
||
</td>
|
||
<td>
|
||
<code>{{ player.hostname }}</code>
|
||
</td>
|
||
<td>
|
||
{{ player.location or '-' }}
|
||
</td>
|
||
<td>
|
||
{{ player.orientation or 'Landscape' }}
|
||
</td>
|
||
<td>
|
||
{% if player.is_online %}
|
||
<span class="status-badge online">Online</span>
|
||
{% else %}
|
||
<span class="status-badge offline">Offline</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if player.last_seen %}
|
||
{{ player.last_seen | localtime }}
|
||
{% else %}
|
||
<span class="text-muted">Never</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<a href="{{ url_for('players.manage_player', player_id=player.id) }}"
|
||
class="btn btn-info btn-sm" title="Manage Player">
|
||
⚙️ Manage
|
||
</a>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% else %}
|
||
<div class="info-box">
|
||
ℹ️ No players yet. <a href="{{ url_for('players.add_player') }}">Add your first player</a>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|