73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
"""
|
|
Upload validation helpers.
|
|
|
|
Validates file extension and size against settings before persisting to storage.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
class UploadValidationError(Exception):
|
|
pass
|
|
|
|
|
|
def validate_upload(file) -> None:
|
|
"""
|
|
Validate an uploaded file against configured size and extension limits.
|
|
Raises UploadValidationError on violation.
|
|
"""
|
|
ext = os.path.splitext(file.name)[1].lower()
|
|
allowed = [e.lower() for e in getattr(settings, "ALLOWED_UPLOAD_EXTENSIONS", [])]
|
|
max_size = getattr(settings, "MAX_UPLOAD_SIZE_BYTES", 100 * 1024 * 1024)
|
|
|
|
if allowed and ext not in allowed:
|
|
raise UploadValidationError(
|
|
f"File extension '{ext}' is not allowed. Permitted: {', '.join(allowed)}"
|
|
)
|
|
|
|
if file.size > max_size:
|
|
max_mb = max_size // (1024 * 1024)
|
|
raise UploadValidationError(
|
|
f"File size {file.size} bytes exceeds limit of {max_mb} MB."
|
|
)
|
|
|
|
|
|
def save_upload(file, org_id: str, uploader) -> object:
|
|
"""Persist an uploaded file and create a MediaAsset record."""
|
|
import uuid
|
|
import mimetypes
|
|
from django.conf import settings as s
|
|
from .models import MediaAsset, ScanStatus
|
|
|
|
ext = os.path.splitext(file.name)[1].lower()
|
|
storage_root = getattr(s, "MEDIA_ROOT", "/tmp/media")
|
|
relative_path = os.path.join("uploads", str(org_id), f"{uuid.uuid4().hex}{ext}")
|
|
full_path = os.path.join(storage_root, relative_path)
|
|
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
|
|
with open(full_path, "wb") as dest:
|
|
for chunk in file.chunks():
|
|
dest.write(chunk)
|
|
|
|
mime_type, _ = mimetypes.guess_type(file.name)
|
|
|
|
asset = MediaAsset.objects.create(
|
|
uploaded_by=uploader,
|
|
org_id=org_id,
|
|
file_name=file.name,
|
|
file_path=relative_path,
|
|
file_size=file.size,
|
|
mime_type=mime_type or "",
|
|
extension=ext,
|
|
scan_status=ScanStatus.PENDING,
|
|
)
|
|
|
|
# Enqueue async scan
|
|
from .tasks import scan_media_asset_task
|
|
scan_media_asset_task.delay(str(asset.id))
|
|
|
|
return asset
|