Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main
boban 2025-10-21 02:32:37 +02:00
parent 878dae8876
commit a25de8c7b7
1 changed files with 66 additions and 22 deletions

View File

@ -16,7 +16,6 @@ class UpdateMailboxStats extends Command
{ {
$q = MailUser::query() $q = MailUser::query()
->where('is_active', true) ->where('is_active', true)
// sichere Filter: E-Mail vorhanden und enthält genau ein "@"
->whereNotNull('email') ->whereNotNull('email')
->where('email', 'like', '%@%'); ->where('email', 'like', '%@%');
@ -33,14 +32,14 @@ class UpdateMailboxStats extends Command
foreach ($users as $u) { foreach ($users as $u) {
$email = trim($u->email); $email = trim($u->email);
if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) { if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) {
// still und leise überspringen kein „Überspringe @“ mehr // ungültig → überspringen
continue; continue;
} }
[$local, $domain] = explode('@', $email, 2); [$local, $domain] = explode('@', $email, 2);
$maildir = "/var/mail/vhosts/{$domain}/{$local}"; $maildir = "/var/mail/vhosts/{$domain}/{$local}";
// 1) Größe in Bytes (rekursiv; ohne "du") // 1) Größe (rekursiv, ohne `du`)
$usedBytes = 0; $usedBytes = 0;
if (is_dir($maildir)) { if (is_dir($maildir)) {
$it = new RecursiveIteratorIterator( $it = new RecursiveIteratorIterator(
@ -53,28 +52,16 @@ class UpdateMailboxStats extends Command
} }
} }
// 2) Message-Count = Dateien in cur/ + new/ // 2) Nachrichtenzahl bevorzugt via doveadm (sudo -u vmail), Fallback: Dateien zählen
$messageCount = 0; $messageCount = $this->messageCountViaDoveadm($email);
foreach (['cur', 'new'] as $sub) { if ($messageCount === null) {
$dir = "{$maildir}/{$sub}"; $messageCount = $this->messageCountViaFilesystem($maildir);
if (is_dir($dir)) {
$dh = opendir($dir);
if ($dh) {
while (($fn = readdir($dh)) !== false) {
// echte Maildir-Dateien haben keinen führenden Punkt
if ($fn !== '.' && $fn !== '..' && $fn[0] !== '.') {
$messageCount++;
}
}
closedir($dh);
}
}
} }
// Update DB // Update DB
$u->forceFill([ $u->forceFill([
'used_bytes' => $usedBytes, 'used_bytes' => $usedBytes,
'message_count' => $messageCount, 'message_count' => (int) $messageCount,
'stats_refreshed_at' => now(), 'stats_refreshed_at' => now(),
])->save(); ])->save();
@ -89,4 +76,61 @@ class UpdateMailboxStats extends Command
$this->info('Mailbox-Statistiken aktualisiert.'); $this->info('Mailbox-Statistiken aktualisiert.');
return self::SUCCESS; return self::SUCCESS;
} }
/**
* Holt die Anzahl der Nachrichten in INBOX über doveadm als vmail (sudo).
* Gibt int bei Erfolg, oder null bei Fehlern zurück.
*/
private function messageCountViaDoveadm(string $email): ?int
{
// bevorzugtes Format: tabellarisch → "INBOX\t123"
$cmd = sprintf(
"sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u %s messages INBOX 2>/dev/null",
escapeshellarg($email)
);
$out = trim((string) shell_exec($cmd));
if ($out === '') {
// Fallback auf normales Format: "messages=123"
$cmd2 = sprintf(
"sudo -n -u vmail /usr/bin/doveadm mailbox status -u %s messages INBOX 2>/dev/null",
escapeshellarg($email)
);
$out = trim((string) shell_exec($cmd2));
}
if ($out === '') {
return null;
}
// Match "INBOX<TAB>123" oder "messages=123"
if (preg_match('/\t(\d+)\s*$/', $out, $m) || preg_match('/messages=(\d+)/', $out, $m)) {
return (int) $m[1];
}
return null;
}
/**
* Fallback: Zählt Dateien in cur/ + new/ (Maildir).
*/
private function messageCountViaFilesystem(string $maildir): int
{
$count = 0;
foreach (['cur', 'new'] as $sub) {
$dir = "{$maildir}/{$sub}";
if (is_dir($dir) && is_readable($dir)) {
$dh = opendir($dir);
if ($dh) {
while (($fn = readdir($dh)) !== false) {
if ($fn !== '.' && $fn !== '..' && $fn[0] !== '.') {
$count++;
}
}
closedir($dh);
}
}
}
return $count;
}
} }