From 9d2cacba6868653574b28d2aaff5a3a2c463d15f Mon Sep 17 00:00:00 2001 From: Naterfute Date: Tue, 13 Jan 2026 02:32:59 -0800 Subject: [PATCH] fix: network page uses globalDaemon type now --- app/Enums/Limits/ResourceLimit.php | 65 +++++++++++++++++++ .../Servers/Elytra/BackupsController.php | 10 +-- .../Client/Servers/Wings/BackupController.php | 2 +- app/Providers/RouteServiceProvider.php | 2 + .../server/network/createServerAllocation.ts | 3 +- .../server/network/deleteServerAllocation.ts | 3 +- .../network/setPrimaryServerAllocation.ts | 3 +- .../network/setServerAllocationNotes.ts | 3 +- .../scripts/api/server/network/subdomain.ts | 1 + 9 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 app/Enums/Limits/ResourceLimit.php diff --git a/app/Enums/Limits/ResourceLimit.php b/app/Enums/Limits/ResourceLimit.php new file mode 100644 index 000000000..fd30156ee --- /dev/null +++ b/app/Enums/Limits/ResourceLimit.php @@ -0,0 +1,65 @@ +name}"); + } + + /** + * Returns a middleware that will throttle the specific resource by server. This + * throttle applies to any user making changes to that resource on the specific + * server, it is NOT per-user. + */ + public function middleware(): string + { + return ThrottleRequests::using($this->throttleKey()); + } + + public function limit(): Limit + { + return match ($this) { + self::Backup => Limit::perMinutes(15, 3), + self::Database => Limit::perMinute(2), + self::FilePull => Limit::perMinutes(10, 5), + self::Subuser => Limit::perMinutes(15, 10), + self::Websocket => Limit::perMinute(5), + default => Limit::perMinute(2), + }; + } + + public static function boot(): void + { + foreach (self::cases() as $case) { + RateLimiter::for($case->throttleKey(), function (Request $request) use ($case) { + Assert::isInstanceOf($server = $request->route()->parameter('server'), Server::class); + + return $case->limit()->by($server->uuid); + }); + } + } +} diff --git a/app/Http/Controllers/Api/Client/Servers/Elytra/BackupsController.php b/app/Http/Controllers/Api/Client/Servers/Elytra/BackupsController.php index 65df79e8c..a0ef51125 100644 --- a/app/Http/Controllers/Api/Client/Servers/Elytra/BackupsController.php +++ b/app/Http/Controllers/Api/Client/Servers/Elytra/BackupsController.php @@ -19,6 +19,8 @@ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Pterodactyl\Http\Requests\Api\Client\Servers\Backups\StoreBackupRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Backups\RestoreBackupRequest; +use Pterodactyl\Enums\Daemon\Adapters; + class BackupsController extends ClientApiController { public function __construct( @@ -44,14 +46,14 @@ class BackupsController extends ClientApiController $rusticBackupSum = $server->backups() ->where('is_successful', true) - ->whereIn('disk', [Backup::ADAPTER_RUSTIC_LOCAL, Backup::ADAPTER_RUSTIC_S3]) + ->whereIn('disk', Adapters::all_elytra()) ->sum('bytes'); $rusticSumMb = round($rusticBackupSum / 1024 / 1024, 2); $legacyBackupSum = $server->backups() ->where('is_successful', true) - ->whereNotIn('disk', [Backup::ADAPTER_RUSTIC_LOCAL, Backup::ADAPTER_RUSTIC_S3]) + ->whereNotIn('disk', Adapters::all_elytra()) ->sum('bytes'); $legacyUsageMb = round($legacyBackupSum / 1024 / 1024, 2); @@ -101,7 +103,7 @@ class BackupsController extends ClientApiController 'backup_create', [ 'operation' => 'create', - 'adapter' => $request->input('adapter', config('backups.default')), + 'adapter' => $request->input('adapter', $server->node->backupDisk), 'ignored' => $request->input('ignored', ''), 'name' => $request->input('name'), ], @@ -407,7 +409,7 @@ class BackupsController extends ClientApiController [ 'operation' => 'delete', 'backup_uuid' => $backup->uuid, - 'adapter_type' => $backup->getElytraAdapterType(), + 'adapter_type' => $backup->disk, 'snapshot_id' => $backup->snapshot_id, 'checksum' => $backup->checksum, ], diff --git a/app/Http/Controllers/Api/Client/Servers/Wings/BackupController.php b/app/Http/Controllers/Api/Client/Servers/Wings/BackupController.php index 20fce2b62..40332a507 100644 --- a/app/Http/Controllers/Api/Client/Servers/Wings/BackupController.php +++ b/app/Http/Controllers/Api/Client/Servers/Wings/BackupController.php @@ -74,7 +74,7 @@ class BackupController extends ClientApiController // how best to allow a user to create a backup that is locked without also preventing // them from just filling up a server with backups that can never be deleted? if ($request->user()->can(Permission::ACTION_BACKUP_DELETE, $server)) { - $action->setIsLocked((bool) $request->input('is_locked')); + $action->setIsLocked($request->boolean('is_locked')); } $backup = $action->handle($server, $request->input('name')); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 43c99f82e..8a60d5072 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -4,6 +4,7 @@ namespace Pterodactyl\Providers; use Illuminate\Http\Request; use Pterodactyl\Models\Database; +use Pterodactyl\Enums\Limits\ResourceLimit; use Illuminate\Support\Facades\Route; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; @@ -106,5 +107,6 @@ class RouteServiceProvider extends ServiceProvider config('http.rate_limit.application') )->by($key); }); + ResourceLimit::boot(); } } diff --git a/resources/scripts/api/server/network/createServerAllocation.ts b/resources/scripts/api/server/network/createServerAllocation.ts index 48867ef77..f0f9b9d64 100644 --- a/resources/scripts/api/server/network/createServerAllocation.ts +++ b/resources/scripts/api/server/network/createServerAllocation.ts @@ -1,9 +1,10 @@ import http from '@/api/http'; import { Allocation } from '@/api/server/getServer'; import { rawDataToServerAllocation } from '@/api/transformers'; +import { getGlobalDaemonType } from '@/api/server/getServer'; export default async (uuid: string): Promise => { - const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations`); + const { data } = await http.post(`/api/client/servers/${getGlobalDaemonType()}/${uuid}/network/allocations`); return rawDataToServerAllocation(data); }; diff --git a/resources/scripts/api/server/network/deleteServerAllocation.ts b/resources/scripts/api/server/network/deleteServerAllocation.ts index ec27a60ef..aef375464 100644 --- a/resources/scripts/api/server/network/deleteServerAllocation.ts +++ b/resources/scripts/api/server/network/deleteServerAllocation.ts @@ -1,5 +1,6 @@ import http from '@/api/http'; import { Allocation } from '@/api/server/getServer'; +import { getGlobalDaemonType } from '@/api/server/getServer'; export default async (uuid: string, id: number): Promise => - await http.delete(`/api/client/servers/${uuid}/network/allocations/${id}`); + await http.delete(`/api/client/servers/${getGlobalDaemonType()}/${uuid}/network/allocations/${id}`); diff --git a/resources/scripts/api/server/network/setPrimaryServerAllocation.ts b/resources/scripts/api/server/network/setPrimaryServerAllocation.ts index d87958551..9a34bf909 100644 --- a/resources/scripts/api/server/network/setPrimaryServerAllocation.ts +++ b/resources/scripts/api/server/network/setPrimaryServerAllocation.ts @@ -1,9 +1,10 @@ import http from '@/api/http'; import { Allocation } from '@/api/server/getServer'; import { rawDataToServerAllocation } from '@/api/transformers'; +import { getGlobalDaemonType } from '@/api/server/getServer'; export default async (uuid: string, id: number): Promise => { - const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}/primary`); + const { data } = await http.post(`/api/client/servers/${getGlobalDaemonType()}/${uuid}/network/allocations/${id}/primary`); return rawDataToServerAllocation(data); }; diff --git a/resources/scripts/api/server/network/setServerAllocationNotes.ts b/resources/scripts/api/server/network/setServerAllocationNotes.ts index 509899a2a..bb1db66d2 100644 --- a/resources/scripts/api/server/network/setServerAllocationNotes.ts +++ b/resources/scripts/api/server/network/setServerAllocationNotes.ts @@ -1,9 +1,10 @@ import http from '@/api/http'; import { Allocation } from '@/api/server/getServer'; import { rawDataToServerAllocation } from '@/api/transformers'; +import { getGlobalDaemonType } from '@/api/server/getServer'; export default async (uuid: string, id: number, notes: string | null): Promise => { - const { data } = await http.post(`/api/client/servers/${uuid}/network/allocations/${id}`, { notes }); + const { data } = await http.post(`/api/client/servers/${getGlobalDaemonType()}/${uuid}/network/allocations/${id}`, { notes }); return rawDataToServerAllocation(data); }; diff --git a/resources/scripts/api/server/network/subdomain.ts b/resources/scripts/api/server/network/subdomain.ts index 3dddce4f9..080789a48 100644 --- a/resources/scripts/api/server/network/subdomain.ts +++ b/resources/scripts/api/server/network/subdomain.ts @@ -25,6 +25,7 @@ export interface AvailabilityResponse { available: boolean; message: string; } +const daemonType = getGlobalDaemonType(); export const getSubdomainInfo = (uuid: string): Promise => { return new Promise((resolve, reject) => {