diff --git a/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php index 0e93c60ca..3ed27de90 100644 --- a/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php +++ b/app/Http/Controllers/Api/Remote/Servers/ServerDetailsController.php @@ -4,14 +4,10 @@ namespace Pterodactyl\Http\Controllers\Api\Remote\Servers; use Illuminate\Http\Request; use Pterodactyl\Models\Server; -use Pterodactyl\Models\Backup; -use Pterodactyl\Models\AuditLog; use Illuminate\Http\JsonResponse; use Pterodactyl\Facades\Activity; -use Illuminate\Database\Query\Builder; -use Illuminate\Database\Query\JoinClause; use Pterodactyl\Http\Controllers\Controller; -use Pterodactyl\Repositories\Eloquent\NodeRepository; +use Illuminate\Database\ConnectionInterface; use Pterodactyl\Services\Eggs\EggConfigurationService; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Http\Resources\Wings\ServerConfigurationCollection; @@ -19,6 +15,11 @@ use Pterodactyl\Services\Servers\ServerConfigurationStructureService; class ServerDetailsController extends Controller { + /** + * @var \Illuminate\Database\ConnectionInterface + */ + protected ConnectionInterface $connection; + /** * @var \Pterodactyl\Services\Eggs\EggConfigurationService */ @@ -38,14 +39,15 @@ class ServerDetailsController extends Controller * ServerConfigurationController constructor. */ public function __construct( + ConnectionInterface $connection, ServerRepository $repository, ServerConfigurationStructureService $configurationStructureService, - EggConfigurationService $eggConfigurationService, - NodeRepository $nodeRepository + EggConfigurationService $eggConfigurationService ) { $this->eggConfigurationService = $eggConfigurationService; $this->repository = $repository; $this->configurationStructureService = $configurationStructureService; + $this->connection = $connection; } /** @@ -110,45 +112,38 @@ class ServerDetailsController extends Controller // For each of those servers we'll track a new audit log entry to mark them as // failed and then update them all to be in a valid state. $servers = Server::query() - ->select('servers.*') - ->selectRaw('JSON_UNQUOTE(JSON_EXTRACT(started.metadata, "$.backup_uuid")) as backup_uuid') - ->leftJoinSub(function (Builder $builder) { - $builder->select('*')->from('audit_logs') - ->where('action', AuditLog::SERVER__BACKUP_RESTORE_STARTED) - ->orderByDesc('created_at') - ->limit(1); - }, 'started', 'started.server_id', '=', 'servers.id') - ->leftJoin('audit_logs as completed', function (JoinClause $clause) { - $clause->whereColumn('completed.created_at', '>', 'started.created_at') - ->whereIn('completed.action', [ - AuditLog::SERVER__BACKUP_RESTORE_COMPLETED, - AuditLog::SERVER__BACKUP_RESTORE_FAILED, - ]); - }) - ->whereNotNull('started.id') - ->whereNull('completed.id') - ->where('servers.node_id', $node->id) - ->where('servers.status', Server::STATUS_RESTORING_BACKUP) + ->with([ + 'activity' => fn ($builder) => $builder + ->where('activity_logs.event', 'server:backup.restore-started') + ->latest('timestamp'), + ]) + ->where('node_id', $node->id) + ->where('status', Server::STATUS_RESTORING_BACKUP) ->get(); - $backups = Backup::query()->whereIn('uuid', $servers->pluck('backup_uuid'))->get(); - - /** @var \Pterodactyl\Models\Server $server */ - foreach ($servers as $server) { - $server->update(['status' => null]); - - if ($backup = $backups->where('uuid', $server->getAttribute('backup_uuid'))->first()) { - // Just create a new audit entry for this event and update the server state - // so that power actions, file management, and backups can resume as normal. - Activity::event('server:backup.restore-failed')->subject($server, $backup)->log(); + $this->connection->transaction(function () use ($node, $servers) { + /** @var \Pterodactyl\Models\Server $server */ + foreach ($servers as $server) { + /** @var \Pterodactyl\Models\ActivityLog|null $activity */ + $activity = $server->activity->first(); + if (!is_null($activity)) { + if ($subject = $activity->subjects->where('subject_type', 'backup')->first()) { + // Just create a new audit entry for this event and update the server state + // so that power actions, file management, and backups can resume as normal. + Activity::event('server:backup.restore-failed') + ->subject($server, $subject->subject) + ->property('name', $subject->subject->name) + ->log(); + } + } } - } - // Update any server marked as installing or restoring as being in a normal state - // at this point in the process. - Server::query()->where('node_id', $node->id) - ->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP]) - ->update(['status' => null]); + // Update any server marked as installing or restoring as being in a normal state + // at this point in the process. + Server::query()->where('node_id', $node->id) + ->whereIn('status', [Server::STATUS_INSTALLING, Server::STATUS_RESTORING_BACKUP]) + ->update(['status' => null]); + }); return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); } diff --git a/app/Models/AuditLog.php b/app/Models/AuditLog.php index eb306da9c..6dea11f3c 100644 --- a/app/Models/AuditLog.php +++ b/app/Models/AuditLog.php @@ -7,41 +7,12 @@ use Illuminate\Http\Request; use Illuminate\Container\Container; /** - * @property int $id - * @property string $uuid - * @property bool $is_system - * @property int|null $user_id - * @property int|null $server_id - * @property string $action - * @property string|null $subaction - * @property array $device - * @property array $metadata - * @property \Carbon\CarbonImmutable $created_at - * @property \Pterodactyl\Models\User|null $user - * @property \Pterodactyl\Models\Server|null $server + * @deprecated — this class will be dropped in a future version, use the activity log */ class AuditLog extends Model { public const UPDATED_AT = null; - public const SERVER__FILESYSTEM_DOWNLOAD = 'server:filesystem.download'; - public const SERVER__FILESYSTEM_WRITE = 'server:filesystem.write'; - public const SERVER__FILESYSTEM_DELETE = 'server:filesystem.delete'; - public const SERVER__FILESYSTEM_RENAME = 'server:filesystem.rename'; - public const SERVER__FILESYSTEM_COMPRESS = 'server:filesystem.compress'; - public const SERVER__FILESYSTEM_DECOMPRESS = 'server:filesystem.decompress'; - public const SERVER__FILESYSTEM_PULL = 'server:filesystem.pull'; - public const SERVER__BACKUP_STARTED = 'server:backup.started'; - public const SERVER__BACKUP_FAILED = 'server:backup.failed'; - public const SERVER__BACKUP_COMPELTED = 'server:backup.completed'; - public const SERVER__BACKUP_DELETED = 'server:backup.deleted'; - public const SERVER__BACKUP_DOWNLOADED = 'server:backup.downloaded'; - public const SERVER__BACKUP_LOCKED = 'server:backup.locked'; - public const SERVER__BACKUP_UNLOCKED = 'server:backup.unlocked'; - public const SERVER__BACKUP_RESTORE_STARTED = 'server:backup.restore.started'; - public const SERVER__BACKUP_RESTORE_COMPLETED = 'server:backup.restore.completed'; - public const SERVER__BACKUP_RESTORE_FAILED = 'server:backup.restore.failed'; - /** * @var string[] */ @@ -104,6 +75,8 @@ class AuditLog extends Model * you can always make modifications to it as needed before saving. * * @return $this + * + * @deprecated */ public static function instance(string $action, array $metadata, bool $isSystem = false) { diff --git a/app/Models/Backup.php b/app/Models/Backup.php index f608edc7a..84ba680a2 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -99,14 +99,4 @@ class Backup extends Model { return $this->belongsTo(Server::class); } - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function audits() - { - return $this->hasMany(AuditLog::class, 'metadata->backup_uuid', 'uuid') - ->where('action', 'LIKE', 'server:backup.%'); - // ->where('metadata->backup_uuid', $this->uuid); - } } diff --git a/app/Models/Server.php b/app/Models/Server.php index 7fe28674a..3f67c396d 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException; /** - * Pterodactyl\Models\Server. + * \Pterodactyl\Models\Server. * * @property int $id * @property string|null $external_id @@ -38,6 +38,8 @@ use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException; * @property int $backup_limit * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\ActivityLog[] $activity + * @property int|null $activity_count * @property \Pterodactyl\Models\Allocation|null $allocation * @property \Illuminate\Database\Eloquent\Collection|\Pterodactyl\Models\Allocation[] $allocations * @property int|null $allocations_count