Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add config #4

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.default
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ REQUIREMENTS_FILE=dev
DJANGO_SECRET_KEY='12b06mtny_e^*(*7&3wy14i26jk=71azifld4+ky_wdsu%qx6m'
ALLOWED_HOSTS=localhost:127.0.0.1
INTERNAL_IPS=127.0.0.1:10.0.2.2

DATABASE_NAME=django_db
DATABASE_USER=django
DATABASE_PASSWORD=django
DATABASE_HOST=localhost
DATABASE_PORT=5432

CORS_ORIGIN_WHITELIST=

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_STORAGE_BUCKET_NAME=
AWS_S3_REGION_NAME=
USE_S3=FALSE
12 changes: 12 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[flake8]
exclude =
build,
.git,
.tox,
./django/conf/app_template/*,
./tests/.env,
./backend/users/migrations/*
ignore = W504, E501
max-line-length = 79
max-complexity = 10
select = B,C,E,F,W,T4,B9
18 changes: 18 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.1.0
hooks:
- id: flake8
language_version: python3.7
- repo: https://github.com/ambv/black
rev: 19.3b0
hooks:
- id: black
language_version: python3.7
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@

* VirtualBox (https://www.virtualbox.org/wiki/Downloads)
* Vagrant (https://www.vagrantup.com/downloads.html

## Installation

$ git clone https://github.com/z1digitalstudio/django-vagrant
$ cd django-vagrant
$ vagrant up --provision

## Development
### Create user
$ vagrant ssh
$ python manage.py createsuperuser
### Create database tables
$ python manage.py migrate
### Start up
### Start up
$ ./run.sh

## Access from the browser
Expand Down
44 changes: 44 additions & 0 deletions backend/base/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
import uuid

from django.db import models

from django.utils.translation import ugettext_lazy as _


class SimpleModel(models.Model):
"""
An abstract base class model that provides:
self-updating 'created' and 'modified' fields.
"""

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

created = models.DateTimeField(
verbose_name=_("created date"), null=True, auto_now_add=True
)
modified = models.DateTimeField(
verbose_name=_("modified date"), null=True, auto_now=True
)

class Meta:
abstract = True
ordering = ("-created",)


class NamedModel(SimpleModel):
"""
An abstract base class model that provides 'name' and
autogenerated 'slug' fields.
"""

name = models.CharField(
verbose_name=_("Name"), max_length=255, null=True, blank=True
)

class Meta:
abstract = True
ordering = ("name",)

def __str__(self):
return self.name
8 changes: 4 additions & 4 deletions backend/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
from celery import Celery


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
app = Celery('django-vagrant')
app.config_from_object('django.conf:settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
app = Celery("django-vagrant")
app.config_from_object("django.conf:settings")
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)


@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
print("Request: {0!r}".format(self.request))
5 changes: 3 additions & 2 deletions backend/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import os
import sys


def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
Expand All @@ -16,5 +17,5 @@ def main():
execute_from_command_line(sys.argv)


if __name__ == '__main__':
if __name__ == "__main__":
main()
Empty file.
Empty file added backend/media_upload/admin.py
Empty file.
5 changes: 5 additions & 0 deletions backend/media_upload/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class MediaUploadConfig(AppConfig):
name = "media_upload"
29 changes: 29 additions & 0 deletions backend/media_upload/backends/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import mimetypes

from django.http import Http404


class BaseMediaUploadBackend(object):
def get_presigned_url(self):
# Subclasses must implement this
raise NotImplementedError

def process_upload(self, *args, **kwargs):
# By default the upload is done outside our system
raise Http404

def __init__(self, request, *args, **kwargs):
self.request = request
self.args = args
self.kwargs = kwargs

def _get_filename(self):
return self.request.GET.get("filename", "data")

def _get_content_type(self):
filename = self._get_filename()
content_type = self.request.GET.get(
"contentType",
mimetypes.guess_type(filename)[0] or "application/octet-stream",
)
return content_type
70 changes: 70 additions & 0 deletions backend/media_upload/backends/local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import os
import datetime

from django.conf import settings
from django.core import signing
from django.core.signing import BadSignature
from django.core.files.storage import default_storage

from media_upload.backends.base import BaseMediaUploadBackend
from media_upload.models import UploadToken

from rest_framework.reverse import reverse
from rest_framework.response import Response


class LocalMediaUploadBackend(BaseMediaUploadBackend):
def get_presigned_url(self):
filename = self._get_filename()
mimetype = self._get_content_type()

name = default_storage.get_available_name(
self._full_path(filename, self.request.user)
)

timestamp = datetime.datetime.now().timestamp()
token = signing.dumps({"date": timestamp, "full_path": name})

UploadToken.objects.create(token=token)
result = reverse(
"upload_file", kwargs={"token": token, "filename": filename}
)

return {
"uploadUrl": self.request.build_absolute_uri(result),
"contentType": mimetype,
"retrieveUrl": self.request.build_absolute_uri(
default_storage.url(name)
),
}

def _full_path(self, filename, user):
if user and user.is_authenticated:
return os.path.join(str(user.id), filename)
return os.path.join("anon", filename)

def _invalid_token_response(self):
return Response("The token is invalid or has expired", status=401)

def process_upload(self):
try:
actual_date = datetime.datetime.now().timestamp()
limit_date = actual_date - int(settings.UPLOAD_TOKEN_EXPIRE_TIME)

token = UploadToken.objects.get(token=self.kwargs["token"])
token_dict = signing.loads(self.kwargs["token"])
token_date = token_dict.get("date")
full_path = token_dict.get("full_path")

is_valid = all([limit_date < token_date])

if is_valid:
uploaded_file = self.request.data["file"]
default_storage.save(full_path, uploaded_file, max_length=100)
token.delete()
else:
token.delete()
return self._invalid_token_response()
except (UploadToken.DoesNotExist, BadSignature):
return self._invalid_token_response()
return Response(None, status=200)
35 changes: 35 additions & 0 deletions backend/media_upload/backends/s3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import boto3
from botocore.exceptions import ClientError

from django.conf import settings
from media_upload.backends.base import BaseMediaUploadBackend


class S3MediaUploadBackend(BaseMediaUploadBackend):
def get_presigned_url(self):
s3_client = boto3.client(
"s3",
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
)

filename = self._get_filename()
mimetype = self._get_content_type()
try:
response = s3_client.generate_presigned_url(
"put_object",
Params={
"Bucket": settings.AWS_BUCKET_NAME,
"Key": filename,
"ContentType": mimetype,
},
ExpiresIn=int(settings.UPLOAD_TOKEN_EXPIRE_TIME),
HttpMethod="PUT",
)
except ClientError:
return
return {
"uploadUrl": response,
"contentType": mimetype,
"retrieveUrl": response.split("?")[0],
}
Empty file.
7 changes: 7 additions & 0 deletions backend/media_upload/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.db import models
from base.models import SimpleModel


class UploadToken(SimpleModel):

token = models.CharField(max_length=255, null=True)
33 changes: 33 additions & 0 deletions backend/media_upload/rest_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.conf import settings
from django.utils.module_loading import import_string

from rest_framework import views, permissions
from rest_framework.parsers import FileUploadParser
from rest_framework.response import Response


class MediaUploadBackendMixin(object):
def get_backend(self, request, *args, **kwargs):
backend_class = import_string(settings.MEDIA_UPLOAD_BACKEND)
return backend_class(request, *args, **kwargs)


class GetFilesView(MediaUploadBackendMixin, views.APIView):
permission_classes = (permissions.IsAuthenticated,)
permission_classes = (permissions.AllowAny,)

def get(self, request, *args, **kwargs):
presigned_url_data = self.get_backend(
request, *args, **kwargs
).get_presigned_url()
if presigned_url_data is None:
return Response({"error": "Invalid data"}, status=400)
return Response(presigned_url_data)


class UploadFileView(MediaUploadBackendMixin, views.APIView):
permission_classes = (permissions.AllowAny,)
parser_classes = (FileUploadParser,)

def put(self, request, *args, **kwargs):
return self.get_backend(request, *args, **kwargs).process_upload()
Empty file added backend/media_upload/tests.py
Empty file.
11 changes: 11 additions & 0 deletions backend/media_upload/urls/files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.urls import path
from media_upload.rest_views import GetFilesView, UploadFileView

urlpatterns = [
path("signed_url/", GetFilesView.as_view(), name="get_files"),
path(
"upload_file/<str:filename>/<str:token>/",
UploadFileView.as_view(),
name="upload_file",
),
]
Loading