feat: improve upload admin workflow

Add users.is_admin SQLite migration for promoted admins.

Support curl PUT request uploads, save uploads into admin file-manager paths, default 25 GiB uploads, tabbed UI, and local dev preview.
This commit is contained in:
Ludwig Lehnert
2026-06-18 17:09:07 +00:00
parent 61f10fd994
commit a2b6054440
19 changed files with 1148 additions and 396 deletions

View File

@@ -38,6 +38,8 @@ export default async function DashboardPage({ searchParams }) {
const success = readSearchParam(resolvedSearchParams, 'success');
const totalBytes = uploads.reduce((total, item) => total + (Number(item.size_bytes) || 0), 0);
const nowTs = Date.now();
const requestedTab = readSearchParam(resolvedSearchParams, 'tab');
const activeTab = ['upload', 'files', 'requests'].includes(requestedTab) ? requestedTab : 'upload';
return (
<main className="page-shell">
@@ -65,27 +67,31 @@ export default async function DashboardPage({ searchParams }) {
<StatusMessage error={error} success={success} />
<div className="dashboard-top-grid">
<section className="panel panel-spotlight">
<nav className="tabs" aria-label="Dashboard-Bereiche">
<a className={`tab ${activeTab === 'upload' ? 'active' : ''}`} href="/manage/dashboard?tab=upload">
Upload
</a>
<a className={`tab ${activeTab === 'files' ? 'active' : ''}`} href="/manage/dashboard?tab=files">
Dateien
</a>
<a className={`tab ${activeTab === 'requests' ? 'active' : ''}`} href="/manage/dashboard?tab=requests">
Anfragen
</a>
</nav>
<div className="summary-line">
<span>{uploads.length} aktive Uploads</span>
<span>{totalBytes > 0 ? formatBytes(totalBytes) : '0 B'} genutzt</span>
</div>
{activeTab === 'upload' ? (
<section className="panel">
<h2>Neue Datei hochladen</h2>
<UploadProgressForm csrfToken={csrfToken} />
</section>
) : null}
<aside className="panel panel-soft">
<h2>Schnellüberblick</h2>
<div className="info-stack">
<div className="info-card">
<strong>{uploads.length}</strong>
<span className="muted">aktive Uploads</span>
</div>
<div className="info-card">
<strong>{totalBytes > 0 ? formatBytes(totalBytes) : '0 B'}</strong>
<span className="muted">genutzter Speicher</span>
</div>
</div>
</aside>
</div>
{activeTab === 'requests' ? (
<section className="panel">
<h2>Upload-Anfragen</h2>
<p className="muted">Benachrichtigung erfolgt an deinen Benutzernamen (E-Mail-Adresse).</p>
@@ -162,7 +168,9 @@ export default async function DashboardPage({ searchParams }) {
</div>
)}
</section>
) : null}
{activeTab === 'files' ? (
<section className="panel">
<h2>Aktuelle Uploads</h2>
{uploads.length === 0 ? (
@@ -225,6 +233,7 @@ export default async function DashboardPage({ searchParams }) {
</div>
)}
</section>
) : null}
</main>
);
}