Phase 5 Components (completing Pyrrhus's work): NEW FILES: - views/dashboard/UpdateBadge.tsx: Dashboard badge component - Shows 🟢 (up to date) or 🟠 (update available) next to server names - Global cache prevents multiple API calls on page load - Reads from local database, never calls external APIs directly - Fire (#FF6B35) and Frost (#4ECDC4) brand colors - console/CheckModpackUpdates.php: Laravel cron command - Run with: php artisan modpackchecker:check - Loops through servers with MODPACK_PLATFORM variable - Checks CurseForge, Modrinth, FTB, Technic APIs - Rate limited (2s sleep between checks) - Stores results in modpackchecker_servers table UPDATED FILES: - Controllers/ModpackAPIController.php: - Added getStatus() method for dashboard badge endpoint - Returns all user's servers' update status in single query - Added DB facade import - routes/client.php: - Added GET /extensions/modpackchecker/status route - build.sh: - Complete rewrite for Phase 5 - Handles both console widget AND dashboard badge - Auto-detects extension directory (dev vs extensions) - Copies CheckModpackUpdates.php to app/Console/Commands/ - Injects UpdateBadge into ServerRow.tsx - Clear status output and next-steps guide Architecture (Gemini-approved): CRON (hourly) → Database cache → Single API endpoint → React badge Dashboard badge is 'dumb' - only reads from cache, never external APIs Completing work started by Chronicler #62 (Pyrrhus). UpdateBadge.tsx was lost in Blueprint corruption - reconstructed from handoff notes and architecture documentation. Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
189 lines
6.2 KiB
PHP
189 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace Pterodactyl\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Pterodactyl\Models\Server;
|
|
|
|
class CheckModpackUpdates extends Command
|
|
{
|
|
protected $signature = 'modpackchecker:check';
|
|
protected $description = 'Check all servers for modpack updates';
|
|
|
|
public function handle(): int
|
|
{
|
|
$this->info('Starting modpack update check...');
|
|
|
|
// Get all servers that have modpack variables set
|
|
$servers = Server::whereHas('variables', function ($q) {
|
|
$q->where('env_variable', 'MODPACK_PLATFORM');
|
|
})->get();
|
|
|
|
$this->info("Found {$servers->count()} servers with modpack configuration");
|
|
|
|
foreach ($servers as $server) {
|
|
$this->checkServer($server);
|
|
// Rate limiting - sleep between checks
|
|
sleep(2);
|
|
}
|
|
|
|
$this->info('Modpack update check complete!');
|
|
return 0;
|
|
}
|
|
|
|
private function checkServer(Server $server): void
|
|
{
|
|
$this->line("Checking: {$server->name} ({$server->uuid})");
|
|
|
|
try {
|
|
// Get platform and modpack ID from variables
|
|
$platform = $this->getVariable($server, 'MODPACK_PLATFORM');
|
|
$modpackId = $this->getVariable($server, 'MODPACK_ID');
|
|
|
|
if (!$platform || !$modpackId) {
|
|
$this->warn(" Skipping - missing platform or modpack ID");
|
|
return;
|
|
}
|
|
|
|
// Get latest version from API
|
|
$latestData = $this->fetchLatestVersion($platform, $modpackId);
|
|
|
|
if (!$latestData) {
|
|
$this->error(" Failed to fetch version data");
|
|
$this->updateDatabase($server, [
|
|
'platform' => $platform,
|
|
'modpack_id' => $modpackId,
|
|
'error_message' => 'Failed to fetch version data',
|
|
'update_available' => false,
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Get current version (from variable or file - for now just use variable)
|
|
$currentVersion = $this->getVariable($server, 'MODPACK_CURRENT_VERSION');
|
|
|
|
// Determine if update is available
|
|
$updateAvailable = $currentVersion && $currentVersion !== $latestData['version'];
|
|
|
|
$this->updateDatabase($server, [
|
|
'platform' => $platform,
|
|
'modpack_id' => $modpackId,
|
|
'modpack_name' => $latestData['name'],
|
|
'current_version' => $currentVersion,
|
|
'latest_version' => $latestData['version'],
|
|
'update_available' => $updateAvailable,
|
|
'error_message' => null,
|
|
]);
|
|
|
|
$status = $updateAvailable ? '🟠 UPDATE AVAILABLE' : '🟢 Up to date';
|
|
$this->info(" {$status}: {$latestData['name']} - {$latestData['version']}");
|
|
|
|
} catch (\Exception $e) {
|
|
$this->error(" Error: {$e->getMessage()}");
|
|
$this->updateDatabase($server, [
|
|
'error_message' => $e->getMessage(),
|
|
'update_available' => false,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function getVariable(Server $server, string $name): ?string
|
|
{
|
|
$variable = $server->variables()
|
|
->where('env_variable', $name)
|
|
->first();
|
|
return $variable?->server_value;
|
|
}
|
|
|
|
private function fetchLatestVersion(string $platform, string $modpackId): ?array
|
|
{
|
|
return match($platform) {
|
|
'modrinth' => $this->checkModrinth($modpackId),
|
|
'curseforge' => $this->checkCurseForge($modpackId),
|
|
'ftb' => $this->checkFTB($modpackId),
|
|
'technic' => $this->checkTechnic($modpackId),
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
private function checkModrinth(string $projectId): ?array
|
|
{
|
|
$response = Http::withHeaders([
|
|
'User-Agent' => 'FirefrostGaming/ModpackChecker/1.0',
|
|
])->get("https://api.modrinth.com/v2/project/{$projectId}");
|
|
|
|
if (!$response->successful()) return null;
|
|
$project = $response->json();
|
|
|
|
$versionResponse = Http::withHeaders([
|
|
'User-Agent' => 'FirefrostGaming/ModpackChecker/1.0',
|
|
])->get("https://api.modrinth.com/v2/project/{$projectId}/version");
|
|
|
|
$versions = $versionResponse->json();
|
|
|
|
return [
|
|
'name' => $project['title'] ?? 'Unknown',
|
|
'version' => $versions[0]['version_number'] ?? 'Unknown',
|
|
];
|
|
}
|
|
|
|
private function checkCurseForge(string $modpackId): ?array
|
|
{
|
|
$apiKey = DB::table('settings')
|
|
->where('key', 'like', '%modpackchecker::curseforge_api_key%')
|
|
->value('value');
|
|
|
|
if (!$apiKey) return null;
|
|
|
|
$response = Http::withHeaders([
|
|
'x-api-key' => $apiKey,
|
|
])->get("https://api.curseforge.com/v1/mods/{$modpackId}");
|
|
|
|
if (!$response->successful()) return null;
|
|
$data = $response->json()['data'] ?? [];
|
|
|
|
return [
|
|
'name' => $data['name'] ?? 'Unknown',
|
|
'version' => $data['latestFiles'][0]['displayName'] ?? 'Unknown',
|
|
];
|
|
}
|
|
|
|
private function checkFTB(string $modpackId): ?array
|
|
{
|
|
$response = Http::get("https://api.modpacks.ch/public/modpack/{$modpackId}");
|
|
if (!$response->successful()) return null;
|
|
$data = $response->json();
|
|
|
|
return [
|
|
'name' => $data['name'] ?? 'Unknown',
|
|
'version' => $data['versions'][0]['name'] ?? 'Unknown',
|
|
];
|
|
}
|
|
|
|
private function checkTechnic(string $slug): ?array
|
|
{
|
|
$response = Http::get("https://api.technicpack.net/modpack/{$slug}?build=1");
|
|
if (!$response->successful()) return null;
|
|
$data = $response->json();
|
|
|
|
return [
|
|
'name' => $data['displayName'] ?? $data['name'] ?? 'Unknown',
|
|
'version' => $data['version'] ?? 'Unknown',
|
|
];
|
|
}
|
|
|
|
private function updateDatabase(Server $server, array $data): void
|
|
{
|
|
DB::table('modpackchecker_servers')->updateOrInsert(
|
|
['server_id' => $server->id],
|
|
array_merge($data, [
|
|
'server_uuid' => $server->uuid,
|
|
'last_checked' => now(),
|
|
'updated_at' => now(),
|
|
])
|
|
);
|
|
}
|
|
}
|