77use Illuminate \Http \JsonResponse ;
88use League \Flysystem \AwsS3v3 \AwsS3Adapter ;
99use Pterodactyl \Http \Controllers \Controller ;
10+ use Pterodactyl \Exceptions \DisplayException ;
1011use Pterodactyl \Extensions \Backups \BackupManager ;
1112use Pterodactyl \Repositories \Eloquent \BackupRepository ;
1213use Symfony \Component \HttpKernel \Exception \BadRequestHttpException ;
@@ -41,16 +42,14 @@ public function __construct(BackupRepository $repository, BackupManager $backupM
4142 *
4243 * @param \Pterodactyl\Http\Requests\Api\Remote\ReportBackupCompleteRequest $request
4344 * @param string $backup
44- *
4545 * @return \Illuminate\Http\JsonResponse
4646 *
47- * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
4847 * @throws \Exception
4948 */
5049 public function __invoke (ReportBackupCompleteRequest $ request , string $ backup )
5150 {
5251 /** @var \Pterodactyl\Models\Backup $model */
53- $ model = $ this -> repository -> findFirstWhere ([[ 'uuid ' , ' = ' , $ backup ]] );
52+ $ model = Backup:: query ()-> where ( 'uuid ' , $ backup)-> firstOrFail ( );
5453
5554 if (! is_null ($ model ->completed_at )) {
5655 throw new BadRequestHttpException (
@@ -60,39 +59,66 @@ public function __invoke(ReportBackupCompleteRequest $request, string $backup)
6059
6160 $ successful = $ request ->input ('successful ' ) ? true : false ;
6261
63- $ model ->forceFill ([
62+ $ model ->fill ([
6463 'is_successful ' => $ successful ,
6564 'checksum ' => $ successful ? ($ request ->input ('checksum_type ' ) . ': ' . $ request ->input ('checksum ' )) : null ,
6665 'bytes ' => $ successful ? $ request ->input ('size ' ) : 0 ,
6766 'completed_at ' => CarbonImmutable::now (),
6867 ])->save ();
6968
70- // Check if we are using the s3 backup adapter.
69+ // Check if we are using the s3 backup adapter. If so, make sure we mark the backup as
70+ // being completed in S3 correctly.
7171 $ adapter = $ this ->backupManager ->adapter ();
7272 if ($ adapter instanceof AwsS3Adapter) {
73- /** @var \Pterodactyl\Models\Backup $backup */
74- $ backup = Backup::query ()->where ('uuid ' , $ backup )->firstOrFail ();
75-
76- $ client = $ adapter ->getClient ();
73+ $ this ->completeMultipartUpload ($ model , $ adapter , $ successful );
74+ }
7775
78- $ params = [
79- 'Bucket ' => $ adapter ->getBucket (),
80- 'Key ' => sprintf ('%s/%s.tar.gz ' , $ backup ->server ->uuid , $ backup ->uuid ),
81- 'UploadId ' => $ backup ->upload_id ,
82- ];
76+ return new JsonResponse ([], JsonResponse::HTTP_NO_CONTENT );
77+ }
8378
84- // If the backup was not successful, send an AbortMultipartUpload request.
79+ /**
80+ * Marks a multipart upload in a given S3-compatiable instance as failed or successful for
81+ * the given backup.
82+ *
83+ * @param \Pterodactyl\Models\Backup $backup
84+ * @param \League\Flysystem\AwsS3v3\AwsS3Adapter $adapter
85+ * @param bool $successful
86+ *
87+ * @throws \Exception
88+ * @throws \Pterodactyl\Exceptions\DisplayException
89+ */
90+ protected function completeMultipartUpload (Backup $ backup , AwsS3Adapter $ adapter , bool $ successful )
91+ {
92+ // This should never really happen, but if it does don't let us fall victim to Amazon's
93+ // wildly fun error messaging. Just stop the process right here.
94+ if (empty ($ backup ->upload_id )) {
95+ // A failed backup doesn't need to error here, this can happen if the backup encouters
96+ // an error before we even start the upload. AWS gives you tooling to clear these failed
97+ // multipart uploads as needed too.
8598 if (! $ successful ) {
86- $ client ->execute ($ client ->getCommand ('AbortMultipartUpload ' , $ params ));
87- } else {
88- // Otherwise send a CompleteMultipartUpload request.
89- $ params ['MultipartUpload ' ] = [
90- 'Parts ' => $ client ->execute ($ client ->getCommand ('ListParts ' , $ params ))['Parts ' ],
91- ];
92- $ client ->execute ($ client ->getCommand ('CompleteMultipartUpload ' , $ params ));
99+ return ;
93100 }
101+ throw new DisplayException ('Cannot complete backup request: no upload_id present on model. ' );
94102 }
95103
96- return new JsonResponse ([], JsonResponse::HTTP_NO_CONTENT );
104+ $ params = [
105+ 'Bucket ' => $ adapter ->getBucket (),
106+ 'Key ' => sprintf ('%s/%s.tar.gz ' , $ backup ->server ->uuid , $ backup ->uuid ),
107+ 'UploadId ' => $ backup ->upload_id ,
108+ ];
109+
110+ $ client = $ adapter ->getClient ();
111+ if (! $ successful ) {
112+ $ client ->execute ($ client ->getCommand ('AbortMultipartUpload ' , $ params ));
113+
114+ return ;
115+ }
116+
117+ // Otherwise send a CompleteMultipartUpload request.
118+ $ params ['MultipartUpload ' ] = [
119+ 'Parts ' => $ client ->execute ($ client ->getCommand ('ListParts ' , $ params ))['Parts ' ],
120+ ];
121+
122+ $ client ->execute ($ client ->getCommand ('CompleteMultipartUpload ' , $ params ));
97123 }
98124}
0 commit comments