mirror of
https://github.com/wahyd4/links.git
synced 2026-08-09 05:06:16 +10:00
Please update collection list page to update the style of collection and make sure each collection has the same width and height regardless the image size
This commit is contained in:
Binary file not shown.
+73
-8
@@ -1,29 +1,51 @@
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from django.shortcuts import get_object_or_404
|
||||
from .models import ImageCollection, Image
|
||||
from .serializers import ImageCollectionSerializer, ImageSerializer
|
||||
from .storage import R2Storage
|
||||
import uuid
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ImageCollectionViewSet(viewsets.ModelViewSet):
|
||||
queryset = ImageCollection.objects.all()
|
||||
serializer_class = ImageCollectionSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Create a new collection"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(
|
||||
{
|
||||
'status': 'success',
|
||||
'message': 'Collection created successfully',
|
||||
'data': serializer.data
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
headers=headers
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def upload_images(self, request, pk=None):
|
||||
"""API endpoint to upload images to a collection"""
|
||||
"""Upload images to a collection"""
|
||||
collection = self.get_object()
|
||||
files = request.FILES.getlist('file')
|
||||
storage = R2Storage()
|
||||
|
||||
logger.debug(f"Processing upload request for collection {collection.id}")
|
||||
logger.debug(f"Number of files: {len(files)}")
|
||||
|
||||
uploaded_images = []
|
||||
for file in files:
|
||||
try:
|
||||
logger.info(os.environ.get('R2_ENDPOINT_URL'))
|
||||
logger.debug(f"Processing file: {file.name}")
|
||||
logger.debug(f"File size: {file.size}")
|
||||
logger.debug(f"Content type: {file.content_type}")
|
||||
|
||||
# Generate unique file key
|
||||
file_key = f"images/{collection.id}/{uuid.uuid4()}/{file.name}"
|
||||
@@ -51,25 +73,68 @@ class ImageCollectionViewSet(viewsets.ModelViewSet):
|
||||
size=file.size
|
||||
)
|
||||
|
||||
logger.debug(f"Created image record: {image.id}")
|
||||
uploaded_images.append(ImageSerializer(image).data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Upload failed for {file.name}", exc_info=True)
|
||||
return Response({
|
||||
'error': str(e)
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response(uploaded_images, status=status.HTTP_201_CREATED)
|
||||
return Response({
|
||||
'status': 'success',
|
||||
'message': f'Successfully uploaded {len(uploaded_images)} images',
|
||||
'data': uploaded_images
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
|
||||
@action(detail=True, methods=['delete'])
|
||||
def delete_image(self, request, pk=None):
|
||||
"""Delete an image from a collection"""
|
||||
collection = self.get_object()
|
||||
image_id = request.data.get('image_id')
|
||||
|
||||
try:
|
||||
image = collection.images.get(id=image_id)
|
||||
storage = R2Storage()
|
||||
|
||||
# Delete from storage
|
||||
if image.file_key:
|
||||
try:
|
||||
storage.delete_file(image.file_key)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete from storage: {e}")
|
||||
|
||||
# Delete from database
|
||||
image.delete()
|
||||
|
||||
return Response({
|
||||
'status': 'success',
|
||||
'message': 'Image deleted successfully'
|
||||
})
|
||||
|
||||
except Image.DoesNotExist:
|
||||
return Response({
|
||||
'status': 'error',
|
||||
'message': 'Image not found'
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
except Exception as e:
|
||||
return Response({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ImageViewSet(viewsets.ModelViewSet):
|
||||
queryset = Image.objects.all()
|
||||
serializer_class = ImageSerializer
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
# Delete from R2 before deleting record
|
||||
"""Delete image from storage when deleting record"""
|
||||
storage = R2Storage()
|
||||
try:
|
||||
storage.delete_file(instance.file_key)
|
||||
if instance.file_key:
|
||||
storage.delete_file(instance.file_key)
|
||||
except Exception as e:
|
||||
pass # Continue with deletion even if R2 delete fails
|
||||
logger.error(f"Failed to delete from storage: {e}")
|
||||
instance.delete()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
class="object-cover w-full h-full">
|
||||
</div>
|
||||
{% empty %}
|
||||
<div class="col-span-2 flex flex-col items-center justify-center h-full bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg border-2 border-dashed border-gray-200">
|
||||
<div class="col-span-2 flex flex-col items-center justify-center h-full bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg">
|
||||
<svg class="w-8 h-8 sm:w-12 sm:h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
|
||||
@@ -213,6 +213,145 @@ curl -X POST http://localhost:8000/api/newsletters \
|
||||
}'
|
||||
```
|
||||
|
||||
## Collection API Usage
|
||||
|
||||
GoLinks provides RESTful APIs for managing image collections. Here are the available endpoints and their usage:
|
||||
|
||||
### List Collections
|
||||
|
||||
Retrieve a paginated list of all collections:
|
||||
```
|
||||
GET /api/collections
|
||||
GET /api/collections?page=2
|
||||
GET /api/collections?page_size=20
|
||||
```
|
||||
Response example:
|
||||
```json
|
||||
{
|
||||
"count": 10,
|
||||
"next": "http://localhost:8000/api/collections?page=2",
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "My Collection",
|
||||
"description": "A collection of images",
|
||||
"image_count": 5,
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
// ... more collections
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Create a Collection
|
||||
|
||||
Create a new image collection:
|
||||
```
|
||||
POST /api/collections
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "My Collection",
|
||||
"description": "A collection of images"
|
||||
}
|
||||
```
|
||||
Response example:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Collection created successfully",
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "My Collection",
|
||||
"description": "A collection of images",
|
||||
"image_count": 0,
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Upload Images to Collection
|
||||
|
||||
Upload one or multiple images to a collection:
|
||||
```
|
||||
POST /api/collections/{collection_id}/upload_images
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: image1.jpg
|
||||
file: image2.jpg
|
||||
```
|
||||
Response example:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Successfully uploaded 2 images",
|
||||
"data": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"title": "image1.jpg",
|
||||
"content_type": "image/jpeg",
|
||||
"size": 1024000,
|
||||
"url": "https://your-r2-domain.com/images/...",
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"title": "image2.jpg",
|
||||
"content_type": "image/jpeg",
|
||||
"size": 2048000,
|
||||
"url": "https://your-r2-domain.com/images/...",
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Delete an Image
|
||||
|
||||
Delete a specific image from a collection:
|
||||
```
|
||||
DELETE /api/images/{image_id}
|
||||
```
|
||||
Response example:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Image deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Delete a Collection
|
||||
|
||||
Delete a collection and all its images:
|
||||
```
|
||||
DELETE /api/collections/{collection_id}
|
||||
```
|
||||
Response example:
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Collection deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Error Responses
|
||||
|
||||
In case of errors, the API will return appropriate status codes and error messages:
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Error description here"
|
||||
}
|
||||
```
|
||||
|
||||
Common status codes:
|
||||
- 200: Success
|
||||
- 201: Created
|
||||
- 400: Bad Request
|
||||
- 404: Not Found
|
||||
- 500: Internal Server Error
|
||||
|
||||
{% endfilter %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user