Source code for MangAdventure.views

"""The main views and error handlers."""

from importlib.util import find_spec
from typing import TYPE_CHECKING, Any, Dict, Optional

from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from django.utils import timezone as tz
from django.views.decorators.cache import cache_control

from reader.models import Category, Chapter

from .bad_bots import BOTS
from .jsonld import breadcrumbs
from .search import parse, query

if TYPE_CHECKING:  # pragma: no cover
    from django.http import HttpRequest


def _error_context(msg: str, status: int = 500) -> Dict[str, Any]:
    return {'error_message': msg, 'error_status': status}


[docs]@cache_control(max_age=600, must_revalidate=True) def index(request: 'HttpRequest') -> HttpResponse: """ View that serves the index page which shows the latest releases. :param request: The original request. :return: A response with the rendered ``index.html`` template. """ _max = settings.CONFIG['MAX_RELEASES'] latest = Chapter.objects.prefetch_related('groups', 'series') \ .filter(published__lte=tz.now()).order_by('-published')[:_max] uri = request.build_absolute_uri('/') crumbs = breadcrumbs([('Home', uri)]) return render(request, 'index.html', { 'latest_releases': latest, 'breadcrumbs': crumbs })
[docs]@cache_control(public=True, max_age=2628000, immutable=True) def opensearch(request: 'HttpRequest') -> HttpResponse: """ View that serves the ``opensearch.xml`` file. :param request: The original request. :return: A response with the rendered ``opensearch.xml`` template. """ _icon = request.build_absolute_uri( settings.CONFIG['FAVICON'] ) _search = request.build_absolute_uri('/search/') _self = request.build_absolute_uri('/opensearch.xml') return render( request, 'opensearch.xml', { 'name': settings.CONFIG['NAME'], 'search': _search, 'self': _self, 'icon': _icon, }, 'application/opesearchdescription+xml' )
[docs]@cache_control(public=True, max_age=31536000, immutable=True) def contribute(request: 'HttpRequest') -> HttpResponse: """ View that serves the ``contribute.json`` file. :param request: The original request. :return: A response with the ``contribute.json`` file. """ return render(request, 'contribute.json', {}, 'application/json')
[docs]@cache_control(public=True, max_age=2628000, immutable=True) def robots(request: 'HttpRequest') -> HttpResponse: """ View that serves the ``robots.txt`` file. :param request: The original request. :return: A response with the generated ``robots.txt`` file. """ ctype = 'text/plain; charset=us-ascii' _robots = 'User-agent: *\nDisallow:\n\n' + '\n'.join( f'User-agent: {ua}\nDisallow: /\n' for ua in BOTS ) return HttpResponse(content=_robots, content_type=ctype)
[docs]def handler400(request: 'HttpRequest', exception: Optional[Exception] = None, template_name: str = 'error.html' ) -> HttpResponse: # pragma: no cover """ Handler for :status:`400` responses. :param request: The original request. :param exception: The exception that occurred. :param template_name: The name of the error template. :return: A :class:`~django.http.JsonResponse` for API URLs, otherwise a response with the rendered error template. """ if request.path.startswith('/api'): return JsonResponse({'error': 'Bad request'}, status=400) context = _error_context( 'The server could not understand the request.', 400 ) return render(request, template_name, context, status=400)
[docs]def handler403(request: 'HttpRequest', exception: Optional[Exception] = None, template_name: str = 'error.html') -> HttpResponse: """ Handler for :status:`403` responses. :param request: The original request. :param exception: The exception that occurred. :param template_name: The name of the error template. :return: A :class:`~django.http.JsonResponse` for API URLs, otherwise a response with the rendered error template. """ if request.path.startswith('/api'): # pragma: no cover return JsonResponse({'error': 'Forbidden'}, status=403) context = _error_context( 'You do not have permission to access this page.', 403 ) return render(request, template_name, context, status=403)
[docs]def handler404(request: 'HttpRequest', exception: Optional[Exception] = None, template_name: str = 'error.html') -> HttpResponse: """ Handler for :status:`404` responses. :param request: The original request. :param exception: The exception that occurred. :param template_name: The name of the error template. :return: A :class:`~django.http.JsonResponse` for API URLs, otherwise a response with the rendered error template. """ if request.path.startswith('/api'): # pragma: no cover return JsonResponse({'error': 'Invalid API endpoint'}, status=501) if find_spec('sentry_sdk'): # pragma: no cover from sentry_sdk import capture_message capture_message('Page not found', level='warning') context = _error_context("Sorry. This page doesn't exist.", 404) return render(request, template_name, context, status=404)
[docs]def handler500(request: 'HttpRequest', exception: Optional[Exception] = None, template_name: str = 'error.html' ) -> HttpResponse: # pragma: no cover """ Handler for :status:`500` responses. :param request: The original request. :param exception: The exception that occurred. :param template_name: The name of the error template. :return: A :class:`~django.http.JsonResponse` for API URLs, otherwise a response with the rendered error template. """ if request.path.startswith('/api'): return JsonResponse({'error': 'Internal server error'}, status=500) context = _error_context( # Shrug 'Whoops! Something went wrong. ¯∖_(ツ)_/¯' ) return render(request, template_name, context, status=500)
__all__ = [ 'index', 'search', 'opensearch', 'contribute', 'robots', 'handler400', 'handler403', 'handler404', 'handler500' ]