246 lines
7.5 KiB
HTML
246 lines
7.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Admin Settings</title>
|
|
<style>
|
|
/*
|
|
Admin settings / maintenance page.
|
|
Shows database health, statistics, upload disk usage,
|
|
and houses the dangerous system reset button.
|
|
*/
|
|
body{
|
|
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
background:#111827;
|
|
color:#f3f4f6;
|
|
margin:0;
|
|
padding:1rem;
|
|
line-height:1.5;
|
|
}
|
|
.container{max-width:900px;margin:0 auto;}
|
|
h1,h2{color:#60a5fa;}
|
|
a{color:#93c5fd;text-decoration:none;}
|
|
.topbar{
|
|
float:right;
|
|
display:flex;
|
|
gap:.75rem;
|
|
align-items:center;
|
|
}
|
|
.topbar a{
|
|
color:#f87171;
|
|
}
|
|
.section{
|
|
background:#1f2937;
|
|
padding:1rem;
|
|
border-radius:.5rem;
|
|
margin-bottom:1rem;
|
|
}
|
|
.flash{
|
|
padding:.8rem;
|
|
background:#064e3b;
|
|
border-radius:.5rem;
|
|
margin-bottom:1rem;
|
|
}
|
|
.flash.error{background:#450a0a;}
|
|
.stat-grid{
|
|
display:grid;
|
|
grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
|
|
gap:1rem;
|
|
}
|
|
.stat-card{
|
|
background:#111827;
|
|
padding:.8rem;
|
|
border-radius:.5rem;
|
|
}
|
|
.stat-card .value{
|
|
font-size:1.5rem;
|
|
font-weight:700;
|
|
color:#60a5fa;
|
|
}
|
|
.stat-card .label{
|
|
font-size:.85rem;
|
|
color:#9ca3af;
|
|
}
|
|
.health-ok{
|
|
color:#10b981;
|
|
font-weight:700;
|
|
}
|
|
.health-bad{
|
|
color:#f87171;
|
|
font-weight:700;
|
|
}
|
|
.status-list{
|
|
margin:0;
|
|
padding-left:1.2rem;
|
|
}
|
|
.status-list li{
|
|
margin-bottom:.3rem;
|
|
}
|
|
button{
|
|
padding:.7rem 1rem;
|
|
border:none;
|
|
border-radius:.5rem;
|
|
background:#3b82f6;
|
|
color:#fff;
|
|
font-weight:700;
|
|
cursor:pointer;
|
|
margin-right:.5rem;
|
|
}
|
|
button.fix{
|
|
background:#10b981;
|
|
}
|
|
button.danger{
|
|
background:#dc2626;
|
|
}
|
|
.danger-zone{
|
|
background:#450a0a;
|
|
border:1px solid #7f1d1d;
|
|
border-radius:.5rem;
|
|
padding:1rem;
|
|
}
|
|
.danger-zone h2{
|
|
color:#f87171;
|
|
margin-top:0;
|
|
}
|
|
.path{
|
|
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
|
|
font-size:.85rem;
|
|
word-break:break-all;
|
|
color:#9ca3af;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="topbar">
|
|
<a href="{{ url_for('admin_dashboard') }}">Dashboard →</a>
|
|
<a href="{{ url_for('admin_logout') }}">Log out</a>
|
|
</div>
|
|
|
|
<h1>Admin Settings</h1>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% for category, message in messages %}
|
|
<div class="flash {{ category }}">{{ message }}</div>
|
|
{% endfor %}
|
|
{% endwith %}
|
|
|
|
<!-- Database health -->
|
|
<div class="section">
|
|
<h2>Database Health</h2>
|
|
{% if health.ok %}
|
|
<p class="health-ok">✅ {{ health.message }}</p>
|
|
{% else %}
|
|
<p class="health-bad">❌ {{ health.message }}</p>
|
|
{% if health.missing_columns %}
|
|
<p>Missing columns:</p>
|
|
<ul class="status-list">
|
|
{% for col in health.missing_columns %}
|
|
<li>{{ col }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
<form method="POST" style="margin-top:.8rem">
|
|
<input type="hidden" name="action" value="fix_db">
|
|
<button type="submit" class="fix">Fix Missing Columns</button>
|
|
</form>
|
|
{% endif %}
|
|
{% endif %}
|
|
<p class="path">DB path: {{ db_path }}</p>
|
|
</div>
|
|
|
|
<!-- Statistics -->
|
|
<div class="section">
|
|
<h2>Database Statistics</h2>
|
|
<div class="stat-grid">
|
|
<div class="stat-card">
|
|
<div class="value">{{ total_records }}</div>
|
|
<div class="label">Total requests</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="value">{{ db_size }}</div>
|
|
<div class="label">Database size</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="value">{{ upload_file_count }}</div>
|
|
<div class="label">Uploaded MP3 files</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="value">{{ upload_dir_count }}</div>
|
|
<div class="label">Request upload folders</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="value">{{ upload_size }}</div>
|
|
<div class="label">Total upload size</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if status_counts %}
|
|
<h3 style="margin-top:1.2rem;color:#93c5fd">By Status</h3>
|
|
<ul class="status-list">
|
|
{% for key,count in status_counts.items() %}
|
|
<li>{{ statuses[key] }} ({{ key }}): <strong>{{ count }}</strong></li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p style="margin-top:1rem;color:#9ca3af">No requests yet.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Paths -->
|
|
<div class="section">
|
|
<h2>Paths</h2>
|
|
<p><strong>Database:</strong><br><span class="path">{{ db_path }}</span></p>
|
|
<p><strong>Uploads:</strong><br><span class="path">{{ upload_path }}</span></p>
|
|
</div>
|
|
|
|
<!-- Settings controls -->
|
|
<div class="section">
|
|
<h2>Booth Settings</h2>
|
|
<form method="POST">
|
|
<input type="hidden" name="action" value="save_max_revisions">
|
|
<label for="max_revisions">Maximum customer revisions allowed</label>
|
|
<input type="number" id="max_revisions" name="max_revisions" min="0" value="{{ current_max_revisions }}">
|
|
<p class="copy-hint">Set to 0 to disable customer-submitted revisions. Operators can still upload new versions manually.</p>
|
|
<button type="submit">Save Revision Limit</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- MP3 metadata defaults -->
|
|
<div class="section">
|
|
<h2>MP3 Metadata Tags</h2>
|
|
<p class="copy-hint">These values are written into every uploaded MP3 file. The song title from the prompt is written to the Title tag automatically.</p>
|
|
<form method="POST">
|
|
<input type="hidden" name="action" value="save_metadata">
|
|
|
|
<label for="artist">Artist</label>
|
|
<input type="text" id="artist" name="artist" value="{{ metadata.get('artist', '') }}" placeholder="e.g. Trollgorithm">
|
|
|
|
<label for="album">Album</label>
|
|
<input type="text" id="album" name="album" value="{{ metadata.get('album', '') }}" placeholder="e.g. Theme Song Booth 2026">
|
|
|
|
<label for="year">Year</label>
|
|
<input type="text" id="year" name="year" value="{{ metadata.get('year', '') }}" placeholder="e.g. 2026">
|
|
|
|
<label for="genre">Genre</label>
|
|
<input type="text" id="genre" name="genre" value="{{ metadata.get('genre', '') }}" placeholder="e.g. Country Metal">
|
|
|
|
<label for="comment">Comment</label>
|
|
<textarea id="comment" name="comment" rows="3" placeholder="e.g. Custom theme song generated at the booth">{{ metadata.get('comment', '') }}</textarea>
|
|
|
|
<button type="submit">Save Metadata Defaults</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- System reset -->
|
|
<div class="danger-zone">
|
|
<h2>⚠️ Reset System</h2>
|
|
<p>Use this only at the start of a new event. It will delete every request and every uploaded MP3 file. This cannot be undone.</p>
|
|
<form method="POST" action="{{ url_for('admin_settings') }}" onsubmit="return confirm('ARE YOU SURE? This will delete ALL requests and ALL uploaded files. This cannot be undone.')">
|
|
<input type="hidden" name="action" value="reset_system">
|
|
<button type="submit" class="danger">Reset System</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|