parent
6fc74a0906
commit
0aea6fd157
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Models\MailUser;
|
use App\Models\MailUser;
|
||||||
|
|
@ -13,43 +12,43 @@ use RecursiveIteratorIterator;
|
||||||
class UpdateMailboxStats extends Command
|
class UpdateMailboxStats extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'mail:update-stats {--user=}';
|
protected $signature = 'mail:update-stats {--user=}';
|
||||||
protected $description = 'Aktualisiert Mailquota und Nachrichtenzahl für alle Mailboxen (oder einen Benutzer)';
|
protected $description = 'Aktualisiert Quota & Nachrichtenzahl (Settings/Redis; DB-frei)';
|
||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
Log::channel('mailstats')->info('=== mail:update-stats START ===', [
|
Log::info('=== mail:update-stats START ===', ['userOpt' => $this->option('user')]);
|
||||||
'userOpt' => $this->option('user')
|
|
||||||
]);
|
|
||||||
|
|
||||||
$q = MailUser::query()
|
$q = MailUser::query()
|
||||||
->where('is_active', true)
|
->with('domain:id,domain')
|
||||||
->whereNotNull('email')
|
->active();
|
||||||
->where('email', 'like', '%@%');
|
|
||||||
|
|
||||||
if ($email = trim((string)$this->option('user'))) {
|
if ($only = trim((string)$this->option('user'))) {
|
||||||
$q->where('email', $email);
|
$q->byEmail($only);
|
||||||
}
|
}
|
||||||
|
|
||||||
$users = $q->get();
|
$users = $q->get();
|
||||||
|
|
||||||
if ($users->isEmpty()) {
|
if ($users->isEmpty()) {
|
||||||
Log::channel('mailstats')->warning('Keine passenden Mailboxen gefunden.');
|
Log::warning('Keine passenden Mailboxen gefunden.');
|
||||||
$this->warn('Keine passenden Mailboxen gefunden.');
|
$this->warn('Keine passenden Mailboxen gefunden.');
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($users as $u) {
|
foreach ($users as $u) {
|
||||||
$email = trim((string)$u->email);
|
// 1) sichere E-Mail ermitteln: roher DB-Wert → Accessor → zusammengesetzt
|
||||||
|
$raw = (string)($u->getRawOriginal('email') ?? '');
|
||||||
|
$email = $raw !== '' ? $raw : ($u->email ?? $u->address ?? '');
|
||||||
|
|
||||||
if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) {
|
if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', (string)$email)) {
|
||||||
Log::channel('mailstats')->warning('Überspringe ungültige Adresse', ['email' => $email]);
|
Log::warning('Ungültige effektive Adresse – skip', [
|
||||||
|
'user_id' => $u->id, 'raw' => $raw, 'computed' => $email
|
||||||
|
]);
|
||||||
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 berechnen (Dateisystem)
|
// 2) Größe (Dateisystem)
|
||||||
$usedBytes = 0;
|
$usedBytes = 0;
|
||||||
if (is_dir($maildir)) {
|
if (is_dir($maildir)) {
|
||||||
$it = new RecursiveIteratorIterator(
|
$it = new RecursiveIteratorIterator(
|
||||||
|
|
@ -60,22 +59,23 @@ class UpdateMailboxStats extends Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Nachrichten zählen – bevorzugt via doveadm als vmail
|
// 3) Message-Count – prefer doveadm als vmail; Fallback: Files
|
||||||
$messageCount = $this->countViaDoveadm($email);
|
$messageCount = $this->countViaDoveadm($email);
|
||||||
if ($messageCount === null) {
|
if ($messageCount === null) {
|
||||||
$messageCount = $this->countViaFilesystem($maildir);
|
$messageCount = $this->countViaFilesystem($maildir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional: in Settings/Redis cachen (für UI)
|
// 4) In Settings (→ Redis + DB-Backup) persistieren
|
||||||
Setting::set("mailbox.{$email}", [
|
Setting::set("mailbox.{$email}", [
|
||||||
'used_bytes' => (int)$usedBytes,
|
'used_bytes' => (int)$usedBytes,
|
||||||
'message_count' => (int)$messageCount,
|
'message_count' => (int)$messageCount,
|
||||||
'updated_at' => now()->toDateTimeString(),
|
'updated_at' => now()->toDateTimeString(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$line = sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes / 1024 / 1024, $messageCount);
|
$this->line(sprintf("%-35s %7.1f MiB %5d msgs",
|
||||||
$this->line($line);
|
$email, $usedBytes / 1024 / 1024, $messageCount));
|
||||||
Log::channel('mailstats')->info('OK', [
|
|
||||||
|
Log::info('Mailbox-Stat OK', [
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'maildir' => $maildir,
|
'maildir' => $maildir,
|
||||||
'used_bytes' => $usedBytes,
|
'used_bytes' => $usedBytes,
|
||||||
|
|
@ -83,23 +83,20 @@ class UpdateMailboxStats extends Command
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::channel('mailstats')->info('=== mail:update-stats DONE ===');
|
Log::info('=== mail:update-stats DONE ===');
|
||||||
$this->info('Mailbox-Statistiken aktualisiert.');
|
$this->info('Mailbox-Statistiken aktualisiert.');
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function countViaDoveadm(string $email): ?int
|
protected function countViaDoveadm(string $email): ?int
|
||||||
{
|
{
|
||||||
$cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1";
|
$cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u "
|
||||||
|
. escapeshellarg($email) . " messages INBOX 2>&1";
|
||||||
$out = [];
|
$out = [];
|
||||||
$rc = 0;
|
$rc = 0;
|
||||||
exec($cmd, $out, $rc);
|
exec($cmd, $out, $rc);
|
||||||
|
|
||||||
Log::channel('mailstats')->debug('doveadm exec', ['cmd' => $cmd, 'rc' => $rc, 'out' => $out]);
|
|
||||||
|
|
||||||
if ($rc !== 0) return null;
|
if ($rc !== 0) return null;
|
||||||
|
|
||||||
// Erwartet: "mailbox\tmessages\nINBOX\t<number>"
|
|
||||||
foreach ($out as $line) {
|
foreach ($out as $line) {
|
||||||
if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) {
|
if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) {
|
||||||
return (int)$m[1];
|
return (int)$m[1];
|
||||||
|
|
@ -114,19 +111,265 @@ class UpdateMailboxStats extends Command
|
||||||
foreach (['cur', 'new'] as $sub) {
|
foreach (['cur', 'new'] as $sub) {
|
||||||
$dir = "{$maildir}/{$sub}";
|
$dir = "{$maildir}/{$sub}";
|
||||||
if (!is_dir($dir)) continue;
|
if (!is_dir($dir)) continue;
|
||||||
$dh = opendir($dir);
|
$h = opendir($dir);
|
||||||
if (!$dh) continue;
|
if (!$h) continue;
|
||||||
while (($fn = readdir($dh)) !== false) {
|
while (($fn = readdir($h)) !== false) {
|
||||||
if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue;
|
if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue;
|
||||||
$n++;
|
$n++;
|
||||||
}
|
}
|
||||||
closedir($dh);
|
closedir($h);
|
||||||
}
|
}
|
||||||
Log::channel('mailstats')->debug('fs count', ['maildir' => $maildir, 'count' => $n]);
|
|
||||||
return $n;
|
return $n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//namespace App\Console\Commands;
|
||||||
|
//
|
||||||
|
//use Illuminate\Console\Command;
|
||||||
|
//use Illuminate\Support\Facades\Log;
|
||||||
|
//use App\Models\MailUser;
|
||||||
|
//use App\Models\Setting;
|
||||||
|
//use RecursiveDirectoryIterator;
|
||||||
|
//use RecursiveIteratorIterator;
|
||||||
|
|
||||||
|
|
||||||
|
//class UpdateMailboxStats extends Command
|
||||||
|
//{
|
||||||
|
// protected $signature = 'mail:update-stats {--user=}';
|
||||||
|
// protected $description = 'Aktualisiert Mailquota und Nachrichtenzahl';
|
||||||
|
//
|
||||||
|
// public function handle(): int
|
||||||
|
// {
|
||||||
|
// Log::info('=== mail:update-stats START ===', ['userOpt' => $this->option('user')]);
|
||||||
|
//
|
||||||
|
// $q = MailUser::query()
|
||||||
|
// ->with('domain:id,domain') // <— Domain fürs Fallback laden
|
||||||
|
// ->where('is_active', true);
|
||||||
|
//
|
||||||
|
// if ($only = trim((string)$this->option('user'))) {
|
||||||
|
// $q->where('email', $only);
|
||||||
|
// } else {
|
||||||
|
// $q->whereNotNull('email')->where('email', 'like', '%@%');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $users = $q->get();
|
||||||
|
// if ($users->isEmpty()) {
|
||||||
|
// Log::warning('Keine passenden Mailboxen gefunden.');
|
||||||
|
// $this->warn('Keine passenden Mailboxen gefunden.');
|
||||||
|
// return self::SUCCESS;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// foreach ($users as $u) {
|
||||||
|
// // 1) rohen DB-Wert nehmen (ohne Accessor/Casts)
|
||||||
|
// $rawEmail = (string) $u->getRawOriginal('email');
|
||||||
|
//
|
||||||
|
// // 2) Fallback aus localpart + domain, falls roher Wert leer
|
||||||
|
// $fallback = ($u->localpart ?? '') . '@' . ($u->domain->domain ?? '');
|
||||||
|
// $email = trim($rawEmail !== '' ? $rawEmail : $fallback);
|
||||||
|
//
|
||||||
|
// if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) {
|
||||||
|
// Log::warning('Ungültige effektive Adresse – skip', [
|
||||||
|
// 'raw' => $rawEmail,
|
||||||
|
// 'fallback' => $fallback,
|
||||||
|
// 'got' => $email,
|
||||||
|
// 'user_id' => $u->id,
|
||||||
|
// ]);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [$local, $domain] = explode('@', $email, 2);
|
||||||
|
// $maildir = "/var/mail/vhosts/{$domain}/{$local}";
|
||||||
|
//
|
||||||
|
// // Größe (Dateisystem)
|
||||||
|
// $usedBytes = 0;
|
||||||
|
// if (is_dir($maildir)) {
|
||||||
|
// $it = new RecursiveIteratorIterator(
|
||||||
|
// new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS)
|
||||||
|
// );
|
||||||
|
// foreach ($it as $f) {
|
||||||
|
// if ($f->isFile()) $usedBytes += $f->getSize();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Zähler via doveadm (als vmail), Fallback: Files zählen
|
||||||
|
// $messageCount = $this->countViaDoveadm($email);
|
||||||
|
// if ($messageCount === null) {
|
||||||
|
// $messageCount = $this->countViaFilesystem($maildir);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Optional zusätzlich cachen (UI liest das bereits)
|
||||||
|
// Setting::set("mailbox.{$email}", [
|
||||||
|
// 'used_bytes' => (int)$usedBytes,
|
||||||
|
// 'message_count' => (int)$messageCount,
|
||||||
|
// 'updated_at' => now()->toDateTimeString(),
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// $this->line(sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes/1024/1024, $messageCount));
|
||||||
|
// Log::info('Mailbox-Stat OK', compact('email','maildir','usedBytes','messageCount'));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Log::info('=== mail:update-stats DONE ===');
|
||||||
|
// $this->info('Mailbox-Statistiken aktualisiert.');
|
||||||
|
// return self::SUCCESS;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected function countViaDoveadm(string $email): ?int
|
||||||
|
// {
|
||||||
|
// $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1";
|
||||||
|
// $out = []; $rc = 0;
|
||||||
|
// exec($cmd, $out, $rc);
|
||||||
|
// if ($rc !== 0) return null;
|
||||||
|
// foreach ($out as $line) {
|
||||||
|
// if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) return (int)$m[1];
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected function countViaFilesystem(string $maildir): int
|
||||||
|
// {
|
||||||
|
// $n = 0;
|
||||||
|
// foreach (['cur','new'] as $sub) {
|
||||||
|
// $dir = "{$maildir}/{$sub}";
|
||||||
|
// if (!is_dir($dir)) continue;
|
||||||
|
// $h = opendir($dir);
|
||||||
|
// if (!$h) continue;
|
||||||
|
// while (($fn = readdir($h)) !== false) {
|
||||||
|
// if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue;
|
||||||
|
// $n++;
|
||||||
|
// }
|
||||||
|
// closedir($h);
|
||||||
|
// }
|
||||||
|
// return $n;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//namespace App\Console\Commands;
|
||||||
|
//
|
||||||
|
//use App\Models\MailUser;
|
||||||
|
//use App\Models\Setting;
|
||||||
|
//use Illuminate\Console\Command;
|
||||||
|
//use Illuminate\Support\Facades\Log;
|
||||||
|
//use RecursiveDirectoryIterator;
|
||||||
|
//use RecursiveIteratorIterator;
|
||||||
|
//
|
||||||
|
//class UpdateMailboxStats extends Command
|
||||||
|
//{
|
||||||
|
// protected $signature = 'mail:update-stats {--user=}';
|
||||||
|
// protected $description = 'Aktualisiert Mailquota und Nachrichtenzahl für alle Mailboxen (oder einen Benutzer)';
|
||||||
|
//
|
||||||
|
// public function handle(): int
|
||||||
|
// {
|
||||||
|
// Log::channel('mailstats')->info('=== mail:update-stats START ===', [
|
||||||
|
// 'userOpt' => $this->option('user')
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// $q = MailUser::query()
|
||||||
|
// ->where('is_active', true)
|
||||||
|
// ->whereNotNull('email')
|
||||||
|
// ->where('email', 'like', '%@%');
|
||||||
|
//
|
||||||
|
// if ($email = trim((string)$this->option('user'))) {
|
||||||
|
// $q->where('email', $email);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $users = $q->get();
|
||||||
|
//
|
||||||
|
// if ($users->isEmpty()) {
|
||||||
|
// Log::channel('mailstats')->warning('Keine passenden Mailboxen gefunden.');
|
||||||
|
// $this->warn('Keine passenden Mailboxen gefunden.');
|
||||||
|
// return self::SUCCESS;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// foreach ($users as $u) {
|
||||||
|
// $email = trim((string)$u->email);
|
||||||
|
//
|
||||||
|
// if (!preg_match('/^[^@\s]+@[^@\s]+\.[^@\s]+$/', $email)) {
|
||||||
|
// Log::channel('mailstats')->warning('Überspringe ungültige Adresse', ['email' => $email]);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [$local, $domain] = explode('@', $email, 2);
|
||||||
|
// $maildir = "/var/mail/vhosts/{$domain}/{$local}";
|
||||||
|
//
|
||||||
|
// // 1) Größe berechnen (Dateisystem)
|
||||||
|
// $usedBytes = 0;
|
||||||
|
// if (is_dir($maildir)) {
|
||||||
|
// $it = new RecursiveIteratorIterator(
|
||||||
|
// new RecursiveDirectoryIterator($maildir, \FilesystemIterator::SKIP_DOTS)
|
||||||
|
// );
|
||||||
|
// foreach ($it as $f) {
|
||||||
|
// if ($f->isFile()) $usedBytes += $f->getSize();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 2) Nachrichten zählen – bevorzugt via doveadm als vmail
|
||||||
|
// $messageCount = $this->countViaDoveadm($email);
|
||||||
|
// if ($messageCount === null) {
|
||||||
|
// $messageCount = $this->countViaFilesystem($maildir);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // optional: in Settings/Redis cachen (für UI)
|
||||||
|
// Setting::set("mailbox.{$email}", [
|
||||||
|
// 'used_bytes' => (int)$usedBytes,
|
||||||
|
// 'message_count' => (int)$messageCount,
|
||||||
|
// 'updated_at' => now()->toDateTimeString(),
|
||||||
|
// ]);
|
||||||
|
//
|
||||||
|
// $line = sprintf("%-35s %7.1f MiB %5d msgs", $email, $usedBytes / 1024 / 1024, $messageCount);
|
||||||
|
// $this->line($line);
|
||||||
|
// Log::channel('mailstats')->info('OK', [
|
||||||
|
// 'email' => $email,
|
||||||
|
// 'maildir' => $maildir,
|
||||||
|
// 'used_bytes' => $usedBytes,
|
||||||
|
// 'message_count' => $messageCount,
|
||||||
|
// ]);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Log::channel('mailstats')->info('=== mail:update-stats DONE ===');
|
||||||
|
// $this->info('Mailbox-Statistiken aktualisiert.');
|
||||||
|
// return self::SUCCESS;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected function countViaDoveadm(string $email): ?int
|
||||||
|
// {
|
||||||
|
// $cmd = "sudo -n -u vmail /usr/bin/doveadm -f tab mailbox status -u " . escapeshellarg($email) . " messages INBOX 2>&1";
|
||||||
|
// $out = [];
|
||||||
|
// $rc = 0;
|
||||||
|
// exec($cmd, $out, $rc);
|
||||||
|
//
|
||||||
|
// Log::channel('mailstats')->debug('doveadm exec', ['cmd' => $cmd, 'rc' => $rc, 'out' => $out]);
|
||||||
|
//
|
||||||
|
// if ($rc !== 0) return null;
|
||||||
|
//
|
||||||
|
// // Erwartet: "mailbox\tmessages\nINBOX\t<number>"
|
||||||
|
// foreach ($out as $line) {
|
||||||
|
// if (preg_match('/^\s*INBOX\s+(\d+)\s*$/i', trim($line), $m)) {
|
||||||
|
// return (int)$m[1];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// protected function countViaFilesystem(string $maildir): int
|
||||||
|
// {
|
||||||
|
// $n = 0;
|
||||||
|
// foreach (['cur', 'new'] as $sub) {
|
||||||
|
// $dir = "{$maildir}/{$sub}";
|
||||||
|
// if (!is_dir($dir)) continue;
|
||||||
|
// $dh = opendir($dir);
|
||||||
|
// if (!$dh) continue;
|
||||||
|
// while (($fn = readdir($dh)) !== false) {
|
||||||
|
// if ($fn === '.' || $fn === '..' || $fn[0] === '.') continue;
|
||||||
|
// $n++;
|
||||||
|
// }
|
||||||
|
// closedir($dh);
|
||||||
|
// }
|
||||||
|
// Log::channel('mailstats')->debug('fs count', ['maildir' => $maildir, 'count' => $n]);
|
||||||
|
// return $n;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
//namespace App\Console\Commands;
|
//namespace App\Console\Commands;
|
||||||
//
|
//
|
||||||
//use App\Models\MailUser;
|
//use App\Models\MailUser;
|
||||||
|
|
|
||||||
|
|
@ -7,55 +7,136 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class MailUser extends Model
|
class MailUser extends Model
|
||||||
{
|
{
|
||||||
// protected $table = 'mail_users';
|
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'domain_id', 'localpart', 'email', 'display_name', 'password_hash',
|
'domain_id', 'localpart', 'email', 'display_name', 'password_hash',
|
||||||
'is_active', 'quota_mb', 'is_system'
|
'is_active', 'quota_mb', 'is_system'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = ['password_hash'];
|
protected $hidden = ['password_hash'];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'is_active' => 'bool',
|
'is_active' => 'bool',
|
||||||
'is_system' => 'boolean',
|
'is_system' => 'bool',
|
||||||
'quota_mb' => 'int',
|
'quota_mb' => 'int',
|
||||||
'last_login_at' => 'datetime',
|
'last_login_at' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function domain(): BelongsTo {
|
public function domain(): BelongsTo
|
||||||
|
{
|
||||||
return $this->belongsTo(Domain::class);
|
return $this->belongsTo(Domain::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPasswordAttribute(string $plain): void {
|
// optional: virtueller Setter
|
||||||
// optional: allow 'password' virtual attribute
|
public function setPasswordAttribute(string $plain): void
|
||||||
|
{
|
||||||
$this->attributes['password_hash'] = password_hash($plain, PASSWORD_BCRYPT);
|
$this->attributes['password_hash'] = password_hash($plain, PASSWORD_BCRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ausgabe: vollständige Adresse
|
/**
|
||||||
public function getEmailAttribute(): string {
|
* KORREKT: DB-Wert hat Vorrang; falls leer → Fallback localpart@domain.domain
|
||||||
return "{$this->local_part}@{$this->domain->name}";
|
*/
|
||||||
|
public function getEmailAttribute($value): ?string
|
||||||
|
{
|
||||||
|
if (!empty($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
$local = (string)($this->attributes['localpart'] ?? $this->localpart ?? '');
|
||||||
|
$dom = $this->relationLoaded('domain')
|
||||||
|
? (string)($this->domain->domain ?? '')
|
||||||
|
: (string)($this->attributes['domain'] ?? '');
|
||||||
|
|
||||||
|
if ($local !== '' && $dom !== '') {
|
||||||
|
return "{$local}@{$dom}";
|
||||||
|
}
|
||||||
|
// nichts zusammenfummeln → nicht "@"
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adresse ohne Accessor/DB-Fallback erzwingen (z.B. in Queries vor-joined)
|
||||||
|
*/
|
||||||
public function getAddressAttribute(): string
|
public function getAddressAttribute(): string
|
||||||
{
|
{
|
||||||
$local = (string)($this->attributes['localpart'] ?? $this->localpart ?? '');
|
$raw = (string)($this->attributes['email'] ?? '');
|
||||||
// bevorzugt den in Queries selektierten Alias `dom`, sonst Relation, sonst Roh-Attribut
|
if ($raw !== '') return $raw;
|
||||||
$dom = (string)(
|
|
||||||
$this->attributes['dom']
|
|
||||||
?? ($this->relationLoaded('domain') ? ($this->domain->domain ?? '') : '')
|
|
||||||
?? ($this->attributes['domain'] ?? '')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($local !== '' && $dom !== '') return "{$local}@{$dom}";
|
$local = (string)($this->attributes['localpart'] ?? $this->localpart ?? '');
|
||||||
if (($this->attributes['email'] ?? null) !== null) return (string)$this->attributes['email'];
|
$dom = $this->relationLoaded('domain')
|
||||||
return $dom !== '' ? "@{$dom}" : '';
|
? (string)($this->domain->domain ?? '')
|
||||||
|
: (string)($this->attributes['domain'] ?? '');
|
||||||
|
|
||||||
|
return ($local !== '' && $dom !== '') ? "{$local}@{$dom}" : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scopes
|
// Scopes
|
||||||
public function scopeActive($q) { return $q->where('is_active', true); }
|
public function scopeActive($q)
|
||||||
|
{
|
||||||
public function scopeSystem($q) { return $q->where('is_system', true); }
|
return $q->where('is_active', true);
|
||||||
|
|
||||||
public function scopeByEmail($q, string $email) { return $q->where('email', $email); }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeSystem($q)
|
||||||
|
{
|
||||||
|
return $q->where('is_system', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeByEmail($q, string $email)
|
||||||
|
{
|
||||||
|
return $q->where('email', $email);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//class MailUser extends Model
|
||||||
|
//{
|
||||||
|
//// protected $table = 'mail_users';
|
||||||
|
//
|
||||||
|
// protected $fillable = [
|
||||||
|
// 'domain_id','localpart','email','display_name','password_hash',
|
||||||
|
// 'is_active','quota_mb','is_system'
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// protected $hidden = ['password_hash'];
|
||||||
|
// protected $casts = [
|
||||||
|
// 'is_active'=>'bool',
|
||||||
|
// 'is_system' => 'boolean',
|
||||||
|
// 'quota_mb'=>'int',
|
||||||
|
// 'last_login_at'=>'datetime',
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// public function domain(): BelongsTo {
|
||||||
|
// return $this->belongsTo(Domain::class);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public function setPasswordAttribute(string $plain): void {
|
||||||
|
// // optional: allow 'password' virtual attribute
|
||||||
|
// $this->attributes['password_hash'] = password_hash($plain, PASSWORD_BCRYPT);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Ausgabe: vollständige Adresse
|
||||||
|
// public function getEmailAttribute(): string {
|
||||||
|
// return "{$this->local_part}@{$this->domain->name}";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public function getAddressAttribute(): string
|
||||||
|
// {
|
||||||
|
// $local = (string)($this->attributes['localpart'] ?? $this->localpart ?? '');
|
||||||
|
// // bevorzugt den in Queries selektierten Alias `dom`, sonst Relation, sonst Roh-Attribut
|
||||||
|
// $dom = (string)(
|
||||||
|
// $this->attributes['dom']
|
||||||
|
// ?? ($this->relationLoaded('domain') ? ($this->domain->domain ?? '') : '')
|
||||||
|
// ?? ($this->attributes['domain'] ?? '')
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// if ($local !== '' && $dom !== '') return "{$local}@{$dom}";
|
||||||
|
// if (($this->attributes['email'] ?? null) !== null) return (string)$this->attributes['email'];
|
||||||
|
// return $dom !== '' ? "@{$dom}" : '';
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Scopes
|
||||||
|
// public function scopeActive($q) { return $q->where('is_active', true); }
|
||||||
|
//
|
||||||
|
// public function scopeSystem($q) { return $q->where('is_system', true); }
|
||||||
|
//
|
||||||
|
// public function scopeByEmail($q, string $email) { return $q->where('email', $email); }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue