Merge pull request 'adding delete method' (#20) from feature/responses-v2 into dev

Reviewed-on: winter-access/backend_nam#20
This commit is contained in:
areeqakbr 2025-06-16 08:39:58 +00:00
commit c90d3ea345
3 changed files with 115 additions and 0 deletions

View File

@ -48,9 +48,52 @@ func (c *DeviceDetailsController) Route() {
deviceDetails.PUT("/:id/bulk-update-customers-by-port", c.bulkUpdateCustomersByPort) // Updated endpoint name deviceDetails.PUT("/:id/bulk-update-customers-by-port", c.bulkUpdateCustomersByPort) // Updated endpoint name
deviceDetails.DELETE("/:id/remove-customer-by-port", c.removeCustomerByPort) deviceDetails.DELETE("/:id/remove-customer-by-port", c.removeCustomerByPort)
// delete images by filename
deviceDetails.DELETE("/:id/images/:filename", c.deleteDeviceImage)
} }
} }
func (c *DeviceDetailsController) deleteDeviceImage(ctx *gin.Context) {
// Parse device ID
id := ctx.Param("id")
deviceID, err := uuid.Parse(id)
if err != nil {
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
return
}
// Get filename from URL parameter
filename := ctx.Param("filename")
if filename == "" {
common.ErrorResponses(ctx, http.StatusBadRequest, "Filename is required")
return
}
// Validate filename format (basic security check)
if strings.Contains(filename, "..") || strings.Contains(filename, "/") || strings.Contains(filename, "\\") {
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid filename format")
return
}
// Call use case to delete the image
err = c.deviceDetailsUC.DeleteDeviceImage(deviceID, filename)
if err != nil {
if err.Error() == "device not found" {
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
return
}
if err.Error() == "image not found in device" {
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
return
}
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
return
}
common.SingleResponses(ctx, fmt.Sprintf("Image %s deleted successfully", filename), nil)
}
func (c *DeviceDetailsController) updateCustomerByPort(ctx *gin.Context) { func (c *DeviceDetailsController) updateCustomerByPort(ctx *gin.Context) {
id := ctx.Param("id") id := ctx.Param("id")
deviceID, err := uuid.Parse(id) deviceID, err := uuid.Parse(id)

View File

@ -44,4 +44,8 @@ type BulkUpdateCustomersByPortDTO struct {
type RemoveCustomerByPortDTO struct { type RemoveCustomerByPortDTO struct {
PortNumber int `json:"port_number" binding:"required,min=1"` PortNumber int `json:"port_number" binding:"required,min=1"`
}
type DeleteImageDTO struct {
Filename string `json:"filename" validate:"required"`
} }

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"mime/multipart" "mime/multipart"
"strings"
"time" "time"
"users_management/m/model/dto/req" "users_management/m/model/dto/req"
"users_management/m/model/dto/res" "users_management/m/model/dto/res"
@ -34,6 +35,7 @@ type DeviceDetailsUseCase interface {
BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error
RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error
UpdateDeviceDetailsWithMultipleImages(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error UpdateDeviceDetailsWithMultipleImages(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error
DeleteDeviceImage(deviceID uuid.UUID, filename string) error
} }
type deviceDetailsUseCase struct { type deviceDetailsUseCase struct {
@ -50,6 +52,72 @@ func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, geo
} }
} }
func (u *deviceDetailsUseCase) DeleteDeviceImage(deviceID uuid.UUID, filename string) error {
// Get current device
currentDevice, err := u.deviceDetailsRepo.GetByID(deviceID)
if err != nil {
return fmt.Errorf("device not found")
}
// Get all current image URLs
allImageURLs := currentDevice.GetAllImageURLs()
// Find the image URL that contains the filename
var imageURLToDelete string
var updatedImageURLs []string
for _, imageURL := range allImageURLs {
if strings.Contains(imageURL, filename) {
imageURLToDelete = imageURL
} else {
updatedImageURLs = append(updatedImageURLs, imageURL)
}
}
// Check if image was found
if imageURLToDelete == "" {
return fmt.Errorf("image not found in device")
}
// Don't allow deleting the last image if it's the primary image
if len(allImageURLs) == 1 && currentDevice.ImageURL != nil && *currentDevice.ImageURL == imageURLToDelete {
return fmt.Errorf("cannot delete the only remaining image")
}
// Prepare updates
updates := map[string]interface{}{
"updated_at": time.Now(),
}
if len(updatedImageURLs) == 0 {
// No images left
updates["image_url"] = nil
updates["image_urls"] = entity.StringSlice([]string{})
} else {
// Update primary image if it was deleted
if currentDevice.ImageURL != nil && *currentDevice.ImageURL == imageURLToDelete {
updates["image_url"] = updatedImageURLs[0] // Set first remaining image as primary
}
// Update all images
updates["image_urls"] = entity.StringSlice(updatedImageURLs)
}
// Update database
err = u.deviceDetailsRepo.Update(deviceID, updates)
if err != nil {
return fmt.Errorf("failed to update device in database: %w", err)
}
// Delete the actual file from filesystem
err = helper.DeleteDeviceImage(imageURLToDelete)
if err != nil {
// Log error but don't fail the request since database was already updated
fmt.Printf("Warning: Failed to delete image file %s: %v\n", imageURLToDelete, err)
}
return nil
}
func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error { func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error {
err := u.validate.Struct(deviceDTO) err := u.validate.Struct(deviceDTO)
if err != nil { if err != nil {