Skip to content

Commit cca0010

Browse files
committed
Update egg import/update logic to all use the same pathwaus
1 parent 6554164 commit cca0010

File tree

12 files changed

+270
-333
lines changed

12 files changed

+270
-333
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Pterodactyl\Observers;
4+
5+
use Pterodactyl\Models\EggVariable;
6+
7+
class EggVariableObserver
8+
{
9+
public function creating(EggVariable $variable): void
10+
{
11+
if ($variable->field_type) {
12+
unset($variable->field_type);
13+
}
14+
}
15+
16+
public function updating(EggVariable $variable): void
17+
{
18+
if ($variable->field_type) {
19+
unset($variable->field_type);
20+
}
21+
}
22+
}

app/Providers/AppServiceProvider.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,11 @@
55
use View;
66
use Cache;
77
use Illuminate\Support\Str;
8-
use Pterodactyl\Models\User;
9-
use Pterodactyl\Models\Server;
10-
use Pterodactyl\Models\Subuser;
118
use Illuminate\Support\Facades\URL;
129
use Illuminate\Pagination\Paginator;
1310
use Illuminate\Support\Facades\Schema;
1411
use Illuminate\Support\ServiceProvider;
15-
use Pterodactyl\Observers\UserObserver;
1612
use Pterodactyl\Extensions\Themes\Theme;
17-
use Pterodactyl\Observers\ServerObserver;
18-
use Pterodactyl\Observers\SubuserObserver;
1913

2014
class AppServiceProvider extends ServiceProvider
2115
{
@@ -26,10 +20,6 @@ public function boot()
2620
{
2721
Schema::defaultStringLength(191);
2822

29-
User::observe(UserObserver::class);
30-
Server::observe(ServerObserver::class);
31-
Subuser::observe(SubuserObserver::class);
32-
3323
View::share('appVersion', $this->versionData()['version'] ?? 'undefined');
3424
View::share('appIsGit', $this->versionData()['is_git'] ?? false);
3525

app/Providers/EventServiceProvider.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
namespace Pterodactyl\Providers;
44

5+
use Pterodactyl\Models\User;
6+
use Pterodactyl\Models\Server;
7+
use Pterodactyl\Models\Subuser;
8+
use Pterodactyl\Models\EggVariable;
9+
use Pterodactyl\Observers\UserObserver;
10+
use Pterodactyl\Observers\ServerObserver;
11+
use Pterodactyl\Observers\SubuserObserver;
12+
use Pterodactyl\Observers\EggVariableObserver;
513
use Pterodactyl\Events\Server\Installed as ServerInstalledEvent;
614
use Pterodactyl\Notifications\ServerInstalled as ServerInstalledNotification;
715
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@@ -18,4 +26,17 @@ class EventServiceProvider extends ServiceProvider
1826
ServerInstalledNotification::class,
1927
],
2028
];
29+
30+
/**
31+
* Boots the service provider and registers model event listeners.
32+
*/
33+
public function boot()
34+
{
35+
parent::boot();
36+
37+
User::observe(UserObserver::class);
38+
Server::observe(ServerObserver::class);
39+
Subuser::observe(SubuserObserver::class);
40+
EggVariable::observe(EggVariableObserver::class);
41+
}
2142
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
namespace Pterodactyl\Services\Eggs;
4+
5+
use Illuminate\Support\Arr;
6+
use Pterodactyl\Models\Egg;
7+
use Illuminate\Http\UploadedFile;
8+
use Illuminate\Support\Collection;
9+
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
10+
11+
class EggParserService
12+
{
13+
/**
14+
* Takes an uploaded file and parses out the egg configuration from within.
15+
*
16+
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
17+
*/
18+
public function handle(UploadedFile $file): array
19+
{
20+
if ($file->getError() !== UPLOAD_ERR_OK || !$file->isFile()) {
21+
throw new InvalidFileUploadException('The selected file is not valid and cannot be imported.');
22+
}
23+
24+
/** @var array $parsed */
25+
$parsed = json_decode($file->openFile()->fread($file->getSize()), true, 512, JSON_THROW_ON_ERROR);
26+
if (!in_array(Arr::get($parsed, 'meta.version') ?? '', ['PTDL_v1', 'PTDL_v2'])) {
27+
throw new InvalidFileUploadException('The JSON file provided is not in a format that can be recognized.');
28+
}
29+
30+
return $this->convertToV2($parsed);
31+
}
32+
33+
/**
34+
* Fills the provided model with the parsed JSON data.
35+
*/
36+
public function fillFromParsed(Egg $model, array $parsed): Egg
37+
{
38+
return $model->forceFill([
39+
'name' => Arr::get($parsed, 'name'),
40+
'description' => Arr::get($parsed, 'description'),
41+
'features' => Arr::get($parsed, 'features'),
42+
'docker_images' => Arr::get($parsed, 'docker_images'),
43+
'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist'))
44+
->filter(fn ($value) => !empty($value)),
45+
'update_url' => Arr::get($parsed, 'meta.update_url'),
46+
'config_files' => Arr::get($parsed, 'config.files'),
47+
'config_startup' => Arr::get($parsed, 'config.startup'),
48+
'config_logs' => Arr::get($parsed, 'config.logs'),
49+
'config_stop' => Arr::get($parsed, 'config.stop'),
50+
'startup' => Arr::get($parsed, 'startup'),
51+
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
52+
'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'),
53+
'script_container' => Arr::get($parsed, 'scripts.installation.container'),
54+
]);
55+
}
56+
57+
/**
58+
* Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles
59+
* the "docker_images" field potentially not being present, and not being in the
60+
* expected "key => value" format.
61+
*/
62+
protected function convertToV2(array $parsed): array
63+
{
64+
if (Arr::get($parsed, 'meta.version') === Egg::EXPORT_VERSION) {
65+
return $parsed;
66+
}
67+
68+
// Maintain backwards compatability for eggs that are still using the old single image
69+
// string format. New eggs can provide an array of Docker images that can be used.
70+
if (!isset($parsed['images'])) {
71+
$images = [Arr::get($parsed, 'image') ?? 'nil'];
72+
} else {
73+
$images = $parsed['images'];
74+
}
75+
76+
unset($parsed['images'], $parsed['image']);
77+
78+
$parsed['docker_images'] = [];
79+
foreach ($images as $image) {
80+
$parsed['docker_images'][$image] = $image;
81+
}
82+
83+
$parsed['variables'] = array_map(function ($value) {
84+
return array_merge($value, ['field_type' => 'text']);
85+
}, $parsed['variables']);
86+
87+
return $parsed;
88+
}
89+
}

app/Services/Eggs/Sharing/EggImporterService.php

Lines changed: 25 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -5,141 +5,52 @@
55
use Ramsey\Uuid\Uuid;
66
use Illuminate\Support\Arr;
77
use Pterodactyl\Models\Egg;
8+
use Pterodactyl\Models\Nest;
89
use Illuminate\Http\UploadedFile;
9-
use Illuminate\Support\Collection;
10+
use Pterodactyl\Models\EggVariable;
1011
use Illuminate\Database\ConnectionInterface;
11-
use Pterodactyl\Contracts\Repository\EggRepositoryInterface;
12-
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
13-
use Pterodactyl\Exceptions\Service\InvalidFileUploadException;
14-
use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface;
12+
use Pterodactyl\Services\Eggs\EggParserService;
1513

1614
class EggImporterService
1715
{
18-
/**
19-
* @var \Illuminate\Database\ConnectionInterface
20-
*/
21-
protected $connection;
16+
protected ConnectionInterface $connection;
2217

23-
/**
24-
* @var \Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface
25-
*/
26-
protected $eggVariableRepository;
27-
28-
/**
29-
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface
30-
*/
31-
protected $nestRepository;
18+
protected EggParserService $parser;
3219

33-
/**
34-
* @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface
35-
*/
36-
protected $repository;
37-
38-
/**
39-
* EggImporterService constructor.
40-
*/
41-
public function __construct(
42-
ConnectionInterface $connection,
43-
EggRepositoryInterface $repository,
44-
EggVariableRepositoryInterface $eggVariableRepository,
45-
NestRepositoryInterface $nestRepository
46-
) {
20+
public function __construct(ConnectionInterface $connection, EggParserService $parser)
21+
{
4722
$this->connection = $connection;
48-
$this->eggVariableRepository = $eggVariableRepository;
49-
$this->repository = $repository;
50-
$this->nestRepository = $nestRepository;
23+
$this->parser = $parser;
5124
}
5225

5326
/**
5427
* Take an uploaded JSON file and parse it into a new egg.
5528
*
56-
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
57-
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
58-
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException
59-
* @throws \JsonException
29+
* @throws \Pterodactyl\Exceptions\Service\InvalidFileUploadException|\Throwable
6030
*/
6131
public function handle(UploadedFile $file, int $nest): Egg
6232
{
63-
if ($file->getError() !== UPLOAD_ERR_OK || !$file->isFile()) {
64-
throw new InvalidFileUploadException(sprintf('The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)', $file->getFilename(), $file->isFile() ? 'true' : 'false', $file->isValid() ? 'true' : 'false', $file->getError(), $file->getErrorMessage()));
65-
}
33+
$parsed = $this->parser->handle($file);
6634

67-
/** @var array $parsed */
68-
$parsed = json_decode($file->openFile()->fread($file->getSize()), true, 512, JSON_THROW_ON_ERROR);
69-
if (!in_array(Arr::get($parsed, 'meta.version') ?? '', ['PTDL_v1', 'PTDL_v2'])) {
70-
throw new InvalidFileUploadException(trans('exceptions.nest.importer.invalid_json_provided'));
71-
}
35+
/** @var \Pterodactyl\Models\Nest $nest */
36+
$nest = Nest::query()->with('eggs', 'eggs.variables')->findOrFail($nest);
7237

73-
if ($parsed['meta']['version'] !== Egg::EXPORT_VERSION) {
74-
$parsed = $this->convertV1ToV2($parsed);
75-
}
38+
return $this->connection->transaction(function () use ($nest, $parsed) {
39+
$egg = (new Egg())->forceFill([
40+
'uuid' => Uuid::uuid4()->toString(),
41+
'nest_id' => $nest->id,
42+
'author' => Arr::get($parsed, 'author'),
43+
'copy_script_from' => null,
44+
]);
7645

77-
$nest = $this->nestRepository->getWithEggs($nest);
78-
$this->connection->beginTransaction();
46+
$egg = $this->parser->fillFromParsed($egg, $parsed);
47+
$egg->save();
7948

80-
/** @var \Pterodactyl\Models\Egg $egg */
81-
$egg = $this->repository->create([
82-
'uuid' => Uuid::uuid4()->toString(),
83-
'nest_id' => $nest->id,
84-
'author' => Arr::get($parsed, 'author'),
85-
'name' => Arr::get($parsed, 'name'),
86-
'description' => Arr::get($parsed, 'description'),
87-
'features' => Arr::get($parsed, 'features'),
88-
'docker_images' => Arr::get($parsed, 'docker_images'),
89-
'file_denylist' => Collection::make(Arr::get($parsed, 'file_denylist'))->filter(function ($value) {
90-
return !empty($value);
91-
}),
92-
'update_url' => Arr::get($parsed, 'meta.update_url'),
93-
'config_files' => Arr::get($parsed, 'config.files'),
94-
'config_startup' => Arr::get($parsed, 'config.startup'),
95-
'config_logs' => Arr::get($parsed, 'config.logs'),
96-
'config_stop' => Arr::get($parsed, 'config.stop'),
97-
'startup' => Arr::get($parsed, 'startup'),
98-
'script_install' => Arr::get($parsed, 'scripts.installation.script'),
99-
'script_entry' => Arr::get($parsed, 'scripts.installation.entrypoint'),
100-
'script_container' => Arr::get($parsed, 'scripts.installation.container'),
101-
'copy_script_from' => null,
102-
], true, true);
49+
foreach ($parsed['variables'] ?? [] as $variable) {
50+
EggVariable::query()->forceCreate(array_merge($variable, ['egg_id' => $egg->id]));
51+
}
10352

104-
Collection::make($parsed['variables'] ?? [])->each(function (array $variable) use ($egg) {
105-
unset($variable['field_type']);
106-
107-
$this->eggVariableRepository->create(array_merge($variable, [
108-
'egg_id' => $egg->id,
109-
]));
53+
return $egg;
11054
});
111-
112-
$this->connection->commit();
113-
114-
return $egg;
115-
}
116-
117-
/**
118-
* Converts a PTDL_V1 egg into the expected PTDL_V2 egg format. This just handles
119-
* the "docker_images" field potentially not being present, and not being in the
120-
* expected "key => value" format.
121-
*/
122-
protected function convertV1ToV2(array $parsed): array
123-
{
124-
// Maintain backwards compatability for eggs that are still using the old single image
125-
// string format. New eggs can provide an array of Docker images that can be used.
126-
if (!isset($parsed['images'])) {
127-
$images = [Arr::get($parsed, 'image') ?? 'nil'];
128-
} else {
129-
$images = $parsed['images'];
130-
}
131-
132-
unset($parsed['images'], $parsed['image']);
133-
134-
$parsed['docker_images'] = [];
135-
foreach ($images as $image) {
136-
$parsed['docker_images'][$image] = $image;
137-
}
138-
139-
$parsed['variables'] = array_map(function ($value) {
140-
return array_merge($value, ['field_type' => 'text']);
141-
}, $parsed['variables']);
142-
143-
return $parsed;
14455
}
14556
}

0 commit comments

Comments
 (0)