From 6fa1c9b78bd4f0c6ff13f78052d2996ab7a70805 Mon Sep 17 00:00:00 2001 From: hobsRKM Date: Wed, 1 May 2024 15:06:18 +0000 Subject: [PATCH] Added Groups Feature --- app/Helpers/PermissionHelper.php | 41 +- app/Http/Controllers/AdminController.php | 386 ++++++++++++++---- app/Http/Controllers/ServerController.php | 87 ++-- app/Http/Requests/StoreAdminRequest.php | 5 +- app/Http/Requests/StoreGroupRequest.php | 54 +++ app/Models/SaAdmin.php | 10 + app/Models/SaGroups.php | 19 + app/Models/SaGroupsFlags.php | 12 + app/Models/SaGroupsServers.php | 20 + app/Models/User.php | 11 + app/View/Components/Loader.php | 27 ++ body.md | 22 +- resources/css/app.css | 3 + resources/js/admin/create.ts | 19 + resources/js/admin/edit.ts | 1 + resources/js/dashboard/servers.ts | 6 +- resources/js/groups/groups.ts | 5 + resources/js/groups/list.ts | 29 ++ resources/js/utility/utility.ts | 8 +- resources/views/admin/admins/create.blade.php | 27 +- resources/views/admin/admins/edit.blade.php | 61 ++- resources/views/admin/dashboard.blade.php | 1 + resources/views/admin/groups/create.blade.php | 68 +++ resources/views/admin/groups/edit.blade.php | 0 resources/views/admin/groups/list.blade.php | 43 ++ .../views/admin/servers/players.blade.php | 5 + resources/views/components/loader.blade.php | 5 + resources/views/layouts/app.blade.php | 6 +- resources/views/partials/nav.blade.php | 4 + resources/views/partials/scripts.blade.php | 1 + routes/web.php | 15 +- vite.config.js | 4 +- 32 files changed, 840 insertions(+), 165 deletions(-) create mode 100644 app/Http/Requests/StoreGroupRequest.php create mode 100644 app/Models/SaGroups.php create mode 100644 app/Models/SaGroupsFlags.php create mode 100644 app/Models/SaGroupsServers.php create mode 100644 app/View/Components/Loader.php create mode 100644 resources/js/groups/groups.ts create mode 100644 resources/js/groups/list.ts create mode 100644 resources/views/admin/groups/create.blade.php create mode 100644 resources/views/admin/groups/edit.blade.php create mode 100644 resources/views/admin/groups/list.blade.php create mode 100644 resources/views/components/loader.blade.php diff --git a/app/Helpers/PermissionHelper.php b/app/Helpers/PermissionHelper.php index e21004a..65550a1 100755 --- a/app/Helpers/PermissionHelper.php +++ b/app/Helpers/PermissionHelper.php @@ -14,10 +14,12 @@ public static function isSuperAdmin() $user = Auth::user(); // Check if the user has the specified permission - if ($user && $user->permissions()->where('flag' ,'@css/root')->exists()) { + if ($user && ( + $user->permissions()->where('flag', '@css/root')->exists() || + $user->groupPermissions()->where('flag', '@css/root')->exists() + )) { return true; } - return false; } @@ -32,7 +34,8 @@ public static function hasUnBanPermission(int $serverId=null) } elseif ($serverId && self::hasValidPermission($user, $serverId, '@css/unban')) { // has permission on the server $allowed = true; - } elseif ($user && !$serverId && $user->permissions()->whereIn('flag', ['@css/root', '@css/unban'])->exists()) { + } elseif ($user && !$serverId && ($user->permissions()->whereIn('flag', ['@css/root', '@css/unban'])->exists() + || $user->groupPermissions()->whereIn('flag',['@css/root', '@css/unban'])->exists())) { // Check perms exists for atleast one of the server - For UI Actions $allowed = true; } @@ -51,7 +54,8 @@ public static function hasUnMutePermission(int $serverId=null) } elseif ($serverId && self::hasValidPermission($user, $serverId, '@css/chat')) { // has permission on the server $allowed = true; - } elseif ($user && !$serverId && $user->permissions()->whereIn('flag', ['@css/chat', '@css/root'])->exists()) { + } elseif ($user && !$serverId && ($user->permissions()->whereIn('flag', ['@css/root', '@css/chat'])->exists() + || $user->groupPermissions()->whereIn('flag',['@css/root', '@css/chat'])->exists())) { // Check perms exists for atleast one of the server - For UI Actions $allowed = true; } @@ -70,7 +74,8 @@ public static function hasBanPermission(int $serverId=null) } elseif ($serverId && self::hasValidPermission($user, $serverId, '@css/ban')) { // has permission on the server $allowed = true; - } elseif ($user && !$serverId && $user->permissions()->whereIn('flag', ['@css/ban', '@css/root'])->exists()) { + } elseif ($user && !$serverId && ($user->permissions()->whereIn('flag', ['@css/root', '@css/ban'])->exists() + || $user->groupPermissions()->whereIn('flag',['@css/root', '@css/ban'])->exists())) { // Check perms exists for atleast one of the server - For UI Actions $allowed = true; } @@ -89,7 +94,8 @@ public static function hasMutePermission(int $serverId=null) } elseif ($serverId && self::hasValidPermission($user, $serverId, '@css/chat')) { // has permission on the server $allowed = true; - } elseif ($user && !$serverId && $user->permissions()->whereIn('flag', ['@css/chat', '@css/root'])->exists()) { + } elseif ($user && !$serverId && ($user->permissions()->whereIn('flag', ['@css/root', '@css/chat'])->exists() + || $user->groupPermissions()->whereIn('flag',['@css/root', '@css/chat'])->exists())) { // Check perms exists for atleast one of the server - For UI Actions $allowed = true; } @@ -108,7 +114,8 @@ public static function hasKickPermission(int $serverId=null) } elseif ($serverId && self::hasValidPermission($user, $serverId, '@css/kick')) { // has permission on the server $allowed = true; - } elseif ($user && !$serverId && $user->permissions()->whereIn('flag', ['@css/kick', '@css/root'])->exists()) { + } elseif ($user && !$serverId && ($user->permissions()->whereIn('flag', ['@css/root', '@css/kick'])->exists() + || $user->groupPermissions()->whereIn('flag',['@css/root', '@css/kick'])->exists())) { // Check perms exists for atleast one of the server - For UI Actions $allowed = true; } @@ -128,15 +135,33 @@ private static function validateExpiryOnAllServers(?\Illuminate\Contracts\Auth\A private static function hasValidPermission(?\Illuminate\Contracts\Auth\Authenticatable $user, int $serverId, string $flag) { - return $user?->servers() + $validPerms = $user?->servers() ->where('server_id', $serverId) ->where(function ($query) { $query->where('ends', '>=', Carbon::now()->toDateTimeString()) ->orWhereNull('ends'); }) + ->whereNull('group_id') ->first() ?->adminFlags() ->whereIn('flag', [$flag, '@css/root']) ->exists(); + if(!$validPerms){ + // check if perms exist in a group + $validPerms =$user?->servers() + ->where('server_id', $serverId) + ->where(function ($query) { + $query->where('ends', '>=', Carbon::now()->toDateTimeString()) + ->orWhereNull('ends'); + }) + ->whereNotNull('group_id') + ->first() + ?->groupsServers() + ->first() + ?->groupsFlags() + ->whereIn('flag', [$flag, '@css/root']) + ->exists(); + } + return $validPerms; } } diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index f0292b2..74b7537 100755 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -5,9 +5,13 @@ use App\Helpers\CommonHelper; use App\Helpers\PermissionsHelper; use App\Http\Requests\StoreAdminRequest; +use App\Http\Requests\StoreGroupRequest; use App\Models\Permission; use App\Models\SaAdmin; use App\Models\SaAdminsFlags; +use App\Models\SaGroupsFlags; +use App\Models\SaGroups; +use App\Models\SaGroupsServers; use App\Models\SaServer; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -24,7 +28,8 @@ public function create() { $permissions = Permission::all(); $servers = SaServer::all(); - return view('admin.admins.create', compact('permissions', 'servers')); + $groups = SaGroups::all(); + return view('admin.admins.create', compact('permissions', 'servers', 'groups')); } public function store(StoreAdminRequest $request) @@ -36,29 +41,52 @@ public function store(StoreAdminRequest $request) } $adminAddedToServerCount = []; foreach ($validatedData['server_ids'] as $server_id) { - foreach ($validatedData['permissions'] as $permissionId) { - $existingAdmin = SaAdmin::where('player_steamid', $validatedData['steam_id']) - ->where('server_id', $server_id) - ->first() - ?->adminFlags() - ->where('flag', $permissionId) - ->exists(); - if (!$existingAdmin) { - $permission = Permission::find($permissionId); - $admin = new SaAdmin(); - $admin->player_steamid = $validatedData['steam_id']; - $admin->player_name = $validatedData['player_name']; - $admin->immunity = $validatedData['immunity']; - $admin->server_id = $server_id; - $admin->ends = isset($validatedData['ends']) ? CommonHelper::formatDate($validatedData['ends']): null; - $admin->created = now(); - $admin->save(); - - $adminFlag = new SaAdminsFlags(); - $adminFlag->admin_id= $admin->id; - $adminFlag->flag = $permission->permission; - $adminFlag->save(); - $adminAddedToServerCount[$server_id] = $server_id; + if(!empty($validatedData['permissions'])) { + foreach ($validatedData['permissions'] as $permissionId) { + $existingAdmin = SaAdmin::where('player_steamid', $validatedData['steam_id']) + ->where('server_id', $server_id) + ->first() + ?->adminFlags() + ->where('flag', $permissionId) + ->exists(); + if (!$existingAdmin) { + $permission = Permission::find($permissionId); + $admin = new SaAdmin(); + $admin->player_steamid = $validatedData['steam_id']; + $admin->player_name = $validatedData['player_name']; + $admin->immunity = $validatedData['immunity']; + $admin->server_id = $server_id; + $admin->ends = isset($validatedData['ends']) ? CommonHelper::formatDate($validatedData['ends']) : null; + $admin->created = now(); + $admin->save(); + + $adminFlag = new SaAdminsFlags(); + $adminFlag->admin_id = $admin->id; + $adminFlag->flag = $permission->permission; + $adminFlag->save(); + $adminAddedToServerCount[$server_id] = $server_id; + } + } + } + if(!empty($validatedData['groups'])) { + foreach ($validatedData['groups'] as $groupId) { + $adminGroupExists = SaAdmin::with('adminGroups.groups') + ->where('player_steamid', $validatedData['steam_id']) + ->where('server_id', $server_id) + ->where('group_id', $groupId) + ->exists(); + if (!$adminGroupExists) { + $saAdmin = new SaAdmin(); + $saAdmin->player_steamid = $validatedData['steam_id']; + $saAdmin->player_name = $validatedData['player_name']; + $saAdmin->immunity = $validatedData['immunity']; + $saAdmin->server_id = $server_id; + $saAdmin->ends = isset($validatedData['ends']) ? CommonHelper::formatDate($validatedData['ends']) : null; + $saAdmin->group_id = $groupId; + $saAdmin->created = now(); + $saAdmin->save(); + $adminAddedToServerCount[$server_id] = $server_id; + } } } } @@ -92,15 +120,26 @@ public function getAdminsList(Request $request) 'player_steamid', 'player_name', 'sa_admins.id', - DB::raw('GROUP_CONCAT(distinct sa_admins_flags.flag SEPARATOR ", ") as flags'), + 'sa_admins.group_id', + DB::raw('CASE WHEN COUNT(sa_admins_flags.flag) = 0 THEN COALESCE(GROUP_CONCAT(DISTINCT sa_groups.name SEPARATOR ", "), "") ELSE COALESCE(GROUP_CONCAT(DISTINCT sa_admins_flags.flag SEPARATOR ", "), "") END AS flags'), DB::raw('GROUP_CONCAT(DISTINCT CONCAT("[Hostname] ", sa_servers.hostname) SEPARATOR ", ") as hostnames'), 'created', 'ends', - 'server_id' - ) + 'sa_admins.server_id' + ) ->join('sa_servers', 'sa_admins.server_id', '=', 'sa_servers.id') - ->join('sa_admins_flags', 'sa_admins_flags.admin_id', '=', 'sa_admins.id') - ->groupBy('player_steamid') + ->leftJoin('sa_admins_flags', function($join) { + $join->on('sa_admins_flags.admin_id', '=', 'sa_admins.id') + ->where('sa_admins_flags.flag', 'not like', '#%'); + }) + ->leftJoin('sa_groups', 'sa_admins.group_id', '=', 'sa_groups.id') + ->leftJoin('sa_groups_flags', 'sa_groups.id', '=', 'sa_groups_flags.group_id') + ->leftJoin('sa_groups_servers', 'sa_groups_servers.group_id', '=', 'sa_groups_flags.group_id') + ->groupBy('player_steamid', DB::raw('CASE + WHEN sa_admins_flags.flag LIKE "#%" THEN "#" + WHEN sa_admins_flags.flag LIKE "@%" THEN "@" + ELSE "" + END')) ->orderBy($orderColumnName, $orderDir) ->offset($start) ->limit($length) @@ -117,7 +156,7 @@ public function getAdminsList(Request $request) "created" => $admin->created, "flags" => $admin->flags, "hostnames" => $admin->hostnames, - 'actions' => PermissionsHelper::isSuperAdmin() ? "" : "", + 'actions' => PermissionsHelper::isSuperAdmin() ? ($admin->group_id !== null ? " " : " ") : "", ]; } $response = [ @@ -130,77 +169,117 @@ public function getAdminsList(Request $request) return response()->json($response); } - public function edit($player_steam, $server_id) + public function editAdmin($player_steam, $server_id) { $admin = SaAdmin::with('adminFlags.permissions') ->where('player_steamid', $player_steam) ->where('server_id', $server_id) ->get(); + $allowMigrate = true; + $groups = SaGroups::all(); + $adminGroups = []; if ($admin->isEmpty()) { return redirect()->route('admins.list')->with('error', 'Admin does not exists for the server!. Add Admin!'); } $permissions = Permission::all(); $servers = SaServer::all(); $adminPermissions = $admin->pluck('adminFlags.*.permissions.permission')->flatten()->toArray(); - return view('admin.admins.edit', compact('admin', 'permissions', 'adminPermissions', 'servers')); + return view('admin.admins.edit', compact('admin', 'permissions', 'adminPermissions', 'servers', 'allowMigrate', 'groups', 'adminGroups')); } - public function update(Request $request, $player_steam) + public function editAdminGroup($player_steam) { + $allowMigrate = false; + $admin = SaAdmin::with('adminGroups.groups') + ->where('player_steamid', $player_steam) + ->get(); + if ($admin->isEmpty()) { + return redirect()->route('admins.list')->with('error', 'Admin with a group does not exist for the selected server!. Add admin to the server!'); + } + $groups = SaGroups::all(); + $servers = SaServer::all(); + $adminGroups = $admin->pluck('adminGroups.*.groups.name')->flatten()->unique()->toArray(); + return view('admin.admins.edit', compact('admin', 'groups', 'adminGroups', 'servers', 'allowMigrate')); + } + public function updateAdmin(Request $request, $player_steam) { // Validate the submitted permissions $validated = $request->validate([ - 'permissions' => 'required|array', + 'permissions' => 'required_without:groups|array', 'permissions.*' => 'exists:permissions,permission', 'ends' => 'required_without:permanent|date|after:today', 'server_id' => 'exists:sa_servers,id', - 'immunity' => 'required' + 'immunity' => 'required', + 'groups' => 'required_without:permissions|array', + 'player_name' => 'required', ]); + if(!empty($validated['groups'])){ + // migrate + $servers = SaServer::all()->pluck('id')->toArray(); + SaAdmin::where('player_steamid', $player_steam) + ->whereIn('server_id', SaServer::all()->pluck('id')->toArray()) + ->whereNull('group_id') + ->delete(); - $admin = SaAdmin::with('adminFlags.permissions') - ->where('player_steamid',$player_steam) - ->where('server_id', $validated['server_id']) - ->get(); - $submittedPermissions = $validated['permissions']; + foreach($servers as $server) { + foreach($validated['groups'] as $groupId){ + $saAdmin = new SaAdmin(); + $saAdmin->player_steamid = $player_steam; + $saAdmin->player_name = $validated['player_name']; + $saAdmin->immunity = $validated['immunity']; + $saAdmin->server_id = $server; + $saAdmin->ends = isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']): null; + $saAdmin->group_id = $groupId; + $saAdmin->created = now(); + $saAdmin->save(); + } + } + } else { + $admin = SaAdmin::with('adminFlags.permissions') + ->where('player_steamid', $player_steam) + ->where('server_id', $validated['server_id']) + ->get(); + $submittedPermissions = $validated['permissions']; - // Fetch current permissions from the database - $currentPermissions = $admin->pluck('adminFlags.*.permissions.permission')->flatten()->toArray(); + // Fetch current permissions from the database + $currentPermissions = $admin->pluck('adminFlags.*.permissions.permission')->flatten()->toArray(); - // Determine permissions to add and delete - $permissionsToAdd = array_diff($submittedPermissions, $currentPermissions); - $permissionsToDelete = array_diff($currentPermissions, $submittedPermissions); + // Determine permissions to add and delete + $permissionsToAdd = array_diff($submittedPermissions, $currentPermissions); + $permissionsToDelete = array_diff($currentPermissions, $submittedPermissions); - // Handle permissions to add - foreach ($permissionsToAdd as $permissionName) { - $saAdmin = new SaAdmin(); - $saAdmin->player_steamid = $admin->first()->player_steamid; - $saAdmin->player_name = $admin->first()->player_name; - $saAdmin->immunity = $validated['immunity']; - $saAdmin->server_id = $admin->first()->server_id; - $admin->ends = isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']): null; - $saAdmin->created = now(); - $saAdmin->save(); + // Handle permissions to add + foreach ($permissionsToAdd as $permissionName) { + $saAdmin = new SaAdmin(); + $saAdmin->player_steamid = $admin->first()->player_steamid; + $saAdmin->player_name = $validated['player_name']; + $saAdmin->immunity = $validated['immunity']; + $saAdmin->server_id = $admin->first()->server_id; + $saAdmin->ends = isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']) : null; + $saAdmin->created = now(); + $saAdmin->save(); - $adminFlag = new SaAdminsFlags(); - $adminFlag->admin_id= $saAdmin->id; - $adminFlag->flag = $permissionName; - $adminFlag->save(); - } + $adminFlag = new SaAdminsFlags(); + $adminFlag->admin_id = $saAdmin->id; + $adminFlag->flag = $permissionName; + $adminFlag->save(); + } - // Handle permissions to delete - $adminData = SaAdmin::where('player_steamid', $player_steam) - ->where('server_id', $validated['server_id']) - ->get('id'); + // Handle permissions to delete + $adminData = SaAdmin::where('player_steamid', $player_steam) + ->where('server_id', $validated['server_id']) + ->get('id'); - SaAdminsFlags::whereIn('flag', $permissionsToDelete) - ->whereIn('admin_id', $adminData->pluck('id')->toArray()) - ->delete(); + SaAdminsFlags::whereIn('flag', $permissionsToDelete) + ->whereIn('admin_id', $adminData->pluck('id')->toArray()) + ->delete(); - // update new expiry - SaAdmin::where('player_steamid', $player_steam) - ->where('server_id', $validated['server_id']) - ->update([ - 'ends' => isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']) : null - ]); + // update new expiry irrespective or no. of servers + SaAdmin::where('player_steamid', $player_steam) + ->whereNull('group_id') + ->update([ + 'ends' => isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']) : null + ]); + } return redirect()->route('admins.list')->with('success', 'Admin updated successfully.'); } @@ -224,4 +303,165 @@ public function delete(Request $request, $player_steam) return redirect()->route('admins.list')->with('success', 'Admin deleted successfully.'); } + + public function createGroup() + { + $permissions = Permission::all(); + $servers = SaServer::all(); + return view('admin.groups.create', compact('permissions', 'servers')); + } + + public function storeGroup(StoreGroupRequest $request) + { + $validatedData = $request->validated(); + try { + if(in_array('all', $validatedData['server_ids'])) { + $validatedData['server_ids'] = SaServer::all()->pluck('id')->toArray(); + } + $groupAddedToServerCount = []; + if (empty($group = SaGroups::where('name', $validatedData['group_name'])->first())) { + $group = new SaGroups(); + $group->name = $validatedData['group_name']; + $group->immunity = $validatedData['immunity']; + $group->save(); + } + foreach ($validatedData['server_ids'] as $server_id) { + if (empty(SaGroupsServers::where('group_id', $group->id)->where('server_id', $server_id)->first())) + { + $groupServer = new SaGroupsServers(); + $groupServer->group_id = $group->id; + $groupServer->server_id = $server_id; + $groupServer->save(); + $groupAddedToServerCount[$server_id] = $server_id; + } + foreach ($validatedData['permissions'] as $permissionId) { + $permission = Permission::find($permissionId); + if(empty($group->groupFlags()->where('flag', $permission->permission)->first())) { + $groupFlags = new SaGroupsFlags(); + $groupFlags->group_id = $group->id; + $groupFlags->flag = $permission->permission; + $groupFlags->save(); + $groupAddedToServerCount[$server_id] = $server_id; + } + } + } + return redirect()->route('admins.list')->with('success', 'Group added successfully to '.count($groupAddedToServerCount).' Servers'); + } catch (\Exception $e) { + return Redirect::back()->withErrors(['msg' => 'There was an error saving the group: ' . $e->getMessage()]); + } + } + + public function updateAdminGroup(Request $request, $player_steam) + { + // Validate the submitted permissions + $validated = $request->validate([ + 'ends' => 'required_without:permanent|date|after:today', + 'server_id' => 'exists:sa_servers,id', + 'immunity' => 'required', + 'groups' => 'required' + ]); + + $admin = SaAdmin::with('adminGroups.groups') + ->where('player_steamid',$player_steam) + ->where('server_id', $validated['server_id']) + ->get(); + $submittedGroups = $validated['groups']; + + // Fetch current groups from the database + $currentGroups = $admin->pluck('adminGroups.*.groups.id')->flatten()->toArray(); + + // Determine groups to add and delete + $groupsToAdd = array_diff($submittedGroups, $currentGroups); + $groupsToDelete = array_diff($currentGroups, $submittedGroups); + // Handle groups to add + foreach ($groupsToAdd as $groupId) { + $saAdmin = new SaAdmin(); + $saAdmin->player_steamid = $admin->first()->player_steamid; + $saAdmin->player_name = $admin->first()->player_name; + $saAdmin->immunity = $validated['immunity']; + $saAdmin->server_id = $admin->first()->server_id; + $saAdmin->ends = isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']): null; + $saAdmin->group_id = $groupId; + $saAdmin->created = now(); + $saAdmin->save(); + } + + // Handle groups to delete + SaAdmin::whereIn('group_id', $groupsToDelete) + ->where('player_steamid',$player_steam) + ->where('server_id', $validated['server_id']) + ->delete(); + + // update new expiry + SaAdmin::where('player_steamid', $player_steam) + ->where('server_id', $validated['server_id']) + ->whereNotNull('group_id') + ->update([ + 'ends' => isset($validated['ends']) ? CommonHelper::formatDate($validated['ends']) : null + ]); + return redirect()->route('admins.list')->with('success', 'Admin Group(s) updated successfully.'); + } + + public function getGroupsList(Request $request) + { + // Extract parameters sent by DataTables + $start = $request->input('start'); + $length = $request->input('length'); + $searchValue = $request->input('search.value'); + $orderColumn = $request->input('order.0.column'); + $orderDirection = $request->input('order.0.dir'); + + $query = SaGroups::query(); + + // Join the sa_groups_flag table to fetch flags + $query->leftJoin('sa_groups_flags', 'sa_groups.id', '=', 'sa_groups_flags.group_id'); + + $query->select( + 'sa_groups.*', + DB::raw('GROUP_CONCAT(sa_groups_flags.flag SEPARATOR ", ") as flags') + ); + + // Apply search filter on group name + if (!empty($searchValue)) { + $query->where('sa_groups.name', 'like', '%' . $searchValue . '%'); + } + + // Group by group id and name + $query->groupBy('sa_groups.id', 'sa_groups.name'); + + // Apply sorting + if ($orderColumn !== null) { + $query->orderBy($request->input('columns.' . $orderColumn . '.data'), $orderDirection); + } + + // Paginate the results + $groups = $query->offset($start)->limit($length)->get(); + + // Get total count for pagination + $totalGroups = SaGroups::count(); + + $formattedData = []; + // Format each group record + foreach ($groups as $group) { + $formattedData[] = [ + "id" => $group->id, + "name" => $group->name, + "flags" => $group->flags ?: "No flags assigned", + ]; + } + + $response = [ + 'draw' => $request->input('draw'), + "recordsTotal" => $totalGroups, + "recordsFiltered" => !empty($searchValue) ? count($formattedData) : $totalGroups, + "data" => $formattedData + ]; + + return response()->json($response); + } + + public function groups() + { + return view('admin.groups.list'); + } } diff --git a/app/Http/Controllers/ServerController.php b/app/Http/Controllers/ServerController.php index 4127a9b..c503e50 100755 --- a/app/Http/Controllers/ServerController.php +++ b/app/Http/Controllers/ServerController.php @@ -11,6 +11,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; @@ -27,38 +28,32 @@ public function getAllServerInfo(RconService $rcon) foreach ($servers as $server) { list($serverIp, $serverPort) = explode(":", $server->address); - - if (!$this->isPortOpen($serverIp, $serverPort)) { - Log::error('rcon.servers.list Port Blocked! Unable to read data from port!'); - $formattedServer = [ - 'id' => $server->id, - 'name' => $server->hostname, - 'ip' => $serverIp, - 'port' => $serverPort, - 'players' => '0', - 'map' => '
Unable To Connect
', - 'connect_button' => '
Unable To Connect
' - ]; - $formattedServers[] = $formattedServer; - continue; - } - - // Fetch server information using the RconService + // Fetch server information using the SteamService try { - $rcon->connect($serverIp, $serverPort); - $serverInfo = $rcon->getInfo(); - $formattedServer = [ - 'id' => $server->id, - 'name' => $server->hostname, - 'ip' => $serverIp, - 'port' => $serverPort, - 'players' => $serverInfo['Players'] . "/" . $serverInfo['MaxPlayers'], - 'map' => $serverInfo['Map'], - 'connect_button' => 'Connect', - ]; - $rcon->disconnect(); + $serverDetails = $this->getServerDetails($serverIp, $serverPort); + if ($serverDetails) { + $formattedServer = [ + 'id' => $server->id, + 'name' => $server->hostname, + 'ip' => $serverIp, + 'port' => $serverPort, + 'players' => $serverDetails['players'] . "/" . $serverDetails['max_players'], + 'map' => $serverDetails['map'], + 'connect_button' => 'Connect', + ]; + } else { + $formattedServer = [ + 'id' => $server->id, + 'name' => $server->hostname, + 'ip' => $serverIp, + 'port' => $serverPort, + 'players' => '0', + 'map' => '
Offline
', + 'connect_button' => '
Offline
' + ]; + } } catch (\Exception $e) { - Log::error('rcon.servers.list.error'. $e->getMessage()); + Log::error('Steam Web API Error: ' . $e->getMessage()); $formattedServer = [ 'id' => $server->id, 'name' => $server->hostname, @@ -94,16 +89,22 @@ private function isPortOpen($ip, $port, $timeout = 1) { */ public function getPlayers(Request $request, $serverId, RconService $rcon) { $players = []; + $error = null; $server = SaServer::where('id', $serverId)->first(); list($serverIp, $serverPort) = explode(":", $server->address); - try { - $rcon->connect($serverIp, $serverPort); - $players = $rcon->getPlayers(); - $rcon->disconnect(); - } catch(\Exception $e){ - Log::error('rcon.players.error'.$e->getMessage()); + if($this->isPortOpen($serverIp, $serverPort)) { + try { + $rcon->connect($serverIp, $serverPort); + $players = $rcon->getPlayers(); + $rcon->disconnect(); + } catch (\Exception $e) { + Log::error('rcon.players.error' . $e->getMessage()); + $error = 'Failed to get server players!'; + } + } else { + $error = 'Could not connect to server! Check your firewall gameserver/web ports!'; } - return view('admin.servers.players', compact('players', 'server')); + return view('admin.servers.players', compact('players', 'server', 'error')); } @@ -218,4 +219,16 @@ private function executeCommand(string $command, string $serverId) ], 500); } } + + private function getServerDetails($ip, $port) + { + $apiKey = env('STEAM_CLIENT_SECRET'); + $response = Http::get("https://api.steampowered.com/IGameServersService/GetServerList/v1/?key=$apiKey&filter=addr\\$ip:$port"); + if ($response->successful()) { + return $response->json('response.servers')[0]; + } else { + Log::error('steam.api.server.listing '. $response->body()); + return null; + } + } } diff --git a/app/Http/Requests/StoreAdminRequest.php b/app/Http/Requests/StoreAdminRequest.php index 08661db..533c685 100755 --- a/app/Http/Requests/StoreAdminRequest.php +++ b/app/Http/Requests/StoreAdminRequest.php @@ -30,10 +30,11 @@ function ($attribute, $value, $fail) { } }, ], - 'permissions' => 'required|array', + 'permissions' => 'required_without:groups|array', 'permissions.*' => 'exists:permissions,id', 'ends' => 'required_without:permanent|date|after:today', - 'immunity' => 'required|numeric' + 'immunity' => 'required|numeric', + 'groups' => 'required_without:permissions|array' ]; } diff --git a/app/Http/Requests/StoreGroupRequest.php b/app/Http/Requests/StoreGroupRequest.php new file mode 100644 index 0000000..4ab87ac --- /dev/null +++ b/app/Http/Requests/StoreGroupRequest.php @@ -0,0 +1,54 @@ +|string> + */ + public function rules() + { + return [ + 'group_name' => 'required|regex:/^#.+$/', + 'server_ids' => 'required|array', + 'server_ids.*' => [ + 'required', + function ($attribute, $value, $fail) { + if ($value !== 'all' && !DB::table('sa_servers')->where('id', $value)->exists()) { + $fail($attribute.' is invalid.'); + } + }, + ], + 'permissions' => 'required|array', + 'permissions.*' => 'exists:permissions,id', + 'immunity' => 'required|numeric' + ]; + } + + public function messages() + { + return [ + 'group_name.required' => 'The group name is required.', + 'group_name.regex' => 'The group name must start with #.', + 'server_id.required' => 'The server field is required.', + 'server_id.exists' => 'The selected server is invalid.', + 'permission_id.required' => 'The permission field is required.', + 'permission_id.exists' => 'The selected permission is invalid.', + 'immunity.required' => 'Immunity filed is required.', + ]; + } +} diff --git a/app/Models/SaAdmin.php b/app/Models/SaAdmin.php index 17d8768..d9cb40a 100755 --- a/app/Models/SaAdmin.php +++ b/app/Models/SaAdmin.php @@ -19,4 +19,14 @@ public function servers() public function adminFlags() { return $this->hasMany(SaAdminsFlags::class, 'admin_id', 'id'); } + + public function adminGroups() { + return $this->hasMany(SaGroupsServers::class, 'group_id', 'group_id'); + } + + public function groupsServers() + { + return $this->hasMany(SaGroupsServers::class, 'server_id', 'server_id'); + } + } diff --git a/app/Models/SaGroups.php b/app/Models/SaGroups.php new file mode 100644 index 0000000..214c881 --- /dev/null +++ b/app/Models/SaGroups.php @@ -0,0 +1,19 @@ +hasMany(SaGroupsFlags::class, 'group_id', 'id'); + } + public function groupServers() { + return $this->hasMany(SaGroupsServers::class, 'group_id', 'id'); + } +} diff --git a/app/Models/SaGroupsFlags.php b/app/Models/SaGroupsFlags.php new file mode 100644 index 0000000..2beaa20 --- /dev/null +++ b/app/Models/SaGroupsFlags.php @@ -0,0 +1,12 @@ +belongsTo(SaGroups::class, 'group_id', 'id'); + } + + public function groupsFlags() { + return $this->belongsTo(SaGroupsFlags::class, 'group_id', 'group_id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 4b89d7b..7cb4e36 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -46,4 +46,15 @@ public function servers() { return $this->hasMany(SaAdmin::class, 'player_steamid', 'steam_id'); } + + public function groupPermissions() { + return $this->hasManyThrough( + SaGroupsFlags::class, + SaAdmin::class, + 'player_steamid', + 'group_id', + 'steam_id', + 'group_id' + ); + } } diff --git a/app/View/Components/Loader.php b/app/View/Components/Loader.php new file mode 100644 index 0000000..13ea016 --- /dev/null +++ b/app/View/Components/Loader.php @@ -0,0 +1,27 @@ +id = $id ?: 'loader'; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('components.loader'); + } +} diff --git a/body.md b/body.md index 01b468a..93eaa39 100644 --- a/body.md +++ b/body.md @@ -1,12 +1,14 @@ -Release notes V 1.2.3 +Release notes V 1.3.0 -# Update Log -- Updated the panel to support latest version of cs2SimpleAdmin 1.3.9 -- Added edit/mute ban , you can now edit banned/unbanned players form listing to extend bans or re add ban -- Added new link **/logs** to investigate errors (Can be used only by @css/root) -- Improved server listing to detect if port is blocked (Now shows as unable to connect) -- Added Servers specific permission checks for admins who have been granted only few servers with ban/mute to avoid access to other servers if doesnt exists. -- Few listing improvements -- Minor fixes on caching -- Added ability to select all servers while adding admins +# Update Log - Major Version +- Added support for groups , now you can create groups and assign admins to groups +- Added ability to migrate existing admins to group (irreversible) +- Panel now supports both individual admin flag support along with group support (!IMPORTANT - Recommended to use always groups) +- Improved server listing - now does not rely on port open status to list servers, server should be just online. (Rcon/port access is still needed for live actions or to view players.) +- Added player name on edit admin screen +- Fixed setup screen being accessible even after completion of setup. +- Note:Adding permissions to an existing group for new servers will append the new permissions to the existing set, applying for all associated servers +- It is recommended to not use individual flags/perms to add admin +- Be sure to manage admins/groups from panel as its optimized to disallow duplicates and avoid as much as possible console commands to manage admins/groups. +#### TODO - Edit group and Delete Group will be added gradually. diff --git a/resources/css/app.css b/resources/css/app.css index b2c8fef..aa3f9b4 100755 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -77,3 +77,6 @@ section { font-weight: 500; font-size: 14px; } +.select2 { + width: 100% !important; +} diff --git a/resources/js/admin/create.ts b/resources/js/admin/create.ts index c19bdf9..f815c85 100755 --- a/resources/js/admin/create.ts +++ b/resources/js/admin/create.ts @@ -2,6 +2,9 @@ $(document).ready(function() { $('#server_id').select2({ placeholder: 'Select Servers', }); + $('#group_id').select2({ + placeholder: 'Select Groups', + }); document.addEventListener('change', function(event) { if (event.target.matches('#permanent')) { var endsInput = document.getElementById('ends'); @@ -11,4 +14,20 @@ $(document).ready(function() { } } }); + + $('#flagsPermission').on('click' ,function(){ + $(".flags").show(); + $(".groups").hide(); + $("#group_id").attr('disabled', 'true'); + + }); + $('#groups').on('click' ,function(){ + $(".flags").hide(); + $(".groups").show(); + $("#group_id").removeAttr('disabled'); + let checkboxes = document.querySelectorAll('input[name="permissions[]"]'); + checkboxes.forEach(function(checkbox) { + checkbox.checked = false; + }); + }); }) diff --git a/resources/js/admin/edit.ts b/resources/js/admin/edit.ts index 803945e..33864c6 100755 --- a/resources/js/admin/edit.ts +++ b/resources/js/admin/edit.ts @@ -6,6 +6,7 @@ $('#server_id').on('change', function() { }); $(document).ready(function() { $('#server_id').select2(); + $("#group_id").select2(); }) document.addEventListener('change', function(event) { if (event.target.matches('#permanent')) { diff --git a/resources/js/dashboard/servers.ts b/resources/js/dashboard/servers.ts index 006385c..98b970d 100755 --- a/resources/js/dashboard/servers.ts +++ b/resources/js/dashboard/servers.ts @@ -5,16 +5,16 @@ import {showLoader} from "../utility/utility"; import {hideLoader} from "../utility/utility"; // Make a GET request to fetch mutes data -showLoader(); +showLoader('server_list_loader'); axios.get(serversListUrl) .then(response => { // Handle successful response - hideLoader(); + hideLoader('server_list_loader'); appendTableData(constructTableRows(response.data), 'serverList'); }) .catch(error => { // Handle error - hideLoader(); + hideLoader('server_list_loader'); console.error('Error:', error); }); diff --git a/resources/js/groups/groups.ts b/resources/js/groups/groups.ts new file mode 100644 index 0000000..6e96396 --- /dev/null +++ b/resources/js/groups/groups.ts @@ -0,0 +1,5 @@ +$(document).ready(function () { + $('#server_ids').select2({ + placeholder: 'Select Servers', + }); +}); diff --git a/resources/js/groups/list.ts b/resources/js/groups/list.ts new file mode 100644 index 0000000..5a546cc --- /dev/null +++ b/resources/js/groups/list.ts @@ -0,0 +1,29 @@ +import DataTable from 'datatables.net-dt'; +let dataTable = null; +function loadGroups() { + dataTable = new DataTable("#groupsList", { + "processing": true, + "serverSide": true, + "ajax": { + "url": groupsListUrl, + "headers": { + "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content') + }, + "type": "POST", + "dataType": "json" + }, + "language": { + "search": "Search by group", + 'processing': '
' + + }, + order: [[0, 'desc']], + "columns": [ + {"data": "id"}, + {"data": "name"}, + {"data": "flags"}, + ] + }); +} +loadGroups(); + diff --git a/resources/js/utility/utility.ts b/resources/js/utility/utility.ts index 8741de5..87ca1c5 100755 --- a/resources/js/utility/utility.ts +++ b/resources/js/utility/utility.ts @@ -62,10 +62,10 @@ export function calculateProgress(created: string, ends: string): number { return progress; } -export function showLoader() { - document.getElementById("loader").style.display = "block"; +export function showLoader(loaderId = 'loader') { + document.getElementById(loaderId).style.display = "block"; } -export function hideLoader() { - document.getElementById("loader").style.display = "none"; +export function hideLoader(loaderId = 'loader') { + document.getElementById(loaderId).style.display = "none"; } diff --git a/resources/views/admin/admins/create.blade.php b/resources/views/admin/admins/create.blade.php index b859f47..493adbb 100755 --- a/resources/views/admin/admins/create.blade.php +++ b/resources/views/admin/admins/create.blade.php @@ -44,8 +44,18 @@ @endforeach -
-
+
+ + +
+ +
+ + +
+ +
+

@foreach($permissions as $permission)
@@ -55,8 +65,19 @@
@endforeach +
+
+ -
diff --git a/resources/views/admin/admins/edit.blade.php b/resources/views/admin/admins/edit.blade.php index 7babdaf..a952cf0 100755 --- a/resources/views/admin/admins/edit.blade.php +++ b/resources/views/admin/admins/edit.blade.php @@ -22,8 +22,12 @@
-
Edit Admin
-
+
Edit Admin Groups
+ @if($allowMigrate) + + @else + + @endif @csrf
@@ -37,21 +41,46 @@ @endforeach
- - -
-
- @foreach($permissions as $permission) -
- permission, $adminPermissions) ? 'checked' : '' }}> - -
- @endforeach +
+ +
+ + @if($allowMigrate) +
+
+ @foreach($permissions as $permission) +
+ permission, $adminPermissions) ? 'checked' : '' }}> + +
+ @endforeach +
+
+
+ Note: Admin will be moved to below groups selected for ALL Servers. Existing individual permissions of admin will be removed! +
+ + + @else + + + @endif
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php index 5670212..8d034d9 100755 --- a/resources/views/admin/dashboard.blade.php +++ b/resources/views/admin/dashboard.blade.php @@ -116,6 +116,7 @@
+
diff --git a/resources/views/admin/groups/create.blade.php b/resources/views/admin/groups/create.blade.php new file mode 100644 index 0000000..29a82d7 --- /dev/null +++ b/resources/views/admin/groups/create.blade.php @@ -0,0 +1,68 @@ +@extends('layouts.app') +@section('content') + @if (session('success')) + + @endif + @if (session('error')) + + @endif + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif +
+
+
+
+
Add New Group
+ + @csrf +
+ Note: Adding permissions to an existing group for new servers will append the new permissions to the existing set, applying for all associated servers. +
+
+ + +
+ +
+ +
+
+
+
+ @foreach($permissions as $permission) +
+ + +
+ @endforeach +
+
+
+ + +
+
+
+
+ +
+
+
+
+@endsection +@vite(['resources/js/groups/groups.ts']) diff --git a/resources/views/admin/groups/edit.blade.php b/resources/views/admin/groups/edit.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/admin/groups/list.blade.php b/resources/views/admin/groups/list.blade.php new file mode 100644 index 0000000..2832cc6 --- /dev/null +++ b/resources/views/admin/groups/list.blade.php @@ -0,0 +1,43 @@ +@php use App\Helpers\PermissionsHelper; @endphp +@extends('layouts.app') + +@section('content') + @if (session('success')) + + @endif + @if (session('error')) + + @endif +
+
+
+
+ Groups +
+
+
+ @if(PermissionsHelper::isSuperAdmin()) + + @endif +
+ + + + + + + + + + + +
IDGroupFlags
+
+
+
+
+@endsection +@vite(['resources/js/groups/list.ts']) + diff --git a/resources/views/admin/servers/players.blade.php b/resources/views/admin/servers/players.blade.php index ac524e7..f0f6e42 100755 --- a/resources/views/admin/servers/players.blade.php +++ b/resources/views/admin/servers/players.blade.php @@ -1,5 +1,10 @@ @php use App\Helpers\PermissionsHelper; @endphp
+ @if (!empty($error)) +
+ {{$error}} +
+ @endif diff --git a/resources/views/components/loader.blade.php b/resources/views/components/loader.blade.php new file mode 100644 index 0000000..4d16c51 --- /dev/null +++ b/resources/views/components/loader.blade.php @@ -0,0 +1,5 @@ +
+
+ Loading... +
+
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index d4bf2c3..d34cbab 100755 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -18,10 +18,6 @@ @vite(['resources/js/mdb.umd.min.js']) @include('partials.scripts') -
-
- Loading... -
-
+ diff --git a/resources/views/partials/nav.blade.php b/resources/views/partials/nav.blade.php index abf5ec1..8dd8858 100755 --- a/resources/views/partials/nav.blade.php +++ b/resources/views/partials/nav.blade.php @@ -29,6 +29,10 @@ Add Mute @endif + @if(PermissionsHelper::isSuperAdmin()) + + Create Group + @endif @if(!empty(Auth::user())) Logout diff --git a/resources/views/partials/scripts.blade.php b/resources/views/partials/scripts.blade.php index a595279..f2e80aa 100755 --- a/resources/views/partials/scripts.blade.php +++ b/resources/views/partials/scripts.blade.php @@ -24,6 +24,7 @@ function getPlayerInfoUrl(serverId) { const adminListUrl = '{!! env('VITE_SITE_DIR') !!}/list/admins'; const bansListUrl = '{!! env('VITE_SITE_DIR') !!}/list/bans'; const playerActionUrl = '{!! env('VITE_SITE_DIR') !!}/players/action'; + const groupsListUrl = '{!! env('VITE_SITE_DIR') !!}/group/list'; diff --git a/routes/web.php b/routes/web.php index af6a9ff..73f14b6 100755 --- a/routes/web.php +++ b/routes/web.php @@ -43,12 +43,21 @@ Route::prefix('admin')->group(function () { Route::get('/create', [AdminController::class, 'create'])->name('admin.create')->middleware('superadmin'); Route::post('/store', [AdminController::class, 'store'])->name('admin.store')->middleware('superadmin'); - Route::get('/edit/{player_steam}/{server_id}', [AdminController::class, 'edit'])->name('admin.edit')->middleware('superadmin'); - Route::post('/update/{player_steam}', [AdminController::class, 'update'])->name('admin.update')->middleware('superadmin'); + Route::get('/edit/{player_steam}/{server_id}', [AdminController::class, 'editAdmin'])->name('admin.edit')->middleware('superadmin'); + Route::post('/update/{player_steam}', [AdminController::class, 'updateAdmin'])->name('admin.update')->middleware('superadmin'); Route::get('/delete/{player_steam}', [AdminController::class, 'showDeleteForm'])->name('admin.showDeleteForm')->middleware('superadmin'); Route::post('/delete/{player_steam}', [AdminController::class, 'delete'])->name('admin.delete')->middleware('superadmin'); + Route::get('/groups/edit/{player_steam}', [AdminController::class, 'editAdminGroup'])->name('admin.group.edit')->middleware('superadmin'); + Route::post('/groups/update/{player_steam}', [AdminController::class, 'updateAdminGroup'])->name('admin.groups.update')->middleware('superadmin'); + }); + Route::prefix('group')->group(function () { + Route::get('/create', [AdminController::class, 'createGroup'])->name('group.create')->middleware('superadmin'); + Route::post('/store', [AdminController::class, 'storeGroup'])->name('group.store')->middleware('superadmin'); + Route::get('/list', [AdminController::class, 'groups'])->name('groups.list')->middleware('superadmin'); + Route::post('/list', [AdminController::class, 'getGroupsList'])->name('group.list')->middleware('superadmin'); + }); Route::prefix('players')->group(function () { Route::post('ban', [BansController::class, 'store'])->name('ban.store')->middleware('permission.ban'); Route::post('mute', [MutesController::class, 'store'])->name('mute.store')->middleware('permission.ban'); @@ -79,7 +88,7 @@ return view('requirement'); })->name('requirement'); Route::get('/setup', function () { - if (env('SETUP') === 'true') { + if (env('SETUP') === true) { return redirect('/'); } return view('setup'); diff --git a/vite.config.js b/vite.config.js index 61ecb7e..2e29984 100755 --- a/vite.config.js +++ b/vite.config.js @@ -24,7 +24,9 @@ export default defineConfig({ 'resources/js/bootstrap.js', 'resources/js/mdb.es.min.js', 'resources/js/mdb.umd.min.js', - 'resources/js/app.js' + 'resources/js/app.js', + 'resources/js/groups/groups.ts', + 'resources/js/groups/list.ts' ], refresh: true, }),