22
33namespace Pterodactyl \Http \Controllers \Api \Client \Servers ;
44
5+ use Illuminate \Http \Request ;
56use Pterodactyl \Models \Backup ;
67use Pterodactyl \Models \Server ;
78use Pterodactyl \Models \AuditLog ;
89use Illuminate \Http \JsonResponse ;
10+ use Pterodactyl \Models \Permission ;
11+ use Illuminate \Validation \UnauthorizedException ;
912use Pterodactyl \Services \Backups \DeleteBackupService ;
10- use Pterodactyl \Repositories \ Eloquent \ BackupRepository ;
13+ use Pterodactyl \Services \ Backups \ DownloadLinkService ;
1114use Pterodactyl \Services \Backups \InitiateBackupService ;
1215use Pterodactyl \Transformers \Api \Client \BackupTransformer ;
16+ use Pterodactyl \Repositories \Wings \DaemonBackupRepository ;
1317use Pterodactyl \Http \Controllers \Api \Client \ClientApiController ;
14- use Pterodactyl \ Http \ Requests \ Api \ Client \ Servers \ Backups \ GetBackupsRequest ;
18+ use Symfony \ Component \ HttpKernel \ Exception \ BadRequestHttpException ;
1519use Pterodactyl \Http \Requests \Api \Client \Servers \Backups \StoreBackupRequest ;
16- use Pterodactyl \Http \Requests \Api \Client \Servers \Backups \DeleteBackupRequest ;
1720
1821class BackupController extends ClientApiController
1922{
@@ -28,39 +31,51 @@ class BackupController extends ClientApiController
2831 private $ deleteBackupService ;
2932
3033 /**
31- * @var \Pterodactyl\Repositories\Eloquent\BackupRepository
34+ * @var \Pterodactyl\Services\Backups\DownloadLinkService
35+ */
36+ private $ downloadLinkService ;
37+
38+ /**
39+ * @var \Pterodactyl\Repositories\Wings\DaemonBackupRepository
3240 */
3341 private $ repository ;
3442
3543 /**
3644 * BackupController constructor.
3745 *
38- * @param \Pterodactyl\Repositories\Eloquent\BackupRepository $repository
46+ * @param \Pterodactyl\Repositories\Wings\DaemonBackupRepository $repository
3947 * @param \Pterodactyl\Services\Backups\DeleteBackupService $deleteBackupService
4048 * @param \Pterodactyl\Services\Backups\InitiateBackupService $initiateBackupService
49+ * @param \Pterodactyl\Services\Backups\DownloadLinkService $downloadLinkService
4150 */
4251 public function __construct (
43- BackupRepository $ repository ,
52+ DaemonBackupRepository $ repository ,
4453 DeleteBackupService $ deleteBackupService ,
45- InitiateBackupService $ initiateBackupService
54+ InitiateBackupService $ initiateBackupService ,
55+ DownloadLinkService $ downloadLinkService
4656 ) {
4757 parent ::__construct ();
4858
59+ $ this ->repository = $ repository ;
4960 $ this ->initiateBackupService = $ initiateBackupService ;
5061 $ this ->deleteBackupService = $ deleteBackupService ;
51- $ this ->repository = $ repository ;
62+ $ this ->downloadLinkService = $ downloadLinkService ;
5263 }
5364
5465 /**
5566 * Returns all of the backups for a given server instance in a paginated
5667 * result set.
5768 *
58- * @param \Pterodactyl \Http\Requests\Api\Client\Servers\Backups\GetBackupsRequest $request
69+ * @param \Illuminate \Http\Request $request
5970 * @param \Pterodactyl\Models\Server $server
6071 * @return array
6172 */
62- public function index (GetBackupsRequest $ request , Server $ server )
73+ public function index (Request $ request , Server $ server )
6374 {
75+ if (! $ request ->user ()->can (Permission::ACTION_BACKUP_READ , $ server )) {
76+ throw new UnauthorizedException ;
77+ }
78+
6479 $ limit = min ($ request ->query ('per_page ' ) ?? 20 , 50 );
6580
6681 return $ this ->fractal ->collection ($ server ->backups ()->paginate ($ limit ))
@@ -100,13 +115,17 @@ public function store(StoreBackupRequest $request, Server $server)
100115 /**
101116 * Returns information about a single backup.
102117 *
103- * @param \Pterodactyl \Http\Requests\Api\Client\Servers\Backups\GetBackupsRequest $request
118+ * @param \Illuminate \Http\Request $request
104119 * @param \Pterodactyl\Models\Server $server
105120 * @param \Pterodactyl\Models\Backup $backup
106121 * @return array
107122 */
108- public function view (GetBackupsRequest $ request , Server $ server , Backup $ backup )
123+ public function view (Request $ request , Server $ server , Backup $ backup )
109124 {
125+ if (! $ request ->user ()->can (Permission::ACTION_BACKUP_READ , $ server )) {
126+ throw new UnauthorizedException ;
127+ }
128+
110129 return $ this ->fractal ->item ($ backup )
111130 ->transformWith ($ this ->getTransformer (BackupTransformer::class))
112131 ->toArray ();
@@ -116,15 +135,19 @@ public function view(GetBackupsRequest $request, Server $server, Backup $backup)
116135 * Deletes a backup from the panel as well as the remote source where it is currently
117136 * being stored.
118137 *
119- * @param \Pterodactyl \Http\Requests\Api\Client\Servers\Backups\DeleteBackupRequest $request
138+ * @param \Illuminate \Http\Request $request
120139 * @param \Pterodactyl\Models\Server $server
121140 * @param \Pterodactyl\Models\Backup $backup
122141 * @return \Illuminate\Http\JsonResponse
123142 *
124143 * @throws \Throwable
125144 */
126- public function delete (DeleteBackupRequest $ request , Server $ server , Backup $ backup )
145+ public function delete (Request $ request , Server $ server , Backup $ backup )
127146 {
147+ if (! $ request ->user ()->can (Permission::ACTION_BACKUP_DELETE , $ server )) {
148+ throw new UnauthorizedException ;
149+ }
150+
128151 $ server ->audit (AuditLog::SERVER__BACKUP_DELETED , function (AuditLog $ audit ) use ($ backup ) {
129152 $ audit ->metadata = ['backup_uuid ' => $ backup ->uuid ];
130153
@@ -133,4 +156,79 @@ public function delete(DeleteBackupRequest $request, Server $server, Backup $bac
133156
134157 return new JsonResponse ([], JsonResponse::HTTP_NO_CONTENT );
135158 }
159+
160+ /**
161+ * Download the backup for a given server instance. For daemon local files, the file
162+ * will be streamed back through the Panel. For AWS S3 files, a signed URL will be generated
163+ * which the user is redirected to.
164+ *
165+ * @param \Illuminate\Http\Request $request
166+ * @param \Pterodactyl\Models\Server $server
167+ * @param \Pterodactyl\Models\Backup $backup
168+ * @return \Illuminate\Http\JsonResponse
169+ */
170+ public function download (Request $ request , Server $ server , Backup $ backup )
171+ {
172+ if (! $ request ->user ()->can (Permission::ACTION_BACKUP_DOWNLOAD , $ server )) {
173+ throw new UnauthorizedException ;
174+ }
175+
176+ switch ($ backup ->disk ) {
177+ case Backup::ADAPTER_WINGS :
178+ case Backup::ADAPTER_AWS_S3 :
179+ return new JsonResponse ([
180+ 'object ' => 'signed_url ' ,
181+ 'attributes ' => ['url ' => '' ],
182+ ]);
183+ default :
184+ throw new BadRequestHttpException ;
185+ }
186+ }
187+
188+ /**
189+ * Handles restoring a backup by making a request to the Wings instance telling it
190+ * to begin the process of finding (or downloading) the backup and unpacking it
191+ * over the server files.
192+ *
193+ * If the "truncate" flag is passed through in this request then all of the
194+ * files that currently exist on the server will be deleted before restoring.
195+ * Otherwise the archive will simply be unpacked over the existing files.
196+ *
197+ * @param \Illuminate\Http\Request $request
198+ * @param \Pterodactyl\Models\Server $server
199+ * @param \Pterodactyl\Models\Backup $backup
200+ * @return \Illuminate\Http\JsonResponse
201+ *
202+ * @throws \Throwable
203+ */
204+ public function restore (Request $ request , Server $ server , Backup $ backup )
205+ {
206+ if (! $ request ->user ()->can (Permission::ACTION_BACKUP_RESTORE , $ server )) {
207+ throw new UnauthorizedException ;
208+ }
209+
210+ // Cannot restore a backup unless a server is fully installed and not currently
211+ // processing a different backup restoration request.
212+ if (! is_null ($ server ->status )) {
213+ throw new BadRequestHttpException ('This server is not currently in a state that allows for a backup to be restored. ' );
214+ }
215+
216+ $ server ->audit (AuditLog::SERVER__BACKUP_RESTORE_STARTED , function (AuditLog $ audit , Server $ server ) use ($ backup , $ request ) {
217+ $ audit ->metadata = ['backup_uuid ' => $ backup ->uuid ];
218+
219+ // If the backup is for an S3 file we need to generate a unique Download link for
220+ // it that will allow Wings to actually access the file.
221+ if ($ backup ->disk === Backup::ADAPTER_AWS_S3 ) {
222+ $ url = $ this ->downloadLinkService ->handle ($ backup , $ request ->user ());
223+ }
224+
225+ // Update the status right away for the server so that we know not to allow certain
226+ // actions against it via the Panel API.
227+ $ server ->update (['status ' => Server::STATUS_RESTORING_BACKUP ]);
228+
229+ $ this ->repository ->restore ($ backup , $ url ?? null , $ request ->input ('truncate ' ) === 'true ' );
230+ });
231+
232+ return new JsonResponse ([], JsonResponse::HTTP_NO_CONTENT );
233+ }
136234}
0 commit comments