-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathservice.py
More file actions
148 lines (124 loc) · 4.32 KB
/
service.py
File metadata and controls
148 lines (124 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import time
from abc import ABC, abstractmethod
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
from requests import Response
from files.constants import SUPPORTED_IMAGES_TYPES
from files.exceptions import SelectelUploadError
from files.helpers import convert_image_to_webp
from files.typings import FileInfo
from procollab.settings import SELECTEL_SWIFT_URL
User = get_user_model()
class File:
def __init__(
self,
file_obj: TemporaryUploadedFile | InMemoryUploadedFile,
quality: int = 70,
):
self.size = file_obj.size
self.name = File._get_name(file_obj)
self.extension = File._get_extension(file_obj)
self.buffer = file_obj.open(mode="rb")
self.content_type = file_obj.content_type
# we can compress given type of image
if self.content_type in SUPPORTED_IMAGES_TYPES:
webp_image = convert_image_to_webp(file_obj, quality)
self.buffer = webp_image.buffer()
self.size = webp_image.size
self.content_type = "image/webp"
self.extension = "webp"
@staticmethod
def _get_name(file_obj) -> str:
file_name_parts = file_obj.name.split(".")
if len(file_name_parts) == 1:
return file_name_parts[0]
return ".".join(file_name_parts[:-1])
@staticmethod
def _get_extension(file_obj) -> str:
file_name_parts = file_obj.name.split(".")
if len(file_name_parts) > 1:
return file_name_parts[-1]
return ""
class Storage(ABC):
@abstractmethod
def delete(self, url: str) -> Response:
pass
@abstractmethod
def upload(self, file_obj: File, user: User) -> FileInfo:
pass
class SelectelSwiftStorage(Storage):
def delete(self, url: str) -> Response:
token = self._get_auth_token()
return requests.delete(url, headers={"X-Auth-Token": token})
def upload(self, file_obj: File, user: User) -> FileInfo:
url = self._upload(file_obj, user)
return FileInfo(
url=url,
name=file_obj.name,
extension=file_obj.extension,
mime_type=file_obj.content_type,
size=file_obj.size,
)
def _upload(self, file_obj: File, user: User) -> str:
token = self._get_auth_token()
url = self._generate_url(file_obj, user)
requests.put(
url,
headers={
"X-Auth-Token": token,
"Content-Type": file_obj.content_type,
},
data=file_obj.buffer,
)
return url
def _generate_url(self, file_obj: File, user: User) -> str:
"""
Generates url for selcdn
Returns:
url: str looks like /hashedEmail/hashedFilename_hashedTime.extension
"""
return (
f"{SELECTEL_SWIFT_URL}"
f"{abs(hash(user.email))}"
f"/{abs(hash(file_obj.name))}"
f"_{abs(hash(time.time()))}"
f".{file_obj.extension}"
)
@staticmethod
def _get_auth_token():
"""
Returns auth token
"""
data = {
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"id": settings.SELECTEL_CONTAINER_USERNAME,
"password": settings.SELECTEL_CONTAINER_PASSWORD,
}
},
}
}
}
response = requests.post(settings.SELECTEL_AUTH_TOKEN_URL, json=data)
if response.status_code not in [200, 201]:
raise SelectelUploadError(
"Couldn't generate a token for Selectel Swift API (selcdn)"
)
return response.headers["x-subject-token"]
class CDN:
def __init__(self, storage: Storage) -> None:
self.storage = storage
def delete(self, url: str) -> Response:
return self.storage.delete(url)
def upload(
self,
file_obj: TemporaryUploadedFile | InMemoryUploadedFile,
user: User,
quality: int = 70,
) -> FileInfo:
return self.storage.upload(File(file_obj, quality), user)