diff --git a/database/migrations/003_teams.sql b/database/migrations/003_teams.sql new file mode 100644 index 0000000..db90e81 --- /dev/null +++ b/database/migrations/003_teams.sql @@ -0,0 +1,47 @@ +-- ============================================================================ +-- ServerManager Teams +-- Team-based access control: teams group users and servers together +-- ============================================================================ + +CREATE TABLE IF NOT EXISTS `teams` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `description` TEXT DEFAULT NULL, + `created_by` INT UNSIGNED NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_name` (`name`), + KEY `idx_created_by` (`created_by`), + CONSTRAINT `fk_teams_created_by` FOREIGN KEY (`created_by`) + REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `team_user` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `team_id` INT UNSIGNED NOT NULL, + `user_id` INT UNSIGNED NOT NULL, + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_team_user` (`team_id`, `user_id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_team_user_team` FOREIGN KEY (`team_id`) + REFERENCES `teams` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_team_user_user` FOREIGN KEY (`user_id`) + REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +CREATE TABLE IF NOT EXISTS `team_server` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `team_id` INT UNSIGNED NOT NULL, + `server_id` INT UNSIGNED NOT NULL, + `role` ENUM('viewer', 'manager') NOT NULL DEFAULT 'viewer', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_team_server` (`team_id`, `server_id`), + KEY `idx_server_id` (`server_id`), + CONSTRAINT `fk_team_server_team` FOREIGN KEY (`team_id`) + REFERENCES `teams` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_team_server_server` FOREIGN KEY (`server_id`) + REFERENCES `servers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/routes/web.php b/routes/web.php index 1f0cd54..5988f68 100755 --- a/routes/web.php +++ b/routes/web.php @@ -9,6 +9,7 @@ use ServerManager\Controllers\ServerController; use ServerManager\Controllers\TerminalController; use ServerManager\Controllers\AdminController; use ServerManager\Controllers\ApiController; +use ServerManager\Controllers\TeamController; use ServerManager\Middleware\AuthMiddleware; use ServerManager\Middleware\CSRFMiddleware; use ServerManager\Middleware\RateLimitMiddleware; @@ -65,11 +66,17 @@ $router->get('/servers/:id/logs', [ServerController::class, 'logs'], [AuthMiddle $router->get('/servers/:id/metrics', [ServerController::class, 'metrics'], [AuthMiddleware::class]); $router->get('/sidebar/servers', [ServerController::class, 'sidebarServers'], [AuthMiddleware::class]); -$router->get('/team', [ServerController::class, 'team'], [AuthMiddleware::class]); -$router->get('/team/:id', [ServerController::class, 'teamServer'], [AuthMiddleware::class]); -$router->post('/team/:id/add-user', [ServerController::class, 'addTeamUser'], [AuthMiddleware::class, CSRFMiddleware::class]); -$router->post('/team/:id/remove-user', [ServerController::class, 'removeTeamUser'], [AuthMiddleware::class, CSRFMiddleware::class]); -$router->post('/team/:id/update-role', [ServerController::class, 'updateTeamUserRole'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->get('/teams', [TeamController::class, 'index'], [AuthMiddleware::class, RoleMiddleware::class]); +$router->get('/teams/create', [TeamController::class, 'create'], [AuthMiddleware::class, RoleMiddleware::class]); +$router->post('/teams/create', [TeamController::class, 'store'], [AuthMiddleware::class, CSRFMiddleware::class, RoleMiddleware::class]); +$router->get('/teams/:id', [TeamController::class, 'show'], [AuthMiddleware::class, RoleMiddleware::class]); +$router->post('/teams/:id/update', [TeamController::class, 'update'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/delete', [TeamController::class, 'delete'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/add-user', [TeamController::class, 'addUser'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/remove-user', [TeamController::class, 'removeUser'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/add-server', [TeamController::class, 'addServer'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/remove-server', [TeamController::class, 'removeServer'], [AuthMiddleware::class, CSRFMiddleware::class]); +$router->post('/teams/:id/update-server-role', [TeamController::class, 'updateServerRole'], [AuthMiddleware::class, CSRFMiddleware::class]); $router->get('/terminal/:id', [TerminalController::class, 'index'], [AuthMiddleware::class]); $router->post('/terminal/:id/execute', [TerminalController::class, 'execute'], [AuthMiddleware::class, CSRFMiddleware::class]); diff --git a/src/Controllers/ApiController.php b/src/Controllers/ApiController.php index 29a95d9..a922b12 100755 --- a/src/Controllers/ApiController.php +++ b/src/Controllers/ApiController.php @@ -77,9 +77,7 @@ class ApiController if ($search) $filters['search'] = $search; $accessibleIds = $serverModel->getAccessibleServerIds((int) $user['id']); - if (!empty($accessibleIds)) { - $filters['accessible_ids'] = $accessibleIds; - } + $filters['accessible_ids'] = !empty($accessibleIds) ? $accessibleIds : [-1]; $servers = $serverModel->getAll($page, $perPage, $filters); diff --git a/src/Controllers/DashboardController.php b/src/Controllers/DashboardController.php index cfa0fcb..22e3a48 100755 --- a/src/Controllers/DashboardController.php +++ b/src/Controllers/DashboardController.php @@ -31,11 +31,9 @@ class DashboardController $serverModel = new Server(); $userId = (int) Session::get('user_id'); $accessibleIds = $serverModel->getAccessibleServerIds($userId); - $servers = $serverModel->findAll(true); - $servers = array_filter($servers, function ($s) use ($accessibleIds) { + $servers = !empty($accessibleIds) ? array_values(array_filter($serverModel->findAll(true), function ($s) use ($accessibleIds) { return in_array((int) $s['id'], $accessibleIds, true); - }); - $servers = array_values($servers); + })) : []; $this->view->display('dashboard.index', [ 'title' => 'Dashboard - ServerManager', diff --git a/src/Controllers/ServerController.php b/src/Controllers/ServerController.php index ad015a1..74e02cf 100755 --- a/src/Controllers/ServerController.php +++ b/src/Controllers/ServerController.php @@ -64,9 +64,7 @@ class ServerController if ($group) $filters['group_name'] = $group; $accessibleIds = $this->serverModel->getAccessibleServerIds($userId); - if (!empty($accessibleIds)) { - $filters['accessible_ids'] = $accessibleIds; - } + $filters['accessible_ids'] = !empty($accessibleIds) ? $accessibleIds : [-1]; $servers = $this->serverModel->getAll($page, 15, $filters); $groups = $this->serverModel->getGroups(); @@ -1797,140 +1795,4 @@ class ServerController ]); } - public function team(): void - { - $userId = (int) Session::get('user_id'); - $servers = $this->serverModel->getOwnedServers($userId); - - $result = []; - foreach ($servers as $server) { - $memberCount = count($this->serverModel->getTeam((int) $server['id'])); - $result[] = [ - 'id' => $server['id'], - 'name' => $server['name'], - 'ip_address' => $server['ip_address'], - 'member_count' => $memberCount, - 'status' => $server['current_status'] ?? 'unknown', - ]; - } - - $this->view->display('servers.team', [ - 'title' => 'Team Management - ServerManager', - 'servers' => $result, - ]); - } - - public function teamServer(int $id): void - { - $userId = (int) Session::get('user_id'); - $server = $this->serverModel->findById($id); - - if (!$server || (int) $server['created_by'] !== $userId) { - $this->view->error(404); - } - - $members = $this->serverModel->getTeam($id); - - $userModel = new User(); - $users = $userModel->getAll(1, 999); - $availableUsers = []; - $memberIds = array_map(fn($m) => $m['user_id'], $members); - foreach ($users['data'] as $u) { - if (!in_array((int) $u['id'], $memberIds, true)) { - $availableUsers[] = $u; - } - } - - $this->view->display('servers.team_server', [ - 'title' => 'Team - ' . $server['name'] . ' - ServerManager', - 'server' => $server, - 'members' => $members, - 'availableUsers' => $availableUsers, - ]); - } - - public function addTeamUser(int $id): void - { - $userId = (int) Session::get('user_id'); - $server = $this->serverModel->findById($id); - - if (!$server || (int) $server['created_by'] !== $userId) { - $this->view->json(['success' => false, 'message' => 'Server not found'], 404); - } - - $targetUserId = (int) ($_POST['user_id'] ?? 0); - $role = $_POST['role'] ?? 'viewer'; - - if ($targetUserId <= 0) { - $this->view->json(['success' => false, 'message' => 'Invalid user']); - } - - if (!in_array($role, ['viewer', 'manager'], true)) { - $this->view->json(['success' => false, 'message' => 'Invalid role']); - } - - if ($this->serverModel->addUser($id, $targetUserId, $role)) { - $this->auditService->log('team_user_added', 'server', $id, [ - 'target_user_id' => $targetUserId, - 'role' => $role, - ]); - $this->view->json(['success' => true, 'message' => 'User added to server.']); - } - - $this->view->json(['success' => false, 'message' => 'User is already assigned to this server.']); - } - - public function removeTeamUser(int $id): void - { - $userId = (int) Session::get('user_id'); - $server = $this->serverModel->findById($id); - - if (!$server || (int) $server['created_by'] !== $userId) { - $this->view->json(['success' => false, 'message' => 'Server not found'], 404); - } - - $targetUserId = (int) ($_POST['user_id'] ?? 0); - - if ($targetUserId <= 0) { - $this->view->json(['success' => false, 'message' => 'Invalid user']); - } - - $this->serverModel->removeUser($id, $targetUserId); - - $this->auditService->log('team_user_removed', 'server', $id, [ - 'target_user_id' => $targetUserId, - ]); - - $this->view->json(['success' => true, 'message' => 'User removed from server.']); - } - - public function updateTeamUserRole(int $id): void - { - $userId = (int) Session::get('user_id'); - $server = $this->serverModel->findById($id); - - if (!$server || (int) $server['created_by'] !== $userId) { - $this->view->json(['success' => false, 'message' => 'Server not found'], 404); - } - - $targetUserId = (int) ($_POST['user_id'] ?? 0); - $role = $_POST['role'] ?? 'viewer'; - - if ($targetUserId <= 0) { - $this->view->json(['success' => false, 'message' => 'Invalid user']); - } - - if (!in_array($role, ['viewer', 'manager'], true)) { - $this->view->json(['success' => false, 'message' => 'Invalid role']); - } - - $this->serverModel->updateUserRole($id, $targetUserId, $role); - - $this->auditService->log('team_user_role_updated', 'server', $id, [ - 'target_user_id' => $targetUserId, - 'role' => $role, - ]); - - $this->view->json(['success' => true, 'message' => 'User role updated.']); - } } diff --git a/src/Controllers/TeamController.php b/src/Controllers/TeamController.php new file mode 100644 index 0000000..ed384f6 --- /dev/null +++ b/src/Controllers/TeamController.php @@ -0,0 +1,283 @@ +view = App::getInstance()->getView(); + $this->teamModel = new Team(); + $this->serverModel = new Server(); + $this->auditService = new AuditService(); + } + + public function index(): void + { + $teams = $this->teamModel->findAll(); + + $this->view->display('teams.index', [ + 'title' => 'Teams - ServerManager', + 'teams' => $teams, + ]); + } + + public function create(): void + { + $this->view->display('teams.create', [ + 'title' => 'Create Team - ServerManager', + ]); + } + + public function store(): void + { + $validator = new Validator(); + $rules = [ + 'name' => 'required|min:2|max:100', + 'description' => 'max:500', + ]; + + if (!$validator->validate($_POST, $rules)) { + Session::setFlash('error', $validator->getFirstError()); + $this->view->redirect('/teams/create'); + } + + $id = $this->teamModel->create([ + 'name' => $_POST['name'], + 'description' => $_POST['description'] ?? '', + ]); + + $this->auditService->log('team_created', 'team', $id, [ + 'name' => $_POST['name'], + ]); + + Session::setFlash('success', 'Team created successfully.'); + $this->view->redirect('/teams'); + } + + public function show(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->error(404); + } + + $members = $this->teamModel->getMembers($id); + $servers = $this->teamModel->getServers($id); + + $userModel = new User(); + $allUsers = $userModel->getAll(1, 999); + $memberIds = array_map(fn($m) => $m['user_id'], $members); + $availableUsers = array_filter($allUsers['data'], fn($u) => !in_array((int) $u['id'], $memberIds, true)); + + $userId = (int) Session::get('user_id'); + $ownedServers = $this->serverModel->getOwnedServers($userId); + $serverIds = array_map(fn($s) => (int) $s['id'], $servers); + $availableServers = array_filter($ownedServers, fn($s) => !in_array((int) $s['id'], $serverIds, true)); + + $this->view->display('teams.show', [ + 'title' => $team['name'] . ' - Teams - ServerManager', + 'team' => $team, + 'members' => $members, + 'servers' => $servers, + 'availableUsers' => $availableUsers, + 'availableServers' => $availableServers, + ]); + } + + public function update(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $validator = new Validator(); + $rules = [ + 'name' => 'required|min:2|max:100', + 'description' => 'max:500', + ]; + + if (!$validator->validate($_POST, $rules)) { + $this->view->json(['success' => false, 'message' => $validator->getFirstError()]); + } + + $this->teamModel->update($id, [ + 'name' => $_POST['name'], + 'description' => $_POST['description'] ?? '', + ]); + + $this->auditService->log('team_updated', 'team', $id, [ + 'name' => $_POST['name'], + ]); + + $this->view->json(['success' => true, 'message' => 'Team updated.']); + } + + public function delete(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $this->teamModel->delete($id); + + $this->auditService->log('team_deleted', 'team', $id, [ + 'name' => $team['name'], + ]); + + Session::setFlash('success', 'Team deleted.'); + $this->view->redirect('/teams'); + } + + public function addUser(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $userId = (int) ($_POST['user_id'] ?? 0); + if ($userId <= 0) { + $this->view->json(['success' => false, 'message' => 'Invalid user']); + } + + if ($this->teamModel->addMember($id, $userId)) { + $this->auditService->log('team_user_added', 'team', $id, [ + 'user_id' => $userId, + ]); + $this->view->json(['success' => true, 'message' => 'User added to team.']); + } + + $this->view->json(['success' => false, 'message' => 'User is already in this team.']); + } + + public function removeUser(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $userId = (int) ($_POST['user_id'] ?? 0); + if ($userId <= 0) { + $this->view->json(['success' => false, 'message' => 'Invalid user']); + } + + $this->teamModel->removeMember($id, $userId); + + $this->auditService->log('team_user_removed', 'team', $id, [ + 'user_id' => $userId, + ]); + + $this->view->json(['success' => true, 'message' => 'User removed from team.']); + } + + public function addServer(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $serverId = (int) ($_POST['server_id'] ?? 0); + $role = $_POST['role'] ?? 'viewer'; + + if ($serverId <= 0) { + $this->view->json(['success' => false, 'message' => 'Invalid server']); + } + + if (!in_array($role, ['viewer', 'manager'], true)) { + $this->view->json(['success' => false, 'message' => 'Invalid role']); + } + + $userId = (int) Session::get('user_id'); + $server = $this->serverModel->findById($serverId); + if (!$server || (int) $server['created_by'] !== $userId) { + $this->view->json(['success' => false, 'message' => 'Server not found or not owned by you'], 404); + } + + if ($this->teamModel->addServer($id, $serverId, $role)) { + $this->auditService->log('team_server_added', 'team', $id, [ + 'server_id' => $serverId, + 'role' => $role, + ]); + $this->view->json(['success' => true, 'message' => 'Server assigned to team.']); + } + + $this->view->json(['success' => false, 'message' => 'Server is already assigned to this team.']); + } + + public function removeServer(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $serverId = (int) ($_POST['server_id'] ?? 0); + if ($serverId <= 0) { + $this->view->json(['success' => false, 'message' => 'Invalid server']); + } + + $this->teamModel->removeServer($id, $serverId); + + $this->auditService->log('team_server_removed', 'team', $id, [ + 'server_id' => $serverId, + ]); + + $this->view->json(['success' => true, 'message' => 'Server removed from team.']); + } + + public function updateServerRole(int $id): void + { + $team = $this->teamModel->findById($id); + + if (!$team) { + $this->view->json(['success' => false, 'message' => 'Team not found'], 404); + } + + $serverId = (int) ($_POST['server_id'] ?? 0); + $role = $_POST['role'] ?? 'viewer'; + + if ($serverId <= 0) { + $this->view->json(['success' => false, 'message' => 'Invalid server']); + } + + if (!in_array($role, ['viewer', 'manager'], true)) { + $this->view->json(['success' => false, 'message' => 'Invalid role']); + } + + $this->teamModel->updateServerRole($id, $serverId, $role); + + $this->auditService->log('team_server_role_updated', 'team', $id, [ + 'server_id' => $serverId, + 'role' => $role, + ]); + + $this->view->json(['success' => true, 'message' => 'Server role updated.']); + } +} diff --git a/src/Models/Server.php b/src/Models/Server.php index 6ebca72..8f7b260 100755 --- a/src/Models/Server.php +++ b/src/Models/Server.php @@ -211,12 +211,31 @@ class Server return 'owner'; } - $assignment = $this->db->fetch( + $direct = $this->db->fetch( 'SELECT role FROM server_user WHERE server_id = ? AND user_id = ?', [$serverId, $userId] ); - return $assignment ? $assignment['role'] : null; + $teamRole = $this->db->fetch( + 'SELECT ts.role FROM team_user tu + JOIN team_server ts ON tu.team_id = ts.team_id + WHERE tu.user_id = ? AND ts.server_id = ? + ORDER BY FIELD(ts.role, \'manager\', \'viewer\') + LIMIT 1', + [$userId, $serverId] + ); + + $directRole = $direct ? $direct['role'] : null; + $teamRoleValue = $teamRole ? $teamRole['role'] : null; + + if ($directRole === 'manager' || $teamRoleValue === 'manager') { + return 'manager'; + } + if ($directRole === 'viewer' || $teamRoleValue === 'viewer') { + return 'viewer'; + } + + return null; } public function getAccessibleServerIds(int $userId): array @@ -231,6 +250,13 @@ class Server [$userId] ); + $teamServerIds = $this->db->fetchAll( + 'SELECT DISTINCT ts.server_id FROM team_user tu + JOIN team_server ts ON tu.team_id = ts.team_id + WHERE tu.user_id = ?', + [$userId] + ); + $ids = []; foreach ($owned as $row) { $ids[] = (int) $row['id']; @@ -238,6 +264,9 @@ class Server foreach ($assigned as $row) { $ids[] = (int) $row['server_id']; } + foreach ($teamServerIds as $row) { + $ids[] = (int) $row['server_id']; + } return array_unique($ids); } diff --git a/src/Models/Team.php b/src/Models/Team.php new file mode 100644 index 0000000..2ed87fc --- /dev/null +++ b/src/Models/Team.php @@ -0,0 +1,155 @@ +db = Database::getInstance(); + } + + public function findAll(): array + { + return $this->db->fetchAll( + 'SELECT t.*, + (SELECT COUNT(*) FROM team_user WHERE team_id = t.id) AS member_count, + (SELECT COUNT(*) FROM team_server WHERE team_id = t.id) AS server_count + FROM teams t + ORDER BY t.name ASC' + ); + } + + public function findById(int $id): ?array + { + $team = $this->db->fetch( + 'SELECT t.*, + (SELECT COUNT(*) FROM team_user WHERE team_id = t.id) AS member_count, + (SELECT COUNT(*) FROM team_server WHERE team_id = t.id) AS server_count + FROM teams t WHERE t.id = ?', + [$id] + ); + return $team ?: null; + } + + public function create(array $data): int + { + $data['created_by'] = (int) Session::get('user_id'); + $data['created_at'] = date('Y-m-d H:i:s'); + $data['updated_at'] = date('Y-m-d H:i:s'); + return $this->db->insert('teams', $data); + } + + public function update(int $id, array $data): bool + { + $data['updated_at'] = date('Y-m-d H:i:s'); + return $this->db->update('teams', $data, 'id = ?', [$id]) > 0; + } + + public function delete(int $id): bool + { + return $this->db->delete('teams', 'id = ?', [$id]) > 0; + } + + public function getMembers(int $teamId): array + { + return $this->db->fetchAll( + 'SELECT tu.*, u.username, u.email + FROM team_user tu + JOIN users u ON tu.user_id = u.id + WHERE tu.team_id = ? + ORDER BY u.username ASC', + [$teamId] + ); + } + + public function addMember(int $teamId, int $userId): bool + { + $existing = $this->db->fetch( + 'SELECT id FROM team_user WHERE team_id = ? AND user_id = ?', + [$teamId, $userId] + ); + if ($existing) { + return false; + } + + $this->db->insert('team_user', [ + 'team_id' => $teamId, + 'user_id' => $userId, + ]); + return true; + } + + public function removeMember(int $teamId, int $userId): bool + { + return $this->db->delete( + 'team_user', + 'team_id = ? AND user_id = ?', + [$teamId, $userId] + ) > 0; + } + + public function getServers(int $teamId): array + { + return $this->db->fetchAll( + 'SELECT ts.*, s.name AS server_name, s.ip_address + FROM team_server ts + JOIN servers s ON ts.server_id = s.id + WHERE ts.team_id = ? + ORDER BY s.name ASC', + [$teamId] + ); + } + + public function addServer(int $teamId, int $serverId, string $role): bool + { + if (!in_array($role, ['viewer', 'manager'], true)) { + return false; + } + + $existing = $this->db->fetch( + 'SELECT id FROM team_server WHERE team_id = ? AND server_id = ?', + [$teamId, $serverId] + ); + if ($existing) { + return false; + } + + $this->db->insert('team_server', [ + 'team_id' => $teamId, + 'server_id' => $serverId, + 'role' => $role, + ]); + return true; + } + + public function removeServer(int $teamId, int $serverId): bool + { + return $this->db->delete( + 'team_server', + 'team_id = ? AND server_id = ?', + [$teamId, $serverId] + ) > 0; + } + + public function updateServerRole(int $teamId, int $serverId, string $role): bool + { + if (!in_array($role, ['viewer', 'manager'], true)) { + return false; + } + + return $this->db->update( + 'team_server', + ['role' => $role], + 'team_id = ? AND server_id = ?', + [$teamId, $serverId] + ) > 0; + } +} diff --git a/views/layouts/main.php b/views/layouts/main.php index 502b48e..ffd3685 100755 --- a/views/layouts/main.php +++ b/views/layouts/main.php @@ -39,15 +39,15 @@ - +