feat: add scoped upload tokens

This commit is contained in:
Ludwig Lehnert
2026-06-18 19:17:51 +00:00
parent a2b6054440
commit 0ed32f7623
17 changed files with 592 additions and 95 deletions

View File

@@ -15,6 +15,8 @@ import {
} from '@/src/lib/format.js';
import { ensureCsrfToken, requireAdminUser } from '@/src/lib/security.js';
import { ActionDialog } from '../../_components/action-dialog.js';
import { AdminTabs } from '../../_components/admin-tabs.js';
import { CopyLinkButton } from '../../_components/copy-link-button.js';
import { StatusMessage } from '../../_components/status-message.js';
@@ -77,38 +79,16 @@ export default async function AdminDashboardPage({ searchParams }) {
<p className="muted">Metriken, Ereignisse und direkte Eingriffe in Uploads.</p>
</div>
<div className="toolbar">
<a className="chip primary" href="/manage/admin/files">
Dateimanager
</a>
<a className="chip primary" href="/manage/admin/users">
Benutzer verwalten
</a>
<form className="inline-form" action={adminLogoutAction}>
<input type="hidden" name="csrfToken" value={csrfToken} />
<button className="btn secondary" type="submit">
Abmelden
</button>
</form>
</div>
<form className="inline-form" action={adminLogoutAction}>
<input type="hidden" name="csrfToken" value={csrfToken} />
<button className="btn secondary" type="submit">
Abmelden
</button>
</form>
</header>
<StatusMessage error={error} success={success} />
<nav className="tabs" aria-label="Admin-Bereiche">
<a className={`tab ${activeTab === 'stats' ? 'active' : ''}`} href="/manage/admin/dashboard?tab=stats">
Statistik
</a>
<a className={`tab ${activeTab === 'uploads' ? 'active' : ''}`} href="/manage/admin/dashboard?tab=uploads">
Uploads
</a>
<a className={`tab ${activeTab === 'requests' ? 'active' : ''}`} href="/manage/admin/dashboard?tab=requests">
Anfragen
</a>
<a className={`tab ${activeTab === 'logs' ? 'active' : ''}`} href="/manage/admin/dashboard?tab=logs">
Logs
</a>
</nav>
<AdminTabs active={activeTab} />
{activeTab === 'stats' ? (
<section className="panel">
@@ -182,9 +162,7 @@ export default async function AdminDashboardPage({ searchParams }) {
<div className="muted">Noch {formatCountdown(item.expires_at)}</div>
</td>
<td>
<details className="action-menu">
<summary>Aktionen</summary>
<div className="action-menu-panel">
<ActionDialog title={item.original_name}>
<div className="row-actions">
<a className="btn secondary" href={sharePath}>
Download
@@ -218,8 +196,7 @@ export default async function AdminDashboardPage({ searchParams }) {
Löschen
</button>
</form>
</div>
</details>
</ActionDialog>
</td>
</tr>
);
@@ -281,9 +258,7 @@ export default async function AdminDashboardPage({ searchParams }) {
)}
</td>
<td>
<details className="action-menu">
<summary>Aktionen</summary>
<div className="action-menu-panel">
<ActionDialog title={`Anfrage ${entry.id}`}>
<a className="btn secondary" href={requestPath}>
Öffnen
</a>
@@ -304,8 +279,7 @@ export default async function AdminDashboardPage({ searchParams }) {
</button>
</form>
) : null}
</div>
</details>
</ActionDialog>
</td>
</tr>
);