Files
deskhub/DeskHub-Webseite/staff/mail_queue.php
T
2026-10-28 07:54:42 +00:00

102 lines
7.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
$admin = require_admin();
$pdo = get_db();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verify_csrf();
$action = (string) ($_POST['action'] ?? '');
$id = (int) ($_POST['id'] ?? 0);
try {
if ($action === 'save') {
set_setting('mail_queue_enabled', !empty($_POST['mail_queue_enabled']) ? '1' : '0');
audit_log($admin['id'], 'Mail-Warteschlange ' . (!empty($_POST['mail_queue_enabled']) ? 'eingeschaltet' : 'ausgeschaltet'));
flash('success', 'Gespeichert.');
} elseif ($action === 'retry_now') {
// Eine Mail (id) oder alle wartenden (id = 0) jetzt versuchen
$now = db_now();
if ($id > 0) $pdo->prepare("UPDATE mail_queue SET status = 'pending', next_attempt_at = ? WHERE id = ? AND status IN ('pending','failed')")->execute([$now, $id]);
else $pdo->prepare("UPDATE mail_queue SET next_attempt_at = ? WHERE status = 'pending'")->execute([$now]);
$r = mail_queue_run($pdo, $id > 0 ? 1 : 50);
flash($r['sent'] > 0 || $r['failed'] === 0 ? 'success' : 'error', $r['sent'] . ' Mail(s) gesendet' . ($r['failed'] > 0 ? ', ' . $r['failed'] . ' weiterhin fehlgeschlagen (Fehler steht in der Liste).' : '.'));
} elseif ($action === 'requeue_failed') {
$pdo->prepare("UPDATE mail_queue SET status = 'pending', attempts = 0, next_attempt_at = ? WHERE status = 'failed'")->execute([db_now()]);
flash('success', 'Aufgegebene Mails werden erneut versucht.');
} elseif ($action === 'delete' && $id > 0) {
$pdo->prepare('DELETE FROM mail_queue WHERE id = ?')->execute([$id]);
audit_log($admin['id'], "Mail #{$id} aus der Warteschlange gelöscht");
flash('success', 'Mail gelöscht.');
} elseif ($action === 'purge') {
$n = $pdo->exec("DELETE FROM mail_queue WHERE status IN ('sent','failed')");
audit_log($admin['id'], 'Erledigte und aufgegebene Mails aus der Warteschlange gelöscht');
flash('success', (int) $n . ' Eintrag/Einträge gelöscht.');
}
} catch (Throwable $e) {
flash('error', 'Aktion nicht möglich: ' . $e->getMessage());
}
redirect('/staff/mail_queue.php');
}
$counts = mail_queue_counts($pdo);
$rows = [];
try {
$rows = $pdo->query("SELECT id, to_email, subject, status, attempts, next_attempt_at, last_error, created_at, sent_at FROM mail_queue ORDER BY (CASE status WHEN 'pending' THEN 0 WHEN 'failed' THEN 1 ELSE 2 END), id DESC LIMIT 200")->fetchAll();
} catch (Throwable $e) {
$rows = [];
}
$statusLabels = ['pending' => ['Wartet auf nächsten Versuch', 'warning'], 'failed' => ['Aufgegeben', 'danger'], 'sent' => ['Nachträglich gesendet', 'success']];
$pageTitle = 'Mail-Warteschlange';
$activePage = 'mail_queue';
require __DIR__ . '/_header.php';
?>
<h1>Mail-Warteschlange</h1>
<p class="muted" style="max-width:860px">Scheitert der Mailversand (zum Beispiel weil der SMTP-Server nicht erreichbar ist), merkt sich DeskHub die Mail und versucht es alle paar Minuten erneut: nach 5, 10, 20, 40 Minuten und dann in größeren Abständen, höchstens 10-mal und höchstens 48 Stunden lang. Danach gilt sie als aufgegeben und bleibt hier zum manuellen Nachsenden. Test-Mails kommen nicht in die Warteschlange.</p>
<div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px">
<div class="card" style="margin:0;min-width:160px"><div class="muted" style="font-size:.8rem">Wartend</div><div style="font-size:1.6rem;font-weight:800"><?= (int) $counts['pending'] ?></div></div>
<div class="card" style="margin:0;min-width:160px"><div class="muted" style="font-size:.8rem">Aufgegeben</div><div style="font-size:1.6rem;font-weight:800"><?= (int) $counts['failed'] ?></div></div>
<div class="card" style="margin:0;min-width:160px"><div class="muted" style="font-size:.8rem">Nachträglich gesendet</div><div style="font-size:1.6rem;font-weight:800"><?= (int) $counts['sent'] ?></div></div>
</div>
<form method="post" class="card" style="max-width:860px;margin-bottom:16px"><?= csrf_field() ?><input type="hidden" name="action" value="save">
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:400"><input type="checkbox" name="mail_queue_enabled" value="1" style="width:auto;margin-top:3px" <?= mail_queue_enabled() ? 'checked' : '' ?>>
<span><strong>Fehlgeschlagene Mails später erneut senden</strong> (empfohlen). Ausgeschaltet gehen Mails bei einer Störung verloren.</span></label>
<div class="btn-row"><button class="btn" type="submit">Speichern</button></div>
</form>
<div class="card">
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<h3 style="margin:0;flex:1">Mails in der Warteschlange</h3>
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="retry_now"><input type="hidden" name="id" value="0"><button class="btn secondary" type="submit" <?= $counts['pending'] ? '' : 'disabled' ?>>Alle wartenden jetzt senden</button></form>
<form method="post"><?= csrf_field() ?><input type="hidden" name="action" value="requeue_failed"><button class="btn secondary" type="submit" <?= $counts['failed'] ? '' : 'disabled' ?>>Aufgegebene erneut versuchen</button></form>
<form method="post" data-confirm="Erledigte und aufgegebene Einträge löschen?" onsubmit="return confirm(this.dataset.confirm)"><?= csrf_field() ?><input type="hidden" name="action" value="purge"><button class="btn secondary" type="submit" <?= ($counts['sent'] + $counts['failed']) ? '' : 'disabled' ?>>Erledigte und aufgegebene löschen</button></form>
</div>
<?php if (!$rows): ?>
<p class="muted">Die Warteschlange ist leer. Alle Mails sind rausgegangen.</p>
<?php else: ?>
<div style="overflow-x:auto"><table style="width:100%">
<thead><tr><th>Status</th><th>Empfänger</th><th>Betreff</th><th>Versuche</th><th>Nächster Versuch</th><th>Letzter Fehler</th><th></th></tr></thead>
<tbody>
<?php foreach ($rows as $r): [$label, $cls] = $statusLabels[$r['status']] ?? [$r['status'], 'muted']; ?>
<tr>
<td><span class="badge <?= e($cls) ?>"><?= e($label) ?></span><div class="muted" style="font-size:.72rem">seit <?= e(format_dt($r['created_at'])) ?></div></td>
<td><?= e($r['to_email']) ?></td>
<td><?= e($r['subject']) ?></td>
<td><?= (int) $r['attempts'] ?></td>
<td><?= $r['status'] === 'pending' ? e(format_dt($r['next_attempt_at'])) : ($r['status'] === 'sent' ? e(format_dt((string) $r['sent_at'])) : '–') ?></td>
<td style="max-width:280px;font-size:.8rem"><?= e((string) $r['last_error']) ?></td>
<td style="white-space:nowrap">
<?php if ($r['status'] !== 'sent'): ?>
<form method="post" style="display:inline"><?= csrf_field() ?><input type="hidden" name="action" value="retry_now"><input type="hidden" name="id" value="<?= (int) $r['id'] ?>"><button class="btn secondary" style="padding:3px 10px;font-size:.8rem" type="submit">Jetzt senden</button></form>
<?php endif; ?>
<form method="post" style="display:inline" data-confirm="Diese Mail endgültig löschen? Sie wird dann nicht mehr gesendet." onsubmit="return confirm(this.dataset.confirm)"><?= csrf_field() ?><input type="hidden" name="action" value="delete"><input type="hidden" name="id" value="<?= (int) $r['id'] ?>"><button class="btn secondary" style="padding:3px 10px;font-size:.8rem" type="submit">Löschen</button></form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table></div>
<?php if (count($rows) >= 200): ?><p class="muted" style="font-size:.8rem">Es werden die ersten 200 Einträge angezeigt.</p><?php endif; ?>
<?php endif; ?>
</div>
<?php require __DIR__ . '/_footer.php'; ?>