mirror of
https://github.com/pelican-dev/panel.git
synced 2025-12-19 13:31:21 +01:00
Egg API Import/Delete (#1947)
Co-authored-by: Boy132 <Boy132@users.noreply.github.com> Co-authored-by: Boy132 <mail@boy132.de>
This commit is contained in:
parent
4a1ecb1adc
commit
014e866d0e
@ -7,15 +7,22 @@ use App\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
use App\Http\Requests\Api\Application\Eggs\ExportEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\GetEggsRequest;
|
||||
use App\Http\Requests\Api\Application\Eggs\ImportEggRequest;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Eggs\Sharing\EggExporterService;
|
||||
use App\Services\Eggs\Sharing\EggImporterService;
|
||||
use App\Transformers\Api\Application\EggTransformer;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Throwable;
|
||||
|
||||
class EggController extends ApplicationApiController
|
||||
{
|
||||
public function __construct(
|
||||
private EggExporterService $exporterService,
|
||||
private EggImporterService $importService
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
@ -48,6 +55,20 @@ class EggController extends ApplicationApiController
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete egg
|
||||
*
|
||||
* Delete an egg from the Panel.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function delete(GetEggRequest $request, Egg $egg): Response
|
||||
{
|
||||
$egg->delete();
|
||||
|
||||
return $this->returnNoContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export egg
|
||||
*
|
||||
@ -63,4 +84,22 @@ class EggController extends ApplicationApiController
|
||||
'Content-Type' => 'application/' . $format->value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import egg
|
||||
*
|
||||
* Create a new egg on the Panel. Returns the created egg and an HTTP/201 status response on success
|
||||
* If no uuid is supplied a new one will be generated
|
||||
* If an uuid is supplied, and it already exists the old configuration get overwritten
|
||||
*
|
||||
* @throws Exception|Throwable
|
||||
*/
|
||||
public function import(ImportEggRequest $request): JsonResponse
|
||||
{
|
||||
$egg = $this->importService->fromContent($request->getContent());
|
||||
|
||||
return $this->fractal->item($egg)
|
||||
->transformWith($this->getTransformer(EggTransformer::class))
|
||||
->respond(201);
|
||||
}
|
||||
}
|
||||
|
||||
14
app/Http/Requests/Api/Application/Eggs/ImportEggRequest.php
Normal file
14
app/Http/Requests/Api/Application/Eggs/ImportEggRequest.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\Application\Eggs;
|
||||
|
||||
use App\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||
use App\Models\Egg;
|
||||
use App\Services\Acl\Api\AdminAcl;
|
||||
|
||||
class ImportEggRequest extends ApplicationApiRequest
|
||||
{
|
||||
protected ?string $resource = Egg::RESOURCE_NAME;
|
||||
|
||||
protected int $permission = AdminAcl::WRITE;
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services\Eggs\Sharing;
|
||||
|
||||
use App\Enums\EggFormat;
|
||||
use App\Exceptions\Service\InvalidFileUploadException;
|
||||
use App\Models\Egg;
|
||||
use App\Models\EggVariable;
|
||||
@ -12,7 +13,6 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use JsonException;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||
use stdClass;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Throwable;
|
||||
@ -32,6 +32,18 @@ class EggImporterService
|
||||
|
||||
public function __construct(protected ConnectionInterface $connection) {}
|
||||
|
||||
/**
|
||||
* Take a JSON or YAML as string and parse it into a new egg.
|
||||
*
|
||||
* @throws InvalidFileUploadException|Throwable
|
||||
*/
|
||||
public function fromContent(string $content, EggFormat $format = EggFormat::YAML, ?Egg $egg = null): Egg
|
||||
{
|
||||
$parsed = $this->parse($content, $format);
|
||||
|
||||
return $this->fromParsed($parsed, $egg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an uploaded JSON or YAML file and parse it into a new egg.
|
||||
*
|
||||
@ -39,8 +51,56 @@ class EggImporterService
|
||||
*/
|
||||
public function fromFile(UploadedFile $file, ?Egg $egg = null): Egg
|
||||
{
|
||||
$parsed = $this->parseFile($file);
|
||||
if ($file->getError() !== UPLOAD_ERR_OK) {
|
||||
throw new InvalidFileUploadException('The selected file was not uploaded successfully');
|
||||
}
|
||||
|
||||
$extension = strtolower($file->getClientOriginalExtension());
|
||||
$mime = $file->getMimeType();
|
||||
|
||||
try {
|
||||
$content = $file->getContent();
|
||||
|
||||
if (in_array($extension, ['yaml', 'yml']) || str_contains($mime, 'yaml')) {
|
||||
return $this->fromContent($content, EggFormat::YAML, $egg);
|
||||
}
|
||||
|
||||
return $this->fromContent($content, EggFormat::JSON, $egg);
|
||||
} catch (Throwable $e) {
|
||||
throw new InvalidFileUploadException('File parse failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a URL (YAML or JSON) and parse it into a new egg or update an existing one.
|
||||
*
|
||||
* @throws InvalidFileUploadException|Throwable
|
||||
*/
|
||||
public function fromUrl(string $url, ?Egg $egg = null): Egg
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$extension = strtolower($info['extension']);
|
||||
|
||||
$format = match ($extension) {
|
||||
'yaml', 'yml' => EggFormat::YAML,
|
||||
'json' => EggFormat::JSON,
|
||||
default => throw new InvalidFileUploadException('Unsupported file format.'),
|
||||
};
|
||||
|
||||
$content = Http::timeout(5)->connectTimeout(1)->get($url)->throw()->body();
|
||||
|
||||
return $this->fromContent($content, $format, $egg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an array and parse it into a new egg.
|
||||
*
|
||||
* @param array<array-key, mixed> $parsed
|
||||
*
|
||||
* @throws InvalidFileUploadException|Throwable
|
||||
*/
|
||||
protected function fromParsed(array $parsed, ?Egg $egg = null): Egg
|
||||
{
|
||||
return $this->connection->transaction(function () use ($egg, $parsed) {
|
||||
$uuid = $parsed['uuid'] ?? Uuid::uuid4()->toString();
|
||||
$egg = $egg ?? Egg::where('uuid', $uuid)->first() ?? new Egg();
|
||||
@ -76,55 +136,17 @@ class EggImporterService
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a URL (YAML or JSON) and parse it into a new egg or update an existing one.
|
||||
*
|
||||
* @throws InvalidFileUploadException|Throwable
|
||||
*/
|
||||
public function fromUrl(string $url, ?Egg $egg = null): Egg
|
||||
{
|
||||
$info = pathinfo($url);
|
||||
$extension = strtolower($info['extension']);
|
||||
|
||||
$tmpDir = TemporaryDirectory::make()->deleteWhenDestroyed();
|
||||
$tmpPath = $tmpDir->path($info['basename']);
|
||||
|
||||
$fileContents = Http::timeout(5)->connectTimeout(1)->get($url)->throw()->body();
|
||||
|
||||
if (!$fileContents || !file_put_contents($tmpPath, $fileContents)) {
|
||||
throw new InvalidFileUploadException('Could not download or write temporary file.');
|
||||
}
|
||||
|
||||
$mime = match ($extension) {
|
||||
'yaml', 'yml' => 'application/yaml',
|
||||
'json' => 'application/json',
|
||||
default => throw new InvalidFileUploadException('Unsupported file format.'),
|
||||
};
|
||||
|
||||
return $this->fromFile(new UploadedFile($tmpPath, $info['basename'], $mime), $egg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an uploaded file and parses out the egg configuration from within.
|
||||
* Takes a string and parses out the egg configuration from within.
|
||||
*
|
||||
* @return array<array-key, mixed>
|
||||
*
|
||||
* @throws InvalidFileUploadException|JsonException
|
||||
*/
|
||||
protected function parseFile(UploadedFile $file): array
|
||||
protected function parse(string $content, EggFormat $format): array
|
||||
{
|
||||
if ($file->getError() !== UPLOAD_ERR_OK) {
|
||||
throw new InvalidFileUploadException('The selected file was not uploaded successfully');
|
||||
}
|
||||
|
||||
$extension = strtolower($file->getClientOriginalExtension());
|
||||
$mime = $file->getMimeType();
|
||||
|
||||
try {
|
||||
$content = $file->getContent();
|
||||
|
||||
$parsed = match (true) {
|
||||
in_array($extension, ['yaml', 'yml']),
|
||||
str_contains($mime, 'yaml') => Yaml::parse($content),
|
||||
$parsed = match ($format) {
|
||||
EggFormat::YAML => Yaml::parse($content),
|
||||
default => json_decode($content, true, 512, JSON_THROW_ON_ERROR),
|
||||
};
|
||||
} catch (Throwable $e) {
|
||||
|
||||
@ -102,6 +102,9 @@ Route::prefix('/eggs')->group(function () {
|
||||
Route::get('/', [Application\Eggs\EggController::class, 'index'])->name('api.application.eggs.eggs');
|
||||
Route::get('/{egg:id}', [Application\Eggs\EggController::class, 'view'])->name('api.application.eggs.eggs.view');
|
||||
Route::get('/{egg:id}/export', [Application\Eggs\EggController::class, 'export'])->name('api.application.eggs.eggs.export');
|
||||
Route::post('/import', [Application\Eggs\EggController::class, 'import'])->name('api.application.eggs.eggs.import');
|
||||
Route::delete('/{egg:id}', [Application\Eggs\EggController::class, 'delete'])->name('api.application.eggs.eggs.delete');
|
||||
Route::delete('/uuid/{egg:uuid}', [Application\Eggs\EggController::class, 'delete'])->name('api.application.eggs.eggs.delete.uuid');
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user