$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"; } $tmp = tempnam(sys_get_temp_dir(), 'mw_sandbox_'); try { file_put_contents($tmp, $content); } catch (\Throwable $e) { @unlink($tmp); return ['ok' => false, 'message' => 'Fehler beim Schreiben: ' . $e->getMessage()]; } $out = []; $code = 0; exec('sudo -n /usr/local/sbin/mailwolt-sandbox-sync ' . escapeshellarg($tmp) . ' 2>&1', $out, $code); @unlink($tmp); if ($code !== 0) { return ['ok' => false, 'message' => 'Sync-Fehler: ' . implode(' ', $out)]; } return ['ok' => true, 'message' => 'Transport-Datei synchronisiert (' . count($lines) . ' Einträge).']; } }