chore: full project audit cleanup, dead code removal, and documentation update
Dead code removed: - Module.php: remove assignBackupPlan(), getSelfServiceCurrencies() (no callers) - Cache.php: remove forgetPattern() (no callers, no-op on filesystem) - module.js: remove vfLoadSelfServiceReport() (no UI trigger) Stale files removed: - .releaserc.json (orphaned, conflicts with tag-based workflow) - .github/workflows/api-sync-check.yml (baseline never populated) - docs/openapi-baseline.yaml (placeholder stub) - scripts/generate-endpoint-doc.sh (broken grep patterns) Security fixes: - AdminHTML: cast $serverId to (int), cast $serviceId to (int) - admin.php: add explicit break after every output() call, sanitize error msgs File hygiene: - Move modify.sql into modules/servers/VirtFusionDirect/ (matches README docs) - Fix CHANGELOG.md: remove duplicate 1.0.0 entry, clean up mixed git host URLs Documentation: - CLAUDE.md: full rewrite with current architecture, Cache class, development rules (try/catch, ownership validation, HTTP methods, caching policy) - README.md: remove stale IPv4 removal references, add new features (traffic, backups, VNC toggle, password reset, OS gallery, copy buttons), add Cache.php to file structure, remove "Primary IPv4 Protection" known issue Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
65
.github/workflows/api-sync-check.yml
vendored
65
.github/workflows/api-sync-check.yml
vendored
@@ -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\n<details><summary>Diff</summary>\n\n\`\`\`diff\n${diff}\n\`\`\`\n</details>\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 }}
|
||||
@@ -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}"
|
||||
}]
|
||||
]
|
||||
}
|
||||
69
CHANGELOG.md
69
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)
|
||||
|
||||
49
CLAUDE.md
49
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, `$('<span>').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
|
||||
|
||||
22
README.md
22
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
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ EOT;
|
||||
|
||||
public static function serverId($serverId)
|
||||
{
|
||||
$serverId = (int) $serverId;
|
||||
return <<<EOT
|
||||
<input type="text" class="form-control input-200 input-inline" name="modulefields[0]" size="20" value="${serverId}" />
|
||||
<span class="text-info"> Changing the Sever ID manually is not recommended. Alterations to this field are usually handled automatically.</span>
|
||||
@@ -34,6 +35,7 @@ EOT;
|
||||
public static function serverInfo($systemUrl, $serviceId)
|
||||
{
|
||||
$systemUrl = htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8');
|
||||
$serviceId = (int) $serviceId;
|
||||
$cacheV = time();
|
||||
return <<<EOT
|
||||
<link href="${systemUrl}modules/servers/VirtFusionDirect/templates/css/module.css?v=${cacheV}" rel="stylesheet">
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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('<tr><td>' + $('<span>').text(desc).html() + '</td><td class="text-right">' + $('<span>').text(cost).html() + '</td></tr>');
|
||||
});
|
||||
} else {
|
||||
tbody.append('<tr><td colspan="2" class="text-muted">No report data available</td></tr>');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function vfAddCredit(serviceId, systemUrl) {
|
||||
var amount = $("#vf-ss-credit-amount").val();
|
||||
var alertDiv = $("#vf-selfservice-alert");
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user