poolRoot(); } /** * @return Collection */ public function poolMembers(EmailServer $server): Collection { $root = $this->poolRoot($server); return EmailServer::query() ->where(function ($query) use ($root) { $query->where('id', $root->id) ->orWhere('primary_email_server_id', $root->id); }) ->whereNotIn('status', [EmailServer::STATUS_DECOMMISSIONED]) ->orderBy('role') ->get(); } public function poolDiskGb(EmailServer $server): int { return (int) $this->poolMembers($server)->sum('disk_gb'); } public function poolUsedStorageMb(EmailServer $server): int { return (int) $this->poolMembers($server)->sum('used_storage_mb'); } public function poolAllocatedStorageMb(EmailServer $server): int { $memberIds = $this->poolMembers($server)->pluck('id'); $fromEmailDomains = EmailDomain::query() ->whereIn('email_server_id', $memberIds) ->withSum('mailboxes as quota_sum', 'quota_mb') ->get() ->sum(fn (EmailDomain $d) => (int) ($d->quota_sum ?? 0)); $fromWebsiteDomains = Mailbox::query() ->whereHas('domain', fn ($q) => $q->whereIn('email_server_id', $memberIds)) ->sum('quota_mb'); return (int) ($fromEmailDomains + $fromWebsiteDomains); } public function poolDomainCount(EmailServer $server): int { $memberIds = $this->poolMembers($server)->pluck('id'); $standalone = EmailDomain::query()->whereIn('email_server_id', $memberIds)->count(); $website = \App\Models\Domain::query()->whereIn('email_server_id', $memberIds)->count(); return $standalone + $website; } public function poolMailboxCount(EmailServer $server): int { $memberIds = $this->poolMembers($server)->pluck('id'); $standalone = Mailbox::query() ->whereHas('emailDomain', fn ($q) => $q->whereIn('email_server_id', $memberIds)) ->count(); $website = Mailbox::query() ->whereHas('domain', fn ($q) => $q->whereIn('email_server_id', $memberIds)) ->count(); return $standalone + $website; } /** * Pick the pool member with the most free physical disk for new mail storage. */ public function findStorageMember(EmailServer $poolRoot): ?EmailServer { $members = $this->poolMembers($poolRoot) ->filter(fn (EmailServer $m) => in_array($m->status, [EmailServer::STATUS_ACTIVE, EmailServer::STATUS_FULL], true)); if ($members->isEmpty()) { return null; } return $members ->sortByDesc(function (EmailServer $member) { $disk = (int) ($member->disk_gb ?? 0); $usedGb = $this->memberUsedStorageGb($member); return max($disk - $usedGb, 0); }) ->first(); } public function memberUsedStorageGb(EmailServer $member): float { return ((int) $member->used_storage_mb) / 1024; } public function poolPressurePercent(EmailServer $server): float { $poolDisk = $this->poolDiskGb($server); if ($poolDisk <= 0) { return 0; } $usedGb = $this->poolUsedStorageMb($server) / 1024; return round(($usedGb / $poolDisk) * 100, 2); } public function syncExtensionCredentials(EmailServer $primary): void { if (! $primary->isPrimary()) { return; } $primary->extensions()->update([ 'mail_host' => $primary->mail_host, 'db_host' => $primary->db_host, 'db_port' => $primary->db_port, 'db_database' => $primary->db_database, 'db_username' => $primary->db_username, 'db_password' => $primary->db_password, ]); } public function syncExtensionFromPrimary(EmailServer $extension, EmailServer $primary): void { $extension->update([ 'mail_host' => $primary->mail_host, 'role' => EmailServer::ROLE_EXTENSION, 'primary_email_server_id' => $primary->id, 'db_host' => $primary->db_host, 'db_port' => $primary->db_port, 'db_database' => $primary->db_database, 'db_username' => $primary->db_username, 'db_password' => $primary->db_password, ]); } }