Reject duplicate guest documents by hash
All checks were successful
build-and-deploy / build-deploy (push) Successful in 31s

This commit is contained in:
androidlover5842
2026-01-31 00:19:58 +05:30
parent bb2c40ed71
commit 0c32ff4102
4 changed files with 85 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
package com.android.trisolarisserver.config
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
@Component
class GuestDocumentSchemaFix(
private val jdbcTemplate: JdbcTemplate
) : PostgresSchemaFix(jdbcTemplate) {
override fun runPostgres(jdbcTemplate: JdbcTemplate) {
val hasTable = jdbcTemplate.queryForObject(
"""
select count(*)
from information_schema.tables
where table_name = 'guest_document'
""".trimIndent(),
Int::class.java
) ?: 0
if (hasTable == 0) return
val hasHash = jdbcTemplate.queryForObject(
"""
select count(*)
from information_schema.columns
where table_name = 'guest_document'
and column_name = 'file_hash'
""".trimIndent(),
Int::class.java
) ?: 0
if (hasHash == 0) {
logger.info("Adding file_hash to guest_document table")
jdbcTemplate.execute(
"""
alter table guest_document
add column file_hash varchar
""".trimIndent()
)
}
}
}

View File

@@ -28,6 +28,7 @@ import java.time.OffsetDateTime
import java.nio.file.Files
import java.nio.file.Paths
import java.util.UUID
import java.security.MessageDigest
@RestController
@RequestMapping("/properties/{propertyId}/guests/{guestId}/documents")
@@ -82,6 +83,17 @@ class GuestDocuments(
}
val stored = storage.store(propertyId, guestId, bookingId, file)
val fileHash = hashFile(stored.storagePath)
if (fileHash != null && guestDocumentRepo.existsByPropertyIdAndGuestIdAndBookingIdAndFileHash(
propertyId,
guestId,
bookingId,
fileHash
)
) {
Files.deleteIfExists(Paths.get(stored.storagePath))
throw ResponseStatusException(HttpStatus.CONFLICT, "Duplicate document")
}
val document = GuestDocument(
property = property,
guest = guest,
@@ -90,7 +102,8 @@ class GuestDocuments(
originalFilename = stored.originalFilename,
contentType = stored.contentType,
sizeBytes = stored.sizeBytes,
storagePath = stored.storagePath
storagePath = stored.storagePath,
fileHash = fileHash
)
val saved = guestDocumentRepo.save(document)
runExtraction(saved.id!!, propertyId, guestId)
@@ -236,6 +249,27 @@ class GuestDocuments(
}
}
private fun hashFile(storagePath: String): String? {
return try {
val path = Paths.get(storagePath)
if (!Files.exists(path)) return null
val digest = MessageDigest.getInstance("SHA-256")
Files.newInputStream(path).use { input ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var read = input.read(buffer)
while (read >= 0) {
if (read > 0) {
digest.update(buffer, 0, read)
}
read = input.read(buffer)
}
}
digest.digest().joinToString("") { "%02x".format(it) }
} catch (_: Exception) {
null
}
}
}
data class GuestDocumentResponse(

View File

@@ -8,4 +8,10 @@ interface GuestDocumentRepo : JpaRepository<GuestDocument, UUID> {
fun findByPropertyIdAndGuestIdOrderByUploadedAtDesc(propertyId: UUID, guestId: UUID): List<GuestDocument>
fun findByIdAndPropertyIdAndGuestId(id: UUID, propertyId: UUID, guestId: UUID): GuestDocument?
fun existsByGuestId(guestId: UUID): Boolean
fun existsByPropertyIdAndGuestIdAndBookingIdAndFileHash(
propertyId: UUID,
guestId: UUID,
bookingId: UUID,
fileHash: String
): Boolean
}

View File

@@ -44,6 +44,9 @@ class GuestDocument(
@Column(name = "storage_path", nullable = false)
var storagePath: String,
@Column(name = "file_hash")
var fileHash: String? = null,
@Column(name = "extracted_data", columnDefinition = "jsonb")
@JdbcTypeCode(SqlTypes.JSON)
var extractedData: String? = null,