add bone for file system #9
@@ -0,0 +1,28 @@
|
||||
package ru.soune.nocopy.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.soune.nocopy.handler.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class HandlerConfig {
|
||||
|
||||
@Bean
|
||||
public Map<Integer, RequestHandler> handlers(
|
||||
RegRequestHandler reg,
|
||||
LoginRequestHandler login,
|
||||
FileUploadHandler upload,
|
||||
FileEntityHandler file
|
||||
) {
|
||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||
map.put(20001, login);
|
||||
map.put(20002, reg);
|
||||
map.put(20004, upload);
|
||||
map.put(20005, file);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
@@ -19,10 +17,7 @@ import ru.soune.nocopy.dto.file.UploadProgress;
|
||||
import ru.soune.nocopy.entity.file.UploadStatus;
|
||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||
import ru.soune.nocopy.exception.ValidationException;
|
||||
import ru.soune.nocopy.handler.FileUploadHandler;
|
||||
import ru.soune.nocopy.handler.LoginRequestHandler;
|
||||
import ru.soune.nocopy.handler.RegRequestHandler;
|
||||
import ru.soune.nocopy.handler.RequestHandler;
|
||||
import ru.soune.nocopy.handler.*;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -35,21 +30,10 @@ import java.util.stream.Collectors;
|
||||
@RequestMapping("/api")
|
||||
@RequiredArgsConstructor
|
||||
public class ApiController {
|
||||
private final ApplicationContext applicationContext;
|
||||
private final FileUploadService fileUploadService;
|
||||
|
||||
private Map<Integer, RequestHandler> handlers = new HashMap<>();
|
||||
private final Map<Integer, RequestHandler> handlers;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
RegRequestHandler regHandler = applicationContext.getBean(RegRequestHandler.class);
|
||||
LoginRequestHandler loginHandler = applicationContext.getBean(LoginRequestHandler.class);
|
||||
FileUploadHandler fileUploadHandler = applicationContext.getBean(FileUploadHandler.class);
|
||||
|
||||
handlers.put(20004, fileUploadHandler);
|
||||
handlers.put(20002, regHandler);
|
||||
handlers.put(20001, loginHandler);
|
||||
}
|
||||
|
||||
@PostMapping("/v{version}/data")
|
||||
public ResponseEntity<?> handlePostRequest(@RequestBody BaseRequest request,
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
package ru.soune.nocopy.controller.file;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.file.FileApiResponse;
|
||||
import ru.soune.nocopy.dto.file.FileEntityResponse;
|
||||
import ru.soune.nocopy.dto.file.FileListResponse;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/file-entities")
|
||||
@RequiredArgsConstructor
|
||||
public class FileEntityController {
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
/**
|
||||
* Получить информацию о файле по ID
|
||||
*/
|
||||
@GetMapping("/{fileId}")
|
||||
public ResponseEntity<FileApiResponse<FileEntityResponse>> getFileInfo(
|
||||
@PathVariable String fileId,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
FileEntityResponse fileInfo = fileEntityService.getById(fileId);
|
||||
|
||||
if (!fileInfo.getUserId().equals(userId)) {
|
||||
return ResponseEntity.status(403)
|
||||
.body(FileApiResponse.error("Access denied"));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(fileInfo));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file info", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to get file info: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить информацию о файле по ID сессии загрузки
|
||||
*/
|
||||
@GetMapping("/by-session/{uploadSessionId}")
|
||||
public ResponseEntity<FileApiResponse<FileEntityResponse>> getFileByUploadSession(
|
||||
@PathVariable String uploadSessionId,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(uploadSessionId);
|
||||
|
||||
if (!fileInfo.getUserId().equals(userId)) {
|
||||
return ResponseEntity.status(403)
|
||||
.body(FileApiResponse.error("Access denied"));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(fileInfo));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file by session", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to get file: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить все файлы пользователя
|
||||
*/
|
||||
@GetMapping("/my-files")
|
||||
public ResponseEntity<FileApiResponse<FileListResponse>> getUserFiles(
|
||||
@RequestHeader("Authorization") String tokenHeader,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
FileListResponse files = fileEntityService.getUserFiles(userId, page, pageSize);
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(files));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting user files", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to get files: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск файлов пользователя
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<FileApiResponse<FileListResponse>> searchFiles(
|
||||
@RequestParam String query,
|
||||
@RequestHeader("Authorization") String tokenHeader,
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "20") int pageSize) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
FileListResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000);
|
||||
|
||||
var filteredFiles = allFiles.getFiles().stream()
|
||||
.filter(f -> f.getOriginalFileName().toLowerCase().contains(query.toLowerCase()))
|
||||
.toList();
|
||||
|
||||
int start = (page - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, filteredFiles.size());
|
||||
|
||||
FileListResponse response = FileListResponse.builder()
|
||||
.files(filteredFiles.subList(start, Math.min(end, filteredFiles.size())))
|
||||
.totalCount(filteredFiles.size())
|
||||
.totalSize(allFiles.getTotalSize())
|
||||
.formattedTotalSize(allFiles.getFormattedTotalSize())
|
||||
.page(page)
|
||||
.pageSize(pageSize)
|
||||
.build();
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(response));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error searching files", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to search files: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить общий размер файлов пользователя
|
||||
*/
|
||||
@GetMapping("/storage/usage")
|
||||
public ResponseEntity<FileApiResponse<Long>> getStorageUsage(
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
long usage = fileEntityService.getUserStorageUsed(userId);
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(usage));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting storage usage", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to get storage usage: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Пометить файл как удаленный
|
||||
*/
|
||||
@DeleteMapping("/{fileId}")
|
||||
public ResponseEntity<FileApiResponse<Void>> deleteFile(
|
||||
@PathVariable String fileId,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
|
||||
try {
|
||||
Long userId = getUserIdFromToken(tokenHeader);
|
||||
FileEntityResponse fileInfo = fileEntityService.getById(fileId);
|
||||
|
||||
if (!fileInfo.getUserId().equals(userId)) {
|
||||
return ResponseEntity.status(403)
|
||||
.body(FileApiResponse.error("Access denied"));
|
||||
}
|
||||
|
||||
fileEntityService.markAsDeleted(fileId);
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(
|
||||
"File marked as deleted", null));
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401)
|
||||
.body(FileApiResponse.error("Authentication required"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error deleting file", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to delete file: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private Long getUserIdFromToken(String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
AuthToken authToken = authTokenRepository.findByToken(token)
|
||||
.orElseThrow(() -> new NotFoundAuthToken("Token not found"));
|
||||
return authToken.getUser().getId();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package ru.soune.nocopy.controller.file;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.soune.nocopy.dto.file.FileApiResponse;
|
||||
import ru.soune.nocopy.dto.file.InitUploadRequest;
|
||||
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.file.FileType;
|
||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||
import ru.soune.nocopy.entity.file.UploadStatus;
|
||||
import ru.soune.nocopy.exception.FileUploadException;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.exception.UploadSessionNotFoundException;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.repository.FileUploadSessionRepository;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/files")
|
||||
@RequiredArgsConstructor
|
||||
public class FileUploadController {
|
||||
|
||||
private final FileUploadService fileUploadService;
|
||||
|
||||
@PostMapping("/chunk")
|
||||
public ResponseEntity<FileApiResponse<UploadProgressResponse>> uploadChunk(
|
||||
@RequestParam("uploadId") String uploadId,
|
||||
@RequestParam("chunkNumber") Integer chunkNumber,
|
||||
@RequestParam("chunk") MultipartFile chunk) {
|
||||
|
||||
log.info("Uploading chunk {} for session {}, file size: {} bytes",
|
||||
chunkNumber, uploadId, chunk.getSize());
|
||||
|
||||
if (chunk.isEmpty()) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Chunk file is empty"));
|
||||
}
|
||||
|
||||
if (uploadId == null || uploadId.isBlank()) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Upload ID is required"));
|
||||
}
|
||||
|
||||
try {
|
||||
UploadProgressResponse progress = fileUploadService.uploadChunk(
|
||||
uploadId, chunkNumber, chunk);
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(progress));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Error uploading chunk", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error(e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ public enum MessageCode {
|
||||
INVALID_JSON_BODY(2, "Invalid fields in JSON object"),
|
||||
INCOMPLETE_UPLOAD(2, "Not load all chunks"),
|
||||
MSG_ID_NOT_FOUND(4, "Message id not found"),
|
||||
|
||||
FILE_ENTITY_ERROR(2, "File entity error"),
|
||||
ACCESS_DENIED(2, "Access denied"),
|
||||
AUTH_EMAIL_NOT_FOUND(4, "Email not found"),
|
||||
FILE_NOT_FOUND(4, "File not found"),
|
||||
AUTH_PASSWORD_NOT_MATCHES(2, "Password does not match");
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DeleteFileResponse {
|
||||
@JsonProperty("file_id")
|
||||
private String fileId;
|
||||
|
||||
@JsonProperty("message")
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FileEntityRequest {
|
||||
@JsonProperty("action")
|
||||
private String action;
|
||||
|
||||
@JsonProperty("file_id")
|
||||
private String fileId;
|
||||
|
||||
@JsonProperty("upload_session_id")
|
||||
private String uploadSessionId;
|
||||
|
||||
@JsonProperty("query")
|
||||
private String query;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("page_size")
|
||||
private Integer pageSize;
|
||||
|
||||
@JsonProperty("token")
|
||||
private String token;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -9,14 +10,24 @@ import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class FileListResponse {
|
||||
|
||||
@JsonProperty("files")
|
||||
private List<FileEntityResponse> files;
|
||||
private int totalCount;
|
||||
private long totalSize;
|
||||
|
||||
@JsonProperty("total_count")
|
||||
private Integer totalCount;
|
||||
|
||||
@JsonProperty("total_size")
|
||||
private Long totalSize;
|
||||
|
||||
@JsonProperty("formatted_total_size")
|
||||
private String formattedTotalSize;
|
||||
private int page;
|
||||
private int pageSize;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("page_size")
|
||||
private Integer pageSize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileResponse {
|
||||
|
||||
private List<FileEntityResponse> files;
|
||||
private int totalCount;
|
||||
private long totalSize;
|
||||
private String formattedTotalSize;
|
||||
private int page;
|
||||
private int pageSize;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class StorageUsageResponse {
|
||||
@JsonProperty("user_id")
|
||||
private Long userId;
|
||||
|
||||
@JsonProperty("storage_used")
|
||||
private Long storageUsed;
|
||||
|
||||
@JsonProperty("formatted_storage_used")
|
||||
private String formattedStorageUsed;
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
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.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class FileEntityHandler implements RequestHandler {
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) {
|
||||
try {
|
||||
FileEntityRequest fileRequest = objectMapper.convertValue(
|
||||
request.getMessageBody(), FileEntityRequest.class);
|
||||
|
||||
String action = fileRequest.getAction();
|
||||
|
||||
switch (action) {
|
||||
case "get_file_info":
|
||||
return handleGetFileInfo(request, fileRequest);
|
||||
case "get_file_by_session":
|
||||
return handleGetFileBySession(request, fileRequest);
|
||||
case "get_user_files":
|
||||
return handleGetUserFiles(request, fileRequest);
|
||||
case "search_files":
|
||||
return handleSearchFiles(request, fileRequest);
|
||||
case "get_storage_usage":
|
||||
return handleGetStorageUsage(request, fileRequest);
|
||||
case "delete_file":
|
||||
return handleDeleteFile(request, fileRequest);
|
||||
default:
|
||||
ActionResponse response = ActionResponse.builder()
|
||||
.action(action)
|
||||
.availableActions(Arrays.asList(
|
||||
"get_file_info", "get_file_by_session", "get_user_files",
|
||||
"search_files", "get_storage_usage", "delete_file"))
|
||||
.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 handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = getUserIdFromToken(fileRequest.getToken());
|
||||
FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId());
|
||||
|
||||
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 = getUserIdFromToken(fileRequest.getToken());
|
||||
FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId());
|
||||
|
||||
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 = getUserIdFromToken(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);
|
||||
|
||||
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 {
|
||||
Long userId = getUserIdFromToken(fileRequest.getToken());
|
||||
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);
|
||||
}
|
||||
|
||||
FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000);
|
||||
|
||||
var filteredFiles = allFiles.getFiles().stream()
|
||||
.filter(f -> f.getOriginalFileName().toLowerCase().contains(fileRequest.getQuery().toLowerCase()))
|
||||
.toList();
|
||||
|
||||
int start = (page - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, filteredFiles.size());
|
||||
|
||||
FileListResponse response = FileListResponse.builder()
|
||||
.files(filteredFiles.subList(start, Math.min(end, filteredFiles.size())))
|
||||
.totalCount(filteredFiles.size())
|
||||
.totalSize(allFiles.getTotalSize())
|
||||
.formattedTotalSize(allFiles.getFormattedTotalSize())
|
||||
.page(page)
|
||||
.pageSize(pageSize)
|
||||
.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 BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = getUserIdFromToken(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 handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = getUserIdFromToken(fileRequest.getToken());
|
||||
FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId());
|
||||
|
||||
if (!fileInfo.getUserId().equals(userId)) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ACCESS_DENIED.getCode(),
|
||||
"Access denied",
|
||||
null);
|
||||
}
|
||||
|
||||
fileEntityService.markAsDeleted(fileRequest.getFileId());
|
||||
|
||||
DeleteFileResponse 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 Long getUserIdFromToken(String token) {
|
||||
if (token == null || token.isBlank()) {
|
||||
throw new NotFoundAuthToken("Token is required");
|
||||
}
|
||||
|
||||
if (token.startsWith("Bearer ")) {
|
||||
token = token.replace("Bearer ", "");
|
||||
}
|
||||
|
||||
AuthToken authToken = authTokenRepository.findByToken(token)
|
||||
.orElseThrow(() -> new NotFoundAuthToken("Token not found"));
|
||||
return authToken.getUser().getId();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.file.FileEntityResponse;
|
||||
import ru.soune.nocopy.dto.file.FileListResponse;
|
||||
import ru.soune.nocopy.dto.file.FileResponse;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||
@@ -27,9 +27,6 @@ public class FileEntityService {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
/**
|
||||
* Создает FileEntity на основе завершенной сессии загрузки
|
||||
*/
|
||||
@Transactional
|
||||
public FileEntity createFromUploadSession(FileUploadSession session, String checksum) {
|
||||
log.info("Creating FileEntity for upload session: {}", session.getUploadId());
|
||||
@@ -72,9 +69,7 @@ public class FileEntityService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлекает расширение файла из имени
|
||||
*/
|
||||
|
||||
private String extractFileExtension(String fileName) {
|
||||
int dotIndex = fileName.lastIndexOf('.');
|
||||
if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
|
||||
@@ -83,9 +78,6 @@ public class FileEntityService {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает FileEntity по ID
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public FileEntityResponse getById(String fileId) {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
||||
@@ -94,9 +86,6 @@ public class FileEntityService {
|
||||
return convertToResponse(fileEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает FileEntity по ID сессии загрузки
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public FileEntityResponse getByUploadSessionId(String uploadSessionId) {
|
||||
FileEntity fileEntity = fileEntityRepository.findByUploadSessionId(uploadSessionId)
|
||||
@@ -106,9 +95,6 @@ public class FileEntityService {
|
||||
return convertToResponse(fileEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает FileEntity по пути файла
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public FileEntityResponse getByFilePath(String filePath) {
|
||||
FileEntity fileEntity = fileEntityRepository.findByFilePath(filePath)
|
||||
@@ -117,11 +103,9 @@ public class FileEntityService {
|
||||
return convertToResponse(fileEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает все файлы пользователя
|
||||
*/
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileListResponse getAllUserFiles(Long userId) {
|
||||
public FileResponse getAllUserFiles(Long userId) {
|
||||
List<FileEntity> fileEntities = fileEntityRepository.findByUserIdAndStatus(
|
||||
userId, FileStatus.ACTIVE);
|
||||
|
||||
@@ -133,7 +117,7 @@ public class FileEntityService {
|
||||
.mapToLong(FileEntity::getFileSize)
|
||||
.sum();
|
||||
|
||||
return FileListResponse.builder()
|
||||
return FileResponse.builder()
|
||||
.files(files)
|
||||
.totalCount(files.size())
|
||||
.totalSize(totalSize)
|
||||
@@ -143,11 +127,9 @@ public class FileEntityService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает файлы пользователя с пагинацией
|
||||
*/
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileListResponse getUserFiles(Long userId, int page, int pageSize) {
|
||||
public FileResponse getUserFiles(Long userId, int page, int pageSize) {
|
||||
List<FileEntity> allFiles = fileEntityRepository.findByUserIdAndStatus(
|
||||
userId, FileStatus.ACTIVE);
|
||||
|
||||
@@ -155,7 +137,7 @@ public class FileEntityService {
|
||||
int end = Math.min(start + pageSize, allFiles.size());
|
||||
|
||||
if (start >= allFiles.size()) {
|
||||
return FileListResponse.builder()
|
||||
return FileResponse.builder()
|
||||
.files(List.of())
|
||||
.totalCount(allFiles.size())
|
||||
.totalSize(0)
|
||||
@@ -175,7 +157,7 @@ public class FileEntityService {
|
||||
.mapToLong(FileEntity::getFileSize)
|
||||
.sum();
|
||||
|
||||
return FileListResponse.builder()
|
||||
return FileResponse.builder()
|
||||
.files(files)
|
||||
.totalCount(allFiles.size())
|
||||
.totalSize(totalSize)
|
||||
@@ -185,9 +167,6 @@ public class FileEntityService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Помечает файл как удаленный (мягкое удаление)
|
||||
*/
|
||||
@Transactional
|
||||
public void markAsDeleted(String fileId) {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
||||
@@ -200,18 +179,12 @@ public class FileEntityService {
|
||||
log.info("FileEntity marked as deleted: {}", fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает общий размер файлов пользователя
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public long getUserStorageUsed(Long userId) {
|
||||
Long totalSize = fileEntityRepository.getTotalSizeByUserId(userId);
|
||||
return totalSize != null ? totalSize : 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск файлов пользователя по имени
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public List<FileEntityResponse> searchFiles(Long userId, String query) {
|
||||
List<FileEntity> files = fileEntityRepository.searchByFileName(userId, query);
|
||||
@@ -222,9 +195,6 @@ public class FileEntityService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, существует ли файл на диске
|
||||
*/
|
||||
private boolean checkFileExistsOnDisk(String filePath) {
|
||||
try {
|
||||
return Files.exists(Paths.get(filePath));
|
||||
@@ -234,9 +204,6 @@ public class FileEntityService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертирует FileEntity в DTO
|
||||
*/
|
||||
private FileEntityResponse convertToResponse(FileEntity fileEntity) {
|
||||
boolean existsOnDisk = checkFileExistsOnDisk(fileEntity.getFilePath());
|
||||
|
||||
@@ -260,9 +227,6 @@ public class FileEntityService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Форматирует размер файла в читаемый вид
|
||||
*/
|
||||
private String formatFileSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
|
||||
Reference in New Issue
Block a user