import json from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.urls import reverse_lazy from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from django.views.generic.edit import FormView from main import forms, models, util def api_docs(request): return render( request, "main/api_docs.html", { "host": settings.CANONICAL_HOST, "protocol": util.get_protocol(), }, ) class APIKeyReset(SuccessMessageMixin, LoginRequiredMixin, FormView): form_class = forms.ResetAPIKeyForm template_name = "main/api_key_reset.html" success_url = reverse_lazy("api_docs") success_message = "API key has been reset" def form_valid(self, form): super().form_valid(form) self.request.user.reset_api_key() return HttpResponseRedirect(self.get_success_url()) def _authenticate_token(request): """Verify request is authenticated with a token.""" # check authorization header auth_header = request.headers.get("Authorization") if auth_header is None: return None # check auth header form if auth_header[:7] != "Bearer ": return None # check token's user token = auth_header[7:] users_from_token = models.User.objects.filter(api_key=token) if not users_from_token: return None return users_from_token.first() @require_http_methods(["POST", "GET"]) @csrf_exempt def api_posts(request): user = _authenticate_token(request) if not user: return JsonResponse({"ok": False, "error": "Not authorized."}, status=403) # handle GET case if request.method == "GET": post_list = models.Post.objects.filter(owner=user) post_list = [ { "title": p.title, "slug": p.slug, "body": p.body, "published_at": p.published_at, "url": util.get_protocol() + p.get_absolute_url(), } for p in post_list ] return JsonResponse( { "ok": True, "post_list": post_list, } ) # POST case - validate input data try: data = json.loads(request.body.decode("utf-8")) except json.JSONDecodeError: return JsonResponse({"ok": False, "message": "Input data invalid."}, status=400) form = forms.APIPost(data) if not form.is_valid(): return JsonResponse({"ok": False, "message": "Input data invalid."}, status=400) if "title" not in data: return JsonResponse( {"ok": False, "message": "Title field is required."}, status=400 ) # POST case - create post slug = util.create_post_slug(data["title"], user) published_at = None if "published_at" in data: published_at = data["published_at"] body = "" if "body" in data: body = data["body"] post = models.Post.objects.create( owner=user, title=data["title"], slug=slug, body=body, published_at=published_at ) return JsonResponse( {"ok": True, "slug": slug, "url": util.get_protocol() + post.get_absolute_url()} ) @require_http_methods(["PATCH", "GET", "DELETE"]) @csrf_exempt def api_post(request, slug): user = _authenticate_token(request) if not user: return JsonResponse({"ok": False, "error": "Not authorized."}, status=403) # validate input data if request.method == "PATCH": try: data = json.loads(request.body.decode("utf-8")) except json.JSONDecodeError: return JsonResponse( {"ok": False, "message": "Input data invalid."}, status=400 ) form = forms.APIPost(data) if not form.is_valid(): return JsonResponse( {"ok": False, "message": "Input data invalid."}, status=400 ) # get post post_list = models.Post.objects.filter(slug=slug, owner=user) if not post_list: return JsonResponse({"ok": False, "error": "Not found."}, status=404) post = post_list.first() if post.owner != user: return JsonResponse({"ok": False, "error": "Not allowed."}, status=403) # delete case if request.method == "DELETE": post.delete() return JsonResponse( { "ok": True, } ) # retrieve case if request.method == "GET": return JsonResponse( { "ok": True, "url": util.get_protocol() + post.get_absolute_url(), "slug": post.slug, "title": post.title, "body": post.body, "published_at": post.published_at, } ) # update post if request.method == "PATCH": if "title" in data: post.title = form.cleaned_data["title"] if "slug" in data: post.slug = util.create_post_slug( form.cleaned_data["slug"], user, post=post ) if "body" in data: post.body = util.remove_control_chars(form.cleaned_data["body"]) if "published_at" in data: post.published_at = form.cleaned_data["published_at"] post.save() return JsonResponse( { "ok": True, "slug": post.slug, "url": util.get_protocol() + post.get_absolute_url(), } )