Add the ability to redirect to the resulting link, some styling
[bigly] / src / bigly / views.py
1 from urllib.parse import urlparse, urlunparse, parse_qs
2
3 from django.shortcuts import redirect, render
4 from django.views.generic.base import TemplateView
5
6 from rest_framework import status, viewsets
7 from rest_framework.response import Response
8 import requests
9
10 from . import serializers
11
12 def _remove_utm(link):
13     parsed_link = urlparse(link)
14     parsed_link = parsed_link._replace(
15         query='&'.join(
16             [
17                 p
18                 for p in parsed_link.query.split('&')
19                 if not p.startswith('utm_')
20             ]
21         )
22     )
23
24     return ''.join((
25         parsed_link.scheme + '://' if parsed_link.scheme else '',
26         parsed_link.netloc,
27         parsed_link.path,
28         ';' + parsed_link.params if parsed_link.params else '',
29         '?' + parsed_link.query if parsed_link.query else '',
30         '#' + parsed_link.fragment if parsed_link.fragment else '',
31     ))
32
33 def _follow_redirects(link, remove_utm):
34     while True:
35         if remove_utm:
36             link = _remove_utm(link)
37
38         response = requests.head(link)
39
40         # TODO Handle timeouts
41
42         if 301 <= response.status_code and response.status_code <= 308:
43             # TODO Handle the different kinds of redirects correctly
44
45             link = response.headers.get('Location')
46
47             if not link:
48                 # TODO Handle this
49                 raise Exception()
50
51         # TODO Handle error responses
52         else:
53             return {
54                 'link': link,
55                 'status': response.status_code,
56             }
57
58 class IndexView(TemplateView):
59     template_name = 'bigly/index.html'
60
61 index = IndexView.as_view()
62
63 def embiggen(request):
64     serializer = serializers.FollowRedirectsSerializer(data=request.GET)
65
66     if not serializer.is_valid():
67         return render(
68             request,
69             'bigly/index.html',
70             {
71                 'errors': serializer.errors,
72             },
73             status=400,
74         )
75
76     result = _follow_redirects(
77         link = serializer.data['link'],
78         remove_utm = serializer.data['remove_utm'],
79     )
80
81     if serializer.data['handler'] == 'redirect':
82         return redirect(result['link'])
83
84     else:
85         return render(
86             request,
87             'bigly/link_info.html',
88             result,
89         )
90
91 class FollowRedirectsViewSet(viewsets.ViewSet):
92     serializer_class = serializers.FollowRedirectsSerializer
93
94     def follow_redirects(self, request):
95         serializer = serializers.FollowRedirectsSerializer(data=request.query_params)
96
97         if not serializer.is_valid():
98             return Response(
99                 serializer.errors,
100                 status=status.HTTP_400_BAD_REQUEST,
101             )
102
103         result = _follow_redirects(
104             link = serializer.data['link'],
105             remove_utm = serializer.data['remove_utm'],
106         )
107
108         return Response(
109             result,
110             status=status.HTTP_200_OK,
111         )
112
113 api_follow_redirects = FollowRedirectsViewSet.as_view({
114     'get': 'follow_redirects',
115 })