diff --git a/app/Http/Controllers/Api/Application/PanelController.php b/app/Http/Controllers/Api/Application/PanelController.php new file mode 100644 index 0000000000..85d8c0bccb --- /dev/null +++ b/app/Http/Controllers/Api/Application/PanelController.php @@ -0,0 +1,23 @@ + + */ + public function __invoke(GetPanelInfoRequest $request): array + { + return $this->fractal->item(null) + ->transformWith($this->getTransformer(PanelInfoTransformer::class)) + ->toArray(); + } +} 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..3f7e124c9b --- /dev/null +++ b/app/Http/Controllers/Api/Application/Plugins/PluginController.php @@ -0,0 +1,125 @@ + + */ + public function index(GetPluginsRequest $request): array + { + return $this->fractal->collection(Plugin::all()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Install a plugin + * + * Runs migrations, seeders, and enables the plugin + * + * @return array + */ + public function install(PluginWriteRequest $request, Plugin $plugin): array + { + $this->pluginService->installPlugin($plugin); + + return $this->fractal->item($plugin->fresh()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Enable a plugin + * + * @return array + */ + public function enable(PluginWriteRequest $request, Plugin $plugin): array + { + $this->pluginService->enablePlugin($plugin); + + return $this->fractal->item($plugin->fresh()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Disable a plugin + * + * @return array + */ + public function disable(PluginWriteRequest $request, Plugin $plugin): array + { + $this->pluginService->disablePlugin($plugin); + + return $this->fractal->item($plugin->fresh()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Uninstall a plugin + * + * Rolls back migrations and removes the plugin files + */ + public function uninstall(PluginWriteRequest $request, Plugin $plugin): Response + { + $this->pluginService->uninstallPlugin($plugin, deleteFiles: true); + + return $this->returnNoContent(); + } + + /** + * Update a plugin + * + * Downloads and installs the latest version + * + * @return array + */ + public function update(PluginWriteRequest $request, Plugin $plugin): array + { + $this->pluginService->updatePlugin($plugin); + + return $this->fractal->item($plugin->fresh()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } + + /** + * Import a plugin from URL + * + * Downloads and extracts a plugin from a remote URL + * + * @return array + */ + public function import(ImportPluginRequest $request): array + { + $this->pluginService->downloadPluginFromUrl($request->input('url')); + + // The plugin model needs to be refreshed to get the new data + Plugin::clearBootedModels(); + + return $this->fractal->collection(Plugin::all()) + ->transformWith($this->getTransformer(PluginTransformer::class)) + ->toArray(); + } +} diff --git a/app/Http/Requests/Api/Application/GetPanelInfoRequest.php b/app/Http/Requests/Api/Application/GetPanelInfoRequest.php new file mode 100644 index 0000000000..0dea69f20c --- /dev/null +++ b/app/Http/Requests/Api/Application/GetPanelInfoRequest.php @@ -0,0 +1,18 @@ +user() !== null; + } +} diff --git a/app/Http/Requests/Api/Application/Plugins/GetPluginsRequest.php b/app/Http/Requests/Api/Application/Plugins/GetPluginsRequest.php new file mode 100644 index 0000000000..fa617ddb09 --- /dev/null +++ b/app/Http/Requests/Api/Application/Plugins/GetPluginsRequest.php @@ -0,0 +1,14 @@ + */ + public function rules(): array + { + return [ + 'url' => ['required', 'string', 'url'], + ]; + } +} diff --git a/app/Http/Requests/Api/Application/Plugins/PluginWriteRequest.php b/app/Http/Requests/Api/Application/Plugins/PluginWriteRequest.php new file mode 100644 index 0000000000..2cb94a0619 --- /dev/null +++ b/app/Http/Requests/Api/Application/Plugins/PluginWriteRequest.php @@ -0,0 +1,14 @@ +versionService = $versionService; + } + + public function getResourceName(): string + { + return 'panel'; + } + + /** + * @param null $model + */ + public function transform($model): array + { + $currentVersion = config('app.version', 'canary'); + + return [ + 'version' => $currentVersion, + 'git_hash' => $this->getGitHash(), + 'fqdn' => parse_url(config('app.url'), PHP_URL_HOST), + 'url' => config('app.url'), + 'timezone' => config('app.timezone'), + 'latest_version' => $this->versionService->latestPanelVersion(), + 'is_latest' => $this->versionService->isLatestPanel(), + ]; + } + + private function getGitHash(): ?string + { + if (file_exists(base_path('.git/HEAD'))) { + $head = explode(' ', file_get_contents(base_path('.git/HEAD'))); + if (array_key_exists(1, $head)) { + $path = base_path('.git/' . trim($head[1])); + if (file_exists($path)) { + return substr(file_get_contents($path), 0, 7); + } + } + } + + return null; + } +} diff --git a/app/Transformers/Api/Application/PluginTransformer.php b/app/Transformers/Api/Application/PluginTransformer.php new file mode 100644 index 0000000000..62f5d73f30 --- /dev/null +++ b/app/Transformers/Api/Application/PluginTransformer.php @@ -0,0 +1,42 @@ + $model->id, + 'name' => $model->name, + 'author' => $model->author, + 'version' => $model->version, + 'description' => $model->description, + 'category' => $model->category->value, + 'url' => $model->url, + 'update_url' => $model->update_url, + 'namespace' => $model->namespace, + 'class' => $model->class, + 'panels' => $model->panels, + 'panel_version' => $model->panel_version, + 'status' => $model->status->value, + 'status_message' => $model->status_message, + 'load_order' => $model->load_order, + 'is_compatible' => $model->isCompatible(), + 'is_update_available' => $model->isUpdateAvailable(), + 'has_settings' => $model->hasSettings(), + 'can_enable' => $model->canEnable(), + 'can_disable' => $model->canDisable(), + ]; + } +} diff --git a/routes/api-application.php b/routes/api-application.php index 07e20b51d4..b9f4cecd5b 100644 --- a/routes/api-application.php +++ b/routes/api-application.php @@ -172,3 +172,31 @@ Route::delete('/{role:id}', [Application\Roles\RoleController::class, 'delete']); }); + +/* +|-------------------------------------------------------------------------- +| Panel Stats Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/application/panel +| +*/ +Route::get('/panel', Application\PanelController::class)->name('api.application.panel'); + +/* +|-------------------------------------------------------------------------- +| Plugin Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/application/plugins +| +*/ +Route::prefix('/plugins')->group(function () { + Route::get('/', [Application\Plugins\PluginController::class, 'index'])->name('api.application.plugins'); + Route::post('/import', [Application\Plugins\PluginController::class, 'import'])->name('api.application.plugins.import'); + Route::post('/{plugin:id}/install', [Application\Plugins\PluginController::class, 'install'])->name('api.application.plugins.install'); + Route::post('/{plugin:id}/update', [Application\Plugins\PluginController::class, 'update'])->name('api.application.plugins.update'); + Route::patch('/{plugin:id}/enable', [Application\Plugins\PluginController::class, 'enable'])->name('api.application.plugins.enable'); + Route::patch('/{plugin:id}/disable', [Application\Plugins\PluginController::class, 'disable'])->name('api.application.plugins.disable'); + Route::delete('/{plugin:id}', [Application\Plugins\PluginController::class, 'uninstall'])->name('api.application.plugins.uninstall'); +});