diff --git a/cp/app/Controllers/ReportsController.php b/cp/app/Controllers/ReportsController.php index 0a7a7f3..a513b01 100644 --- a/cp/app/Controllers/ReportsController.php +++ b/cp/app/Controllers/ReportsController.php @@ -7,6 +7,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Container\ContainerInterface; use Nyholm\Psr7\Stream; +use Utopia\System\System; class ReportsController extends Controller { @@ -100,4 +101,125 @@ class ReportsController extends Controller // Output the CSV content to the response body return $response->withBody($stream); } + + public function serverHealth(Request $request, Response $response) + { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + $csrfTokenName = $this->container->get('csrf')->getTokenName(); + $csrfTokenValue = $this->container->get('csrf')->getTokenValue(); + + $system = new System(); + + $serverHealth = [ + 'getCPUCores' => $system->getCPUCores(), + 'getCPUUsage' => $system->getCPUUsage(), + 'getMemoryTotal' => $system->getMemoryTotal(), + 'getMemoryFree' => $system->getMemoryFree(), + 'getDiskTotal' => $system->getDiskTotal(), + 'getDiskFree' => $system->getDiskFree() + ]; + + $logFile = '/var/log/namingo/backup.log'; + + // Check if the file exists + if (!file_exists($logFile)) { + $backupSummary = "Backup log file not found."; + } else { + // Read and decode JSON file + $logData = json_decode(file_get_contents($logFile), true); + + if (json_last_error() !== JSON_ERROR_NONE || !is_array($logData)) { + $backupSummary = "Invalid JSON format in backup log file."; + } else { + // Start building the summary + $backupSummary = "Backup Summary:\n"; + $backupSummary .= "Timestamp: " . date('Y-m-d H:i:s', $logData['timestamp'] ?? time()) . "\n"; + $backupSummary .= "Duration: " . round($logData['duration'] ?? 0, 2) . " seconds\n"; + $backupSummary .= "Total Backups: " . ($logData['backupCount'] ?? 0) . "\n"; + $backupSummary .= "Failed Backups: " . ($logData['backupFailed'] ?? 0) . "\n"; + $backupSummary .= "Errors: " . ($logData['errorCount'] ?? 0) . "\n"; + + if (!empty($logData['backups'])) { + foreach ($logData['backups'] as $backup) { + $backupSummary .= "\nBackup: " . ($backup['name'] ?? 'Unknown') . "\n"; + $backupSummary .= "- Status: " . (($backup['status'] ?? 1) === 0 ? 'Success' : 'Failed') . "\n"; + $backupSummary .= "- Checks: " . ($backup['checks']['executed'] ?? 0) . " executed, " . ($backup['checks']['failed'] ?? 0) . " failed\n"; + $backupSummary .= "- Syncs: " . ($backup['syncs']['executed'] ?? 0) . " executed, " . ($backup['syncs']['failed'] ?? 0) . " failed\n"; + $backupSummary .= "- Cleanup: " . ($backup['cleanup']['executed'] ?? 0) . " executed, " . ($backup['cleanup']['failed'] ?? 0) . " failed\n"; + } + } + + if (!empty($logData['debug'])) { + $backupSummary .= "\nDebug Info (last 5 entries):\n"; + $debugEntries = array_slice($logData['debug'], -5); + foreach ($debugEntries as $entry) { + $backupSummary .= "- $entry\n"; + } + } + } + } + + return $this->view->render($response, 'admin/reports/serverHealth.twig', [ + 'serverHealth' => $serverHealth, + 'csrfTokenName' => $csrfTokenName, + 'csrfTokenValue' => $csrfTokenValue, + 'backupLog' => nl2br(htmlspecialchars($backupSummary)), + ]); + } + + public function clearCache(Request $request, Response $response): Response + { + if ($_SESSION["auth_roles"] != 0) { + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + $result = [ + 'success' => true, + 'message' => 'Cache cleared successfully!', + ]; + $cacheDir = '/var/www/cp/cache'; + + try { + // Check if the cache directory exists + if (!is_dir($cacheDir)) { + throw new RuntimeException('Cache directory does not exist.'); + } + + // Iterate through the files and directories in the cache directory + $files = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($cacheDir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $fileinfo) { + // Check if the parent directory name is exactly two letters/numbers long + if (preg_match('/^[a-zA-Z0-9]{2}$/', $fileinfo->getFilename()) || + preg_match('/^[a-zA-Z0-9]{2}$/', basename(dirname($fileinfo->getPathname())))) { + $action = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); + $action($fileinfo->getRealPath()); + } + } + + // Delete the two-letter/number directories themselves + $dirs = new \DirectoryIterator($cacheDir); + foreach ($dirs as $dir) { + if ($dir->isDir() && !$dir->isDot() && preg_match('/^[a-zA-Z0-9]{2}$/', $dir->getFilename())) { + rmdir($dir->getRealPath()); + } + } + } catch (Exception $e) { + $result = [ + 'success' => false, + 'message' => 'Error clearing cache: ' . $e->getMessage(), + ]; + } + + // Respond with the result as JSON + $response->getBody()->write(json_encode($result)); + return $response->withHeader('Content-Type', 'application/json')->withStatus(200); + } + } \ No newline at end of file diff --git a/cp/bootstrap/app.php b/cp/bootstrap/app.php index e0c1d84..ee4d2ae 100644 --- a/cp/bootstrap/app.php +++ b/cp/bootstrap/app.php @@ -266,6 +266,9 @@ $csrfMiddleware = function ($request, $handler) use ($container) { if ($path && $path === '/create-crypto-payment') { return $handler->handle($request); } + if ($path && $path === '/clear-cache') { + return $handler->handle($request); + } // If not skipped, apply the CSRF Guard return $csrf->process($request, $handler); diff --git a/cp/composer.json b/cp/composer.json index 1b40611..986a172 100644 --- a/cp/composer.json +++ b/cp/composer.json @@ -45,7 +45,8 @@ "giggsey/libphonenumber-for-php-lite": "^8.13", "egulias/email-validator": "^4.0", "utopia-php/messaging": "^0.12.0", - "brick/postcode": "^0.3.3" + "brick/postcode": "^0.3.3", + "utopia-php/system": "^0.9.0" }, "autoload": { "psr-4": { diff --git a/cp/resources/views/admin/reports/serverHealth.twig b/cp/resources/views/admin/reports/serverHealth.twig new file mode 100644 index 0000000..d78be9a --- /dev/null +++ b/cp/resources/views/admin/reports/serverHealth.twig @@ -0,0 +1,169 @@ +{% extends "layouts/app.twig" %} + +{% block title %}{{ __('Server Health') }}{% endblock %} + +{% block content %} + +
+ + + +
+
+
+
+ +
+
+
+

{{ __('CPU and Memory') }}

+
+
+
+
+

{{ __('CPU Cores') }}

+
{{ serverHealth.getCPUCores }}
+
+
+ {{ serverHealth.getCPUUsage }}% +
+
+
+
+ {{ serverHealth.getCPUUsage }}% +
+
+ +
+ +
+
+

{{ __('Memory') }}

+
{{ serverHealth.getMemoryFree }} {{ __('MB free of') }} {{ serverHealth.getMemoryTotal }} MB
+
+
+ {{ (serverHealth.getMemoryFree / serverHealth.getMemoryTotal * 100)|round(1) }}% +
+
+
+
+ {{ (serverHealth.getMemoryFree / serverHealth.getMemoryTotal * 100)|round(1) }}% +
+
+
+
+
+ + +
+
+
+

{{ __('Disk and Network') }}

+
+
+
+
+

{{ __('Disk Usage') }}

+
{{ serverHealth.getDiskFree }} {{ __('GB free of') }} {{ serverHealth.getDiskTotal }} GB
+
+
+ {{ (serverHealth.getDiskFree / serverHealth.getDiskTotal * 100)|round(1) }}% +
+
+
+
+ {{ (serverHealth.getDiskFree / serverHealth.getDiskTotal * 100)|round(1) }}% +
+
+
+
+
+
+ +
+
+
+
+

{{ __('Backup Log') }}

+
+
+
{{ backupLog }}
+
+
+
+
+
+
+
+ {% include 'partials/footer.twig' %} +
+ +{% endblock %} \ No newline at end of file diff --git a/cp/resources/views/layouts/app.twig b/cp/resources/views/layouts/app.twig index 69f6946..39d7d32 100644 --- a/cp/resources/views/layouts/app.twig +++ b/cp/resources/views/layouts/app.twig @@ -206,7 +206,7 @@ -
  • +
  • {{ __('Reports') }} + + {{ __('Server Health') }} + {% endif %} {{ __('Message Queue') }} @@ -307,6 +310,8 @@ {% include 'partials/js-tlds.twig' %} {% elseif route_is('profile') %} {% include 'partials/js-profile.twig' %} +{% elseif route_is('server') %} + {% include 'partials/js-server.twig' %} {% else %} {% include 'partials/js.twig' %} {% endif %} diff --git a/cp/resources/views/partials/js-server.twig b/cp/resources/views/partials/js-server.twig new file mode 100644 index 0000000..b7d5280 --- /dev/null +++ b/cp/resources/views/partials/js-server.twig @@ -0,0 +1,2 @@ + + diff --git a/cp/routes/web.php b/cp/routes/web.php index b25e476..10a0a31 100644 --- a/cp/routes/web.php +++ b/cp/routes/web.php @@ -108,6 +108,8 @@ $app->group('', function ($route) { $route->get('/reports', ReportsController::class .':view')->setName('reports'); $route->get('/export', ReportsController::class .':exportDomains')->setName('exportDomains'); + $route->get('/server', ReportsController::class .':serverHealth')->setName('serverHealth'); + $route->post('/clear-cache', ReportsController::class .':clearCache')->setName('clearCache'); $route->get('/invoices', FinancialsController::class .':invoices')->setName('invoices'); $route->get('/invoice/{invoice}', FinancialsController::class . ':viewInvoice')->setName('viewInvoice');