diff --git a/penparse/config/settings.py b/penparse/config/settings.py index 9259a0c..df72cd6 100644 --- a/penparse/config/settings.py +++ b/penparse/config/settings.py @@ -65,6 +65,10 @@ REST_FRAMEWORK = { # or allow read-only access for unauthenticated users. 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' + ], + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.TokenAuthentication', ] } diff --git a/penparse/penparse/api/__init__.py b/penparse/penparse/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/penparse/penparse/api/v1/__init__.py b/penparse/penparse/api/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/penparse/penparse/api/v1/serializers.py b/penparse/penparse/api/v1/serializers.py new file mode 100644 index 0000000..2a42942 --- /dev/null +++ b/penparse/penparse/api/v1/serializers.py @@ -0,0 +1,23 @@ +from django.urls import reverse +from rest_framework import serializers +from rest_framework.request import Request +from penparse.models import ImageMemo + + +class ImageMemoSerializer(serializers.ModelSerializer): + + image = serializers.SerializerMethodField() + + class Meta: + model = ImageMemo + fields = ('id', 'image', 'content', 'model_name', 'status', + 'error_message', 'created_at', 'updated_at') + + def get_image(self, obj): + request = self.context.get('request') + if request is not None and isinstance(request, Request): + url = reverse('document_image', args=[obj.pk]) + return request.build_absolute_uri(url) + else: + # Fallback to relative URL if request is not available + return reverse('document_image', args=[obj.pk]) diff --git a/penparse/penparse/api/v1/urls.py b/penparse/penparse/api/v1/urls.py new file mode 100644 index 0000000..b50a407 --- /dev/null +++ b/penparse/penparse/api/v1/urls.py @@ -0,0 +1,14 @@ +from django.urls import include, path +from rest_framework import routers + +from .views import ImageMemoViewSet + + +router = routers.DefaultRouter() + +router.register(r'memos', ImageMemoViewSet) + + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/penparse/penparse/api/v1/views.py b/penparse/penparse/api/v1/views.py new file mode 100644 index 0000000..6c30178 --- /dev/null +++ b/penparse/penparse/api/v1/views.py @@ -0,0 +1,16 @@ +from rest_framework import permissions, viewsets + +from .serializers import ImageMemoSerializer +from penparse.models import ImageMemo + + +class ImageMemoViewSet(viewsets.ModelViewSet): + """ + API endpoint that allows users to be viewed or edited. + """ + queryset = ImageMemo.objects.all() + serializer_class = ImageMemoSerializer + permission_classes = [permissions.IsAuthenticated] + + def get_queryset(self): + return ImageMemo.objects.filter(author=self.request.user).all().order_by('-updated_at') diff --git a/penparse/penparse/models.py b/penparse/penparse/models.py index a3e13e6..a9e5ffd 100644 --- a/penparse/penparse/models.py +++ b/penparse/penparse/models.py @@ -94,6 +94,12 @@ class User(AbstractUser): """Return string representation of our user""" return self.email + @property + def api_key(self): + """Return the API key (token) for the user""" + token = Token.objects.filter(user_id=self.id).first() + return token.key if token else None + @receiver(post_save, sender=settings.AUTH_USER_MODEL) def create_auth_token(sender, instance=None, created=False, **kwargs): diff --git a/penparse/penparse/templates/settings.html b/penparse/penparse/templates/settings.html new file mode 100644 index 0000000..2e867b5 --- /dev/null +++ b/penparse/penparse/templates/settings.html @@ -0,0 +1,88 @@ +{% extends "main.html" %} {% block content %} +
+

Your Settings

+

+ Manage your account settings and API key +

+ +
+ Back to Dashboard +
+ +
+

API Key

+
+ +
+ + + +
+
+
+ {% csrf_token %} + +
+
+
+ + + +{% endblock %} diff --git a/penparse/penparse/urls.py b/penparse/penparse/urls.py index 0125ef3..73ceeb7 100644 --- a/penparse/penparse/urls.py +++ b/penparse/penparse/urls.py @@ -1,4 +1,4 @@ -from django.urls import path +from django.urls import path, include from . import views @@ -6,6 +6,8 @@ urlpatterns = [ path("", views.index, name="index"), path("dashboard", views.dashboard, name="dashboard"), path("settings", views.settings, name="settings"), + path("settings/regenerate-key", views.regenerate_api_key, + name="regenerate_api_key"), path("documents/upload", views.upload_document, name="upload_document"), path("documents/", views.view_document, name="view_document"), path( @@ -21,6 +23,11 @@ urlpatterns = [ path( "documents//download", views.download_document, name="download_document" ), - path("documents//delete", views.delete_document, name="delete_document"), + path("documents//delete", + views.delete_document, name="delete_document"), path("auth/register", views.register, name="register"), + + path( + "api/v1/", include(("penparse.api.v1.urls", "api"), namespace="api") + ), ] diff --git a/penparse/penparse/views/__init__.py b/penparse/penparse/views/__init__.py index 890ebd9..6a0c974 100644 --- a/penparse/penparse/views/__init__.py +++ b/penparse/penparse/views/__init__.py @@ -1,3 +1,7 @@ +from .delete import delete_document +from .upload import upload_document +from .register import register +from .thumbnail import document_thumbnail, document_image import logging import os import markdown @@ -14,11 +18,6 @@ from django.contrib.auth.decorators import login_required logger = logging.getLogger(__name__) -from .thumbnail import document_thumbnail, document_image -from .register import register -from .upload import upload_document -from .delete import delete_document - def index(request): # return HttpResponse("Hello, world. You're at the polls index.") @@ -39,6 +38,11 @@ __all__ = [ ] +@login_required +def regenerate_api_key(request): + return HttpResponse("TODO: regenerate api key") + + @login_required def dashboard(request: HttpRequest): @@ -58,18 +62,18 @@ def settings(request): @login_required def view_document(request: HttpRequest, pk: str): - ## check that the document exists and belongs to the user + # check that the document exists and belongs to the user # find document with given ID (pk path param) and current user id - document = ImageMemo.objects.filter(id=pk, author__id=request.user.id).first() + document = ImageMemo.objects.filter( + id=pk, author__id=request.user.id).first() doc_markup = markdown.markdown(document.content) if not document: - logger.debug(f"No memo found for user={request.user.id} and memo_id={pk}") + logger.debug( + f"No memo found for user={request.user.id} and memo_id={pk}") return HttpResponse(content="Document not found", status=404) - - return render(request, "document.html", context={"document": document, "markup": doc_markup})