add media endpoint for XSendfile
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
cc0bdc0472
commit
d411e6790d
3 changed files with 53 additions and 2 deletions
|
@ -36,5 +36,6 @@ urlpatterns = [
|
||||||
path('api/', include('toolshed.api.inventory')),
|
path('api/', include('toolshed.api.inventory')),
|
||||||
path('api/', include('toolshed.api.info')),
|
path('api/', include('toolshed.api.info')),
|
||||||
path('api/', include('toolshed.api.files')),
|
path('api/', include('toolshed.api.files')),
|
||||||
|
path('media/', include('files.media_urls')),
|
||||||
path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'),
|
path('docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'),
|
||||||
]
|
]
|
||||||
|
|
22
backend/files/media_urls.py
Normal file
22
backend/files/media_urls.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from django.urls import path
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from files.models import File
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
def media_urls(request, id, format=None):
|
||||||
|
try:
|
||||||
|
file = File.objects.get(file=id)
|
||||||
|
return Response(status=status.HTTP_200_OK,
|
||||||
|
headers={'X-Accel-Redirect': f'/redirect_media/{id}'}) # TODO Expires and Cache-Control
|
||||||
|
|
||||||
|
except File.DoesNotExist:
|
||||||
|
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('<path:id>', media_urls),
|
||||||
|
]
|
|
@ -2,7 +2,7 @@ from django.core.files.base import ContentFile
|
||||||
from django.core.files.storage import DefaultStorage
|
from django.core.files.storage import DefaultStorage
|
||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.test import Client
|
from django.test import Client
|
||||||
from authentication.tests import SignatureAuthClient, ToolshedTestCase
|
from authentication.tests import SignatureAuthClient, ToolshedTestCase, UserTestMixin
|
||||||
from nacl.hash import sha256
|
from nacl.hash import sha256
|
||||||
from nacl.encoding import HexEncoder
|
from nacl.encoding import HexEncoder
|
||||||
import base64
|
import base64
|
||||||
|
@ -95,7 +95,7 @@ class FilesTestCase(FilesTestMixin, ToolshedTestCase):
|
||||||
self.assertTrue(created)
|
self.assertTrue(created)
|
||||||
self.assertEqual(file.file.read(), self.f['test_content4'])
|
self.assertEqual(file.file.read(), self.f['test_content4'])
|
||||||
self.assertEqual(file.file.name,
|
self.assertEqual(file.file.name,
|
||||||
f"{self.f['hash4'][:2]}/{self.f['hash4'][2:4]}/{self.f['hash4'][4:6]}/{self.f['hash4'][6:]}")
|
f"{self.f['hash4'][:2]}/{self.f['hash4'][2:4]}/{self.f['hash4'][4:6]}/{self.f['hash4'][6:]}")
|
||||||
|
|
||||||
def test_file_upload_get_or_create_fail(self):
|
def test_file_upload_get_or_create_fail(self):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
@ -103,3 +103,31 @@ class FilesTestCase(FilesTestMixin, ToolshedTestCase):
|
||||||
File.objects.get_or_create(hash=self.f['hash3'])
|
File.objects.get_or_create(hash=self.f['hash3'])
|
||||||
self.assertEqual(File.objects.count(), 3)
|
self.assertEqual(File.objects.count(), 3)
|
||||||
self.assertEqual(countdir(DefaultStorage(), ''), 3)
|
self.assertEqual(countdir(DefaultStorage(), ''), 3)
|
||||||
|
|
||||||
|
|
||||||
|
class MediaUrlTestCase(FilesTestMixin, UserTestMixin, ToolshedTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.prepare_files()
|
||||||
|
self.prepare_users()
|
||||||
|
|
||||||
|
def test_file_url(self):
|
||||||
|
reply = client.get(
|
||||||
|
f"/media/{self.f['hash1'][:2]}/{self.f['hash1'][2:4]}/{self.f['hash1'][4:6]}/{self.f['hash1'][6:]}",
|
||||||
|
self.f['local_user1'])
|
||||||
|
self.assertEqual(reply.status_code, 200)
|
||||||
|
self.assertEqual(reply.headers['X-Accel-Redirect'],
|
||||||
|
f"/redirect_media/{self.f['hash1'][:2]}/{self.f['hash1'][2:4]}/{self.f['hash1'][4:6]}/{self.f['hash1'][6:]}")
|
||||||
|
reply = client.get(
|
||||||
|
f"/media/{self.f['hash2'][:2]}/{self.f['hash2'][2:4]}/{self.f['hash2'][4:6]}/{self.f['hash2'][6:]}",
|
||||||
|
self.f['local_user1'])
|
||||||
|
self.assertEqual(reply.status_code, 200)
|
||||||
|
self.assertEqual(reply.headers['X-Accel-Redirect'],
|
||||||
|
f"/redirect_media/{self.f['hash2'][:2]}/{self.f['hash2'][2:4]}/{self.f['hash2'][4:6]}/{self.f['hash2'][6:]}")
|
||||||
|
|
||||||
|
def test_file_url_fail(self):
|
||||||
|
reply = client.get('/media/{}/'.format('nonexistent'), self.f['local_user1'])
|
||||||
|
self.assertEqual(reply.status_code, 404)
|
||||||
|
self.assertTrue('X-Accel-Redirect' not in reply.headers)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue