feat(integration-requests): let the admin bypass limits and auto-approve (#551)

## What

The user matching \`ADMIN_EMAIL\` curates the integration-requests
board, so they get special treatment:

- **No monthly cap** — \`actionsRemaining()\` reports a full quota for
the admin, so neither the backend checks nor the frontend buttons ever
gate them.
- **Auto-approved proposals** — requests created by the admin are stored
as \`approved\` instead of \`pending\`, skipping moderation.

## How

- New \`User::isAdmin()\` helper, mirroring \`isDemoAccount()\`,
comparing against \`config('mail.admin_email')\` (already wired to
\`ADMIN_EMAIL\`).
- \`IntegrationRequestController::actionsRemaining()\` early-returns the
limit for the admin.
- \`IntegrationRequestController::store()\` sets \`status\` to
\`Approved\` for the admin.

## Tests

- Added a feature test covering the admin bypassing the monthly limit
and auto-approval. All 16 \`IntegrationRequestTest\` tests pass.
This commit is contained in:
Víctor Falcón 2026-06-17 16:12:51 +02:00 committed by GitHub
parent 5e8f227fbd
commit 7e9122eaf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 1 deletions

View File

@ -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()

View File

@ -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';

View File

@ -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();