mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
fix: file upload defaults private, internal network access, video inline
- _save_uploaded_file: keep is_public=False by default (no auto-public) - PublicFileView: internal network (192.168/10/172.16-31) serves any file; external only serves is_public=True files - Video files: Content-Disposition inline for browser playback - DRF API: fix misleading error message (files not files[])
This commit is contained in:
+36
-6
@@ -78,7 +78,7 @@ class FileDownloadView(View):
|
||||
if not os.path.exists(record.file_path):
|
||||
raise Http404("File not found on disk")
|
||||
FileUpload.objects.filter(pk=pk).update(download_count=F('download_count') + 1)
|
||||
disposition = 'inline' if record.is_image else f'attachment; filename="{record.name}"'
|
||||
disposition = 'inline' if (record.is_image or record.mime_type.startswith('video/')) else f'attachment; filename="{record.name}"'
|
||||
response = FileResponse(open(record.file_path, 'rb'), content_type=record.mime_type)
|
||||
response['Content-Disposition'] = disposition
|
||||
return response
|
||||
@@ -131,14 +131,44 @@ class FileSetExpiryView(View):
|
||||
|
||||
|
||||
class PublicFileView(View):
|
||||
"""Serve uploaded files via /public/files/{uuid}-{name}.
|
||||
|
||||
Access rules:
|
||||
- Internal network (192.168.x.x, 10.x.x.x, 172.16-31.x.x): serve any file
|
||||
- External (go.junv.cc, xgo.junv.cc): only serve is_public=True files
|
||||
- Videos and images stream inline; other files force download.
|
||||
"""
|
||||
|
||||
INTERNAL_NETS = ('192.168.', '10.', '172.16.', '172.17.', '172.18.',
|
||||
'172.19.', '172.20.', '172.21.', '172.22.', '172.23.',
|
||||
'172.24.', '172.25.', '172.26.', '172.27.', '172.28.',
|
||||
'172.29.', '172.30.', '172.31.')
|
||||
|
||||
def _is_internal(self, request):
|
||||
xff = request.META.get('HTTP_X_FORWARDED_FOR', '')
|
||||
client_ip = xff.split(',')[0].strip() if xff else request.META.get('REMOTE_ADDR', '')
|
||||
return any(client_ip.startswith(net) for net in self.INTERNAL_NETS)
|
||||
|
||||
def get(self, request, pk, filename=''):
|
||||
record = get_object_or_404(FileUpload, pk=pk, is_public=True)
|
||||
record = get_object_or_404(FileUpload, pk=pk)
|
||||
|
||||
# External access: only public files
|
||||
if not self._is_internal(request) and not record.is_public:
|
||||
raise Http404("This file is private")
|
||||
|
||||
if record.is_expired:
|
||||
raise Http404("This public link has expired")
|
||||
raise Http404("This link has expired")
|
||||
if not os.path.exists(record.file_path):
|
||||
raise Http404("File not found")
|
||||
|
||||
FileUpload.objects.filter(pk=record.pk).update(download_count=F('download_count') + 1)
|
||||
disposition = 'inline' if record.is_image else f'attachment; filename="{record.name}"'
|
||||
|
||||
# Inline for media, attachment for everything else
|
||||
if record.is_image or record.mime_type.startswith('video/'):
|
||||
disposition = 'inline'
|
||||
else:
|
||||
disposition = f'attachment; filename="{record.name}"'
|
||||
|
||||
response = FileResponse(open(record.file_path, 'rb'), content_type=record.mime_type)
|
||||
response['Content-Disposition'] = disposition
|
||||
return response
|
||||
@@ -155,7 +185,7 @@ class FileUploadViewSet(viewsets.ModelViewSet):
|
||||
def create(self, request, *args, **kwargs):
|
||||
uploaded = request.FILES.getlist('files')
|
||||
if not uploaded:
|
||||
return Response({'error': 'No files provided. Use files[] field.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({'error': 'No files provided. Use "files" field.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
created = [_save_uploaded_file(f) for f in uploaded]
|
||||
serializer = self.get_serializer(created, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
@@ -201,7 +231,7 @@ class FileUploadViewSet(viewsets.ModelViewSet):
|
||||
if not os.path.exists(record.file_path):
|
||||
raise Http404("File not found")
|
||||
FileUpload.objects.filter(pk=pk).update(download_count=F('download_count') + 1)
|
||||
disposition = 'inline' if record.is_image else f'attachment; filename="{record.name}"'
|
||||
disposition = 'inline' if (record.is_image or record.mime_type.startswith('video/')) else f'attachment; filename="{record.name}"'
|
||||
response = FileResponse(open(record.file_path, 'rb'), content_type=record.mime_type)
|
||||
response['Content-Disposition'] = disposition
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user