feat: add admin console, cleanup, and thumbnail workers

- Implement a token-authenticated admin console at `/admin` with overview metrics and file management.
- Add a background worker to periodically clean up expired boxes based on `WARPBOX_CLEANUP_EVERY`.
- Add a background worker to generate image and video thumbnails based on `WARPBOX_THUMBNAIL_EVERY`.
- Update file storage paths to use `@each@` and `@thumb@` prefixes to separate original files from thumbnails.
- Add severity fields to startup logs and update configuration templates.
This commit is contained in:
2026-05-25 16:52:57 +03:00
parent e12878887c
commit 26619bacbc
28 changed files with 1576 additions and 178 deletions

View File

@@ -0,0 +1,95 @@
{{define "admin.html"}}{{template "base" .}}{{end}}
{{define "content"}}
<section class="admin-view" aria-labelledby="admin-title">
<div class="admin-header">
<div>
<p class="kicker">Operator console</p>
<h1 id="admin-title">Admin overview</h1>
</div>
<form action="/admin/logout" method="post">
<button class="button button-outline" type="submit">Logout</button>
</form>
</div>
<div class="metric-grid">
<article class="metric-card">
<span>Total boxes</span>
<strong>{{.Data.Stats.TotalBoxes}}</strong>
</article>
<article class="metric-card">
<span>Total files</span>
<strong>{{.Data.Stats.TotalFiles}}</strong>
</article>
<article class="metric-card">
<span>Storage used</span>
<strong>{{.Data.Stats.TotalSizeLabel}}</strong>
</article>
<article class="metric-card">
<span>Uploads 24h</span>
<strong>{{.Data.Stats.UploadsLast24H}}</strong>
</article>
<article class="metric-card">
<span>Protected</span>
<strong>{{.Data.Stats.ProtectedBoxes}}</strong>
</article>
<article class="metric-card">
<span>Expired</span>
<strong>{{.Data.Stats.ExpiredBoxes}}</strong>
</article>
</div>
<div class="card admin-table-card">
<div class="card-content">
<div class="table-header">
<div>
<h2>Recent uploads</h2>
<p>View or remove anonymous boxes.</p>
</div>
<a class="button button-outline" href="/admin/files">View all</a>
</div>
<div class="admin-table-wrap">
<table class="admin-table">
<thead>
<tr>
<th>Box</th>
<th>Files</th>
<th>Size</th>
<th>Downloads</th>
<th>Created</th>
<th>Expires</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .Data.Boxes}}
<tr>
<td><code>{{.ID}}</code></td>
<td>{{.FileCount}}</td>
<td>{{.TotalSizeLabel}}</td>
<td>{{.DownloadCount}}{{if .MaxDownloads}} / {{.MaxDownloads}}{{end}}</td>
<td>{{.CreatedAt}}</td>
<td>{{.ExpiresAt}}</td>
<td>
{{if .Expired}}<span class="badge">expired</span>{{else}}<span class="badge">active</span>{{end}}
{{if .Protected}}<span class="badge">protected</span>{{end}}
</td>
<td class="table-actions">
<a class="button button-outline" href="/d/{{.ID}}">View</a>
<form action="/admin/boxes/{{.ID}}/delete" method="post">
<button class="button button-danger" type="submit">Delete</button>
</form>
</td>
</tr>
{{else}}
<tr><td colspan="8">No uploads yet.</td></tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
</section>
{{end}}