mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: update file image urls to backend apis
This commit is contained in:
parent
94f445cc08
commit
e1f0da5e6c
@ -7,7 +7,6 @@ from .user import (
|
||||
UserAdminLiteSerializer,
|
||||
UserMeSerializer,
|
||||
UserMeSettingsSerializer,
|
||||
UserAssetSerializer,
|
||||
)
|
||||
from .workspace import (
|
||||
WorkSpaceSerializer,
|
||||
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
||||
|
||||
# Module import
|
||||
from .base import BaseSerializer
|
||||
from plane.db.models import User, Workspace, WorkspaceMemberInvite, UserAsset
|
||||
from plane.db.models import User, Workspace, WorkspaceMemberInvite
|
||||
|
||||
|
||||
class UserSerializer(BaseSerializer):
|
||||
@ -196,12 +196,3 @@ class ResetPasswordSerializer(serializers.Serializer):
|
||||
"""
|
||||
|
||||
new_password = serializers.CharField(required=True, min_length=8)
|
||||
|
||||
|
||||
class UserAssetSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = UserAsset
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"user",
|
||||
]
|
||||
|
@ -3,7 +3,6 @@ from django.urls import path
|
||||
|
||||
from plane.app.views import (
|
||||
FileAssetEndpoint,
|
||||
UserAssetsEndpoint,
|
||||
FileAssetViewSet,
|
||||
)
|
||||
|
||||
|
@ -14,6 +14,7 @@ from plane.app.views import (
|
||||
ProjectPublicCoverImagesEndpoint,
|
||||
ProjectDeployBoardViewSet,
|
||||
UserProjectRolesEndpoint,
|
||||
ProjectCoverImageEndpoint,
|
||||
)
|
||||
|
||||
|
||||
@ -175,4 +176,14 @@ urlpatterns = [
|
||||
),
|
||||
name="project-deploy-board",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/cover-image/<str:workspace_id>/<str:cover_image_key>/",
|
||||
ProjectCoverImageEndpoint.as_view(),
|
||||
name="project-cover-image",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/cover-image/",
|
||||
ProjectCoverImageEndpoint.as_view(),
|
||||
name="project-cover-image",
|
||||
),
|
||||
]
|
||||
|
@ -8,7 +8,6 @@ from plane.app.views import (
|
||||
UserActivityEndpoint,
|
||||
ChangePasswordEndpoint,
|
||||
SetUserPasswordEndpoint,
|
||||
UserAssetsEndpoint,
|
||||
## End User
|
||||
## Workspaces
|
||||
UserWorkSpacesEndpoint,
|
||||
@ -16,6 +15,10 @@ from plane.app.views import (
|
||||
UserIssueCompletedGraphEndpoint,
|
||||
UserWorkspaceDashboardEndpoint,
|
||||
## End Workspaces
|
||||
# Asset Endpoints ##
|
||||
UserAvatarEndpoint,
|
||||
UserCoverImageEndpoint,
|
||||
## End Asset Endpoint ##
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
@ -96,15 +99,26 @@ urlpatterns = [
|
||||
SetUserPasswordEndpoint.as_view(),
|
||||
name="set-password",
|
||||
),
|
||||
# User Assets
|
||||
path(
|
||||
"users/assets/",
|
||||
UserAssetsEndpoint.as_view(),
|
||||
name="user-assets",
|
||||
"users/avatar/",
|
||||
UserAvatarEndpoint.as_view(),
|
||||
name="user-avatar",
|
||||
),
|
||||
path(
|
||||
"users/assets/<str:key>/",
|
||||
UserAssetsEndpoint.as_view(),
|
||||
name="user-assets",
|
||||
"users/avatar/<str:avatar_key>/",
|
||||
UserAvatarEndpoint.as_view(),
|
||||
name="user-avatar",
|
||||
),
|
||||
## End User Graph
|
||||
path(
|
||||
"users/cover-image/",
|
||||
UserCoverImageEndpoint.as_view(),
|
||||
name="user-avatar",
|
||||
),
|
||||
path(
|
||||
"users/cover-image/<str:cover_image_key>/",
|
||||
UserCoverImageEndpoint.as_view(),
|
||||
name="user-avatar",
|
||||
),
|
||||
## User Assets
|
||||
]
|
||||
|
@ -22,6 +22,7 @@ from plane.app.views import (
|
||||
WorkspaceUserPropertiesEndpoint,
|
||||
WorkspaceStatesEndpoint,
|
||||
WorkspaceEstimatesEndpoint,
|
||||
WorkspaceLogoEndpoint,
|
||||
)
|
||||
|
||||
|
||||
@ -219,4 +220,14 @@ urlpatterns = [
|
||||
WorkspaceEstimatesEndpoint.as_view(),
|
||||
name="workspace-estimate",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/logo/",
|
||||
WorkspaceLogoEndpoint.as_view(),
|
||||
name="workspace-logo",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/logo/<str:workspace_id>/<str:logo_key>/",
|
||||
WorkspaceLogoEndpoint.as_view(),
|
||||
name="workspace-logo",
|
||||
),
|
||||
]
|
||||
|
@ -12,13 +12,15 @@ from .project import (
|
||||
ProjectPublicCoverImagesEndpoint,
|
||||
ProjectDeployBoardViewSet,
|
||||
UserProjectRolesEndpoint,
|
||||
ProjectCoverImageEndpoint,
|
||||
)
|
||||
from .user import (
|
||||
UserEndpoint,
|
||||
UpdateUserOnBoardedEndpoint,
|
||||
UpdateUserTourCompletedEndpoint,
|
||||
UserActivityEndpoint,
|
||||
UserAssetsEndpoint,
|
||||
UserAvatarEndpoint,
|
||||
UserCoverImageEndpoint,
|
||||
)
|
||||
|
||||
from .oauth import OauthEndpoint
|
||||
@ -50,6 +52,7 @@ from .workspace import (
|
||||
WorkspaceUserPropertiesEndpoint,
|
||||
WorkspaceStatesEndpoint,
|
||||
WorkspaceEstimatesEndpoint,
|
||||
WorkspaceLogoEndpoint,
|
||||
)
|
||||
from .state import StateViewSet
|
||||
from .view import (
|
||||
|
@ -18,12 +18,13 @@ from django.db.models import (
|
||||
from django.core.validators import validate_email
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
# Third Party imports
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework import serializers
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
|
||||
# Module imports
|
||||
from .base import BaseViewSet, BaseAPIView, WebhookMixin
|
||||
@ -37,6 +38,8 @@ from plane.app.serializers import (
|
||||
ProjectDeployBoardSerializer,
|
||||
ProjectMemberAdminSerializer,
|
||||
ProjectMemberRoleSerializer,
|
||||
ProjectLiteSerializer,
|
||||
FileAssetSerializer,
|
||||
)
|
||||
|
||||
from plane.app.permissions import (
|
||||
@ -62,10 +65,10 @@ from plane.db.models import (
|
||||
Inbox,
|
||||
ProjectDeployBoard,
|
||||
IssueProperty,
|
||||
FileAsset,
|
||||
)
|
||||
|
||||
from plane.bgtasks.project_invitation_task import project_invitation
|
||||
|
||||
from plane.utils.presigned_url_generator import generate_download_presigned_url
|
||||
|
||||
class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
serializer_class = ProjectListSerializer
|
||||
@ -1138,3 +1141,45 @@ class UserProjectRolesEndpoint(BaseAPIView):
|
||||
for member in project_members
|
||||
}
|
||||
return Response(project_members, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ProjectCoverImageEndpoint(BaseAPIView):
|
||||
|
||||
parser_classes = (
|
||||
MultiPartParser,
|
||||
FormParser,
|
||||
JSONParser,
|
||||
)
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == "POST" or self.request.method == "DELETE":
|
||||
return [
|
||||
IsAuthenticated(),
|
||||
]
|
||||
return [
|
||||
AllowAny(),
|
||||
]
|
||||
|
||||
def get(self, request, slug, project_id, workspace_id, cover_image_key):
|
||||
key = f"{workspace_id}/{cover_image_key}"
|
||||
url = generate_download_presigned_url(key)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def post(self, request, slug, project_id):
|
||||
serializer = FileAssetSerializer(data=request.data)
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
if serializer.is_valid():
|
||||
serializer.save(workspace=workspace)
|
||||
project = Project.objects.get(pk=project_id)
|
||||
project.cover_image = f"/api/workspaces/{slug}/projects/{project_id}/cover-image/{serializer.data['asset']}/"
|
||||
project.save()
|
||||
project_serializer = ProjectLiteSerializer(project)
|
||||
return Response(project_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, project_id, workspace_id, cover_image_key):
|
||||
key = f"{workspace_id}/{cover_image_key}"
|
||||
file_asset = FileAsset.objects.get(asset=key)
|
||||
file_asset.is_deleted = True
|
||||
file_asset.save()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
@ -2,7 +2,7 @@
|
||||
import requests
|
||||
|
||||
# Django imports
|
||||
from django.http import StreamingHttpResponse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.conf import settings
|
||||
|
||||
# Third party imports
|
||||
@ -10,6 +10,7 @@ import boto3
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
|
||||
# Module imports
|
||||
from plane.app.serializers import (
|
||||
@ -17,14 +18,14 @@ from plane.app.serializers import (
|
||||
IssueActivitySerializer,
|
||||
UserMeSerializer,
|
||||
UserMeSettingsSerializer,
|
||||
UserAssetSerializer,
|
||||
FileAssetSerializer,
|
||||
)
|
||||
|
||||
from plane.app.views.base import BaseViewSet, BaseAPIView
|
||||
from plane.db.models import User, IssueActivity, WorkspaceMember, ProjectMember, UserAsset
|
||||
from plane.db.models import User, IssueActivity, WorkspaceMember, ProjectMember, FileAsset
|
||||
from plane.license.models import Instance, InstanceAdmin
|
||||
from plane.utils.paginator import BasePaginator
|
||||
from plane.utils.file_stream import get_file_streams
|
||||
from plane.utils.presigned_url_generator import generate_download_presigned_url
|
||||
|
||||
from django.db.models import Q, F, Count, Case, When, IntegerField
|
||||
|
||||
@ -188,25 +189,82 @@ class UserActivityEndpoint(BaseAPIView, BasePaginator):
|
||||
)
|
||||
|
||||
|
||||
class UserAssetsEndpoint(BaseAPIView):
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
class UserAvatarEndpoint(BaseAPIView):
|
||||
|
||||
parser_classes = (
|
||||
MultiPartParser,
|
||||
FormParser,
|
||||
JSONParser,
|
||||
)
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == "POST" or self.request.method == "DELETE":
|
||||
return [
|
||||
IsAuthenticated(),
|
||||
]
|
||||
return [
|
||||
AllowAny(),
|
||||
]
|
||||
|
||||
|
||||
def get(self, request, avatar_key):
|
||||
url = generate_download_presigned_url(avatar_key)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def post(self, request):
|
||||
serializer = UserAssetSerializer(data=request.data)
|
||||
serializer = FileAssetSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save(user=request.user)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
# Get the workspace
|
||||
serializer.save()
|
||||
user = request.user
|
||||
user.avatar = "/api/users/avatar/" + serializer.data["asset"] + "/"
|
||||
user.save()
|
||||
user_serializer = UserMeSerializer(user)
|
||||
return Response(user_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, asset_key):
|
||||
user_asset = UserAsset.objects.get(
|
||||
asset=asset_key, created_by=request.user
|
||||
)
|
||||
user_asset.is_deleted = True
|
||||
user_asset.save()
|
||||
def delete(self, request, avatar_key):
|
||||
file_asset = FileAsset.objects.get(asset=avatar_key)
|
||||
file_asset.is_deleted = True
|
||||
file_asset.save()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
def get(self, request, key):
|
||||
response = get_file_streams(key, key)
|
||||
return response
|
||||
class UserCoverImageEndpoint(BaseAPIView):
|
||||
|
||||
parser_classes = (
|
||||
MultiPartParser,
|
||||
FormParser,
|
||||
JSONParser,
|
||||
)
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == "POST" or self.request.method == "DELETE":
|
||||
return [
|
||||
IsAuthenticated(),
|
||||
]
|
||||
return [
|
||||
AllowAny(),
|
||||
]
|
||||
|
||||
def get(self, request, cover_image_key):
|
||||
url = generate_download_presigned_url(cover_image_key)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def post(self, request):
|
||||
serializer = FileAssetSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
# Get the workspace
|
||||
serializer.save()
|
||||
user = request.user
|
||||
user.avatar = "/api/users/cover-image/" + serializer.data["asset"] + "/"
|
||||
user.save()
|
||||
user_serializer = UserMeSerializer(user)
|
||||
return Response(user_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, cover_image_key):
|
||||
file_asset = FileAsset.objects.get(asset=cover_image_key)
|
||||
file_asset.is_deleted = True
|
||||
file_asset.save()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
@ -25,11 +25,13 @@ from django.db.models import (
|
||||
)
|
||||
from django.db.models.functions import ExtractWeek, Cast, ExtractDay
|
||||
from django.db.models.fields import DateField
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
# Third party modules
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
|
||||
# Module imports
|
||||
from plane.app.serializers import (
|
||||
@ -49,6 +51,7 @@ from plane.app.serializers import (
|
||||
WorkspaceEstimateSerializer,
|
||||
StateSerializer,
|
||||
LabelSerializer,
|
||||
FileAssetSerializer,
|
||||
)
|
||||
from plane.app.views.base import BaseAPIView
|
||||
from . import BaseViewSet
|
||||
@ -73,6 +76,7 @@ from plane.db.models import (
|
||||
WorkspaceUserProperties,
|
||||
Estimate,
|
||||
EstimatePoint,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.app.permissions import (
|
||||
WorkSpaceBasePermission,
|
||||
@ -84,6 +88,7 @@ from plane.app.permissions import (
|
||||
)
|
||||
from plane.bgtasks.workspace_invitation_task import workspace_invitation
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
from plane.utils.presigned_url_generator import generate_download_presigned_url
|
||||
from plane.bgtasks.event_tracking_task import workspace_invite_event
|
||||
|
||||
|
||||
@ -1524,3 +1529,44 @@ class WorkspaceUserPropertiesEndpoint(BaseAPIView):
|
||||
)
|
||||
serializer = WorkspaceUserPropertiesSerializer(workspace_properties)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class WorkspaceLogoEndpoint(BaseAPIView):
|
||||
|
||||
parser_classes = (
|
||||
MultiPartParser,
|
||||
FormParser,
|
||||
JSONParser,
|
||||
)
|
||||
|
||||
def get_permissions(self):
|
||||
if self.request.method == "POST" or self.request.method == "DELETE":
|
||||
return [
|
||||
IsAuthenticated(),
|
||||
]
|
||||
return [
|
||||
AllowAny(),
|
||||
]
|
||||
|
||||
def get(self, request, slug, workspace_id, logo_key):
|
||||
key = f"{workspace_id}/{logo_key}"
|
||||
url = generate_download_presigned_url(key)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def post(self, request, slug):
|
||||
serializer = FileAssetSerializer(data=request.data)
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
if serializer.is_valid():
|
||||
serializer.save(workspace=workspace)
|
||||
workspace.logo = f"/api/workspaces/{slug}/logo/{serializer.data['asset']}/"
|
||||
workspace.save()
|
||||
workspace_serializer = WorkSpaceSerializer(workspace)
|
||||
return Response(workspace_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, slug, workspace_id, logo_key):
|
||||
key = f"{workspace_id}/{logo_key}"
|
||||
file_asset = FileAsset.objects.get(asset=key)
|
||||
file_asset.is_deleted = True
|
||||
file_asset.save()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
@ -9,49 +9,52 @@ import plane.db.models.asset
|
||||
def update_urls(apps, schema_editor):
|
||||
# Check if the app is using minio or s3
|
||||
if settings.USE_MINIO:
|
||||
prefix = (
|
||||
prefix1 = (
|
||||
f"{settings.AWS_S3_URL_PROTOCOL}//{settings.AWS_S3_CUSTOM_DOMAIN}/"
|
||||
)
|
||||
prefix2 = prefix
|
||||
prefix2 = prefix1
|
||||
else:
|
||||
prefix = f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.{settings.AWS_REGION}.amazonaws.com/"
|
||||
prefix1 = f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.{settings.AWS_REGION}.amazonaws.com/"
|
||||
prefix2 = (
|
||||
f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/"
|
||||
)
|
||||
|
||||
User = apps.get_model("db", "User")
|
||||
UserAsset = apps.get_model("db", "UserAsset")
|
||||
bulk_users = []
|
||||
bulk_user_assets = []
|
||||
for user in User.objects.all():
|
||||
if user.avatar and (
|
||||
user.avatar.startswith(prefix) or user.avatar.startswith(prefix2)
|
||||
):
|
||||
avatar = user.avatar[len(prefix) :]
|
||||
user.avatar = avatar
|
||||
bulk_user_assets.append(
|
||||
UserAsset(
|
||||
user=user,
|
||||
asset=avatar,
|
||||
)
|
||||
)
|
||||
|
||||
if user.cover_image and (
|
||||
user.cover_image.startswith(prefix)
|
||||
or user.cover_image.startswith(prefix2)
|
||||
):
|
||||
cover_image = user.cover_image[len(prefix) :]
|
||||
user.cover_image = cover_image
|
||||
bulk_user_assets.append(
|
||||
UserAsset(
|
||||
user=user,
|
||||
asset=cover_image,
|
||||
# Loop through all the users and update the cover image
|
||||
for user in User.objects.all():
|
||||
# prefix 1
|
||||
if user.avatar and (user.avatar.startswith(prefix1)):
|
||||
avatar_key = user.avatar
|
||||
user.avatar = "/api/users/avatar/" + avatar_key[len(prefix1) :] + "/"
|
||||
bulk_users.append(user)
|
||||
|
||||
# prefix 2
|
||||
if not settings.USE_MINIO and user.avatar and user.avatar.startswith(prefix2):
|
||||
avatar_key = user.avatar
|
||||
user.avatar = "/api/users/avatar/" + avatar_key[len(prefix2) :] + "/"
|
||||
bulk_users.append(user)
|
||||
|
||||
# prefix 1
|
||||
if user.cover_image and (user.cover_image.startswith(prefix1)):
|
||||
cover_image_key = user.cover_image
|
||||
user.cover_image = (
|
||||
"/api/users/cover-image/" + cover_image_key[len(prefix1) :] + "/"
|
||||
)
|
||||
bulk_users.append(user)
|
||||
|
||||
# prefix 2
|
||||
if not settings.USE_MINIO and user.cover_image and user.cover_image.startswith(prefix2):
|
||||
cover_image_key = user.cover_image
|
||||
user.cover_image = (
|
||||
"/api/users/cover-image/" + cover_image_key[len(prefix2) :] + "/"
|
||||
)
|
||||
bulk_users.append(user)
|
||||
|
||||
User.objects.bulk_update(
|
||||
bulk_users, ["avatar", "cover_image"], batch_size=100
|
||||
)
|
||||
UserAsset.objects.bulk_create(bulk_user_assets, batch_size=100)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@ -60,80 +63,6 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="UserAsset",
|
||||
fields=[
|
||||
(
|
||||
"created_at",
|
||||
models.DateTimeField(
|
||||
auto_now_add=True, verbose_name="Created At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_at",
|
||||
models.DateTimeField(
|
||||
auto_now=True, verbose_name="Last Modified At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
db_index=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"asset",
|
||||
models.FileField(
|
||||
max_length=500,
|
||||
storage=plane.settings.storage.S3PrivateBucketStorage(),
|
||||
upload_to=plane.db.models.user.get_upload_path,
|
||||
validators=[plane.db.models.user.file_size],
|
||||
),
|
||||
),
|
||||
("is_deleted", models.BooleanField(default=False)),
|
||||
("size", models.PositiveBigIntegerField(null=True)),
|
||||
("attributes", models.JSONField(default=dict)),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_created_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Created By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_updated_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Last Modified By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="assets",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "User Asset",
|
||||
"verbose_name_plural": "User Assets",
|
||||
"db_table": "user_assets",
|
||||
"ordering": ("-created_at",),
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="fileasset",
|
||||
name="asset",
|
||||
|
85
apiserver/plane/db/migrations/0060_fileasset_size.py
Normal file
85
apiserver/plane/db/migrations/0060_fileasset_size.py
Normal file
@ -0,0 +1,85 @@
|
||||
# Generated by Django 4.2.7 on 2024-02-02 07:23
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def update_workspace_urls(apps, schema_editor):
|
||||
# Check if the app is using minio or s3
|
||||
if settings.USE_MINIO:
|
||||
prefix1 = (
|
||||
f"{settings.AWS_S3_URL_PROTOCOL}//{settings.AWS_S3_CUSTOM_DOMAIN}/"
|
||||
)
|
||||
prefix2 = prefix1
|
||||
else:
|
||||
prefix1 = f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.{settings.AWS_REGION}.amazonaws.com/"
|
||||
prefix2 = (
|
||||
f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/"
|
||||
)
|
||||
|
||||
Workspace = apps.get_model("db", "Workspace")
|
||||
bulk_workspaces = []
|
||||
|
||||
# Loop through all the users and update the cover image
|
||||
for workspace in Workspace.objects.all():
|
||||
# prefix 1
|
||||
if workspace.logo and (workspace.logo.startswith(prefix1)):
|
||||
logo_key = workspace.logo
|
||||
workspace.logo = f"/api/workspaces/{workspace.slug}/logo/{logo_key[len(prefix1) :]}/"
|
||||
bulk_workspaces.append(workspace)
|
||||
|
||||
# prefix 2
|
||||
if not settings.USE_MINIO and workspace.logo and (workspace.logo.startswith(prefix2)):
|
||||
logo_key = workspace.logo
|
||||
workspace.logo = f"/api/workspaces/{workspace.slug}/logo/{logo_key[len(prefix2) :]}/"
|
||||
bulk_workspaces.append(workspace)
|
||||
|
||||
Workspace.objects.bulk_update(bulk_workspaces, ["logo"], batch_size=100)
|
||||
|
||||
def update_project_urls(apps, schema_editor):
|
||||
# Check if the app is using minio or s3
|
||||
if settings.USE_MINIO:
|
||||
prefix1 = (
|
||||
f"{settings.AWS_S3_URL_PROTOCOL}//{settings.AWS_S3_CUSTOM_DOMAIN}/"
|
||||
)
|
||||
prefix2 = prefix1
|
||||
else:
|
||||
prefix1 = f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.{settings.AWS_REGION}.amazonaws.com/"
|
||||
prefix2 = (
|
||||
f"https://{settings.AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com/"
|
||||
)
|
||||
|
||||
Project = apps.get_model("db", "Project")
|
||||
bulk_projects = []
|
||||
|
||||
# Loop through all the users and update the cover image
|
||||
for project in Project.objects.all():
|
||||
# prefix 1
|
||||
if project.cover_image and (project.cover_image.startswith(prefix1)):
|
||||
cover_image_key = project.cover_image
|
||||
project.cover_image = f"/api/workspaces/{project.workspace.slug}/projects/{project.id}/cover-image/{cover_image_key[len(prefix1) :]}/"
|
||||
bulk_projects.append(project)
|
||||
|
||||
# prefix 2
|
||||
if not settings.USE_MINIO and project.cover_image and (project.cover_image.startswith(prefix2)):
|
||||
cover_image_key = project.cover_image
|
||||
project.cover_image = f"/api/workspaces/{project.workspace.slug}/projects/{project.id}/cover-image/{cover_image_key[len(prefix2) :]}/"
|
||||
bulk_projects.append(project)
|
||||
|
||||
Project.objects.bulk_update(bulk_projects, ["cover_image"], batch_size=100)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("db", "0059_auto_20240131_1334"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="fileasset",
|
||||
name="size",
|
||||
field=models.PositiveBigIntegerField(null=True),
|
||||
),
|
||||
migrations.RunPython(update_workspace_urls),
|
||||
migrations.RunPython(update_project_urls),
|
||||
]
|
@ -1,6 +1,6 @@
|
||||
from .base import BaseModel
|
||||
|
||||
from .user import User, UserAsset
|
||||
from .user import User
|
||||
|
||||
from .workspace import (
|
||||
Workspace,
|
||||
|
@ -41,6 +41,7 @@ class FileAsset(BaseModel):
|
||||
related_name="assets",
|
||||
)
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
size = models.PositiveBigIntegerField(null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "File Asset"
|
||||
@ -50,3 +51,7 @@ class FileAsset(BaseModel):
|
||||
|
||||
def __str__(self):
|
||||
return str(self.asset)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.size = self.asset.size
|
||||
super(FileAsset, self).save(*args, **kwargs)
|
||||
|
@ -149,42 +149,6 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||
super(User, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
def get_upload_path(instance, filename):
|
||||
return f"user-{uuid.uuid4().hex}"
|
||||
|
||||
def file_size(value):
|
||||
if value.size > settings.FILE_SIZE_LIMIT:
|
||||
raise ValidationError("File too large. Size should not exceed 5 MB.")
|
||||
|
||||
class UserAsset(BaseModel):
|
||||
|
||||
user = models.ForeignKey("db.User", on_delete=models.CASCADE, related_name="assets")
|
||||
asset = models.FileField(
|
||||
upload_to=get_upload_path,
|
||||
validators=[
|
||||
file_size,
|
||||
],
|
||||
storage=S3PrivateBucketStorage(),
|
||||
max_length=500,
|
||||
)
|
||||
is_deleted = models.BooleanField(default=False)
|
||||
size = models.PositiveBigIntegerField(null=True)
|
||||
attributes = models.JSONField(default=dict)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.size = self.asset.size
|
||||
super(UserAsset, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "User Asset"
|
||||
verbose_name_plural = "User Assets"
|
||||
db_table = "user_assets"
|
||||
ordering = ("-created_at",)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.asset)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def send_welcome_slack(sender, instance, created, **kwargs):
|
||||
try:
|
||||
|
Loading…
Reference in New Issue
Block a user