API token management (#1248)

This commit is contained in:
Sascha Ißbrücker
2025-12-14 17:51:53 +01:00
committed by GitHub
parent 492de5618c
commit 83092ccb48
22 changed files with 560 additions and 124 deletions

View File

@@ -1,6 +1,6 @@
from django.http import Http404
from bookmarks.models import Bookmark, BookmarkAsset, BookmarkBundle, Toast
from bookmarks.models import ApiToken, Bookmark, BookmarkAsset, BookmarkBundle, Toast
from bookmarks.type_defs import HttpRequest
@@ -65,3 +65,10 @@ def toast_write(request: HttpRequest, toast_id: int | str):
return Toast.objects.get(pk=toast_id, owner=request.user)
except Toast.DoesNotExist:
raise Http404("Toast does not exist")
def api_token_write(request: HttpRequest, token_id: int | str):
try:
return ApiToken.objects.get(id=token_id, user=request.user)
except (ApiToken.DoesNotExist, ValueError):
raise Http404("API token does not exist")

View File

@@ -12,9 +12,9 @@ from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from django.urls import reverse
from django.utils import timezone
from rest_framework.authtoken.models import Token
from bookmarks.models import (
ApiToken,
Bookmark,
UserProfileForm,
FeedToken,
@@ -25,6 +25,7 @@ from bookmarks.services import exporter, tasks
from bookmarks.services import importer
from bookmarks.type_defs import HttpRequest
from bookmarks.utils import app_version
from bookmarks.views import access
logger = logging.getLogger(__name__)
@@ -168,7 +169,14 @@ def get_ttl_hash(seconds=3600):
@login_required
def integrations(request):
application_url = request.build_absolute_uri(reverse("linkding:bookmarks.new"))
api_token = Token.objects.get_or_create(user=request.user)[0]
api_tokens = ApiToken.objects.filter(user=request.user).order_by("-created")
api_token_key = request.session.pop("api_token_key", None)
api_token_name = request.session.pop("api_token_name", None)
api_success_message = _find_message_with_tag(
messages.get_messages(request), "api_success_message"
)
feed_token = FeedToken.objects.get_or_create(user=request.user)[0]
all_feed_url = request.build_absolute_uri(
reverse("linkding:feeds.all", args=[feed_token.key])
@@ -182,12 +190,16 @@ def integrations(request):
public_shared_feed_url = request.build_absolute_uri(
reverse("linkding:feeds.public_shared")
)
return render(
request,
"settings/integrations.html",
{
"application_url": application_url,
"api_token": api_token.key,
"api_tokens": api_tokens,
"api_token_key": api_token_key,
"api_token_name": api_token_name,
"api_success_message": api_success_message,
"all_feed_url": all_feed_url,
"unread_feed_url": unread_feed_url,
"shared_feed_url": shared_feed_url,
@@ -196,6 +208,46 @@ def integrations(request):
)
@login_required
def create_api_token(request):
if request.method == "POST":
name = request.POST.get("name", "").strip()
if not name:
name = "API Token"
token = ApiToken(user=request.user, name=name)
token.save()
request.session["api_token_key"] = token.key
request.session["api_token_name"] = token.name
messages.success(
request,
f'API token "{token.name}" created successfully',
"api_success_message",
)
return HttpResponseRedirect(reverse("linkding:settings.integrations"))
return render(request, "settings/create_api_token_modal.html")
@login_required
def delete_api_token(request):
if request.method == "POST":
token_id = request.POST.get("token_id")
token = access.api_token_write(request, token_id)
token_name = token.name
token.delete()
messages.success(
request,
f'API token "{token_name}" has been deleted.',
"api_success_message",
)
return HttpResponseRedirect(reverse("linkding:settings.integrations"))
@login_required
def bookmark_import(request: HttpRequest):
import_file = request.FILES.get("import_file")