diff --git a/app/Http/Controllers/IntegrationRequestController.php b/app/Http/Controllers/IntegrationRequestController.php index acc1c18a..5acffc91 100644 --- a/app/Http/Controllers/IntegrationRequestController.php +++ b/app/Http/Controllers/IntegrationRequestController.php @@ -40,7 +40,11 @@ class IntegrationRequestController extends Controller return $this->limitReachedResponse(); } - $integrationRequest = $user->integrationRequests()->create($request->only(['name', 'url'])); + $integrationRequest = $user->integrationRequests()->create([ + ...$request->only(['name', 'url']), + // The admin curates the board, so their proposals skip moderation. + 'status' => $user->isAdmin() ? IntegrationRequestStatus::Approved : IntegrationRequestStatus::Pending, + ]); $integrationRequest->votes()->create(['user_id' => $user->id]); return $this->payload($user, 201); @@ -96,6 +100,12 @@ class IntegrationRequestController extends Controller private function actionsRemaining(User $user): int { + // The admin has no monthly cap; report a full quota so neither the + // backend checks nor the frontend buttons ever gate them. + if ($user->isAdmin()) { + return self::MONTHLY_ACTION_LIMIT; + } + $start = now()->startOfMonth(); $used = $user->integrationRequests()->where('created_at', '>=', $start)->count() diff --git a/app/Models/User.php b/app/Models/User.php index cdc0cf63..c9be818b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -304,6 +304,11 @@ class User extends Authenticatable implements HasLocalePreference, MustVerifyEma return $this->email === config('app.demo.email'); } + public function isAdmin(): bool + { + return $this->email === config('mail.admin_email'); + } + public function preferredLocale(): string { return $this->locale ?? 'en'; diff --git a/tests/Feature/IntegrationRequestTest.php b/tests/Feature/IntegrationRequestTest.php index aaa97d33..246625ce 100644 --- a/tests/Feature/IntegrationRequestTest.php +++ b/tests/Feature/IntegrationRequestTest.php @@ -108,6 +108,29 @@ test('a user cannot exceed the monthly action limit', function () { ->assertStatus(422); }); +test('the admin bypasses the monthly limit and their requests are auto-approved', function () { + config(['mail.admin_email' => 'admin@whisper.test']); + $admin = User::factory()->create(['email' => 'admin@whisper.test']); + IntegrationRequest::factory()->count(5)->create(['user_id' => $admin->id]); + + $this->actingAs($admin) + ->postJson('/integration-requests', ['name' => 'Revolut', 'url' => 'https://revolut.com']) + ->assertCreated() + ->assertJsonPath('actionsRemaining', 3); + + $this->assertDatabaseHas('integration_requests', [ + 'user_id' => $admin->id, + 'name' => 'Revolut', + 'status' => IntegrationRequestStatus::Approved->value, + ]); + + $other = IntegrationRequest::factory()->approved()->create(); + + $this->actingAs($admin) + ->postJson("/integration-requests/{$other->id}/vote") + ->assertOk(); +}); + test('removing a vote is allowed even at the monthly limit', function () { $user = User::factory()->create(); $request = IntegrationRequest::factory()->approved()->create();