Skip to content

Commit 141ab38

Browse files
yakserVeryBigSad
andcommitted
Refactor FileView
Co-authored-by: VeryBigSad <VeryBigSad@users.noreply.github.com>
1 parent 9dc1cc0 commit 141ab38

4 files changed

Lines changed: 94 additions & 89 deletions

File tree

files/helpers.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import requests
2+
import time
3+
from files.exceptions import SelectelUploadError
4+
5+
from procollab.settings import (
6+
DEBUG,
7+
SELECTEL_ACCOUNT_ID,
8+
SELECTEL_CONTAINER_NAME,
9+
SELECTEL_CONTAINER_PASSWORD,
10+
SELECTEL_CONTAINER_USERNAME,
11+
)
12+
13+
14+
class FileAPI:
15+
def __init__(self, file, user) -> None:
16+
self.file = file
17+
self.user = user
18+
19+
@staticmethod
20+
def delete(url: str) -> int:
21+
"""Deletes file from selcdn"""
22+
token = FileAPI._get_selectel_swift_token()
23+
response = requests.delete(url, headers={"X-Auth-Token": token})
24+
return response.status_code
25+
26+
def upload(self):
27+
return self._upload_via_selectel_swift()
28+
29+
def _upload_via_selectel_swift(self) -> tuple[int, str]:
30+
token = self._get_selectel_swift_token()
31+
url = self._generate_selectel_swift_file_url()
32+
33+
with self.file.open(mode="rb") as file_object:
34+
response = requests.put(
35+
url,
36+
headers={"X-Auth-Token": token, "Content-Type": file_object.content_type},
37+
data=file_object.read(),
38+
)
39+
40+
return response.status_code, url
41+
42+
def _generate_selectel_swift_link(sefl):
43+
link = f"https://api.selcdn.ru/v1/SEL_{SELECTEL_ACCOUNT_ID}/{SELECTEL_CONTAINER_NAME}/"
44+
if DEBUG:
45+
link += "debug/"
46+
return link
47+
48+
@staticmethod
49+
def _get_selectel_swift_token():
50+
"""Returns auth token for selcdn"""
51+
data = {
52+
"auth": {
53+
"identity": {
54+
"methods": ["password"],
55+
"password": {
56+
"user": {
57+
"id": SELECTEL_CONTAINER_USERNAME,
58+
"password": SELECTEL_CONTAINER_PASSWORD,
59+
}
60+
},
61+
}
62+
}
63+
}
64+
r = requests.post("https://api.selcdn.ru/v3/auth/tokens", json=data)
65+
if r.status_code not in [200, 201]:
66+
raise SelectelUploadError("Couldn't generate a token for selcdn")
67+
return r.headers["x-subject-token"]
68+
69+
def _get_file_extension(self) -> str:
70+
if len(self.file.name.split(".")) > 1:
71+
return "." + self.file.name.split(".")[1]
72+
return ""
73+
74+
def _generate_selectel_swift_file_url(self) -> str:
75+
"""
76+
Generates url for selcdn
77+
78+
Returns:
79+
url: str looks like /hashedEmail/hashedFilename_hashedTime.extension
80+
"""
81+
link = self._generate_selectel_swift_link()
82+
extension = self._get_file_extension()
83+
return (
84+
link
85+
+ f"{abs(hash(self.user.email))}/{abs(hash(self.file.name))}_{abs(hash(time.time()))}{extension}"
86+
)

files/models.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from django.db import models
21
from django.contrib.auth import get_user_model
2+
from django.db import models
33

44
User = get_user_model()
55

@@ -18,9 +18,5 @@ class UserFile(models.Model):
1818
link = models.URLField(primary_key=True, null=False)
1919
datetime_uploaded = models.DateTimeField(auto_now_add=True)
2020

21-
def delete(self, using=None, keep_parents=False):
22-
# TODO: add request to CDN to delete the object
23-
super(UserFile, self).delete(using=using, keep_parents=keep_parents)
24-
2521
def __str__(self):
2622
return f"UserFile by {self.user}, {self.link}"

files/urls.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from django.urls import path
22

3-
43
from files.views import FileView
54

65
app_name = "industries"
76

87
urlpatterns = [
98
path("", FileView.as_view()),
10-
path("<int:pk>", FileView.as_view()),
119
]

files/views.py

Lines changed: 7 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,22 @@
1-
import time
2-
3-
import requests
41
from django.db import transaction
52
from rest_framework import permissions, status
63
from rest_framework.response import Response
74
from rest_framework.views import APIView
85

9-
from files.exceptions import SelectelUploadError
6+
from files.helpers import FileAPI
107
from files.models import UserFile
11-
from procollab.settings import (
12-
DEBUG,
13-
SELECTEL_ACCOUNT_ID,
14-
SELECTEL_CONTAINER_NAME,
15-
SELECTEL_CONTAINER_PASSWORD,
16-
SELECTEL_CONTAINER_USERNAME,
17-
)
188

199

2010
class FileView(APIView):
2111
permission_classes = [permissions.AllowAny]
2212

2313
@transaction.atomic
2414
def post(self, request):
25-
if DEBUG is True:
26-
return Response(
27-
{"message": "Files doesn't save in development mode, sorry <3"},
28-
status=status.HTTP_406_NOT_ACCEPTABLE,
29-
)
30-
31-
file = request.FILES["file"]
32-
link = f"https://api.selcdn.ru/v1/SEL_{SELECTEL_ACCOUNT_ID}/{SELECTEL_CONTAINER_NAME}/"
33-
user = request.user
34-
token = self._get_token()
35-
36-
if len(file.name.split(".")) > 1:
37-
extension = file.name.split(".")[1]
38-
else:
39-
extension = ""
40-
41-
# looks like /hashedEmail/hashedFilename_hashedTime.extension
42-
url = (
43-
link + f"{SELECTEL_CONTAINER_NAME}/{abs(hash(user.email))}/"
44-
f"{abs(hash(file.name))}_{abs(hash(time.time()))}{'.' + extension if extension else ''}"
45-
)
46-
with file.open(mode="rb") as file_object:
47-
r = requests.put(
48-
url,
49-
headers={"X-Auth-Token": token, "Content-Type": file_object.content_type},
50-
data=file_object.read(),
51-
)
52-
if r.status_code != 201:
53-
return Response("Failed to upload file", status=status.HTTP_409_CONFLICT)
54-
self._save_to_db(user, url)
55-
return Response({"url": url}, status=status.HTTP_201_CREATED)
56-
57-
def delete(self, request, pk):
58-
try:
59-
UserFile.objects.get(pk=pk).delete()
60-
except UserFile.DoesNotExist:
61-
return Response(status=status.HTTP_404_NOT_FOUND)
62-
return Response(status=status.HTTP_200_OK)
15+
file_api = FileAPI(request.FILES["file"], request.user)
16+
status_code, url = file_api.upload()
6317

64-
@classmethod
65-
def _save_to_db(cls, user, url):
66-
"""creates userfile object for file uploads"""
67-
return UserFile.objects.create(user=user, link=url)
18+
if status_code == 201:
19+
UserFile.objects.create(user=request.user, link=url)
20+
return Response({"url": url}, status=status.HTTP_201_CREATED)
6821

69-
@classmethod
70-
def _get_token(cls):
71-
"""returns auth token for sentry"""
72-
data = {
73-
"auth": {
74-
"identity": {
75-
"methods": ["password"],
76-
"password": {
77-
"user": {
78-
"id": SELECTEL_CONTAINER_USERNAME,
79-
"password": SELECTEL_CONTAINER_PASSWORD,
80-
}
81-
},
82-
}
83-
}
84-
}
85-
r = requests.post("https://api.selcdn.ru/v3/auth/tokens", json=data)
86-
if r.status_code not in [200, 201]:
87-
raise SelectelUploadError("couldn't generate a token for selcdn")
88-
return r.headers["x-subject-token"]
89-
# async with server.post(
90-
# "https://api.selcdn.ru/v3/auth/tokens",
91-
# data=json.dumps(data),
92-
# ) as response:
93-
# if response.status != 201:
94-
# return Response(
95-
# "Failed to get token", status_code=status.HTTP_409_CONFLICT
96-
# )
97-
# return response.json()
22+
return Response("Failed to upload file", status=status.HTTP_409_CONFLICT)

0 commit comments

Comments
 (0)