takahe/mediaproxy/views.py
2022-12-29 10:35:14 -07:00

113 lines
3.5 KiB
Python

from urllib.parse import urlparse
import httpx
from django.conf import settings
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.generic import View
from activities.models import Emoji, PostAttachment
from users.models import Identity
class BaseProxyView(View):
"""
Base class for proxying remote content.
"""
def get(self, request, **kwargs):
self.kwargs = kwargs
remote_url = self.get_remote_url()
# See if we can do the nginx trick or a normal forward
if request.headers.get("x-takahe-accel") and not request.GET.get("no_accel"):
bits = urlparse(remote_url)
redirect_url = (
f"/__takahe_accel__/{bits.scheme}/{bits.hostname}/{bits.path}"
)
if bits.query:
redirect_url += f"?{bits.query}"
return HttpResponse(
"",
headers={
"X-Accel-Redirect": "/__takahe_accel__/",
"X-Takahe-RealUri": remote_url,
"Cache-Control": "public",
},
)
else:
try:
remote_response = httpx.get(
remote_url,
headers={"User-Agent": settings.TAKAHE_USER_AGENT},
follow_redirects=True,
timeout=settings.SETUP.REMOTE_TIMEOUT,
)
except httpx.RequestError:
return HttpResponse(status=502)
if remote_response.status_code >= 400:
return HttpResponse(status=502)
return HttpResponse(
remote_response.content,
headers={
"Content-Type": remote_response.headers.get(
"Content-Type", "application/octet-stream"
),
"Cache-Control": remote_response.headers.get(
"Cache-Control", "public, max-age=3600"
),
},
)
def get_remote_url(self) -> str:
raise NotImplementedError()
class EmojiCacheView(BaseProxyView):
"""
Proxies Emoji
"""
def get_remote_url(self):
self.emoji = get_object_or_404(Emoji, pk=self.kwargs["emoji_id"])
if not self.emoji.remote_url:
raise Http404()
return self.emoji.remote_url
class IdentityIconCacheView(BaseProxyView):
"""
Proxies identity icons (avatars)
"""
def get_remote_url(self):
self.identity = get_object_or_404(Identity, pk=self.kwargs["identity_id"])
if self.identity.local or not self.identity.icon_uri:
raise Http404()
return self.identity.icon_uri
class IdentityImageCacheView(BaseProxyView):
"""
Proxies identity profile header images
"""
def get_remote_url(self):
self.identity = get_object_or_404(Identity, pk=self.kwargs["identity_id"])
if self.identity.local or not self.identity.image_uri:
raise Http404()
return self.identity.image_uri
class PostAttachmentCacheView(BaseProxyView):
"""
Proxies post media (images only, videos should always be offloaded to remote)
"""
def get_remote_url(self):
self.post_attachment = get_object_or_404(
PostAttachment, pk=self.kwargs["attachment_id"]
)
if not self.post_attachment.is_image():
raise Http404()
return self.post_attachment.remote_url