VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
155 lines
4.7 KiB
PHP
155 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Mail;
|
|
|
|
use App\Models\EmailDomain;
|
|
use App\Models\EmailServer;
|
|
use App\Models\Mailbox;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class EmailServerPoolService
|
|
{
|
|
public function poolRoot(EmailServer $server): EmailServer
|
|
{
|
|
return $server->poolRoot();
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, EmailServer>
|
|
*/
|
|
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,
|
|
]);
|
|
}
|
|
}
|