# WHMCS VPSManager Módulo VPSManager para WHMCS con controles de reinicio, parada, arranque, firewall, snapshots, suspensión/reactivación, enlace al panel y métricas Graphite de servicios LX. Esta versión parte del módulo facilitado y conserva su comportamiento de provisioning. `CreateAccount` ya devolvía `success` sin provisionar; `TerminateAccount`, `ChangePackage` y `ChangePassword` estaban vacíos y siguen así. No se debe asumir que estas funciones estén implementadas. El tratamiento de errores de los comandos de provisioning sigue siendo el heredado: un `success` no acredita por sí solo que el backend ejecutó la acción. No se ha reescrito suspensión/reactivación ni su contrato HTTP. ## Requisitos e instalación - PHP 8.1 o posterior con cURL, JSON y certificados CA actualizados. Probado con PHP 8.1. - WHMCS con módulos de provisioning y los custom fields actuales del producto. - Acceso HTTPS desde PHP a Graphite. Las consultas no las realiza el navegador. - La versión y la plantilla reales de WHMCS no se han facilitado. Validar el módulo en staging antes de sustituirlo en producción; las pruebas locales no equivalen a esa validación. Instalar `vpsmanager.php`, `lib/`, `WhmcsHelpers/`, `templates/` y `assets/` dentro de `/modules/servers/vpsmanager/`. Mantener el nombre del directorio `vpsmanager`. No instalar `.git/`, `tests/` ni `tools/` en el servidor web. Los ficheros vacíos `logo.png` y `hooks.php` del original no se distribuyen. No se cambia el orden de las opciones existentes del módulo ni los nombres de sus custom fields. Es obligatorio que `uuid`, `vtype`, `gzid`, `url`, `username` y `password` sean campos administrados por el servidor/administrador, no editables ni mostrados al cliente. Revisar la configuración de custom fields y la plantilla WHMCS: el módulo ya no imprime contraseñas, pero no puede corregir una plantilla externa que imprima esos campos. ## Configuración externa Copiar `config.example.php` fuera del árbol web y del repositorio, normalmente a: ```text /etc/open6hosting/whmcs-vpsmanager.php ``` La variable de entorno `O6H_VPSMANAGER_CONFIG` permite usar otra ruta absoluta. Si se utiliza PHP-FPM, comprobar que el pool realmente transmite esa variable. - `vpsmanager_api_url`: base de la API VPSManager, sin barra final. Se admite HTTP para conservar la compatibilidad con la API original; preferir HTTPS o transporte privado protegido. No deshabilitar la verificación TLS. - `graphite_url`: base HTTPS de Graphite, sin `/render/`, credenciales embebidas, query string ni fragmento. La implementación añade `/render/`. - `graphs_url`: base HTTPS de la vista completa antigua, opcional. - `graphs_authorization_confirmed`: poner a `true` únicamente tras comprobar que la vista completa autentica al cliente y autoriza el VPS solicitado. Con URL ausente o sin esa comprobación se conserva el botón **Graphs**, desactivado. El UUID en la URL no es un control de acceso. Las nuevas tres gráficas no dependen de esa vista externa. Configurar solamente los valores privados reales en el fichero externo, nunca en el archivo de ejemplo. No se han incluido valores de producción en este repositorio. Permisos recomendados: directorio `0750`, fichero `0640`, propietario administrador y un grupo que permita lectura al usuario real de PHP-FPM/servidor web. Adaptar usuario y grupo a la instalación. PHP no debe poder escribir esa configuración. Nunca `chmod 777`. La configuración PHP es código de confianza controlado por el administrador. La falta de configuración no debe generar un fatal del módulo: las métricas muestran un aviso discreto, los comandos que requieren la API devuelven un error genérico y los selectores de nodos/paquetes quedan vacíos. No guardar una configuración de producto hasta restablecer estos selectores. ## Métricas LX Solo `vtype = lx`. El UUID debe tener formato hexadecimal `8-4-4-4-12`, por ejemplo el UUID ficticio `00000000-0000-4000-8000-000000000001`. Los targets internos son: | Gráfica / serie | Target bajo `lx..` | Transformación | | --- | --- | --- | | Carga / 1 min | `load.load.shortterm` | Ninguna | | Carga / 5 min | `load.load.midterm` | Ninguna | | Carga / 15 min | `load.load.longterm` | Ninguna | | Memoria usada | `memory.memory-used` | Bytes, presentación IEC | | Memoria libre | `memory.memory-free` | Bytes, usada para calcular el total | | Red RX | `interface-eth0.if_octets.rx` | Bytes/s → bits/s, una vez al presentar | | Red TX | `interface-eth0.if_octets.tx` | Bytes/s → bits/s, una vez al presentar | La carga **no es porcentaje de CPU**. Memoria total = usada + libre en el mismo timestamp; porcentaje = usada / total × 100 si ambos datos existen y el total es positivo. No hay una capacidad fija. Una serie de memoria ausente no se interpreta como cero. La red ya contiene tasas: no se aplica `derivative` ni `nonNegativeDerivative`, ni se calcula volumen mensual. Se agrupan los siete targets en una petición, con `format=json`, `until=now` y `maxDataPoints=1000`. Se conserva la resolución devuelta por Graphite, los timestamps y los huecos `null`; no se unen las líneas a través de esos huecos. El resumen corresponde al timestamp más reciente de la gráfica y muestra su fecha/hora local del navegador; no sustituye un dato ausente por una muestra antigua. No existe actualización automática. Rangos: `1h`, `6h`, **`24h` por defecto**, `7d`, `30d`. Cualquier otra entrada cae a `24h`. Los enlaces recargan `clientarea.php?action=productdetails&id=&metrics_range=`. No hay endpoint AJAX ni endpoint de métricas accesible directamente. WHMCS autoriza el servicio antes de invocar `vpsmanager_ClientArea`; el módulo solo toma UUID/tipo de `$params` y nunca de GET/POST. Cambiar `id` vuelve a pasar por la autorización de WHMCS. PHP mantiene verificación TLS y no sigue redirecciones. Connect timeout 3 s, total 5 s, respuesta limitada a 2 MiB. Un fallo HTTP, timeout, JSON inválido o dato mal formado muestra **Las métricas no están disponibles temporalmente.** sin bloquear los demás controles. Las series ausentes pueden convivir con las disponibles. No se registran cuerpos HTTP, excepciones detalladas, configuración, credenciales ni parámetros completos de WHMCS. No se incluyen credenciales de Graphite en la implementación. Esta versión requiere que el servidor WHMCS pueda acceder al Render API con la configuración de acceso de la red; si la instalación exige un mecanismo adicional de autenticación, debe integrarse del lado servidor antes del despliegue. Nunca añadir tokens a URLs públicas. ## Interfaz y acceso al panel Las vistas y la lógica Graphite están separadas de las operaciones existentes. Las tres cards usan clases compatibles con Bootstrap y estilos limitados al módulo. Se reutiliza `window.Chart` si es Chart.js 4.x; en otro caso se carga Chart.js **4.4.8** localmente y se restaura cualquier `window.Chart` previo. No se consultan CDNs en visitas. No se ha podido inspeccionar la plantilla instalada; comprobar compatibilidad en staging. El eje temporal numérico permite timestamps irregulares sin adaptador de fechas externo. **Antes:** el módulo insertaba usuario/contraseña ISPConfig en JavaScript y los enviaba por AJAX desde el navegador, tanto en cliente como en administración. **Después:** **Acceder al Panel** abre únicamente el login HTTPS en otra pestaña, con `noopener noreferrer`. No se incorpora usuario ni contraseña. No hay autologin: el código recibido no incluía un mecanismo SSO seguro. No se ha inventado ninguno. Las URLs inseguras, con credenciales, query string o fragmentos no generan enlaces activos. Las plantillas de acciones conservan las operaciones y añaden escape HTML y el token CSRF de WHMCS. El módulo depende del dispatcher de WHMCS para autenticación, autorización y validación del token. No exponer las acciones mediante un endpoint personalizado. ## Desarrollo y pruebas Desde la raíz del repositorio: ```bash find . -type f -name '*.php' -not -path './.git/*' -print0 | xargs -0 -n1 php -n -l php -n tests/run.php node tests/frontend.cjs node --check assets/metrics.js python3 tools/audit.py ``` `php -n` es deliberado: el test sustituye cURL por dobles y no realiza ninguna conexión. Cubre configuración, UUID/tipo/rangos, fallos HTTP/JSON/timeout, respuesta grande, series parciales, huecos, alineación temporal, cálculos, ausencia de secretos en HTML, manipulación GET/POST y rutas/payloads de las operaciones existentes. Los fixtures usan dominios `.invalid`, IPs de documentación y contraseñas ficticias identificadas con `FAKE_`. Los tests JS verifican unidades, huecos, carga local y conservación de otra versión de Chart.js. `tools/scope-chart.cjs` genera el bundle aislado a partir del fichero upstream, cuya licencia MIT y procedencia constan en `assets/Chart.js.LICENSE.md` y `assets/THIRD-PARTY.md`. No confundir esa licencia con una licencia para nuestro proyecto. La prueba de aislamiento de UUID invoca el hook con los parámetros de un servicio y peticiones GET/POST que intentan elegir otro. La prueba end-to-end del dispatcher requiere WHMCS real: con dos clientes y servicios distintos, verificar que el cliente A no puede acceder al `id` del B, con cualquiera de los cinco rangos, ni alterar los campos UUID/tipo. Verificar además token CSRF de las acciones, biblioteca/tema instalado, CSP y los controles en staging. No se han ejecutado reinicios, restauraciones o suspensiones sobre infraestructura real. ## Historial, publicación y licencia El repositorio público debe empezar con un único commit limpio, sin importar historial anterior. No copiar el ZIP original, backups, configuración real ni el directorio de trabajo. `tools/audit.py` revisa árbol, índice o archivos versionados; revisar también sus límites en `AUDIT.md`. Ningún escáner prueba la ausencia absoluta de todos los posibles secretos. Destino previsto: `https://gitea.open6hosting.com/Open6Hosting/whmcs-vpsmanager`. No hacer force-push sobre un repositorio existente. **Licencia del proyecto pendiente de decisión del titular.** No se concede una licencia open source por defecto. Se conserva el aviso de copyright original de `WhmcsHelpers/CustomField.php`; confirmar también los derechos de redistribución de ese helper antes de declarar una licencia global. La licencia de Chart.js solo cubre ese componente de terceros. ## Referencias de integración - [WHMCS: Client Area Output](https://developers.whmcs.com/provisioning-modules/client-area-output) - [WHMCS: Module Parameters](https://developers.whmcs.com/provisioning-modules/module-parameters) - [WHMCS: Module Logging](https://developers.whmcs.com/provisioning-modules/module-logging) - [Graphite Render API](https://graphite.readthedocs.io/en/latest/render_api.html) - [Chart.js: ejes numéricos](https://www.chartjs.org/docs/latest/axes/cartesian/linear.html)