Files
shopit-back/app/Domains/Integration/Services/MailService.php

215 lines
7.1 KiB
PHP

<?php
namespace App\Domains\Integration\Services;
use App\Domains\Client\Models\Client;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Tenant\Models\WebsiteType;
use Exception;
use Illuminate\Contracts\Mail\Factory as MailFactory;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\MailManager;
use Illuminate\Support\Facades\Blade;
use InvalidArgumentException;
class MailService extends BaseIntegrationService
{
private const REQUIRED_SMTP_FIELDS = [
'MAIL_HOST',
'MAIL_PORT',
'MAIL_USERNAME',
'MAIL_PASSWORD',
'MAIL_FROM_ADDRESS',
];
protected string $integrationCode = 'email';
private readonly MailFactory $mailFactory;
private ?Mailer $mailer = null;
private bool $usesClientMailer = false;
public function __construct(?MailFactory $mailFactory = null)
{
$this->mailFactory = $mailFactory ?? app(MailFactory::class);
}
public function forTenant(string $tenantCode): self
{
parent::forTenant($tenantCode);
if ($this->clientIntegration) {
$this->mailer = $this->resolveMailer();
$this->usesClientMailer = true;
} else {
$this->mailer = $this->mailFactory->mailer();
$this->usesClientMailer = false;
}
return $this;
}
public function forClient(Client|string $client): self
{
parent::forClient($client);
$this->tenant = $this->clientContext?->tenants()->first();
if ($this->clientIntegration) {
$this->mailer = $this->resolveMailer();
$this->usesClientMailer = true;
} else {
$this->mailer = $this->mailFactory->mailer();
$this->usesClientMailer = false;
}
return $this;
}
public function getHeaders(): array
{
return [];
}
public function send(
string|array $recipient,
string $subject,
string $content,
Tenant|WebsiteType|null $brand = null,
): void {
if (! $this->mailer || ! $this->tenant) {
throw new Exception('MailService no está configurado. Llamá a forTenant() o forClient() primero.');
}
$brand ??= $this->tenant;
$branding = $this->brandingFor($brand);
$html = Blade::render(
<<<'BLADE'
<x-mail.branded-layout :branding="$branding" :header-logo-url="$headerLogoUrl" :footer-logo-url="$footerLogoUrl">
{!! $content !!}
</x-mail.branded-layout>
BLADE,
[
'branding' => $branding,
'headerLogoUrl' => $brand instanceof WebsiteType
? $brand->siteLogo?->getTemporaryUrl(1440)
: $brand->headerLogo?->getTemporaryUrl(1440),
'footerLogoUrl' => $brand->footerLogo?->getTemporaryUrl(1440),
'content' => $content,
],
);
$mail = (new Mailable)
->subject($subject)
->html($html);
$this->mailer->to($recipient)->send($mail);
}
public function mailerName(): string
{
return $this->usesClientMailer
? 'client-smtp'
: (string) config('mail.default');
}
/** @return array{name: string, primary_color: string, body_color: string, background_color: string, surface_color: string, header_bg_color: string, footer_bg_color: string} */
private function brandingFor(Tenant|WebsiteType $brand): array
{
if ($brand instanceof WebsiteType) {
$brand->loadMissing(['siteLogo', 'footerLogo']);
return [
'name' => $brand->nombre,
'primary_color' => $brand->primary_color ?? '#FF7006',
'body_color' => $brand->body_color ?? '#666666',
'background_color' => $brand->background_color ?? '#f8f8f8',
'surface_color' => $brand->surface_color ?? '#ffffff',
'header_bg_color' => $brand->surface_color ?? '#ffffff',
'footer_bg_color' => $brand->login_header_footer_color ?? '#838383',
];
}
$brand->loadMissing(['headerLogo', 'footerLogo']);
return [
'name' => $brand->nombre,
'primary_color' => $brand->primary_color ?? '#6376f3',
'body_color' => '#334155',
'background_color' => '#f1f5f9',
'surface_color' => '#ffffff',
'header_bg_color' => $brand->header_bg_color ?? '#ffffff',
'footer_bg_color' => $brand->footer_bg_color ?? '#334155',
];
}
public function onSetup(): void
{
if (! $this->mailer || ! $this->clientContext) {
throw new Exception('MailService no está configurado. Llamá a forTenant() o forClient() primero.');
}
$recipient = $this->getIntegrationSetting('MAIL_FROM_ADDRESS');
if (! is_string($recipient) || $recipient === '') {
throw new InvalidArgumentException('Falta MAIL_FROM_ADDRESS en la configuración SMTP del cliente.');
}
$subject = 'Configuración de correo validada';
$content = '<h1 style="margin: 0 0 20px;">Configuración de correo validada</h1>'
.'<p>La integración SMTP de '.e($this->clientContext->name).' fue configurada correctamente.</p>'
.'<p style="color: #64748b; font-size: 13px;">Este mensaje fue enviado automáticamente para validar las credenciales de correo.</p>';
if ($this->tenant) {
$this->send($recipient, $subject, $content);
return;
}
$this->mailer->to($recipient)->send(
(new Mailable)->subject($subject)->html($content)
);
}
private function resolveMailer(): Mailer
{
$data = $this->clientIntegration?->integration_data;
if (! is_array($data)) {
throw new InvalidArgumentException('La configuración SMTP del cliente no es válida.');
}
foreach (self::REQUIRED_SMTP_FIELDS as $field) {
if (! array_key_exists($field, $data) || $data[$field] === null || $data[$field] === '') {
throw new InvalidArgumentException("Falta {$field} en la configuración SMTP del cliente.");
}
}
// MailFake implements MailFactory but cannot build transports.
if (! $this->mailFactory instanceof MailManager) {
return $this->mailFactory->mailer();
}
$mailer = $this->mailFactory->build([
'name' => 'client-smtp-'.$this->clientContext?->id,
'transport' => 'smtp',
'scheme' => $data['MAIL_SCHEME'] ?? null,
'host' => $data['MAIL_HOST'],
'port' => (int) $data['MAIL_PORT'],
'username' => $data['MAIL_USERNAME'],
'password' => $data['MAIL_PASSWORD'],
'timeout' => isset($data['MAIL_TIMEOUT']) ? (int) $data['MAIL_TIMEOUT'] : null,
'local_domain' => $data['MAIL_EHLO_DOMAIN'] ?? null,
]);
$mailer->alwaysFrom(
$data['MAIL_FROM_ADDRESS'],
$data['MAIL_FROM_NAME'] ?? $this->clientContext?->name,
);
return $mailer;
}
}