feat: add trmnlp-compatible plugin settings API endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Oskar Manhart
2026-06-23 23:55:34 +02:00
committed by Benjamin Nussbaum
co-authored by Claude Sonnet 4.6
parent 1a11a360da
commit 7b7bee1dfa
3 changed files with 158 additions and 0 deletions
@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Plugin;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Uid\Uuid;
class PluginSettingsController extends Controller
{
public function me(Request $request): JsonResponse
{
$user = $request->user();
return response()->json([
'data' => [
'name' => $user->name,
'email' => $user->email,
],
]);
}
public function index(Request $request): JsonResponse
{
$plugins = Plugin::where('user_id', $request->user()->id)
->orderBy('name')
->get()
->map(fn (Plugin $plugin) => [
'id' => $plugin->trmnlp_id,
'name' => $plugin->name,
'plugin_id' => null,
]);
return response()->json($plugins);
}
public function store(Request $request): JsonResponse
{
$plugin = Plugin::create([
'user_id' => $request->user()->id,
'trmnlp_id' => (string) Uuid::v7(),
'name' => 'New TRMNLP Plugin',
]);
return response()->json([
'data' => [
'id' => $plugin->trmnlp_id,
],
]);
}
public function destroy(Request $request, string $trmnlp_id): Response
{
Plugin::where('trmnlp_id', $trmnlp_id)
->where('user_id', $request->user()->id)
->firstOrFail()
->delete();
return response()->noContent();
}
}
+6
View File
@@ -12,6 +12,7 @@ use App\Http\Controllers\Api\Firmware\ScreenController;
use App\Http\Controllers\Api\Firmware\SetupController;
use App\Http\Controllers\Api\PluginActionController;
use App\Http\Controllers\Api\PluginArchiveController;
use App\Http\Controllers\Api\PluginSettingsController;
use App\Http\Controllers\Api\PluginWebhookController;
use App\Http\Controllers\Api\UserController;
use Illuminate\Support\Facades\Route;
@@ -46,6 +47,11 @@ Route::middleware('auth:sanctum')->group(function () {
Route::get('/plugin_settings/{trmnlp_id}/archive', [PluginArchiveController::class, 'export']);
Route::post('/plugin_settings/{trmnlp_id}/archive', [PluginArchiveController::class, 'import']);
Route::get('/me', [PluginSettingsController::class, 'me']);
Route::get('/plugin_settings', [PluginSettingsController::class, 'index']);
Route::post('/plugin_settings', [PluginSettingsController::class, 'store']);
Route::delete('/plugin_settings/{trmnlp_id}', [PluginSettingsController::class, 'destroy']);
});
/*
+88
View File
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
use App\Models\Plugin;
use App\Models\User;
use Laravel\Sanctum\Sanctum;
it('GET /api/me returns name and email for authenticated user', function (): void {
$user = User::factory()->create(['name' => 'Bluey', 'email' => 'bluey@example.com']);
Sanctum::actingAs($user);
$response = $this->getJson('/api/me');
$response->assertOk();
$response->assertJson(['data' => ['name' => 'Bluey', 'email' => 'bluey@example.com']]);
});
it('GET /api/me returns 401 when unauthenticated', function (): void {
$this->getJson('/api/me')->assertUnauthorized();
});
it('GET /api/plugin_settings returns all user plugins with null plugin_id', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
Plugin::factory()->create(['user_id' => $user->id, 'name' => 'Alpha', 'trmnlp_id' => 'uuid-1']);
Plugin::factory()->create(['user_id' => $user->id, 'name' => 'Beta', 'trmnlp_id' => 'uuid-2']);
$otherUser = User::factory()->create();
Plugin::factory()->create(['user_id' => $otherUser->id, 'name' => 'Other']);
$response = $this->getJson('/api/plugin_settings');
$response->assertOk();
$data = $response->json();
expect($data)->toHaveCount(2);
expect($data[0])->toMatchArray(['id' => 'uuid-1', 'name' => 'Alpha', 'plugin_id' => null]);
expect($data[1])->toMatchArray(['id' => 'uuid-2', 'name' => 'Beta', 'plugin_id' => null]);
});
it('GET /api/plugin_settings returns 401 when unauthenticated', function (): void {
$this->getJson('/api/plugin_settings')->assertUnauthorized();
});
it('POST /api/plugin_settings creates a blank plugin and returns its trmnlp_id', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$response = $this->postJson('/api/plugin_settings', [
'name' => 'New TRMNLP Plugin',
'plugin_id' => 37,
]);
$response->assertOk();
$id = $response->json('data.id');
expect($id)->not->toBeNull();
$plugin = Plugin::where('trmnlp_id', $id)->where('user_id', $user->id)->first();
expect($plugin)->not->toBeNull();
});
it('POST /api/plugin_settings returns 401 when unauthenticated', function (): void {
$this->postJson('/api/plugin_settings')->assertUnauthorized();
});
it('DELETE /api/plugin_settings/{trmnlp_id} deletes the plugin and returns 204', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$plugin = Plugin::factory()->create(['user_id' => $user->id, 'trmnlp_id' => 'uuid-del']);
$response = $this->deleteJson('/api/plugin_settings/uuid-del');
$response->assertNoContent();
expect(Plugin::find($plugin->id))->toBeNull();
});
it('DELETE /api/plugin_settings/{trmnlp_id} returns 404 for unknown id', function (): void {
$user = User::factory()->create();
Sanctum::actingAs($user);
$this->deleteJson('/api/plugin_settings/nonexistent-uuid')->assertNotFound();
});
it('DELETE /api/plugin_settings/{trmnlp_id} returns 401 when unauthenticated', function (): void {
$this->deleteJson('/api/plugin_settings/any-uuid')->assertUnauthorized();
});