extended expressjs admin dashboard

This commit is contained in:
Ludwig Lehnert
2026-01-12 18:27:05 +01:00
parent 3e5c36f8d8
commit f0168139f3

View File

@@ -510,6 +510,7 @@ app.get(`${basePath}/admin/dashboard`, requireAdminPage, async (req, res) => {
totalDeletes, totalDeletes,
lastCleanup, lastCleanup,
recentLogs, recentLogs,
allUploads,
] = await Promise.all([ ] = await Promise.all([
get('SELECT COUNT(*) as count FROM uploads'), get('SELECT COUNT(*) as count FROM uploads'),
get('SELECT COALESCE(SUM(size_bytes), 0) as total FROM uploads'), get('SELECT COALESCE(SUM(size_bytes), 0) as total FROM uploads'),
@@ -518,6 +519,7 @@ app.get(`${basePath}/admin/dashboard`, requireAdminPage, async (req, res) => {
get('SELECT COUNT(*) as count FROM admin_logs WHERE event IN (?, ?)', ['delete', 'cleanup']), get('SELECT COUNT(*) as count FROM admin_logs WHERE event IN (?, ?)', ['delete', 'cleanup']),
get('SELECT MAX(created_at) as ts FROM admin_logs WHERE event = ?', ['cleanup']), get('SELECT MAX(created_at) as ts FROM admin_logs WHERE event = ?', ['cleanup']),
all('SELECT event, owner, detail, created_at FROM admin_logs ORDER BY created_at DESC LIMIT 500'), all('SELECT event, owner, detail, created_at FROM admin_logs ORDER BY created_at DESC LIMIT 500'),
all('SELECT id, owner, original_name, stored_name, size_bytes, expires_at FROM uploads ORDER BY uploaded_at DESC'),
]); ]);
const stats = ` const stats = `
@@ -540,6 +542,33 @@ app.get(`${basePath}/admin/dashboard`, requireAdminPage, async (req, res) => {
</tr> </tr>
`).join(''); `).join('');
const adminUploadsRows = allUploads.map((item) => {
const fileUrl = `/_share/${item.stored_name}`;
return `
<tr>
<td>${item.owner}</td>
<td>
<div><strong>${item.original_name}</strong></div>
<div class="muted"><a href="${fileUrl}" target="_blank" rel="noopener">${item.stored_name}</a></div>
</td>
<td>${formatBytes(item.size_bytes)}</td>
<td>
<div>${formatTimestamp(item.expires_at)}</div>
<div class="muted">Noch ${formatCountdown(item.expires_at)}</div>
</td>
<td class="actions">
<form method="post" action="${baseUrl(`/admin/files/${item.id}/delete`)}">
<button type="submit" class="secondary">Löschen</button>
</form>
<form method="post" action="${baseUrl(`/admin/files/${item.id}/extend`)}">
<input name="extendHours" placeholder="Stunden hinzufügen" />
<button type="submit">Verlängern</button>
</form>
</td>
</tr>
`;
}).join('');
const body = ` const body = `
<header> <header>
<div> <div>
@@ -570,10 +599,65 @@ app.get(`${basePath}/admin/dashboard`, requireAdminPage, async (req, res) => {
</tbody> </tbody>
</table> </table>
</section> </section>
<section class="card">
<h2>Aktive Uploads</h2>
${allUploads.length ? `
<table>
<thead>
<tr>
<th>Nutzer</th>
<th>Datei</th>
<th>Größe</th>
<th>Läuft ab</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
${adminUploadsRows}
</tbody>
</table>
` : '<div class="muted">Keine aktiven Uploads.</div>'}
</section>
`; `;
res.send(renderPage('Adminübersicht', body)); res.send(renderPage('Adminübersicht', body));
}); });
app.post(`${basePath}/admin/files/:id/delete`, requireAdminPage, async (req, res) => {
const uploadEntry = await get('SELECT id, stored_path FROM uploads WHERE id = ?', [req.params.id]);
if (!uploadEntry) {
res.status(404).send(renderPage('Nicht gefunden', '<p class="card">Upload nicht gefunden.</p>'));
return;
}
try {
await fs.promises.unlink(uploadEntry.stored_path);
} catch (err) {
// Ignore missing files.
}
await run('DELETE FROM uploads WHERE id = ?', [uploadEntry.id]);
await logEvent('delete', 'admin', { id: uploadEntry.id });
res.redirect(baseUrl('/admin/dashboard'));
});
app.post(`${basePath}/admin/files/:id/extend`, requireAdminPage, async (req, res) => {
const uploadEntry = await get('SELECT id, expires_at FROM uploads WHERE id = ?', [req.params.id]);
if (!uploadEntry) {
res.status(404).send(renderPage('Nicht gefunden', '<p class="card">Upload nicht gefunden.</p>'));
return;
}
const override = parseFloat(req.body.extendHours || '');
const extensionSeconds = Number.isFinite(override) && override > 0
? Math.round(override * 3600)
: uploadTtlSeconds;
const base = Math.max(uploadEntry.expires_at, Date.now());
const nextExpiry = base + extensionSeconds * 1000;
await run('UPDATE uploads SET expires_at = ? WHERE id = ?', [nextExpiry, uploadEntry.id]);
await logEvent('extend', 'admin', { id: uploadEntry.id, expires_at: nextExpiry });
res.redirect(baseUrl('/admin/dashboard'));
});
app.get(`${basePath}/dashboard`, requireAuthPage, async (req, res) => { app.get(`${basePath}/dashboard`, requireAuthPage, async (req, res) => {
const uploads = await all( const uploads = await all(
'SELECT id, original_name, stored_name, size_bytes, uploaded_at, expires_at FROM uploads WHERE owner = ? ORDER BY uploaded_at DESC', 'SELECT id, original_name, stored_name, size_bytes, uploaded_at, expires_at FROM uploads WHERE owner = ? ORDER BY uploaded_at DESC',