Added dns updates
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<x-filament-panels::page>
|
||||
<form wire:submit="generateDMARC">
|
||||
{{ $this->form }}
|
||||
</form>
|
||||
|
||||
<x-filament::modal id="dmarc-record-modal" width="2xl">
|
||||
<x-slot name="heading">
|
||||
Generated DMARC Record
|
||||
</x-slot>
|
||||
|
||||
<div class="space-y-4">
|
||||
<pre class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 text-sm overflow-x-auto whitespace-pre-wrap break-all">{{ $generatedRecord }}</pre>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
x-on:click="navigator.clipboard.writeText(@js($generatedRecord))"
|
||||
class="fi-btn"
|
||||
>
|
||||
Copy to clipboard
|
||||
</button>
|
||||
</div>
|
||||
</x-filament::modal>
|
||||
<div wire:poll.5s>
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
@@ -1,316 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Resources\DmarcResource\Pages;
|
||||
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Repeater;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Slider;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\DmarcResource;
|
||||
use VEximweb\Plugin\DnsTools\Models\DmarcCheck;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Services\DmarcCheckService;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\DmarcRecord;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcPolicy;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcAlignment;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcReporting;
|
||||
use VEximweb\Core\Data\Models\Domain;
|
||||
|
||||
class GenerateDmarc extends CreateRecord
|
||||
{
|
||||
protected static string $resource = DmarcResource::class;
|
||||
|
||||
public Domain $domain;
|
||||
public ?DmarcCheck $existingDmarc = null;
|
||||
|
||||
public function mount($record = null): void
|
||||
{
|
||||
if ($record) {
|
||||
$this->domain = Domain::find($record);
|
||||
$this->existingDmarc = DmarcCheck::where('domain', $this->domain->domain)->first();
|
||||
}
|
||||
|
||||
parent::mount();
|
||||
|
||||
if ($this->existingDmarc) {
|
||||
$defaults = [
|
||||
'policy' => $this->existingDmarc->policy ?? 'none',
|
||||
'subdomain_policy' => $this->existingDmarc->subdomain_policy ?? null,
|
||||
'adkim' => $this->existingDmarc->adkim ?? 'relaxed',
|
||||
'aspf' => $this->existingDmarc->aspf ?? 'relaxed',
|
||||
'reporting' => json_decode($this->existingDmarc->reporting ?? '["all"]', true),
|
||||
'percentage' => $this->existingDmarc->percentage ?? 100,
|
||||
't' => $this->existingDmarc->t ?? 'n',
|
||||
];
|
||||
|
||||
if ($this->existingDmarc->rua) {
|
||||
$rua = json_decode($this->existingDmarc->rua, true);
|
||||
if (is_array($rua)) {
|
||||
$defaults['rua'] = collect($rua)->map(function ($item) {
|
||||
return ['email' => str_replace('mailto:', '', $item)];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->existingDmarc->ruf) {
|
||||
$ruf = json_decode($this->existingDmarc->ruf, true);
|
||||
if (is_array($ruf)) {
|
||||
$defaults['ruf'] = collect($ruf)->map(function ($item) {
|
||||
return ['email' => str_replace('mailto:', '', $item)];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
$this->form->fill($defaults);
|
||||
}
|
||||
}
|
||||
|
||||
public function form(Form $form): Form
|
||||
{
|
||||
$service = app(DmarcCheckService::class);
|
||||
$addresses = $service->getDefaultReportingAddresses($this->domain->domain ?? config('app.domain'));
|
||||
|
||||
return $form
|
||||
->schema([
|
||||
Select::make('policy')
|
||||
->label('Policy')
|
||||
->options([
|
||||
'none' => 'None (Monitor only)',
|
||||
'quarantine' => 'Quarantine (Mark as spam)',
|
||||
'reject' => 'Reject (Block delivery)',
|
||||
])
|
||||
->required()
|
||||
->default('none')
|
||||
->live(),
|
||||
|
||||
Select::make('subdomain_policy')
|
||||
->label('Subdomain Policy')
|
||||
->options([
|
||||
'' => 'Inherit from main policy',
|
||||
'none' => 'None (Monitor only)',
|
||||
'quarantine' => 'Quarantine (Mark as spam)',
|
||||
'reject' => 'Reject (Block delivery)',
|
||||
])
|
||||
->nullable(),
|
||||
|
||||
Repeater::make('rua')
|
||||
->label('Aggregate Reports (RUA)')
|
||||
->schema([
|
||||
TextInput::make('email')
|
||||
->label('Email')
|
||||
->email()
|
||||
->prefix('mailto:')
|
||||
->required(),
|
||||
])
|
||||
->default(function () use ($addresses) {
|
||||
if ($this->existingDmarc && $this->existingDmarc->rua) {
|
||||
$rua = json_decode($this->existingDmarc->rua, true);
|
||||
if (is_array($rua)) {
|
||||
return collect($rua)->map(function ($item) {
|
||||
return ['email' => str_replace('mailto:', '', $item)];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
return [['email' => str_replace('mailto:', '', $addresses['rua'])]];
|
||||
})
|
||||
->addable()
|
||||
->deletable()
|
||||
->reorderable()
|
||||
->columnSpanFull(),
|
||||
|
||||
Repeater::make('ruf')
|
||||
->label('Forensic Reports (RUF)')
|
||||
->schema([
|
||||
TextInput::make('email')
|
||||
->label('Email')
|
||||
->email()
|
||||
->prefix('mailto:')
|
||||
->required(),
|
||||
])
|
||||
->default(function () use ($addresses) {
|
||||
if ($this->existingDmarc && $this->existingDmarc->ruf) {
|
||||
$ruf = json_decode($this->existingDmarc->ruf, true);
|
||||
if (is_array($ruf)) {
|
||||
return collect($ruf)->map(function ($item) {
|
||||
return ['email' => str_replace('mailto:', '', $item)];
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
return [['email' => str_replace('mailto:', '', $addresses['ruf'])]];
|
||||
})
|
||||
->addable()
|
||||
->deletable()
|
||||
->reorderable()
|
||||
->columnSpanFull(),
|
||||
|
||||
Select::make('adkim')
|
||||
->label('DKIM Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed (subdomains allowed)',
|
||||
'strict' => 'Strict (exact match)',
|
||||
])
|
||||
->default('relaxed'),
|
||||
|
||||
Select::make('aspf')
|
||||
->label('SPF Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed (subdomains allowed)',
|
||||
'strict' => 'Strict (exact match)',
|
||||
])
|
||||
->default('relaxed'),
|
||||
|
||||
Select::make('reporting')
|
||||
->label('Failure Reporting (FO)')
|
||||
->multiple()
|
||||
->options([
|
||||
'all' => 'All failures',
|
||||
'any' => 'Any failure',
|
||||
'dkim' => 'DKIM failures only',
|
||||
'spf' => 'SPF failures only',
|
||||
])
|
||||
->default(['all']),
|
||||
|
||||
Slider::make('percentage')
|
||||
->label('Enforcement Percentage')
|
||||
->min(0)
|
||||
->max(100)
|
||||
->default(100),
|
||||
|
||||
Select::make('t')
|
||||
->label('Testing Mode')
|
||||
->options([
|
||||
'n' => 'Off (Apply policy)',
|
||||
'y' => 'On (Don\'t apply policy)',
|
||||
])
|
||||
->default('n'),
|
||||
|
||||
Placeholder::make('generated_record_preview')
|
||||
->label('Preview')
|
||||
->content(function ($get) {
|
||||
try {
|
||||
$settings = $this->prepareSettings($get);
|
||||
$dmarcRecord = new DmarcRecord(
|
||||
policy: DmarcPolicy::tryFrom($settings['policy']),
|
||||
subdomainPolicy: $settings['subdomain_policy'] ? DmarcPolicy::tryFrom($settings['subdomain_policy']) : null,
|
||||
rua: $settings['rua'],
|
||||
ruf: $settings['ruf'],
|
||||
adkim: DmarcAlignment::tryFrom($settings['adkim']),
|
||||
aspf: DmarcAlignment::tryFrom($settings['aspf']),
|
||||
reporting: array_map(fn($r) => DmarcReporting::tryFrom($r), $settings['reporting']),
|
||||
percentage: (int)$settings['percentage'],
|
||||
t: $settings['t'],
|
||||
);
|
||||
|
||||
if ($this->domain) {
|
||||
return "Name: _dmarc.{$this->domain->domain}\n" .
|
||||
"Type: TXT\n" .
|
||||
"Value: v=DMARC1; " . $dmarcRecord->toDnsRecord();
|
||||
}
|
||||
return "Please select a domain to preview";
|
||||
} catch (\Exception $e) {
|
||||
return "Error generating preview: " . $e->getMessage();
|
||||
}
|
||||
})
|
||||
->extraAttributes(['class' => 'font-mono text-sm bg-gray-50 dark:bg-gray-800 p-4 rounded'])
|
||||
->columnSpanFull(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function prepareSettings(array $data): array
|
||||
{
|
||||
return [
|
||||
'policy' => $data['policy'] ?? 'none',
|
||||
'subdomain_policy' => $data['subdomain_policy'] ?? null,
|
||||
'rua' => collect($data['rua'] ?? [])
|
||||
->filter(fn($item) => !empty($item['email']))
|
||||
->map(fn($item) => 'mailto:' . $item['email'])
|
||||
->values()
|
||||
->toArray(),
|
||||
'ruf' => collect($data['ruf'] ?? [])
|
||||
->filter(fn($item) => !empty($item['email']))
|
||||
->map(fn($item) => 'mailto:' . $item['email'])
|
||||
->values()
|
||||
->toArray(),
|
||||
'adkim' => $data['adkim'] ?? 'relaxed',
|
||||
'aspf' => $data['aspf'] ?? 'relaxed',
|
||||
'reporting' => $data['reporting'] ?? ['all'],
|
||||
'percentage' => (int)($data['percentage'] ?? 100),
|
||||
't' => $data['t'] ?? 'n',
|
||||
];
|
||||
}
|
||||
|
||||
protected function handleRecordCreation(array $data): \Illuminate\Database\Eloquent\Model
|
||||
{
|
||||
$settings = $this->prepareSettings($data);
|
||||
|
||||
$dmarcRecord = new DmarcRecord(
|
||||
policy: DmarcPolicy::tryFrom($settings['policy']),
|
||||
subdomainPolicy: $settings['subdomain_policy'] ? DmarcPolicy::tryFrom($settings['subdomain_policy']) : null,
|
||||
rua: $settings['rua'],
|
||||
ruf: $settings['ruf'],
|
||||
adkim: DmarcAlignment::tryFrom($settings['adkim']),
|
||||
aspf: DmarcAlignment::tryFrom($settings['aspf']),
|
||||
reporting: array_map(fn($r) => DmarcReporting::tryFrom($r), $settings['reporting']),
|
||||
percentage: (int)$settings['percentage'],
|
||||
t: $settings['t'],
|
||||
);
|
||||
|
||||
$dnsRecord = 'v=DMARC1; ' . $dmarcRecord->toDnsRecord();
|
||||
|
||||
return DmarcCheck::updateOrCreate(
|
||||
['domain' => $this->domain->domain],
|
||||
[
|
||||
'domain_id' => $this->domain->domain_id,
|
||||
'record' => $dnsRecord,
|
||||
'policy' => $settings['policy'],
|
||||
'subdomain_policy' => $settings['subdomain_policy'],
|
||||
'adkim' => $settings['adkim'],
|
||||
'aspf' => $settings['aspf'],
|
||||
'rua' => json_encode($settings['rua']),
|
||||
'ruf' => json_encode($settings['ruf']),
|
||||
'reporting' => json_encode($settings['reporting']),
|
||||
'percentage' => (int)$settings['percentage'],
|
||||
't' => $settings['t'],
|
||||
'valid' => true,
|
||||
'error_message' => null,
|
||||
'last_checked_at' => now(),
|
||||
'next_check_at' => now()->addHours(24),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getCreatedNotificationTitle(): ?string
|
||||
{
|
||||
return "DMARC record generated for {$this->domain->domain}";
|
||||
}
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return DmarcResource::getUrl('view', ['record' => $this->domain->getKey()]);
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('back')
|
||||
->label('Back to View')
|
||||
->icon('heroicon-o-arrow-left')
|
||||
->url(DmarcResource::getUrl('view', ['record' => $this->domain->getKey()])),
|
||||
|
||||
Action::make('back_to_list')
|
||||
->label('Back to List')
|
||||
->icon('heroicon-o-list-bullet')
|
||||
->url(DmarcResource::getUrl('index')),
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
$action = $this->existingDmarc ? 'Update' : 'Generate';
|
||||
return "{$action} DMARC Record";
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Pages\Dmarc;
|
||||
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Columns\BadgeColumn;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Notifications\Notification;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\DmarcResource;
|
||||
use VEximweb\Plugin\DnsTools\Models\DmarcCheck;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Services\DmarcCheckService;
|
||||
use VEximweb\Core\Data\Models\Domain;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class ListDmarc extends ListRecords
|
||||
{
|
||||
protected static string $resource = DmarcResource::class;
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Domain::where('enabled', true);
|
||||
}
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query($this->getTableQuery())
|
||||
->columns([
|
||||
TextColumn::make('domain')
|
||||
->label('Domain')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->size('lg')
|
||||
->weight('bold'),
|
||||
|
||||
BadgeColumn::make('dmarc_status')
|
||||
->label('DMARC Status')
|
||||
->getStateUsing(function ($record) {
|
||||
$dmarc = DmarcCheck::where('domain', $record->domain)->first();
|
||||
if (!$dmarc) {
|
||||
return 'Not Checked';
|
||||
}
|
||||
if (!$dmarc->valid) {
|
||||
return 'No Record';
|
||||
}
|
||||
return $dmarc->getPolicyLabel();
|
||||
})
|
||||
->colors([
|
||||
'success' => 'Monitor',
|
||||
'warning' => 'Quarantine',
|
||||
'danger' => 'Reject',
|
||||
'gray' => ['Not Checked', 'No Record'],
|
||||
]),
|
||||
|
||||
BadgeColumn::make('dmarc_policy')
|
||||
->label('Policy')
|
||||
->getStateUsing(function ($record) {
|
||||
$dmarc = DmarcCheck::where('domain', $record->domain)->first();
|
||||
return $dmarc?->policy ?? '-';
|
||||
})
|
||||
->colors([
|
||||
'success' => 'none',
|
||||
'warning' => 'quarantine',
|
||||
'danger' => 'reject',
|
||||
'gray' => '-',
|
||||
]),
|
||||
|
||||
TextColumn::make('dmarc_last_checked')
|
||||
->label('Last Checked')
|
||||
->getStateUsing(function ($record) {
|
||||
$dmarc = DmarcCheck::where('domain', $record->domain)->first();
|
||||
return $dmarc?->last_checked_at;
|
||||
})
|
||||
->since()
|
||||
->toggleable(),
|
||||
|
||||
TextColumn::make('exim_users_count')
|
||||
->label('Accounts')
|
||||
->counts('eximUsers')
|
||||
->badge()
|
||||
->color('info'),
|
||||
])
|
||||
->filters([
|
||||
\Filament\Tables\Filters\SelectFilter::make('dmarc_status')
|
||||
->label('DMARC Status')
|
||||
->options([
|
||||
'has_record' => 'Has DMARC Record',
|
||||
'no_record' => 'No DMARC Record',
|
||||
'not_checked' => 'Not Checked',
|
||||
])
|
||||
->query(function ($query, $data) {
|
||||
if (empty($data['value'])) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return match($data['value']) {
|
||||
'has_record' => $query->whereIn('domain',
|
||||
DmarcCheck::where('valid', true)->pluck('domain')->toArray()
|
||||
),
|
||||
'no_record' => $query->whereIn('domain',
|
||||
DmarcCheck::where('valid', false)->pluck('domain')->toArray()
|
||||
),
|
||||
'not_checked' => $query->whereNotIn('domain',
|
||||
DmarcCheck::pluck('domain')->toArray()
|
||||
),
|
||||
default => $query,
|
||||
};
|
||||
}),
|
||||
|
||||
\Filament\Tables\Filters\SelectFilter::make('policy')
|
||||
->label('Policy')
|
||||
->options([
|
||||
'none' => 'Monitor',
|
||||
'quarantine' => 'Quarantine',
|
||||
'reject' => 'Reject',
|
||||
])
|
||||
->query(function ($query, $data) {
|
||||
if (empty($data['value'])) {
|
||||
return $query;
|
||||
}
|
||||
$domains = DmarcCheck::where('policy', $data['value'])
|
||||
->where('valid', true)
|
||||
->pluck('domain')
|
||||
->toArray();
|
||||
return $query->whereIn('domain', $domains);
|
||||
}),
|
||||
])
|
||||
->actions([
|
||||
Action::make('view')
|
||||
->label('View DMARC')
|
||||
->icon('heroicon-o-eye')
|
||||
->color('info')
|
||||
->url(fn ($record): string => DmarcResource::getUrl('view', ['record' => $record])),
|
||||
|
||||
Action::make('generate')
|
||||
->label('Generate Record')
|
||||
->icon('heroicon-o-document-plus')
|
||||
->color('success')
|
||||
->url(fn ($record): string => DmarcResource::getUrl('generate', ['record' => $record])),
|
||||
|
||||
Action::make('check_dns')
|
||||
->label('Check DNS')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->color('warning')
|
||||
->action(function ($record) {
|
||||
$service = app(DmarcCheckService::class);
|
||||
$result = $service->checkDomain($record->domain, $record->domain_id);
|
||||
|
||||
if ($result && $result->valid) {
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('DMARC Record Found')
|
||||
->body("Policy: {$result->getPolicyLabel()}")
|
||||
->send();
|
||||
} else {
|
||||
Notification::make()
|
||||
->warning()
|
||||
->title('No DMARC Record Found')
|
||||
->body($result?->error_message ?? 'No DMARC record found in DNS')
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
])
|
||||
->bulkActions([
|
||||
BulkActionGroup::make([
|
||||
Action::make('check_all_dmarc')
|
||||
->label('Check DMARC for Selected')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->action(function ($records) {
|
||||
$service = app(DmarcCheckService::class);
|
||||
$valid = 0;
|
||||
|
||||
foreach ($records as $record) {
|
||||
$result = $service->checkDomain($record->domain, $record->domain_id);
|
||||
if ($result && $result->valid) {
|
||||
$valid++;
|
||||
}
|
||||
}
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('DMARC Check Complete')
|
||||
->body("Checked {$records->count()} domains, {$valid} have valid DMARC records")
|
||||
->send();
|
||||
}),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Pages\Dmarc;
|
||||
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\DmarcResource;
|
||||
use VEximweb\Plugin\DnsTools\Models\DmarcCheck;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Services\DmarcCheckService;
|
||||
|
||||
class ViewDmarc extends ViewRecord
|
||||
{
|
||||
protected static string $resource = DmarcResource::class;
|
||||
|
||||
public ?DmarcCheck $dmarc = null;
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
// Get the DMARC record for this domain
|
||||
$this->dmarc = DmarcCheck::where('domain', $this->record->domain)->first();
|
||||
|
||||
// If no cache or invalid, check now
|
||||
if (!$this->dmarc || !$this->dmarc->valid) {
|
||||
$service = app(DmarcCheckService::class);
|
||||
$this->dmarc = $service->checkDomain($this->record->domain, $this->record->domain_id);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Action::make('check_dns')
|
||||
->label('Check DNS Again')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->color('warning')
|
||||
->action(function () {
|
||||
$service = app(DmarcCheckService::class);
|
||||
$this->dmarc = $service->checkDomain($this->record->domain, $this->record->domain_id);
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title('DMARC Record Updated')
|
||||
->body($this->dmarc?->valid ? 'Record found and updated' : 'No valid record found')
|
||||
->send();
|
||||
|
||||
$this->refreshFormData();
|
||||
}),
|
||||
|
||||
Action::make('generate')
|
||||
->label('Generate New Record')
|
||||
->icon('heroicon-o-document-plus')
|
||||
->color('success')
|
||||
->url(DmarcResource::getUrl('generate', ['record' => $this->record])),
|
||||
|
||||
Action::make('back')
|
||||
->label('Back to List')
|
||||
->icon('heroicon-o-arrow-left')
|
||||
->url(DmarcResource::getUrl('index')),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getViewData(): array
|
||||
{
|
||||
return [
|
||||
'dmarc' => $this->dmarc,
|
||||
];
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return "DMARC Record for {$this->record->domain}";
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Providers;
|
||||
|
||||
use Filament\Panel;
|
||||
use Filament\PanelProvider;
|
||||
use Filament\Support\Colors\Color;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\DmarcResource;
|
||||
|
||||
class DnsToolsPanelProvider extends PanelProvider
|
||||
{
|
||||
public function panel(Panel $panel): Panel
|
||||
{
|
||||
return $panel
|
||||
->id('dns-tools')
|
||||
->path('dns-tools')
|
||||
->colors([
|
||||
'primary' => Color::Blue,
|
||||
])
|
||||
->resources([
|
||||
DmarcResource::class,
|
||||
])
|
||||
->navigationGroups([
|
||||
'DNS Tools',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Resources;
|
||||
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Notifications\Notification;
|
||||
use VEximweb\Core\Data\Models\Domain;
|
||||
use VEximweb\Plugin\DnsTools\Models\DmarcCheck;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Services\DmarcCheckService;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\DmarcRecord;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcPolicy;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcAlignment;
|
||||
use VEximweb\Plugin\DnsTools\Dmarc\Enums\DmarcReporting;
|
||||
use BackedEnum;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Pages\Dmarc\ListDmarc;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Pages\Dmarc\ViewDmarc;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Pages\Dmarc\GenerateDmarc;
|
||||
|
||||
class DmarcResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Domain::class;
|
||||
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-shield-check';
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'DNS Tools';
|
||||
protected static ?string $navigationLabel = 'DMARC Management';
|
||||
protected static ?string $pluralLabel = 'DMARC Management';
|
||||
protected static ?int $navigationSort = 1;
|
||||
protected static ?string $slug = 'dnstools/dmarc';
|
||||
|
||||
public static function getModel(): string
|
||||
{
|
||||
return Domain::class;
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListDmarc::route('/'),
|
||||
'view' => ViewDmarc::route('/{record}/view'),
|
||||
'generate' => GenerateDmarc::route('/{record}/generate'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -9,32 +9,75 @@ use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use VEximweb\Plugin\DnsTools\Models\SystemDomains as Domain;
|
||||
|
||||
class GenerateDmarcForm
|
||||
{
|
||||
protected static ?string $domain = null;
|
||||
protected static ?Domain $domainRecord = null;
|
||||
|
||||
/**
|
||||
* Set the domain for the form.
|
||||
*/
|
||||
public static function setDomain(string $domain): void
|
||||
public static function extend(callable $components, callable $onSave): void
|
||||
{
|
||||
static::$domain = $domain;
|
||||
$existing = app()->bound('dmarcform.extenders')
|
||||
? app('dmarcform.extenders')
|
||||
: ['components' => [], 'hooks' => []];
|
||||
|
||||
$existing['components'][] = $components;
|
||||
$existing['hooks'][] = $onSave;
|
||||
|
||||
app()->instance('dmarcform.extenders', $existing);
|
||||
}
|
||||
|
||||
public static function runSaveHooks(mixed $record, array $data): void
|
||||
{
|
||||
if (! app()->bound('dmarcform.extenders')) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (app('dmarcform.extenders')['hooks'] as $hook) {
|
||||
$hook($record, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current domain.
|
||||
* Set the domain record for the form.
|
||||
*/
|
||||
protected static function getDomain(): string
|
||||
public static function setDomainRecord(Domain $domain): void
|
||||
{
|
||||
return static::$domain ?? 'example.com';
|
||||
static::$domainRecord = $domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Filament form schema.
|
||||
* Get the current domain record.
|
||||
*/
|
||||
public static function schema(string $domain): array
|
||||
protected static function getDomainRecord(): ?Domain
|
||||
{
|
||||
return static::$domainRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Filament form schema - now accepts Domain record instead of string
|
||||
*/
|
||||
public static function schema(Domain $domain): array
|
||||
{
|
||||
// Set the domain record for extensions
|
||||
static::setDomainRecord($domain);
|
||||
|
||||
$extra = [];
|
||||
|
||||
if (app()->bound('dmarcform.extenders')) {
|
||||
foreach (app('dmarcform.extenders')['components'] as $extender) {
|
||||
// Pass the domain record directly to the extender
|
||||
if (is_callable($extender)) {
|
||||
$result = $extender($domain);
|
||||
if (is_array($result)) {
|
||||
$extra = array_merge($extra, $result);
|
||||
}
|
||||
} else {
|
||||
$extra = array_merge($extra, $extender());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
Section::make('Policy')
|
||||
->columns(2)
|
||||
@@ -106,27 +149,29 @@ class GenerateDmarcForm
|
||||
|
||||
TextInput::make('dmarc_rua_localpart')
|
||||
->label('Aggregate Reports Email')
|
||||
->helperText('Email address for aggregate reports (auto-appended with @' . $domain . ')')
|
||||
->helperText('Email address for aggregate reports (auto-appended with @' . $domain->domain . ')')
|
||||
->default('dmarc-reports')
|
||||
->prefix('📊')
|
||||
->suffix('@' . $domain),
|
||||
->suffix('@' . $domain->domain),
|
||||
|
||||
TextInput::make('dmarc_ruf_localpart')
|
||||
->label('Forensic Reports Email')
|
||||
->helperText('Email address for forensic reports (auto-appended with @' . $domain . ')')
|
||||
->helperText('Email address for forensic reports (auto-appended with @' . $domain->domain . ')')
|
||||
->default('dmarc-forensic')
|
||||
->prefix('🔍')
|
||||
->suffix('@' . $domain),
|
||||
->suffix('@' . $domain->domain),
|
||||
]),
|
||||
|
||||
...$extra,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form from the repository.
|
||||
* Populate the form from the repository - now accepts domain record
|
||||
*/
|
||||
public static function values(SettingRepositoryInterface $settings): array
|
||||
public static function values(SettingRepositoryInterface $settings, Domain $domain): array
|
||||
{
|
||||
// Get settings for this domain (you might need to adjust this)
|
||||
// Get settings for this domain
|
||||
$rua_localpart = $settings->get('dmarc_rua_localpart', 'dmarc-reports');
|
||||
$ruf_localpart = $settings->get('dmarc_ruf_localpart', 'dmarc-forensic');
|
||||
|
||||
@@ -147,13 +192,13 @@ class GenerateDmarcForm
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the form back to the repository.
|
||||
* Save the form back to the repository - now accepts domain record
|
||||
*/
|
||||
public static function save(
|
||||
public static function save(
|
||||
SettingRepositoryInterface $settings,
|
||||
array $data,
|
||||
string $domain,
|
||||
): void {
|
||||
Domain $domain,
|
||||
): void {
|
||||
foreach ([
|
||||
'dmarc_policy',
|
||||
'dmarc_subdomain_policy',
|
||||
@@ -170,8 +215,8 @@ public static function save(
|
||||
$settings->set($key, $data[$key]);
|
||||
}
|
||||
|
||||
$settings->set('dmarc_rua', [$data['dmarc_rua_localpart'] . '@' . $domain]);
|
||||
$settings->set('dmarc_ruf', [$data['dmarc_ruf_localpart'] . '@' . $domain]);
|
||||
$settings->set('dmarc_rua', [$data['dmarc_rua_localpart'] . '@' . $domain->domain]);
|
||||
$settings->set('dmarc_ruf', [$data['dmarc_ruf_localpart'] . '@' . $domain->domain]);
|
||||
$settings->set('dmarc_reporting', $data['dmarc_reporting'] ?? []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Pages;
|
||||
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Components\Actions;
|
||||
use Filament\Forms\Components\CheckboxList;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Schemas\Schema;
|
||||
use BackedEnum;
|
||||
use UnitEnum;
|
||||
use Filament\Notifications\Notification;
|
||||
use VEximweb\Core\Data\Repositories\Interfaces\SettingRepositoryInterface;
|
||||
use VEximweb\Plugin\DnsTools\Models\SystemDomains as Domain;
|
||||
use VEximweb\Plugin\DnsTools\Services\DmarcRecordService;
|
||||
|
||||
class GenerateDmarcPage extends Page
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
protected string $view = 'dns-tools::filament.pages.generate-dmarc';
|
||||
|
||||
public ?array $data = [];
|
||||
|
||||
public Domain $domain;
|
||||
|
||||
public ?string $generatedRecord = null;
|
||||
|
||||
public static function getResource(): string
|
||||
{
|
||||
return \VEximweb\Plugin\DnsTools\Filament\Resources\DnsToolsResource::class;
|
||||
}
|
||||
|
||||
public static function getNavigationIcon(): string|BackedEnum|null
|
||||
{
|
||||
return 'heroicon-o-document-text';
|
||||
}
|
||||
|
||||
public static function getNavigationGroup(): string|UnitEnum|null
|
||||
{
|
||||
return 'Generate DMARC';
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Generate DMARC Record';
|
||||
}
|
||||
|
||||
public function getListeners(): array
|
||||
{
|
||||
return [
|
||||
'refreshNotifications' => 'refreshNotifications',
|
||||
];
|
||||
}
|
||||
|
||||
public function mount(Domain $domain): void
|
||||
{
|
||||
$this->domain = $domain;
|
||||
$domainId = $this->domain->id;
|
||||
|
||||
// Load existing settings if any
|
||||
$settings = app(SettingRepositoryInterface::class);
|
||||
$this->form->fill($this->getValues($settings));
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->schema($this->getFormSchema())
|
||||
->statePath('data');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form schema as an array of components
|
||||
*/
|
||||
protected function getFormSchema(): array
|
||||
{
|
||||
$extra = [];
|
||||
|
||||
if (app()->bound('dmarcform.extenders')) {
|
||||
foreach (app('dmarcform.extenders')['components'] as $extender) {
|
||||
if (is_callable($extender)) {
|
||||
$result = $extender($this->domain);
|
||||
if (is_array($result)) {
|
||||
$extra = array_merge($extra, $result);
|
||||
}
|
||||
} else {
|
||||
$extra = array_merge($extra, $extender());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
Section::make('Policy')
|
||||
->columns(2)
|
||||
->schema([
|
||||
Hidden::make('domain_id')
|
||||
->default($this->domain->id),
|
||||
Select::make('dmarc_policy')
|
||||
->label('Policy')
|
||||
->options([
|
||||
'none' => 'None',
|
||||
'quarantine' => 'Quarantine',
|
||||
'reject' => 'Reject',
|
||||
])
|
||||
->required(),
|
||||
|
||||
Select::make('dmarc_subdomain_policy')
|
||||
->label('Subdomain Policy')
|
||||
->options([
|
||||
'' => 'Inherit Policy',
|
||||
'none' => 'None',
|
||||
'quarantine' => 'Quarantine',
|
||||
'reject' => 'Reject',
|
||||
]),
|
||||
|
||||
Hidden::make('dmarc_np')
|
||||
->default(''),
|
||||
|
||||
Hidden::make('dmarc_psd')
|
||||
->default('n'),
|
||||
|
||||
Hidden::make('dmarc_t')
|
||||
->default('n'),
|
||||
|
||||
Hidden::make('dmarc_percentage')
|
||||
->default(100),
|
||||
|
||||
Hidden::make('dmarc_report_interval')
|
||||
->default(86400),
|
||||
]),
|
||||
|
||||
Section::make('Alignment')
|
||||
->columns(2)
|
||||
->schema([
|
||||
Select::make('dmarc_adkim')
|
||||
->label('DKIM Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed',
|
||||
'strict' => 'Strict',
|
||||
]),
|
||||
|
||||
Select::make('dmarc_aspf')
|
||||
->label('SPF Alignment')
|
||||
->options([
|
||||
'relaxed' => 'Relaxed',
|
||||
'strict' => 'Strict',
|
||||
]),
|
||||
]),
|
||||
|
||||
Section::make('Reporting')
|
||||
->schema([
|
||||
CheckboxList::make('dmarc_reporting')
|
||||
->label('Forensic Report Triggers')
|
||||
->helperText('"Any" and "All" are mutually exclusive; DKIM/SPF can be combined.')
|
||||
->options([
|
||||
'any' => 'Any mechanism fails (DKIM or SPF, most common) ',
|
||||
'all' => 'All mechanisms fail (Not recommended)',
|
||||
'dkim' => 'DKIM fails',
|
||||
'spf' => 'SPF fails',
|
||||
])
|
||||
->rule(fn () => function (string $attribute, $value, \Closure $fail) {
|
||||
if (in_array('all', $value ?? []) && in_array('any', $value ?? [])) {
|
||||
$fail("'Any' and 'All' cannot both be selected.");
|
||||
}
|
||||
}),
|
||||
|
||||
TextInput::make('dmarc_rua_localpart')
|
||||
->label('Aggregate Reports Email')
|
||||
->helperText('Email address for aggregate reports (auto-appended with @' . $this->domain->domain . ')')
|
||||
->default('dmarc-reports')
|
||||
->suffix('@' . $this->domain->domain),
|
||||
|
||||
TextInput::make('dmarc_ruf_localpart')
|
||||
->label('Forensic Reports Email')
|
||||
->helperText('Email address for forensic reports (auto-appended with @' . $this->domain->domain . ')')
|
||||
->default('dmarc-forensic')
|
||||
->suffix('@' . $this->domain->domain),
|
||||
]),
|
||||
|
||||
...$extra,
|
||||
|
||||
Actions::make([
|
||||
Action::make('generate')
|
||||
->label('Generate DMARC Record')
|
||||
->submit('generateDMARC'),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getValues(SettingRepositoryInterface $settings): array
|
||||
{
|
||||
$rua_localpart = $settings->get('dmarc_rua_localpart', 'dmarc-reports');
|
||||
$ruf_localpart = $settings->get('dmarc_ruf_localpart', 'dmarc-forensic');
|
||||
|
||||
return [
|
||||
'domain_id' => $this->domain->domain_id,
|
||||
'dmarc_policy' => $settings->get('dmarc_policy'),
|
||||
'dmarc_subdomain_policy' => $settings->get('dmarc_subdomain_policy'),
|
||||
'dmarc_np' => $settings->get('dmarc_np', ''),
|
||||
'dmarc_psd' => $settings->get('dmarc_psd', 'n'),
|
||||
'dmarc_t' => $settings->get('dmarc_t', 'n'),
|
||||
'dmarc_percentage' => $settings->get('dmarc_percentage', 100),
|
||||
'dmarc_report_interval' => $settings->get('dmarc_report_interval', 86400),
|
||||
'dmarc_adkim' => $settings->get('dmarc_adkim'),
|
||||
'dmarc_aspf' => $settings->get('dmarc_aspf'),
|
||||
'dmarc_reporting' => $settings->get('dmarc_reporting', []),
|
||||
'dmarc_rua_localpart' => $rua_localpart,
|
||||
'dmarc_ruf_localpart' => $ruf_localpart,
|
||||
];
|
||||
}
|
||||
|
||||
public function generateDMARC(DmarcRecordService $dmarc): void
|
||||
{
|
||||
$data = $this->form->getState();
|
||||
\Log::debug('Form data:'. json_encode($data));
|
||||
|
||||
$this->generatedRecord = $dmarc->generate($this->domain, $data);
|
||||
|
||||
$domainName = $this->domain->domain ?? $this->domain['domain'] ?? (string) $this->domain;
|
||||
|
||||
\Log::debug('DMARC Event data:', [
|
||||
'zone' => $domainName,
|
||||
'name' => '_dmarc',
|
||||
'content' => $this->generatedRecord
|
||||
]);
|
||||
|
||||
event(new \App\Events\DmarcKeyGenerated(
|
||||
zone: $domainName,
|
||||
name: '_dmarc',
|
||||
type: 'TXT',
|
||||
content: $this->generatedRecord,
|
||||
ttl: 3600,
|
||||
operation: 'create'
|
||||
));
|
||||
|
||||
$this->dispatch('open-modal', id: 'dmarc-record-modal');
|
||||
}
|
||||
|
||||
|
||||
// Keep the static extend method for backward compatibility
|
||||
public static function extend(callable $components, callable $onSave): void
|
||||
{
|
||||
$existing = app()->bound('dmarcform.extenders')
|
||||
? app('dmarcform.extenders')
|
||||
: ['components' => [], 'hooks' => []];
|
||||
|
||||
$existing['components'][] = $components;
|
||||
$existing['hooks'][] = $onSave;
|
||||
|
||||
app()->instance('dmarcform.extenders', $existing);
|
||||
}
|
||||
}
|
||||
@@ -14,18 +14,19 @@ use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Pages\GenerateDmarcPage;
|
||||
|
||||
class DnsToolsResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Domain::class;
|
||||
|
||||
protected static ?string $slug = 'dnstools/domains';
|
||||
protected static ?string $slug = 'dnstools';
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::WrenchScrewdriver;
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'domain';
|
||||
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'DNS Tools';
|
||||
protected static string|\UnitEnum|null $navigationGroup = 'DNS';
|
||||
|
||||
protected static ?string $navigationLabel = 'DNS Tools';
|
||||
|
||||
@@ -81,10 +82,6 @@ class DnsToolsResource extends Resource
|
||||
return $schema;
|
||||
}
|
||||
|
||||
public static function form_old(Schema $schema): Schema
|
||||
{
|
||||
return DomainForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
@@ -168,6 +165,8 @@ class DnsToolsResource extends Resource
|
||||
{
|
||||
return [
|
||||
'index' => ListDomains::route('/'),
|
||||
//'dmarc' => GenerateDmarcPage::route('/dmarc'),
|
||||
'generate' => GenerateDmarcPage::route('/{domain}/generate-dmarc'),
|
||||
//'create' => CreateDomain::route('/create'),
|
||||
//'edit' => EditDomain::route('/{record}/edit'),
|
||||
];
|
||||
|
||||
@@ -22,6 +22,8 @@ use Filament\Notifications\Notification;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\Dmarc\Modals\GenerateDmarcForm;
|
||||
use VEximweb\Core\Data\Repositories\Interfaces\SettingRepositoryInterface;
|
||||
use VEximweb\Plugin\DnsTools\Filament\Resources\DnsToolsResource;
|
||||
|
||||
|
||||
class DomainsTable
|
||||
{
|
||||
@@ -208,9 +210,25 @@ class DomainsTable
|
||||
|
||||
//EditAction::make(),
|
||||
ActionGroup::make([
|
||||
Action::make('generateDmarc')
|
||||
->label('Generate DMARC')
|
||||
->icon('heroicon-o-document-text')
|
||||
->url(fn ($record) => DnsToolsResource::getUrl('generate', ['domain' => $record])),
|
||||
EditAction::make(),
|
||||
EditAction::make(),
|
||||
EditAction::make(),
|
||||
|
||||
Action::make('dmarc')
|
||||
->fillForm(fn (SettingRepositoryInterface $settings, $record) =>
|
||||
GenerateDmarcForm::values($settings, $record) // Pass the record
|
||||
)
|
||||
->form(fn ($record) =>
|
||||
GenerateDmarcForm::schema($record) // Pass the full record, not just domain string
|
||||
)
|
||||
->action(fn (array $data, SettingRepositoryInterface $settings, $record) =>
|
||||
GenerateDmarcForm::save($settings, $data, $record) // Pass the record
|
||||
),
|
||||
/*
|
||||
Action::make('dmarc')
|
||||
->fillForm(fn (SettingRepositoryInterface $settings) =>
|
||||
GenerateDmarcForm::values($settings)
|
||||
@@ -222,7 +240,7 @@ Action::make('dmarc')
|
||||
GenerateDmarcForm::save($settings, $data, $record->domain)
|
||||
),
|
||||
|
||||
/*
|
||||
*/ /*
|
||||
Action::make('dmarc')
|
||||
->fillForm(fn (SettingRepositoryInterface $settings) =>
|
||||
GenerateDmarcForm::values($settings)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace VEximweb\Plugin\DnsTools\Services;
|
||||
|
||||
use CbowOfRivia\DmarcRecordBuilder\DmarcRecord;
|
||||
use VEximweb\Plugin\DnsTools\Models\SystemDomains as Domain;
|
||||
|
||||
class DmarcRecordService
|
||||
{
|
||||
public function build(Domain $domain, array $data): DmarcRecord
|
||||
{
|
||||
return new DmarcRecord(
|
||||
policy: $data['dmarc_policy'],
|
||||
subdomain_policy: $data['dmarc_subdomain_policy'] ?: null,
|
||||
rua: 'mailto:' . $data['dmarc_rua_localpart'] . '@' . $domain->domain,
|
||||
ruf: 'mailto:' . $data['dmarc_ruf_localpart'] . '@' . $domain->domain,
|
||||
adkim: $data['dmarc_adkim'],
|
||||
aspf: $data['dmarc_aspf'],
|
||||
reporting: $data['dmarc_reporting'] ?? [],
|
||||
np: $data['dmarc_np'] ?: null,
|
||||
psd: $data['dmarc_psd'],
|
||||
t: $data['dmarc_t'],
|
||||
);
|
||||
}
|
||||
|
||||
public function generate(Domain $domain, array $data): string
|
||||
{
|
||||
return (string) $this->build($domain, $data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user