feat: major enhancement — OS gallery, server rename, traffic chart, backups, VNC toggle, password reset, Redis caching, UX improvements
- Remove client IP removal capability (keep backend methods removed too) - Add copy-to-clipboard buttons for IP addresses with tooltip feedback - Replace OS dropdown with tile gallery (grouped, searchable, brand colors, EOL badges) in rebuild panel and checkout page - Add inline server rename with friendly name generator and RFC 1123 validation - Add traffic statistics canvas chart with responsive resize in resources panel - Add backup listing timeline in manage panel with show-all expansion - Add VNC enable/disable toggle with connection details and password copy - Add server root password reset with auto-clipboard copy (never displayed) - Add skeleton loading placeholders, action cooldowns (power 3s, rebuild 30s), progress indicator with elapsed timer - Sanitize all client-facing error messages (no raw API errors exposed) - Convert all state-mutating AJAX calls from GET to POST - Add explicit break after all output() calls in client.php - Add Redis-backed API response caching (Cache.php): OS templates 10min, traffic/backups 2min, currencies 30min, packages 10min - Add GitHub Actions workflow for weekly VirtFusion API change detection - Move cache busting step after semantic-release in publish workflow - Add endpoint doc generator script and OpenAPI baseline placeholder - Improve hostname generation entropy (bin2hex random_bytes) - Add .superpowers/ to .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
131
modules/servers/VirtFusionDirect/lib/Cache.php
Normal file
131
modules/servers/VirtFusionDirect/lib/Cache.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace WHMCS\Module\Server\VirtFusionDirect;
|
||||
|
||||
class Cache
|
||||
{
|
||||
const PREFIX = 'vfd:';
|
||||
|
||||
/** @var \Redis|null */
|
||||
private static $redis = null;
|
||||
|
||||
/** @var bool */
|
||||
private static $available = true;
|
||||
|
||||
/**
|
||||
* Get a Redis connection, or null if unavailable.
|
||||
*
|
||||
* @return \Redis|null
|
||||
*/
|
||||
private static function redis()
|
||||
{
|
||||
if (!self::$available) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self::$redis !== null) {
|
||||
return self::$redis;
|
||||
}
|
||||
|
||||
if (!class_exists('Redis')) {
|
||||
self::$available = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$redis = new \Redis();
|
||||
$redis->connect('127.0.0.1', 6379, 1.0);
|
||||
self::$redis = $redis;
|
||||
return $redis;
|
||||
} catch (\Exception $e) {
|
||||
self::$available = false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cached value.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed|null Returns null on miss
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $redis->get(self::PREFIX . $key);
|
||||
if ($data === false) {
|
||||
return null;
|
||||
}
|
||||
return json_decode($data, true);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a value in cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $ttl Time-to-live in seconds
|
||||
*/
|
||||
public static function set($key, $value, $ttl = 300)
|
||||
{
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$redis->setex(self::PREFIX . $key, $ttl, json_encode($value));
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail — caching is optional
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a cached value.
|
||||
*
|
||||
* @param string $key
|
||||
*/
|
||||
public static function forget($key)
|
||||
{
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$redis->del(self::PREFIX . $key);
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$keys = $redis->keys(self::PREFIX . $pattern);
|
||||
if (!empty($keys)) {
|
||||
$redis->del($keys);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user