Add duplicate image check and delete endpoint
All checks were successful
build-and-deploy / build-deploy (push) Successful in 28s

This commit is contained in:
androidlover5842
2026-01-27 16:50:56 +05:30
parent 5bfbd295c9
commit cb1b65937e
3 changed files with 45 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
@@ -25,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile
import org.springframework.web.server.ResponseStatusException
import java.nio.file.Files
import java.nio.file.Paths
import java.security.MessageDigest
import java.util.UUID
@RestController
@@ -68,6 +70,10 @@ class RoomImages(
if (file.isEmpty) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "File is empty")
}
val contentHash = sha256Hex(file.bytes)
if (roomImageRepo.existsByRoomIdAndContentHash(roomId, contentHash)) {
throw ResponseStatusException(HttpStatus.CONFLICT, "Duplicate image for room")
}
val stored = try {
storage.store(propertyId, roomId, file)
} catch (ex: IllegalArgumentException) {
@@ -83,6 +89,7 @@ class RoomImages(
thumbnailPath = stored.thumbnailPath,
contentType = stored.contentType,
sizeBytes = stored.sizeBytes,
contentHash = contentHash,
roomTypeCode = room.roomType.code,
tags = tags?.toMutableSet() ?: mutableSetOf(),
roomSortOrder = nextRoomSortOrder,
@@ -91,6 +98,30 @@ class RoomImages(
return roomImageRepo.save(image).toResponse(publicBaseUrl)
}
@DeleteMapping("/{imageId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
fun delete(
@PathVariable propertyId: UUID,
@PathVariable roomId: UUID,
@PathVariable imageId: UUID,
@AuthenticationPrincipal principal: MyPrincipal?
) {
requirePrincipal(principal)
propertyAccess.requireMember(propertyId, principal!!.userId)
propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER)
ensureRoom(propertyId, roomId)
val image = roomImageRepo.findByIdAndRoomIdAndPropertyId(imageId, roomId, propertyId)
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Image not found")
try {
Files.deleteIfExists(Paths.get(image.originalPath))
Files.deleteIfExists(Paths.get(image.thumbnailPath))
} catch (ex: Exception) {
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to delete image files")
}
roomImageRepo.delete(image)
}
@GetMapping("/{imageId}/file")
fun file(
@PathVariable propertyId: UUID,
@@ -129,6 +160,16 @@ class RoomImages(
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
}
}
private fun sha256Hex(bytes: ByteArray): String {
val md = MessageDigest.getInstance("SHA-256")
val hash = md.digest(bytes)
val sb = StringBuilder(hash.size * 2)
for (b in hash) {
sb.append(String.format("%02x", b))
}
return sb.toString()
}
}
private fun RoomImage.toResponse(baseUrl: String): RoomImageResponse {