adding the s3 and cloudfront
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 34s
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 34s
This commit is contained in:
parent
05e313b837
commit
f1ee0b5774
6 changed files with 117 additions and 1 deletions
|
|
@ -14,11 +14,29 @@ class CategoryListField(serializers.RelatedField):
|
||||||
class SupplierProfileSerializer(serializers.ModelSerializer):
|
class SupplierProfileSerializer(serializers.ModelSerializer):
|
||||||
onboarding_step = serializers.SerializerMethodField()
|
onboarding_step = serializers.SerializerMethodField()
|
||||||
categories = CategoryListField(many=True, queryset=Category.objects.all(), required=False)
|
categories = CategoryListField(many=True, queryset=Category.objects.all(), required=False)
|
||||||
|
aadhar_url = serializers.SerializerMethodField()
|
||||||
|
pan_url = serializers.SerializerMethodField()
|
||||||
|
logo_url = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SupplierProfile
|
model = SupplierProfile
|
||||||
exclude = ('user',)
|
exclude = ('user',)
|
||||||
|
|
||||||
|
def get_aadhar_url(self, obj):
|
||||||
|
from .views import get_document_url
|
||||||
|
request = self.context.get('request')
|
||||||
|
return get_document_url(obj.aadhar_s3_key, request)
|
||||||
|
|
||||||
|
def get_pan_url(self, obj):
|
||||||
|
from .views import get_document_url
|
||||||
|
request = self.context.get('request')
|
||||||
|
return get_document_url(obj.pan_s3_key, request)
|
||||||
|
|
||||||
|
def get_logo_url(self, obj):
|
||||||
|
from .views import get_document_url
|
||||||
|
request = self.context.get('request')
|
||||||
|
return get_document_url(obj.logo_s3_key, request)
|
||||||
|
|
||||||
def get_onboarding_step(self, obj):
|
def get_onboarding_step(self, obj):
|
||||||
if obj.is_profile_complete:
|
if obj.is_profile_complete:
|
||||||
return 7
|
return 7
|
||||||
|
|
|
||||||
49
api/tests.py
49
api/tests.py
|
|
@ -190,3 +190,52 @@ def test_wallet_withdrawal(api_client, create_user):
|
||||||
# Invalid amount (exceeds outstanding)
|
# Invalid amount (exceeds outstanding)
|
||||||
response = api_client.post(url, {'amount': '1500.00'}, format='json')
|
response = api_client.post(url, {'amount': '1500.00'}, format='json')
|
||||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_document_cloudfront_url_generation(api_client, create_user):
|
||||||
|
from django.conf import settings
|
||||||
|
from api.views import get_document_url
|
||||||
|
|
||||||
|
profile = create_user.profile
|
||||||
|
profile.aadhar_s3_key = 'suppliers/123/aadhar.pdf'
|
||||||
|
profile.save()
|
||||||
|
|
||||||
|
# Case 1: CloudFront Domain set
|
||||||
|
settings.AWS_CLOUDFRONT_DOMAIN = 'd12345.cloudfront.net'
|
||||||
|
url = get_document_url(profile.aadhar_s3_key)
|
||||||
|
assert url == 'https://d12345.cloudfront.net/suppliers/123/aadhar.pdf'
|
||||||
|
|
||||||
|
# Case 2: CloudFront Domain set with protocol prefix
|
||||||
|
settings.AWS_CLOUDFRONT_DOMAIN = 'https://d98765.cloudfront.net/'
|
||||||
|
url = get_document_url(profile.aadhar_s3_key)
|
||||||
|
assert url == 'https://d98765.cloudfront.net/suppliers/123/aadhar.pdf'
|
||||||
|
|
||||||
|
# Case 3: CloudFront Domain empty, fallback to Local Mock
|
||||||
|
settings.AWS_CLOUDFRONT_DOMAIN = ''
|
||||||
|
url = get_document_url(profile.aadhar_s3_key)
|
||||||
|
assert 'mock-download' in url
|
||||||
|
|
||||||
|
# Case 4: API Response includes aadhar_url, pan_url, logo_url
|
||||||
|
url = reverse('profile')
|
||||||
|
api_client.force_authenticate(user=create_user)
|
||||||
|
response = api_client.get(url)
|
||||||
|
assert response.status_code == status.HTTP_200_OK
|
||||||
|
assert 'aadhar_url' in response.data['profile']
|
||||||
|
assert 'pan_url' in response.data['profile']
|
||||||
|
assert 'logo_url' in response.data['profile']
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_mock_download_view(api_client, create_user):
|
||||||
|
api_client.force_authenticate(user=create_user)
|
||||||
|
|
||||||
|
# 1. Upload mock file first using PUT request
|
||||||
|
upload_url = reverse('mock-upload') + '?key=test_key.pdf'
|
||||||
|
file_content = b'PDF content'
|
||||||
|
upload_resp = api_client.put(upload_url, data=file_content, content_type='application/octet-stream')
|
||||||
|
assert upload_resp.status_code == status.HTTP_200_OK
|
||||||
|
|
||||||
|
# 2. Download it
|
||||||
|
download_url = reverse('mock-download') + '?key=test_key.pdf'
|
||||||
|
download_resp = api_client.get(download_url)
|
||||||
|
assert download_resp.status_code == status.HTTP_200_OK
|
||||||
|
assert b"".join(download_resp.streaming_content) == file_content
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from rest_framework_simplejwt.views import TokenRefreshView
|
||||||
from .views import (
|
from .views import (
|
||||||
RegisterView, LoginView, LogoutView, VerifyOtpView, ProfileView,
|
RegisterView, LoginView, LogoutView, VerifyOtpView, ProfileView,
|
||||||
ProductViewSet, BulkUploadView, OrderViewSet, ReturnRequestViewSet,
|
ProductViewSet, BulkUploadView, OrderViewSet, ReturnRequestViewSet,
|
||||||
WalletView, WithdrawView, SubmitGstinView, PresignedUrlView, MockUploadView
|
WalletView, WithdrawView, SubmitGstinView, PresignedUrlView, MockUploadView, MockDownloadView
|
||||||
)
|
)
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
|
@ -22,6 +22,7 @@ urlpatterns = [
|
||||||
path('profile/submit-gstin/', SubmitGstinView.as_view(), name='submit-gstin'),
|
path('profile/submit-gstin/', SubmitGstinView.as_view(), name='submit-gstin'),
|
||||||
path('profile/presigned-url/', PresignedUrlView.as_view(), name='presigned-url'),
|
path('profile/presigned-url/', PresignedUrlView.as_view(), name='presigned-url'),
|
||||||
path('profile/mock-upload/', MockUploadView.as_view(), name='mock-upload'),
|
path('profile/mock-upload/', MockUploadView.as_view(), name='mock-upload'),
|
||||||
|
path('profile/mock-download/', MockDownloadView.as_view(), name='mock-download'),
|
||||||
path('products/bulk-upload/', BulkUploadView.as_view(), name='bulk-upload'),
|
path('products/bulk-upload/', BulkUploadView.as_view(), name='bulk-upload'),
|
||||||
path('wallet/', WalletView.as_view(), name='wallet'),
|
path('wallet/', WalletView.as_view(), name='wallet'),
|
||||||
path('wallet/withdraw/', WithdrawView.as_view(), name='withdraw'),
|
path('wallet/withdraw/', WithdrawView.as_view(), name='withdraw'),
|
||||||
|
|
|
||||||
43
api/views.py
43
api/views.py
|
|
@ -7,6 +7,7 @@ import io
|
||||||
import os
|
import os
|
||||||
import zipfile
|
import zipfile
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.http import FileResponse, Http404
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
|
|
@ -286,6 +287,48 @@ class MockUploadView(APIView):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return Response({"error": f"Failed to save file locally: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
return Response({"error": f"Failed to save file locally: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
||||||
|
def get_document_url(s3_key, request=None):
|
||||||
|
if not s3_key:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 1. CloudFront distribution
|
||||||
|
cloudfront_domain = getattr(settings, 'AWS_CLOUDFRONT_DOMAIN', None)
|
||||||
|
if cloudfront_domain:
|
||||||
|
domain = cloudfront_domain.replace('https://', '').replace('http://', '').strip('/')
|
||||||
|
return f"https://{domain}/{s3_key}"
|
||||||
|
|
||||||
|
# 2. AWS S3 fallback
|
||||||
|
aws_access = os.environ.get('AWS_ACCESS_KEY_ID') or os.environ.get('AWS_ACCESS_KEY')
|
||||||
|
aws_secret = os.environ.get('AWS_SECRET_ACCESS_KEY') or os.environ.get('AWS_SECRET_KEY')
|
||||||
|
bucket_name = os.environ.get('AWS_STORAGE_BUCKET_NAME') or os.environ.get('AWS_BUCKET_NAME') or 'betasupplierdocument-764709663363-ap-south-2-an'
|
||||||
|
region_name = os.environ.get('AWS_REGION', 'ap-south-2')
|
||||||
|
|
||||||
|
if aws_access and aws_secret:
|
||||||
|
return f"https://{bucket_name}.s3.{region_name}.amazonaws.com/{s3_key}"
|
||||||
|
|
||||||
|
# 3. Local Mock fallback
|
||||||
|
path = f"/api/profile/mock-download/?key={s3_key}"
|
||||||
|
if request:
|
||||||
|
return request.build_absolute_uri(path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
class MockDownloadView(APIView):
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
s3_key = request.query_params.get('key')
|
||||||
|
if not s3_key:
|
||||||
|
return Response({"error": "key query param is required"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
base_dir = os.environ.get('UPLOAD_MOUNT_DIR', '/home/ubuntu/mnt/s3files/supplierdocuments')
|
||||||
|
if not os.path.exists('/home/ubuntu/mnt/s3files/supplierdocuments') and not os.environ.get('UPLOAD_MOUNT_DIR'):
|
||||||
|
base_dir = os.path.join(settings.BASE_DIR, 'mnt/s3files/supplierdocuments')
|
||||||
|
|
||||||
|
file_path = os.path.join(base_dir, s3_key)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
return FileResponse(open(file_path, 'rb'))
|
||||||
|
raise Http404("File not found")
|
||||||
|
|
||||||
class PresignedUrlView(APIView):
|
class PresignedUrlView(APIView):
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -314,3 +314,7 @@ LOGGING = {
|
||||||
# Media files (uploads)
|
# Media files (uploads)
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||||
|
|
||||||
|
# CloudFront Domain Configuration
|
||||||
|
AWS_CLOUDFRONT_DOMAIN = os.environ.get('AWS_CLOUDFRONT_DOMAIN', '')
|
||||||
|
|
||||||
|
|
|
||||||
1
mnt/s3files/supplierdocuments/test_key.pdf
Normal file
1
mnt/s3files/supplierdocuments/test_key.pdf
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
PDF content
|
||||||
Loading…
Reference in a new issue