diff --git a/plugins/check_port/action.php b/plugins/check_port/action.php index 8787f1a85..9019d8705 100644 --- a/plugins/check_port/action.php +++ b/plugins/check_port/action.php @@ -42,9 +42,21 @@ function get_public_ip($version, $timeout) { if (filter_var($ip, FILTER_VALIDATE_IP, $flag)) { return $ip; // Return the valid IP } - error_log("check_port plugin: {$url} returned invalid IP: " . $ip); + // If looking for IPv6 and received a valid IPv4, it indicates no IPv6 is available (e.g. NAT64/DNS64). + // If looking for IPv4 and received a valid IPv6, it indicates no IPv4 is available. + // These are not error conditions, so we can return null without logging. + $isIPv6RequestWithIPv4Response = ($version == '6' && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)); + $isIPv4RequestWithIPv6Response = ($version == '4' && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); + + if (!$isIPv6RequestWithIPv4Response && !$isIPv4RequestWithIPv6Response) { + error_log("check_port plugin: {$url} returned invalid IP: " . $ip); + } } else { - error_log("check_port plugin: Failed to fetch from {$url}. Status: {$snoopy->status}, Error: {$snoopy->error}"); + // cURL error 7 ("Failed to connect to host") indicates a lack of connectivity for the requested IP version. + // This is not a plugin error, so we suppress the log for both IPv4 and IPv6 checks. + if ($snoopy->status != 7) { + error_log("check_port plugin: Failed to fetch from {$url}. Status: {$snoopy->status}, Error: {$snoopy->error}"); + } } return null; // Return null on failure } @@ -72,6 +84,7 @@ function check_port_yougetsignal($ip, $port, $timeout) { if (stripos($client->results, "is open") !== false) return 2; // Port is open error_log("check_port: yougetsignal response indicators not found for IP {$ip}. Response: " . substr($client->results, 0, 500)); } else { + // This log will be triggered if the service is offline or unreachable. error_log("check_port: Failed fetch from yougetsignal for IP {$ip}. Status: {$client->status}, Error: {$client->error}"); } return 0; @@ -122,6 +135,7 @@ function check_port_portchecker($ip, $port, $timeout) { if (stripos($client->results, 'is open') !== false) return 2; // Port is open error_log("check_port: portchecker response indicators not found for IP {$ip}. Response: " . substr($client->results, 0, 500)); } else { + // This log will be triggered if the service is offline or unreachable. error_log("check_port: Failed fetch from portchecker endpoint for IP {$ip}. Status: {$client->status}, Error: {$client->error}"); } return 0; // Status is unknown @@ -154,6 +168,10 @@ function get_and_check_ip($ip_version, $use_website, $rtorrent_ip, $rtorrent_por // Call the appropriate checking function based on the selected service if ($use_website == "yougetsignal") { $status = check_port_yougetsignal($ip_to_check, $rtorrent_port, $timeout); + // If yougetsignal fails, fall back to portchecker for IPv4 + if ($status === 0 && $ip_version == '4') { + $status = check_port_portchecker($ip_to_check, $rtorrent_port, $timeout); + } } elseif ($use_website == "portchecker") { $status = check_port_portchecker($ip_to_check, $rtorrent_port, $timeout); } @@ -174,6 +192,10 @@ function get_and_check_ip($ip_version, $use_website, $rtorrent_ip, $rtorrent_por "ipv6" => "-", "ipv6_port" => (int)$port, "ipv6_status" => 0, ]; +// Add enabled flags to the response +$response['use_ipv4'] = ($currentUseWebsiteIPv4 !== false); +$response['use_ipv6'] = ($currentUseWebsiteIPv6 !== false); + // Perform the IPv4 check if it's enabled in conf.php if ($currentUseWebsiteIPv4 !== false) { $ipv4_result = get_and_check_ip('4', $currentUseWebsiteIPv4, $ip_glob, $port, $currentCheckPortTimeout); diff --git a/plugins/check_port/check_port.css b/plugins/check_port/check_port.css index 16949b4a2..a3def8810 100644 --- a/plugins/check_port/check_port.css +++ b/plugins/check_port/check_port.css @@ -1,35 +1,67 @@ -/* +/** * check_port Plugin Stylesheet * * Defines the icons and layout for the port status indicator in the status bar. */ -/* Define CSS variables for the different status icons. */ -/* Using data URIs for the icons avoids extra HTTP requests. */ +/* 1. Icon Definitions */ #StatusBar { - /* Icon for when the port status is unknown or is being checked. */ --pane-port-unknown-icon: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAA7ElEQVR4AYyRtUEAQRBFzw2HiDbogYzLKIROKIFKSJEYpwDc3Yd9wUfOgz9r88bWM7MmxetLniEzKypvs6xsEvTnIQP4+ri3j4cDwV5FGSZymnRiHzilAr/eTtrAAjP152IcR/t6sZfjZUNf75eNMKYgE5kpGSect1fmUFu5gTY+mZXt8XBRIPu/WQv5Y3KnGS54pC+B6G5nwd5vVwUyUTahMgqirxrIuVoyZpxIXBIZJ+lsswS0h/2SgBWwkg2nWsbdeaSsJrBQNiAJQNkEuoACc0+fzeSGCF+Y74E1koNxJXIuEEawEeKwdA0A1zpJYUqqdYUAAAAASUVORK5CYII=); - /* Icon for when the port is confirmed to be closed (error state). */ --pane-port-error-icon: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAVFBMVEX////////////////////////////////////////////////ORirORirdcWDlTzzpgW/qXEHtY0XtjnvxbEjyppbznIf0dk71f1j5tqH72dP///8Q8E8oAAAADXRSTlMAAgUGBwgKDRMUFRnTtFxmSQAAAHpJREFUCB0FgAEOwiAMRV8pq5LI/Q+6xDlG+43FAiZw7zIZMBlOXpzg8HkNGXrTH5z5irLetCOP1SAKIHkG+BwyspOl8t5xbYASOA0EJUQJOplC2wtUtPMKiaMBxzq7c40vPyBucNQyqsriVjYDJuHkop5mJmACe2f1P5ZxRkN1OzPZAAAAAElFTkSuQmCC); - /* Icon for when the port is confirmed to be open (success state). */ --pane-port-success-icon: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAA/ElEQVR4AWL4//8/DLMDMRuIrdMt9h8ZAwEXSBxKMwExC4ijDMSsIBqmcPGVKSgYJg7VzAGiGWAmgSR6T1f/X393GZguOZwIwjAxEBumWRSmkQ1ZU8rhEGwYXTMLTCOKpqTdPoC6ycIGgRgAgFgUl31YhCHZBB0Bd3e9Tw6nydWvXgElUsVcyD0x4z+J0Fu36QOBHIiO6JKao+qbdDofHZhJniIFRJjtp87wJpXKRQZ7F10CDcjLw/xKOF/OSvC+VI7W5SIqs1wl8XBwvEM6K791FpZImy8JMX2n8CK7bCD/JTkjb480SQPYGch/PLkYkBFIUPnjkfsJwn6EG7GmEErgVbTJAAAAAElFTkSuQmCC); } -/* General styling for the icon element within the port status pane. */ -#port-pane .icon { - background-position: center; /* Center the icon within its container. */ - background-repeat: no-repeat; /* Prevent the icon from tiling. */ - width: 14px; /* Explicitly set width to match the icon image. */ - height: 14px; /* Explicitly set height to match the icon image. */ + +/* 2. Port Status Component Layout */ +#port-pane.port-status-container { + display: flex; + align-items: center; + gap: 3px; + padding: 0 4px; /* Symmetrical padding */ + box-sizing: border-box; + /* Crucial: Prevents content from expanding the container and breaking the layout */ + overflow: hidden; +} + +/* 3. Group for each IP version (icon + text) */ +.port-group { + display: flex; + align-items: center; + gap: 3px; /* Space between icon and text inside the group */ + overflow: hidden; /* Prevent internal content from breaking out */ + min-width: 0; /* Allow the group to shrink */ +} + +/* 4. IP Text Behavior */ +.port-status-container .port-ip-text-segment { + /* Prevents long text from wrapping and breaking the flexbox layout */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + /* This is the key fix: it allows the text element to shrink below its content size */ + min-width: 0; } -/* Assigns the 'unknown' status icon. */ -#port-pane .pstatus0 { + +/* 5. Icon Base and Status Styles */ +.port-status-container .icon { + background-position: center; + background-repeat: no-repeat; + width: 14px; + height: 14px; + /* Required to prevent the icon from shrinking in the flex container */ + flex-shrink: 0; +} + +/* Port status is unknown */ +.port-status-container .pstatus0 { background-image: var(--pane-port-unknown-icon); } -/* Assigns the 'error' (closed) status icon. */ -#port-pane .pstatus1 { + +/* Port is closed (error state) */ +.port-status-container .pstatus1 { background-image: var(--pane-port-error-icon); } -/* Assigns the 'success' (open) status icon. */ -#port-pane .pstatus2 { + +/* Port is open (success state) */ +.port-status-container .pstatus2 { background-image: var(--pane-port-success-icon); } diff --git a/plugins/check_port/init.js b/plugins/check_port/init.js index a24488c5e..173551a59 100644 --- a/plugins/check_port/init.js +++ b/plugins/check_port/init.js @@ -10,27 +10,14 @@ const a = {}; * @param {boolean} isUpdate - If true, indicates a manual refresh, adding "..." to the title */ plugin.resetStatus = function(isUpdate) { - // Reset icons to the "unknown" state (pstatus0) - a.iconIPv4.removeClass().addClass("icon port-icon-ipv4 pstatus0"); - a.iconIPv6.removeClass().addClass("icon port-icon-ipv6 pstatus0"); - // Hide IP address text and the separator - a.textIPv4.text("").hide(); - a.separator.text("").hide(); - a.textIPv6.text("").hide(); - // Set a tooltip to indicate that a check is in progress let title = theUILang.checkingPort || "Checking port status..."; if (isUpdate) { title += "..."; // Append ellipsis for manual updates } - a.pane.prop("title", title); -}; - -// Initial check when the plugin is first loaded -plugin.init = function() { - plugin.resetStatus(false); - // Request the initial port status from the backend - theWebUI.request("?action=initportcheck", [plugin.getPortStatus, plugin]); + if (a.pane) { + a.pane.prop("title", title); + } }; // Function to manually trigger an update of the port status @@ -40,69 +27,57 @@ plugin.update = function() { theWebUI.request("?action=updateportcheck", [plugin.getPortStatus, plugin]); }; -/** - * Updates the UI for a specific IP protocol (IPv4 or IPv6) based on data from the backend - * @param {object} data - The response data containing status for both protocols - * @param {string} proto - The protocol to update, either "ipv4" or "ipv6" - * @param {function} getStatusText - A function to retrieve the localized status string - * @returns {string} The formatted title line for this protocol's status - */ -function updateProtocolStatus(data, proto, getStatusText) { - const status = parseInt(data[proto + '_status']); - const address = data[proto]; - const port = data[proto + '_port']; - const isAvailable = address && address !== "-"; // Check if an IP address was returned - - // Select the correct UI elements for the given protocol - const icon = (proto === 'ipv4') ? a.iconIPv4 : a.iconIPv6; - const textEl = (proto === 'ipv4') ? a.textIPv4 : a.textIPv6; - - icon.removeClass("pstatus0 pstatus1 pstatus2").addClass("pstatus" + status); - - let displayText = ""; - let titleText = ""; - - if (isAvailable) { - // Format display text as IP:PORT, with brackets for IPv6 - displayText = (proto === 'ipv6') ? `[${address}]:${port}` : `${address}:${port}`; - textEl.text(displayText).show(); - // Create a detailed title for the tooltip - titleText = `${proto.toUpperCase()}: ${displayText} (${getStatusText(status)})`; - } else { - // If IP is not available, hide the text element - textEl.text("").hide(); - titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")} (${getStatusText(status)})`; - } - return titleText; // Return the generated title string -} - /** * Main callback to process the port status response from the backend and update the UI * @param {object} d - The JSON object received from the backend response */ plugin.getPortStatus = function(d) { - // Helper function to get the localized text for a status code + // Always clear the pane first to rebuild the UI dynamically + a.pane.empty(); + const getStatusText = (statusCode) => theUILang.portStatus[statusCode] || theUILang.portStatus[0] || "Unknown"; + const isIPv4Available = d.ipv4 && d.ipv4 !== "-"; + const isIPv6Available = d.ipv6 && d.ipv6 !== "-"; + const titleLines = []; + + // Conditionally create and append the IPv4 group only if the IP is available + if (isIPv4Available) { + const status = parseInt(d.ipv4_status); + const displayText = `${d.ipv4}:${d.ipv4_port}`; + const ipv4Group = $("