fix: defensive ServiceProvider (class_exists guards), fixed install.sh apply_patch logic
This commit is contained in:
@@ -16,13 +16,6 @@ namespace Pterodactyl\Addons\AdvancedAdmin;
|
|||||||
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
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
|
class AddonServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -38,7 +31,10 @@ class AddonServiceProvider extends ServiceProvider
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Bind the PermissionResolver as a singleton for performance
|
// 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
|
public function boot(): void
|
||||||
{
|
{
|
||||||
// ── Routes ────────────────────────────────────────────────────────────
|
// ── Routes ────────────────────────────────────────────────────────────
|
||||||
$this->loadRoutesFrom(__DIR__ . '/../../routes/addon-client.php');
|
$clientRoutes = __DIR__ . '/../../routes/addon-client.php';
|
||||||
$this->loadRoutesFrom(__DIR__ . '/../../routes/addon-application.php');
|
$appRoutes = __DIR__ . '/../../routes/addon-application.php';
|
||||||
|
|
||||||
|
if (file_exists($clientRoutes)) {
|
||||||
|
$this->loadRoutesFrom($clientRoutes);
|
||||||
|
}
|
||||||
|
if (file_exists($appRoutes)) {
|
||||||
|
$this->loadRoutesFrom($appRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Migrations ────────────────────────────────────────────────────────
|
// ── Migrations ────────────────────────────────────────────────────────
|
||||||
$this->loadMigrationsFrom(__DIR__ . '/../../database/migrations/addon');
|
$migrationsPath = __DIR__ . '/../../database/migrations/addon';
|
||||||
|
if (is_dir($migrationsPath)) {
|
||||||
|
$this->loadMigrationsFrom($migrationsPath);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Scheduled Jobs ────────────────────────────────────────────────────
|
// ── Scheduled Jobs ────────────────────────────────────────────────────
|
||||||
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
|
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
|
||||||
if (config('advanced-admin.network.enabled')) {
|
// Network metric collection
|
||||||
$schedule->job(CollectNetworkMetricsJob::class)
|
$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()
|
->everyTenSeconds()
|
||||||
->withoutOverlapping(5)
|
->withoutOverlapping(5)
|
||||||
->onOneServer();
|
->onOneServer();
|
||||||
|
|
||||||
$schedule->job(DownsampleNetworkMetricsJob::class)
|
if (class_exists($downsampleJob)) {
|
||||||
|
$schedule->job(new $downsampleJob())
|
||||||
->hourly()
|
->hourly()
|
||||||
->withoutOverlapping(30)
|
->withoutOverlapping(30)
|
||||||
->onOneServer();
|
->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()
|
->daily()
|
||||||
->withoutOverlapping(60)
|
->withoutOverlapping(60)
|
||||||
->onOneServer();
|
->onOneServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config('advanced-admin.revisions.enabled')) {
|
if (config('advanced-admin.console.enabled') && class_exists($expireJob)) {
|
||||||
$schedule->job(PruneFileRevisionsJob::class)
|
$schedule->job(new $expireJob())
|
||||||
->daily()
|
|
||||||
->withoutOverlapping(60)
|
|
||||||
->onOneServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config('advanced-admin.console.enabled')) {
|
|
||||||
$schedule->job(ExpireStalePlayersJob::class)
|
|
||||||
->everyFiveMinutes()
|
->everyFiveMinutes()
|
||||||
->withoutOverlapping(4);
|
->withoutOverlapping(4);
|
||||||
}
|
}
|
||||||
@@ -88,23 +105,26 @@ class AddonServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
// ── Event Listeners ───────────────────────────────────────────────────
|
// ── Event Listeners ───────────────────────────────────────────────────
|
||||||
// Console output listener (parses join/leave for player cache)
|
// Console output listener (parses join/leave for player cache)
|
||||||
// Hooked to Pterodactyl's existing console output events if they exist.
|
$consoleEvent = 'Pterodactyl\Events\Server\ConsoleDataReceived';
|
||||||
// Falls back gracefully if event is not fired by this panel version.
|
$consoleListener = 'Pterodactyl\Addons\AdvancedAdmin\Listeners\Console\ConsoleOutputListener';
|
||||||
if (class_exists(\Pterodactyl\Events\Server\ConsoleDataReceived::class)) {
|
if (class_exists($consoleEvent) && class_exists($consoleListener)) {
|
||||||
$this->app['events']->listen(
|
$this->app['events']->listen($consoleEvent, $consoleListener);
|
||||||
\Pterodactyl\Events\Server\ConsoleDataReceived::class,
|
|
||||||
ConsoleOutputListener::class
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Artisan Commands ──────────────────────────────────────────────────
|
// ── Artisan Commands ──────────────────────────────────────────────────
|
||||||
if ($this->app->runningInConsole()) {
|
if ($this->app->runningInConsole()) {
|
||||||
$this->commands([
|
$commands = [];
|
||||||
Console\Commands\AddonPatchCommand::class,
|
$candidates = [
|
||||||
Console\Commands\AddonInstallCommand::class,
|
'Pterodactyl\Addons\AdvancedAdmin\Console\Commands\CollectNetworkCommand',
|
||||||
Console\Commands\AddonUninstallCommand::class,
|
];
|
||||||
Console\Commands\CollectNetworkCommand::class,
|
foreach ($candidates as $cmd) {
|
||||||
]);
|
if (class_exists($cmd)) {
|
||||||
|
$commands[] = $cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($commands)) {
|
||||||
|
$this->commands($commands);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Publishable Assets ────────────────────────────────────────────────
|
// ── Publishable Assets ────────────────────────────────────────────────
|
||||||
|
|||||||
14
install.sh
14
install.sh
@@ -133,15 +133,22 @@ apply_patch() {
|
|||||||
local REPLACE="$3"
|
local REPLACE="$3"
|
||||||
local LABEL="$4"
|
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."
|
warn "Patch '${LABEL}' already applied — skipping."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if ! grep -qF "$REPLACE" "$FILE"; then
|
|
||||||
# Use python for reliable multiline sed (available on most Linux)
|
if ! grep -qF "$SEARCH" "$FILE"; then
|
||||||
|
warn "Anchor '${SEARCH}' not found in ${FILE} — skipping patch '${LABEL}'."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import sys
|
import sys
|
||||||
content = open('$FILE').read()
|
content = open('$FILE').read()
|
||||||
|
if '''$REPLACE''' in content:
|
||||||
|
sys.exit(0)
|
||||||
if '''$SEARCH''' not in content:
|
if '''$SEARCH''' not in content:
|
||||||
sys.stderr.write('WARN: anchor not found in $FILE\n')
|
sys.stderr.write('WARN: anchor not found in $FILE\n')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -149,7 +156,6 @@ content = content.replace('''$SEARCH''', '''$SEARCH\n$REPLACE''', 1)
|
|||||||
open('$FILE','w').write(content)
|
open('$FILE','w').write(content)
|
||||||
"
|
"
|
||||||
success "Patch applied: ${LABEL}"
|
success "Patch applied: ${LABEL}"
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Patch 1: AppServiceProvider — register addon
|
# Patch 1: AppServiceProvider — register addon
|
||||||
|
|||||||
32
scripts/fix_asp.py
Normal file
32
scripts/fix_asp.py
Normal file
@@ -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)
|
||||||
Reference in New Issue
Block a user