add apis
Run Tests / Run Tests (push) Successful in 46s
Details
Run Tests / Run Tests (push) Successful in 46s
Details
This commit is contained in:
parent
d323754153
commit
5d337def7e
|
@ -65,6 +65,10 @@ REST_FRAMEWORK = {
|
||||||
# or allow read-only access for unauthenticated users.
|
# or allow read-only access for unauthenticated users.
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
|
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
|
||||||
|
],
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework.authentication.BasicAuthentication',
|
||||||
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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])
|
|
@ -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)),
|
||||||
|
]
|
|
@ -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')
|
|
@ -94,6 +94,12 @@ class User(AbstractUser):
|
||||||
"""Return string representation of our user"""
|
"""Return string representation of our user"""
|
||||||
return self.email
|
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)
|
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
def create_auth_token(sender, instance=None, created=False, **kwargs):
|
def create_auth_token(sender, instance=None, created=False, **kwargs):
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
{% extends "main.html" %} {% block content %}
|
||||||
|
<section class="mb-16">
|
||||||
|
<h1 class="text-4xl font-bold text-gray-800 mb-4">Your Settings</h1>
|
||||||
|
<p class="text-xl text-gray-600 mb-8">
|
||||||
|
Manage your account settings and API key
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="mb-8">
|
||||||
|
<a href="{% url 'dashboard' %}" class="text-blue-500 hover:text-blue-600"
|
||||||
|
>Back to Dashboard</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="bg-white p-6 rounded-lg shadow border-2 border-dotted border-gray-300 max-w-2xl mx-auto"
|
||||||
|
>
|
||||||
|
<h2 class="text-2xl font-bold text-gray-800 mb-4">API Key</h2>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="api-key" class="block text-sm font-medium text-gray-700 mb-2"
|
||||||
|
>Your API Key:</label
|
||||||
|
>
|
||||||
|
<div class="flex">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="api-key"
|
||||||
|
value="{{ user.api_key }}"
|
||||||
|
readonly
|
||||||
|
class="flex-grow px-3 py-2 bg-gray-100 text-gray-800 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onclick="toggleApiKeyVisibility()"
|
||||||
|
id="toggle-visibility"
|
||||||
|
class="bg-gray-300 text-gray-700 px-4 py-2 hover:bg-gray-400 transition duration-300"
|
||||||
|
>
|
||||||
|
Show
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick="copyApiKey()"
|
||||||
|
class="bg-blue-500 text-white px-4 py-2 rounded-r-md hover:bg-blue-600 transition duration-300"
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form action="{% url 'regenerate_api_key' %}" method="post" class="mt-6">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600 transition duration-300"
|
||||||
|
onclick="return confirm('Are you sure you want to regenerate your API key? This will invalidate your current key.');"
|
||||||
|
>
|
||||||
|
Regenerate API Key
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function toggleApiKeyVisibility() {
|
||||||
|
const apiKeyInput = document.getElementById("api-key");
|
||||||
|
const toggleButton = document.getElementById("toggle-visibility");
|
||||||
|
|
||||||
|
if (apiKeyInput.type === "password") {
|
||||||
|
apiKeyInput.type = "text";
|
||||||
|
toggleButton.textContent = "Hide";
|
||||||
|
} else {
|
||||||
|
apiKeyInput.type = "password";
|
||||||
|
toggleButton.textContent = "Show";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyApiKey() {
|
||||||
|
const apiKeyInput = document.getElementById("api-key");
|
||||||
|
const currentType = apiKeyInput.type;
|
||||||
|
|
||||||
|
// Temporarily change to text to allow copying
|
||||||
|
apiKeyInput.type = "text";
|
||||||
|
apiKeyInput.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
|
||||||
|
// Restore the original input type
|
||||||
|
apiKeyInput.type = currentType;
|
||||||
|
|
||||||
|
alert("API key copied to clipboard!");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -1,4 +1,4 @@
|
||||||
from django.urls import path
|
from django.urls import path, include
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("dashboard", views.dashboard, name="dashboard"),
|
path("dashboard", views.dashboard, name="dashboard"),
|
||||||
path("settings", views.settings, name="settings"),
|
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/upload", views.upload_document, name="upload_document"),
|
||||||
path("documents/<str:pk>", views.view_document, name="view_document"),
|
path("documents/<str:pk>", views.view_document, name="view_document"),
|
||||||
path(
|
path(
|
||||||
|
@ -21,6 +23,11 @@ urlpatterns = [
|
||||||
path(
|
path(
|
||||||
"documents/<str:pk>/download", views.download_document, name="download_document"
|
"documents/<str:pk>/download", views.download_document, name="download_document"
|
||||||
),
|
),
|
||||||
path("documents/<str:pk>/delete", views.delete_document, name="delete_document"),
|
path("documents/<str:pk>/delete",
|
||||||
|
views.delete_document, name="delete_document"),
|
||||||
path("auth/register", views.register, name="register"),
|
path("auth/register", views.register, name="register"),
|
||||||
|
|
||||||
|
path(
|
||||||
|
"api/v1/", include(("penparse.api.v1.urls", "api"), namespace="api")
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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 logging
|
||||||
import os
|
import os
|
||||||
import markdown
|
import markdown
|
||||||
|
@ -14,11 +18,6 @@ from django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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):
|
def index(request):
|
||||||
# return HttpResponse("Hello, world. You're at the polls index.")
|
# 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
|
@login_required
|
||||||
def dashboard(request: HttpRequest):
|
def dashboard(request: HttpRequest):
|
||||||
|
|
||||||
|
@ -58,18 +62,18 @@ def settings(request):
|
||||||
@login_required
|
@login_required
|
||||||
def view_document(request: HttpRequest, pk: str):
|
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
|
# 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)
|
doc_markup = markdown.markdown(document.content)
|
||||||
|
|
||||||
if not document:
|
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 HttpResponse(content="Document not found", status=404)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return render(request, "document.html", context={"document": document, "markup": doc_markup})
|
return render(request, "document.html", context={"document": document, "markup": doc_markup})
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue