getEggVariable($server, 'MODPACK_PLATFORM'); $modpackId = $this->getEggVariable($server, 'MODPACK_ID'); // Also check platform-specific variables if (empty($modpackId)) { $modpackId = match($platform) { 'curseforge' => $this->getEggVariable($server, 'CURSEFORGE_ID'), 'modrinth' => $this->getEggVariable($server, 'MODRINTH_PROJECT_ID'), 'ftb' => $this->getEggVariable($server, 'FTB_MODPACK_ID'), 'technic' => $this->getEggVariable($server, 'TECHNIC_SLUG'), default => null }; } // 2. If no egg variables, try file detection if (empty($platform) || empty($modpackId)) { $detected = $this->detectFromFiles($server); $platform = $platform ?: ($detected['platform'] ?? null); $modpackId = $modpackId ?: ($detected['modpack_id'] ?? null); } // 3. If still nothing, return helpful error if (empty($platform) || empty($modpackId)) { return response()->json([ 'success' => false, 'message' => 'Could not detect modpack. Set MODPACK_PLATFORM and MODPACK_ID in startup variables.', ]); } // 4. Check the appropriate API try { $versionData = match($platform) { 'curseforge' => $this->checkCurseForge($modpackId), 'modrinth' => $this->checkModrinth($modpackId), 'ftb' => $this->checkFTB($modpackId), 'technic' => $this->checkTechnic($modpackId), default => throw new \Exception("Unknown platform: {$platform}") }; return response()->json([ 'success' => true, 'platform' => $platform, 'modpack_id' => $modpackId, 'modpack_name' => $versionData['name'] ?? 'Unknown', 'latest_version' => $versionData['version'] ?? 'Unknown', 'status' => 'checked', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'platform' => $platform, 'modpack_id' => $modpackId, 'error' => $e->getMessage(), ]); } } /** * Get an egg variable value for a server */ private function getEggVariable(Server $server, string $name): ?string { $variable = $server->variables() ->where('env_variable', $name) ->first(); return $variable?->server_value; } /** * Attempt to detect modpack from files */ private function detectFromFiles(Server $server): array { try { // Try CurseForge manifest.json $manifest = $this->readServerFile($server, 'manifest.json'); if ($manifest) { $data = json_decode($manifest, true); if (isset($data['manifestType']) && $data['manifestType'] === 'minecraftModpack') { return [ 'platform' => 'curseforge', 'modpack_id' => $data['projectID'] ?? null, 'name' => $data['name'] ?? null, 'version' => $data['version'] ?? null, ]; } } // Try Modrinth modrinth.index.json $modrinthIndex = $this->readServerFile($server, 'modrinth.index.json'); if ($modrinthIndex) { $data = json_decode($modrinthIndex, true); if (isset($data['formatVersion'])) { return [ 'platform' => 'modrinth', 'modpack_id' => $data['dependencies']['minecraft'] ?? null, 'name' => $data['name'] ?? null, 'version' => $data['versionId'] ?? null, ]; } } } catch (\Exception $e) { // File detection failed, return empty } return []; } /** * Read a file from the server via Wings */ private function readServerFile(Server $server, string $path): ?string { try { $this->fileRepository->setServer($server); return $this->fileRepository->getContent($path); } catch (\Exception $e) { return null; } } /** * Check CurseForge API for latest version */ private function checkCurseForge(string $modpackId): array { $apiKey = $this->blueprint->dbGet('modpackchecker', 'curseforge_api_key'); if (empty($apiKey)) { throw new \Exception('CurseForge API key not configured'); } $response = Http::withHeaders([ 'x-api-key' => $apiKey, 'Accept' => 'application/json', ])->get("https://api.curseforge.com/v1/mods/{$modpackId}"); if (!$response->successful()) { throw new \Exception('CurseForge API request failed: ' . $response->status()); } $data = $response->json(); $mod = $data['data'] ?? []; return [ 'name' => $mod['name'] ?? 'Unknown', 'version' => $mod['latestFiles'][0]['displayName'] ?? 'Unknown', ]; } /** * Check Modrinth API for latest version */ private function checkModrinth(string $projectId): array { $response = Http::withHeaders([ 'Accept' => 'application/json', 'User-Agent' => 'FirefrostGaming/ModpackChecker/1.0', ])->get("https://api.modrinth.com/v2/project/{$projectId}/version"); if (!$response->successful()) { throw new \Exception('Modrinth API request failed: ' . $response->status()); } $versions = $response->json(); $latest = $versions[0] ?? []; // Get project name $projectResponse = Http::withHeaders([ 'Accept' => 'application/json', 'User-Agent' => 'FirefrostGaming/ModpackChecker/1.0', ])->get("https://api.modrinth.com/v2/project/{$projectId}"); $project = $projectResponse->json(); return [ 'name' => $project['title'] ?? 'Unknown', 'version' => $latest['version_number'] ?? 'Unknown', ]; } /** * Check FTB (modpacks.ch) API for latest version */ private function checkFTB(string $modpackId): array { $response = Http::withHeaders([ 'Accept' => 'application/json', ])->get("https://api.modpacks.ch/public/modpack/{$modpackId}"); if (!$response->successful()) { throw new \Exception('FTB API request failed: ' . $response->status()); } $data = $response->json(); return [ 'name' => $data['name'] ?? 'Unknown', 'version' => $data['versions'][0]['name'] ?? 'Unknown', ]; } /** * Check Technic API for latest version */ private function checkTechnic(string $slug): array { $response = Http::withHeaders([ 'Accept' => 'application/json', ])->get("https://api.technicpack.net/modpack/{$slug}?build=1"); if (!$response->successful()) { throw new \Exception('Technic API request failed: ' . $response->status()); } $data = $response->json(); return [ 'name' => $data['displayName'] ?? $data['name'] ?? 'Unknown', 'version' => $data['version'] ?? 'Unknown', ]; } /** * Get cached update status for all servers (dashboard badge view) * Called once on page load, returns status for all user's servers */ public function getStatus(Request $request): JsonResponse { $user = $request->user(); // Get all servers the user has access to $serverIds = $user->accessibleServers()->pluck('id')->toArray(); // Query our cache table for these servers $statuses = DB::table('modpackchecker_servers') ->whereIn('server_id', $serverIds) ->get() ->keyBy('server_uuid'); $result = []; foreach ($statuses as $uuid => $status) { $result[$uuid] = [ 'update_available' => (bool) $status->update_available, 'modpack_name' => $status->modpack_name, 'current_version' => $status->current_version, 'latest_version' => $status->latest_version, ]; } return response()->json($result); } }