From d413bca7e90947c4fd7f76c71e6be0bb945ae0a6 Mon Sep 17 00:00:00 2001 From: BaileyCodes Date: Thu, 11 Jun 2026 21:05:57 -0500 Subject: [PATCH] fix: defensive ServiceProvider (class_exists guards), fixed install.sh apply_patch logic --- .../AdvancedAdmin/AddonServiceProvider.php | 100 +++++++++++------- install.sh | 16 ++- scripts/fix_asp.py | 32 ++++++ 3 files changed, 103 insertions(+), 45 deletions(-) create mode 100644 scripts/fix_asp.py diff --git a/app/Addons/AdvancedAdmin/AddonServiceProvider.php b/app/Addons/AdvancedAdmin/AddonServiceProvider.php index a09c4a4..1158c28 100644 --- a/app/Addons/AdvancedAdmin/AddonServiceProvider.php +++ b/app/Addons/AdvancedAdmin/AddonServiceProvider.php @@ -16,13 +16,6 @@ namespace Pterodactyl\Addons\AdvancedAdmin; use Illuminate\Support\ServiceProvider; use Illuminate\Console\Scheduling\Schedule; -use Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\CollectNetworkMetricsJob; -use Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\DownsampleNetworkMetricsJob; -use Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\PruneNetworkMetricsJob; -use Pterodactyl\Addons\AdvancedAdmin\Jobs\Revisions\PruneFileRevisionsJob; -use Pterodactyl\Addons\AdvancedAdmin\Jobs\Console\ExpireStalePlayersJob; -use Pterodactyl\Addons\AdvancedAdmin\Listeners\Console\ConsoleOutputListener; -use Pterodactyl\Addons\AdvancedAdmin\Services\Roles\PermissionResolver; class AddonServiceProvider extends ServiceProvider { @@ -38,7 +31,10 @@ class AddonServiceProvider extends ServiceProvider ); // Bind the PermissionResolver as a singleton for performance - $this->app->singleton(PermissionResolver::class); + $resolver = 'Pterodactyl\Addons\AdvancedAdmin\Services\Roles\PermissionResolver'; + if (class_exists($resolver)) { + $this->app->singleton($resolver); + } } /** @@ -47,40 +43,61 @@ class AddonServiceProvider extends ServiceProvider public function boot(): void { // ── Routes ──────────────────────────────────────────────────────────── - $this->loadRoutesFrom(__DIR__ . '/../../routes/addon-client.php'); - $this->loadRoutesFrom(__DIR__ . '/../../routes/addon-application.php'); + $clientRoutes = __DIR__ . '/../../routes/addon-client.php'; + $appRoutes = __DIR__ . '/../../routes/addon-application.php'; + + if (file_exists($clientRoutes)) { + $this->loadRoutesFrom($clientRoutes); + } + if (file_exists($appRoutes)) { + $this->loadRoutesFrom($appRoutes); + } // ── Migrations ──────────────────────────────────────────────────────── - $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations/addon'); + $migrationsPath = __DIR__ . '/../../database/migrations/addon'; + if (is_dir($migrationsPath)) { + $this->loadMigrationsFrom($migrationsPath); + } // ── Scheduled Jobs ──────────────────────────────────────────────────── $this->callAfterResolving(Schedule::class, function (Schedule $schedule) { - if (config('advanced-admin.network.enabled')) { - $schedule->job(CollectNetworkMetricsJob::class) + // Network metric collection + $collectJob = 'Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\CollectNetworkMetricsJob'; + $downsampleJob = 'Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\DownsampleNetworkMetricsJob'; + $pruneNetJob = 'Pterodactyl\Addons\AdvancedAdmin\Jobs\Network\PruneNetworkMetricsJob'; + $pruneRevJob = 'Pterodactyl\Addons\AdvancedAdmin\Jobs\Revisions\PruneFileRevisionsJob'; + $expireJob = 'Pterodactyl\Addons\AdvancedAdmin\Jobs\Console\ExpireStalePlayersJob'; + + if (config('advanced-admin.network.enabled') && class_exists($collectJob)) { + $schedule->job(new $collectJob()) ->everyTenSeconds() ->withoutOverlapping(5) ->onOneServer(); - $schedule->job(DownsampleNetworkMetricsJob::class) - ->hourly() - ->withoutOverlapping(30) - ->onOneServer(); + if (class_exists($downsampleJob)) { + $schedule->job(new $downsampleJob()) + ->hourly() + ->withoutOverlapping(30) + ->onOneServer(); + } - $schedule->job(PruneNetworkMetricsJob::class) + if (class_exists($pruneNetJob)) { + $schedule->job(new $pruneNetJob()) + ->daily() + ->withoutOverlapping(60) + ->onOneServer(); + } + } + + if (config('advanced-admin.revisions.enabled') && class_exists($pruneRevJob)) { + $schedule->job(new $pruneRevJob()) ->daily() ->withoutOverlapping(60) ->onOneServer(); } - if (config('advanced-admin.revisions.enabled')) { - $schedule->job(PruneFileRevisionsJob::class) - ->daily() - ->withoutOverlapping(60) - ->onOneServer(); - } - - if (config('advanced-admin.console.enabled')) { - $schedule->job(ExpireStalePlayersJob::class) + if (config('advanced-admin.console.enabled') && class_exists($expireJob)) { + $schedule->job(new $expireJob()) ->everyFiveMinutes() ->withoutOverlapping(4); } @@ -88,23 +105,26 @@ class AddonServiceProvider extends ServiceProvider // ── Event Listeners ─────────────────────────────────────────────────── // Console output listener (parses join/leave for player cache) - // Hooked to Pterodactyl's existing console output events if they exist. - // Falls back gracefully if event is not fired by this panel version. - if (class_exists(\Pterodactyl\Events\Server\ConsoleDataReceived::class)) { - $this->app['events']->listen( - \Pterodactyl\Events\Server\ConsoleDataReceived::class, - ConsoleOutputListener::class - ); + $consoleEvent = 'Pterodactyl\Events\Server\ConsoleDataReceived'; + $consoleListener = 'Pterodactyl\Addons\AdvancedAdmin\Listeners\Console\ConsoleOutputListener'; + if (class_exists($consoleEvent) && class_exists($consoleListener)) { + $this->app['events']->listen($consoleEvent, $consoleListener); } // ── Artisan Commands ────────────────────────────────────────────────── if ($this->app->runningInConsole()) { - $this->commands([ - Console\Commands\AddonPatchCommand::class, - Console\Commands\AddonInstallCommand::class, - Console\Commands\AddonUninstallCommand::class, - Console\Commands\CollectNetworkCommand::class, - ]); + $commands = []; + $candidates = [ + 'Pterodactyl\Addons\AdvancedAdmin\Console\Commands\CollectNetworkCommand', + ]; + foreach ($candidates as $cmd) { + if (class_exists($cmd)) { + $commands[] = $cmd; + } + } + if (!empty($commands)) { + $this->commands($commands); + } } // ── Publishable Assets ──────────────────────────────────────────────── diff --git a/install.sh b/install.sh index 6dccf63..c0229e7 100644 --- a/install.sh +++ b/install.sh @@ -133,15 +133,22 @@ apply_patch() { local REPLACE="$3" local LABEL="$4" - if grep -qF "$SEARCH" "$FILE"; then + # Check if the injected content is already present (not the anchor) + if grep -qF "$REPLACE" "$FILE"; then warn "Patch '${LABEL}' already applied — skipping." return 0 fi - if ! grep -qF "$REPLACE" "$FILE"; then - # Use python for reliable multiline sed (available on most Linux) - python3 -c " + + if ! grep -qF "$SEARCH" "$FILE"; then + warn "Anchor '${SEARCH}' not found in ${FILE} — skipping patch '${LABEL}'." + return 0 + fi + + python3 -c " import sys content = open('$FILE').read() +if '''$REPLACE''' in content: + sys.exit(0) if '''$SEARCH''' not in content: sys.stderr.write('WARN: anchor not found in $FILE\n') sys.exit(0) @@ -149,7 +156,6 @@ content = content.replace('''$SEARCH''', '''$SEARCH\n$REPLACE''', 1) open('$FILE','w').write(content) " success "Patch applied: ${LABEL}" - fi } # Patch 1: AppServiceProvider — register addon diff --git a/scripts/fix_asp.py b/scripts/fix_asp.py new file mode 100644 index 0000000..4922f18 --- /dev/null +++ b/scripts/fix_asp.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Fix AppServiceProvider — ensure AddonServiceProvider is registered.""" +import sys + +f = '/var/www/pterodactyl/app/Providers/AppServiceProvider.php' +content = open(f).read() + +if 'AddonServiceProvider' in content and '$this->app->register' in content.split('AddonServiceProvider')[0].split('\n')[-2:][0]: + print('Already correctly patched') + sys.exit(0) + +# Remove the broken line (the one without $this->) +lines = content.splitlines(keepends=True) +fixed = [] +for line in lines: + # Replace the broken orphan line + if '->app->register(\\Pterodactyl\\Addons\\AdvancedAdmin\\AddonServiceProvider::class)' in line and not '$this' in line: + fixed.append(' $this->app->register(\\Pterodactyl\\Addons\\AdvancedAdmin\\AddonServiceProvider::class);\n') + else: + fixed.append(line) + +content = ''.join(fixed) +open(f, 'w').write(content) +print('Fixed: $this-> added to AddonServiceProvider line') + +# Verify +result = open(f).read() +if '$this->app->register(\\Pterodactyl\\Addons\\AdvancedAdmin\\AddonServiceProvider::class)' in result: + print('Verification OK') +else: + print('ERROR: Verification failed') + sys.exit(1)