diff --git a/app/Http/Controllers/Api/Application/Plugins/PluginController.php b/app/Http/Controllers/Api/Application/Plugins/PluginController.php new file mode 100644 index 0000000000..8697414e22 --- /dev/null +++ b/app/Http/Controllers/Api/Application/Plugins/PluginController.php @@ -0,0 +1,203 @@ + + */ + public function index(ReadPluginRequest $request): array + { + $plugins = QueryBuilder::for(Plugin::class) + ->allowedFilters(['id', 'name', 'author', 'category']) + ->allowedSorts(['id', 'name', 'author', 'category']) + ->paginate($request->query('per_page') ?? 10); + + return $this->fractal->collection($plugins) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * View plugin + * + * Return a single plugin. + * + * @return array + */ + public function view(ReadPluginRequest $request, Plugin $plugin): array + { + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Import plugin (file) + * + * Imports a new plugin file. + * + * @throws Exception + */ + public function importFile(WritePluginRequest $request): Response + { + if (!$request->hasFile('plugin')) { + throw new PanelException("No 'plugin' file in request"); + } + + $this->pluginService->downloadPluginFromFile($request->file('plugin')); + + return new Response('', Response::HTTP_CREATED); + } + + /** + * Import plugin (url) + * + * Imports a new plugin from an url. + * + * @throws Exception + */ + public function importUrl(ImportFilePluginRequest $request): Response + { + $this->pluginService->downloadPluginFromUrl($request->input('url')); + + return new Response('', Response::HTTP_CREATED); + } + + /** + * Install plugin + * + * Installs and enables a plugin. + * + * @return array + * + * @throws Exception + */ + public function install(WritePluginRequest $request, Plugin $plugin): array + { + if ($plugin->status !== PluginStatus::NotInstalled) { + throw new PanelException('Plugin is already installed'); + } + + $this->pluginService->installPlugin($plugin); + + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Update plugin + * + * Downloads and installs an update for a plugin. Will throw if no update is available. + * + * @return array + * + * @throws Exception + */ + public function update(WritePluginRequest $request, Plugin $plugin): array + { + if (!$plugin->isUpdateAvailable()) { + throw new PanelException("Plugin doesn't need updating"); + } + + $this->pluginService->updatePlugin($plugin); + + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Uninstall plugin + * + * Uninstalls a plugin. Optionally it will delete the plugin folder too. + * + * @return array + * + * @throws Exception + */ + public function uninstall(UninstallPluginRequest $request, Plugin $plugin): array + { + if ($plugin->status === PluginStatus::NotInstalled) { + throw new PanelException('Plugin is not installed'); + } + + $this->pluginService->uninstallPlugin($plugin, $request->boolean('delete')); + + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Enable plugin + * + * Enables a plugin. + * + * @return array + * + * @throws Exception + */ + public function enable(WritePluginRequest $request, Plugin $plugin): array + { + if (!$plugin->canEnable()) { + throw new PanelException("Plugin can't be enabled"); + } + + $this->pluginService->enablePlugin($plugin); + + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Disable plugin + * + * Disables a plugin. + * + * @return array + * + * @throws Exception + */ + public function disable(WritePluginRequest $request, Plugin $plugin): array + { + if (!$plugin->canDisable()) { + throw new PanelException("Plugin can't be disabled"); + } + + $this->pluginService->disablePlugin($plugin); + + return $this->fractal->item($plugin) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/Plugins/ImportFilePluginRequest.php b/app/Http/Requests/Api/Application/Plugins/ImportFilePluginRequest.php new file mode 100644 index 0000000000..99e888912d --- /dev/null +++ b/app/Http/Requests/Api/Application/Plugins/ImportFilePluginRequest.php @@ -0,0 +1,13 @@ + 'required|string', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Plugins/ReadPluginRequest.php b/app/Http/Requests/Api/Application/Plugins/ReadPluginRequest.php new file mode 100644 index 0000000000..798d3cab09 --- /dev/null +++ b/app/Http/Requests/Api/Application/Plugins/ReadPluginRequest.php @@ -0,0 +1,14 @@ +|null $rules + * @return array + */ + public function rules(?array $rules = null): array + { + return [ + 'delete' => 'boolean', + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Plugins/WritePluginRequest.php b/app/Http/Requests/Api/Application/Plugins/WritePluginRequest.php new file mode 100644 index 0000000000..8c4c7a8155 --- /dev/null +++ b/app/Http/Requests/Api/Application/Plugins/WritePluginRequest.php @@ -0,0 +1,14 @@ + $model->id, + 'name' => $model->name, + 'author' => $model->author, + 'version' => $model->version, + 'description' => $model->description, + 'category' => $model->category, + 'url' => $model->url, + 'update_url' => $model->update_url, + 'namespace' => $model->namespace, + 'class' => $model->class, + 'panels' => $model->panels ? explode(',', $model->panels) : null, + 'panel_version' => $model->panel_version, + 'composer_packages' => $model->composer_packages ? json_decode($model->composer_packages, true, 512, JSON_THROW_ON_ERROR) : null, + 'meta' => [ + 'status' => $model->status, + 'status_message' => $model->status_message, + 'load_order' => $model->load_order, + 'is_compatible' => $model->isCompatible(), + 'update_available' => $model->isUpdateAvailable(), + 'can_enable' => $model->canEnable(), + 'can_disable' => $model->canDisable(), + ], + ]; + } +} diff --git a/routes/api-application.php b/routes/api-application.php index 07e20b51d4..c4925159f5 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -172,3 +172,26 @@ Route::delete('/{role:id}', [Application\Roles\RoleController::class, 'delete']); }); + +/* +|-------------------------------------------------------------------------- +| Plugin Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/application/plugins +| +*/ +Route::prefix('/plugins')->group(function () { + Route::get('/', [Application\Plugins\PluginController::class, 'index'])->name('api.application.plugins'); + Route::get('/{plugin:id}', [Application\Plugins\PluginController::class, 'view'])->name('api.application.plugins.view'); + + Route::post('/import/file', [Application\Plugins\PluginController::class, 'importFile']); + Route::post('/import/url', [Application\Plugins\PluginController::class, 'importUrl']); + + Route::post('/{plugin:id}/install', [Application\Plugins\PluginController::class, 'install']); + Route::post('/{plugin:id}/update', [Application\Plugins\PluginController::class, 'update']); + Route::post('/{plugin:id}/uninstall', [Application\Plugins\PluginController::class, 'uninstall']); + + Route::post('/{plugin:id}/enable', [Application\Plugins\PluginController::class, 'enable']); + Route::post('/{plugin:id}/disable', [Application\Plugins\PluginController::class, 'disable']); +});