diff --git a/apiserver/plane/app/urls/asset.py b/apiserver/plane/app/urls/asset.py index 77dd3d00efa..93356b04cb4 100644 --- a/apiserver/plane/app/urls/asset.py +++ b/apiserver/plane/app/urls/asset.py @@ -13,6 +13,8 @@ ProjectAssetEndpoint, ProjectBulkAssetEndpoint, AssetCheckEndpoint, + WorkspaceAssetDownloadEndpoint, + ProjectAssetDownloadEndpoint, ) @@ -89,4 +91,14 @@ AssetCheckEndpoint.as_view(), name="asset-check", ), + path( + "assets/v2/workspaces//download//", + WorkspaceAssetDownloadEndpoint.as_view(), + name="workspace-asset-download", + ), + path( + "assets/v2/workspaces//projects//download//", + ProjectAssetDownloadEndpoint.as_view(), + name="project-asset-download", + ), ] diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 55642a53358..6d56473e3f1 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -107,6 +107,8 @@ ProjectAssetEndpoint, ProjectBulkAssetEndpoint, AssetCheckEndpoint, + WorkspaceAssetDownloadEndpoint, + ProjectAssetDownloadEndpoint, ) from .issue.base import ( IssueListEndpoint, diff --git a/apiserver/plane/app/views/asset/v2.py b/apiserver/plane/app/views/asset/v2.py index aecba04b8c3..5994ffd8c16 100644 --- a/apiserver/plane/app/views/asset/v2.py +++ b/apiserver/plane/app/views/asset/v2.py @@ -718,3 +718,56 @@ def get(self, request, slug, asset_id): id=asset_id, workspace__slug=slug, deleted_at__isnull=True ).exists() return Response({"exists": asset}, status=status.HTTP_200_OK) + + +class WorkspaceAssetDownloadEndpoint(BaseAPIView): + """Endpoint to generate a download link for an asset with content-disposition=attachment.""" + + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE") + def get(self, request, slug, asset_id): + try: + asset = FileAsset.objects.get( + id=asset_id, + workspace__slug=slug, + is_uploaded=True, + ) + except FileAsset.DoesNotExist: + return Response( + {"error": "The requested asset could not be found."}, + status=status.HTTP_404_NOT_FOUND, + ) + + storage = S3Storage(request=request) + signed_url = storage.generate_presigned_url( + object_name=asset.asset.name, + disposition=f"attachment; filename={asset.asset.name}", + ) + + return HttpResponseRedirect(signed_url) + + +class ProjectAssetDownloadEndpoint(BaseAPIView): + """Endpoint to generate a download link for an asset with content-disposition=attachment.""" + + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="PROJECT") + def get(self, request, slug, project_id, asset_id): + try: + asset = FileAsset.objects.get( + id=asset_id, + workspace__slug=slug, + project_id=project_id, + is_uploaded=True, + ) + except FileAsset.DoesNotExist: + return Response( + {"error": "The requested asset could not be found."}, + status=status.HTTP_404_NOT_FOUND, + ) + + storage = S3Storage(request=request) + signed_url = storage.generate_presigned_url( + object_name=asset.asset.name, + disposition=f"attachment; filename={asset.asset.name}", + ) + + return HttpResponseRedirect(signed_url)