|
5 | 5 | use Ramsey\Uuid\Uuid; |
6 | 6 | use Illuminate\Support\Arr; |
7 | 7 | use Pterodactyl\Models\Egg; |
| 8 | +use Pterodactyl\Models\Nest; |
8 | 9 | use Illuminate\Http\UploadedFile; |
9 | | -use Illuminate\Support\Collection; |
| 10 | +use Pterodactyl\Models\EggVariable; |
10 | 11 | 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; |
15 | 13 |
|
16 | 14 | class EggImporterService |
17 | 15 | { |
18 | | - /** |
19 | | - * @var \Illuminate\Database\ConnectionInterface |
20 | | - */ |
21 | | - protected $connection; |
| 16 | + protected ConnectionInterface $connection; |
22 | 17 |
|
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; |
32 | 19 |
|
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 | + { |
47 | 22 | $this->connection = $connection; |
48 | | - $this->eggVariableRepository = $eggVariableRepository; |
49 | | - $this->repository = $repository; |
50 | | - $this->nestRepository = $nestRepository; |
| 23 | + $this->parser = $parser; |
51 | 24 | } |
52 | 25 |
|
53 | 26 | /** |
54 | 27 | * Take an uploaded JSON file and parse it into a new egg. |
55 | 28 | * |
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 |
60 | 30 | */ |
61 | 31 | public function handle(UploadedFile $file, int $nest): Egg |
62 | 32 | { |
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); |
66 | 34 |
|
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); |
72 | 37 |
|
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 | + ]); |
76 | 45 |
|
77 | | - $nest = $this->nestRepository->getWithEggs($nest); |
78 | | - $this->connection->beginTransaction(); |
| 46 | + $egg = $this->parser->fillFromParsed($egg, $parsed); |
| 47 | + $egg->save(); |
79 | 48 |
|
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 | + } |
103 | 52 |
|
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; |
110 | 54 | }); |
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; |
144 | 55 | } |
145 | 56 | } |
0 commit comments