c74bc28a9aef7fad523c8ae0fd789561a011b91b
[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 class FAQView(TemplateView):
64     template_name = 'bigly/faq.html'
65
66 faq = FAQView.as_view()
67
68 def embiggen(request):
69     serializer = serializers.FollowRedirectsSerializer(data=request.GET)
70
71     if not serializer.is_valid():
72         return render(
73             request,
74             'bigly/index.html',
75             {
76                 'errors': serializer.errors,
77             },
78             status=400,
79         )
80
81     result = _follow_redirects(
82         link = serializer.data['link'],
83         remove_utm = serializer.data['remove_utm'],
84     )
85
86     if serializer.data['handler'] == 'redirect':
87         return redirect(result['link'])
88
89     else:
90         return render(
91             request,
92             'bigly/link_info.html',
93             result,
94         )
95
96 class FollowRedirectsViewSet(viewsets.ViewSet):
97     serializer_class = serializers.FollowRedirectsSerializer
98
99     def follow_redirects(self, request):
100         serializer = serializers.FollowRedirectsSerializer(data=request.query_params)
101
102         if not serializer.is_valid():
103             return Response(
104                 serializer.errors,
105                 status=status.HTTP_400_BAD_REQUEST,
106             )
107
108         result = _follow_redirects(
109             link = serializer.data['link'],
110             remove_utm = serializer.data['remove_utm'],
111         )
112
113         return Response(
114             result,
115             status=status.HTTP_200_OK,
116         )
117
118 api_follow_redirects = FollowRedirectsViewSet.as_view({
119     'get': 'follow_redirects',
120 })