From 5711546f78a69d69675bab2cbde13990fbb97e3b Mon Sep 17 00:00:00 2001 From: Pinga <121483313+getpinga@users.noreply.github.com> Date: Wed, 12 Feb 2025 01:02:47 +0200 Subject: [PATCH] Added ability to send registrar notifications, fixed #166 Also fixed msg_producer (again) --- automation/msg_producer.php | 32 +++-- cp/app/Controllers/RegistrarsController.php | 125 ++++++++++++++++++ .../admin/registrars/notifyRegistrars.twig | 93 +++++++++++++ cp/resources/views/layouts/app.twig | 10 +- cp/routes/web.php | 1 + 5 files changed, 244 insertions(+), 17 deletions(-) create mode 100644 cp/resources/views/admin/registrars/notifyRegistrars.twig diff --git a/automation/msg_producer.php b/automation/msg_producer.php index 0b98a3d..b489a98 100644 --- a/automation/msg_producer.php +++ b/automation/msg_producer.php @@ -37,13 +37,13 @@ class RedisPool { */ public function initialize(int $size = 10): void { for ($i = 0; $i < $size; $i++) { - // Create a coroutine for each connection. - Swoole\Coroutine\run(function () { + go(function () { $redis = new Redis(); if (!$redis->connect($this->host, $this->port)) { throw new Exception("Failed to connect to Redis at {$this->host}:{$this->port}"); } $this->pool->push($redis); + echo "Added Redis connection to pool\n"; // Debugging log }); } } @@ -52,7 +52,7 @@ class RedisPool { * Get a Redis connection from the pool. * Optionally, you can add a timeout to avoid indefinite blocking. */ - public function get(float $timeout = 1.0): Redis { + public function get(float $timeout = 2.0): Redis { $conn = $this->pool->pop($timeout); if (!$conn) { throw new Exception("No available Redis connection in pool"); @@ -63,13 +63,13 @@ class RedisPool { /** * Return a Redis connection back to the pool. */ - public function put(Redis $redis): void { - $this->pool->push($redis); + public function put(?Redis $redis): void { + if ($redis && $redis->isConnected()) { + $this->pool->push($redis); + } } -} -// Global RedisPool instance -$redisPool = new RedisPool('127.0.0.1', 6379, 10); +} // Create the Swoole HTTP server $server = new Server("127.0.0.1", 8250); @@ -80,24 +80,28 @@ $server->set([ 'log_file' => '/var/log/namingo/msg_producer.log', 'log_level' => SWOOLE_LOG_INFO, 'worker_num' => swoole_cpu_num() * 2, - 'pid_file' => '/var/run/msg_producer.pid' + 'pid_file' => '/var/run/msg_producer.pid', + 'enable_coroutine' => true ]); /** * Instead of initializing the Redis pool in the "start" event (which runs in the master process), * we initialize it in the "workerStart" event so that it runs in a coroutine-enabled worker process. */ -$server->on("workerStart", function () use ($redisPool, $logger) { +$server->on("workerStart", function ($server, $workerId) use (&$logger) { try { - $redisPool->initialize(10); - $logger->info("Redis pool initialized in worker process"); + $server->redisPool = new RedisPool('127.0.0.1', 6379, 10); // Store in server object + $server->redisPool->initialize(10); + $logger->info("Redis pool initialized in worker process {$workerId}"); } catch (Exception $e) { - $logger->error("Failed to initialize Redis pool: " . $e->getMessage()); + $logger->error("Worker {$workerId}: Failed to initialize Redis pool - " . $e->getMessage()); } }); // Handle incoming requests -$server->on("request", function (Request $request, Response $response) use ($redisPool, $logger) { +$server->on("request", function (Request $request, Response $response) use ($server, $logger) { + $redisPool = $server->redisPool ?? null; + if (!$redisPool) { $logger->error("Redis pool not initialized"); $response->status(500); diff --git a/cp/app/Controllers/RegistrarsController.php b/cp/app/Controllers/RegistrarsController.php index 4e2c40c..20c60f3 100644 --- a/cp/app/Controllers/RegistrarsController.php +++ b/cp/app/Controllers/RegistrarsController.php @@ -1467,4 +1467,129 @@ class RegistrarsController extends Controller Auth::leaveImpersonation(); } + public function notifyRegistrars(Request $request, Response $response) + { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + if ($request->getMethod() === 'POST') { + // Retrieve POST data + $data = $request->getParsedBody(); + $db = $this->container->get('db'); + + // Ensure registrars array exists and is not empty + if (!isset($data['registrars']) || empty($data['registrars'])) { + $this->container->get('flash')->addMessage('error', 'No registrars selected'); + return $response->withHeader('Location', '/registrars/notify')->withStatus(302); + } + + $registrars = $data['registrars']; // Array of registrar IDs + $subject = isset($data['subject']) && is_string($data['subject']) ? trim($data['subject']) : 'No subject'; + $message = isset($data['message']) && is_string($data['message']) ? trim($data['message']) : 'No message'; + + // Enforce length limits + $subjectMaxLength = 255; + $messageMaxLength = 5000; + + if (strlen($subject) > $subjectMaxLength) { + $subject = substr($subject, 0, $subjectMaxLength); + } + + if (strlen($message) > $messageMaxLength) { + $message = substr($message, 0, $messageMaxLength); + } + + // Escape HTML to prevent XSS if displaying in HTML later + $subject = htmlspecialchars($subject, ENT_QUOTES, 'UTF-8'); + $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); + + $url = 'http://127.0.0.1:8250'; + + // Retrieve registrar names from database + $registrarNames = []; + $registrarEmails = []; + $placeholders = implode(',', array_fill(0, count($registrars), '?')); + $rows = $db->select( + "SELECT id, name, email FROM registrar WHERE id IN ($placeholders)", + $registrars + ); + + foreach ($rows as $row) { + $registrarNames[$row['id']] = $row['name']; + $registrarEmails[$row['id']] = $row['email']; + } + + $notifiedRegistrars = []; + + foreach ($registrars as $registrarId) { + $data = [ + 'type' => 'sendmail', + 'toEmail' => $registrarEmails[$registrarId] ?? null, + 'subject' => $subject, + 'body' => $message + ]; + + $jsonData = json_encode($data); + + $options = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_POSTFIELDS => $jsonData, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'Content-Length: ' . strlen($jsonData) + ], + ]; + + $curl = curl_init($url); + curl_setopt_array($curl, $options); + $curlResponse = curl_exec($curl); + + if ($curlResponse === false) { + $this->container->get('flash')->addMessage('error', 'cURL Error: ' . curl_error($curl)); + curl_close($curl); + return $response->withHeader('Location', '/registrars/notify')->withStatus(302); + } else { + $notifiedRegistrars[] = $registrarNames[$registrarId] ?? "Registrar ID: $registrarId"; + } + + curl_close($curl); + } + + // Create success message with registrar names + $successMessage = "Notification sent to: " . implode(', ', $notifiedRegistrars); + $this->container->get('flash')->addMessage('success', $successMessage); + + return $response->withHeader('Location', '/registrars/notify')->withStatus(302); + } else { + // Prepare the view + $db = $this->container->get('db'); + $uri = $request->getUri()->getPath(); + + // Get all registrars + $registrars = $db->select("SELECT id, clid, name, email, abuse_email FROM registrar"); + + // Fetch last login for each registrar + foreach ($registrars as &$registrar) { + // Get the latest user_id associated with the registrar + $user_id = $db->selectValue("SELECT user_id FROM registrar_users WHERE registrar_id = ? ORDER BY user_id DESC LIMIT 1", [$registrar['id']]); + + // Fetch last login time if user_id exists + if ($user_id) { + $last_login = $db->selectValue("SELECT last_login FROM users WHERE id = ?", [$user_id]); + $registrar['last_login'] = ($last_login && is_numeric($last_login)) ? date('Y-m-d H:i:s', $last_login) : null; + } else { + $registrar['last_login'] = null; + } + } + + // Default view for GET requests or if POST data is not set + return view($response,'admin/registrars/notifyRegistrars.twig', [ + 'registrars' => $registrars, + 'currentUri' => $uri, + ]); + } + } + } \ No newline at end of file diff --git a/cp/resources/views/admin/registrars/notifyRegistrars.twig b/cp/resources/views/admin/registrars/notifyRegistrars.twig new file mode 100644 index 0000000..a792362 --- /dev/null +++ b/cp/resources/views/admin/registrars/notifyRegistrars.twig @@ -0,0 +1,93 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Notify Registrars') }}{% endblock %} + +{% block content %} +