$type, 'target' => $target], ['is_active' => true] ); } /** * Disable a sandbox route by id. */ public function disable(int $id): void { SandboxRoute::findOrFail($id)->update(['is_active' => false]); } /** * Delete a sandbox route by id. */ public function delete(int $id): void { SandboxRoute::findOrFail($id)->delete(); } /** * Write /etc/postfix/transport.sandbox and run postmap. * Returns ['ok' => bool, 'message' => string]. */ public function syncTransportFile(): array { $file = config('sandbox.transport_file', '/etc/postfix/transport.sandbox'); $routes = SandboxRoute::where('is_active', true) ->orderBy('type') ->orderBy('target') ->get(); $lines = []; $hasGlobal = false; foreach ($routes as $route) { if ($route->type === 'global') { $hasGlobal = true; } elseif ($route->type === 'domain') { $lines[] = $route->target . ' sandbox:'; } elseif ($route->type === 'address') { $lines[] = $route->target . ' sandbox:'; } } // Global catch-all goes at the end if ($hasGlobal) { $lines[] = '* sandbox:'; } $content = implode("\n", $lines); if ($lines) { $content .= "\n"; } try { file_put_contents($file, $content); } catch (\Throwable $e) { return ['ok' => false, 'message' => 'Fehler beim Schreiben: ' . $e->getMessage()]; } $out = []; $code = 0; exec('postmap ' . escapeshellarg($file) . ' 2>&1', $out, $code); if ($code !== 0) { return ['ok' => false, 'message' => 'postmap Fehler: ' . implode(' ', $out)]; } return ['ok' => true, 'message' => 'Transport-Datei synchronisiert (' . count($lines) . ' Einträge).']; } }