From bf5ba8113fba75e3ef962243449ee5a8feb2d468 Mon Sep 17 00:00:00 2001 From: cantalupo555 Date: Wed, 9 Jul 2025 22:54:52 -0300 Subject: [PATCH 1/5] feat(check_port): Conditionally render protocol status display The plugin now offers two ways to conditionally hide the status display for a specific IP protocol (IPv4 or IPv6), providing a cleaner and more adaptive interface. The first approach is manual hiding via configuration. Users can set `$useWebsiteIPv4` or `$useWebsiteIPv6` to `false` in `conf.php` to completely hide the corresponding protocol status from the UI. The second approach is automatic hiding for unavailable protocols. If a protocol is enabled but detected as unavailable on the server, its status is now automatically hidden to prevent showing a persistent "unknown" state. --- plugins/check_port/action.php | 4 ++++ plugins/check_port/init.js | 36 +++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/plugins/check_port/action.php b/plugins/check_port/action.php index 8787f1a85..cf6e5845e 100644 --- a/plugins/check_port/action.php +++ b/plugins/check_port/action.php @@ -174,6 +174,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/init.js b/plugins/check_port/init.js index a24488c5e..e0ff5a734 100644 --- a/plugins/check_port/init.js +++ b/plugins/check_port/init.js @@ -48,28 +48,40 @@ plugin.update = function() { * @returns {string} The formatted title line for this protocol's status */ function updateProtocolStatus(data, proto, getStatusText) { + const isEnabled = data['use_' + proto]; + const icon = (proto === 'ipv4') ? a.iconIPv4 : a.iconIPv6; + const textEl = (proto === 'ipv4') ? a.textIPv4 : a.textIPv6; + + // Handle the case where the protocol is not enabled + if (!isEnabled) { + icon.hide(); + textEl.hide(); + return ""; // Return an empty title line if not enabled + } + + // Proceed with showing the icon if the protocol is enabled + icon.show(); + + // Extract status, address, and port data for the protocol 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; - + // Update the icon class to reflect the current status (0: unknown, 1: closed, 2: open) 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}`; + const 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 + // If IP is not available, hide the icon and the text element + icon.hide(); textEl.text("").hide(); titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")} (${getStatusText(status)})`; } @@ -88,14 +100,10 @@ plugin.getPortStatus = function(d) { const titleLines = [ updateProtocolStatus(d, 'ipv4', getStatusText), updateProtocolStatus(d, 'ipv6', getStatusText) - ]; - - // Check if both IPv4 and IPv6 addresses are available - const ipv4Available = d.ipv4 && d.ipv4 !== "-"; - const ipv6Available = d.ipv6 && d.ipv6 !== "-"; + ].filter(line => line); // Filter out empty strings for disabled protocols - // Show a separator only if both protocols have an IP address to display - if (ipv4Available && ipv6Available) { + // Show a separator only if both protocol icons are visible + if (a.iconIPv4.is(":visible") && a.iconIPv6.is(":visible")) { a.separator.text("|").show(); } else { a.separator.text("").hide(); From 24f8ebfaf0ad1b712bbc60ba5aae93ddb2b3b914 Mon Sep 17 00:00:00 2001 From: cantalupo555 Date: Sat, 12 Jul 2025 14:04:07 -0300 Subject: [PATCH 2/5] refactor(check_port): Improve component layout using modern CSS Refactors the port status component to use a modern Flexbox layout instead of the previous inline-block and dynamic margin approach. This change provides a more robust and maintainable method for handling vertical alignment and consistent spacing between elements using the `gap` property. As a result, the corresponding JavaScript was simplified by removing the now-unnecessary logic for manipulating margins, leading to cleaner and more declarative code. --- plugins/check_port/check_port.css | 56 +++++++++++++++++++++---------- plugins/check_port/init.js | 30 ++++++++--------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/plugins/check_port/check_port.css b/plugins/check_port/check_port.css index 16949b4a2..2fa823177 100644 --- a/plugins/check_port/check_port.css +++ b/plugins/check_port/check_port.css @@ -1,35 +1,55 @@ -/* +/** * 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. 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; +} + +/* 4. 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; } -/* Assigns the 'unknown' status icon. */ -#port-pane .pstatus0 { + +.port-status-container .pstatus0 { background-image: var(--pane-port-unknown-icon); } -/* Assigns the 'error' (closed) status icon. */ -#port-pane .pstatus1 { + +.port-status-container .pstatus1 { background-image: var(--pane-port-error-icon); } -/* Assigns the 'success' (open) status icon. */ -#port-pane .pstatus2 { + +.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 e0ff5a734..bc6d9ae42 100644 --- a/plugins/check_port/init.js +++ b/plugins/check_port/init.js @@ -10,9 +10,10 @@ 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"); + // Reset icons to the "unknown" state (pstatus0) and ensure they are visible for the loading state + a.iconIPv4.removeClass().addClass("icon pstatus0").show(); + a.iconIPv6.removeClass().addClass("icon pstatus0").show(); + // Hide IP address text and the separator a.textIPv4.text("").hide(); a.separator.text("").hide(); @@ -52,40 +53,38 @@ function updateProtocolStatus(data, proto, getStatusText) { const icon = (proto === 'ipv4') ? a.iconIPv4 : a.iconIPv6; const textEl = (proto === 'ipv4') ? a.textIPv4 : a.textIPv6; - // Handle the case where the protocol is not enabled + // Handle the case where the protocol is not enabled in conf.php if (!isEnabled) { icon.hide(); textEl.hide(); return ""; // Return an empty title line if not enabled } - // Proceed with showing the icon if the protocol is enabled - icon.show(); - - // Extract status, address, and port data for the protocol 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 - // Update the icon class to reflect the current status (0: unknown, 1: closed, 2: open) + // Update the icon class to reflect the current status icon.removeClass("pstatus0 pstatus1 pstatus2").addClass("pstatus" + status); let titleText = ""; if (isAvailable) { + icon.show(); // Format display text as IP:PORT, with brackets for IPv6 const 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 icon and the text element + // If IP is not available on the server, hide the icon and the text element icon.hide(); - textEl.text("").hide(); - titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")} (${getStatusText(status)})`; + textEl.hide(); + // Still provide a title for debugging or information + titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")}`; } - return titleText; // Return the generated title string + return titleText; } /** @@ -100,9 +99,10 @@ plugin.getPortStatus = function(d) { const titleLines = [ updateProtocolStatus(d, 'ipv4', getStatusText), updateProtocolStatus(d, 'ipv6', getStatusText) - ].filter(line => line); // Filter out empty strings for disabled protocols + ].filter(line => line); // Filter out empty strings for disabled/unavailable protocols // Show a separator only if both protocol icons are visible + // The CSS 'gap' property will handle the spacing automatically if (a.iconIPv4.is(":visible") && a.iconIPv6.is(":visible")) { a.separator.text("|").show(); } else { @@ -144,7 +144,7 @@ plugin.onLangLoaded = function() { const ipv4Icon = $("
").attr("id", "port-icon-ipv4").addClass("icon"); const ipv4Text = $("").attr("id", "port-ip-text-ipv4").addClass("d-none d-lg-block port-ip-text-segment"); - const separator = $("").attr("id", "port-ip-separator").addClass("d-none d-lg-block").css({"margin-left": "3px", "margin-right": "3px"}); + const separator = $("").attr("id", "port-ip-separator").addClass("d-none d-lg-block"); const ipv6Icon = $("
").attr("id", "port-icon-ipv6").addClass("icon"); const ipv6Text = $("").attr("id", "port-ip-text-ipv6").addClass("d-none d-lg-block port-ip-text-segment"); From ef72bd08e078a360387b47db217f7112b9d5a020 Mon Sep 17 00:00:00 2001 From: cantalupo555 Date: Sat, 12 Jul 2025 14:34:11 -0300 Subject: [PATCH 3/5] fix(check_port): Fix layout bug by rendering UI dynamically Previously, if both IP versions were enabled in the configuration, the plugin would render HTML elements for both, even if the system only had connectivity for one. The unused elements were then hidden with CSS, which caused a layout bug where the Flexbox gap property created unwanted spacing. This commit refactors init.js to build the UI completely dynamically on each update. The getPortStatus function now checks for the actual availability of each IP address at runtime before creating and appending its corresponding DOM elements (the .port-group). This ensures that only the necessary HTML is rendered, resulting in a cleaner DOM and definitively fixing the layout spacing issue regardless of the user's network connectivity. --- plugins/check_port/check_port.css | 16 +++- plugins/check_port/init.js | 153 ++++++++++-------------------- 2 files changed, 64 insertions(+), 105 deletions(-) diff --git a/plugins/check_port/check_port.css b/plugins/check_port/check_port.css index 2fa823177..a3def8810 100644 --- a/plugins/check_port/check_port.css +++ b/plugins/check_port/check_port.css @@ -22,7 +22,16 @@ overflow: hidden; } -/* 3. IP Text Behavior */ +/* 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; @@ -32,7 +41,7 @@ min-width: 0; } -/* 4. Icon Base and Status Styles */ +/* 5. Icon Base and Status Styles */ .port-status-container .icon { background-position: center; background-repeat: no-repeat; @@ -42,14 +51,17 @@ flex-shrink: 0; } +/* Port status is unknown */ .port-status-container .pstatus0 { background-image: var(--pane-port-unknown-icon); } +/* Port is closed (error state) */ .port-status-container .pstatus1 { background-image: var(--pane-port-error-icon); } +/* 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 bc6d9ae42..173551a59 100644 --- a/plugins/check_port/init.js +++ b/plugins/check_port/init.js @@ -10,28 +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) and ensure they are visible for the loading state - a.iconIPv4.removeClass().addClass("icon pstatus0").show(); - a.iconIPv6.removeClass().addClass("icon pstatus0").show(); - - // 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 @@ -41,76 +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 isEnabled = data['use_' + proto]; - const icon = (proto === 'ipv4') ? a.iconIPv4 : a.iconIPv6; - const textEl = (proto === 'ipv4') ? a.textIPv4 : a.textIPv6; - - // Handle the case where the protocol is not enabled in conf.php - if (!isEnabled) { - icon.hide(); - textEl.hide(); - return ""; // Return an empty title line if not enabled - } - - 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 - - // Update the icon class to reflect the current status - icon.removeClass("pstatus0 pstatus1 pstatus2").addClass("pstatus" + status); - - let titleText = ""; - - if (isAvailable) { - icon.show(); - // Format display text as IP:PORT, with brackets for IPv6 - const 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 on the server, hide the icon and the text element - icon.hide(); - textEl.hide(); - // Still provide a title for debugging or information - titleText = `${proto.toUpperCase()}: ${(theUILang.notAvailable || "N/A")}`; - } - return titleText; -} - /** * 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 = $("
").attr("id", "port-group-ipv4").addClass("port-group"); + ipv4Group.append($("
").attr("id", "port-icon-ipv4").addClass("icon pstatus" + status)); + ipv4Group.append($("").attr("id", "port-ip-text-ipv4").addClass("d-none d-lg-block port-ip-text-segment").text(displayText)); + a.pane.append(ipv4Group); + titleLines.push(`IPV4: ${displayText} (${getStatusText(status)})`); + } else if (d.use_ipv4) { + titleLines.push(`IPV4: ${(theUILang.notAvailable || "N/A")}`); + } + + // Conditionally create and append the separator + if (isIPv4Available && isIPv6Available) { + a.pane.append($("").attr("id", "port-ip-separator").addClass("d-none d-lg-block").text("|")); + } - // Update the status for both IPv4 and IPv6 and collect their title lines - const titleLines = [ - updateProtocolStatus(d, 'ipv4', getStatusText), - updateProtocolStatus(d, 'ipv6', getStatusText) - ].filter(line => line); // Filter out empty strings for disabled/unavailable protocols - - // Show a separator only if both protocol icons are visible - // The CSS 'gap' property will handle the spacing automatically - if (a.iconIPv4.is(":visible") && a.iconIPv6.is(":visible")) { - a.separator.text("|").show(); - } else { - a.separator.text("").hide(); + // Conditionally create and append the IPv6 group only if the IP is available + if (isIPv6Available) { + const status = parseInt(d.ipv6_status); + const displayText = `[${d.ipv6}]:${d.ipv6_port}`; + const ipv6Group = $("
").attr("id", "port-group-ipv6").addClass("port-group"); + ipv6Group.append($("
").attr("id", "port-icon-ipv6").addClass("icon pstatus" + status)); + ipv6Group.append($("").attr("id", "port-ip-text-ipv6").addClass("d-none d-lg-block port-ip-text-segment").text(displayText)); + a.pane.append(ipv6Group); + titleLines.push(`IPV6: ${displayText} (${getStatusText(status)})`); + } else if (d.use_ipv6) { + titleLines.push(`IPV6: ${(theUILang.notAvailable || "N/A")}`); } - // Set the combined tooltip for the entire status pane + // Set the final combined tooltip for the entire status pane a.pane.prop("title", titleLines.join(" | ")); + + // Re-attach the context menu handler since we cleared the pane + if (plugin.canChangeMenu()) { + a.pane.off("mousedown", plugin.createPortMenu).on("mousedown", plugin.createPortMenu); + } }; // Defines the AJAX request for the initial port check @@ -139,36 +106,16 @@ plugin.createPortMenu = function(e) { }; plugin.onLangLoaded = function() { - // Create status bar elements in a more readable way - const container = $("
").addClass("port-status-container"); - - const ipv4Icon = $("
").attr("id", "port-icon-ipv4").addClass("icon"); - const ipv4Text = $("").attr("id", "port-ip-text-ipv4").addClass("d-none d-lg-block port-ip-text-segment"); - const separator = $("").attr("id", "port-ip-separator").addClass("d-none d-lg-block"); - const ipv6Icon = $("
").attr("id", "port-icon-ipv6").addClass("icon"); - const ipv6Text = $("").attr("id", "port-ip-text-ipv6").addClass("d-none d-lg-block port-ip-text-segment"); + // Create a temporary loading state immediately + const container = $("
").addClass("port-status-container") + .append($("
").addClass("icon pstatus0")); // Add a single "unknown" icon as a placeholder - // Assemble the elements into the container - container.append(ipv4Icon, ipv4Text, separator, ipv6Icon, ipv6Text); - - // Add the newly created pane to the ruTorrent status bar plugin.addPaneToStatusbar("port-pane", container, -1, true); - - // Now that the pane is in the DOM, cache all the jQuery elements for future use a.pane = $("#port-pane"); - a.iconIPv4 = $("#port-icon-ipv4"); - a.textIPv4 = $("#port-ip-text-ipv4"); - a.separator = $("#port-ip-separator"); - a.iconIPv6 = $("#port-icon-ipv6"); - a.textIPv6 = $("#port-ip-text-ipv6"); - - // If the user has permissions, attach the right-click context menu - if (plugin.canChangeMenu()) { - a.pane.on("mousedown", plugin.createPortMenu); - } + a.pane.prop("title", theUILang.checkingPort || "Checking port status..."); - // Trigger the initial port check - plugin.init(); + // Trigger the initial port check to get the configuration and build the final UI + theWebUI.request("?action=initportcheck", [plugin.getPortStatus, plugin]); }; // This function is called when the plugin is removed/unloaded From bcb97df146bb81b28fd96d547a8d4b5cd1fea1f2 Mon Sep 17 00:00:00 2001 From: cantalupo555 Date: Tue, 15 Jul 2025 00:42:39 -0300 Subject: [PATCH 4/5] fix(check_port): Improve IP detection and error logging This commit enhances the check_port plugin to more intelligently handle different network configurations and to prevent unnecessary error logging. Improved IP detection for single-stack environments: The plugin now suppresses error logs when an IP check fails due to the system lacking either IPv4 or IPv6 connectivity. cURL error 7 ("Failed to connect to host") is ignored, as it is an expected outcome in these scenarios. The "invalid IP" error is also suppressed when a service like ipify returns an IP of a different version than requested (e.g., an IPv4 on an IPv6 query), which is common in NAT64/DNS64 networks. --- plugins/check_port/action.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/check_port/action.php b/plugins/check_port/action.php index cf6e5845e..bd8f3db60 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 From 3eae10102f7a82e14fb6b4cca6241c4ddd7fc53f Mon Sep 17 00:00:00 2001 From: cantalupo555 Date: Tue, 15 Jul 2025 12:51:34 -0300 Subject: [PATCH 5/5] feat(check_port): Add fallback mechanism for IPv4 port check The yougetsignal service, the default for IPv4, has become unstable and is frequently offline. This caused the port check functionality to fail for users with the default configuration. This commit introduces a fallback mechanism to improve service reliability. Fallback Implementation: If the primary service (yougetsignal) fails an IPv4 check (returning an "unknown" status), the plugin will automatically try the check again using the portchecker service as an alternative. This is the first step towards a more robust implementation. Future improvements may include adding more service providers and implementing a similar fallback for the IP detection service (ipify.org). --- plugins/check_port/action.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/check_port/action.php b/plugins/check_port/action.php index bd8f3db60..9019d8705 100644 --- a/plugins/check_port/action.php +++ b/plugins/check_port/action.php @@ -168,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); }