with('mailboxes')->find($this->domainId); if (! $domain) { return; } if (! $domain->isActiveForMail()) { return; } if (! $provisioner->isConfigured()) { Log::info('MailProvisioningJob: Skipped — mail server DB not configured', ['domain_id' => $domain->id]); return; } $key = $dkim->ensureActive($domain); $beforeStatus = (string) $domain->status; $beforeState = (string) ($domain->onboarding_state ?? ''); try { // 1. Provision domain in Postfix/Dovecot DB $provisioner->provisionDomain($domain); // 2. Provision each mailbox in Postfix/Dovecot DB foreach ($domain->mailboxes as $mailbox) { try { $provisioner->provisionMailbox($mailbox); } catch (\Throwable $e) { Log::warning('MailProvisioningJob: Mailbox provision skipped (may need password)', [ 'email' => $mailbox->address, 'error' => $e->getMessage(), ]); } } // 3. Deploy DKIM key to mail server $provisioner->deployDkim($domain, $key); $this->markReady($domain); $afterStatus = (string) $domain->status; $afterState = (string) ($domain->onboarding_state ?? ''); $this->logCheck($domain, 'success', 'Mail stack provisioned via direct DB', [], $beforeStatus, $beforeState, $afterStatus, $afterState); } catch (\Throwable $exception) { $this->markFailed($domain); $afterStatus = (string) $domain->status; $afterState = (string) ($domain->onboarding_state ?? ''); $this->logCheck($domain, 'failed', $exception->getMessage(), [], $beforeStatus, $beforeState, $afterStatus, $afterState); Log::error('Mail provisioning failed', ['domain_id' => $domain->id, 'error' => $exception->getMessage()]); throw $exception; } } private function markReady(Domain $domain): void { $domain->update([ 'mail_ready_at' => $domain->mail_ready_at ?? now(), 'active_at' => $domain->active_at ?? now(), 'status' => 'verified', 'onboarding_state' => Domain::STATE_ACTIVE, ]); } private function markFailed(Domain $domain): void { $domain->update([ 'status' => 'verifying', 'onboarding_state' => Domain::STATE_MAIL_READY, ]); } private function logCheck(Domain $domain, string $result, string $notes, array $payload = []): void { $stateBefore = $stateAfter = (string) ($domain->onboarding_state ?? ''); $statusBefore = $statusAfter = (string) $domain->status; if (! empty($payload)) { $notes .= ' '.json_encode($payload); } DomainNsCheck::create([ 'domain_id' => $domain->id, 'check_type' => 'mail_provision', 'result' => $result, 'notes' => $notes.(! empty($payload) ? ' '.json_encode($payload) : ''), 'status_before' => $statusBefore, 'status_after' => $statusAfter, 'state_before' => $stateBefore, 'state_after' => $stateAfter, 'checked_at' => now(), ]); } }