2025-12-18 01:13:19 +07:00
|
|
|
package ru.soune.nocopy.handler;
|
|
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
import ru.soune.nocopy.dto.*;
|
|
|
|
|
import ru.soune.nocopy.dto.file.*;
|
2025-12-26 16:23:17 +07:00
|
|
|
import ru.soune.nocopy.entity.file.FileEntity;
|
|
|
|
|
import ru.soune.nocopy.entity.file.FileStatus;
|
2026-01-28 21:18:42 +07:00
|
|
|
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
2025-12-26 16:23:17 +07:00
|
|
|
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
2025-12-18 01:13:19 +07:00
|
|
|
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
2025-12-26 16:23:17 +07:00
|
|
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
2026-01-21 12:02:38 +07:00
|
|
|
import ru.soune.nocopy.service.register.AuthService;
|
2025-12-18 01:13:19 +07:00
|
|
|
import ru.soune.nocopy.service.file.FileEntityService;
|
2026-01-06 12:50:39 +07:00
|
|
|
import ru.soune.nocopy.service.file.FileStatsService;
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
import java.util.Arrays;
|
2025-12-26 13:52:53 +07:00
|
|
|
import java.util.List;
|
2026-01-28 21:18:42 +07:00
|
|
|
import java.util.Optional;
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
@Component
|
|
|
|
|
@RequiredArgsConstructor
|
|
|
|
|
public class FileEntityHandler implements RequestHandler {
|
2026-01-06 12:50:39 +07:00
|
|
|
|
2025-12-18 01:13:19 +07:00
|
|
|
private final FileEntityService fileEntityService;
|
2025-12-26 16:23:17 +07:00
|
|
|
|
2026-01-05 16:26:46 +07:00
|
|
|
private final AuthService authService;
|
2026-01-06 12:50:39 +07:00
|
|
|
|
|
|
|
|
private final FileStatsService fileStatsService;
|
2025-12-26 16:23:17 +07:00
|
|
|
|
2025-12-18 01:13:19 +07:00
|
|
|
private final ObjectMapper objectMapper;
|
|
|
|
|
|
2025-12-26 16:23:17 +07:00
|
|
|
private final FileEntityRepository fileEntityRepository;
|
|
|
|
|
|
2025-12-18 01:13:19 +07:00
|
|
|
@Override
|
|
|
|
|
public BaseResponse handle(BaseRequest request) {
|
|
|
|
|
try {
|
|
|
|
|
FileEntityRequest fileRequest = objectMapper.convertValue(
|
|
|
|
|
request.getMessageBody(), FileEntityRequest.class);
|
|
|
|
|
|
|
|
|
|
String action = fileRequest.getAction();
|
|
|
|
|
|
|
|
|
|
switch (action) {
|
2025-12-24 17:26:13 +07:00
|
|
|
case "file_info":
|
2025-12-18 01:13:19 +07:00
|
|
|
return handleGetFileInfo(request, fileRequest);
|
2026-01-06 12:50:39 +07:00
|
|
|
case "user_files_info":
|
|
|
|
|
return handleGetFilesUserInfo(request, fileRequest);
|
2025-12-24 17:26:13 +07:00
|
|
|
case "file_by_session":
|
2025-12-18 01:13:19 +07:00
|
|
|
return handleGetFileBySession(request, fileRequest);
|
2025-12-24 17:26:13 +07:00
|
|
|
case "user_files":
|
2025-12-18 01:13:19 +07:00
|
|
|
return handleGetUserFiles(request, fileRequest);
|
|
|
|
|
case "search_files":
|
|
|
|
|
return handleSearchFiles(request, fileRequest);
|
2025-12-24 17:26:13 +07:00
|
|
|
case "storage_usage":
|
2025-12-18 01:13:19 +07:00
|
|
|
return handleGetStorageUsage(request, fileRequest);
|
2026-01-28 21:18:42 +07:00
|
|
|
case "check_protected":
|
|
|
|
|
return handleProtectFile(request, fileRequest);
|
2025-12-18 01:13:19 +07:00
|
|
|
case "delete_file":
|
|
|
|
|
return handleDeleteFile(request, fileRequest);
|
|
|
|
|
default:
|
|
|
|
|
ActionResponse response = ActionResponse.builder()
|
|
|
|
|
.action(action)
|
|
|
|
|
.availableActions(Arrays.asList(
|
2025-12-24 17:26:13 +07:00
|
|
|
"file_info", "file_by_session", "user_files",
|
2026-01-09 23:05:37 +07:00
|
|
|
"search_files", "storage_usage", "delete_file",
|
|
|
|
|
"user_files_info"))
|
2025-12-18 01:13:19 +07:00
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_ACTION.getCode(),
|
|
|
|
|
"Invalid action: " + action,
|
|
|
|
|
response);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error in FileEntityHandler", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Handler error: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 12:50:39 +07:00
|
|
|
private BaseResponse handleGetFilesUserInfo(BaseRequest request, FileEntityRequest fileRequest) {
|
2026-01-09 22:55:06 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2026-01-06 12:50:39 +07:00
|
|
|
FileInfoUserResponse userFileStats = fileStatsService.getUserFileStats(userId);
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
userFileStats);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 01:13:19 +07:00
|
|
|
private BaseResponse handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-18 14:40:27 +07:00
|
|
|
FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion());
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
if (!fileInfo.getUserId().equals(userId)) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.ACCESS_DENIED.getCode(),
|
|
|
|
|
"Access denied",
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
fileInfo);
|
|
|
|
|
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required",
|
|
|
|
|
null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error getting file info", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to get file info: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BaseResponse handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-18 14:40:27 +07:00
|
|
|
FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId(),
|
|
|
|
|
request.getVersion());
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
if (!fileInfo.getUserId().equals(userId)) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.ACCESS_DENIED.getCode(),
|
|
|
|
|
"Access denied",
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
fileInfo);
|
|
|
|
|
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required",
|
|
|
|
|
null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error getting file by session", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to get file: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BaseResponse handleGetUserFiles(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-18 01:13:19 +07:00
|
|
|
int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
|
|
|
|
|
int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
|
|
|
|
|
|
2025-12-18 14:40:27 +07:00
|
|
|
FileResponse files = fileEntityService.getUserFiles(userId, page, pageSize, request.getVersion());
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
files);
|
|
|
|
|
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required",
|
|
|
|
|
null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error getting user files", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to get files: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-18 01:13:19 +07:00
|
|
|
int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
|
|
|
|
|
int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
|
|
|
|
|
|
|
|
|
|
if (fileRequest.getQuery() == null || fileRequest.getQuery().isBlank()) {
|
|
|
|
|
return handleGetUserFiles(request, fileRequest);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 14:40:27 +07:00
|
|
|
FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion());
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
var filteredFiles = allFiles.getFiles().stream()
|
2025-12-26 14:51:02 +07:00
|
|
|
.filter(f -> (f.getOriginalFileName() + f.getFileExtension()).toLowerCase()
|
2025-12-26 13:52:53 +07:00
|
|
|
.contains(fileRequest.getQuery().toLowerCase()))
|
2025-12-18 01:13:19 +07:00
|
|
|
.toList();
|
|
|
|
|
|
|
|
|
|
int start = (page - 1) * pageSize;
|
|
|
|
|
int end = Math.min(start + pageSize, filteredFiles.size());
|
2025-12-26 13:52:53 +07:00
|
|
|
List<FileEntityResponse> fileEntityResponses = filteredFiles.subList(start, Math.min(end, filteredFiles.size()));
|
|
|
|
|
long sumSize = fileEntityResponses.stream()
|
|
|
|
|
.mapToLong(FileEntityResponse::getFileSize)
|
|
|
|
|
.sum();
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
FileListResponse response = FileListResponse.builder()
|
2025-12-26 13:52:53 +07:00
|
|
|
.files(fileEntityResponses)
|
2025-12-18 01:13:19 +07:00
|
|
|
.totalCount(filteredFiles.size())
|
2025-12-26 13:52:53 +07:00
|
|
|
.totalSize(sumSize)
|
|
|
|
|
.formattedTotalSize(fileEntityService.formatFileSize(sumSize))
|
2025-12-18 01:13:19 +07:00
|
|
|
.page(page)
|
|
|
|
|
.pageSize(pageSize)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
response);
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
2025-12-26 13:52:53 +07:00
|
|
|
return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required", null);
|
2025-12-18 01:13:19 +07:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error searching files", e);
|
2025-12-26 13:52:53 +07:00
|
|
|
return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to search files: " + e.getMessage(), null);
|
2025-12-18 01:13:19 +07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-18 01:13:19 +07:00
|
|
|
long usage = fileEntityService.getUserStorageUsed(userId);
|
|
|
|
|
|
|
|
|
|
StorageUsageResponse response = StorageUsageResponse.builder()
|
|
|
|
|
.userId(userId)
|
|
|
|
|
.storageUsed(usage)
|
|
|
|
|
.formattedStorageUsed(formatFileSize(usage))
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
MessageCode.SUCCESS.getDescription(),
|
|
|
|
|
response);
|
|
|
|
|
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required",
|
|
|
|
|
null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error getting storage usage", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to get storage usage: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 21:18:42 +07:00
|
|
|
private BaseResponse handleProtectFile(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
String fileId = fileRequest.getFileId();
|
|
|
|
|
Optional<FileEntity> optionalFileEntity = fileEntityRepository.findById(fileId);
|
|
|
|
|
|
|
|
|
|
if (optionalFileEntity.isEmpty()) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_NOT_FOUND.getCode(),
|
|
|
|
|
"File not found exception: " + fileRequest.getFileId(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FileEntity fileEntity = optionalFileEntity.get();
|
|
|
|
|
ProtectionStatus protectionStatus = fileEntity.getProtectionStatus();
|
|
|
|
|
|
|
|
|
|
Integer code = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getCode():
|
|
|
|
|
MessageCode.FILE_IS_NOT_PROTECTED.getCode();
|
|
|
|
|
String message = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getDescription():
|
|
|
|
|
MessageCode.FILE_IS_NOT_PROTECTED.getDescription();
|
|
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(), code, message, fileEntityService.getById(fileId,
|
|
|
|
|
request.getVersion()));
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 01:13:19 +07:00
|
|
|
private BaseResponse handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) {
|
|
|
|
|
try {
|
2026-01-05 16:26:46 +07:00
|
|
|
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
2025-12-26 16:23:17 +07:00
|
|
|
String fileId = fileRequest.getFileId();
|
|
|
|
|
FileEntityResponse fileInfo = fileEntityService.getById(fileId, request.getVersion());
|
|
|
|
|
DeleteFileResponse response;
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
if (!fileInfo.getUserId().equals(userId)) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.ACCESS_DENIED.getCode(),
|
|
|
|
|
"Access denied",
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-26 16:23:17 +07:00
|
|
|
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
|
|
|
|
.orElseThrow(() -> new FileEntityNotFoundException(fileId));
|
2026-01-27 08:09:54 +07:00
|
|
|
int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();
|
2025-12-18 01:13:19 +07:00
|
|
|
|
2026-01-27 08:09:54 +07:00
|
|
|
if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
|
2026-02-16 15:04:58 +07:00
|
|
|
fileEntityService.deleteFromStorageDisk(fileEntity);
|
2025-12-26 16:23:17 +07:00
|
|
|
response = DeleteFileResponse.builder()
|
|
|
|
|
.fileId(fileRequest.getFileId())
|
|
|
|
|
.message("File deleted from disk")
|
|
|
|
|
.build();
|
|
|
|
|
} else {
|
2026-01-27 08:09:54 +07:00
|
|
|
fileEntityService.softDeleteFileWithHash(fileEntity);
|
|
|
|
|
|
2025-12-26 16:23:17 +07:00
|
|
|
response = DeleteFileResponse.builder()
|
|
|
|
|
.fileId(fileRequest.getFileId())
|
|
|
|
|
.message("File marked as deleted")
|
|
|
|
|
.build();
|
|
|
|
|
}
|
2025-12-18 01:13:19 +07:00
|
|
|
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.SUCCESS.getCode(),
|
|
|
|
|
"File marked as deleted",
|
|
|
|
|
response);
|
|
|
|
|
} catch (NotFoundAuthToken e) {
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.INVALID_TOKEN.getCode(),
|
|
|
|
|
"Authentication required",
|
|
|
|
|
null);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("Error deleting file", e);
|
|
|
|
|
return new BaseResponse(request.getMsgId(),
|
|
|
|
|
MessageCode.FILE_UPLOAD_ERROR.getCode(),
|
|
|
|
|
"Failed to delete file: " + e.getMessage(),
|
|
|
|
|
null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String formatFileSize(long size) {
|
|
|
|
|
if (size < 1024) return size + " B";
|
|
|
|
|
int exp = (int) (Math.log(size) / Math.log(1024));
|
|
|
|
|
String pre = "KMGTPE".charAt(exp-1) + "";
|
|
|
|
|
return String.format("%.1f %sB", size / Math.pow(1024, exp), pre);
|
|
|
|
|
}
|
|
|
|
|
}
|