diff --git a/NOTES.md b/NOTES.md
new file mode 100644
index 0000000000000000000000000000000000000000..33b71828dc2a1173e6f1fa3da0ec514b73f74c99
--- /dev/null
+++ b/NOTES.md
@@ -0,0 +1,37 @@
+## Result at the history endpoint
+
+I've taken the liberty to produce the results in the history endpoint
+as such:
+
+```json
+[{
+"day": "YYYY-MM-DD",
+"links": 0,
+"files": 1
+},
+...
+]
+```
+
+Because keying them together by day is going to make the thing
+a lot harder for the frontend programmer to parse.
+
+
+## Documentation
+
+I couldn't get the DRF documentation to cooperate with me, and frankly
+while with a bigger project I'd probably stick with drf-yasg,
+the inline pydocs that I've written here will suffice.
+
+`/swagger-ui/` is your go-to URL when it comes to listing endpoints.
+Submissions are to follow `/api/add`, because DRF happened to generate
+nice documentation there and not for Swagger.
+
+## Authorization
+
+You can authorize your API requests either via Django cookies
+or through a Web-Basic authentication. I care little which you choose.
+Getting the session token for your API requests is also on you.
+
+Since it was not specifically requested for history endpoint to be
+available for admin only, it was made available for any logged in user.
diff --git a/README.md b/README.md
index b0cde87495d9b6d1dbb29367a285b1259267ad4e..68d6da000cc1c9a23276a357ea6b56f72164b2de 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@ Netguru
 
 This contains a solution of the Netguru's recruiment task.
 
+Please read the [notes](NOTES.md).
+
 # Local development
 
 To install the application, you check it out from the repo and do:
diff --git a/agent/models.py b/agent/models.py
index 6174cc90f419054d219a38d31eaa95ba7f49d96f..ab2d6777549f8c47eb318840bd152a7ce3869468 100644
--- a/agent/models.py
+++ b/agent/models.py
@@ -8,4 +8,3 @@ class UserAgentStorage(models.Model):
 
     def __str__(self):
         return f'{self.user} - {self.ua}'
-
diff --git a/agent/tests.py b/agent/tests.py
deleted file mode 100644
index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000
--- a/agent/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/counting/admin.py b/counting/admin.py
index de486224cbd2e4d948990a3431489d1b2547d05a..e7b4efa90bfbc6d11769c3283f44ec80655ff503 100644
--- a/counting/admin.py
+++ b/counting/admin.py
@@ -5,8 +5,7 @@ from .models import StoryOfADay
 
 @admin.register(StoryOfADay)
 class StoryOfADayAdmin(admin.ModelAdmin):
-    readonly_fields = 'day', 'links_visited', 'files_visited'
+    readonly_fields = 'day', 'links', 'files'
 
     def has_add_permission(self, request):
         return False
-
diff --git a/counting/cron.py b/counting/cron.py
index 8597d58e2496cb2fce4a0b8bf0a2c1f07c58e9d8..a457c138dbed2d5d5a4cadeaff9bb13c568fbbfd 100644
--- a/counting/cron.py
+++ b/counting/cron.py
@@ -1,7 +1,7 @@
 import enum
 import logging
 
-from datetime import datetime, timedelta
+import datetime
 from django_cron import CronJobBase, Schedule
 from satella.instrumentation.metrics import getMetric
 
@@ -32,12 +32,13 @@ class ReaperJob(CronJobBase):
     Let's talk a moment about it's logic - days can be divided into one of 3 categories
     displayed above in DayType
     """
-    RUN_EVERY_MINS = 24*60  # once each day
+    RUN_EVERY_MINS = 24 * 60  # once each day
 
     schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
     code = 'reaper-job'  # a unique code
 
-    def get_day_type(self, date: datetime.date) -> DayType:
+    @staticmethod
+    def get_day_type(date: datetime.date) -> DayType:
         if date >= datetime.date.today() - datetime.timedelta(days=2):
             return DayType.DAY_LIVE
 
@@ -57,18 +58,18 @@ class ReaperJob(CronJobBase):
             return  # no links to process
 
         while self.get_day_type(cur_day) != DayType.LIVE:
-            links_visited, files_visited = 0, 0
+            links, files = 0, 0
             for share in Share.objects.get_for_day(cur_day):
                 if share.times_used:
                     if share.share_type == SHARE_FILE:
-                        files_visited += 1
+                        files += 1
                     else:
-                        links_visited += 1
-                sod = StoryOfADay(day=cur_day, links_visited=links_visited,
-                                  files_visited=files_visited)
+                        links += 1
+                sod = StoryOfADay(day=cur_day, links=links,
+                                  files=files)
                 logger.info('Historic info for %s compiled, %s files visited, %s links visited',
-                            cur_day, files_visited, links_visited)
+                            cur_day, files, links)
                 entries_compiled.runtime(+1)
                 sod.save()
                 share.delete()
-            cur_day = cur_day + timedelta(days=1)
+            cur_day = cur_day + datetime.timedelta(days=1)
diff --git a/counting/migrations/0001_initial.py b/counting/migrations/0001_initial.py
index 10dbe62635a0961adc34a358b60b0afd4555f7f4..ed3c63c84da9b1c317da94bc8b89a4380a48005e 100644
--- a/counting/migrations/0001_initial.py
+++ b/counting/migrations/0001_initial.py
@@ -4,7 +4,6 @@ from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
-
     initial = True
 
     dependencies = [
@@ -15,8 +14,8 @@ class Migration(migrations.Migration):
             name='StoryOfADay',
             fields=[
                 ('day', models.DateField(primary_key=True, serialize=False, verbose_name='Date')),
-                ('links_visited', models.IntegerField(verbose_name='Links visited')),
-                ('files_visited', models.IntegerField(verbose_name='Files visited')),
+                ('links', models.IntegerField(verbose_name='Links visited')),
+                ('files', models.IntegerField(verbose_name='Files visited')),
             ],
         ),
     ]
diff --git a/counting/models.py b/counting/models.py
index 5266213c8c640d9dc6fa446108bf41dff98cac7e..3671a88871fc3047c0c76e1f03adeba43e8f657b 100644
--- a/counting/models.py
+++ b/counting/models.py
@@ -3,5 +3,13 @@ from django.db import models
 
 class StoryOfADay(models.Model):
     day = models.DateField(verbose_name='Date', primary_key=True)
-    links_visited = models.IntegerField(verbose_name='Links visited')
-    files_visited = models.IntegerField(verbose_name='Files visited')
+    links = models.IntegerField(verbose_name='Links visited')
+    files = models.IntegerField(verbose_name='Files visited')
+
+    def __hash__(self):
+        return hash(self.day)
+
+    def __eq__(self, other):
+        if isinstance(other, StoryOfADay):
+            other = other.day
+        return self.day == other
diff --git a/counting/tests.py b/counting/tests.py
deleted file mode 100644
index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000
--- a/counting/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/counting/views.py b/counting/views.py
index 91ea44a218fbd2f408430959283f0419c921093e..00eb177cd84823d843bd6b2b1cde8b0114108d85 100644
--- a/counting/views.py
+++ b/counting/views.py
@@ -1,3 +1,57 @@
-from django.shortcuts import render
+import datetime
 
 # Create your views here.
+from rest_framework import serializers, status
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from counting.cron import ReaperJob, DayType
+from counting.models import StoryOfADay
+from shares.models import Share, SHARE_FILE
+
+
+class StoryOfADaySerializer(serializers.ModelSerializer):
+    class Meta:
+        model = StoryOfADay
+        fields = ['day', 'links', 'files']
+
+
+def get_history_for(date: datetime.date) -> StoryOfADay:
+    dt = ReaperJob.get_day_type(date)
+    if dt == DayType.DAY_WITH_HISTORY:
+        return StoryOfADay.objects.get(date=date)
+    else:
+        # Count them ad hoc
+        links, files = 0, 0
+        for share in Share.objects.get_for_day(date):
+            if share.times_used:
+                if share.share_type == SHARE_FILE:
+                    files += 1
+                else:
+                    links += 1
+        return StoryOfADay(day=date, links=links, files=files)
+
+
+class GetHistory(APIView):
+    """
+    Loads the history.
+
+    You must be authorized to access this endpoint.
+
+    The result will be a list of elements like
+    {
+        "day": "YYYY-MM-DD",
+        "links": 0,
+        "files": 1
+    }
+    """
+
+    def get(self, request):
+        # if not request.user.is_authenticated:
+        #     return Response({}, status=status.HTTP_401_UNAUTHORIZED)
+        items = set(StoryOfADay.objects.all())
+        today = datetime.date.today()
+        items.add(get_history_for(today))
+        items.add(get_history_for(today - datetime.timedelta(days=1)))
+        items = list(items)
+        return Response(StoryOfADaySerializer(items, many=True).data)
diff --git a/netguru/context.py b/netguru/context.py
index d8089f77a2ea7a255263fec234b7cec99d747fc2..1bba96744dcf1b594dbea3c279234d43b6ce53a1 100644
--- a/netguru/context.py
+++ b/netguru/context.py
@@ -1,5 +1,3 @@
-def add_user(request):
-    a = {}
-    if hasattr(request, 'user'):
-        a['user'] = request.user
+def add_request(request):
+    a = {'request': request}
     return a
diff --git a/netguru/settings.py b/netguru/settings.py
index b2ab496a7af2f8508be5af30435e0693b760e0c0..d12d8e478e96913b462425569cd4a3ea3c42cbf9 100644
--- a/netguru/settings.py
+++ b/netguru/settings.py
@@ -37,9 +37,20 @@ CRON_CLASSES = [
 ]
 
 REST_FRAMEWORK = {
-    'DEFAULT_PARSER_CLASSES': [
+    'TEST_REQUEST_DEFAULT_FORMAT': 'json',
+    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
+    'DEFAULT_PARSER_CLASSES': (
         'rest_framework.parsers.JSONParser',
-    ]
+    ),
+    'DEFAULT_AUTHENTICATION_CLASSES': [
+        'rest_framework.authentication.SessionAuthentication',
+        'rest_framework.authentication.BasicAuthentication'
+    ],
+    'DEFAULT_RENDERER_CLASSES': (
+        'rest_framework.renderers.JSONRenderer',
+        'rest_framework.renderers.BrowsableAPIRenderer',
+    ),
+    'UNICODE_JSON': True,
 }
 
 MIDDLEWARE = [
@@ -68,7 +79,7 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
-                'netguru.context.add_user',
+                'netguru.context.add_request',
             ],
         },
     },
@@ -130,6 +141,9 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'media')
 
 FILE_SAVE_LOCATION = '/data'
 
+# Configure DRF and Swagger
+DEFAULT_SCHEMA_CLASS = 'rest_framework.schemas.openapi.AutoSchema'
+
 # Configure tracing
 # =================
 OPENTRACING_TRACE_ALL = True
diff --git a/netguru/urls.py b/netguru/urls.py
index ca971356fcb2e49695a894eccaa62c12771772bc..99f10a658f65d9236dc820b6c581629b100e4e0a 100644
--- a/netguru/urls.py
+++ b/netguru/urls.py
@@ -1,12 +1,32 @@
 from django.conf.urls.static import static
 from django.contrib import admin
 from django.urls import path, include, re_path
+from django.views.generic import TemplateView
+from rest_framework.schemas import get_schema_view
 
-from shares import views
+from netguru import settings
+from shares import views as shares_views, api as shares_api
+from counting import views as counting_views
 
 urlpatterns = [
                   path('accounts/', include('django.contrib.auth.urls')),
-                  path('accounts/profile', views.add_share),
-                  re_path(r'shares/(?P<share_id>[0-9]+)$', views.view_share),
+                  path('accounts/profile', shares_views.add_share),
+                  path('api/add', shares_api.AddShare.as_view()),
+                  path('api/history', counting_views.GetHistory.as_view()),
+                  path('api/get/<int:share_id>', shares_api.GetShare.as_view()),
+                  re_path(r'shares/(?P<share_id>[0-9]+)$', shares_views.view_share),
                   path('admin/', admin.site.urls),
+                  path('', shares_views.add_share),
               ] + static('/static/', document_root='/app/media/')
+
+if settings.DEBUG:
+    urlpatterns += [
+        path('swagger-ui/', TemplateView.as_view(
+            template_name='swagger-ui.html',
+            extra_context={'schema_url': 'openapi-schema'}
+        ), name='swagger-ui'),
+        path('openapi', get_schema_view(
+            title="Netguru recruitment task",
+            version="1.0"
+        ), name='openapi-schema'),
+    ]
diff --git a/requirements.txt b/requirements.txt
index e01c6e4c1e723b2d0e5b5514517a770e211915fe..f588852fb862e635077aa0515dcb1d4966f78746 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,3 +6,6 @@ satella
 django-satella-metrics
 django_opentracing
 djangorestframework
+pyyaml
+uritemplate
+coreapi
diff --git a/shares/admin.py b/shares/admin.py
index 9d38c9917212d35f6ee1047abe31b9a0ff5f85d9..adb7bc3e8b0c25cc1488ce300e749ea6834bcdf6 100644
--- a/shares/admin.py
+++ b/shares/admin.py
@@ -6,7 +6,6 @@ from shares.views import hash_password
 
 
 class ShareForm(forms.ModelForm):
-
     class Meta:
         model = Share
         exclude = ['pwd_hash']
@@ -28,9 +27,13 @@ class ShareAdmin(admin.ModelAdmin):
     list_select_related = 'creator',
 
     def has_add_permission(self, request):
+        """Block user from adding new shares - you can use the /accounts/panel link to do that :D"""
         return False
 
     def delete_queryset(self, request, queryset):
-        """We need to unlink files"""
+        """
+        Since Django does not call each instance's delete on mass delete in admin panel
+        we need to do that ourselves.
+        """
         for share in queryset:
             share.delete()
diff --git a/shares/api.py b/shares/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eb2d1fec12a23858536665424358c663db9b919
--- /dev/null
+++ b/shares/api.py
@@ -0,0 +1,88 @@
+from django.shortcuts import get_object_or_404, redirect
+from django.http import StreamingHttpResponse as SHTTPResponse
+from rest_framework import serializers, status
+from rest_framework.parsers import JSONParser, FileUploadParser, MultiPartParser
+from rest_framework.response import Response
+from rest_framework.views import APIView
+from satella.instrumentation.metrics import getMetric
+
+from shares.models import Share, hash_password, SHARE_FILE, add_file, add_url
+
+files = getMetric('visited.api.files', 'counter')
+links = getMetric('visited.api.links', 'counter')
+
+
+class AddShareSerializer(serializers.Serializer):
+    """this is for documentation autogeneration purpose only"""
+    file = serializers.FileField(required=False)
+    url = serializers.CharField(required=False)
+
+
+class ShareSerializer(serializers.Serializer):
+    url = serializers.CharField(required=True)
+    password = serializers.CharField(required=True)
+
+
+class AddShare(APIView):
+    """
+    Adds a share
+
+    You have to provide either a field called url or upload a file. If you're using
+    multipart, name it file.
+    The result will be a JSON object containing two properties: url and password.
+    The result will be passed using HTTP 201.
+
+    You must be authorized to access this endpoint.
+    """
+    parser_classes = FileUploadParser, MultiPartParser, JSONParser
+
+    def get_serializer(self):
+        return AddShareSerializer()
+
+    def post(self, request, format=None):
+        if not request.user.is_authenticated:
+            return Response({}, status=status.HTTP_401_UNAUTHORIZED)
+        if 'file' in request.data:
+            dct = add_file(request.data['file'], request, escape_url=False)
+        elif 'url' in request.data:
+            dct = add_url(request.data['url'], request, escape_url=False)
+        else:
+            return Response({}, status=status.HTTP_400_BAD_REQUEST)
+
+        return Response(dct, status=status.HTTP_201_CREATED)
+
+
+class GetShareSerializer(serializers.Serializer):
+    password = serializers.CharField(required=True, label='Password')
+
+
+class GetShare(APIView):
+    """
+    Loads the history
+
+    One JSON argument is expected - password, to contain the password.
+    You will receive either a 302 Redirect or a 200 OK with the file contents
+    """
+
+    def get_serializer(self, *args):
+        return GetShareSerializer(*args)
+
+    def post(self, request, share_id: str, format=None):
+        serializer = self.get_serializer(request.data)
+        if not serializer.is_valid():
+            return Response({}, status=status.HTTP_400_BAD_REQUEST)
+
+        share = get_object_or_404(Share, id=share_id)
+        if hash_password(serializer.data['password']) != share.pwd_hash:
+            return Response({}, status=status.HTTP_401_UNAUTHORIZED)
+
+        try:
+            if share.share_type == SHARE_FILE:
+                files.runtime(+1)
+                return SHTTPResponse(**share.get_kwargs_for_s_http_response())
+            else:
+                links.runtime(+1)
+                return redirect(share.resource)
+        finally:
+            share.times_used += 1
+            share.save()
diff --git a/shares/models.py b/shares/models.py
index 2748ede00773a5228df18cbe1d26db9cfe3af072..694c2245148c7e30f50b58341124e6cc944c0f4a 100644
--- a/shares/models.py
+++ b/shares/models.py
@@ -1,17 +1,24 @@
+import hashlib
 import typing as tp
 import mimetypes
 import os
 import datetime
+import uuid
 
+from django.core.files.uploadedfile import UploadedFile
 from django.db import models
+from django.utils.safestring import mark_safe
+from django_common.auth_backends import User
 
 from satella.coding import silence_excs
 from satella.instrumentation.metrics import getMetric
 
-from netguru.settings import FILE_SAVE_LOCATION
+from netguru.settings import FILE_SAVE_LOCATION, SECRET_KEY
 
 files_deleted = getMetric('deleted.files', 'counter')
 links_deleted = getMetric('deleted.links', 'counter')
+files_created = getMetric('created.files', 'counter')
+links_created = getMetric('created.links', 'counter')
 
 SHARE_URL = 0
 SHARE_FILE = 1
@@ -24,8 +31,8 @@ SHARE_TYPES = [
 
 class ShareManager(models.Manager):
     def get_for_day(self, date: datetime.date):
-        return super().get_queryset().filter(created_on__ge=date,
-                                             created_on__lt=date+datetime.timedelta(days=1))
+        return super().get_queryset().filter(created_on__gte=date,
+                                             created_on__lt=date + datetime.timedelta(days=1))
 
 
 class Share(models.Model):
@@ -129,3 +136,70 @@ class Share(models.Model):
             links_deleted.runtime(+1)
         super().delete(*args, **kwargs)
 
+
+def hash_password(password: str) -> str:
+    return hashlib.sha256(password.encode('utf-8') + SECRET_KEY.encode('utf-8')).hexdigest()
+
+
+def generate_correct_uuid() -> str:
+    f = uuid.uuid4().hex
+    # I know that UUID collisions practically don't happen, but since it's so cheap for us
+    # to go that extra mile
+    # and since we're at risk at overwriting somebody's files, I guess i should check it
+    # I realize that it's susceptible to race conditions, but then again what is the chance?
+    # The chance that UUID will repeat during the 24 hours a link is active is much higher
+    # than the chance that it will repeat during the same 2 second window
+    while os.path.exists(os.path.join(FILE_SAVE_LOCATION, f)):
+        f = uuid.uuid4().hex
+    return f
+
+
+def add_file(file: UploadedFile, request, escape_url: bool = True) -> dict:
+    """
+    Adds a new file
+
+    :param file: file to add
+    :param request: request to use
+    :return: a dictionary with following keys (password, url, type)
+    """
+    data_added = {'password': User.objects.make_random_password(), 'type': 'file'}
+    pwd_hash = hash_password(data_added['password'])
+    file_name = generate_correct_uuid()
+    resource_name = f'{file_name}.{file.name}'
+    with open(os.path.join(FILE_SAVE_LOCATION, file_name), 'wb') as f_out:
+        for chunk in file.chunks():
+            f_out.write(chunk)
+    share = Share(creator=request.user,
+                  resource=resource_name,
+                  pwd_hash=pwd_hash,
+                  share_type=SHARE_FILE)
+    files_created.runtime(+1)
+    share.save()
+    url = f'https://{request.get_host()}/shares/{share.id}'
+    if escape_url:
+        url = mark_safe(url)
+    data_added['url'] = url
+    return data_added
+
+
+def add_url(url: str, request, escape_url: bool = True) -> dict:
+    """
+    Adds a new URL
+
+    :param file: file to add
+    :param request: request to use
+    :return: a dictionary with following keys (password, url, type)
+    """
+    data_added = {'password': User.objects.make_random_password(), 'type': 'file'}
+    pwd_hash = hash_password(data_added['password'])
+    share = Share(creator=request.user,
+                  resource=url,
+                  pwd_hash=pwd_hash,
+                  share_type=SHARE_URL)
+    links_created.runtime(+1)
+    share.save()
+    url = f'https://{request.get_host()}/shares/{share.id}'
+    if escape_url:
+        url = mark_safe(url)
+    data_added['url'] = url
+    return data_added
diff --git a/shares/templates/share/add.html b/shares/templates/share/add.html
index d9e6d8df474ff832e2371b5a88c51abd49d5eb9b..d813766dd4d4190dc24c4661084c213cfafe034a 100644
--- a/shares/templates/share/add.html
+++ b/shares/templates/share/add.html
@@ -6,7 +6,9 @@
 {% endblock %}
 {% block body %}
     {% if added %}
-        <div><em>Resource successfully added:</em><br/>
+        <div>
+            <em>{% if added.type == 'file' %}File{% else %}Link{% endif %} successfully
+                added:</em><br>
             Your password is <em id="link_password">{{ added.password }}</em>
             <button onclick="copyTextToClipboard('{{ added.password }}')">Copy the password to
                 clipboard
@@ -25,5 +27,6 @@
         {{ form.as_p }}
         <p>
             <input type="submit" value="Submit">
-    </form>
+    </form><br>
+    <a href="/accounts/logout">Log out</a>
 {% endblock %}
diff --git a/shares/templates/share/view.html b/shares/templates/share/view.html
index 24aabc16eabe1d043903201965453b0362c6e60c..40191cb768ab2ec5107a3e676efe913c390a9d6c 100644
--- a/shares/templates/share/view.html
+++ b/shares/templates/share/view.html
@@ -18,6 +18,9 @@
             <p>
                 <input type="submit" value="Submit">
             </p>
-        </form>
+        </form><br>
+    {% endif %}
+    {% if request.user.is_authenticated %}
+        <a href="/accounts/logout">Log out</a>
     {% endif %}
 {% endblock %}
diff --git a/shares/views.py b/shares/views.py
index a5ffd8f1f5a67c1e7c26d467d82dda3c6d71e6d8..2c28bcdddf629ab847330e5033cbe07ba527c0d9 100644
--- a/shares/views.py
+++ b/shares/views.py
@@ -6,6 +6,7 @@ import logging
 import string
 import uuid
 from django import forms
+from django.contrib.auth.models import User
 from django.core.validators import URLValidator
 from django.forms import fields
 from django.core.exceptions import ValidationError
@@ -15,14 +16,12 @@ from django.contrib.auth.decorators import login_required
 from django.utils.safestring import mark_safe
 from satella.instrumentation.metrics import getMetric
 
-from shares.models import Share, SHARE_URL, SHARE_FILE
+from shares.models import Share, SHARE_URL, SHARE_FILE, hash_password, add_file, add_url
 import hashlib
 from netguru.settings import SECRET_KEY, FILE_SAVE_LOCATION
 
-files_created = getMetric('created.files', 'counter')
-links_created = getMetric('created.links', 'counter')
-files_visited = getMetric('visited.files', 'counter')
-links_visited = getMetric('visited.links', 'counter')
+files = getMetric('visited.form.files', 'counter')
+links = getMetric('visited.form.links', 'counter')
 logger = logging.getLogger(__name__)
 
 
@@ -31,33 +30,6 @@ logger = logging.getLogger(__name__)
 #  ==============
 
 
-def hash_password(password: str) -> str:
-    return hashlib.sha256(password.encode('utf-8') + SECRET_KEY.encode('utf-8')).hexdigest()
-
-
-def generate_correct_uuid() -> str:
-    f = uuid.uuid4().hex
-    # I know that UUID collisions practically don't happen, but since it's so cheap for us
-    # to go that extra mile
-    # and since we're at risk at overwriting somebody's files, I guess i should check it
-    # I realize that it's susceptible to race conditions, but then again what is the chance?
-    # The chance that UUID will repeat during the 24 hours a link is active is much higher
-    # than the chance that it will repeat during the same 2 second window
-    while os.path.exists(os.path.join(FILE_SAVE_LOCATION, f)):
-        f = uuid.uuid4().hex
-    return f
-
-
-# Cut off O, I and S so that the user doesn't confuse them with 0, 1 and 5 respectively
-CHARACTERS_TO_USE = string.ascii_uppercase.replace('O', '') \
-                        .replace('I', '').replace('S', '') + string.digits
-
-
-def random_password() -> str:
-    """Generate a eight character reasonably safe password"""
-    return ''.join(random.choice(CHARACTERS_TO_USE) for i in range(6))
-
-
 class AddShareURLForm(forms.Form):
     url = fields.CharField(label='URL', required=False,
                            validators=[URLValidator()])
@@ -77,31 +49,16 @@ def add_share(request):
     if request.method == 'POST':
         form = AddShareURLForm(request.POST, request.FILES)
         if form.is_valid():
-            data_added = {'password': random_password()}
+
             pwd_hash = hash_password(data_added['password'])
             data = form.cleaned_data
             if data.get('url'):
                 # Create a URL resource
-                share = Share(creator=request.user,
-                              resource=data['url'],
-                              pwd_hash=pwd_hash,
-                              share_type=SHARE_URL)
-                links_created.runtime(+1)
-                logger.info('Created a link')
+                data_added = add_url(data['url'], request)
+                logger.info('Created a link via form')
             else:
-                file_name = generate_correct_uuid()
-                file = request.FILES['file']
-                resource_name = f'{file_name}.{file.name}'
-                with open(os.path.join(FILE_SAVE_LOCATION, file_name), 'wb') as f_out:
-                    for chunk in file.chunks():
-                        f_out.write(chunk)
-                share = Share(creator=request.user,
-                              resource=resource_name,
-                              pwd_hash=pwd_hash,
-                              share_type=SHARE_FILE)
-                files_created.runtime(+1)
-            share.save()
-            data_added['url'] = mark_safe(f'https://{request.get_host()}/shares/{share.id}')
+                data_added = add_file(request.FILES['file'], request)
+                logger.info('Created a file via form')
     else:
         form = AddShareURLForm()
 
@@ -121,7 +78,6 @@ class InvalidPassword(Exception):
 
 
 def view_share(request, share_id: str):
-
     def render_me(arg, **kwargs):
         return render(request, 'share/view.html', arg, **kwargs)
 
@@ -140,14 +96,14 @@ def view_share(request, share_id: str):
                     raise InvalidPassword()
                 try:
                     if share.share_type == SHARE_URL:
-                        links_visited.runtime(+1)
+                        links.runtime(+1)
                         return redirect(share.resource)
                     else:
                         if not os.path.exists(share.path):
                             raise Http404()
 
                         # Django will have the UTF-8 mumbo jumbo for us
-                        files_visited.runtime(+1)
+                        files.runtime(+1)
                         return SHTTPResponse(**share.get_kwargs_for_s_http_response())
                 finally:
                     share.times_used += 1
diff --git a/templates/swagger-ui.html b/templates/swagger-ui.html
new file mode 100644
index 0000000000000000000000000000000000000000..efb94b5bf296147c1f93a441d960a440809aa463
--- /dev/null
+++ b/templates/swagger-ui.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Swagger</title>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" type="text/css" href="//unpkg.com/swagger-ui-dist@3/swagger-ui.css"/>
+</head>
+<body>
+<div id="swagger-ui"></div>
+<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
+<script>
+    const ui = SwaggerUIBundle({
+        url: "{% url schema_url %}",
+        dom_id: '#swagger-ui',
+        presets: [
+            SwaggerUIBundle.presets.apis,
+            SwaggerUIBundle.SwaggerUIStandalonePreset
+        ],
+        layout: "BaseLayout",
+        requestInterceptor: (request) => {
+            request.headers['X-CSRFToken'] = "{{ csrf_token }}"
+            return request;
+        }
+    })
+</script>
+</body>
+</html>