Skip to content

Conversation

@Boy132
Copy link
Member

@Boy132 Boy132 commented Jan 28, 2026

No description provided.

@Boy132 Boy132 requested a review from lancepioch January 28, 2026 09:14
@Boy132 Boy132 self-assigned this Jan 28, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

Adds a new PluginController with endpoints for listing, viewing, importing (file/URL), installing, updating, uninstalling, enabling, and disabling plugins; introduces request classes for read/write/uninstall/import, a PluginTransformer, Plugin::RESOURCE_NAME, ApiKey exposure, and route registrations under /api/application/plugins. (41 words)

Changes

Cohort / File(s) Summary
Plugin API Controller
app/Http/Controllers/Api/Application/Plugins/PluginController.php
New controller implementing plugin management endpoints: listing (filters/sort/pagination), single view, import via file and URL (201), install, update, uninstall (with delete flag), enable, and disable. Delegates to PluginService and returns transformed resources.
Plugin Request Classes
app/Http/Requests/Api/Application/Plugins/ReadPluginRequest.php, app/Http/Requests/Api/Application/Plugins/WritePluginRequest.php, app/Http/Requests/Api/Application/Plugins/UninstallPluginRequest.php, app/Http/Requests/Api/Application/Plugins/ImportFilePluginRequest.php
Adds typed request classes for READ/WRITE permission scoping and resource binding. UninstallPluginRequest adds delete:boolean validation; ImportFilePluginRequest defines `url:required
Plugin Transformer
app/Transformers/Api/Application/PluginTransformer.php
New transformer mapping Plugin model to API output: id and metadata, panels (comma-split), composer_packages (JSON decoded), and meta fields (status, status_message, load_order, is_compatible, update_available, can_enable, can_disable).
Model / Permissions
app/Models/Plugin.php, app/Models/ApiKey.php
Adds Plugin::RESOURCE_NAME = 'plugin' and includes it in ApiKey::DEFAULT_RESOURCE_NAMES so API keys can reference plugin permissions.
Routes
routes/api-application.php
Registers /api/application/plugins route group with endpoints: GET / (index), GET /{plugin:id} (view), POST /import/file, POST /import/url, POST /{plugin:id}/install, POST /{plugin:id}/update, POST /{plugin:id}/uninstall, POST /{plugin:id}/enable, POST /{plugin:id}/disable.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Controller as PluginController
  participant Service as PluginService
  participant Storage as Storage/Filesystem
  participant DB as Database
  participant Transformer as PluginTransformer

  Client->>Controller: POST /api/application/plugins/import/file (multipart file)
  Controller->>Controller: validate request (WritePluginRequest)
  Controller->>Service: importFromFile(file)
  Service->>Storage: store/process uploaded file
  Service->>DB: create/update Plugin record
  Service-->>Controller: Plugin model
  Controller->>Transformer: transform(Plugin)
  Transformer-->>Controller: JSON resource
  Controller-->>Client: 201 Created + transformed Plugin
Loading

Possibly related PRs

  • Plugin system #1866: Related PR introducing PluginService methods (download/install/update/uninstall/enable/disable) that PluginController depends on.
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate relevance to the changeset. Add a description explaining the purpose, scope, and key features of the plugin API being introduced.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add basic api for plugins' accurately and concisely describes the main change: introducing a new API for plugin management with multiple endpoints.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@app/Http/Controllers/Api/Application/Plugins/PluginController.php`:
- Around line 87-92: The call in importUrl passes a Stringable from
ImportFilePluginRequest->string('url') into downloadPluginFromUrl which requires
a native string; fix by explicitly casting the request value to string (e.g.
(string) $request->string('url')) before calling downloadPluginFromUrl in the
importUrl method so downloadPluginFromUrl receives a real string.
- Around line 109-114: After calling
$this->pluginService->installPlugin($plugin) the $plugin model isn't reloaded so
the transformer returns stale status/meta; update installPlugin usage to refresh
the model before transforming (e.g. call $plugin->refresh() or re-query via
fresh()) and apply the same pattern in update(), uninstall(), enable(), and
disable() so the response uses the updated Plugin model when passing to
$this->fractal->item($plugin)->transformWith($this->getTransformer(PluginTransformer::class)).

In `@app/Transformers/Api/Application/PluginTransformer.php`:
- Line 35: In PluginTransformer (file: PluginTransformer.php) the
'composer_packages' decoding uses json_decode with JSON_THROW_ON_ERROR which
will throw a JsonException when $model->composer_packages is null or empty;
mirror the null/empty check used for 'panels' (line 33) and only call
json_decode when $model->composer_packages is a non-empty string, otherwise
return an empty array (or suitable default) for the 'composer_packages' field so
decoding never receives null/empty input.
🧹 Nitpick comments (3)
app/Http/Requests/Api/Application/Plugins/ImportFilePluginRequest.php (1)

5-12: Class name does not match its purpose.

This request class validates a url field but is named ImportFilePluginRequest. Based on the controller usage, importUrl() uses this class while importFile() uses WritePluginRequest. Consider renaming to ImportUrlPluginRequest for clarity.

app/Http/Controllers/Api/Application/Plugins/PluginController.php (2)

41-41: Consider bounding per_page to prevent excessive queries.

The per_page parameter is taken directly from user input without an upper limit. A malicious or careless client could request an excessively large page size. Consider using min() to cap it.

♻️ Proposed fix
-            ->paginate($request->query('per_page') ?? 10);
+            ->paginate(min($request->query('per_page') ?? 10, 100));

69-78: Consider validating file presence in request class.

The manual file check could be moved to a dedicated ImportFilePluginRequest (with the current one renamed to ImportUrlPluginRequest) for consistency with other endpoints that use request validation. This would centralize validation logic.

@Boy132 Boy132 merged commit 26312e3 into main Jan 31, 2026
32 checks passed
@Boy132 Boy132 deleted the boy132/plugins-api branch January 31, 2026 23:10
@github-actions github-actions bot locked and limited conversation to collaborators Jan 31, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants