diff --git a/.github/workflows/api-sync-check.yml b/.github/workflows/api-sync-check.yml deleted file mode 100644 index cd0a851..0000000 --- a/.github/workflows/api-sync-check.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: VirtFusion API Change Detection - -on: - schedule: - - cron: '0 9 * * 1' # Monday 9am UTC - workflow_dispatch: - -jobs: - check-api: - runs-on: ubuntu-latest - permissions: - contents: write - issues: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - - - name: Download current API spec - run: curl -sSL -o /tmp/openapi-current.yaml https://docs.virtfusion.com/api/openapi.yaml - - - name: Compare with baseline - id: diff - run: | - if [ ! -f docs/openapi-baseline.yaml ]; then - echo "No baseline found — creating initial baseline" - cp /tmp/openapi-current.yaml docs/openapi-baseline.yaml - echo "changed=initial" >> "$GITHUB_OUTPUT" - elif ! diff -q docs/openapi-baseline.yaml /tmp/openapi-current.yaml > /dev/null 2>&1; then - echo "API spec has changed" - diff docs/openapi-baseline.yaml /tmp/openapi-current.yaml > /tmp/api-diff.txt || true - echo "changed=true" >> "$GITHUB_OUTPUT" - else - echo "No changes detected" - echo "changed=false" >> "$GITHUB_OUTPUT" - fi - - - name: Create issue on change - if: steps.diff.outputs.changed == 'true' - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const diff = fs.readFileSync('/tmp/api-diff.txt', 'utf8').substring(0, 60000); - await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: `VirtFusion API spec changed (${new Date().toISOString().split('T')[0]})`, - body: `The VirtFusion OpenAPI spec has been updated.\n\nDiff\n\n\`\`\`diff\n${diff}\n\`\`\`\n\n\nReview the changes and update the module if needed.`, - labels: ['api-sync'] - }); - - - name: Update baseline and create PR - if: steps.diff.outputs.changed == 'true' || steps.diff.outputs.changed == 'initial' - run: | - cp /tmp/openapi-current.yaml docs/openapi-baseline.yaml - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - BRANCH="api-sync/$(date +%Y-%m-%d)" - git checkout -b "$BRANCH" - git add docs/openapi-baseline.yaml - git commit -m "chore: update VirtFusion API baseline spec" - git push origin "$BRANCH" - gh pr create --title "chore: update VirtFusion API baseline" --body "Automated update of the VirtFusion OpenAPI baseline spec." --base main - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.releaserc.json b/.releaserc.json deleted file mode 100644 index b9660c1..0000000 --- a/.releaserc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "branches": ["main"], - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - ["@semantic-release/changelog", { "changelogFile": "CHANGELOG.md" }], - "@semantic-release/github", - ["@semantic-release/git", { - "assets": ["CHANGELOG.md"], - "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" - }] - ] -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bb7bc3..15b3b16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,42 +1,39 @@ -# 1.0.0 (2026-03-19) - - -### Bug Fixes - -* add null/false guards, proper error handling, and VNC popup fix ([49fdd9e](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/49fdd9e49ba87bfb4b72dd741e15f790c1050033)) -* OS gallery accordion auto-collapses other sections when one opens ([a9565ff](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/a9565ff6f920ab480a9298c055b8f4581786f3a4)) -* OS gallery accordion layout and remove broken remote icon fetching ([9cd737c](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/9cd737c5d5d26587bea8fa40bf75f5e25544ff18)) -* TestConnection for unsaved servers, traffic display, and cache-busting ([e8d2eb0](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/e8d2eb0aa1f173f13bb0b8d7dfca0acebb821ac7)) -* XSS escaping, null guards, JS bug fixes, and documentation updates ([6c7cdc6](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/6c7cdc6421678390746adcee4877a7ade8f2a061)) - - -### Features - -* add client-side SSH Ed25519 key generator on order page ([209e01d](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/209e01deb6832dce76a307410fbab28b1e420093)) -* add VNC check, SSH key paste, resources panel, sliders, and self-service billing ([1e471af](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/1e471affd0ae9a68358afa5704523bce9bb413d0)) -* major enhancement — OS gallery, server rename, traffic chart, backups, VNC toggle, password reset, Redis caching, UX improvements ([90a97c4](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/90a97c4afb61a179eda40e23b97637dd90507b55)) -* streamline network panel, conditional self-service, remove IP add endpoints ([e73e85c](https://git.ezscale.cloud/EZSCALE/virtfusion-whmcs-module/commit/e73e85c5a9faa79b50e4949328c1d2a3cbc49ddf)) - -# 1.0.0 (2026-02-07) - - -### Bug Fixes - -* add null/false guards, proper error handling, and VNC popup fix ([49fdd9e](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/49fdd9e49ba87bfb4b72dd741e15f790c1050033)) -* TestConnection for unsaved servers, traffic display, and cache-busting ([e8d2eb0](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/e8d2eb0aa1f173f13bb0b8d7dfca0acebb821ac7)) -* XSS escaping, null guards, JS bug fixes, and documentation updates ([6c7cdc6](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/6c7cdc6421678390746adcee4877a7ade8f2a061)) - - -### Features - -* add client-side SSH Ed25519 key generator on order page ([209e01d](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/209e01deb6832dce76a307410fbab28b1e420093)) -* add VNC check, SSH key paste, resources panel, sliders, and self-service billing ([1e471af](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/1e471affd0ae9a68358afa5704523bce9bb413d0)) -* streamline network panel, conditional self-service, remove IP add endpoints ([e73e85c](https://github.com/EZSCALE/virtfusion-whmcs-module/commit/e73e85c5a9faa79b50e4949328c1d2a3cbc49ddf)) - # Changelog All notable changes to the VirtFusion Direct Provisioning Module for WHMCS. +## [1.0.0] - 2026-03-19 + +### Features +- OS template tile gallery with accordion categories, brand icons, and search +- Inline server rename with friendly name generator +- Traffic statistics canvas chart in resources panel +- Backup listing timeline in manage panel +- VNC enable/disable toggle with connection details and password copy +- Server root password reset with auto-clipboard copy +- Redis-backed API response caching with filesystem fallback +- Skeleton loading, action cooldowns, progress indicators +- Copy-to-clipboard buttons for IP addresses +- Client-side SSH Ed25519 key generator on checkout page +- VNC console support, resources panel, self-service billing +- Configurable option sliders on checkout page + +### Bug Fixes +- XSS escaping, null guards, and proper error handling +- All state-mutating operations use POST instead of GET +- Explicit break after all output() calls in client.php +- Server-side regex validation on rename endpoint +- Error messages sanitized (no raw API errors exposed to clients) + +### Removed +- Client IP removal capability (IPs managed by VirtFusion) +- IP add buttons (managed by VirtFusion during provisioning) +- Firewall panel (non-functional; managed in VirtFusion admin) + +### Infrastructure +- Tag-based release workflow (compatible with Gitea and GitHub) +- Codebase consolidation: resolveServiceContext(), groupOsTemplates(), vfUrl(), vfShowAlert() + ## [0.0.18] - 2025-10-01 ### Changed @@ -93,6 +90,6 @@ All notable changes to the VirtFusion Direct Provisioning Module for WHMCS. - Admin services tab with server ID management - Package change (upgrade/downgrade) support - Configurable option mapping for dynamic resource allocation -- GitHub Actions CI/CD with semantic-release +- GitHub Actions CI/CD - Security policy (SECURITY.md) - License (GPL v3) diff --git a/CLAUDE.md b/CLAUDE.md index ad04e21..3560560 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,12 +15,27 @@ There is no automated test suite, linter, or build step. Testing is manual: - **Module logging:** WHMCS Admin → Utilities → Logs → Module Log captures all API calls and responses - **Server object viewer:** Admin services tab shows full JSON response from VirtFusion API +## Development Rules + +- **Error handling:** Always use try...catch blocks around API calls, database operations, and any code that may throw exceptions. Never let exceptions bubble up unhandled to the user. Log caught exceptions via `Log::insert()`. +- **Ownership validation:** Every client-facing action MUST verify service ownership via `validateUserOwnsService()` before performing any operation. Server IDs must be cross-referenced against the authenticated client to prevent cross-customer data access. +- **Security:** All input must be validated server-side. Never trust client-side validation alone. Cast IDs to `(int)`, validate strings with regex, escape output with `htmlspecialchars()`. +- **Control flow:** Every `$vf->output()` call in switch cases must be followed by `break`. Do not rely on `exit()` inside `output()` for flow control. +- **HTTP methods:** Read-only operations use GET. State-mutating operations (power, rebuild, rename, password reset, credit, VNC toggle) use POST with data in the request body. +- **Caching:** Use the `Cache` class for slow-changing API responses. Never cache real-time data (server status, VNC sessions, login tokens) or mutation responses. + ## Release Process -Releases are automated via GitHub Actions using semantic-release on pushes to `main`. Use **conventional commits**: -- `fix:` → patch release -- `feat:` → minor release -- `BREAKING CHANGE:` in commit body → major release +Releases are triggered by pushing a git tag: +```bash +git tag v1.1.0 +git push origin v1.1.0 +``` + +The `publish-release.yml` workflow creates a GitHub/Gitea release with auto-generated notes from the commit log. Use **conventional commits** for clear changelogs: +- `fix:` → patch-level change +- `feat:` → feature addition +- `refactor:` → code improvement without behavior change ## Architecture @@ -31,38 +46,39 @@ Releases are automated via GitHub Actions using semantic-release on pushes to `m | File | Purpose | |------|---------| | `VirtFusionDirect.php` | WHMCS module interface — non-namespaced functions (`VirtFusionDirect_CreateAccount()`, etc.) that delegate to library classes | -| `client.php` | Client-facing AJAX API — authenticated by WHMCS session + service ownership validation | +| `client.php` | Client-facing AJAX API — authenticated by WHMCS session + service ownership validation. POST for mutations, GET for reads. | | `admin.php` | Admin-facing AJAX API — requires WHMCS admin authentication | -| `hooks.php` | WHMCS hooks — checkout validation (OS selection), dynamic dropdown/slider injection, SSH key paste | +| `hooks.php` | WHMCS hooks — checkout validation (OS selection), OS gallery + SSH key UI injection, slider UI for configurable options | ### Core Classes (in `lib/`) | Class | Role | |-------|------| -| `Module` | Base class with API integration, auth checks, power/network/VNC/backup/resource/self-service methods. All client/admin actions route through here. | +| `Module` | Base class with API integration, auth checks, and all feature methods (power, network, VNC, backup, resource, self-service, traffic, rename, password reset). Contains `resolveServiceContext()` for DRY service lookups and `groupOsTemplates()` for shared OS category logic. | | `ModuleFunctions` | Extends `Module`. Service lifecycle: create, suspend, unsuspend, terminate, change package, usage updates, client area rendering. | | `ConfigureService` | Extends `Module`. Order-time operations: package discovery, OS template fetching, server build initialization, SSH key retrieval and creation. | | `Database` | Static methods for `mod_virtfusion_direct` table operations and WHMCS DB queries. Auto-creates/migrates schema on first use. | -| `Curl` | HTTP client wrapper with Bearer token auth, SSL verification, 30s timeout. Methods: `get`, `post`, `put`, `patch`, `delete`. | +| `Curl` | HTTP client wrapper with Bearer token auth, SSL verification, 30s timeout. Methods: `get`, `post`, `put`, `patch`, `delete`. Single-use — each instance makes one request. | +| `Cache` | Two-tier caching: Redis (if `ext-redis` available) with atomic filesystem fallback. TTLs: OS templates 10min, traffic/backups 2min, packages 10min. | | `ServerResource` | Transforms VirtFusion API response into flat key-value format for Smarty templates. | | `AdminHTML` | Static methods generating admin services tab HTML (server ID editor, JSON viewer, action buttons). | | `Log` | Thin wrapper around WHMCS module logging. | ### Class Hierarchy -`ModuleFunctions` and `ConfigureService` both extend `Module`. Most business logic lives in `Module` — it handles API calls, auth, validation, and all feature-specific operations (power, network, VNC, backup, resource modification). `ModuleFunctions` orchestrates the WHMCS service lifecycle (provisioning flow, suspension, termination). +`ModuleFunctions` and `ConfigureService` both extend `Module`. Most business logic lives in `Module` — it handles API calls, auth, validation, and all feature-specific operations. The `resolveServiceContext()` method provides a standardized way to look up service → WHMCS service → control panel → curl client in a single call, eliminating boilerplate across all API methods. ### Client-Side -- **`templates/overview.tpl`** — Smarty template for client area (server info, power, network, rebuild, resources, VNC, self-service billing, billing overview) -- **`templates/js/module.js`** — Vanilla JS (1000+ lines) handling AJAX calls to `client.php`, DOM updates, status badges, power actions, all management UIs +- **`templates/overview.tpl`** — Smarty template for client area (server info, power, network, rebuild with OS gallery, resources with traffic chart, VNC toggle, self-service billing, billing overview, backups timeline, server rename, password reset) +- **`templates/js/module.js`** — Vanilla JS + jQuery handling AJAX calls, DOM updates, status badges, power actions, all management UIs. Key helpers: `vfUrl()` (URL builder), `vfShowAlert()` (alert display), `vfRenderOsGallery()` (accordion gallery), `vfDrawTrafficChart()` (canvas chart) - **`templates/js/keygen.js`** — Client-side SSH Ed25519 key generator using Web Crypto API (loaded on checkout page) - **`templates/css/module.css`** — Cross-theme styles with Bootstrap 3/4/5 dual class support (`panel card`, `panel-body card-body`) ### Removed Features - **Firewall** — Removed (non-functional; rulesets must be created in VirtFusion admin panel) -- **IP add buttons** — Removed (`addIPv4`, `addIPv6` endpoints and UI); IPs are managed by VirtFusion during provisioning +- **IP add/remove buttons** — Removed; IPs are managed by VirtFusion during provisioning - **Upgrade/Downgrade link** — Removed from resources panel ### Data Flow: Server Creation @@ -81,12 +97,13 @@ Custom option names can be mapped in `config/ConfigOptionMapping.php` (copy from ## Security Patterns -- All PHP files start with `if (!defined("WHMCS")) die()` to prevent direct access +- All PHP files start with `if (!defined("WHMCS")) die()` to prevent direct access (except entry points using `init.php`) - Client endpoints validate WHMCS session AND service ownership before any operation - API tokens stored encrypted in WHMCS server password field (decrypted via `localAPI('DecryptPassword')`) -- Input validation: type casting, regex filtering, `filter_var()` for IP addresses -- Output escaping: `htmlspecialchars()` in Smarty, `encodeURIComponent()` / `.text()` in JS +- Input validation: type casting (`(int)`), regex filtering, `filter_var()` for IP addresses +- Output escaping: `htmlspecialchars()` in PHP, `$('').text()` in jQuery, `{escape:'htmlall'}` in Smarty - SSL verification enabled on all API calls (`CURLOPT_SSL_VERIFYPEER` + `CURLOPT_SSL_VERIFYHOST = 2`) +- Server rename validated both client-side and server-side with RFC 1123 regex ## VirtFusion API Compatibility @@ -95,6 +112,7 @@ Custom option names can be mapped in `config/ConfigOptionMapping.php` (copy from - **VNC console:** v6.1.0+ - **Resource modification:** v6.2.0+ - **Self-service billing:** Requires self-service feature enabled in VirtFusion +- **OS icon path:** `{baseUrl}/img/logo/{icon_filename}` (public, no auth required) ## Product Config Options @@ -111,4 +129,5 @@ Custom option names can be mapped in `config/ConfigOptionMapping.php` (copy from - WHMCS 8.x+ (tested 8.0–8.10) - PHP 8.0+ with cURL extension +- Redis extension optional (improves caching performance, falls back to filesystem) - All WHMCS themes supported (Six, Twenty-One, Lagom, custom) via Bootstrap 3/4/5 dual classes diff --git a/README.md b/README.md index ac50078..39a960d 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ You also need a VirtFusion API token with the following permissions: - **Control Panel SSO** - One-click login to VirtFusion panel - **Server Rebuild** - Reinstall with any available OS template - **Password Reset** - Reset VirtFusion panel login credentials -- **Network Management** - View and remove IPv4 addresses; view IPv6 subnets +- **Network Management** - View IPv4 addresses and IPv6 subnets with copy-to-clipboard - **Resources Panel** - Current memory, CPU, storage, traffic allocation with usage bars - **VNC Console** - Browser-based console access (panel auto-hides when VNC is disabled on the server) - **Self-Service Billing** - Credit balance display, usage breakdown, and credit top-up (when enabled) @@ -79,7 +79,7 @@ You also need a VirtFusion API token with the following permissions: - **Update Server Object** - Refresh cached server data from VirtFusion ### Ordering Process -- Dynamic OS template dropdown populated from VirtFusion API +- OS template tile gallery with accordion categories, search, and brand icons - SSH key selection dropdown for users with saved keys, with option to paste a new public key - **SSH Ed25519 key generator** — Client-side keypair generation using Web Crypto API - Checkout validation ensuring OS selection before order placement @@ -305,7 +305,7 @@ Four power control buttons: ### Network Management - View all IPv4 addresses and IPv6 subnets assigned to the server -- Remove secondary IPv4 addresses (primary cannot be removed) +- Copy IP addresses to clipboard with one click ### VNC Console - Opens a browser-based VNC console to the server @@ -407,12 +407,6 @@ WHMCS automatically loads theme-specific templates when they exist. Copy the ori | `GET` | `/media/templates/fromServerPackageSpec/{id}` | OS templates | | `GET` | `/ssh_keys/user/{id}` | SSH key listing | -### Network - -| Method | Endpoint | Purpose | -|---|---|---| -| `DELETE` | `/servers/{id}/ipv4` | Remove IPv4 address | - ### SSH Keys | Method | Endpoint | Purpose | @@ -426,7 +420,10 @@ WHMCS automatically loads theme-specific templates when they exist. Copy the ori | `GET` | `/selfService/usage/byUserExtRelationId/{id}` | Usage data by WHMCS client ID | | `GET` | `/selfService/report/byUserExtRelationId/{id}` | Billing report by WHMCS client ID | | `POST` | `/selfService/credit/byUserExtRelationId/{id}` | Add credit by WHMCS client ID | -| `GET` | `/selfService/currencies` | Available self-service currencies | +| `GET` | `/servers/{id}/traffic` | Traffic statistics | +| `GET` | `/backups/server/{id}` | Backup listing | +| `POST` | `/servers/{id}/vnc` | Toggle VNC on/off | +| `POST` | `/servers/{id}/resetPassword` | Reset server root password | ### Advanced @@ -533,9 +530,7 @@ This data appears in the WHMCS client area and admin product details. 7. **Concurrent API Calls** - The module makes individual API calls for each feature panel on the client area page. If the VirtFusion API is slow, the page may take longer to fully load. All panels load asynchronously to minimize perceived delay. -8. **Primary IPv4 Protection** - The first IPv4 address cannot be removed through the client area interface. This is by design to prevent users from accidentally removing their primary IP address. - -9. **Self-Signed SSL Certificates** - SSL verification is enforced by default. VirtFusion panels using self-signed certificates will cause connection failures. Use a valid SSL certificate (e.g., Let's Encrypt) on your VirtFusion panel. +8. **Self-Signed SSL Certificates** - SSL verification is enforced by default. VirtFusion panels using self-signed certificates will cause connection failures. Use a valid SSL certificate (e.g., Let's Encrypt) on your VirtFusion panel. ## Security @@ -570,6 +565,7 @@ modules/servers/VirtFusionDirect/ ModuleFunctions.php # Provisioning: create, suspend, unsuspend, terminate, change package ConfigureService.php # Order configuration: OS templates, SSH keys, server build init Database.php # Database operations: custom table, WHMCS table queries + Cache.php # Two-tier cache: Redis with filesystem fallback Curl.php # HTTP client: GET, POST, PUT, PATCH, DELETE with SSL verification ServerResource.php # Data transformer: VirtFusion API response -> display format AdminHTML.php # Admin interface: HTML generation for admin services tab diff --git a/docs/openapi-baseline.yaml b/docs/openapi-baseline.yaml deleted file mode 100644 index de4aaf3..0000000 --- a/docs/openapi-baseline.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# VirtFusion OpenAPI Baseline -# This file will be auto-populated by the api-sync-check workflow -# on first run. Do not edit manually. -openapi: "3.0.0" -info: - title: VirtFusion API Baseline Placeholder - version: "0.0.0" diff --git a/modules/servers/VirtFusionDirect/admin.php b/modules/servers/VirtFusionDirect/admin.php index d7773b7..e97e498 100644 --- a/modules/servers/VirtFusionDirect/admin.php +++ b/modules/servers/VirtFusionDirect/admin.php @@ -13,81 +13,71 @@ $vf->adminOnly(); switch ($vf->validateAction(true)) { /** - * * Get server information. - * */ case 'serverData': - if ($vf->validateServiceID(true)) { + $serviceID = $vf->validateServiceID(true); - /** No need to validate ownership **/ - - $whmcsService = Database::getWhmcsService((int)$_GET['serviceID']); - - if (!$whmcsService) { - $vf->output(['success' => false, 'errors' => 'Service not found.'], true, true, 404); - } - - if ($whmcsService->domainstatus == 'Pending' || $whmcsService->domainstatus == 'Terminated' || $whmcsService->domainstatus == 'Cancelled' || $whmcsService->domainstatus == 'Fraud') { - $vf->output(['success' => false, 'errors' => 'Server is not Active, Suspended or Completed. Not fetching remote data.'], true, true, 400); - } - - $data = $vf->fetchServerData((int)$_GET['serviceID']); - - if (!$data) { - $vf->output(['success' => false, 'errors' => 'No data returned from VirtFusion.'], true, true, 502); - - } - - $vf->updateWhmcsServiceParamsOnServerObject((int)$_GET['serviceID'], $data); - $vf->output(['success' => true, 'data' => (new ServerResource())->process($data)], true, true, 200); + $whmcsService = Database::getWhmcsService($serviceID); + if (!$whmcsService) { + $vf->output(['success' => false, 'errors' => 'Service not found.'], true, true, 404); + break; } + + if (in_array($whmcsService->domainstatus, ['Pending', 'Terminated', 'Cancelled', 'Fraud'], true)) { + $vf->output(['success' => false, 'errors' => 'Server is not Active, Suspended or Completed. Not fetching remote data.'], true, true, 400); + break; + } + + $data = $vf->fetchServerData($serviceID); + + if (!$data) { + $vf->output(['success' => false, 'errors' => 'No data returned from VirtFusion.'], true, true, 502); + break; + } + + $vf->updateWhmcsServiceParamsOnServerObject($serviceID, $data); + $vf->output(['success' => true, 'data' => (new ServerResource())->process($data)], true, true, 200); break; /** - * * Impersonate server owner. - * */ case 'impersonateServerOwner': - if ($vf->validateServiceID(true)) { - - $service = Database::getSystemService((int)$_GET['serviceID']); - - if (!$service) { - $vf->output(['success' => false, 'errors' => 'Service not found'], true, true, 404); - } - - $whmcsService = Database::getWhmcsService((int)$_GET['serviceID']); - - if (!$whmcsService) { - $vf->output(['success' => false, 'errors' => 'WHMCS service not found'], true, true, 404); - } - - $cp = $vf->getCP($whmcsService->server); - - if (!$cp) { - $vf->output(['success' => false, 'errors' => 'Control server not found'], true, true, 500); - } - - $request = $vf->initCurl($cp['token']); - - $data = $request->get($cp['url'] . '/users/' . $whmcsService->userid . '/byExtRelation'); - - if ($request->getRequestInfo('http_code') === 200) { - $vf->output(['success' => true, 'url' => $cp['base_url'], 'user' => json_decode($data, true)['data']], true, true, 200); - } - - $vf->output(['success' => false, 'errors' => 'Received HTTP code ' . $request->getRequestInfo('http_code')], true, true, 502); + $serviceID = $vf->validateServiceID(true); + $service = Database::getSystemService($serviceID); + if (!$service) { + $vf->output(['success' => false, 'errors' => 'Service not found'], true, true, 404); + break; } + + $whmcsService = Database::getWhmcsService($serviceID); + if (!$whmcsService) { + $vf->output(['success' => false, 'errors' => 'WHMCS service not found'], true, true, 404); + break; + } + + $cp = $vf->getCP($whmcsService->server); + if (!$cp) { + $vf->output(['success' => false, 'errors' => 'Control server not found'], true, true, 500); + break; + } + + $request = $vf->initCurl($cp['token']); + $data = $request->get($cp['url'] . '/users/' . (int) $whmcsService->userid . '/byExtRelation'); + + if ($request->getRequestInfo('http_code') === 200) { + $vf->output(['success' => true, 'url' => $cp['base_url'], 'user' => json_decode($data, true)['data']], true, true, 200); + break; + } + + $vf->output(['success' => false, 'errors' => 'Unable to fetch user data'], true, true, 502); break; default: - /** No valid action was specified **/ - $vf->output(['success' => false, 'errors' => 'invalid action'], true, true, 400); } diff --git a/modules/servers/VirtFusionDirect/lib/AdminHTML.php b/modules/servers/VirtFusionDirect/lib/AdminHTML.php index 3dcafe5..6f8f4a2 100644 --- a/modules/servers/VirtFusionDirect/lib/AdminHTML.php +++ b/modules/servers/VirtFusionDirect/lib/AdminHTML.php @@ -25,6 +25,7 @@ EOT; public static function serverId($serverId) { + $serverId = (int) $serverId; return << Changing the Sever ID manually is not recommended. Alterations to this field are usually handled automatically. @@ -34,6 +35,7 @@ EOT; public static function serverInfo($systemUrl, $serviceId) { $systemUrl = htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8'); + $serviceId = (int) $serviceId; $cacheV = time(); return << diff --git a/modules/servers/VirtFusionDirect/lib/Cache.php b/modules/servers/VirtFusionDirect/lib/Cache.php index a7a9e7a..7a1c3eb 100644 --- a/modules/servers/VirtFusionDirect/lib/Cache.php +++ b/modules/servers/VirtFusionDirect/lib/Cache.php @@ -170,27 +170,4 @@ class Cache } } - /** - * Delete all cache keys matching a pattern. - * - * @param string $pattern Glob pattern (e.g., "os:*") - */ - public static function forgetPattern($pattern) - { - $redis = self::redis(); - if ($redis) { - try { - $keys = $redis->keys(self::PREFIX . $pattern); - if (!empty($keys)) { - $redis->del($keys); - } - } catch (\Exception $e) { - // Continue to file cleanup - } - } - - // File cache: can only clear all files for pattern matches - // Since file names are md5 hashed, we can't match patterns. - // For non-Redis, TTL expiry handles cleanup naturally. - } } diff --git a/modules/servers/VirtFusionDirect/lib/Module.php b/modules/servers/VirtFusionDirect/lib/Module.php index 24b2cb4..338b49c 100644 --- a/modules/servers/VirtFusionDirect/lib/Module.php +++ b/modules/servers/VirtFusionDirect/lib/Module.php @@ -379,31 +379,6 @@ class Module return false; } - /** - * Assign a backup plan to a server. - * - * @param int $serviceID - * @param int $planId Backup plan ID (0 to remove) - * @return object|false - */ - public function assignBackupPlan($serviceID, $planId) - { - $planId = (int) $planId; - $ctx = $this->resolveServiceContext($serviceID); - if (!$ctx) return false; - - $ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode(['planId' => $planId])); - $endpoint = $ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/backup/plan'; - $data = $planId > 0 ? $ctx['request']->post($endpoint) : $ctx['request']->delete($endpoint); - Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data); - - $httpCode = $ctx['request']->getRequestInfo('http_code'); - if ($httpCode == 200 || $httpCode == 204) { - return json_decode($data) ?: (object) ['success' => true]; - } - return false; - } - // ========================================================================= // VNC Console // ========================================================================= @@ -740,40 +715,6 @@ class Module return false; } - /** - * Get available self-service currencies. - * - * @param int $serviceID - * @return array|false - */ - public function getSelfServiceCurrencies($serviceID) - { - $cacheKey = 'ss_currencies'; - $cached = Cache::get($cacheKey); - if ($cached !== null) { - return $cached; - } - - $serviceID = (int) $serviceID; - $whmcsService = Database::getWhmcsService($serviceID); - if (!$whmcsService) return false; - - $cp = $this->getCP($whmcsService->server); - if (!$cp) return false; - - $request = $this->initCurl($cp['token']); - $data = $request->get($cp['url'] . '/selfService/currencies'); - - Log::insert(__FUNCTION__, $request->getRequestInfo(), $data); - - if ($request->getRequestInfo('http_code') == 200) { - $result = json_decode($data, true); - Cache::set($cacheKey, $result, 1800); - return $result; - } - return false; - } - /** * Decodes a response from JSON into an associative array. * diff --git a/modify.sql b/modules/servers/VirtFusionDirect/modify.sql similarity index 100% rename from modify.sql rename to modules/servers/VirtFusionDirect/modify.sql diff --git a/modules/servers/VirtFusionDirect/templates/js/module.js b/modules/servers/VirtFusionDirect/templates/js/module.js index b144642..9a79f99 100644 --- a/modules/servers/VirtFusionDirect/templates/js/module.js +++ b/modules/servers/VirtFusionDirect/templates/js/module.js @@ -663,31 +663,6 @@ function vfLoadSelfServiceUsage(serviceId, systemUrl) { }); } -function vfLoadSelfServiceReport(serviceId, systemUrl) { - $.ajax({ - type: "GET", - dataType: "json", - url: vfUrl(systemUrl, serviceId, "selfServiceReport") - }).done(function (response) { - if (response.success && response.data) { - var data = response.data.data || response.data; - var tbody = $("#vf-ss-usage-table"); - tbody.empty(); - - var items = data.items || data.report || []; - if (Array.isArray(items) && items.length > 0) { - $.each(items, function (i, item) { - var desc = item.description || item.name || "Item"; - var cost = item.cost !== undefined ? parseFloat(item.cost).toFixed(2) : "-"; - tbody.append('' + $('').text(desc).html() + '' + $('').text(cost).html() + ''); - }); - } else { - tbody.append('No report data available'); - } - } - }); -} - function vfAddCredit(serviceId, systemUrl) { var amount = $("#vf-ss-credit-amount").val(); var alertDiv = $("#vf-selfservice-alert"); diff --git a/scripts/generate-endpoint-doc.sh b/scripts/generate-endpoint-doc.sh deleted file mode 100755 index a57ae22..0000000 --- a/scripts/generate-endpoint-doc.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Generate API endpoint documentation from PHP source files -# Usage: bash scripts/generate-endpoint-doc.sh > docs/API-ENDPOINTS.md - -MODULE_DIR="modules/servers/VirtFusionDirect" - -echo "# VirtFusion WHMCS Module — API Endpoints" -echo "" -echo "Auto-generated from source code. Do not edit manually." -echo "" -echo "| Endpoint Pattern | HTTP Method | PHP File | Function |" -echo "|---|---|---|---|" - -# Extract API URL patterns from PHP files -grep -rn "->get\|->post\|->put\|->patch\|->delete" "$MODULE_DIR/lib/" 2>/dev/null | \ - grep -oP "(?<=>)(get|post|put|patch|delete)\(.*?'[^']*'" | \ - while IFS= read -r line; do - method=$(echo "$line" | grep -oP "^(get|post|put|patch|delete)" | tr '[:lower:]' '[:upper:]') - url=$(echo "$line" | grep -oP "'[^']*'" | tr -d "'") - echo "| \`$url\` | $method | - | - |" - done - -echo "" -echo "## Client Endpoints (client.php)" -echo "" -echo "| Action | Description |" -echo "|---|---|" - -grep -n "case '" "$MODULE_DIR/client.php" 2>/dev/null | \ - while IFS= read -r line; do - action=$(echo "$line" | grep -oP "case '\K[^']+") - echo "| \`$action\` | - |" - done