From 5700adafb2b017fe81a59f9dbd877074684cade8 Mon Sep 17 00:00:00 2001 From: vladp Date: Tue, 27 Jan 2026 08:09:54 +0700 Subject: [PATCH] NCBACK-33 --- .../nocopy/controller/ApiController.java | 8 +-- .../controller/UserContentController.java | 11 ---- .../nocopy/controller/UserController.java | 59 +++++++++++++++++++ .../nocopy/entity/file/ImageProtection.java | 0 .../ru/soune/nocopy/entity/user/User.java | 1 - .../nocopy/handler/FileEntityHandler.java | 14 ++++- .../handler/ImageFoundRequestHandler.java | 4 +- .../service/file/FileEntityService.java | 33 +++++++++-- .../GoogleVisionSearchService.java | 2 +- 9 files changed, 103 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/ru/soune/nocopy/controller/UserContentController.java delete mode 100644 src/main/java/ru/soune/nocopy/entity/file/ImageProtection.java rename src/main/java/ru/soune/nocopy/service/{ => search}/GoogleVisionSearchService.java (99%) diff --git a/src/main/java/ru/soune/nocopy/controller/ApiController.java b/src/main/java/ru/soune/nocopy/controller/ApiController.java index d1799fc..7cf63c3 100644 --- a/src/main/java/ru/soune/nocopy/controller/ApiController.java +++ b/src/main/java/ru/soune/nocopy/controller/ApiController.java @@ -114,7 +114,7 @@ public class ApiController { @RequestParam(value = "upload_id", required = false) String uploadId, @RequestParam(value = "chunk_number", required = false) Integer chunkNumber, @RequestParam(value = "chunk", required = false) MultipartFile chunk, - @RequestParam(value = "findSimilar", required = false) Integer findSimilar) { + @RequestParam(value = "findSimilar", required = false, defaultValue = "0") Integer findSimilar) { try { if (chunk == null || chunk.isEmpty()) { return buildErrorResponse(uploadId, chunkNumber, "Chunk file null or empty"); @@ -153,11 +153,11 @@ public class ApiController { } } - @GetMapping("/v{version}/files/{fileId}/similar/{authToken}") + @GetMapping("/v{version}/files/{fileId}/similar") public ResponseEntity findSimilarFiles( @PathVariable("version") int version, @PathVariable String fileId, - @PathVariable(required = false) String authToken, + @RequestParam(value = "auth_token",required = false) String authToken, @RequestParam(required = false) List similarityLevels, @PageableDefault(size = 20, sort = "hammingDistance") Pageable pageable) { SimilarityFilter filter = SimilarityFilter.builder() @@ -443,7 +443,7 @@ public class ApiController { throws IOException { fileEntityService.deleteFromDisk(fileEntity); - fileEntityService.markAsDeleted(fileEntity); + fileEntityService.softDeleteFileWithHash(fileEntity); Optional originalFile = fileEntityRepository.findById(similarFiles.get(0).getFileId()); diff --git a/src/main/java/ru/soune/nocopy/controller/UserContentController.java b/src/main/java/ru/soune/nocopy/controller/UserContentController.java deleted file mode 100644 index 51a4d5c..0000000 --- a/src/main/java/ru/soune/nocopy/controller/UserContentController.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.soune.nocopy.controller; - -import lombok.AllArgsConstructor; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("v1/api/content") -@AllArgsConstructor -public class UserContentController { -} diff --git a/src/main/java/ru/soune/nocopy/controller/UserController.java b/src/main/java/ru/soune/nocopy/controller/UserController.java index eb12863..f4f0ffd 100644 --- a/src/main/java/ru/soune/nocopy/controller/UserController.java +++ b/src/main/java/ru/soune/nocopy/controller/UserController.java @@ -2,20 +2,29 @@ package ru.soune.nocopy.controller; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; +import ru.soune.nocopy.dto.BaseResponse; +import ru.soune.nocopy.dto.MessageCode; import ru.soune.nocopy.dto.register.ChangePasswordRequest; +import ru.soune.nocopy.dto.register.RegAnswer; +import ru.soune.nocopy.dto.register.RegRequest; import ru.soune.nocopy.dto.user.UserDTO; import ru.soune.nocopy.dto.user.UserRequest; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.InvalidUserEmail; import ru.soune.nocopy.exception.NotFoundAuthToken; +import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.mapper.UserMapper; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.UserRepository; +import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.user.UserService; +import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Optional; @RestController @@ -31,6 +40,10 @@ public class UserController { private final UserMapper userMapper; + private final PasswordEncoder passwordEncoder; + + private final AuthService authService; + @GetMapping("/all") public ResponseEntity> getAllUsers() { List allUsers = userRepository.findAll().stream() @@ -108,4 +121,50 @@ public class UserController { return ResponseEntity.ok(userService.updateUser(userRequest, user)); } + + @PostMapping("/create-user") + public ResponseEntity addUser(@RequestBody RegRequest registerRequest) { + + if (userRepository.existsByEmail(registerRequest.getEmail()) || + userRepository.existsByPhone(registerRequest.getPhone())) { + RegAnswer regAnswer = new RegAnswer(); + regAnswer.setFieldErrors(Arrays.asList(Map.of("email", registerRequest.getEmail()))); + regAnswer.setFieldErrors(Arrays.asList(Map.of("phone", registerRequest.getPhone()))); + + throw new NotValidFieldException("User already exists with email:" + registerRequest.getEmail() + " or phone: " + + registerRequest.getPhone(), new BaseResponse(2, + MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getCode(), + MessageCode.REG_EMAIL_OR_PHONE_EXISTS.getDescription(), regAnswer)); + } + + User user = new User(); + user.setFullName(registerRequest.getFullName()); + user.setEmail(registerRequest.getEmail()); + user.setPassword(passwordEncoder.encode(registerRequest.getPassword())); + user.setActive(true); + user.setEmailVerified(true); + + if (registerRequest.getCompanyName() != null) { + user.setCompany(registerRequest.getCompanyName()); + } + + if (registerRequest.getPhone() != null) { + user.setPhone(registerRequest.getPhone()); + } + + User savedUser = userRepository.save(user); + + AuthToken authToken = authService.generateAuthToken(savedUser); + + authTokenRepository.save(authToken); + + return ResponseEntity.ok().body(savedUser); + } + + @DeleteMapping("/delete-user/{userId}") + public ResponseEntity addUser(@PathVariable Long userId) { + userRepository.deleteById(userId); + return ResponseEntity.ok().body(Map.of("userId", userId, "deleted", "true")); + } + } diff --git a/src/main/java/ru/soune/nocopy/entity/file/ImageProtection.java b/src/main/java/ru/soune/nocopy/entity/file/ImageProtection.java deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/java/ru/soune/nocopy/entity/user/User.java b/src/main/java/ru/soune/nocopy/entity/user/User.java index d6a1867..6346c0d 100644 --- a/src/main/java/ru/soune/nocopy/entity/user/User.java +++ b/src/main/java/ru/soune/nocopy/entity/user/User.java @@ -6,7 +6,6 @@ import jakarta.validation.constraints.Size; import lombok.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import ru.soune.nocopy.entity.file.ImageProtection; import ru.soune.nocopy.entity.file.Violation; import java.time.LocalDate; diff --git a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java index 5603581..2d6fcdf 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java @@ -6,12 +6,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.file.*; -import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.exception.FileEntityNotFoundException; import ru.soune.nocopy.exception.NotFoundAuthToken; import ru.soune.nocopy.repository.FileEntityRepository; +import ru.soune.nocopy.repository.ImageHashRepository; import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; @@ -34,6 +34,8 @@ public class FileEntityHandler implements RequestHandler { private final FileEntityRepository fileEntityRepository; + private final ImageHashRepository imageHashRepository; + @Override public BaseResponse handle(BaseRequest request) { try { @@ -273,15 +275,21 @@ public class FileEntityHandler implements RequestHandler { FileEntity fileEntity = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); + int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete(); - if (fileEntity.getStatus().equals(FileStatus.DELETED) || fileRequest.getFullDelete() == 1) { + if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) { fileEntityService.deleteFromDisk(fileEntity); response = DeleteFileResponse.builder() .fileId(fileRequest.getFileId()) .message("File deleted from disk") .build(); } else { - fileEntityService.markAsDeleted(fileEntity); + fileEntityService.softDeleteFileWithHash(fileEntity); +// +// imageHashRepository.deleteByFileId(fileEntity.getId()); +// +// fileEntityService.markAsDeleted(fileEntity); + response = DeleteFileResponse.builder() .fileId(fileRequest.getFileId()) .message("File marked as deleted") diff --git a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java index 79d6ac6..e82ca62 100644 --- a/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/ImageFoundRequestHandler.java @@ -7,12 +7,10 @@ import org.springframework.stereotype.Component; import ru.soune.nocopy.dto.BaseRequest; import ru.soune.nocopy.dto.BaseResponse; import ru.soune.nocopy.dto.MessageCode; -import ru.soune.nocopy.dto.file.GoogleVisionSearchResponse; import ru.soune.nocopy.dto.file.ImageSearchRequest; import ru.soune.nocopy.dto.file.YandexSearchResponse; import ru.soune.nocopy.service.search.YandexSearchService; -import ru.soune.nocopy.service.GoogleVisionSearchService; -import ru.soune.nocopy.service.YandexSearchService; +import ru.soune.nocopy.service.search.GoogleVisionSearchService; @Slf4j @Component diff --git a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java index fd4c8bc..6aa130a 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java @@ -13,6 +13,7 @@ import ru.soune.nocopy.entity.file.ProtectionStatus; import ru.soune.nocopy.exception.DuplicateImageException; import ru.soune.nocopy.exception.FileEntityNotFoundException; import ru.soune.nocopy.repository.FileEntityRepository; +import ru.soune.nocopy.repository.ImageHashRepository; import ru.soune.nocopy.repository.SimilarImageProjection; import ru.soune.nocopy.service.FileSimilarityService; import ru.soune.nocopy.service.ImageHashService; @@ -38,6 +39,8 @@ public class FileEntityService { private final FileSimilarityService fileSimilarityService; + private final ImageHashRepository imageHashRepository; + @Transactional(noRollbackFor = DuplicateImageException.class) public FileEntity createFromUploadSession(FileUploadSession session, String checksum) { log.info("Creating FileEntity for upload session: {}", session.getUploadId()); @@ -185,27 +188,45 @@ public class FileEntityService { } @Transactional - public void markAsDeleted(FileEntity fileEntity) { + public FileEntity markAsDeleted(FileEntity fileEntity) throws IOException { fileEntity.setStatus(FileStatus.DELETED); fileEntity.setUpdatedAt(LocalDateTime.now()); + fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED); + + Path path = Paths.get(fileEntity.getProtectedFilePath()); + Files.deleteIfExists(path); + fileEntity.setProtectedFilePath(""); + + return fileEntityRepository.save(fileEntity); + } + + public void softDeleteFileWithHash(FileEntity fileEntity) throws IOException { + if (fileEntity.getImageHash() != null) { + fileEntity.setImageHash(null); + } + + fileEntity.setStatus(FileStatus.DELETED); + fileEntity.setUpdatedAt(LocalDateTime.now()); + fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED); + + Path path = Paths.get(fileEntity.getProtectedFilePath()); + Files.deleteIfExists(path); + fileEntity.setProtectedFilePath(""); fileEntityRepository.save(fileEntity); } @Transactional - public boolean deleteFromDisk(FileEntity fileEntity) throws IOException { + public void deleteFromDisk(FileEntity fileEntity) throws IOException { Path path = Paths.get(fileEntity.getFilePath()); if (!Files.exists(path)) { - return true; + return; } Files.delete(path); -// markAsDeleted(fileEntity); fileEntityRepository.delete(fileEntity); - - return true; } @Transactional(readOnly = true) diff --git a/src/main/java/ru/soune/nocopy/service/GoogleVisionSearchService.java b/src/main/java/ru/soune/nocopy/service/search/GoogleVisionSearchService.java similarity index 99% rename from src/main/java/ru/soune/nocopy/service/GoogleVisionSearchService.java rename to src/main/java/ru/soune/nocopy/service/search/GoogleVisionSearchService.java index f0829a1..4e3e997 100644 --- a/src/main/java/ru/soune/nocopy/service/GoogleVisionSearchService.java +++ b/src/main/java/ru/soune/nocopy/service/search/GoogleVisionSearchService.java @@ -1,4 +1,4 @@ -package ru.soune.nocopy.service; +package ru.soune.nocopy.service.search; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper;