@@ -0,0 +1,58 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.AuthResponse;
|
||||
import ru.soune.nocopy.dto.LoginRequest;
|
||||
import ru.soune.nocopy.dto.LoginResponse;
|
||||
import ru.soune.nocopy.dto.RegisterRequest;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.exception.TokenNotFoundException;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.service.AuthService;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("v1/api/auth")
|
||||
@RequiredArgsConstructor
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<AuthResponse> register(@Valid @RequestBody RegisterRequest registerRequest) {
|
||||
AuthToken authToken = authService.register(registerRequest);
|
||||
|
||||
return ResponseEntity.ok(new AuthResponse(true, "success.user.register", authToken.getToken(),
|
||||
authToken.getExpiresAt()));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
AuthToken login = authService.login(request);
|
||||
|
||||
return ResponseEntity.ok(new LoginResponse(true, login.getUser().getEmail(),
|
||||
login.getToken(),login.getExpiresAt().toString()));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<?> logout(@RequestHeader("Authorization") String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
Optional<AuthToken> opToken = authTokenRepository.findByToken(token);
|
||||
|
||||
if (opToken.isEmpty()) {
|
||||
throw new TokenNotFoundException("Token not found");
|
||||
}
|
||||
|
||||
authService.logout(token);
|
||||
|
||||
return ResponseEntity.ok(Map.of("success", true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
public class HealtCheckController {
|
||||
|
||||
@GetMapping("/healt")
|
||||
public HttpStatus healtCheck() {
|
||||
return HttpStatus.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("v1/api/content")
|
||||
@AllArgsConstructor
|
||||
public class UserContentController {
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.ChangePasswordRequest;
|
||||
import ru.soune.nocopy.dto.UserDTO;
|
||||
import ru.soune.nocopy.dto.UserRequest;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.exception.InvalidUserEmail;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.mapper.UserMapper;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.UserService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("v1/api/user")
|
||||
@RequiredArgsConstructor
|
||||
public class UserController {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@GetMapping("/all")
|
||||
public ResponseEntity<List<UserDTO>> getAllUsers() {
|
||||
List<UserDTO> allUsers = userRepository.findAll().stream()
|
||||
.map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(),
|
||||
u.getPhone(), u.getGenderType(),
|
||||
u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType()))
|
||||
.toList();
|
||||
|
||||
return ResponseEntity.ok(allUsers);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<UserDTO> getUser(@RequestParam("email") String email,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
|
||||
|
||||
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
|
||||
|
||||
if (authToken != null) {
|
||||
Optional<User> user = userRepository.findByEmail(email);
|
||||
//TODO add mapper
|
||||
if (user.isPresent()) {
|
||||
UserDTO userDTO = userMapper.toDTO(user.get());
|
||||
userDTO.setEmail(email);
|
||||
userDTO.setFullName(user.get().getFullName());
|
||||
userDTO.setCompany(user.get().getCompany());
|
||||
userDTO.setPhone(user.get().getPhone());
|
||||
userDTO.setGenderType(user.get().getGenderType());
|
||||
userDTO.setBirthday(user.get().getBirthday());
|
||||
userDTO.setCreatedAt(user.get().getCreatedAt());
|
||||
userDTO.setSubscriptionType(user.get().getSubscriptionType());
|
||||
userDTO.setActive(user.get().getIsActive());
|
||||
|
||||
return ResponseEntity.ok(userDTO);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
//TODO fix mapper,doesnot exist all fields
|
||||
@PostMapping("/change-password")
|
||||
public ResponseEntity<UserDTO> updateUser(@RequestBody ChangePasswordRequest changePasswordRequest,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
|
||||
|
||||
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
|
||||
|
||||
User user = authToken.getUser();
|
||||
|
||||
if (!changePasswordRequest.getEmail().equals(user.getEmail())) {
|
||||
throw new InvalidUserEmail("Email is not valid: " + changePasswordRequest.getEmail() + "not found");
|
||||
}
|
||||
|
||||
User updateUser = userService.changePassword(user, changePasswordRequest);
|
||||
|
||||
return ResponseEntity.ok(userMapper.toDTO(updateUser));
|
||||
}
|
||||
|
||||
@PatchMapping("/user-update")
|
||||
public ResponseEntity<UserDTO> updateUser(@RequestBody UserRequest userRequest,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
|
||||
|
||||
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
|
||||
|
||||
User user = authToken.getUser();
|
||||
|
||||
return ResponseEntity.ok(userService.updateUser(userRequest, user));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package ru.soune.nocopy.controller.file;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.file.FileEntityResponse;
|
||||
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.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/files")
|
||||
@RequiredArgsConstructor
|
||||
public class FileDownloadController {
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
/**
|
||||
* Скачать файл по ID FileEntity
|
||||
*/
|
||||
@GetMapping("/download/{fileId}")
|
||||
public ResponseEntity<Resource> downloadFile(
|
||||
@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).build();
|
||||
}
|
||||
|
||||
if (!fileInfo.isExistsOnDisk()) {
|
||||
return ResponseEntity.status(404)
|
||||
.body(null);
|
||||
}
|
||||
|
||||
Path filePath = Paths.get(fileInfo.getFilePath());
|
||||
Resource resource = new UrlResource(filePath.toUri());
|
||||
|
||||
if (!resource.exists()) {
|
||||
return ResponseEntity.status(404).build();
|
||||
}
|
||||
|
||||
String contentType = determineContentType(filePath);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + fileInfo.getOriginalFileName() + "\"")
|
||||
.header(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileInfo.getFileSize()))
|
||||
.body(resource);
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Error downloading file", e);
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить предпросмотр файла (если поддерживается)
|
||||
*/
|
||||
@GetMapping("/preview/{fileId}")
|
||||
public ResponseEntity<Resource> previewFile(
|
||||
@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).build();
|
||||
}
|
||||
|
||||
if (!isPreviewSupported(fileInfo.getMimeType())) {
|
||||
return ResponseEntity.status(415)
|
||||
.body(null);
|
||||
}
|
||||
|
||||
Path filePath = Paths.get(fileInfo.getFilePath());
|
||||
Resource resource = new UrlResource(filePath.toUri());
|
||||
|
||||
if (!resource.exists()) {
|
||||
return ResponseEntity.status(404).build();
|
||||
}
|
||||
|
||||
String contentType = determineContentType(filePath);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"inline; filename=\"" + fileInfo.getOriginalFileName() + "\"")
|
||||
.body(resource);
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Error previewing file", e);
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить информацию о файле для фронтенда
|
||||
*/
|
||||
@GetMapping("/info/{fileId}")
|
||||
public ResponseEntity<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).build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(fileInfo);
|
||||
|
||||
} catch (NotFoundAuthToken e) {
|
||||
return ResponseEntity.status(401).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file info", e);
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
|
||||
private String determineContentType(Path filePath) throws IOException {
|
||||
String contentType = Files.probeContentType(filePath);
|
||||
if (contentType == null) {
|
||||
contentType = "application/octet-stream";
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private boolean isPreviewSupported(String mimeType) {
|
||||
if (mimeType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mimeType.startsWith("image/") ||
|
||||
mimeType.startsWith("text/") ||
|
||||
mimeType.equals("application/pdf") ||
|
||||
mimeType.startsWith("video/") ||
|
||||
mimeType.startsWith("audio/");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
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;
|
||||
|
||||
private final AuthTokenRepository authTokenRepository;
|
||||
|
||||
private final FileUploadSessionRepository fileUploadSessionRepository;
|
||||
|
||||
//TODO ADD TEMPLATE JSON, RESPONSE OK,ADD MESSAGE CODE
|
||||
@PostMapping("/init")
|
||||
public ResponseEntity<FileApiResponse<FileUploadSession>> initUpload(@Valid @RequestBody InitUploadRequest request,
|
||||
@RequestHeader("Authorization") String tokenHeader) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
|
||||
|
||||
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
|
||||
|
||||
try {
|
||||
FileUploadSession session = fileUploadService.initUpload(authToken.getUser().getId(), request.getFileName(),
|
||||
request.getFileType(), request.getExtension(), request.getFileSize());
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(session));
|
||||
} catch (Exception e) {
|
||||
log.error("Error initializing upload", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/file-types")
|
||||
public ResponseEntity<List<FileType>> getFileTypes() {
|
||||
return ResponseEntity.ok(Arrays.asList(FileType.values()));
|
||||
}
|
||||
|
||||
|
||||
@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()));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/progress/{uploadId}")
|
||||
public ResponseEntity<FileApiResponse<UploadProgressResponse>> getProgress(
|
||||
@PathVariable String uploadId) {
|
||||
|
||||
try {
|
||||
UploadProgressResponse progress = fileUploadService.getUploadProgress(uploadId);
|
||||
return ResponseEntity.ok(FileApiResponse.success(progress));
|
||||
|
||||
} catch (UploadSessionNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(FileApiResponse.error("Upload session not found"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting progress", e);
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error("Failed to get progress"));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{uploadId}/complete")
|
||||
public ResponseEntity<FileApiResponse<UploadProgressResponse>> completeUpload(
|
||||
@PathVariable String uploadId) {
|
||||
|
||||
log.info("Manual completion requested for session: {}", uploadId);
|
||||
|
||||
try {
|
||||
UploadProgressResponse progress = fileUploadService.getUploadProgress(uploadId);
|
||||
|
||||
if (progress.getStatus() == UploadStatus.COMPLETED) {
|
||||
return ResponseEntity.ok(FileApiResponse.success(
|
||||
"Upload already completed", progress));
|
||||
}
|
||||
|
||||
if (progress.getUploadedChunks() < progress.getTotalChunks()) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error(
|
||||
String.format("Not all chunks uploaded: %d/%d",
|
||||
progress.getUploadedChunks(), progress.getTotalChunks())));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(
|
||||
"File assembly in progress", progress));
|
||||
|
||||
} catch (UploadSessionNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(FileApiResponse.error("Upload session not found"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error completing upload", e);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(FileApiResponse.error("Failed to complete upload"));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{uploadId}/cancel")
|
||||
public ResponseEntity<FileApiResponse<Void>> cancelUpload(
|
||||
@PathVariable String uploadId) {
|
||||
|
||||
log.info("Cancelling upload session: {}", uploadId);
|
||||
|
||||
try {
|
||||
fileUploadService.cancelUpload(uploadId);
|
||||
return ResponseEntity.ok(FileApiResponse.success(
|
||||
"Upload cancelled successfully", null));
|
||||
|
||||
} catch (UploadSessionNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(FileApiResponse.error("Upload session not found"));
|
||||
} catch (FileUploadException e) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error(e.getMessage()));
|
||||
} catch (Exception e) {
|
||||
log.error("Error cancelling upload", e);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(FileApiResponse.error("Failed to cancel upload"));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/{uploadId}/retry")
|
||||
public ResponseEntity<FileApiResponse<UploadProgressResponse>> retryUpload(
|
||||
@PathVariable String uploadId) {
|
||||
|
||||
log.info("Retry requested for failed upload: {}", uploadId);
|
||||
|
||||
try {
|
||||
fileUploadService.retryFailedUpload(uploadId);
|
||||
UploadProgressResponse progress = fileUploadService.getUploadProgress(uploadId);
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(
|
||||
"Upload retry initiated", progress));
|
||||
|
||||
} catch (UploadSessionNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(FileApiResponse.error("Upload session not found"));
|
||||
} catch (FileUploadException e) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(FileApiResponse.error(e.getMessage()));
|
||||
} catch (Exception e) {
|
||||
log.error("Error retrying upload", e);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(FileApiResponse.error("Failed to retry upload"));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/{uploadId}/chunks")
|
||||
public ResponseEntity<FileApiResponse<Map<Integer, Boolean>>> getChunkStatus(
|
||||
@PathVariable String uploadId) {
|
||||
|
||||
try {
|
||||
FileUploadSession session = fileUploadSessionRepository.findById(uploadId)
|
||||
.orElseThrow(() -> new UploadSessionNotFoundException(uploadId));
|
||||
|
||||
Map<Integer, Boolean> chunkStatus = new HashMap<>();
|
||||
for (int i = 0; i < session.getTotalChunks(); i++) {
|
||||
chunkStatus.put(i, session.getChunkPaths().containsKey(i));
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(FileApiResponse.success(chunkStatus));
|
||||
|
||||
} catch (UploadSessionNotFoundException e) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(FileApiResponse.error("Upload session not found"));
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting chunk status", e);
|
||||
return ResponseEntity.internalServerError()
|
||||
.body(FileApiResponse.error("Failed to get chunk status"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user