* @license MIT */ namespace Pterodactyl\Addons\AdvancedAdmin\Http\Controllers\Client; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Pterodactyl\Addons\AdvancedAdmin\Models\ConsolePlayerCache; use Pterodactyl\Addons\AdvancedAdmin\Services\Console\AutocompleteEngine; use Pterodactyl\Models\Server; class AutocompleteController extends Controller { public function __construct(private readonly AutocompleteEngine $engine) {} /** GET /api/client/servers/{server}/addons/console/autocomplete?q= */ public function suggest(Request $request, Server $server): JsonResponse { $this->requireAccess($request, $server); // Pterodactyl's route model binding resolves by UUID; ensure we have the integer PK if ($server->id === null) { $server = Server::where('uuid', $server->uuid)->firstOrFail(); } $query = (string) $request->get('q', ''); $suggestions = $this->engine->suggest( $server->id, $server->node_id, $server->egg_id, $query, ); return response()->json(['data' => $suggestions]); } /** GET /api/client/servers/{server}/addons/console/players */ public function players(Request $request, Server $server): JsonResponse { $this->requireAccess($request, $server); $players = ConsolePlayerCache::query() ->where('server_id', $server->id) ->where('last_seen_at', '>=', now()->subMinutes( config('advanced-admin.console.player_cache_ttl_minutes', 30) )) ->orderBy('player_name') ->pluck('player_name'); return response()->json(['data' => $players]); } private function requireAccess(Request $request, Server $server): void { $user = $request->user(); abort_unless($user, 401); $isOwner = $server->owner_id === $user->id; $isAdmin = $user->root_admin; if (!$isOwner && !$isAdmin) { $subuser = $server->subusers()->where('user_id', $user->id)->first(); abort_unless($subuser && in_array('control.console', $subuser->permissions ?? [], true), 403); } } }