diff --git a/routes/web.php b/routes/web.php index 3e6eaa1..67764cc 100755 --- a/routes/web.php +++ b/routes/web.php @@ -54,6 +54,8 @@ $router->get('/servers/:id/ssl', [ServerController::class, 'ssl'], [AuthMiddlewa $router->post('/servers/:id/ssl', [ServerController::class, 'sslAction'], [AuthMiddleware::class, CSRFMiddleware::class]); $router->get('/servers/:id/users', [ServerController::class, 'systemUsers'], [AuthMiddleware::class]); $router->post('/servers/:id/users', [ServerController::class, 'systemUsersAction'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->get('/servers/:id/services', [ServerController::class, 'services'], [AuthMiddleware::class]); +$router->post('/servers/:id/services', [ServerController::class, 'servicesAction'], [AuthMiddleware::class, CSRFMiddleware::class]); $router->get('/servers/:id/database', [ServerController::class, 'database'], [AuthMiddleware::class]); $router->post('/servers/:id/database', [ServerController::class, 'databaseAction'], [AuthMiddleware::class, CSRFMiddleware::class]); $router->post('/servers/:id/nginx', [ServerController::class, 'nginxAction'], [AuthMiddleware::class, CSRFMiddleware::class]); diff --git a/src/Controllers/ServerController.php b/src/Controllers/ServerController.php index 4be2504..3f384a7 100755 --- a/src/Controllers/ServerController.php +++ b/src/Controllers/ServerController.php @@ -1475,6 +1475,87 @@ class ServerController $this->view->json(['success' => true, 'data' => $result]); } + public function services(int $id): void + { + $server = $this->serverModel->findById($id); + if (!$server) { $this->view->error(404); } + + try { + $ssh = new SSHService(); + if (!$ssh->connect($server)) { + throw new \RuntimeException('Could not establish SSH connection'); + } + $this->auditService->logCredentialAccess($id, 'View services'); + + $listResult = $ssh->exec("systemctl list-units --type=service --all --no-pager --no-legend 2>/dev/null | awk '{print \$1, \$3}'"); + $serviceNames = []; + foreach (explode("\n", trim($listResult['output'] ?? '')) as $line) { + if (preg_match('/^(\S+)\s+(\S+)/', $line, $m)) { + $serviceNames[$m[1]] = $m[2] === 'active' ? 'active' : ($m[2] === 'inactive' ? 'inactive' : 'other'); + } + } + $enabledNames = []; + $enabledResult = $ssh->exec("systemctl list-unit-files --type=service --no-pager --no-legend 2>/dev/null | awk '\$2==\"enabled\"{print \$1}'"); + foreach (explode("\n", trim($enabledResult['output'] ?? '')) as $line) { + $enabledNames[trim($line)] = true; + } + $services = []; + foreach ($serviceNames as $name => $status) { + $services[] = [ + 'name' => $name, + 'status' => $status, + 'enabled' => isset($enabledNames[$name]), + ]; + } + $ssh->disconnect(); + + $this->view->display('servers.services', [ + 'title' => 'Services - ' . $server['name'] . ' - ServerManager', + 'server' => $server, + 'services' => $services, + ]); + } catch (\Throwable $e) { + Session::setFlash('error', 'Failed to get services: ' . $e->getMessage()); + $this->view->redirect("/servers/{$id}"); + } + } + + public function servicesAction(int $id): void + { + $server = $this->serverModel->findById($id); + if (!$server) { + $this->view->json(['success' => false, 'message' => 'Server not found'], 404); + } + + $action = $_POST['action'] ?? ''; + $service = $_POST['service'] ?? ''; + $allowed = ['start', 'stop', 'restart', 'reload', 'enable', 'disable']; + + if (empty($service) || !in_array($action, $allowed, true)) { + $this->view->json(['success' => false, 'message' => 'Invalid action or service']); + } + + try { + $ssh = new SSHService(); + if (!$ssh->connect($server)) { + throw new \RuntimeException('Could not establish SSH connection'); + } + $this->auditService->logServerAction($id, "service_{$action}", "{$service} {$action}"); + + $result = $ssh->exec("sudo systemctl {$action} {$service} 2>&1; echo EXIT:\$?"); + $exitCode = (int) trim(explode('EXIT:', $result['output'] ?? 'EXIT:-1')[1] ?? '-1'); + $ssh->disconnect(); + + $this->view->json([ + 'success' => $exitCode === 0, + 'message' => $exitCode === 0 ? "Service '{$service}' {$action}ed." : "Failed to {$action} '{$service}'.", + 'output' => trim($result['output']), + ]); + } catch (\Throwable $e) { + $this->view->json(['success' => false, 'message' => 'Failed: ' . $e->getMessage()]); + } + } + public function systemUsers(int $id): void { $server = $this->serverModel->findById($id); diff --git a/views/servers/services.php b/views/servers/services.php new file mode 100644 index 0000000..8c2716a --- /dev/null +++ b/views/servers/services.php @@ -0,0 +1,140 @@ + + +
= htmlspecialchars($server['name']) ?> · = count($services) ?> services
+| Service | Status | Actions |
|---|---|---|
| + | + | + |