Add rate limiting support to API keys

This commit is contained in:
Peter Stockings
2025-12-02 17:02:17 +11:00
parent 814691c235
commit d04b7f2120
5 changed files with 152 additions and 44 deletions

View File

@@ -23,8 +23,7 @@
class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 py-4 px-1 text-sm font-medium cursor-pointer">
Login History
</a>
<a hx-get="{{ url_for('settings.account') }}" hx-target="#container" hx-swap="innerHTML"
hx-push-url="true"
<a hx-get="{{ url_for('settings.account') }}" hx-target="#container" hx-swap="innerHTML" hx-push-url="true"
class="border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300 py-4 px-1 text-sm font-medium cursor-pointer">
Account
</a>
@@ -44,24 +43,33 @@
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Name</th>
<th
Name
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Key Prefix</th>
<th
Key Prefix
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Scopes</th>
<th
Scopes
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Created</th>
<th
Rate Limit
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Last Used</th>
<th
class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Actions</th>
Created
</th>
<th scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Last Used
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">Delete</span>
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
@@ -72,11 +80,26 @@
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ key.key[:8]
}}...</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{% for scope in key.scopes %}
<span
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 mr-1">{{
scope }}</span>
{% endfor %}
<div class="text-sm text-gray-900 dark:text-white">
{% if key.scopes == ['*'] %}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">Full
Access</span>
{% else %}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">{{
key.scopes|length }} Scopes</span>
{% endif %}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900 dark:text-white">
{% if key.rate_limit_count %}
{{ key.rate_limit_count }} / {{ key.rate_limit_period }}
{% else %}
<span class="text-gray-500 dark:text-gray-400">Unlimited</span>
{% endif %}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{
key.created_at.strftime('%Y-%m-%d') }}</td>
@@ -91,7 +114,7 @@
</tr>
{% else %}
<tr>
<td colspan="6" class="px-6 py-4 text-center text-sm text-gray-500 dark:text-gray-400">No API keys
<td colspan="7" class="px-6 py-4 text-center text-sm text-gray-500 dark:text-gray-400">No API keys
found. Generate one to get started.</td>
</tr>
{% endfor %}
@@ -133,6 +156,27 @@
</div>
<p class="mt-1 text-xs text-gray-500">Select "Full Access" or specific functions.</p>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Rate Limit
(Optional)</label>
<div class="flex space-x-4">
<div class="w-1/2">
<input type="number" name="rate_limit_count" placeholder="Requests count (e.g. 100)" min="1"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<div class="w-1/2">
<select name="rate_limit_period"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<option value="">No Limit</option>
<option value="minute">Per Minute</option>
<option value="hour">Per Hour</option>
<option value="day">Per Day</option>
</select>
</div>
</div>
</div>
<div class="flex justify-end space-x-3">
<button type="button" onclick="document.getElementById('create-key-modal').close()"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-600">Cancel</button>
@@ -142,4 +186,4 @@
</form>
</div>
</dialog>
{% endblock %}
{% endblock %}