diff --git a/src/main/java/ru/soune/nocopy/controller/ApiController.java b/src/main/java/ru/soune/nocopy/controller/ApiController.java index 9ecad18..ea129c0 100644 --- a/src/main/java/ru/soune/nocopy/controller/ApiController.java +++ b/src/main/java/ru/soune/nocopy/controller/ApiController.java @@ -387,11 +387,11 @@ public class ApiController { return false; } - return mimeType.startsWith("image/") || - mimeType.startsWith("text/") || - mimeType.equals("application/pdf") || - mimeType.startsWith("video/") || - mimeType.startsWith("audio/"); + return mimeType.startsWith("image") || + mimeType.startsWith("text") || + mimeType.equals("pdf") || + mimeType.startsWith("video") || + mimeType.startsWith("audio"); } private Long getUserIdFromToken(String tokenHeader) { diff --git a/src/main/java/ru/soune/nocopy/entity/file/FileType.java b/src/main/java/ru/soune/nocopy/entity/file/FileType.java index 233a7c2..cfe080d 100644 --- a/src/main/java/ru/soune/nocopy/entity/file/FileType.java +++ b/src/main/java/ru/soune/nocopy/entity/file/FileType.java @@ -2,21 +2,34 @@ package ru.soune.nocopy.entity.file; import lombok.Getter; +import java.util.Arrays; +import java.util.List; + @Getter public enum FileType { - PHOTO("photo"), - IMAGE("image"), - VIDEO("video"), - AUDIO("audio"), - DOCUMENT("document"); + PHOTO("photo", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp")), + IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "tiff")), + VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm")), + AUDIO("audio", Arrays.asList("mp3", "wav", "ogg", "aac", "flac", "m4a")), + DOCUMENT("document", Arrays.asList("pdf", "doc", "docx", "txt", "rtf", "odt", "xls", "xlsx", "ppt", "pptx")); - private final String code; + private final String displayName; + private final List allowedExtensions; - FileType(String code) { - this.code = code; + FileType(String displayName, List allowedExtensions) { + this.displayName = displayName; + this.allowedExtensions = allowedExtensions; } - public String getCode() { - return code; + public String getDisplayName() { + return displayName; + } + + public List getAllowedExtensions() { + return allowedExtensions; + } + + public boolean supportsExtension(String extension) { + return allowedExtensions.contains(extension.toLowerCase()); } } diff --git a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java index 26f6dba..7674f4c 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java @@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.entity.AuthToken; @@ -17,10 +18,8 @@ import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.FileUploadSessionRepository; import ru.soune.nocopy.service.file.FileUploadService; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @Component @@ -70,18 +69,31 @@ public class FileUploadHandler implements RequestHandler { } private BaseResponse handleInitUpload(BaseRequest request, FileUploadRequest fileRequest) { - try { String token = fileRequest.getToken(); Optional tokenOptional = authTokenRepository.findByToken(token); + if (tokenOptional.isEmpty()) { + return new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), + MessageCode.INVALID_TOKEN.getDescription(), Map.of("token", token)); + } + AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token not found")); BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest"); fileUploadRequestValidator.validate(fileRequest, bindingResult); if (bindingResult.hasErrors()) { - throw new ValidationException(bindingResult, request.getMsgId()); + Map fieldErrors = bindingResult.getFieldErrors() + .stream() + .collect(Collectors.toMap( + FieldError::getField, + fieldError -> fieldError.getDefaultMessage() != null + ? fieldError.getDefaultMessage() + : "Validation error")); + + return new BaseResponse(request.getMsgId(), MessageCode.INVALID_FIELD.getCode(), + MessageCode.INVALID_FIELD.getDescription(), fieldErrors); } FileUploadSession session = fileUploadService.initUpload( @@ -101,15 +113,6 @@ public class FileUploadHandler implements RequestHandler { return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), fileResponse); - - } catch (NotFoundAuthToken e) { - InitFileResponse initFileResponse = InitFileResponse.builder() - .build(); - - throw new NotValidFieldException("Invalid or expired token: " + fileRequest.getToken(), - new BaseResponse(request.getMsgId(), MessageCode.INVALID_TOKEN.getCode(), - MessageCode.INVALID_TOKEN.getDescription(), initFileResponse)); - } } private BaseResponse handleGetProgress(BaseRequest request, FileUploadRequest fileRequest) { diff --git a/src/main/java/ru/soune/nocopy/handler/validator/FileUploadRequestValidator.java b/src/main/java/ru/soune/nocopy/handler/validator/FileUploadRequestValidator.java index f40487a..fa12270 100644 --- a/src/main/java/ru/soune/nocopy/handler/validator/FileUploadRequestValidator.java +++ b/src/main/java/ru/soune/nocopy/handler/validator/FileUploadRequestValidator.java @@ -7,9 +7,12 @@ import ru.soune.nocopy.dto.file.FileUploadRequest; import ru.soune.nocopy.entity.file.FileType; import java.util.Arrays; +import java.util.List; @Component public class FileUploadRequestValidator implements Validator { + private final List supportedFileTypes = Arrays.asList(FileType.values()); + @Override public boolean supports(Class clazz) { return FileUploadRequest.class.isAssignableFrom(clazz); @@ -21,7 +24,7 @@ public class FileUploadRequestValidator implements Validator { validateType(request.getFileType(), errors); validateFileName(request.getFileName(), errors); - validateExtension(request.getExtension(), errors); + validateExtension(request.getExtension(), request.getFileName(), errors); } private void validateType(String fileType, Errors errors) { @@ -31,7 +34,12 @@ public class FileUploadRequestValidator implements Validator { } try { - FileType.valueOf(fileType.toUpperCase()); + FileType parsedType = FileType.valueOf(fileType.toUpperCase()); + + if (!supportedFileTypes.contains(parsedType)) { + errors.rejectValue("fileType", "fileType.unsupported", + "Unsupported file type. Valid types: " + Arrays.toString(FileType.values())); + } } catch (IllegalArgumentException e) { errors.rejectValue("fileType", "fileType.invalid", "Invalid file type. Valid types: " + Arrays.toString(FileType.values())); @@ -50,10 +58,50 @@ public class FileUploadRequestValidator implements Validator { } } - private void validateExtension(String extension, Errors errors) { - if (extension == null || extension.isBlank()) { - errors.rejectValue("extension", "extension.required", "Extension is required"); + private void validateExtension(String fileType, String fileName, Errors errors) { + if (fileType == null || fileType.isBlank()) { + errors.rejectValue("fileType", "fileType.required", "File type is required"); return; } + + String fileExtension = getFileExtension(fileName); + + if (fileExtension == null || fileExtension.isBlank()) { + errors.rejectValue("fileName", "fileName.invalid.extension", "File must have an extension"); + return; + } + + if (fileExtension.contains(".")) { + errors.rejectValue("extension", "extension.required", "Extension contains comma"); + return; + } + + try { + FileType parsedType = FileType.valueOf(fileType.toUpperCase()); + + if (!supportedFileTypes.contains(parsedType)) { + errors.rejectValue("fileType", "fileType.unsupported", + "Unsupported file type. Supported types: " + supportedFileTypes); + return; + } + + if (!parsedType.supportsExtension(fileExtension)) { + errors.rejectValue("fileName", "fileType.extension.mismatch", + String.format("File extension '%s' does not match file type '%s'. Allowed extensions for %s: %s", + fileExtension, parsedType.getDisplayName(), parsedType.getDisplayName(), + parsedType.getAllowedExtensions())); + } + + } catch (IllegalArgumentException e) { + errors.rejectValue("fileType", "fileType.invalid", + "Invalid file type. Valid types: " + Arrays.toString(FileType.values())); + } + } + + private String getFileExtension(String fileName) { + if (fileName == null || fileName.lastIndexOf('.') == -1) { + return null; + } + return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); } } \ No newline at end of file