diff --git a/README.md b/README.md index d133197..aa5e26c 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,13 @@ GRANT ALL ON SEQUENCES TO ncp_db; Зайти в контейнер: docker exec -it {name} bash +------ + +Создать для сущности FileEntity sequence для икремента индетификатора + +CREATE SEQUENCE IF NOT EXISTS file_support_id_seq START 1; + +ALTER TABLE file_entities +ALTER COLUMN support_id +SET DEFAULT nextval('file_support_id_seq'); + diff --git a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java index 674b27c..d8cba33 100644 --- a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java +++ b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java @@ -17,7 +17,8 @@ public class HandlerConfig { FileUploadHandler upload, FileEntityHandler file, LogoutRequestHandler logoutHandler, - ImageFoundRequestHandler imageFoundRequestHandler + ImageFoundRequestHandler imageFoundRequestHandler, + AuthRequestHandler authRequestHandler ) { Map map = new HashMap<>(); map.put(20001, login); @@ -26,6 +27,7 @@ public class HandlerConfig { map.put(20005, file); map.put(20006, logoutHandler); map.put(20007, imageFoundRequestHandler); + map.put(20008, authRequestHandler); return map; } diff --git a/src/main/java/ru/soune/nocopy/controller/ApiController.java b/src/main/java/ru/soune/nocopy/controller/ApiController.java index bae2817..d80b45c 100644 --- a/src/main/java/ru/soune/nocopy/controller/ApiController.java +++ b/src/main/java/ru/soune/nocopy/controller/ApiController.java @@ -21,6 +21,10 @@ import ru.soune.nocopy.dto.RegAnswer; import ru.soune.nocopy.dto.file.*; import ru.soune.nocopy.entity.AuthToken; import ru.soune.nocopy.entity.file.FileEntity; +import ru.soune.nocopy.dto.file.ChunkUploadResponse; +import ru.soune.nocopy.dto.file.CompleteUploadResponse; +import ru.soune.nocopy.dto.file.FileEntityResponse; +import ru.soune.nocopy.dto.file.UploadProgress; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.entity.file.UploadStatus; import ru.soune.nocopy.exception.*; @@ -28,6 +32,7 @@ import ru.soune.nocopy.handler.*; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.FileEntityRepository; import ru.soune.nocopy.service.FileSimilarityService; +import ru.soune.nocopy.service.auth.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileUploadService; @@ -52,7 +57,7 @@ public class ApiController { private final FileEntityService fileEntityService; - private final AuthTokenRepository authTokenRepository; + private final AuthService authService; private final FileSimilarityService fileSimilarityService; @@ -79,6 +84,9 @@ public class ApiController { return ResponseEntity.ok().body(response); } catch (ValidationException e) { return createValidationErrorResponse(e.getBindingResult(), e.getMsgId()); + } catch (NotFoundAuthToken e) { + return ResponseEntity.ok().body(new BaseResponse(msgId, MessageCode.AUTH_TOKEN_NOT_FOUND.getCode(), + MessageCode.AUTH_TOKEN_NOT_FOUND.getDescription(), new HashMap<>())); } catch (NotValidFieldException e) { throw e; } catch (Exception e) { @@ -272,7 +280,7 @@ public class ApiController { errorData)); } - Long userId = getUserIdFromToken(tokenHeader); + Long userId = authService.useUserAuthToken(tokenHeader); FileEntityResponse entityResponse = fileEntityService.getById(fileId, version); if (!entityResponse.getUserId().equals(userId)) { @@ -455,10 +463,15 @@ public class ApiController { return contentType; } - 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(); + private boolean isPreviewSupported(String mimeType) { + if (mimeType == null) { + return false; + } + + return mimeType.startsWith("image") || + mimeType.startsWith("text") || + mimeType.equals("pdf") || + mimeType.startsWith("video") || + mimeType.startsWith("audio"); } } \ No newline at end of file diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index 3332a30..3a4c540 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -20,6 +20,7 @@ public enum MessageCode { AUTH_EMAIL_NOT_FOUND(4, "Email not found"), AUTH_EMAIL_OR_TOKEN_NOT_FOUND(4, "Email or Token not found "), AUTH_TOKEN_MISMATCH(4, "Token mismatch"), + AUTH_TOKEN_NOT_FOUND(4, "Token not found"), FILE_NOT_FOUND(4, "File not found"), AUTH_PASSWORD_NOT_MATCHES(2, "Password does not match"), SIMILAR_FILES_FOUND(0, "Similar files found"); diff --git a/src/main/java/ru/soune/nocopy/dto/TokenProcessRequest.java b/src/main/java/ru/soune/nocopy/dto/TokenProcessRequest.java new file mode 100644 index 0000000..646f9c6 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/TokenProcessRequest.java @@ -0,0 +1,11 @@ +package ru.soune.nocopy.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class TokenProcessRequest { + + @JsonProperty("token") + private String token; +} diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileApiResponse.java b/src/main/java/ru/soune/nocopy/dto/file/FileApiResponse.java deleted file mode 100644 index 9feb546..0000000 --- a/src/main/java/ru/soune/nocopy/dto/file/FileApiResponse.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.soune.nocopy.dto.file; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class FileApiResponse { - - private boolean success; - private String message; - private T data; - private String error; - - public static FileApiResponse success(T data) { - return FileApiResponse.builder() - .success(true) - .message("Operation completed successfully") - .data(data) - .build(); - } - - public static FileApiResponse success(String message, T data) { - return FileApiResponse.builder() - .success(true) - .message(message) - .data(data) - .build(); - } - - public static FileApiResponse error(String error) { - return FileApiResponse.builder() - .success(false) - .error(error) - .build(); - } -} diff --git a/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java b/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java index 3e89725..cbfae88 100644 --- a/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java +++ b/src/main/java/ru/soune/nocopy/dto/file/FileEntityResponse.java @@ -30,4 +30,5 @@ public class FileEntityResponse { private String formattedSize; private String downloadUrl; private boolean existsOnDisk; + private Integer supportId; } diff --git a/src/main/java/ru/soune/nocopy/entity/AuthToken.java b/src/main/java/ru/soune/nocopy/entity/AuthToken.java index 63cf34a..6fa1a9c 100644 --- a/src/main/java/ru/soune/nocopy/entity/AuthToken.java +++ b/src/main/java/ru/soune/nocopy/entity/AuthToken.java @@ -30,7 +30,7 @@ public class AuthToken { private String token; @Column(name = "expires_at", nullable = false) - private LocalDateTime expiresAt = LocalDateTime.now().plusDays(30); + private LocalDateTime expiresAt = LocalDateTime.now().plusHours(1); @CreatedDate @Column(name = "created_at", updatable = false, nullable = false) @@ -41,8 +41,4 @@ public class AuthToken { @Column(name = "is_active") private Boolean isActive = true; - - public boolean isValid() { - return Boolean.TRUE.equals(isActive) && expiresAt.isAfter(LocalDateTime.now()); - } } diff --git a/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java b/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java index e07b591..16e25a4 100644 --- a/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java +++ b/src/main/java/ru/soune/nocopy/entity/file/FileEntity.java @@ -3,10 +3,13 @@ package ru.soune.nocopy.entity.file; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.GenerationTime; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.sql.DriverManager; +import java.sql.SQLException; import java.time.LocalDateTime; @Data @@ -17,11 +20,13 @@ import java.time.LocalDateTime; @Table(name = "file_entities") @EntityListeners(AuditingEntityListener.class) public class FileEntity { - @Id @GeneratedValue(strategy = GenerationType.UUID) private String id; + @Column(name = "support_id", unique = true, updatable = false) + private Integer supportId; + @Column(name = "user_id", nullable = false) private Long userId; @@ -75,9 +80,4 @@ public class FileEntity { } this.updatedAt = LocalDateTime.now(); } - - @PreUpdate - public void preUpdate() { - this.updatedAt = LocalDateTime.now(); - } } diff --git a/src/main/java/ru/soune/nocopy/handler/AuthRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/AuthRequestHandler.java new file mode 100644 index 0000000..c79ec06 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/AuthRequestHandler.java @@ -0,0 +1,37 @@ +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.BaseRequest; +import ru.soune.nocopy.dto.BaseResponse; +import ru.soune.nocopy.dto.MessageCode; +import ru.soune.nocopy.dto.TokenProcessRequest; +import ru.soune.nocopy.exception.NotFoundAuthToken; +import ru.soune.nocopy.service.auth.AuthService; + +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class AuthRequestHandler implements RequestHandler { + + private final ObjectMapper objectMapper; + + private final AuthService authService; + + @Override + public BaseResponse handle(BaseRequest request) throws NotFoundAuthToken { + TokenProcessRequest tokenProcessRequest = objectMapper.convertValue(request.getMessageBody(), + TokenProcessRequest.class); + + authService.useUserAuthToken(tokenProcessRequest.getToken()); + + return new BaseResponse(request.getMsgId(), + MessageCode.SUCCESS.getCode(), + "Token processed successfully", + Map.of("token", tokenProcessRequest.getToken())); + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java index a7f9c73..4676310 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileEntityHandler.java @@ -6,13 +6,12 @@ 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.entity.file.FileEntity; import ru.soune.nocopy.entity.file.FileStatus; import ru.soune.nocopy.exception.FileEntityNotFoundException; import ru.soune.nocopy.exception.NotFoundAuthToken; -import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.FileEntityRepository; +import ru.soune.nocopy.service.auth.AuthService; import ru.soune.nocopy.service.file.FileEntityService; import ru.soune.nocopy.service.file.FileStatsService; @@ -26,9 +25,9 @@ public class FileEntityHandler implements RequestHandler { private final FileEntityService fileEntityService; - private final FileStatsService fileStatsService; + private final AuthService authService; - private final AuthTokenRepository authTokenRepository; + private final FileStatsService fileStatsService; private final ObjectMapper objectMapper; @@ -81,7 +80,7 @@ public class FileEntityHandler implements RequestHandler { } private BaseResponse handleGetFilesUserInfo(BaseRequest request, FileEntityRequest fileRequest) { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileInfoUserResponse userFileStats = fileStatsService.getUserFileStats(userId); return new BaseResponse(request.getMsgId(), @@ -92,7 +91,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion()); if (!fileInfo.getUserId().equals(userId)) { @@ -123,7 +122,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId(), request.getVersion()); @@ -155,7 +154,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleGetUserFiles(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; @@ -182,7 +181,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1; int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20; @@ -229,7 +228,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); long usage = fileEntityService.getUserStorageUsed(userId); StorageUsageResponse response = StorageUsageResponse.builder() @@ -259,7 +258,7 @@ public class FileEntityHandler implements RequestHandler { private BaseResponse handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) { try { - Long userId = getUserIdFromToken(fileRequest.getToken()); + Long userId = authService.useUserAuthToken(fileRequest.getToken()); String fileId = fileRequest.getFileId(); FileEntityResponse fileInfo = fileEntityService.getById(fileId, request.getVersion()); DeleteFileResponse response; @@ -306,20 +305,6 @@ public class FileEntityHandler implements RequestHandler { } } - 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)); diff --git a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java index 2c75fb0..7b98da3 100644 --- a/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/FileUploadHandler.java @@ -17,6 +17,7 @@ import ru.soune.nocopy.exception.*; import ru.soune.nocopy.handler.validator.FileUploadRequestValidator; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.FileUploadSessionRepository; +import ru.soune.nocopy.service.auth.AuthService; import ru.soune.nocopy.service.file.FileUploadService; import java.util.*; @@ -28,6 +29,8 @@ import java.util.stream.Collectors; public class FileUploadHandler implements RequestHandler { private final FileUploadService fileUploadService; + private final AuthService authService; + private final AuthTokenRepository authTokenRepository; private final FileUploadSessionRepository fileUploadSessionRepository; @@ -86,10 +89,9 @@ public class FileUploadHandler implements RequestHandler { MessageCode.INVALID_TOKEN.getDescription(), Map.of("token", token)); } - AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token not found")); - + Long userId = authService.useUserAuthToken(token); BindingResult bindingResult = new BeanPropertyBindingResult(fileRequest, "fileRequest"); - fileUploadRequestValidator.validate(fileRequest, bindingResult); + fileUploadRequestValidator.validate(fileRequest, bindingResult); if (bindingResult.hasErrors()) { Map fieldErrors = bindingResult.getFieldErrors() @@ -105,7 +107,7 @@ public class FileUploadHandler implements RequestHandler { } FileUploadSession session = fileUploadService.initUpload( - authToken.getUser().getId(), + userId, fileRequest.getFileName(), fileRequest.getFileType(), fileRequest.getExtension(), diff --git a/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java index 95abf63..6add2a0 100644 --- a/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/LoginRequestHandler.java @@ -7,7 +7,7 @@ import ru.soune.nocopy.dto.*; import ru.soune.nocopy.entity.AuthToken; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.UserRepository; -import ru.soune.nocopy.service.AuthService; +import ru.soune.nocopy.service.auth.AuthService; import java.util.Arrays; import java.util.Map; @@ -17,7 +17,9 @@ import java.util.Map; public class LoginRequestHandler implements RequestHandler { private final UserRepository userRepository; + private final AuthService authService; + private final ObjectMapper objectMapper; @Override @@ -34,9 +36,12 @@ public class LoginRequestHandler implements RequestHandler { } AuthToken authToken = authService.login(loginRequest); + String token = authToken.getToken(); LoginAnswer loginAnswer = new LoginAnswer(); - loginAnswer.setToken(authToken.getToken()); + loginAnswer.setToken(token); + + authService.useUserAuthToken(token); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), loginAnswer); diff --git a/src/main/java/ru/soune/nocopy/handler/LogoutRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/LogoutRequestHandler.java index 44fa253..e12284c 100644 --- a/src/main/java/ru/soune/nocopy/handler/LogoutRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/LogoutRequestHandler.java @@ -9,7 +9,7 @@ import ru.soune.nocopy.entity.User; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.UserRepository; -import ru.soune.nocopy.service.AuthService; +import ru.soune.nocopy.service.auth.AuthService; import java.util.Objects; import java.util.Optional; diff --git a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java index 349f0a7..86fcbc7 100644 --- a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java @@ -12,7 +12,7 @@ import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.exception.ValidationException; import ru.soune.nocopy.handler.validator.RegRequestValidator; import ru.soune.nocopy.repository.UserRepository; -import ru.soune.nocopy.service.AuthService; +import ru.soune.nocopy.service.auth.AuthService; import java.util.Arrays; import java.util.Map; @@ -52,9 +52,11 @@ public class RegRequestHandler implements RequestHandler { } AuthToken authToken = authService.register(regRequest); + String token = authToken.getToken(); + authService.useUserAuthToken(token); RegAnswer regAnswer = new RegAnswer(); - regAnswer.setToken(authToken.getToken()); + regAnswer.setToken(token); return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(), MessageCode.SUCCESS.getDescription(), regAnswer); diff --git a/src/main/java/ru/soune/nocopy/repository/AuthTokenRepository.java b/src/main/java/ru/soune/nocopy/repository/AuthTokenRepository.java index 74826fd..cbd7529 100644 --- a/src/main/java/ru/soune/nocopy/repository/AuthTokenRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/AuthTokenRepository.java @@ -4,11 +4,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import ru.soune.nocopy.entity.AuthToken; -import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; @Repository public interface AuthTokenRepository extends JpaRepository { - Optional findByTokenAndExpiresAtAfter(String token, LocalDate expiresAtAfter); + List findByExpiresAtBefore(LocalDateTime expiresAtBefore); + Optional findByLastUsedAtBefore(LocalDateTime lastUsedAt); Optional findByToken(String token); } diff --git a/src/main/java/ru/soune/nocopy/service/AuthService.java b/src/main/java/ru/soune/nocopy/service/auth/AuthService.java similarity index 80% rename from src/main/java/ru/soune/nocopy/service/AuthService.java rename to src/main/java/ru/soune/nocopy/service/auth/AuthService.java index 2377b2b..f14d6d1 100644 --- a/src/main/java/ru/soune/nocopy/service/AuthService.java +++ b/src/main/java/ru/soune/nocopy/service/auth/AuthService.java @@ -1,13 +1,13 @@ -package ru.soune.nocopy.service; +package ru.soune.nocopy.service.auth; import lombok.RequiredArgsConstructor; -import org.springframework.context.MessageSource; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.*; import ru.soune.nocopy.entity.AuthToken; import ru.soune.nocopy.entity.User; +import ru.soune.nocopy.exception.NotFoundAuthToken; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.AuthTokenRepository; import ru.soune.nocopy.repository.UserRepository; @@ -29,8 +29,6 @@ public class AuthService { private final PasswordEncoder passwordEncoder; - private final MessageSource messageSource; - private final SecureRandom secureRandom = new SecureRandom(); @Transactional @@ -81,6 +79,26 @@ public class AuthService { return authTokenRepository.save(authToken); } + @Transactional + public Long useUserAuthToken(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")); + authToken.setLastUsedAt(LocalDateTime.now()); + authToken.setExpiresAt(LocalDateTime.now().plusHours(1)); + + authTokenRepository.save(authToken); + + return authToken.getUser().getId(); + } + @Transactional public void logout(String token) { authTokenRepository.findByToken(token) diff --git a/src/main/java/ru/soune/nocopy/service/auth/CleanupTokenSessionsService.java b/src/main/java/ru/soune/nocopy/service/auth/CleanupTokenSessionsService.java new file mode 100644 index 0000000..5ac5128 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/service/auth/CleanupTokenSessionsService.java @@ -0,0 +1,37 @@ +package ru.soune.nocopy.service.auth; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import ru.soune.nocopy.entity.AuthToken; +import ru.soune.nocopy.repository.AuthTokenRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CleanupTokenSessionsService { + @Autowired + private AuthTokenRepository authTokenRepository; + + @Value("${file.storage.auth-token-life-hours}") + private int authTokenLife; + + @Transactional + @Scheduled(fixedDelay = 30000) + public void cleanupExpiredTokens() { + LocalDateTime now = LocalDateTime.now(); + + List expiredTokens = authTokenRepository.findByExpiresAtBefore(now); + + if (!expiredTokens.isEmpty()) { + authTokenRepository.deleteAll(expiredTokens); + } + } +} diff --git a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java index 0cdf62c..1f3464e 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java @@ -207,6 +207,7 @@ public class FileEntityService { .formattedSize(formatFileSize(fileEntity.getFileSize())) .downloadUrl("/api/v" + version + "/files/download/" + fileEntity.getId()) .existsOnDisk(existsOnDisk) + .supportId(fileEntity.getSupportId()) .build(); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d31998b..c460c1f 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -33,6 +33,7 @@ file: max-retry-attempts: ${MAX_RETRY_ATTEMPTS:3} chunk-timeout-ms: ${CHUNK_TIMEOUT_MS:300000} # 5 минут temp-ttl-hours: ${TEMP_TTL_HOURS:72} # 3 дня + auth-token-life-hours: ${AUTH_TOKEN_LIFE:1} # 1 час session-expiry-hours: ${SESSION_EXPIRY_HOURS:24} security: