diff --git a/build.gradle b/build.gradle index d8aaad8..0a81b99 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ dependencies { testImplementation 'org.mockito:mockito-core:5.3.1' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation name: 'testlib-fat-0.3.0-all' + implementation name: 'testlib-fat-0.3.1-all' implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.12.0' diff --git a/libs/testlib-fat-0.3.0-all.jar b/libs/testlib-fat-0.3.1-all.jar similarity index 95% rename from libs/testlib-fat-0.3.0-all.jar rename to libs/testlib-fat-0.3.1-all.jar index 6d617f8..9b0a93c 100644 Binary files a/libs/testlib-fat-0.3.0-all.jar and b/libs/testlib-fat-0.3.1-all.jar differ diff --git a/src/main/java/ru/soune/nocopy/configuration/file/NoCopyInitializer.java b/src/main/java/ru/soune/nocopy/configuration/file/NoCopyInitializer.java index 39eef84..4129f19 100644 --- a/src/main/java/ru/soune/nocopy/configuration/file/NoCopyInitializer.java +++ b/src/main/java/ru/soune/nocopy/configuration/file/NoCopyInitializer.java @@ -4,16 +4,12 @@ import lombok.AllArgsConstructor; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import ru.soune.nocopy.service.file.FileProcessingOrchestrator; @Component @AllArgsConstructor public class NoCopyInitializer { - private final FileProcessingOrchestrator orchestrator; - @EventListener(ApplicationReadyEvent.class) public void initializeOnStartup() { - orchestrator.initializeProcessingQueue(); } } \ No newline at end of file diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileUploadRequest.java b/src/main/java/ru/soune/nocopy/dto/file/FileUploadRequest.java index 3b72bb4..030ef24 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileUploadRequest.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileUploadRequest.java @@ -28,4 +28,7 @@ public class FileUploadRequest { @JsonProperty("token") private String token; + + @JsonProperty("convertTo") + private String convertTo; } diff --git a/src/main/java/ru/soune/nocopy/entity/file/FileUploadSession.java b/src/main/java/ru/soune/nocopy/entity/file/FileUploadSession.java index 80dc12f..e2e93c8 100644 --- a/src/main/java/ru/soune/nocopy/entity/file/FileUploadSession.java +++ b/src/main/java/ru/soune/nocopy/entity/file/FileUploadSession.java @@ -75,6 +75,9 @@ public class FileUploadSession { @Column(name = "completed_at") private LocalDateTime completedAt; + @Column(name = "convert_to") + String convertTo; + @ElementCollection @CollectionTable( name = "uploaded_chunks", diff --git a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java index 67755c0..8bc41ff 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java @@ -111,6 +111,7 @@ public class FileUploadHandler implements RequestHandler { fileRequest.getFileName(), fileRequest.getFileType(), fileRequest.getExtension(), + fileRequest.getConvertTo(), fileRequest.getFileSize()); InitFileResponse fileResponse = InitFileResponse.builder() diff --git a/src/main/java/ru/soune/nocopy/repository/FileUploadSessionRepository.java b/src/main/java/ru/soune/nocopy/repository/FileUploadSessionRepository.java index 2b222a3..4bb6f6a 100644 --- a/src/main/java/ru/soune/nocopy/repository/FileUploadSessionRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/FileUploadSessionRepository.java @@ -27,4 +27,11 @@ public interface FileUploadSessionRepository extends JpaRepository findExpiredSessions( @Param("now") LocalDateTime now, @Param("statuses") Set statuses); + + + @Query("SELECT s FROM FileUploadSession s WHERE s.filePath = :filePath AND s.status = :status AND s.userId = :userId") + FileUploadSession findByFilePathAndStatus( + @Param("filePath") String filePath, + @Param("status") String status, + @Param("userId") Long userId); } diff --git a/src/main/java/ru/soune/nocopy/service/file/FileProcessingOrchestrator.java b/src/main/java/ru/soune/nocopy/service/file/FileProcessingOrchestrator.java deleted file mode 100644 index 7df7418..0000000 --- a/src/main/java/ru/soune/nocopy/service/file/FileProcessingOrchestrator.java +++ /dev/null @@ -1,55 +0,0 @@ -package ru.soune.nocopy.service.file; - -import com.vrt.NoCopyFileService; -import com.vrt.fileprotection.FileProtector; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import ru.soune.nocopy.entity.file.FileEntity; -import ru.soune.nocopy.entity.file.ProtectionStatus; -import ru.soune.nocopy.repository.FileEntityRepository; -import ru.soune.nocopy.util.FileUtil; - -import java.util.List; - -@Slf4j -@Service -@RequiredArgsConstructor -public class FileProcessingOrchestrator { - - @Lazy - private final NoCopyFileService noCopyFileService; - - private final FileEntityRepository fileRepository; - - private final FileUtil fileUtil; - - public void initializeProcessingQueue() { - List filesToProtect = fileRepository.findByProtectionStatus(ProtectionStatus.NOT_PROTECTED); - - for (FileEntity fileEntity : filesToProtect) { - try { - FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity); - - noCopyFileService.addFile(fileInfo); - - log.info("Add to query: {}", fileEntity.getOriginalFileName()); - } catch (Exception e) { - log.error("Fail add to query: {}", fileEntity.getId(), e); - } - } - } - - @Scheduled(fixedDelay = 120000) - public void checkNewFilesForProtection() { - List newFiles = fileRepository.findAllActiveFilesAndNotProtected(); - - for (FileEntity fileEntity : newFiles) { - FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity); - - noCopyFileService.addFile(fileInfo); - } - } -} diff --git a/src/main/java/ru/soune/nocopy/service/file/FileUploadService.java b/src/main/java/ru/soune/nocopy/service/file/FileUploadService.java index 1ae1a28..f1d94d3 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileUploadService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileUploadService.java @@ -9,7 +9,7 @@ import java.io.IOException; public interface FileUploadService { FileUploadSession initUpload(Long userId, String fileName, - String fileType, String extension, long fileSize); + String fileType, String extension, String convertTo, long fileSize); void handleExpiredSession(FileUploadSession session); diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/AudioLocalSearchImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/AudioLocalSearchImpl.java index dd47c08..7d2e137 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/AudioLocalSearchImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/AudioLocalSearchImpl.java @@ -28,7 +28,7 @@ public class AudioLocalSearchImpl implements AudioLocalSearch { if (fileEntity == null) return null; - return fileUtil.createFileInfo(fileEntity); + return fileUtil.createFileInfo(fileEntity, null); } @Override diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/DocumentLocalSearchImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/DocumentLocalSearchImpl.java index dea41ef..b7151d1 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/DocumentLocalSearchImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/DocumentLocalSearchImpl.java @@ -23,7 +23,7 @@ public class DocumentLocalSearchImpl implements DocumentLocalSearch { @Override public @Nullable FileProtector.FileInfo findByIds(@NotNull String ownerId, @NotNull String fileId) { FileEntity fileEntity = fileEntityRepository.findByIdAndUserId(fileId, Long.valueOf(ownerId)); - FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity); + FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity, null); log.info("fileInfo: {}", fileInfo); return fileInfo; diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java index 6d3131e..e601f8c 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/FileUploadServiceImpl.java @@ -107,7 +107,7 @@ public class FileUploadServiceImpl implements FileUploadService { @Override @Transactional public FileUploadSession initUpload(Long userId, String fileName, - String fileType, String extension, long fileSize) { + String fileType, String extension, String convertTo, long fileSize) { log.info("Initializing upload for user {}: {} ({} bytes, type: {})", userId, fileName, fileSize, fileType); @@ -138,6 +138,7 @@ public class FileUploadServiceImpl implements FileUploadService { .status(UploadStatus.INITIATED) .expiresAt(LocalDateTime.now().plusHours(sessionExpiryHours)) .retryCount(0) + .convertTo(convertTo) .build(); FileUploadSession savedSession = sessionRepository.save(session); @@ -248,7 +249,7 @@ public class FileUploadServiceImpl implements FileUploadService { cleanupSessionFiles(session); if (status != FileStatus.TEMP) { - FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity); + FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity, session.getConvertTo()); noCopyFileService.addFile(fileInfo); } @@ -615,76 +616,6 @@ public class FileUploadServiceImpl implements FileUploadService { } } - private void assembleFile(FileUploadSession session) throws IOException { - log.info("Starting file assembly for session: {} ({})", - session.getUploadId(), session.getFileName()); - - Path finalFilePath = null; - String checksum; - - try { - finalFilePath = prepareFinalFile(session); - log.info("Final file path: {}", finalFilePath); - - validateAllChunksExist(session); - - mergeChunksToFile(session, finalFilePath); - - validateFinalFile(session, finalFilePath); - - checksum = calculateChecksum(finalFilePath); - log.debug("File checksum calculated: {}", checksum); - - session.setFilePath(finalFilePath.toString()); - session.setChecksum(checksum); - session.setStatus(UploadStatus.COMPLETED); - session.setCompletedAt(LocalDateTime.now()); - - sessionRepository.save(session); - log.info("Upload session updated to COMPLETED: {}", session.getUploadId()); - - try { - FileEntity fileEntity = fileEntityService.createFromUploadSession(session, checksum); - - FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity); - - noCopyFileService.addFile(fileInfo); - - log.info("FileEntity successfully created for session: {}", - session.getUploadId()); - } catch (DuplicateImageException e) { - throw new DuplicateImageException("Duplicate", e.duplicateFileId(), e.userId()); - } catch (Exception e) { - log.error("Failed to create FileEntity for session {} (file uploaded, but metadata not saved): {}", - session.getUploadId(), e.getMessage()); - } - - cleanupSessionFiles(session); - - log.info("File assembly completed successfully: {} -> {} ({} bytes)", - session.getFileName(), finalFilePath, session.getFileSize()); - - } catch (Exception e) { - log.error("File assembly failed for session {}: {}", - session.getUploadId(), e.getMessage(), e); - - if (finalFilePath != null) { - try { - Files.deleteIfExists(finalFilePath); - log.debug("Cleaned up partial file: {}", finalFilePath); - } catch (IOException ioException) { - log.warn("Failed to cleanup partial file: {}", finalFilePath, ioException); - } - } - - session.setStatus(UploadStatus.FAILED); - session.setLastError(e.getMessage()); - sessionRepository.save(session); - - throw new IOException("File assembly failed: " + e.getMessage(), e); - } - } - private Path prepareFinalFile(FileUploadSession session) throws IOException { Path userUploadsDir = storageRoot.resolve("uploads") .resolve(String.valueOf(session.getUserId())) diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/ImageLocalSearchImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/ImageLocalSearchImpl.java index a6ea133..ea65c88 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/ImageLocalSearchImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/ImageLocalSearchImpl.java @@ -51,6 +51,6 @@ public class ImageLocalSearchImpl implements ImageLocalSearch { FileEntity file = fileEntityRepository.findByFileId(imageHashEntity.getFileId()); return new FileProtector.FileInfo(FileProtector.Type.IMAGE, imageHashEntity.getFileId(), - String.valueOf(file.getUserId())); + String.valueOf(file.getUserId()), null); } } diff --git a/src/main/java/ru/soune/nocopy/service/file/impl/ProtectionFileProviderImpl.java b/src/main/java/ru/soune/nocopy/service/file/impl/ProtectionFileProviderImpl.java index 9661001..304db80 100644 --- a/src/main/java/ru/soune/nocopy/service/file/impl/ProtectionFileProviderImpl.java +++ b/src/main/java/ru/soune/nocopy/service/file/impl/ProtectionFileProviderImpl.java @@ -7,7 +7,11 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.springframework.stereotype.Component; +import ru.soune.nocopy.entity.file.FileEntity; +import ru.soune.nocopy.repository.FileEntityRepository; +import ru.soune.nocopy.repository.FileUploadSessionRepository; import ru.soune.nocopy.service.file.FileEntityService; +import ru.soune.nocopy.service.file.FileUploadService; import java.io.File; import java.io.IOException; @@ -24,6 +28,10 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider { private final FileEntityService fileEntityService; + private final FileUploadSessionRepository fileUploadSessionRepository; + + private final FileEntityRepository fileEntityRepository; + @Nullable @Override public File getImageFile(@NotNull String id) { diff --git a/src/main/java/ru/soune/nocopy/util/FileUtil.java b/src/main/java/ru/soune/nocopy/util/FileUtil.java index 5e7b2bd..f53642b 100644 --- a/src/main/java/ru/soune/nocopy/util/FileUtil.java +++ b/src/main/java/ru/soune/nocopy/util/FileUtil.java @@ -13,17 +13,17 @@ public class FileUtil { public ImageLocalSearch.Result convertToResult(SimilarFileDTO response) { FileProtector.FileInfo fileInfo = new FileProtector.FileInfo(FileProtector.Type.IMAGE, response.getFileId(), - String.valueOf(response.getOwnerId())); + String.valueOf(response.getOwnerId()), null); ImageScore imageScore = new ImageScore(ImageScore.Rate.valueOf(response.getSimilarityLevel()), response.getHammingDistance()); return new ImageLocalSearch.Result(fileInfo, imageScore); } - public FileProtector.FileInfo createFileInfo(FileEntity fileEntity) { + public FileProtector.FileInfo createFileInfo(FileEntity fileEntity, String convertTo) { FileProtector.Type type = determineFileType(fileEntity.getMimeType()); - return new FileProtector.FileInfo(type, fileEntity.getId(), String.valueOf(fileEntity.getUserId())); + return new FileProtector.FileInfo(type, fileEntity.getId(), String.valueOf(fileEntity.getUserId()), convertTo); } public int hamming64(long aHi, long aLo, long bHi, long bLo) {