Files
server-manager/src/Controllers/ApiController.php
Agent 470f2e9404 Change Android login to username/password
- Add POST /api/auth/login endpoint on server side
- Update Android login screen to username/password fields
- Hardcode production URL https://servermanager.devlab.lat
- Rebuild APK with login changes
2026-06-07 18:05:00 -04:00

364 lines
10 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace ServerManager\Controllers;
use ServerManager\Core\App;
use ServerManager\Core\Session;
use ServerManager\Models\Server;
use ServerManager\Models\User;
use ServerManager\Services\MonitoringService;
use ServerManager\Services\SSHService;
use ServerManager\Services\AuditService;
use ServerManager\Models\CommandHistory;
use ServerManager\Core\Validator;
class ApiController
{
private \ServerManager\Core\View $view;
public function __construct()
{
$this->view = App::getInstance()->getView();
}
private function authenticateRequest(): ?array
{
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
$token = str_replace('Bearer ', '', $token);
if (empty($token) && isset($_GET['api_token'])) {
$token = $_GET['api_token'];
}
if (empty($token)) {
$this->view->json(['error' => 'API token is required'], 401);
}
$userModel = new User();
$user = $userModel->findByApiToken($token);
if (!$user) {
$this->view->json(['error' => 'Invalid API token'], 401);
}
return $user;
}
public function status(): void
{
$this->authenticateRequest();
$monitoringService = new MonitoringService();
$stats = $monitoringService->getDashboardStats();
$this->view->json([
'success' => true,
'data' => [
'application' => 'ServerManager',
'version' => '1.0.0',
'stats' => $stats,
'timestamp' => date('c'),
],
]);
}
public function servers(): void
{
$user = $this->authenticateRequest();
$serverModel = new Server();
$page = (int) ($_GET['page'] ?? 1);
$perPage = (int) ($_GET['per_page'] ?? 20);
$search = $_GET['search'] ?? '';
$filters = [];
if ($search) $filters['search'] = $search;
$accessibleIds = $serverModel->getAccessibleServerIds((int) $user['id']);
$filters['accessible_ids'] = !empty($accessibleIds) ? $accessibleIds : [-1];
$servers = $serverModel->getAll($page, $perPage, $filters);
$this->view->json([
'success' => true,
'data' => $servers['data'],
'pagination' => [
'page' => $servers['page'],
'per_page' => $servers['per_page'],
'total' => $servers['total'],
'total_pages' => $servers['total_pages'],
],
]);
}
public function serverDetail(int $id): void
{
$user = $this->authenticateRequest();
$serverModel = new Server();
$server = $serverModel->findById($id);
if (!$server) {
$this->view->json(['error' => 'Server not found'], 404);
}
if (!$serverModel->canView($id, (int) $user['id'])) {
$this->view->json(['error' => 'Server not found'], 404);
}
$monitoringService = new MonitoringService();
$metrics = $monitoringService->getLatestMetrics($id);
$this->view->json([
'success' => true,
'data' => [
'server' => $server,
'metrics' => $metrics,
],
]);
}
public function serverAdd(): void
{
$user = $this->authenticateRequest();
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$validator = new Validator();
$rules = [
'name' => 'required|min:2|max:100',
'ip_address' => 'required',
'ssh_port' => 'required|numeric|port',
'ssh_user' => 'required|min:2|max:50',
];
if (!$validator->validate($input, $rules)) {
$this->view->json(['error' => $validator->getFirstError()], 400);
}
$serverModel = new Server();
$id = $serverModel->create($input);
$this->view->json([
'success' => true,
'message' => 'Server created successfully.',
'server_id' => $id,
], 201);
}
public function serverUpdate(int $id): void
{
$user = $this->authenticateRequest();
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$serverModel = new Server();
$server = $serverModel->findById($id);
if (!$server) {
$this->view->json(['error' => 'Server not found'], 404);
}
if (!$serverModel->canManage($id, (int) $user['id'])) {
$this->view->json(['error' => 'Forbidden'], 403);
}
$serverModel->update($id, $input);
$this->view->json([
'success' => true,
'message' => 'Server updated successfully.',
]);
}
public function serverDelete(int $id): void
{
$user = $this->authenticateRequest();
$serverModel = new Server();
$server = $serverModel->findById($id);
if (!$server) {
$this->view->json(['error' => 'Server not found'], 404);
}
if (!$serverModel->canManage($id, (int) $user['id'])) {
$this->view->json(['error' => 'Forbidden'], 403);
}
$serverModel->delete($id);
$this->view->json([
'success' => true,
'message' => 'Server deleted successfully.',
]);
}
public function serverMetrics(int $id): void
{
$user = $this->authenticateRequest();
$serverModel = new Server();
if (!$serverModel->canView($id, (int) $user['id'])) {
$this->view->json(['error' => 'Server not found'], 404);
}
$monitoringService = new MonitoringService();
$hours = (int) ($_GET['hours'] ?? 24);
if ($hours > 168) $hours = 168;
$metrics = $monitoringService->getHistoricalMetrics($id, $hours);
$this->view->json([
'success' => true,
'data' => $metrics,
]);
}
public function executeCommand(int $id): void
{
$user = $this->authenticateRequest();
$serverModel = new Server();
if (!$serverModel->canView($id, (int) $user['id'])) {
$this->view->json(['error' => 'Server not found'], 404);
}
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$command = $input['command'] ?? '';
if (empty($command)) {
$this->view->json(['error' => 'Command is required'], 400);
}
$serverModel = new Server();
$server = $serverModel->findById($id);
if (!$server) {
$this->view->json(['error' => 'Server not found'], 404);
}
try {
$ssh = new SSHService();
if (!$ssh->connect($server)) {
throw new \RuntimeException('Could not establish SSH connection');
}
$result = $ssh->exec($command);
$ssh->disconnect();
$commandHistory = new CommandHistory();
$commandHistory->log(
$id,
$user['id'],
$command,
$result['output'],
$result['exit_status'] ?? 0
);
$auditService = new AuditService();
$auditService->logServerAction($id, 'api_command_executed', $command);
$this->view->json([
'success' => true,
'data' => [
'output' => $result['output'],
'exit_status' => $result['exit_status'],
'stderr' => $result['stderr'],
],
]);
} catch (\Throwable $e) {
$this->view->json([
'error' => 'Command execution failed: ' . $e->getMessage(),
], 500);
}
}
public function users(): void
{
$currentUser = $this->authenticateRequest();
if ($currentUser['role'] !== 'super_admin' && $currentUser['role'] !== 'admin') {
$this->view->json(['error' => 'Forbidden'], 403);
}
$userModel = new User();
$users = $userModel->getAll(1, 100);
$this->view->json([
'success' => true,
'data' => $users['data'],
]);
}
public function refreshAllMetrics(): void
{
$user = $this->authenticateRequest();
$monitoringService = new MonitoringService();
$results = $monitoringService->collectAllMetrics();
$this->view->json([
'success' => true,
'data' => $results,
]);
}
public function login(): void
{
$input = json_decode(file_get_contents('php://input'), true) ?? $_POST;
$validator = new Validator();
$rules = [
'username' => 'required',
'password' => 'required|min:6',
];
if (!$validator->validate($input, $rules)) {
$this->view->json([
'success' => false,
'error' => $validator->getFirstError(),
], 400);
return;
}
$userModel = new User();
$user = $userModel->authenticate($input['username'], $input['password']);
if (!$user) {
$auditService = new AuditService();
$auditService->logLogin($input['username'], false, 'Invalid credentials');
$this->view->json([
'success' => false,
'error' => 'Invalid username or password.',
], 401);
return;
}
$token = $user['api_token'];
if (empty($token)) {
$security = new \ServerManager\Core\Security();
$token = $security->generateApiToken();
$userModel->update((int) $user['id'], ['api_token' => $token]);
}
$auditService = new AuditService();
$auditService->logLogin($input['username'], true);
$this->view->json([
'success' => true,
'data' => [
'token' => $token,
'user' => [
'id' => (int) $user['id'],
'username' => $user['username'],
'email' => $user['email'],
'role' => $user['role'],
],
],
]);
}
}