diff --git a/delivery/controller/device_details.go b/delivery/controller/device_details.go index b4de611..d462313 100644 --- a/delivery/controller/device_details.go +++ b/delivery/controller/device_details.go @@ -48,9 +48,52 @@ func (c *DeviceDetailsController) Route() { deviceDetails.PUT("/:id/bulk-update-customers-by-port", c.bulkUpdateCustomersByPort) // Updated endpoint name 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) { id := ctx.Param("id") deviceID, err := uuid.Parse(id) diff --git a/model/dto/req/deviceDetails.go b/model/dto/req/deviceDetails.go index dd8f521..ade3702 100644 --- a/model/dto/req/deviceDetails.go +++ b/model/dto/req/deviceDetails.go @@ -44,4 +44,8 @@ type BulkUpdateCustomersByPortDTO struct { type RemoveCustomerByPortDTO struct { PortNumber int `json:"port_number" binding:"required,min=1"` +} + +type DeleteImageDTO struct { + Filename string `json:"filename" validate:"required"` } \ No newline at end of file diff --git a/usecase/device_details.go b/usecase/device_details.go index face209..b8ea36c 100644 --- a/usecase/device_details.go +++ b/usecase/device_details.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "mime/multipart" + "strings" "time" "users_management/m/model/dto/req" "users_management/m/model/dto/res" @@ -34,6 +35,7 @@ type DeviceDetailsUseCase interface { BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error UpdateDeviceDetailsWithMultipleImages(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error + DeleteDeviceImage(deviceID uuid.UUID, filename string) error } 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 { err := u.validate.Struct(deviceDTO) if err != nil {