package ru.soune.nocopy.handler; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.stereotype.Component; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.entity.file.FileAppeal; import ru.soune.nocopy.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.entity.file.ProtectionStatus; import ru.soune.nocopy.entity.file.moderation.ModerationLog; import ru.soune.nocopy.exception.*; import ru.soune.nocopy.repository.FileAppealRepository; import ru.soune.nocopy.repository.FileEntityRepository; import ru.soune.nocopy.repository.ModerationLogRepository; import ru.soune.nocopy.service.file.moderation.ModerationService; import ru.soune.nocopy.service.register.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; @Slf4j @Component @RequiredArgsConstructor public class FileEntityHandler implements RequestHandler { private final FileEntityService fileEntityService; private final AuthService authService; private final FileStatsService fileStatsService; private final ObjectMapper objectMapper; private final FileEntityRepository fileEntityRepository; private final ModerationService moderationService; private final FileAppealRepository fileAppealRepository; private final ModerationLogRepository moderationLogRepository; @Override public BaseResponse handle(BaseRequest request) { try { FileEntityRequest fileRequest = objectMapper.convertValue( request.getMessageBody(), FileEntityRequest.class); String action = fileRequest.getAction(); switch (action) { case "file_info": return handleGetFileInfo(request, fileRequest); case "user_files_info": return handleGetFilesUserInfo(request, fileRequest); case "file_by_session": return handleGetFileBySession(request, fileRequest); case "user_files": return handleGetUserFiles(request, fileRequest); case "search_files": return handleSearchFiles(request, fileRequest); case "storage_usage": return handleGetStorageUsage(request, fileRequest); case "check_protected": return handleProtectFile(request, fileRequest); case "delete_file": return handleDeleteFile(request, fileRequest); case "change_file_status": return handleChangeStatus(request, fileRequest); case "moderate_file": return handleModerateFile(request, fileRequest); case "submit_appeal": return handleSubmitAppeal(request, fileRequest); case "review_appeal": return handleReviewAppeal(request, fileRequest); case "user_appeals": return handleGetUserAppeals(request, fileRequest); case "files_for_moderation": return handleGetFilesForModeration(request, fileRequest); default: ActionResponse response = ActionResponse.builder() .action(action) .availableActions(Arrays.asList( "file_info", "file_by_session", "user_files", "search_files", "storage_usage", "delete_file", "user_files_info")) .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); } } private BaseResponse handleGetFilesUserInfo(BaseRequest request, FileEntityRequest fileRequest) { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileInfoUserResponse userFileStats = fileStatsService.getUserFileStats(userId); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), userFileStats); } private BaseResponse handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion()); 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 { Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId(), request.getVersion()); 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 { Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; FileResponse files = fileEntityService.getUserFiles(userId, page, pageSize, request.getVersion()); 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 handleChangeStatus(BaseRequest request, FileEntityRequest fileRequest) { FileStatus newStatus = fileRequest.getFileStatus(); String fileId = fileRequest.getFileId(); try { fileEntityService.changeFileStatus(newStatus ,fileRequest.getFileId()); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), MessageCode.FILE_NOT_FOUND.getDescription(), FileEntityResponse.builder().id(fileId).build()); } return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), FileEntityResponse.builder().status(newStatus).build()); } private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; String sortBy = fileRequest.getSortBy() != null ? fileRequest.getSortBy() : "fileName"; SortOrder sortOrder = SortOrder.fromString(fileRequest.getSortOrder()); FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); String searchQuery = fileRequest.getQuery() != null ? fileRequest.getQuery().toLowerCase().trim() : ""; String[] searchTerms = searchQuery.split("\\s+"); String filterType = fileRequest.getType(); String dateFilter = fileRequest.getDateFilter(); List filteredFiles = allFiles.getFiles().stream() .filter(f -> matchesSearch(f, searchTerms, searchQuery, filterType)) .filter(f -> matchesDateFilter(f, dateFilter)) .collect(Collectors.toList()); Comparator comparator = getComparator(sortBy, sortOrder); filteredFiles.sort(comparator); int start = (page - 1) * pageSize; int end = Math.min(start + pageSize, filteredFiles.size()); if (start >= filteredFiles.size()) { return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue()); } List paginatedFiles = filteredFiles.subList(start, end); long totalSize = paginatedFiles.stream() .mapToLong(FileEntityResponse::getFileSize) .sum(); FileListResponse response = FileListResponse.builder() .files(paginatedFiles) .totalCount(filteredFiles.size()) .totalSize(totalSize) .formattedTotalSize(fileEntityService.formatFileSize(totalSize)) .page(page) .pageSize(pageSize) .sortBy(sortBy) .sortOrder(sortOrder.getValue()) .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 searching files", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to search files: " + e.getMessage(), null); } } private boolean matchesDateFilter(FileEntityResponse file, String dateFilter) { if (dateFilter == null || dateFilter.isEmpty()) { return true; } LocalDate fileDate = file.getCreatedAt().toLocalDate(); LocalDate now = LocalDate.now(); switch (dateFilter.toLowerCase()) { case "today": return fileDate.equals(now); case "week": return fileDate.isAfter(now.minusWeeks(1)) && !fileDate.isAfter(now); case "month": return fileDate.isAfter(now.minusMonths(1)) && !fileDate.isAfter(now); default: return true; } } private boolean matchesSearch(FileEntityResponse file, String[] searchTerms, String fullQuery, String type) { String fileName = getSafeString(file.getFileName()); String originalFileName = getSafeString(file.getOriginalFileName()); String mimeType = getSafeString(file.getMimeType()); String fileFormat = getSafeString(file.getFileFormat()); String fullFileName = fileName + " " + originalFileName; boolean matchesType = true; if (type != null && !type.isEmpty()) { matchesType = matchesMimeType(mimeType, type); } if (!matchesType) { return false; } if (fullQuery != null && !fullQuery.isEmpty()) { if (fullFileName.toLowerCase().contains(fullQuery)) { return true; } for (String term : searchTerms) { if (term.length() < 2) continue; if (fullFileName.toLowerCase().contains(term)) { return true; } } if (fileFormat.toLowerCase().contains(fullQuery)) { return true; } if (mimeType.toLowerCase().contains(fullQuery)) { return true; } if (!fileFormat.isEmpty() && !fileName.toLowerCase().endsWith("." + fileFormat.toLowerCase())) { String fileNameWithExtension = fileName + "." + fileFormat; if (fileNameWithExtension.toLowerCase().contains(fullQuery)) { return true; } } return false; } return true; } private boolean matchesMimeType(String mimeType, String filterType) { if (filterType == null || filterType.isEmpty()) return true; String mimeTypeLower = mimeType.toLowerCase(); String filterTypeLower = filterType.toLowerCase().trim(); return mimeTypeLower.equals(filterTypeLower); } private String getSafeString(String value) { return value != null ? value : ""; } private Comparator getComparator(String sortBy, SortOrder sortOrder) { Comparator comparator; switch (sortBy) { case "fileSize": comparator = Comparator.comparingLong( FileEntityResponse::getFileSize ); break; case "fileType": comparator = Comparator.comparing( FileEntityResponse::getMimeType); break; case "createdAt": comparator = Comparator.comparing( FileEntityResponse::getCreatedAt, Comparator.nullsLast(Comparator.naturalOrder()) ); break; case "updatedAt": comparator = Comparator.comparing( FileEntityResponse::getUpdatedAt, Comparator.nullsLast(Comparator.naturalOrder()) ); break; case "fileName": comparator = Comparator.comparing( f -> getSafeString(f.getFileName()).toLowerCase(), Comparator.nullsLast(Comparator.naturalOrder()) ); break; case "protectStatus": comparator = Comparator.comparing( f -> getSafeString(f.getProtectStatus()).toLowerCase(), Comparator.nullsLast(Comparator.naturalOrder()) ); break; case "supportId": comparator = Comparator.comparing( FileEntityResponse::getSupportId, Comparator.nullsLast(Comparator.naturalOrder()) ); break; default: comparator = Comparator.comparing( f -> getSafeString(f.getFileName()).toLowerCase(), Comparator.nullsLast(Comparator.naturalOrder()) ); break; } return sortOrder == SortOrder.DESC ? comparator.reversed() : comparator; } private BaseResponse createEmptyResponse(BaseRequest request, int page, int pageSize, String sortBy, String sortOrder) { FileListResponse emptyResponse = FileListResponse.builder() .files(Collections.emptyList()) .totalCount(0) .totalSize(0L) .formattedTotalSize("0 B") .page(page) .pageSize(pageSize) .sortBy(sortBy) .sortOrder(sortOrder) .build(); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), emptyResponse); } private BaseResponse handleGetUserFilesWithSort(BaseRequest request, FileEntityRequest fileRequest, String sortBy, SortOrder sortOrder) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion()); List files = new ArrayList<>(allFiles.getFiles()); Comparator comparator = getComparator(sortBy, sortOrder); files.sort(comparator); int start = (page - 1) * pageSize; int end = Math.min(start + pageSize, files.size()); if (start >= files.size()) { return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue()); } List paginatedFiles = files.subList(start, end); long totalSize = paginatedFiles.stream() .mapToLong(FileEntityResponse::getFileSize) .sum(); FileListResponse response = FileListResponse.builder() .files(paginatedFiles) .totalCount(files.size()) .totalSize(totalSize) .formattedTotalSize(fileEntityService.formatFileSize(totalSize)) .page(page) .pageSize(pageSize) .sortBy(sortBy) .sortOrder(sortOrder.getValue()) .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 user files with sort", e); return new BaseResponse(request.getMsgId(), MessageCode.FILE_UPLOAD_ERROR.getCode(), "Failed to get files: " + e.getMessage(), null); } } private BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); 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); } } private BaseResponse handleProtectFile(BaseRequest request, FileEntityRequest fileRequest) { String fileId = fileRequest.getFileId(); Optional 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())); } private BaseResponse handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); String fileId = fileRequest.getFileId(); FileEntityResponse fileInfo = fileEntityService.getById(fileId, request.getVersion()); DeleteFileResponse response; if (!fileInfo.getUserId().equals(userId)) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Access denied", null); } FileEntity fileEntity = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete(); 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.softDeleteFileWithHash(fileEntity); response = DeleteFileResponse.builder() .fileId(fileRequest.getFileId()) .message("File marked as deleted") .build(); } 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 BaseResponse handleModerateFile(BaseRequest request, FileEntityRequest fileRequest) { try { //TODO добавить проверку на админа // Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken()); ModerationActionRequest moderationRequest = ModerationActionRequest.builder() .fileId(fileRequest.getFileId()) .newStatus(fileRequest.getFileStatus()) .comment(fileRequest.getComment()) .build(); //TODO set moderator ID moderationService.moderateFile(moderationRequest, 0L); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS_MODERATION.getCode(), MessageCode.SUCCESS_MODERATION.getDescription(), FileEntityResponse.builder().id(fileRequest.getFileId()).build()); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), MessageCode.ACCESS_DENIED.getDescription(), FileEntityResponse.builder().id(fileRequest.getFileId()).build()); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), MessageCode.FILE_NOT_FOUND.getDescription(), FileEntityResponse.builder() .id(fileRequest.getFileId()).build()); } catch (IllegalStateException e) { return new BaseResponse(request.getMsgId(), MessageCode.ILLEGAL_STATE.getCode(), MessageCode.ILLEGAL_STATE.getDescription(), FileEntityResponse.builder() .status(fileRequest.getFileStatus()).build()); } } private BaseResponse handleSubmitAppeal(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); AppealRequest appealRequest = AppealRequest.builder() .fileId(fileRequest.getFileId()) .appealReason(fileRequest.getAppealReason()) .additionalInfo(fileRequest.getAdditionalInfo()) .build(); AppealResponse response = moderationService.submitAppeal(appealRequest, userId); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), "Appeal submitted successfully", response); } catch (InvalidAppealException e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), e.getMessage(), null); } catch (Exception e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to submit appeal: " + e.getMessage(), null); } } private BaseResponse handleReviewAppeal(BaseRequest request, FileEntityRequest fileRequest) { try { // TODO // Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken()); String appealId = fileRequest.getAppealId(); Boolean approve = fileRequest.getApprove(); String comment = fileRequest.getComment(); if (appealId == null || approve == null) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "appealId and approve are required", null); } //TODO moderationService.reviewAppeal(appealId, approve, comment, 0L); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), "Appeal reviewed successfully", Map.of( "appealId", appealId, "approved", approve, "comment", comment )); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Admin access required", null); } catch (IllegalStateException e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), e.getMessage(), null); } catch (Exception e) { log.error("Error reviewing appeal", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to review appeal: " + e.getMessage(), null); } } private BaseResponse handleGetUserAppeals(BaseRequest request, FileEntityRequest fileRequest) { try { Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; Page appeals = moderationService.getUserAppeals(userId, page - 1, pageSize); Map response = Map.of( "appeals", appeals.getContent(), "totalCount", appeals.getTotalElements(), "totalPages", appeals.getTotalPages(), "currentPage", page, "pageSize", pageSize); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), "Authentication required", null); } catch (Exception e) { log.error("Error getting user appeals", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to get appeals: " + e.getMessage(), null); } } private BaseResponse handleGetFilesForModeration(BaseRequest request, FileEntityRequest fileRequest) { try { //TODO // authService.useAdminAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; Page files = moderationService.getFilesForModeration(page - 1, pageSize); List> filesWithInfo = files.getContent().stream() .map(file -> { FileModerationInfo info = moderationService.getFileModerationInfo(file.getId()); Map fileMap = new HashMap<>(); fileMap.put("fileId", file.getId()); fileMap.put("fileName", file.getOriginalFileName()); fileMap.put("userId", file.getUserId()); fileMap.put("status", file.getStatus() != null ? file.getStatus().name() : null); Map moderationInfoMap = new HashMap<>(); moderationInfoMap.put("hasActiveAppeal", info != null && info.getHasActiveAppeal()); if (info != null && info.getActiveAppeal() != null) { Map appealInfoMap = new HashMap<>(); appealInfoMap.put("appealId", info.getActiveAppeal().getAppealId()); appealInfoMap.put("status", info.getActiveAppeal().getStatus()); appealInfoMap.put("createdAt", info.getActiveAppeal().getCreatedAt()); moderationInfoMap.put("appealInfo", appealInfoMap); } else { moderationInfoMap.put("appealInfo", null); } fileMap.put("moderationInfo", moderationInfoMap); return fileMap; }) .collect(Collectors.toList()); Map response = Map.of( "files", filesWithInfo, "totalCount", files.getTotalElements(), "totalPages", files.getTotalPages(), "currentPage", page, "pageSize", pageSize ); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Admin access required", null); } catch (Exception e) { log.error("Error getting files for moderation", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to get files: " + e.getMessage(), null); } } private BaseResponse handleGetFileModerationInfo(BaseRequest request, FileEntityRequest fileRequest) { try { // TODO // authService.useAdminAuthToken(fileRequest.getToken()); String fileId = fileRequest.getFileId(); if (fileId == null) { return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "fileId is required", null); } FileModerationInfo moderationInfo = moderationService.getFileModerationInfo(fileId); FileEntity file = fileEntityRepository.findById(fileId) .orElseThrow(() -> new FileEntityNotFoundException(fileId)); Map fileInfoMap = new HashMap<>(); fileInfoMap.put("id", file.getId()); fileInfoMap.put("fileName", file.getOriginalFileName()); fileInfoMap.put("storedFileName", file.getStoredFileName()); fileInfoMap.put("fileSize", file.getFileSize()); fileInfoMap.put("formattedSize", fileEntityService.formatFileSize(file.getFileSize())); fileInfoMap.put("mimeType", file.getMimeType()); fileInfoMap.put("fileExtension", file.getFileExtension()); fileInfoMap.put("checksum", file.getChecksum()); fileInfoMap.put("status", file.getStatus() != null ? file.getStatus().name() : null); fileInfoMap.put("createdAt", file.getCreatedAt()); fileInfoMap.put("updatedAt", file.getUpdatedAt()); Map response = new HashMap<>(); response.put("fileInfo", fileInfoMap); response.put("moderationInfo", moderationInfo != null ? moderationInfo : new HashMap<>()); response.put("moderationHistory", getModerationHistory(fileId)); response.put("appeals", getFileAppeals(fileId)); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), response); } catch (SecurityException e) { return new BaseResponse(request.getMsgId(), MessageCode.ACCESS_DENIED.getCode(), "Admin access required", null); } catch (FileEntityNotFoundException e) { return new BaseResponse(request.getMsgId(), MessageCode.FILE_NOT_FOUND.getCode(), e.getMessage(), null); } catch (Exception e) { log.error("Error getting file moderation info", e); return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), "Failed to get file info: " + e.getMessage(), null); } } private List> getModerationHistory(String fileId) { List logs = moderationLogRepository.findByFileIdOrderByCreatedAtDesc(fileId); if (logs == null || logs.isEmpty()) { return Collections.emptyList(); } return logs.stream() .map(log -> { Map logMap = new HashMap<>(); logMap.put("id", log.getId()); logMap.put("moderatorId", log.getModeratorId()); logMap.put("oldStatus", log.getOldStatus() != null ? log.getOldStatus().name() : null); logMap.put("newStatus", log.getNewStatus() != null ? log.getNewStatus().name() : null); logMap.put("reason", log.getReason()); logMap.put("comment", log.getComment()); logMap.put("createdAt", log.getCreatedAt()); return logMap; }) .collect(Collectors.toList()); } private List> getFileAppeals(String fileId) { List appeals = fileAppealRepository.findByFileId(fileId); if (appeals == null || appeals.isEmpty()) { return Collections.emptyList(); } return appeals.stream() .map(appeal -> { Map appealMap = new HashMap<>(); appealMap.put("id", appeal.getId()); appealMap.put("userId", appeal.getUserId()); appealMap.put("appealReason", appeal.getAppealReason()); appealMap.put("additionalInfo", appeal.getAdditionalInfo()); appealMap.put("status", appeal.getStatus() != null ? appeal.getStatus().name() : null); appealMap.put("adminComment", appeal.getAdminComment()); appealMap.put("moderatorId", appeal.getModeratorId()); appealMap.put("createdAt", appeal.getCreatedAt()); appealMap.put("resolvedAt", appeal.getResolvedAt()); return appealMap; }) .collect(Collectors.toList()); } 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); } }