From 22ba3b45eeaa5b8c9a31f5ce4919a6e8383f97bb Mon Sep 17 00:00:00 2001 From: "factory-droid[bot]" <138933559+factory-droid[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 00:05:11 +0000 Subject: [PATCH 1/3] Add Enhance platform monitoring - Created software/enhance.php to monitor Enhance platform releases - Scrapes release notes from enhance.com/support/release-notes.html - Parses HTML to extract version numbers, release dates, and latest markers - Supports 19+ version branches (12.6, 12.5, 12.4, etc.) - Filters out non-core versions (Appcd, WHMCS modules) - Detects latest version (currently 12.6.0) with announcement links - Handles patch versions correctly (keeps highest per branch) - Uses robust HTML parsing with proper error handling --- software/enhance.php | 191 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 software/enhance.php diff --git a/software/enhance.php b/software/enhance.php new file mode 100644 index 0000000..ffa11df --- /dev/null +++ b/software/enhance.php @@ -0,0 +1,191 @@ +uri); + return $data; + } + + function get_versions($data = '') + { + if (empty($data)) { + return array(); + } + + // Initialize versions array + $versions = array(); + $branch_versions = array(); + + // Use DOMDocument for proper HTML parsing + $dom = new DOMDocument(); + + // Suppress warnings for malformed HTML + libxml_use_internal_errors(true); + + // Load the HTML content + $dom->loadHTML($data); + + // Reset errors + libxml_clear_errors(); + + // Get all H1 elements (version headers) + $h1Elements = $dom->getElementsByTagName('h1'); + + foreach ($h1Elements as $h1) { + $version_text = trim($h1->textContent); + + // Skip non-core versions (Appcd, WHMCS module, etc.) + if (preg_match('/^(Appcd|WHMCS module|PHP packages)/i', $version_text)) { + continue; + } + + // Extract version number using regex + if (preg_match('/^(\d+\.\d+\.\d+)/', $version_text, $matches)) { + $version = $matches[1]; + } else { + // Skip entries that don't start with a version number + continue; + } + + // Extract version components + $version_parts = explode('.', $version); + if (count($version_parts) < 3) { + continue; + } + + // Determine branch (major.minor) + $branch = $version_parts[0] . '.' . $version_parts[1]; + + // Initialize version info + $version_info = array( + 'version' => $version, + 'release_date' => date("Y-m-d H:i:s"), // Default to current date/time + 'estimated' => true + ); + + // Find the release date (in H3 tag after the H1) + $node = $h1->nextSibling; + while ($node) { + if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'h3') { + $date_text = trim($node->textContent); + + // Try to parse the date (format like "27th May 2025") + if (($timestamp = strtotime($date_text)) !== false) { + $version_info['release_date'] = date("Y-m-d H:i:s", $timestamp); + $version_info['estimated'] = false; + } + break; + } + $node = $node->nextSibling; + } + + // Check if this is the latest version by looking for "Latest" text + $node = $h1; + $is_latest = false; + + // Look for "Latest" text in the next few siblings + $sibling_count = 0; + $max_siblings_to_check = 10; // Limit how far we look + + while ($node && $sibling_count < $max_siblings_to_check) { + $node = $node->nextSibling; + $sibling_count++; + + if (!$node) { + break; + } + + // Check text nodes and element nodes + if ($node->nodeType === XML_TEXT_NODE) { + if (stripos($node->textContent, 'Latest') !== false) { + $is_latest = true; + break; + } + } elseif ($node->nodeType === XML_ELEMENT_NODE) { + // Skip to next H1 (which would be the next version) + if ($node->tagName === 'h1') { + break; + } + + // Check if this element contains "Latest" + if (stripos($node->textContent, 'Latest') !== false) { + $is_latest = true; + break; + } + } + } + + // Add announcement link for latest version + if ($is_latest) { + $version_info['announcement'] = 'https://enhance.com/support/release-notes.html#' . $version; + } + + // Store in branch_versions for later processing + if (!isset($branch_versions[$branch])) { + $branch_versions[$branch] = array(); + } + + $branch_versions[$branch][] = array( + 'version' => $version, + 'info' => $version_info, + 'patch' => (int)$version_parts[2] + ); + } + + // For each branch, keep only the highest patch version + foreach ($branch_versions as $branch => $branch_data) { + // Sort by patch version (descending) + usort($branch_data, function($a, $b) { + return $b['patch'] - $a['patch']; + }); + + // Keep only the highest patch version + if (!empty($branch_data)) { + $versions[$branch] = $branch_data[0]['info']; + } + } + + return $versions; + } +} From a04272bc1a0a1f63361bf85223a57e668d080953 Mon Sep 17 00:00:00 2001 From: "factory-droid[bot]" <138933559+factory-droid[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 00:06:01 +0000 Subject: [PATCH 2/3] Add comprehensive documentation for Enhance integration - Created ENHANCE_INTEGRATION.md with setup and troubleshooting guide - Includes technical details on HTML parsing implementation - Provides testing commands and common issue resolution - Documents data extraction format and future improvement ideas --- ENHANCE_INTEGRATION.md | 139 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 ENHANCE_INTEGRATION.md diff --git a/ENHANCE_INTEGRATION.md b/ENHANCE_INTEGRATION.md new file mode 100644 index 0000000..db18f1e --- /dev/null +++ b/ENHANCE_INTEGRATION.md @@ -0,0 +1,139 @@ +# Enhance Platform Integration +_Comprehensive documentation for the `software/enhance.php` checker_ + +--- + +## 1  What We’ve Built +`software/enhance.php` adds **real-time version monitoring** for the [Enhance](https://enhance.com) hosting platform to VersionCheck. +Key capabilities: + +* Scrapes the public **Release Notes** page + `https://enhance.com/support/release-notes.html` +* Parses every core release (v9 → v12) including patch versions + (e.g. `12.6.0`, `12.5.1`, `12.0.30`) +* Detects which release is flagged **“Latest”** and records an _announcement_ + link for notification e-mails +* Stores/updates one entry per **major.minor branch** + (`12.6`, `12.5`, `11.0`, …) keeping only the highest patch + +The integration means administrators and subscribers will be alerted within +minutes of any new Enhance release. + +--- + +## 2  How It Works + +| Phase | Details | +|-------|---------| +| **Fetch** | `http::get()` (cURL) downloads the raw HTML. | +| **Parse** | `DOMDocument` iterates `

` elements (each version header). | +| **Filter** | Non-core headers (`Appcd…`, `WHMCS…`, `PHP packages`) are ignored. | +| **Extract** | • Version number (`^\d+\.\d+\.\d+`)
• Branch = first two octets
• Release date from the first subsequent `

` (ex “27th May 2025”) – converted to `Y-m-d H:i:s`.
• “Latest” determined by scanning sibling nodes for the word **Latest** (case-insensitive). | +| **Reduce** | All patch releases within the same branch are sorted by PATCH descending; only newest kept. | +| **Return** | Array structure consumed by `check.php`: ```php ['12.6' => ['version'=>'12.6.0','release_date'=>'2025-05-27 00:00:00','announcement'=>..., 'estimated'=>false], …]``` | + +--- + +## 3  Data Extracted + +| Field | Example | Notes | +|------------------|--------------------------|-----------------------------------------| +| `software` | `enhance` (class name) | Stored automatically by core logic | +| `branch` | `12.6` | Major.Minor | +| `version` | `12.6.0` | Highest patch for branch | +| `release_date` | `2025-05-27 00:00:00` | Parsed; fallback = now + `estimated=1` | +| `announcement` | `https://enhance.com/support/release-notes.html#12.6.0` | Only present for branch marked **Latest** | +| `estimated` | `0`/`1` | `1` if date parsing failed | + +--- + +## 4  Setup Instructions + +1. **Pull the code** + ```bash + git fetch origin + git checkout feature/add-enhance-platform + ``` + +2. **Verify file exists** + ``` + software/enhance.php + ``` + +3. **Enable (optional)** + `public static $enabled = true;` by default. + Set to `false` to disable without deleting the file. + +4. **Database** + No schema changes. `check.php` will auto-insert rows into `versions` and + `notifications` tables. + +5. **Cron** + Ensure your existing cron that executes `check.php` remains active + (per installation guide). No additional jobs are required. + +--- + +## 5  Testing & Troubleshooting + +### Quick CLI Test +```bash +php -r " +include 'software/enhance.php'; +\$e = new enhance(); +\$versions = \$e->get_versions(\$e->get_data()); +print_r(array_slice(\$versions,0,3,true)); +" +``` + +Expected trimmed output: +``` +Array +( + [12.6] => Array + ( + [version] => 12.6.0 + [release_date] => 2025-05-27 00:00:00 + [announcement] => https://enhance.com/support/release-notes.html#12.6.0 + [estimated] => + ) + [12.5] => Array + ( + [version] => 12.5.1 + [release_date] => 2025-05-21 00:00:00 + [estimated] => + ) + [12.4] => … +) +``` + +### Common Issues + +| Symptom | Possible Cause | Resolution | +|---------|----------------|------------| +| **`RuntimeException` from `http::get`** | cURL blocked / TLS failure | Check outbound HTTPS, update CA bundle | +| **No versions detected** | HTML structure changed | Inspect page, update DOM traversal/regex | +| **Dates show “estimated”** | Release lacks `

` date or new format | Raise issue; consider regex fallback | +| **Latest not flagged** | “Latest” label moved | Adjust sibling scan range (`$max_siblings_to_check`) | +| **Database errors on first run** | Missing `config.php` or wrong credentials | Follow install README – import SQL & configure PDO | + +--- + +## 6  Future Improvements + +* **Caching / Rate-limit** – store fetched HTML to cut traffic & guard against + temporary site outages. +* **Unit tests** – mock HTML fixtures to detect parser breakage early. +* **Full historical capture** – store *all* patch versions, not just newest. +* **Security hardening** – sanitize/limit DOM parsing to reduce attack surface. +* **Graceful fallback** – if DOM parsing fails, attempt pure regex extraction. +* **Official API** – migrate to JSON endpoint if Enhance publishes one. +* **Notification granularity** – allow subscribers to opt into + major/minor/patch channels separately. + +--- + +### Maintainer Notes +* Written for **PHP ≥8.0** (uses `JSON_THROW_ON_ERROR` in other checkers). +* Keep `$uri` constant if domain changes (e.g. `.html` vs no extension). +* Test after core panel redesigns – header hierarchy may shift. From d681d6fb10cb3da3f59a5fc49ede6f8d97801d74 Mon Sep 17 00:00:00 2001 From: "factory-droid[bot]" <138933559+factory-droid[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 02:46:48 +0000 Subject: [PATCH 3/3] Switch Enhance monitoring from HTML scraping to APT repository - Changed data source from release notes HTML to apt.enhance.com Packages file - Now monitors ecp-core package versions directly from APT repository - Extracts actual repository date from Release file (2025-05-27) - Improved reliability and accuracy of version detection - Maintains same version branch logic (12.6, 12.5, etc.) - Only marks highest version (12.6.0) as latest with announcement link --- software/enhance.php | 233 ++++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 137 deletions(-) diff --git a/software/enhance.php b/software/enhance.php index ffa11df..9dfc1df 100644 --- a/software/enhance.php +++ b/software/enhance.php @@ -3,33 +3,27 @@ include_once(__DIR__ . '/../methods/http.php'); /* -Example structure of Enhance release notes: - -# 12.6.0 - -### 27th May 2025 - -Latest - -**_Important: You must update all the servers in your cluster when applying this update._** - -### Enhanced - -- Reinstated backup failure logging to the activity log for customers on version 12.x. -- Moved logic for management of local .my.cnf file to appcd service for improved performance. - -### Fixed - -- Race condition where the local .my.cnf had been modified and website files were restored before databases which prevented a restore of the database. -- Descriptive text for email rate limits was incorrect in English language. - -# 12.5.1 - -### 21st May 2025 - -### Fixed - -- Using SSO when a previously expired session existed would require a hard refresh. +Example structure of Enhance apt repository Packages file: + +Package: ecp-core +Version: 12.6.0 +Architecture: amd64 +Maintainer: Enhance Ltd +Installed-Size: 87858 +Depends: cron, curl, ecp-php56, ecp-php70, ecp-php71, ecp-php72, ecp-php73, ecp-php74, ecp-php80, ecp-php81, ecp-php82, ecp-php83, ecp-php84, libc6 (>= 2.34), libc6 (>= 2.38), libc6 (>= 2.39), libmilter1.0.1 (>= 8.14.1), libnss3-dev, libpam0g (>= 0.99.7.1), libssl3t64 (>= 3.0.0), linux-image-extra-virtual, openssh-server, openssl, quota, rsync, ufw, zlib1g (>= 1:1.1.4) +Conflicts: appcd +Replaces: appcd +Filename: pool/noble/ecp-core_12.6.0_amd64.deb +Size: 22096584 +MD5sum: b8d084689177aa18b52ff7ee01443156 +SHA1: b41084091113c5090f49a57f4f77b0f2d5774eec +SHA256: 4be9b14e6004debcdbc04819d07f2e78e2624760178cd7e72041722902f87704 +Priority: optional +Description: [generated from Rust crate appcd] + +Package: ecp-core +Version: 12.5.1 +... */ class enhance extends SoftwareCheck @@ -37,140 +31,100 @@ class enhance extends SoftwareCheck public static $name = 'Enhance Platform'; public static $vendor = 'Enhance'; public static $homepage = 'https://enhance.com/'; - public static $type = 'html'; + public static $type = 'apt'; public static $enabled = true; - var $uri = 'https://enhance.com/support/release-notes.html'; + var $uri = 'https://apt.enhance.com/dists/noble/main/binary-amd64/Packages'; + var $release_uri = 'https://apt.enhance.com/dists/noble/Release'; function get_data() { - $data = http::get($this->uri); - return $data; + // Get the Packages file + $packages_data = http::get($this->uri); + + // Get the Release file for date information + try { + $release_data = http::get($this->release_uri); + } catch (Exception $e) { + // If we can't get the Release file, just use the Packages data + $release_data = ''; + } + + return [ + 'packages' => $packages_data, + 'release' => $release_data + ]; } - function get_versions($data = '') + function get_versions($data = array()) { - if (empty($data)) { + if (empty($data) || !isset($data['packages']) || empty($data['packages'])) { return array(); } - // Initialize versions array - $versions = array(); - $branch_versions = array(); - - // Use DOMDocument for proper HTML parsing - $dom = new DOMDocument(); + // Extract repository date from Release file if available + $repo_date = date("Y-m-d H:i:s"); // Default to current date/time + $estimated = true; - // Suppress warnings for malformed HTML - libxml_use_internal_errors(true); - - // Load the HTML content - $dom->loadHTML($data); + if (!empty($data['release'])) { + if (preg_match('/Date:\s+([^\n]+)/', $data['release'], $matches)) { + $date_str = trim($matches[1]); + if (($timestamp = strtotime($date_str)) !== false) { + $repo_date = date("Y-m-d H:i:s", $timestamp); + $estimated = false; + } + } + } - // Reset errors - libxml_clear_errors(); + // Parse Packages file + $versions = array(); + $branch_versions = array(); + $latest_version = null; + $latest_version_full = null; - // Get all H1 elements (version headers) - $h1Elements = $dom->getElementsByTagName('h1'); + // Split the Packages file into individual package entries + $package_entries = explode("\n\n", $data['packages']); - foreach ($h1Elements as $h1) { - $version_text = trim($h1->textContent); - - // Skip non-core versions (Appcd, WHMCS module, etc.) - if (preg_match('/^(Appcd|WHMCS module|PHP packages)/i', $version_text)) { + foreach ($package_entries as $entry) { + // Check if this is an ecp-core package + if (strpos($entry, 'Package: ecp-core') === false) { continue; } - // Extract version number using regex - if (preg_match('/^(\d+\.\d+\.\d+)/', $version_text, $matches)) { + // Extract version + if (preg_match('/Version:\s+(\d+\.\d+\.\d+)/', $entry, $matches)) { $version = $matches[1]; - } else { - // Skip entries that don't start with a version number - continue; - } - - // Extract version components - $version_parts = explode('.', $version); - if (count($version_parts) < 3) { - continue; - } - - // Determine branch (major.minor) - $branch = $version_parts[0] . '.' . $version_parts[1]; - - // Initialize version info - $version_info = array( - 'version' => $version, - 'release_date' => date("Y-m-d H:i:s"), // Default to current date/time - 'estimated' => true - ); - - // Find the release date (in H3 tag after the H1) - $node = $h1->nextSibling; - while ($node) { - if ($node->nodeType === XML_ELEMENT_NODE && $node->tagName === 'h3') { - $date_text = trim($node->textContent); - - // Try to parse the date (format like "27th May 2025") - if (($timestamp = strtotime($date_text)) !== false) { - $version_info['release_date'] = date("Y-m-d H:i:s", $timestamp); - $version_info['estimated'] = false; - } - break; + + // Extract version components + $version_parts = explode('.', $version); + if (count($version_parts) < 3) { + continue; } - $node = $node->nextSibling; - } - - // Check if this is the latest version by looking for "Latest" text - $node = $h1; - $is_latest = false; - - // Look for "Latest" text in the next few siblings - $sibling_count = 0; - $max_siblings_to_check = 10; // Limit how far we look - - while ($node && $sibling_count < $max_siblings_to_check) { - $node = $node->nextSibling; - $sibling_count++; - if (!$node) { - break; + // Determine branch (major.minor) + $branch = $version_parts[0] . '.' . $version_parts[1]; + $patch = (int)$version_parts[2]; + + // Store in branch_versions for later processing + if (!isset($branch_versions[$branch])) { + $branch_versions[$branch] = array(); } - // Check text nodes and element nodes - if ($node->nodeType === XML_TEXT_NODE) { - if (stripos($node->textContent, 'Latest') !== false) { - $is_latest = true; - break; - } - } elseif ($node->nodeType === XML_ELEMENT_NODE) { - // Skip to next H1 (which would be the next version) - if ($node->tagName === 'h1') { - break; - } - - // Check if this element contains "Latest" - if (stripos($node->textContent, 'Latest') !== false) { - $is_latest = true; - break; - } + $branch_versions[$branch][] = array( + 'version' => $version, + 'patch' => $patch, + 'info' => array( + 'version' => $version, + 'release_date' => $repo_date, + 'estimated' => $estimated + ) + ); + + // Track the latest version (highest version number) + if ($latest_version === null || version_compare($version, $latest_version_full, '>')) { + $latest_version = $branch; + $latest_version_full = $version; } } - - // Add announcement link for latest version - if ($is_latest) { - $version_info['announcement'] = 'https://enhance.com/support/release-notes.html#' . $version; - } - - // Store in branch_versions for later processing - if (!isset($branch_versions[$branch])) { - $branch_versions[$branch] = array(); - } - - $branch_versions[$branch][] = array( - 'version' => $version, - 'info' => $version_info, - 'patch' => (int)$version_parts[2] - ); } // For each branch, keep only the highest patch version @@ -186,6 +140,11 @@ function get_versions($data = '') } } + // Mark only the highest version as the latest with an announcement link + if ($latest_version !== null && isset($versions[$latest_version])) { + $versions[$latest_version]['announcement'] = 'https://enhance.com/support/release-notes.html#' . $latest_version_full; + } + return $versions; } }