@@ -387,11 +387,11 @@ public class ApiController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mimeType.startsWith("image/") ||
|
return mimeType.startsWith("image") ||
|
||||||
mimeType.startsWith("text/") ||
|
mimeType.startsWith("text") ||
|
||||||
mimeType.equals("application/pdf") ||
|
mimeType.equals("pdf") ||
|
||||||
mimeType.startsWith("video/") ||
|
mimeType.startsWith("video") ||
|
||||||
mimeType.startsWith("audio/");
|
mimeType.startsWith("audio");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long getUserIdFromToken(String tokenHeader) {
|
private Long getUserIdFromToken(String tokenHeader) {
|
||||||
|
|||||||
@@ -2,21 +2,34 @@ package ru.soune.nocopy.entity.file;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum FileType {
|
public enum FileType {
|
||||||
PHOTO("photo"),
|
PHOTO("photo", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp")),
|
||||||
IMAGE("image"),
|
IMAGE("image", Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "tiff")),
|
||||||
VIDEO("video"),
|
VIDEO("video", Arrays.asList("mp4", "avi", "mov", "wmv", "flv", "mkv", "webm")),
|
||||||
AUDIO("audio"),
|
AUDIO("audio", Arrays.asList("mp3", "wav", "ogg", "aac", "flac", "m4a")),
|
||||||
DOCUMENT("document");
|
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<String> allowedExtensions;
|
||||||
|
|
||||||
FileType(String code) {
|
FileType(String displayName, List<String> allowedExtensions) {
|
||||||
this.code = code;
|
this.displayName = displayName;
|
||||||
|
this.allowedExtensions = allowedExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
public String getDisplayName() {
|
||||||
return code;
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAllowedExtensions() {
|
||||||
|
return allowedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsExtension(String extension) {
|
||||||
|
return allowedExtensions.contains(extension.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.validation.BeanPropertyBindingResult;
|
import org.springframework.validation.BeanPropertyBindingResult;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.validation.FieldError;
|
||||||
import ru.soune.nocopy.dto.*;
|
import ru.soune.nocopy.dto.*;
|
||||||
import ru.soune.nocopy.dto.file.*;
|
import ru.soune.nocopy.dto.file.*;
|
||||||
import ru.soune.nocopy.entity.AuthToken;
|
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.repository.FileUploadSessionRepository;
|
||||||
import ru.soune.nocopy.service.file.FileUploadService;
|
import ru.soune.nocopy.service.file.FileUploadService;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -70,18 +69,31 @@ public class FileUploadHandler implements RequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BaseResponse handleInitUpload(BaseRequest request, FileUploadRequest fileRequest) {
|
private BaseResponse handleInitUpload(BaseRequest request, FileUploadRequest fileRequest) {
|
||||||
try {
|
|
||||||
String token = fileRequest.getToken();
|
String token = fileRequest.getToken();
|
||||||
|
|
||||||
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
|
Optional<AuthToken> 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"));
|
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token not found"));
|
||||||
|
|
||||||
BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest");
|
BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest");
|
||||||
fileUploadRequestValidator.validate(fileRequest, bindingResult);
|
fileUploadRequestValidator.validate(fileRequest, bindingResult);
|
||||||
|
|
||||||
if (bindingResult.hasErrors()) {
|
if (bindingResult.hasErrors()) {
|
||||||
throw new ValidationException(bindingResult, request.getMsgId());
|
Map<String, String> 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(
|
FileUploadSession session = fileUploadService.initUpload(
|
||||||
@@ -101,15 +113,6 @@ public class FileUploadHandler implements RequestHandler {
|
|||||||
|
|
||||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||||
MessageCode.SUCCESS.getDescription(), fileResponse);
|
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) {
|
private BaseResponse handleGetProgress(BaseRequest request, FileUploadRequest fileRequest) {
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ import ru.soune.nocopy.dto.file.FileUploadRequest;
|
|||||||
import ru.soune.nocopy.entity.file.FileType;
|
import ru.soune.nocopy.entity.file.FileType;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class FileUploadRequestValidator implements Validator {
|
public class FileUploadRequestValidator implements Validator {
|
||||||
|
private final List<FileType> supportedFileTypes = Arrays.asList(FileType.values());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supports(Class<?> clazz) {
|
public boolean supports(Class<?> clazz) {
|
||||||
return FileUploadRequest.class.isAssignableFrom(clazz);
|
return FileUploadRequest.class.isAssignableFrom(clazz);
|
||||||
@@ -21,7 +24,7 @@ public class FileUploadRequestValidator implements Validator {
|
|||||||
|
|
||||||
validateType(request.getFileType(), errors);
|
validateType(request.getFileType(), errors);
|
||||||
validateFileName(request.getFileName(), errors);
|
validateFileName(request.getFileName(), errors);
|
||||||
validateExtension(request.getExtension(), errors);
|
validateExtension(request.getExtension(), request.getFileName(), errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateType(String fileType, Errors errors) {
|
private void validateType(String fileType, Errors errors) {
|
||||||
@@ -31,7 +34,12 @@ public class FileUploadRequestValidator implements Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (IllegalArgumentException e) {
|
||||||
errors.rejectValue("fileType", "fileType.invalid",
|
errors.rejectValue("fileType", "fileType.invalid",
|
||||||
"Invalid file type. Valid types: " + Arrays.toString(FileType.values()));
|
"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) {
|
private void validateExtension(String fileType, String fileName, Errors errors) {
|
||||||
if (extension == null || extension.isBlank()) {
|
if (fileType == null || fileType.isBlank()) {
|
||||||
errors.rejectValue("extension", "extension.required", "Extension is required");
|
errors.rejectValue("fileType", "fileType.required", "File type is required");
|
||||||
return;
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user