Skip to content

Commit

Permalink
Add ability to configure the API rate limit via .env (#857)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kovah committed Oct 24, 2024
1 parent 085a86e commit 6b2c632
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 90 deletions.
2 changes: 1 addition & 1 deletion app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Kernel extends HttpKernel
],

'api' => [
'throttle:60,1',
// Throttling is configured in routes/api.php
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Expand Down
22 changes: 12 additions & 10 deletions config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,29 +133,31 @@

/*
|--------------------------------------------------------------------------
| Guest Access
| Trusted Proxies
|--------------------------------------------------------------------------
|
| If enabled, guest will have access to all links, categories and tags.
| However, they will not see private links and any notes and - obviously -
| can't edit any of the entries.
| Specify which proxies should be trusted by default. As it's unknown which
| proxy ist used in front of LinkAce, all are allowed by default.
| Also see App\Http\Middleware\TrustProxies
|
*/

'guest_access' => (bool)env('GUEST_ACCESS', false),
'trusted_proxies' => env('TRUSTED_PROXIES', '*'),

/*
|--------------------------------------------------------------------------
| Trusted Proxies
| API Rate Limiting
|--------------------------------------------------------------------------
|
| Specify which proxies should be trusted by default. As it's unknown which
| proxy ist used in front of LinkAce, all are allowed by default.
| Also see App\Http\Middleware\TrustProxies
| By default, the LinkAce API is rate limited: 60 requests per minute.
| Use the API_RATE_LIMIT setting in your .env file to configure this.
| the first number defines the number of requests, the second the time
| frame for the requests.
| 60,1 is the default which means: 60 requests per 1 minute
|
*/

'trusted_proxies' => env('TRUSTED_PROXIES', '*'),
'api_rate_limit' => env('API_RATE_LIMIT', '60,1'),

/*
|--------------------------------------------------------------------------
Expand Down
160 changes: 81 additions & 79 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,82 +24,84 @@
|
*/

Route::prefix('v2')->middleware(['auth:sanctum'])->group(function () {

Route::get('links/check', LinkCheckController::class)
->name('api.links.check');

Route::apiResource('links', LinkController::class)
->names([
'index' => 'api.links.index',
'show' => 'api.links.show',
'store' => 'api.links.store',
'update' => 'api.links.update',
'destroy' => 'api.links.destroy',
]);

Route::get('links/{link}/notes', LinkNotesController::class)
->name('api.links.notes');

Route::apiResource('lists', ListController::class)
->names([
'index' => 'api.lists.index',
'show' => 'api.lists.show',
'store' => 'api.lists.store',
'update' => 'api.lists.update',
'destroy' => 'api.lists.destroy',
]);

Route::get('lists/{list}/links', ListLinksController::class)
->name('api.lists.links');

Route::apiResource('tags', TagController::class)
->names([
'index' => 'api.tags.index',
'show' => 'api.tags.show',
'store' => 'api.tags.store',
'update' => 'api.tags.update',
'destroy' => 'api.tags.destroy',
]);

Route::get('tags/{tag}/links', TagLinksController::class)
->name('api.tags.links');

Route::apiResource('notes', NoteController::class)
->names([
'store' => 'api.notes.store',
'update' => 'api.notes.update',
'destroy' => 'api.notes.destroy',
])
->except(['index', 'show']);

Route::post('bulk/links', [BulkStoreController::class, 'storeLinks'])->name('api.bulk.links.store');
Route::post('bulk/lists', [BulkStoreController::class, 'storeLists'])->name('api.bulk.lists.store');
Route::post('bulk/tags', [BulkStoreController::class, 'storeTags'])->name('api.bulk.tags.store');

Route::patch('bulk/links', [BulkEditController::class, 'updateLinks'])->name('api.bulk.links.update');
Route::patch('bulk/lists', [BulkEditController::class, 'updateLists'])->name('api.bulk.lists.update');
Route::patch('bulk/tags', [BulkEditController::class, 'updateTags'])->name('api.bulk.tags.update');
Route::delete('bulk/delete', [BulkEditController::class, 'delete'])->name('api.bulk.delete');

Route::get('search/links', [SearchController::class, 'searchLinks'])
->name('api.search.links');
Route::get('search/tags', [SearchController::class, 'searchByTags'])
->name('api.search.tags');
Route::get('search/lists', [SearchController::class, 'searchByLists'])
->name('api.search.lists');

Route::get('trash/links', [TrashController::class, 'getLinks'])
->name('api.trash.links');
Route::get('trash/lists', [TrashController::class, 'getLists'])
->name('api.trash.lists');
Route::get('trash/tags', [TrashController::class, 'getTags'])
->name('api.trash.tags');
Route::get('trash/notes', [TrashController::class, 'getNotes'])
->name('api.trash.notes');

Route::delete('trash/clear', [TrashController::class, 'clear'])
->name('api.trash.clear');
Route::patch('trash/restore', [TrashController::class, 'restore'])
->name('api.trash.restore');
});
Route::prefix('v2')
->middleware(['auth:sanctum', 'throttle:' . config('app.api_rate_limit')])
->group(function () {

Route::get('links/check', LinkCheckController::class)
->name('api.links.check');

Route::apiResource('links', LinkController::class)
->names([
'index' => 'api.links.index',
'show' => 'api.links.show',
'store' => 'api.links.store',
'update' => 'api.links.update',
'destroy' => 'api.links.destroy',
]);

Route::get('links/{link}/notes', LinkNotesController::class)
->name('api.links.notes');

Route::apiResource('lists', ListController::class)
->names([
'index' => 'api.lists.index',
'show' => 'api.lists.show',
'store' => 'api.lists.store',
'update' => 'api.lists.update',
'destroy' => 'api.lists.destroy',
]);

Route::get('lists/{list}/links', ListLinksController::class)
->name('api.lists.links');

Route::apiResource('tags', TagController::class)
->names([
'index' => 'api.tags.index',
'show' => 'api.tags.show',
'store' => 'api.tags.store',
'update' => 'api.tags.update',
'destroy' => 'api.tags.destroy',
]);

Route::get('tags/{tag}/links', TagLinksController::class)
->name('api.tags.links');

Route::apiResource('notes', NoteController::class)
->names([
'store' => 'api.notes.store',
'update' => 'api.notes.update',
'destroy' => 'api.notes.destroy',
])
->except(['index', 'show']);

Route::post('bulk/links', [BulkStoreController::class, 'storeLinks'])->name('api.bulk.links.store');
Route::post('bulk/lists', [BulkStoreController::class, 'storeLists'])->name('api.bulk.lists.store');
Route::post('bulk/tags', [BulkStoreController::class, 'storeTags'])->name('api.bulk.tags.store');

Route::patch('bulk/links', [BulkEditController::class, 'updateLinks'])->name('api.bulk.links.update');
Route::patch('bulk/lists', [BulkEditController::class, 'updateLists'])->name('api.bulk.lists.update');
Route::patch('bulk/tags', [BulkEditController::class, 'updateTags'])->name('api.bulk.tags.update');
Route::delete('bulk/delete', [BulkEditController::class, 'delete'])->name('api.bulk.delete');

Route::get('search/links', [SearchController::class, 'searchLinks'])
->name('api.search.links');
Route::get('search/tags', [SearchController::class, 'searchByTags'])
->name('api.search.tags');
Route::get('search/lists', [SearchController::class, 'searchByLists'])
->name('api.search.lists');

Route::get('trash/links', [TrashController::class, 'getLinks'])
->name('api.trash.links');
Route::get('trash/lists', [TrashController::class, 'getLists'])
->name('api.trash.lists');
Route::get('trash/tags', [TrashController::class, 'getTags'])
->name('api.trash.tags');
Route::get('trash/notes', [TrashController::class, 'getNotes'])
->name('api.trash.notes');

Route::delete('trash/clear', [TrashController::class, 'clear'])
->name('api.trash.clear');
Route::patch('trash/restore', [TrashController::class, 'restore'])
->name('api.trash.restore');
});
24 changes: 24 additions & 0 deletions tests/Controller/API/GeneralApiTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Controller\API;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Controller\API\ApiTestCase;
use Tests\Controller\Traits\PreparesTestData;

class GeneralApiTest extends ApiTestCase
{
public static function setUpBeforeClass(): void
{
parent::setUpBeforeClass();
$_ENV['API_RATE_LIMIT'] = '120,1';
}

public function testCustomRateLimit(): void
{
$response = $this->getJsonAuthorized('api/v2/links');

$response->assertHeader('x-ratelimit-limit', 120);
$response->assertHeader('x-ratelimit-remaining', 119);
}
}

0 comments on commit 6b2c632

Please sign in to comment.