NEW: app/Services/ModpackApiService.php
- Centralized API logic for all 4 platforms
- Technic build number cached for 12 hours (RV-Ready)
- Single source of truth for API calls
Controller (ModpackAPIController.php):
- Now uses injected ModpackApiService instead of duplicated code
- Added RateLimiter: 2 requests/minute per server on manualCheck()
- Returns 429 with countdown when rate limited
- Removed 400+ lines of duplicated API code
Console Command (CheckModpackUpdates.php):
- FIXED: updateDatabase() now uses server_uuid (not server_id)
- FIXED: status column uses strings ('update_available', 'up_to_date', 'error')
- FIXED: Technic API now uses dynamic build via service
- Now uses injected ModpackApiService
SECURITY:
- Rate limiting prevents API key abuse via button spam
- Technic build caching reduces external API calls
Reviewed by: Gemini AI (Architecture Consultant)
Signed-off-by: Claude (Chronicler #63) <claude@firefrostgaming.com>
160 lines
5.5 KiB
PHP
160 lines
5.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* =============================================================================
|
|
* MODPACK VERSION CHECKER - CRON COMMAND
|
|
* =============================================================================
|
|
*
|
|
* Laravel Artisan command that checks all servers for modpack updates.
|
|
* This is the "brain" that populates the cache used by the dashboard badges.
|
|
*
|
|
* USAGE:
|
|
* php artisan modpackchecker:check
|
|
*
|
|
* RECOMMENDED CRON SCHEDULE:
|
|
* # Check for updates every 6 hours (adjust based on your server count)
|
|
* 0 */6 * * * cd /var/www/pterodactyl && php artisan modpackchecker:check >> /dev/null 2>&1
|
|
*
|
|
* HOW IT WORKS:
|
|
* 1. Finds all servers with MODPACK_PLATFORM egg variable set
|
|
* 2. Loops through each server, checking via ModpackApiService
|
|
* 3. Stores results in modpackchecker_servers database table
|
|
* 4. Dashboard badges read from this table (never calling APIs directly)
|
|
*
|
|
* RATE LIMITING:
|
|
* Each API call is followed by a 2-second sleep to avoid rate limits.
|
|
* For 50 servers, a full check takes ~2 minutes.
|
|
*
|
|
* @package Pterodactyl\Console\Commands
|
|
* @author Firefrost Gaming / Frostystyle <dev@firefrostgaming.com>
|
|
* @version 1.0.0
|
|
* @see ModpackApiService.php (centralized API logic)
|
|
* @see ModpackAPIController.php (provides getStatus endpoint for badges)
|
|
* =============================================================================
|
|
*/
|
|
|
|
namespace Pterodactyl\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Pterodactyl\Models\Server;
|
|
use Pterodactyl\Services\ModpackApiService;
|
|
|
|
class CheckModpackUpdates extends Command
|
|
{
|
|
protected $signature = 'modpackchecker:check';
|
|
protected $description = 'Check all servers for modpack updates';
|
|
|
|
public function __construct(private ModpackApiService $apiService)
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*
|
|
* @return int Exit code (0 = success)
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Check a single server for modpack updates.
|
|
*
|
|
* @param Server $server The server to check
|
|
* @return void
|
|
*/
|
|
private function checkServer(Server $server): void
|
|
{
|
|
$this->line("Checking: {$server->name} ({$server->uuid})");
|
|
|
|
try {
|
|
$platform = $this->getVariable($server, 'MODPACK_PLATFORM');
|
|
$modpackId = $this->getVariable($server, 'MODPACK_ID');
|
|
|
|
if (!$platform || !$modpackId) {
|
|
$this->warn(" Skipping - missing platform or modpack ID");
|
|
return;
|
|
}
|
|
|
|
// Centralized API Call via Service
|
|
$latestData = $this->apiService->fetchLatestVersion($platform, $modpackId);
|
|
$currentVersion = $this->getVariable($server, 'MODPACK_CURRENT_VERSION');
|
|
$updateAvailable = $currentVersion && $currentVersion !== $latestData['version'];
|
|
|
|
$this->updateDatabase($server, [
|
|
'platform' => $platform,
|
|
'modpack_id' => $modpackId,
|
|
'modpack_name' => $latestData['name'],
|
|
'current_version' => $currentVersion,
|
|
'latest_version' => $latestData['version'],
|
|
'status' => $updateAvailable ? 'update_available' : 'up_to_date',
|
|
'error_message' => null,
|
|
'last_checked' => now(),
|
|
]);
|
|
|
|
$statusIcon = $updateAvailable ? '🟠 UPDATE AVAILABLE' : '🟢 Up to date';
|
|
$this->info(" {$statusIcon}: {$latestData['name']} - {$latestData['version']}");
|
|
|
|
} catch (\Exception $e) {
|
|
$this->error(" Error: {$e->getMessage()}");
|
|
$this->updateDatabase($server, [
|
|
'status' => 'error',
|
|
'error_message' => $e->getMessage(),
|
|
'last_checked' => now(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get an egg variable value from a server.
|
|
*
|
|
* @param Server $server The server to query
|
|
* @param string $name The variable name
|
|
* @return string|null The variable value, or null if not set
|
|
*/
|
|
private function getVariable(Server $server, string $name): ?string
|
|
{
|
|
$variable = $server->variables()
|
|
->where('env_variable', $name)
|
|
->first();
|
|
return $variable?->server_value;
|
|
}
|
|
|
|
/**
|
|
* Store or update the modpack check results in the database.
|
|
*
|
|
* Uses updateOrInsert for upsert behavior.
|
|
* The server_uuid column is the unique key for matching.
|
|
*
|
|
* @param Server $server The server being checked
|
|
* @param array $data The data to store
|
|
* @return void
|
|
*/
|
|
private function updateDatabase(Server $server, array $data): void
|
|
{
|
|
DB::table('modpackchecker_servers')->updateOrInsert(
|
|
['server_uuid' => $server->uuid],
|
|
array_merge($data, ['updated_at' => now()])
|
|
);
|
|
}
|
|
}
|