feat: add Daedalus API token management to profile settings
- Display the user's DRF auth token on the profile settings page - Add copy-to-clipboard button for easy token retrieval - Add token regeneration endpoint with confirmation prompt - Auto-create token on first visit via get_or_create - Instruct users to set DAEDALUS_MNEMOSYNE_API_KEY in Daedalus env
This commit is contained in:
@@ -140,5 +140,32 @@
|
|||||||
<button type="submit" class="btn btn-primary">Save Settings</button>
|
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Daedalus API Token — separate form, outside the settings form -->
|
||||||
|
<div class="card bg-base-200 mb-6">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title text-lg">Daedalus API Token</h2>
|
||||||
|
<p class="text-sm opacity-70 mb-4">
|
||||||
|
Used by Daedalus to authenticate with Mnemosyne. Set
|
||||||
|
<code class="font-mono text-xs">DAEDALUS_MNEMOSYNE_API_KEY</code>
|
||||||
|
in your Daedalus environment to this value.
|
||||||
|
</p>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<code class="font-mono bg-base-300 px-3 py-2 rounded flex-1 break-all select-all text-sm">{{ api_token.key }}</code>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-ghost btn-sm"
|
||||||
|
onclick="navigator.clipboard.writeText('{{ api_token.key }}').then(() => { this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000); }).catch(() => {})">
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<form method="post" action="{% url 'themis:api-token-regenerate' %}"
|
||||||
|
onsubmit="return confirm('Regenerate token? Daedalus will stop working until you update DAEDALUS_MNEMOSYNE_API_KEY.')">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-warning btn-sm">Regenerate</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ urlpatterns = [
|
|||||||
path("live/", views.live, name="live"),
|
path("live/", views.live, name="live"),
|
||||||
# Profile settings
|
# Profile settings
|
||||||
path("profile/settings/", views.profile_settings, name="profile-settings"),
|
path("profile/settings/", views.profile_settings, name="profile-settings"),
|
||||||
|
path("profile/api-token/regenerate/", views.api_token_regenerate, name="api-token-regenerate"),
|
||||||
# API key management
|
# API key management
|
||||||
path("profile/keys/", views.key_list, name="key-list"),
|
path("profile/keys/", views.key_list, name="key-list"),
|
||||||
path("profile/keys/add/", views.key_create, name="key-create"),
|
path("profile/keys/add/", views.key_create, name="key-create"),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ from django.shortcuts import get_object_or_404, redirect, render
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.decorators.http import require_GET, require_http_methods, require_POST
|
from django.views.decorators.http import require_GET, require_http_methods, require_POST
|
||||||
|
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
|
||||||
from themis.encryption import encrypt_value
|
from themis.encryption import encrypt_value
|
||||||
from themis.forms import APIKeyCreateForm, APIKeyEditForm, ProfileSettingsForm
|
from themis.forms import APIKeyCreateForm, APIKeyEditForm, ProfileSettingsForm
|
||||||
from themis.models import UserAPIKey, UserNotification
|
from themis.models import UserAPIKey, UserNotification
|
||||||
@@ -63,6 +65,7 @@ def live(request):
|
|||||||
def profile_settings(request):
|
def profile_settings(request):
|
||||||
"""Display and update user profile preferences."""
|
"""Display and update user profile preferences."""
|
||||||
profile = request.user.profile
|
profile = request.user.profile
|
||||||
|
api_token, _ = Token.objects.get_or_create(user=request.user)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = ProfileSettingsForm(request.POST, instance=profile)
|
form = ProfileSettingsForm(request.POST, instance=profile)
|
||||||
@@ -73,7 +76,17 @@ def profile_settings(request):
|
|||||||
else:
|
else:
|
||||||
form = ProfileSettingsForm(instance=profile)
|
form = ProfileSettingsForm(instance=profile)
|
||||||
|
|
||||||
return render(request, "themis/profile/settings.html", {"form": form})
|
return render(request, "themis/profile/settings.html", {"form": form, "api_token": api_token})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@require_POST
|
||||||
|
def api_token_regenerate(request):
|
||||||
|
"""Delete and recreate the user's DRF API token."""
|
||||||
|
Token.objects.filter(user=request.user).delete()
|
||||||
|
Token.objects.create(user=request.user)
|
||||||
|
messages.success(request, "API token regenerated.")
|
||||||
|
return redirect("themis:profile-settings")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user