Compare commits
62
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69e758a710 | ||
|
|
d7874aa37a | ||
|
|
ad4850414a | ||
|
|
3d2c7ecb05 | ||
|
|
51170891ce | ||
|
|
d775802a99 | ||
|
|
dfb1fca8cd | ||
|
|
3f375d3b12 | ||
|
|
a7b9f22939 | ||
|
|
e78f882417 | ||
|
|
2445b70c7a | ||
|
|
41ac8933e9 | ||
|
|
7af909c4f2 | ||
|
|
976d1f751e | ||
|
|
dbef82d896 | ||
|
|
7e1877ffc3 | ||
|
|
8ce07c6b81 | ||
|
|
fa1892585a | ||
|
|
6d6721fb0a | ||
|
|
535be69840 | ||
|
|
fdea57d947 | ||
|
|
0aa7f5c865 | ||
|
|
067b967a17 | ||
|
|
9a8a4a72f4 | ||
|
|
964910ec69 | ||
|
|
547e70ecdd | ||
|
|
78b97b48c9 | ||
|
|
4e0579c952 | ||
|
|
57dcc12cf2 | ||
|
|
a01adfc848 | ||
|
|
8965ffdd1f | ||
|
|
a21eb899b3 | ||
|
|
95ebcf0241 | ||
|
|
a3e6e0091a | ||
|
|
2f38a7c455 | ||
|
|
17aa52d4ac | ||
|
|
8eb0772196 | ||
|
|
c1782cf59b | ||
|
|
46cf252699 | ||
|
|
ebcb68135b | ||
|
|
e68ad3d3d3 | ||
|
|
12e001211b | ||
|
|
40f8effb31 | ||
|
|
69c3d876ae | ||
|
|
610415951d | ||
|
|
9ca116856c | ||
|
|
86db70caf4 | ||
|
|
0b2ef108ca | ||
|
|
0fbe725eb9 | ||
|
|
29cdbb887f | ||
|
|
2bf5e99fbb | ||
|
|
655e8f0844 | ||
|
|
31aa1c0545 | ||
|
|
97d6c8317e | ||
|
|
a42f3e8ed8 | ||
|
|
46c75deacb | ||
|
|
4930a73762 | ||
|
|
726da25ddf | ||
|
|
a9b7b7ca6e | ||
|
|
daef30ec11 | ||
|
|
a25b33fe13 | ||
|
|
2985d84b04 |
@@ -490,3 +490,13 @@ killall 3proxy # Остановка прокси
|
||||
bash
|
||||
curl -x http://193.46.217.94:3128 https://api.ipify.org # Проверка прокси
|
||||
telnet 193.46.217.94 3128 # Проверка порта
|
||||
|
||||
------
|
||||
|
||||
Настройка Яндекс Клауд
|
||||
|
||||
1. YCAJEpWAtaVkVGX0sH6_EupEg - индетификатор ключа
|
||||
2. YCMmVykfXrZ_nfU13Vo4yoCVGa70DnTlBgF1pUzO - секретный ключ
|
||||
|
||||
private static final String KEY_ID = "YCAJEmHZcWxzf4oGWvETG3mms";
|
||||
private static final String SECRET_KEY = "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG";
|
||||
@@ -65,6 +65,12 @@ dependencies {
|
||||
|
||||
implementation project(':referral')
|
||||
|
||||
//cloud
|
||||
implementation("software.amazon.awssdk:aws-sdk-java:2.29.33")
|
||||
implementation("software.amazon.awssdk:apache-client:2.29.33")
|
||||
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
||||
@@ -9,5 +9,6 @@ interface Referral {
|
||||
val totalIncome: Int
|
||||
val availableIncome: Int
|
||||
val holdBalance: Int
|
||||
val spentBalance: Int
|
||||
val active: Boolean
|
||||
}
|
||||
@@ -18,6 +18,7 @@ class ReferralService(
|
||||
override val totalIncome = 0
|
||||
override val availableIncome = 0
|
||||
override val holdBalance: Int = 0
|
||||
override val spentBalance: Int = 0
|
||||
override val active: Boolean = false
|
||||
}
|
||||
|
||||
@@ -59,7 +60,8 @@ class ReferralService(
|
||||
totalIncome = referral.totalIncome,
|
||||
availableIncome = referral.availableIncome,
|
||||
holdBalance = referral.holdBalance,
|
||||
referralLink = referral.referralLink
|
||||
referralLink = referral.referralLink,
|
||||
spentBalance = referral.spentBalance,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@ data class ReferralStat(
|
||||
val availableIncome: Int,
|
||||
val holdBalance: Int,
|
||||
val referralLink: String,
|
||||
val spentBalance: Int,
|
||||
)
|
||||
@@ -12,14 +12,22 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@AllArgsConstructor
|
||||
public class ApplicationConfig {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final FileUtil fileUtil;
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
@@ -36,8 +44,11 @@ public class ApplicationConfig {
|
||||
AudioFilePathProvider audioFilePathProvider,
|
||||
DocumentLocalSearch documentLocalSearch) {
|
||||
|
||||
List<FileProtector.FileInfo> initialFiles = loadProcessingFile();
|
||||
|
||||
//TODO инициализация файлов при создании
|
||||
return new com.vrt.NoCopyFileService(
|
||||
Collections.emptyList(),
|
||||
initialFiles,
|
||||
fileProvider,
|
||||
processingListener,
|
||||
imageUniqueCheck,
|
||||
@@ -47,5 +58,13 @@ public class ApplicationConfig {
|
||||
documentLocalSearch
|
||||
);
|
||||
}
|
||||
|
||||
private List<FileProtector.FileInfo> loadProcessingFile() {
|
||||
List<FileEntity> byProtectionStatus =
|
||||
fileEntityRepository.findByProtectionStatus(ProtectionStatus.PROCESSING);
|
||||
|
||||
return byProtectionStatus.stream().map(fileEntity ->
|
||||
fileUtil.createFileInfo(fileEntity, null)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ public class HandlerConfig {
|
||||
ViolationHandler violationHandler,
|
||||
ViolationStatisticsHandler violationStatisticsHandler,
|
||||
GlobalSearchHandler globalSearchHandler,
|
||||
ViolationNotionHandler violationNotionHandler
|
||||
ViolationNotionHandler violationNotionHandler,
|
||||
ComplaintEntityHandler complaintEntityHandler,
|
||||
UserInfoHandler userInfoHandler
|
||||
) {
|
||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||
map.put(20001, login);
|
||||
@@ -56,6 +58,8 @@ public class HandlerConfig {
|
||||
map.put(30010, violationStatisticsHandler);
|
||||
map.put(30011, globalSearchHandler);
|
||||
map.put(30012, violationNotionHandler);
|
||||
map.put(30013, complaintEntityHandler);
|
||||
map.put(30014, userInfoHandler);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import ru.soune.nocopy.dto.register.RegAnswer;
|
||||
import ru.soune.nocopy.entity.company.Company;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
import ru.soune.nocopy.entity.file.UploadStatus;
|
||||
import ru.soune.nocopy.entity.user.AuthToken;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
@@ -37,6 +38,7 @@ import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.FileSimilarityService;
|
||||
import ru.soune.nocopy.service.file.CheckCounterService;
|
||||
import ru.soune.nocopy.service.file.ProtectionsLimitService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
@@ -47,7 +49,10 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@@ -67,6 +72,8 @@ public class ApiController {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final NoCopyFileService noCopyFileService;
|
||||
|
||||
private final FileUtil fileUtil;
|
||||
|
||||
private final ProtectionsLimitService protectionsLimitService;
|
||||
@@ -77,6 +84,8 @@ public class ApiController {
|
||||
|
||||
private final CheckCounterService checkCounterService;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@PostMapping("/v{version}/data")
|
||||
public ResponseEntity<?> handlePostRequest(@RequestBody BaseRequest request,
|
||||
@PathVariable("version") int version) {
|
||||
@@ -157,7 +166,7 @@ public class ApiController {
|
||||
MessageCode.DUPLICATE_FILE_UPLOAD.getDescription(),
|
||||
duplicateData
|
||||
));
|
||||
}catch (FileFormatException e){
|
||||
} catch (FileFormatException e){
|
||||
return buildErrorResponse(uploadId, chunkNumber, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("Error uploading chunk", e);
|
||||
@@ -391,6 +400,18 @@ public class ApiController {
|
||||
|
||||
Long userId = authService.useUserAuthToken(tokenHeader);
|
||||
FileEntityResponse entityResponse = fileEntityService.getById(fileId, version);
|
||||
FileStatus status = entityResponse.getStatus();
|
||||
|
||||
if (status.equals(FileStatus.BLOCKED) || status.equals(FileStatus.DELETED) ||
|
||||
status.equals(FileStatus.REMOVED)) {
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
errorData.put("fileId", entityResponse.getId());
|
||||
|
||||
return ResponseEntity.ok().body(new BaseResponse(20004,
|
||||
MessageCode.FILE_IS_BLOCKED.getCode(),
|
||||
MessageCode.FILE_IS_BLOCKED.getDescription(),
|
||||
errorData));
|
||||
}
|
||||
|
||||
User fileUser = userRepository.findById(entityResponse.getUserId()).orElseThrow();
|
||||
User user = userRepository.findById(userId).orElseThrow();
|
||||
@@ -436,20 +457,21 @@ public class ApiController {
|
||||
errorData));
|
||||
}
|
||||
|
||||
|
||||
Path filePath = Paths.get(entityResponse.getProtectedFilePath());
|
||||
FileSystemResource resource = new FileSystemResource(filePath);
|
||||
File file = cloudStorageService.readFileFromStorageByPath(
|
||||
entityResponse.getProtectedFilePath());
|
||||
long fileSize = Files.size(filePath);
|
||||
|
||||
if (!resource.exists()) {
|
||||
if (!file.exists()) {
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
errorData.put("resource", resource.exists());
|
||||
errorData.put("file", file.exists());
|
||||
|
||||
return ResponseEntity.ok().body(new BaseResponse(20004,
|
||||
MessageCode.FILE_DOWNLOAD_ERROR.getCode(),
|
||||
MessageCode.FILE_DOWNLOAD_ERROR.getDescription(),
|
||||
errorData));
|
||||
}
|
||||
|
||||
String contentType = determineContentType(filePath);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
@@ -457,7 +479,7 @@ public class ApiController {
|
||||
.contentType(MediaType.parseMediaType(contentType))
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + entityResponse.getFileName() + "\"")
|
||||
.body(resource);
|
||||
.body(file);
|
||||
} catch (FileEntityNotFoundException e) {
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
errorData.put("fileId", fileId);
|
||||
@@ -500,33 +522,6 @@ public class ApiController {
|
||||
return ResponseEntity.ok().body(fileTypeStats);
|
||||
}
|
||||
|
||||
private boolean hasDuplicate(List<SimilarFileDTO> similarFiles) {
|
||||
return similarFiles.stream().anyMatch(f -> f.getHammingDistance() <= 5);
|
||||
}
|
||||
|
||||
private ResponseEntity<BaseResponse> handleDuplicate(FileEntity fileEntity, List<SimilarFileDTO> similarFiles)
|
||||
throws IOException {
|
||||
fileEntityService.deleteFromDisk(fileEntity);
|
||||
|
||||
fileEntityService.softDeleteFileWithHash(fileEntity);
|
||||
|
||||
Optional<FileEntity> originalFile = fileEntityRepository.findById(similarFiles.get(0).getFileId());
|
||||
|
||||
if (originalFile.isPresent()) {
|
||||
Map<String, String> duplicateInfo = Map.of(
|
||||
"duplicate_file_id", originalFile.get().getId(),
|
||||
"owner_user_id", String.valueOf(originalFile.get().getUserId()));
|
||||
|
||||
return ResponseEntity.ok().body(new BaseResponse(
|
||||
20004,
|
||||
MessageCode.DUPLICATE_FILE_UPLOAD.getCode(),
|
||||
"Failed to upload chunk, duplicate",
|
||||
duplicateInfo));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ResponseEntity<BaseResponse> buildSuccessResponse(String uploadId, Integer chunkNumber, MultipartFile chunk,
|
||||
String fileId) {
|
||||
ChunkUploadResponse responseBody = ChunkUploadResponse.builder()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import com.vrt.NoCopyFileService;
|
||||
import com.vrt.fileprotection.FileProtector;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -15,6 +17,7 @@ import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.file.CheckCounterService;
|
||||
import ru.soune.nocopy.service.file.FileStorageService;
|
||||
import ru.soune.nocopy.service.file.ImageResizeService;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URLEncoder;
|
||||
@@ -45,6 +48,12 @@ public class FileController {
|
||||
@Autowired
|
||||
private ImageResizeService imageResizeService;
|
||||
|
||||
@Autowired
|
||||
private NoCopyFileService noCopyFileService;
|
||||
|
||||
@Autowired
|
||||
private FileUtil fileUtil;
|
||||
|
||||
@GetMapping("/public/{fileId}")
|
||||
public ResponseEntity<Resource> getPublicFile(@PathVariable String fileId) {
|
||||
try {
|
||||
@@ -145,4 +154,16 @@ public class FileController {
|
||||
.ok()
|
||||
.body(protectedFileCheck);
|
||||
}
|
||||
|
||||
@PostMapping("protect/{fileId}/{convertTo}")
|
||||
public ResponseEntity<FileProtector.FileInfo> convert(@PathVariable String fileId, @PathVariable String convertTo) {
|
||||
FileEntity file = fileRepository.findByFileId(fileId);
|
||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(file, convertTo);
|
||||
|
||||
noCopyFileService.addFile(fileInfo);
|
||||
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.body(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,17 @@ import ru.soune.nocopy.entity.payout.PayoutRequest;
|
||||
import ru.soune.nocopy.entity.payout.PayoutType;
|
||||
import ru.soune.nocopy.entity.user.AuthToken;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.exception.InsufficientFundsException;
|
||||
import ru.soune.nocopy.exception.InvalidPayoutMethodException;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.exception.PendingPayoutExistsException;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
import ru.soune.nocopy.service.payout.PayoutMethodService;
|
||||
import ru.soune.nocopy.service.payout.PayoutRequestService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -40,7 +44,7 @@ public class PayoutController {
|
||||
Create payout
|
||||
*/
|
||||
@PostMapping("/create-request")
|
||||
public ResponseEntity<PayoutRequest> createPayoutRequest(@RequestHeader("Authorization") String tokenHeader,
|
||||
public ResponseEntity<?> createPayoutRequest(@RequestHeader("Authorization") String tokenHeader,
|
||||
@Valid @RequestBody PayoutRequestDTO request) {
|
||||
String token = tokenHeader.replace("Bearer ", "");
|
||||
|
||||
@@ -50,9 +54,12 @@ public class PayoutController {
|
||||
|
||||
User user = authToken.getUser();
|
||||
|
||||
try {
|
||||
PayoutRequest payoutRequest = payoutRequestService.createPayoutRequest(user.getId(), request);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(payoutRequest);
|
||||
} catch (InsufficientFundsException | PendingPayoutExistsException | InvalidPayoutMethodException e) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2,9 +2,11 @@ package ru.soune.nocopy.dto;
|
||||
|
||||
public enum MessageCode {
|
||||
SUCCESS(0, "Operation successful"),
|
||||
SUCCESS_MODERATION(0, "File moderated successfully"),
|
||||
REG_EMAIL_EXISTS(1, "Email already registered"),
|
||||
REG_EMAIL_OR_PHONE_EXISTS(1, "Email or phone already registered"),
|
||||
REFERRAL_LINK_IS_NOT_EXIST(1, "Refferal link is not exist"),
|
||||
FILE_IS_BLOCKED(1, "File is blocked,deleted or removed"),
|
||||
INVALID_FIELD(2, "Invalid field"),
|
||||
MAIL_VERIFIED_NULL(2, "Mail verified null"),
|
||||
INVALID_TOKEN(2, "Token not found or time expired"),
|
||||
@@ -51,10 +53,12 @@ public enum MessageCode {
|
||||
MONITORING_TYPE_NOT_FOUND(4, "Monitoring type not found"),
|
||||
ERROR_TARIFF_INFO(2, "Erorr with tariff info"),
|
||||
FILE_FOR_SEARCH_NOT_VALID(2, "File for search unsupported"),
|
||||
ILLEGAL_STATE(2, "File is not in moderation state"),
|
||||
NOT_VALID_FILE_TYPE_OR_COUNT_FILE(2, "Cost for file type not found, count is negative or files count" +
|
||||
"more than max valid"),
|
||||
USER_NOT_ACTIVE(2, "User not active"),
|
||||
NOTION_NOT_FOUND(4, "Notion not found"),
|
||||
NOT_FOUND(4, "Notion not found"),
|
||||
MESSAGE_IS_REQUIRED_FOR_NOTION(4, "Message is required for notion");
|
||||
|
||||
private final Integer code;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package ru.soune.nocopy.dto.complaint;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ComplaintRequest {
|
||||
@JsonProperty("action")
|
||||
private String action;
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("violation_id")
|
||||
private Long violationId;
|
||||
|
||||
@JsonProperty("text")
|
||||
private String complaintText;
|
||||
|
||||
@JsonProperty("email")
|
||||
private String email;
|
||||
|
||||
@JsonProperty("status")
|
||||
private String status;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page;
|
||||
|
||||
@JsonProperty("size")
|
||||
private Integer size;
|
||||
|
||||
@JsonProperty("sort_by")
|
||||
private String sortBy;
|
||||
|
||||
@JsonProperty("sort_direction")
|
||||
private String sortDirection;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package ru.soune.nocopy.dto.complaint;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ComplaintResponse {
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("status")
|
||||
private String status;
|
||||
|
||||
@JsonProperty("complaint_text")
|
||||
private String complaintText;
|
||||
|
||||
@JsonProperty("violation_id")
|
||||
private Long violationId;
|
||||
|
||||
@JsonProperty("email")
|
||||
private String email;
|
||||
|
||||
@JsonProperty("created_at")
|
||||
private String createdAt;
|
||||
|
||||
@JsonProperty("updated_at")
|
||||
private String updatedAt;
|
||||
|
||||
@JsonProperty("not_moderated")
|
||||
private Boolean notModerated;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class AppealInfo {
|
||||
private String appealId;
|
||||
private String status;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class AppealRequest {
|
||||
private String fileId;
|
||||
private String appealReason;
|
||||
private String additionalInfo;
|
||||
private String token;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class AppealResponse {
|
||||
private String appealId;
|
||||
private String fileId;
|
||||
private String appealReason;
|
||||
private String additionalInfo;
|
||||
private String status;
|
||||
private String adminComment;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime resolvedAt;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@@ -44,6 +45,30 @@ public class FileEntityRequest {
|
||||
@JsonProperty("type")
|
||||
private String type;
|
||||
|
||||
@JsonProperty("file_status")
|
||||
private FileStatus fileStatus;
|
||||
|
||||
@JsonProperty("date_filter")
|
||||
private String dateFilter;
|
||||
|
||||
@JsonProperty("comment")
|
||||
private String comment;
|
||||
|
||||
@JsonProperty("appeal_reason")
|
||||
private String appealReason;
|
||||
|
||||
@JsonProperty("additional_info")
|
||||
private String additionalInfo;
|
||||
|
||||
@JsonProperty("appeal_id")
|
||||
private String appealId;
|
||||
|
||||
@JsonProperty("approve")
|
||||
private Boolean approve;
|
||||
|
||||
@JsonProperty("reason_text")
|
||||
private String reasonText;
|
||||
|
||||
@JsonProperty("search_query")
|
||||
private String searchQuery;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class FileModerationInfo {
|
||||
private String fileId;
|
||||
private String fileName;
|
||||
private String ownerName;
|
||||
private String ownerEmail;
|
||||
private FileStatus currentStatus;
|
||||
private LocalDateTime uploadDate;
|
||||
private Boolean hasActiveAppeal;
|
||||
private AppealInfo activeAppeal;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.soune.nocopy.dto.file;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ModerationActionRequest {
|
||||
private String fileId;
|
||||
private FileStatus newStatus;
|
||||
private String comment;
|
||||
private String token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,4 +20,5 @@ public class SimilarFileDTO {
|
||||
LocalDateTime uploadDate;
|
||||
String url;
|
||||
Boolean owner;
|
||||
String fileStatus;
|
||||
}
|
||||
|
||||
@@ -37,4 +37,10 @@ public class RegRequest {
|
||||
|
||||
@JsonProperty("mail_verified")
|
||||
private String mailVerified;
|
||||
|
||||
@JsonProperty("ip")
|
||||
private String ip;
|
||||
|
||||
@JsonProperty("user_agent")
|
||||
private String userAgent;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ public class TariffDTO {
|
||||
private int maxFilesCount;
|
||||
private Long diskSize;
|
||||
private Long maxUsers;
|
||||
private String tariffTerm;
|
||||
}
|
||||
|
||||
@@ -49,4 +49,7 @@ public class TariffRequest {
|
||||
|
||||
@JsonProperty("user_token")
|
||||
private String userToken;
|
||||
|
||||
@JsonProperty("tariff_term")
|
||||
private String tariffTerm;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.soune.nocopy.dto.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserAdditionalInfoBody {
|
||||
|
||||
@JsonProperty("action")
|
||||
private String action;
|
||||
|
||||
@JsonProperty("user_id")
|
||||
private Long userId;
|
||||
|
||||
@JsonProperty("ip_address")
|
||||
private String ipAddress;
|
||||
|
||||
@JsonProperty("user_agent")
|
||||
private String userAgent;
|
||||
}
|
||||
@@ -10,6 +10,9 @@ public class ViolationRequest {
|
||||
@JsonProperty("file_id")
|
||||
private String fileId;
|
||||
|
||||
@JsonProperty("violation_id")
|
||||
private Long violationId;
|
||||
|
||||
@JsonProperty("page")
|
||||
private Integer page = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package ru.soune.nocopy.entity.complaint;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import ru.soune.nocopy.entity.violation.Violation;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "complaint")
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter @Setter
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class ComplaintEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private ComplaintStatus status;
|
||||
|
||||
@Column(name = "complaint_text", columnDefinition = "TEXT")
|
||||
private String complaintText;
|
||||
|
||||
@Column(name = "email")
|
||||
private String email;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "violation_id")
|
||||
@ToString.Exclude
|
||||
@JsonIgnore
|
||||
private Violation violation;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "law_case_id")
|
||||
@ToString.Exclude
|
||||
@JsonIgnore
|
||||
private LawCase lawCase;
|
||||
|
||||
@Column(name = "created_at", updatable = false)
|
||||
@CreatedDate
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
@LastModifiedDate
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@Column(name = "not_moderated_file")
|
||||
private Boolean not_moderated_file;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.soune.nocopy.entity.complaint;
|
||||
|
||||
public enum ComplaintStatus {
|
||||
CREATED, SHOWED, IN_WORK, COMPLETED, CANCELLED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.soune.nocopy.entity.complaint;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Getter @Setter
|
||||
@Table(name = "law_case")
|
||||
public class LawCase {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.soune.nocopy.entity.file;
|
||||
|
||||
public enum AppealStatus {
|
||||
PENDING,
|
||||
IN_REVIEW,
|
||||
APPROVED,
|
||||
REJECTED,
|
||||
EXPIRED
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package ru.soune.nocopy.entity.file;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "file_appeals")
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class FileAppeal {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String id;
|
||||
|
||||
@Column(name = "file_id", nullable = false)
|
||||
private String fileId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "file_id", insertable = false, updatable = false)
|
||||
@ToString.Exclude
|
||||
private FileEntity file;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@Column(name = "appeal_reason", nullable = false, columnDefinition = "TEXT")
|
||||
private String appealReason;
|
||||
|
||||
@Column(name = "additional_info", columnDefinition = "TEXT")
|
||||
private String additionalInfo;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false)
|
||||
private AppealStatus status;
|
||||
|
||||
@Column(name = "admin_comment", columnDefinition = "TEXT")
|
||||
private String adminComment;
|
||||
|
||||
@Column(name = "moderator_id")
|
||||
private Long moderatorId;
|
||||
|
||||
@Column(name = "moderation_before_status")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private FileStatus moderationBeforeStatus;
|
||||
|
||||
@Column(name = "resolved_at")
|
||||
private LocalDateTime resolvedAt;
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
if (this.status == null) {
|
||||
this.status = AppealStatus.PENDING;
|
||||
}
|
||||
if (this.createdAt == null) {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,9 @@ public class FileEntity {
|
||||
@Column(name = "medium_path")
|
||||
private String mediumPath;
|
||||
|
||||
@Column
|
||||
private String newExtension;
|
||||
|
||||
@OneToOne(mappedBy = "file", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JsonIgnore
|
||||
private ImageHashEntity imageHash;
|
||||
@@ -98,7 +101,7 @@ public class FileEntity {
|
||||
}
|
||||
|
||||
if (this.status == null) {
|
||||
this.status = FileStatus.ACTIVE;
|
||||
this.status = FileStatus.MODERATION;
|
||||
}
|
||||
|
||||
if (this.protectionStatus == null) {
|
||||
|
||||
@@ -7,5 +7,8 @@ public enum FileStatus {
|
||||
VIOLATION,
|
||||
CHECKED,
|
||||
ERROR,
|
||||
TEMP
|
||||
TEMP,
|
||||
REMOVED,
|
||||
MODERATION,
|
||||
BLOCKED
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package ru.soune.nocopy.entity.file.moderation;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Entity
|
||||
@Table(name = "moderation_logs")
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class ModerationLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String id;
|
||||
|
||||
@Column(name = "file_id", nullable = false)
|
||||
private String fileId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "file_id", insertable = false, updatable = false)
|
||||
@ToString.Exclude
|
||||
private FileEntity file;
|
||||
|
||||
@Column(name = "moderator_id", nullable = false)
|
||||
private Long moderatorId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "old_status", nullable = false)
|
||||
private FileStatus oldStatus;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "new_status", nullable = false)
|
||||
private FileStatus newStatus;
|
||||
|
||||
@Column(name = "reason", columnDefinition = "TEXT")
|
||||
private String reason;
|
||||
|
||||
@Column(name = "comment", columnDefinition = "TEXT")
|
||||
private String comment;
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", updatable = false)
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -40,4 +40,8 @@ public class Tariff {
|
||||
|
||||
@Column(name = "tariffAccountType")
|
||||
private String tariffAccountType;
|
||||
|
||||
@Column(name = "tariff_term")
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TariffTimeTerm tariffTerm;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.soune.nocopy.entity.tarif;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum TariffTimeTerm {
|
||||
MONTHLY(30),YEAR(365);
|
||||
|
||||
private final Integer days;
|
||||
|
||||
TariffTimeTerm(Integer days) {
|
||||
this.days = days;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ru.soune.nocopy.entity.tarif;
|
||||
|
||||
|
||||
public enum TariffType {
|
||||
START, BASIC, PRO, ENTERPRISE, DEMO, BUISNES, CORPORAT, STUDIO, FREE, TOKEN_100, TOKEN_500, TOKEN_1000, TOKEN_5000,
|
||||
START, START_YEAR, BASIC, BASIC_YEAR, PRO, PRO_YEAR, ENTERPRISE, ENTERPRISE_YEAR, DEMO, BUISNES, BUISNES_YEAR,
|
||||
CORPORAT, CORPORAT_YEAR, STUDIO, STUDIO_YEAR, FREE, TOKEN_100, TOKEN_500, TOKEN_1000, TOKEN_5000,
|
||||
MONITORING_DAILY, MONITORING_WEEKLY, MONITORING_MONTHLY;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.soune.nocopy.entity.user;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.hibernate.annotations.CreationTimestamp;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Getter @Setter
|
||||
@Builder
|
||||
@Table(name = "user_additional_info")
|
||||
public class UserAdditionalInfo {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, name = "register_date")
|
||||
@CreationTimestamp
|
||||
private Timestamp registrationDate;
|
||||
|
||||
@Column(nullable = false, name = "ip")
|
||||
private String ipAddress;
|
||||
|
||||
@Column(nullable = false, name = "user_agent")
|
||||
private String userAgent;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", unique = true, nullable = false)
|
||||
private User user;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package ru.soune.nocopy.entity.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserAdditionalInfoAnswer {
|
||||
|
||||
@JsonProperty("id")
|
||||
private Long id;
|
||||
|
||||
@JsonProperty("user_id")
|
||||
private Long userId;
|
||||
|
||||
@JsonProperty("ip_address")
|
||||
private String ipAddress;
|
||||
|
||||
@JsonProperty("user_agent")
|
||||
private String userAgent;
|
||||
|
||||
@JsonProperty("registration_date")
|
||||
private Timestamp registrationDate;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.soune.nocopy.entity.user;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UserAdditionalInfoListAnswer {
|
||||
|
||||
@JsonProperty("items")
|
||||
private List<UserAdditionalInfoAnswer> items;
|
||||
|
||||
@JsonProperty("count")
|
||||
private int count;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class ComplaintNotFoundException extends RuntimeException {
|
||||
public ComplaintNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class DuplicateComplaintException extends RuntimeException {
|
||||
public DuplicateComplaintException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class InsufficientTokensException extends RuntimeException {
|
||||
public InsufficientTokensException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class InvalidAppealException extends RuntimeException {
|
||||
public InvalidAppealException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class RegistrationAdditionalInfoNullException extends RuntimeException {
|
||||
public RegistrationAdditionalInfoNullException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class SearchFailedException extends RuntimeException {
|
||||
public SearchFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.soune.nocopy.exception;
|
||||
|
||||
public class UserAdditionalInfoNotFound extends RuntimeException {
|
||||
public UserAdditionalInfoNotFound(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package ru.soune.nocopy.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
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.complaint.ComplaintRequest;
|
||||
import ru.soune.nocopy.dto.complaint.ComplaintResponse;
|
||||
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
|
||||
import ru.soune.nocopy.service.complaint.ComplaintEntityService;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ComplaintEntityHandler implements RequestHandler {
|
||||
private final ComplaintEntityService complaintService;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||
Integer msgId = request.getMsgId();
|
||||
ComplaintRequest complaintRequest = objectMapper.convertValue(request.getMessageBody(), ComplaintRequest.class);
|
||||
String action = complaintRequest.getAction();
|
||||
|
||||
try {
|
||||
switch (action.toLowerCase()) {
|
||||
case "create":
|
||||
return handleCreate(msgId, complaintRequest);
|
||||
case "get":
|
||||
return handleGet(msgId, complaintRequest);
|
||||
case "get_all":
|
||||
return handleGetAll(msgId, complaintRequest);
|
||||
case "get_by_violation":
|
||||
return handleGetByViolation(msgId, complaintRequest);
|
||||
case "update_status":
|
||||
return handleUpdateStatus(msgId, complaintRequest);
|
||||
case "update":
|
||||
return handleUpdate(msgId, complaintRequest);
|
||||
case "delete":
|
||||
return handleDelete(msgId, complaintRequest);
|
||||
default:
|
||||
return errorResponse(msgId, MessageCode.MSG_ID_NOT_FOUND.getCode(), "Unknown action: " + action);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error handling complaint request", e);
|
||||
return errorResponse(msgId, MessageCode.INVALID_JSON_BODY.getCode(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleCreate(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getViolationId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(),
|
||||
"violation_id is required");
|
||||
if (req.getComplaintText() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(),
|
||||
"text is required");
|
||||
|
||||
ComplaintResponse response = complaintService.createComplaint(req);
|
||||
|
||||
return successResponse(msgId, "Complaint created successfully", response);
|
||||
}
|
||||
|
||||
private BaseResponse handleGet(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(), "id is required");
|
||||
|
||||
ComplaintResponse response = complaintService.getComplaintById(req.getId());
|
||||
return successResponse(msgId, "Complaint retrieved successfully", response);
|
||||
}
|
||||
|
||||
private BaseResponse handleGetAll(Integer msgId, ComplaintRequest req) {
|
||||
Pageable pageable = PageRequest.of(
|
||||
req.getPage() != null ? req.getPage() : 0,
|
||||
req.getSize() != null ? req.getSize() : 20,
|
||||
Sort.by(Sort.Direction.fromString(req.getSortDirection() != null ? req.getSortDirection() : "desc"),
|
||||
req.getSortBy() != null ? req.getSortBy() : "createdAt")
|
||||
);
|
||||
|
||||
var page = complaintService.getAllComplaints(pageable);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("content", page.getContent());
|
||||
data.put("page", page.getNumber());
|
||||
data.put("size", page.getSize());
|
||||
data.put("totalElements", page.getTotalElements());
|
||||
data.put("totalPages", page.getTotalPages());
|
||||
|
||||
return successResponse(msgId, "Complaints retrieved successfully", data);
|
||||
}
|
||||
|
||||
private BaseResponse handleGetByViolation(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getViolationId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(),
|
||||
"violation_id is required");
|
||||
|
||||
var complaints = complaintService.getComplaintsByViolationId(req.getViolationId());
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("content", complaints);
|
||||
data.put("totalElements", complaints.size());
|
||||
|
||||
return successResponse(msgId, "Complaints retrieved successfully", data);
|
||||
}
|
||||
|
||||
private BaseResponse handleUpdateStatus(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(), "id is required");
|
||||
if (req.getStatus() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(), "status is required");
|
||||
|
||||
ComplaintStatus status = ComplaintStatus.valueOf(req.getStatus().toUpperCase());
|
||||
ComplaintResponse response = complaintService.updateComplaintStatus(req.getId(), status);
|
||||
return successResponse(msgId, "Status updated successfully", response);
|
||||
}
|
||||
|
||||
private BaseResponse handleUpdate(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(), "id is required");
|
||||
|
||||
ComplaintResponse response = complaintService.updateComplaint(req.getId(), req);
|
||||
|
||||
return successResponse(msgId, "Complaint updated successfully", response);
|
||||
}
|
||||
|
||||
private BaseResponse handleDelete(Integer msgId, ComplaintRequest req) {
|
||||
if (req.getId() == null) return errorResponse(msgId, MessageCode.NOT_FOUND.getCode(), "id is required");
|
||||
|
||||
ComplaintResponse response = complaintService.deleteComplaint(req.getId());
|
||||
return successResponse(msgId, "Complaint deleted successfully", response);
|
||||
}
|
||||
|
||||
private BaseResponse successResponse(Integer msgId, String message, Object data) {
|
||||
return new BaseResponse(msgId, MessageCode.SUCCESS.getCode(), message, data);
|
||||
}
|
||||
|
||||
private BaseResponse errorResponse(Integer msgId, Integer code, String message) {
|
||||
return new BaseResponse(msgId, code, message, new HashMap<>());
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,21 @@ package ru.soune.nocopy.handler;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.dto.file.*;
|
||||
import ru.soune.nocopy.entity.file.FileAppeal;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
|
||||
import ru.soune.nocopy.exception.*;
|
||||
import ru.soune.nocopy.repository.FileAppealRepository;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.repository.ModerationLogRepository;
|
||||
import ru.soune.nocopy.service.file.moderation.ModerationService;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileStatsService;
|
||||
@@ -35,6 +41,14 @@ public class FileEntityHandler implements RequestHandler {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
private final ModerationService moderationService;
|
||||
|
||||
private final FileAppealRepository fileAppealRepository;
|
||||
|
||||
private final ModerationLogRepository moderationLogRepository;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) {
|
||||
try {
|
||||
@@ -60,6 +74,18 @@ public class FileEntityHandler implements RequestHandler {
|
||||
return handleProtectFile(request, fileRequest);
|
||||
case "delete_file":
|
||||
return handleDeleteFile(request, fileRequest);
|
||||
case "change_file_status":
|
||||
return handleChangeStatus(request, fileRequest);
|
||||
case "moderate_file":
|
||||
return handleModerateFile(request, fileRequest);
|
||||
case "submit_appeal":
|
||||
return handleSubmitAppeal(request, fileRequest);
|
||||
case "review_appeal":
|
||||
return handleReviewAppeal(request, fileRequest);
|
||||
case "user_appeals":
|
||||
return handleGetUserAppeals(request, fileRequest);
|
||||
case "files_for_moderation":
|
||||
return handleGetFilesForModeration(request, fileRequest);
|
||||
default:
|
||||
ActionResponse response = ActionResponse.builder()
|
||||
.action(action)
|
||||
@@ -183,6 +209,25 @@ public class FileEntityHandler implements RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleChangeStatus(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
FileStatus newStatus = fileRequest.getFileStatus();
|
||||
String fileId = fileRequest.getFileId();
|
||||
|
||||
try {
|
||||
fileEntityService.changeFileStatus(newStatus ,fileRequest.getFileId());
|
||||
} catch (FileEntityNotFoundException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.FILE_NOT_FOUND.getCode(),
|
||||
MessageCode.FILE_NOT_FOUND.getDescription(),
|
||||
FileEntityResponse.builder().id(fileId).build());
|
||||
}
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
FileEntityResponse.builder().status(newStatus).build());
|
||||
}
|
||||
|
||||
private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
||||
@@ -536,7 +581,7 @@ public class FileEntityHandler implements RequestHandler {
|
||||
int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();
|
||||
|
||||
if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
|
||||
fileEntityService.deleteFromDisk(fileEntity);
|
||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
||||
response = DeleteFileResponse.builder()
|
||||
.fileId(fileRequest.getFileId())
|
||||
.message("File deleted from disk")
|
||||
@@ -568,6 +613,326 @@ public class FileEntityHandler implements RequestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleModerateFile(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
//TODO добавить проверку на админа
|
||||
// Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken());
|
||||
|
||||
ModerationActionRequest moderationRequest = ModerationActionRequest.builder()
|
||||
.fileId(fileRequest.getFileId())
|
||||
.newStatus(fileRequest.getFileStatus())
|
||||
.comment(fileRequest.getComment())
|
||||
.build();
|
||||
|
||||
//TODO set moderator ID
|
||||
moderationService.moderateFile(moderationRequest, 0L);
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS_MODERATION.getCode(),
|
||||
MessageCode.SUCCESS_MODERATION.getDescription(),
|
||||
FileEntityResponse.builder().id(fileRequest.getFileId()).build());
|
||||
|
||||
} catch (SecurityException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ACCESS_DENIED.getCode(),
|
||||
MessageCode.ACCESS_DENIED.getDescription(),
|
||||
FileEntityResponse.builder().id(fileRequest.getFileId()).build());
|
||||
} catch (FileEntityNotFoundException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.FILE_NOT_FOUND.getCode(),
|
||||
MessageCode.FILE_NOT_FOUND.getDescription(),
|
||||
FileEntityResponse.builder()
|
||||
.id(fileRequest.getFileId()).build());
|
||||
} catch (IllegalStateException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ILLEGAL_STATE.getCode(),
|
||||
MessageCode.ILLEGAL_STATE.getDescription(),
|
||||
FileEntityResponse.builder()
|
||||
.status(fileRequest.getFileStatus()).build());
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleSubmitAppeal(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
||||
|
||||
AppealRequest appealRequest = AppealRequest.builder()
|
||||
.fileId(fileRequest.getFileId())
|
||||
.appealReason(fileRequest.getAppealReason())
|
||||
.additionalInfo(fileRequest.getAdditionalInfo())
|
||||
.build();
|
||||
|
||||
AppealResponse response = moderationService.submitAppeal(appealRequest, userId);
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
"Appeal submitted successfully",
|
||||
response);
|
||||
} catch (InvalidAppealException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
e.getMessage(),
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"Failed to submit appeal: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleReviewAppeal(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
// TODO
|
||||
// Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken());
|
||||
|
||||
String appealId = fileRequest.getAppealId();
|
||||
Boolean approve = fileRequest.getApprove();
|
||||
String comment = fileRequest.getComment();
|
||||
|
||||
if (appealId == null || approve == null) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"appealId and approve are required",
|
||||
null);
|
||||
}
|
||||
//TODO
|
||||
moderationService.reviewAppeal(appealId, approve, comment, 0L);
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
"Appeal reviewed successfully",
|
||||
Map.of(
|
||||
"appealId", appealId,
|
||||
"approved", approve,
|
||||
"comment", comment
|
||||
));
|
||||
|
||||
} catch (SecurityException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ACCESS_DENIED.getCode(),
|
||||
"Admin access required",
|
||||
null);
|
||||
} catch (IllegalStateException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
e.getMessage(),
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
log.error("Error reviewing appeal", e);
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"Failed to review appeal: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleGetUserAppeals(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
Long userId = authService.useUserAuthToken(fileRequest.getToken());
|
||||
|
||||
int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
|
||||
int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
|
||||
|
||||
Page<AppealResponse> appeals = moderationService.getUserAppeals(userId, page - 1, pageSize);
|
||||
|
||||
Map<String, Object> response = Map.of(
|
||||
"appeals", appeals.getContent(),
|
||||
"totalCount", appeals.getTotalElements(),
|
||||
"totalPages", appeals.getTotalPages(),
|
||||
"currentPage", page,
|
||||
"pageSize", pageSize);
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
response);
|
||||
|
||||
} catch (SecurityException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_TOKEN.getCode(),
|
||||
"Authentication required",
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting user appeals", e);
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"Failed to get appeals: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleGetFilesForModeration(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
//TODO
|
||||
// authService.useAdminAuthToken(fileRequest.getToken());
|
||||
|
||||
int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
|
||||
int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
|
||||
|
||||
Page<FileEntity> files = moderationService.getFilesForModeration(page - 1, pageSize);
|
||||
|
||||
List<Map<String, Object>> filesWithInfo = files.getContent().stream()
|
||||
.map(file -> {
|
||||
FileModerationInfo info = moderationService.getFileModerationInfo(file.getId());
|
||||
|
||||
Map<String, Object> fileMap = new HashMap<>();
|
||||
fileMap.put("fileId", file.getId());
|
||||
fileMap.put("fileName", file.getOriginalFileName());
|
||||
fileMap.put("userId", file.getUserId());
|
||||
fileMap.put("status", file.getStatus() != null ? file.getStatus().name() : null);
|
||||
|
||||
Map<String, Object> moderationInfoMap = new HashMap<>();
|
||||
moderationInfoMap.put("hasActiveAppeal", info != null && info.getHasActiveAppeal());
|
||||
|
||||
if (info != null && info.getActiveAppeal() != null) {
|
||||
Map<String, Object> appealInfoMap = new HashMap<>();
|
||||
appealInfoMap.put("appealId", info.getActiveAppeal().getAppealId());
|
||||
appealInfoMap.put("status", info.getActiveAppeal().getStatus());
|
||||
appealInfoMap.put("createdAt", info.getActiveAppeal().getCreatedAt());
|
||||
moderationInfoMap.put("appealInfo", appealInfoMap);
|
||||
} else {
|
||||
moderationInfoMap.put("appealInfo", null);
|
||||
}
|
||||
|
||||
fileMap.put("moderationInfo", moderationInfoMap);
|
||||
|
||||
return fileMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> response = Map.of(
|
||||
"files", filesWithInfo,
|
||||
"totalCount", files.getTotalElements(),
|
||||
"totalPages", files.getTotalPages(),
|
||||
"currentPage", page,
|
||||
"pageSize", pageSize
|
||||
);
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
response);
|
||||
|
||||
} catch (SecurityException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ACCESS_DENIED.getCode(),
|
||||
"Admin access required",
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting files for moderation", e);
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"Failed to get files: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse handleGetFileModerationInfo(BaseRequest request, FileEntityRequest fileRequest) {
|
||||
try {
|
||||
// TODO
|
||||
// authService.useAdminAuthToken(fileRequest.getToken());
|
||||
|
||||
String fileId = fileRequest.getFileId();
|
||||
if (fileId == null) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"fileId is required",
|
||||
null);
|
||||
}
|
||||
|
||||
FileModerationInfo moderationInfo = moderationService.getFileModerationInfo(fileId);
|
||||
|
||||
FileEntity file = fileEntityRepository.findById(fileId)
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(fileId));
|
||||
|
||||
Map<String, Object> fileInfoMap = new HashMap<>();
|
||||
fileInfoMap.put("id", file.getId());
|
||||
fileInfoMap.put("fileName", file.getOriginalFileName());
|
||||
fileInfoMap.put("storedFileName", file.getStoredFileName());
|
||||
fileInfoMap.put("fileSize", file.getFileSize());
|
||||
fileInfoMap.put("formattedSize", fileEntityService.formatFileSize(file.getFileSize()));
|
||||
fileInfoMap.put("mimeType", file.getMimeType());
|
||||
fileInfoMap.put("fileExtension", file.getFileExtension());
|
||||
fileInfoMap.put("checksum", file.getChecksum());
|
||||
fileInfoMap.put("status", file.getStatus() != null ? file.getStatus().name() : null);
|
||||
fileInfoMap.put("createdAt", file.getCreatedAt());
|
||||
fileInfoMap.put("updatedAt", file.getUpdatedAt());
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("fileInfo", fileInfoMap);
|
||||
response.put("moderationInfo", moderationInfo != null ? moderationInfo : new HashMap<>());
|
||||
response.put("moderationHistory", getModerationHistory(fileId));
|
||||
response.put("appeals", getFileAppeals(fileId));
|
||||
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
response);
|
||||
|
||||
} catch (SecurityException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.ACCESS_DENIED.getCode(),
|
||||
"Admin access required",
|
||||
null);
|
||||
} catch (FileEntityNotFoundException e) {
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.FILE_NOT_FOUND.getCode(),
|
||||
e.getMessage(),
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file moderation info", e);
|
||||
return new BaseResponse(request.getMsgId(),
|
||||
MessageCode.INVALID_FIELD.getCode(),
|
||||
"Failed to get file info: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> getModerationHistory(String fileId) {
|
||||
List<ModerationLog> logs = moderationLogRepository.findByFileIdOrderByCreatedAtDesc(fileId);
|
||||
if (logs == null || logs.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return logs.stream()
|
||||
.map(log -> {
|
||||
Map<String, Object> logMap = new HashMap<>();
|
||||
logMap.put("id", log.getId());
|
||||
logMap.put("moderatorId", log.getModeratorId());
|
||||
logMap.put("oldStatus", log.getOldStatus() != null ? log.getOldStatus().name() : null);
|
||||
logMap.put("newStatus", log.getNewStatus() != null ? log.getNewStatus().name() : null);
|
||||
logMap.put("reason", log.getReason());
|
||||
logMap.put("comment", log.getComment());
|
||||
logMap.put("createdAt", log.getCreatedAt());
|
||||
return logMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> getFileAppeals(String fileId) {
|
||||
List<FileAppeal> appeals = fileAppealRepository.findByFileId(fileId);
|
||||
if (appeals == null || appeals.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return appeals.stream()
|
||||
.map(appeal -> {
|
||||
Map<String, Object> appealMap = new HashMap<>();
|
||||
appealMap.put("id", appeal.getId());
|
||||
appealMap.put("userId", appeal.getUserId());
|
||||
appealMap.put("appealReason", appeal.getAppealReason());
|
||||
appealMap.put("additionalInfo", appeal.getAdditionalInfo());
|
||||
appealMap.put("status", appeal.getStatus() != null ? appeal.getStatus().name() : null);
|
||||
appealMap.put("adminComment", appeal.getAdminComment());
|
||||
appealMap.put("moderatorId", appeal.getModeratorId());
|
||||
appealMap.put("createdAt", appeal.getCreatedAt());
|
||||
appealMap.put("resolvedAt", appeal.getResolvedAt());
|
||||
return appealMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String formatFileSize(long size) {
|
||||
if (size < 1024) return size + " B";
|
||||
int exp = (int) (Math.log(size) / Math.log(1024));
|
||||
|
||||
@@ -21,6 +21,7 @@ import ru.soune.nocopy.repository.ReferralJpaRepository;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.mail.EmailService;
|
||||
import ru.soune.nocopy.service.user.AdditionalInfoService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
@@ -43,6 +44,8 @@ public class RegRequestHandler implements RequestHandler {
|
||||
|
||||
private final ReferralJpaRepository referralJpaRepository;
|
||||
|
||||
private final AdditionalInfoService additionalInfoService;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) throws ValidationException {
|
||||
RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class);
|
||||
@@ -124,9 +127,10 @@ public class RegRequestHandler implements RequestHandler {
|
||||
referralService.onRegister(authToken.getUser().getId(), regRequest.getReferralLink());
|
||||
}
|
||||
|
||||
additionalInfoService.additionalInfo(regRequest.getIp(), regRequest.getUserAgent(), authToken.getUser());
|
||||
|
||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(), regAnswer);
|
||||
|
||||
} catch (CompanyAlreadyExist e) {
|
||||
return new BaseResponse(request.getMsgId(), MessageCode.COMPANY_ALREADY_EXISTS.getCode(),
|
||||
MessageCode.COMPANY_ALREADY_EXISTS.getDescription(), Map.of(
|
||||
|
||||
@@ -10,6 +10,7 @@ import ru.soune.nocopy.dto.MessageCode;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffRequest;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffResponse;
|
||||
import ru.soune.nocopy.entity.tarif.TariffTimeTerm;
|
||||
import ru.soune.nocopy.entity.user.AuthToken;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
@@ -64,7 +65,7 @@ public class TariffHandler implements RequestHandler {
|
||||
private BaseResponse handleAddTariff(BaseRequest request, TariffRequest tariffRequest) {
|
||||
tariffService.addTariff(tariffRequest.getName(), tariffRequest.getPrice(), tariffRequest.getType(),
|
||||
tariffRequest.getTokens(), tariffRequest.getDiskSize(), tariffRequest.getMaxUsers(),
|
||||
tariffRequest.getMaxFilesCount());
|
||||
tariffRequest.getMaxFilesCount(), tariffRequest.getTariffTerm());
|
||||
|
||||
TariffResponse tariffResponse = TariffResponse.builder()
|
||||
.tariffName(tariffRequest.getName())
|
||||
@@ -122,7 +123,8 @@ public class TariffHandler implements RequestHandler {
|
||||
private BaseResponse handleGetTariffs(BaseRequest request, TariffRequest tariffRequest) {
|
||||
AuthToken authToken = authTokenRepository.findByToken(tariffRequest.getUserToken()).orElseThrow();
|
||||
User user = authToken.getUser();
|
||||
List<TariffDTO> allTariffs = tariffService.getTariffByAccountType(user);
|
||||
List<TariffDTO> allTariffs = tariffService.getTariffByAccountTypeAndTariffTerm(user,
|
||||
TariffTimeTerm.valueOf(tariffRequest.getTariffTerm().toUpperCase()));
|
||||
|
||||
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
|
||||
@@ -12,6 +12,7 @@ import ru.soune.nocopy.dto.tarriff.TariffInfoRequest;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffInfoResponse;
|
||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
||||
import ru.soune.nocopy.entity.tarif.TariffStatus;
|
||||
import ru.soune.nocopy.entity.tarif.TariffTimeTerm;
|
||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||
import ru.soune.nocopy.repository.TariffRepository;
|
||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||
@@ -75,13 +76,14 @@ public class TariffInfoHandler implements RequestHandler {
|
||||
try {
|
||||
Long tariffId = tariffInfoRequest.getTariffId();
|
||||
Tariff tariff= tariffRepository.findById(tariffId).orElseThrow();
|
||||
TariffTimeTerm tariffTerm = tariff.getTariffTerm();
|
||||
LocalDateTime endDateTime = LocalDateTime.now().plusDays(tariffTerm.getDays());
|
||||
|
||||
TariffInfoDTO tariffInfoDTO = new TariffInfoDTO(null,
|
||||
tariffInfoRequest.getStatus() != null ? tariffInfoRequest.getStatus() : TariffStatus.ACTIVE.name(),
|
||||
tariffInfoRequest.getStartTariff() != null ? tariffInfoRequest.getStartTariff() : LocalDateTime.now(),
|
||||
tariffInfoRequest.getEndTariff() != null ? tariffInfoRequest.getEndTariff() : LocalDateTime.now().plusDays(30),
|
||||
tariffInfoRequest.getTariffId(), tariff.getName(), tariff.getTokens(), tariff.getDiskSize(), 0L,
|
||||
0, tariff.getMaxFilesCount());
|
||||
endDateTime, tariffInfoRequest.getTariffId(), tariff.getName(), tariff.getTokens(),
|
||||
tariff.getDiskSize(), 0L, 0, tariff.getMaxFilesCount());
|
||||
|
||||
TariffInfoDTO createdTariffInfo = tariffInfoService.createTariffInfo(tariffInfoDTO);
|
||||
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
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.user.UserAdditionalInfoBody;
|
||||
|
||||
import ru.soune.nocopy.entity.user.UserAdditionalInfo;
|
||||
import ru.soune.nocopy.entity.user.UserAdditionalInfoAnswer;
|
||||
import ru.soune.nocopy.entity.user.UserAdditionalInfoListAnswer;
|
||||
import ru.soune.nocopy.exception.UserAdditionalInfoNotFound;
|
||||
import ru.soune.nocopy.service.user.AdditionalInfoService;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class UserInfoHandler implements RequestHandler {
|
||||
|
||||
private final AdditionalInfoService additionalInfoService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||
try {
|
||||
UserAdditionalInfoBody body = objectMapper.convertValue(request.getMessageBody(),
|
||||
UserAdditionalInfoBody.class);
|
||||
|
||||
log.info("Processing UserInfoHandler with action: {}", body.getAction());
|
||||
|
||||
return switch (body.getAction()) {
|
||||
case "getAll" -> getAllAdditionalInfos(request);
|
||||
case "getById" -> getAdditionalInfoById(request, body.getUserId());
|
||||
case "update" -> updateAdditionalInfo(request, body);
|
||||
case "delete" -> deleteAdditionalInfo(request, body.getUserId());
|
||||
default -> new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.INVALID_ACTION.getCode(),
|
||||
"Invalid action: " + body.getAction(),
|
||||
null
|
||||
);
|
||||
};
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("Validation error in UserInfoHandler", e);
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.VALIDATION_ERROR.getCode(),
|
||||
e.getMessage(),
|
||||
null);
|
||||
} catch (UserAdditionalInfoNotFound e) {
|
||||
log.error("User additional info not found", e);
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.NOT_FOUND.getCode(),
|
||||
e.getMessage(),
|
||||
null);
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error in UserInfoHandler", e);
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.INTERNAL_ERROR.getCode(),
|
||||
"Internal server error: " + e.getMessage(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseResponse getAllAdditionalInfos(BaseRequest request) {
|
||||
log.info("Getting all additional infos");
|
||||
|
||||
List<UserAdditionalInfo> infos = additionalInfoService.getAllAdditionalInfos();
|
||||
|
||||
List<UserAdditionalInfoAnswer> answerItems = infos.stream()
|
||||
.map(this::convertToAnswer)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UserAdditionalInfoListAnswer listAnswer = UserAdditionalInfoListAnswer.builder()
|
||||
.items(answerItems)
|
||||
.count(answerItems.size())
|
||||
.build();
|
||||
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
listAnswer
|
||||
);
|
||||
}
|
||||
|
||||
private BaseResponse getAdditionalInfoById(BaseRequest request, Long userId) {
|
||||
log.info("Getting additional info for user: {}", userId);
|
||||
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("User ID is required for getById action");
|
||||
}
|
||||
|
||||
UserAdditionalInfo info = additionalInfoService.getAdditionalInfoByUser(userId);
|
||||
|
||||
if (info == null) {
|
||||
throw new UserAdditionalInfoNotFound(
|
||||
String.format("User additional info not found for userId: %d", userId)
|
||||
);
|
||||
}
|
||||
|
||||
UserAdditionalInfoAnswer answer = convertToAnswer(info);
|
||||
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
answer
|
||||
);
|
||||
}
|
||||
|
||||
private BaseResponse updateAdditionalInfo(BaseRequest request, UserAdditionalInfoBody body) {
|
||||
log.info("Updating additional info for user: {}", body.getUserId());
|
||||
|
||||
if (body.getUserId() == null) {
|
||||
throw new IllegalArgumentException("User ID is required for update action");
|
||||
}
|
||||
|
||||
validateIpAddress(body.getIpAddress());
|
||||
validateUserAgent(body.getUserAgent());
|
||||
|
||||
Optional<UserAdditionalInfo> updatedInfo = additionalInfoService.updateAdditionalInfo(
|
||||
body.getUserId(),
|
||||
body.getIpAddress(),
|
||||
body.getUserAgent()
|
||||
);
|
||||
|
||||
if (updatedInfo.isEmpty()) {
|
||||
throw new UserAdditionalInfoNotFound(
|
||||
String.format("User additional info not found for userId: %d", body.getUserId())
|
||||
);
|
||||
}
|
||||
|
||||
UserAdditionalInfoAnswer answer = convertToAnswer(updatedInfo.get());
|
||||
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
answer
|
||||
);
|
||||
}
|
||||
|
||||
private BaseResponse deleteAdditionalInfo(BaseRequest request, Long userId) {
|
||||
log.info("Deleting additional info for user: {}", userId);
|
||||
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("User ID is required for delete action");
|
||||
}
|
||||
|
||||
UserAdditionalInfo existingInfo = additionalInfoService.getAdditionalInfoByUser(userId);
|
||||
|
||||
if (existingInfo == null) {
|
||||
throw new UserAdditionalInfoNotFound(
|
||||
String.format("User additional info not found for userId: %d", userId)
|
||||
);
|
||||
}
|
||||
|
||||
additionalInfoService.deleteAdditionalInfo(userId);
|
||||
|
||||
return new BaseResponse(
|
||||
request.getMsgId(),
|
||||
MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(),
|
||||
Map.of("deleted", true, "user_id", userId)
|
||||
);
|
||||
}
|
||||
|
||||
private UserAdditionalInfoAnswer convertToAnswer(UserAdditionalInfo info) {
|
||||
return UserAdditionalInfoAnswer.builder()
|
||||
.id(info.getId())
|
||||
.userId(info.getUser() != null ? info.getUser().getId() : null)
|
||||
.ipAddress(info.getIpAddress())
|
||||
.userAgent(info.getUserAgent())
|
||||
.registrationDate(info.getRegistrationDate())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void validateIpAddress(String ip) {
|
||||
if (ip == null || ip.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("IP address is required for update action");
|
||||
}
|
||||
|
||||
String ipv4Pattern =
|
||||
"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
|
||||
|
||||
if (!ip.trim().matches(ipv4Pattern)) {
|
||||
log.warn("Invalid IP address format: {}", ip);
|
||||
throw new IllegalArgumentException("Invalid IP address format");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateUserAgent(String userAgent) {
|
||||
if (userAgent == null || userAgent.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("User agent is required for update action");
|
||||
}
|
||||
|
||||
if (userAgent.length() > 500) {
|
||||
throw new IllegalArgumentException("User agent must not exceed 500 characters");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.register.AuthService;
|
||||
import ru.soune.nocopy.service.violation.ViolationService;
|
||||
import ru.soune.nocopy.service.violation.ViolationStatus;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -66,6 +67,18 @@ public class ViolationHandler implements RequestHandler {
|
||||
endDate = parseDate(violationRequest.getEndDate());
|
||||
}
|
||||
|
||||
if ("updateStatus".equals(violationRequest.getAction())) {
|
||||
String status = violationRequest.getStatus();
|
||||
Long violationId = violationRequest.getViolationId();
|
||||
|
||||
violationService.changeStatus(ViolationStatus.valueOf(status), violationId);
|
||||
|
||||
return BaseResponse.builder()
|
||||
.msgId(request.getMsgId())
|
||||
.messageBody(Map.of("status", status))
|
||||
.build();
|
||||
}
|
||||
|
||||
if ("group".equalsIgnoreCase(violationRequest.getAction())) {
|
||||
Map<String, Long> groupedData = violationService.getGroupedViolations(
|
||||
targetFiles,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ru.soune.nocopy.handler.validator;
|
||||
|
||||
import org.apache.commons.validator.routines.EmailValidator;
|
||||
import org.apache.commons.validator.routines.InetAddressValidator;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
@@ -28,6 +29,7 @@ public class RegRequestValidator implements Validator {
|
||||
validateEmail(request.getEmail(), errors);
|
||||
validateCompanyName(request.getCompanyName(), errors);
|
||||
validateFullName(request.getFullName(), errors);
|
||||
validateAdditionalInfo(request.getIp(), request.getUserAgent(), errors);
|
||||
}
|
||||
|
||||
private void validateFullName(String fullName, Errors errors) {
|
||||
@@ -73,6 +75,21 @@ public class RegRequestValidator implements Validator {
|
||||
}
|
||||
}
|
||||
|
||||
public void validateAdditionalInfo(String ip, String userAgent, Errors errors) {
|
||||
if (ip == null || ip.trim().isEmpty()) {
|
||||
errors.rejectValue("ip", "ip.required", "IP address is required");
|
||||
} else if (!InetAddressValidator.getInstance().isValid(ip.trim())) {
|
||||
errors.rejectValue("ip", "ip.invalid", "Invalid IP address format");
|
||||
}
|
||||
|
||||
if (userAgent == null || userAgent.trim().isEmpty()) {
|
||||
errors.rejectValue("userAgent", "userAgent.required", "User agent is required");
|
||||
} else if (userAgent.length() > 500) {
|
||||
errors.rejectValue("userAgent", "userAgent.tooLong", "User agent must not exceed " +
|
||||
"500 characters");
|
||||
}
|
||||
}
|
||||
|
||||
public void validateEmail(String email, Errors errors) {
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
errors.rejectValue("email", "email.is.empty", "Email must not be empty");
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.entity.complaint.ComplaintEntity;
|
||||
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface ComplaintEntityRepository extends JpaRepository<ComplaintEntity, String> {
|
||||
Optional<ComplaintEntity> findById(Long id);
|
||||
Page<ComplaintEntity> findAll(Pageable pageable);
|
||||
Page<ComplaintEntity> findByStatus(ComplaintStatus status, Pageable pageable);
|
||||
List<ComplaintEntity> findByViolationId(Long violationId);
|
||||
boolean existsByViolationId(Long violationId);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE ComplaintEntity c SET c.status = :status WHERE c.id = :id")
|
||||
int updateStatus(Long id, ComplaintStatus status);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import ru.soune.nocopy.entity.file.FileAppeal;
|
||||
import ru.soune.nocopy.entity.file.AppealStatus;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FileAppealRepository extends JpaRepository<FileAppeal, String> {
|
||||
|
||||
Optional<FileAppeal> findByFileIdAndStatus(String fileId, AppealStatus status);
|
||||
|
||||
List<FileAppeal> findByFileId(String fileId);
|
||||
|
||||
Page<FileAppeal> findByStatus(AppealStatus status, Pageable pageable);
|
||||
|
||||
Page<FileAppeal> findByUserId(Long userId, Pageable pageable);
|
||||
|
||||
@Query("SELECT fa FROM FileAppeal fa WHERE fa.status = :status ORDER BY fa.createdAt ASC")
|
||||
Page<FileAppeal> findPendingAppeals(@Param("status") AppealStatus status, Pageable pageable);
|
||||
|
||||
boolean existsByFileIdAndStatusIn(String fileId, List<AppealStatus> statuses);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
@@ -8,6 +10,7 @@ import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -21,6 +24,8 @@ public interface FileEntityRepository extends JpaRepository<FileEntity, String>
|
||||
|
||||
List<FileEntity> findByUserIdAndStatus(Long userId, FileStatus status);
|
||||
|
||||
List<FileEntity> findByUserIdAndStatusIn(Long userId, Collection<FileStatus> statuses);
|
||||
|
||||
Optional<FileEntity> findByFilePath(String filePath);
|
||||
|
||||
Optional<FileEntity> findByUploadSessionId(String uploadSessionId);
|
||||
@@ -66,4 +71,11 @@ public interface FileEntityRepository extends JpaRepository<FileEntity, String>
|
||||
Integer findMaxSupportId();
|
||||
|
||||
List<FileEntity> findByThumbnailPathIsNull();
|
||||
|
||||
Page<FileEntity> findByStatusIn(List<FileStatus> statuses, Pageable pageable);
|
||||
|
||||
List<FileEntity> findByUserIdAndStatusIn(Long userId, List<FileStatus> statuses);
|
||||
|
||||
@Query("SELECT f FROM FileEntity f WHERE f.userId = :userId AND f.status IN :statuses")
|
||||
List<FileEntity> findByStatuses(Long userId, @Param("statuses") List<String> statuses);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public interface ImageSimilarityRepository
|
||||
f.created_at AS uploadDate,
|
||||
f.protection_status AS protectionStatus,
|
||||
f.file_extension AS extension,
|
||||
f.status AS fileStatus,
|
||||
h.hash64_hi AS hash64Hi,
|
||||
h.hash64_lo AS hash64Lo
|
||||
FROM image_hashes ref
|
||||
@@ -30,7 +31,7 @@ public interface ImageSimilarityRepository
|
||||
JOIN file_entities f
|
||||
ON f.id = h.file_id
|
||||
WHERE ref.file_id = :fileId
|
||||
AND f.status = 'ACTIVE'
|
||||
AND f.status IN ('ACTIVE', 'MODERATION')
|
||||
""",
|
||||
nativeQuery = true)
|
||||
List<SimilarImageProjection> findCandidates(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ModerationLogRepository extends JpaRepository<ModerationLog, String> {
|
||||
|
||||
List<ModerationLog> findByFileIdOrderByCreatedAtDesc(String fileId);
|
||||
|
||||
List<ModerationLog> findByModeratorId(Long moderatorId);
|
||||
}
|
||||
@@ -15,4 +15,5 @@ public interface SimilarImageProjection {
|
||||
LocalDateTime getUploadDate();
|
||||
ProtectionStatus getProtectionStatus();
|
||||
String getExtension();
|
||||
String getFileStatus();
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package ru.soune.nocopy.repository;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
||||
import ru.soune.nocopy.entity.tarif.TariffTimeTerm;
|
||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -13,5 +14,6 @@ public interface TariffRepository extends JpaRepository<Tariff, Long> {
|
||||
Optional<Tariff> findByType(String type);
|
||||
List<Tariff> findAllByOrderByPriceAsc();
|
||||
List<Tariff> findByTariffAccountType(String typeAccount);
|
||||
List<Tariff> findByTariffAccountTypeAndTariffTerm(String typeAccount, TariffTimeTerm tariffTerm);
|
||||
Tariff findByName(String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.soune.nocopy.entity.user.UserAdditionalInfo;
|
||||
|
||||
@Repository
|
||||
public interface UserAdditionalInfoRepository extends JpaRepository<UserAdditionalInfo, Long> {
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||
import ru.soune.nocopy.service.monitoring.MonitoringSearchService;
|
||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@@ -12,10 +12,12 @@ import org.springframework.stereotype.Service;
|
||||
import ru.soune.nocopy.dto.file.SimilarFileDTO;
|
||||
import ru.soune.nocopy.entity.company.Company;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.ImageHashEntity;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.exception.DuplicateImageException;
|
||||
import ru.soune.nocopy.repository.*;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
@@ -40,6 +42,8 @@ public class FileSimilarityService {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@Value("${server.baseurl}")
|
||||
private String baseUrl;
|
||||
|
||||
@@ -154,22 +158,19 @@ public class FileSimilarityService {
|
||||
var imageHashEntity = hashRepository.findById(fileId)
|
||||
.orElseThrow(() -> new RuntimeException("Hash not found for file: " + fileId));
|
||||
|
||||
List<SimilarImageProjection> candidates;
|
||||
|
||||
List<Long> userIds;
|
||||
// if (authToken == null || "all".equals(authToken)) {
|
||||
candidates = repository.findCandidates(fileId);
|
||||
// } else {
|
||||
List<SimilarImageProjection> candidates = repository.findCandidates(fileId);
|
||||
|
||||
Long userId = authTokenRepository.findUserIdByToken(authToken);
|
||||
User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
|
||||
Company company = user.getCompany();
|
||||
|
||||
if (company != null) {
|
||||
userIds = company.getUsers().stream().map(User::getId).collect(Collectors.toList());
|
||||
// candidates = repository.findCandidatesFromUserFiles(fileId, userIds);
|
||||
} else {
|
||||
userIds = List.of(userId);
|
||||
// candidates = repository.findCandidatesFromUserFiles(fileId, userId);
|
||||
}
|
||||
// }
|
||||
|
||||
List<String> levels = (similarityLevels != null && !similarityLevels.isEmpty())
|
||||
? similarityLevels
|
||||
@@ -180,6 +181,7 @@ public class FileSimilarityService {
|
||||
imageHashEntity.getHash64Hi(),
|
||||
imageHashEntity.getHash64Lo(), userIds))
|
||||
.filter(dto -> levels.contains(dto.getSimilarityLevel()))
|
||||
.filter(dto -> !(dto.getFileStatus().equals(FileStatus.MODERATION.name()) && !dto.getOwner()))
|
||||
.sorted(Comparator.comparingInt(SimilarFileDTO::getHammingDistance))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@@ -199,8 +201,9 @@ public class FileSimilarityService {
|
||||
|
||||
private String calculateFileHash(String path) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
File file = cloudStorageService.readFileFromStorageByPath(path);
|
||||
|
||||
try (InputStream is = new FileInputStream(path)) {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = is.read(buffer)) > 0) {
|
||||
@@ -228,6 +231,9 @@ public class FileSimilarityService {
|
||||
level = "DIFFERENT";
|
||||
}
|
||||
|
||||
boolean owner = userIds.contains(similarImageProjection.getUserId());
|
||||
|
||||
|
||||
return SimilarFileDTO.builder()
|
||||
.fileId(similarImageProjection.getId())
|
||||
.ownerId(similarImageProjection.getUserId())
|
||||
@@ -240,9 +246,10 @@ public class FileSimilarityService {
|
||||
.supportId(similarImageProjection.getSupportId())
|
||||
.uploadDate(similarImageProjection.getUploadDate())
|
||||
.status(similarImageProjection.getProtectionStatus())
|
||||
.owner(userIds.contains(similarImageProjection.getUserId()))
|
||||
.owner(owner)
|
||||
// .url(baseUrl + "/api/files/protected/" + similarImageProjection.getId())
|
||||
.url(baseUrl + "/api/files/protected/" + similarImageProjection.getId() + "/thumbnail")
|
||||
.fileStatus(similarImageProjection.getFileStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -255,6 +262,7 @@ public class FileSimilarityService {
|
||||
"." + fileEntity.getFileExtension())
|
||||
.fileSize(fileEntity.getFileSize())
|
||||
.uploadDate(fileEntity.getCreatedAt())
|
||||
.fileStatus(fileEntity.getStatus().name())
|
||||
.status(fileEntity.getProtectionStatus())
|
||||
.supportId(Long.valueOf(fileEntity.getSupportId()))
|
||||
.build();
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.ImageHashEntity;
|
||||
import ru.soune.nocopy.repository.ImageHashRepository;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -22,8 +23,10 @@ public class ImageHashService {
|
||||
|
||||
private final ImageHashRepository repository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
public Map<String, Long> calculateHash(Path imagePath) throws IOException {
|
||||
File file = imagePath.toFile();
|
||||
File file = cloudStorageService.readFileFromStorageByPath(imagePath.toString());
|
||||
|
||||
PHash pHash = PerceptualHashHelper.INSTANCE.generateDCTPerceptualHash(file);
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
package ru.soune.nocopy.service.complaint;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.complaint.ComplaintRequest;
|
||||
import ru.soune.nocopy.dto.complaint.ComplaintResponse;
|
||||
import ru.soune.nocopy.entity.complaint.ComplaintEntity;
|
||||
import ru.soune.nocopy.entity.complaint.ComplaintStatus;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.violation.Violation;
|
||||
import ru.soune.nocopy.exception.ComplaintNotFoundException;
|
||||
import ru.soune.nocopy.exception.DuplicateComplaintException;
|
||||
import ru.soune.nocopy.exception.ViolationNotFoundException;
|
||||
import ru.soune.nocopy.repository.ComplaintEntityRepository;
|
||||
import ru.soune.nocopy.repository.ViolationRepository;
|
||||
import ru.soune.nocopy.service.violation.ViolationService;
|
||||
import ru.soune.nocopy.service.violation.ViolationStatus;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional
|
||||
public class ComplaintEntityService {
|
||||
private final ComplaintEntityRepository complaintRepository;
|
||||
|
||||
private final ViolationRepository violationRepository;
|
||||
|
||||
private final ViolationService violationService;
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public ComplaintResponse createComplaint(ComplaintRequest request) {
|
||||
Violation violation = violationRepository.findById(request.getViolationId())
|
||||
.orElseThrow(() -> new ViolationNotFoundException("Violation not found: " + request.getViolationId()));
|
||||
|
||||
if (complaintRepository.existsByViolationId(request.getViolationId())) {
|
||||
throw new DuplicateComplaintException("Complaint already exists for violation: " + request.getViolationId());
|
||||
}
|
||||
|
||||
ComplaintEntity complaint = ComplaintEntity.builder()
|
||||
.status(ComplaintStatus.CREATED)
|
||||
.complaintText(request.getComplaintText())
|
||||
.email(request.getEmail())
|
||||
.violation(violation)
|
||||
.not_moderated_file(violation.getFileEntity().getStatus().equals(FileStatus.MODERATION))
|
||||
.build();
|
||||
|
||||
violationService.changeStatus(ViolationStatus.COMPLAINT_IN_WORK, request.getViolationId());
|
||||
|
||||
return mapToResponse(complaintRepository.save(complaint));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ComplaintResponse getComplaintById(Long id) {
|
||||
return complaintRepository.findById(id)
|
||||
.map(this::mapToResponse)
|
||||
.orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<ComplaintResponse> getAllComplaints(Pageable pageable) {
|
||||
return complaintRepository.findAll(pageable).map(this::mapToResponse);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<ComplaintResponse> getComplaintsByViolationId(Long violationId) {
|
||||
if (!violationRepository.existsById(violationId)) {
|
||||
throw new ViolationNotFoundException("Violation not found: " + violationId);
|
||||
}
|
||||
return complaintRepository.findByViolationId(violationId)
|
||||
.stream()
|
||||
.map(this::mapToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ComplaintResponse updateComplaintStatus(Long id, ComplaintStatus status) {
|
||||
ComplaintEntity complaint = complaintRepository.findById(id)
|
||||
.orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
|
||||
complaint.setStatus(status);
|
||||
return mapToResponse(complaintRepository.save(complaint));
|
||||
}
|
||||
|
||||
public ComplaintResponse updateComplaint(Long id, ComplaintRequest request) {
|
||||
ComplaintEntity complaint = complaintRepository.findById(id)
|
||||
.orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
|
||||
|
||||
if (request.getViolationId() != null) {
|
||||
Violation violation = violationRepository.findById(request.getViolationId())
|
||||
.orElseThrow(() -> new ViolationNotFoundException("Violation not found: " + request.getViolationId()));
|
||||
complaint.setViolation(violation);
|
||||
}
|
||||
if (request.getComplaintText() != null) complaint.setComplaintText(request.getComplaintText());
|
||||
if (request.getEmail() != null) complaint.setEmail(request.getEmail());
|
||||
if (request.getStatus() != null) complaint.setStatus(ComplaintStatus.valueOf(request.getStatus()));
|
||||
|
||||
return mapToResponse(complaintRepository.save(complaint));
|
||||
}
|
||||
|
||||
public ComplaintResponse deleteComplaint(Long id) {
|
||||
ComplaintEntity complaint = complaintRepository.findById(id)
|
||||
.orElseThrow(() -> new ComplaintNotFoundException("Complaint not found: " + id));
|
||||
complaint.setStatus(ComplaintStatus.CANCELLED);
|
||||
return mapToResponse(complaintRepository.save(complaint));
|
||||
}
|
||||
|
||||
private ComplaintResponse mapToResponse(ComplaintEntity entity) {
|
||||
|
||||
return ComplaintResponse.builder()
|
||||
.id(entity.getId())
|
||||
.status(entity.getStatus() != null ? entity.getStatus().name() : null)
|
||||
.complaintText(entity.getComplaintText())
|
||||
.email(entity.getEmail())
|
||||
.violationId(entity.getViolation() != null ? entity.getViolation().getId() : null)
|
||||
.createdAt(entity.getCreatedAt() != null ? entity.getCreatedAt().format(DATE_FORMATTER) : null)
|
||||
.updatedAt(entity.getUpdatedAt() != null ? entity.getUpdatedAt().format(DATE_FORMATTER) : null)
|
||||
.notModerated(entity.getNot_moderated_file() != null ? entity.getNot_moderated_file() : null)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import ru.soune.nocopy.entity.user.ProtectedFileCheck;
|
||||
import ru.soune.nocopy.repository.ProtectedFileCheckRepository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
@@ -15,7 +16,10 @@ import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -34,6 +38,8 @@ public class FileCleanupService {
|
||||
public void cleanupExpiredFiles() {
|
||||
log.info("Starting cleanup of expired temporary files");
|
||||
|
||||
cleanupTempFilesNightly();
|
||||
|
||||
Path tempDir = Paths.get(basePath, "temp");
|
||||
if (!Files.exists(tempDir)) {
|
||||
return;
|
||||
@@ -83,4 +89,50 @@ public class FileCleanupService {
|
||||
log.error("Error during cleanup", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupTempFilesNightly() {
|
||||
log.info("Starting nightly cleanup of temporary files");
|
||||
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
Path tempPath = Paths.get(tempDir);
|
||||
|
||||
if (!Files.exists(tempPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instant cutoffTime = Instant.now().minus(24, ChronoUnit.HOURS); // 24 часа
|
||||
int deletedCount = 0;
|
||||
|
||||
try (Stream<Path> files = Files.list(tempPath)) {
|
||||
List<Path> tempFiles = files
|
||||
.filter(path -> {
|
||||
String filename = path.getFileName().toString();
|
||||
return filename.contains("_") &&
|
||||
!filename.endsWith(".tmp") &&
|
||||
Files.isRegularFile(path);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (Path filePath : tempFiles) {
|
||||
try {
|
||||
File file = filePath.toFile();
|
||||
Instant lastModified = Instant.ofEpochMilli(file.lastModified());
|
||||
|
||||
if (lastModified.isBefore(cutoffTime)) {
|
||||
boolean deleted = Files.deleteIfExists(filePath);
|
||||
if (deleted) {
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error deleting file: {}", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Nightly cleanup deleted {} temporary files", deletedCount);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to list temp directory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,18 @@ import ru.soune.nocopy.dto.file.FileEntityResponse;
|
||||
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;
|
||||
import ru.soune.nocopy.entity.file.ProtectionStatus;
|
||||
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.exception.DuplicateImageException;
|
||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||
import ru.soune.nocopy.handler.MonitoringHandler;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||
import ru.soune.nocopy.repository.SimilarImageProjection;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.FileSimilarityService;
|
||||
import ru.soune.nocopy.service.ImageHashService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -32,7 +28,6 @@ import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -43,74 +38,15 @@ public class FileEntityService {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final ImageHashService imageHashService;
|
||||
|
||||
private final FileSimilarityService fileSimilarityService;
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final ImageResizeService imageResizeService;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@Value("${server.baseurl}")
|
||||
private String baseUrl;
|
||||
|
||||
@Transactional(noRollbackFor = DuplicateImageException.class)
|
||||
public FileEntity createFromUploadSession(FileUploadSession session, String checksum) {
|
||||
log.info("Creating FileEntity for upload session: {}", session.getUploadId());
|
||||
|
||||
try {
|
||||
Path filePath = Paths.get(session.getFilePath());
|
||||
|
||||
if (!Files.exists(filePath)) {
|
||||
throw new IOException("File not found on disk: " + filePath);
|
||||
}
|
||||
|
||||
Map<String, Long> imageHash = Map.of();
|
||||
|
||||
if (session.getFileType().startsWith("image")) {
|
||||
imageHash = imageHashService.calculateHash(filePath);
|
||||
List<SimilarImageProjection> duplicatedByHash = fileSimilarityService.findDuplicatedByHash(
|
||||
imageHash.get("hi"), imageHash.get("low"));
|
||||
|
||||
if (!duplicatedByHash.isEmpty()) {
|
||||
SimilarImageProjection similarImageProjection = duplicatedByHash.get(0);
|
||||
|
||||
throw new DuplicateImageException("Duplicate", similarImageProjection.getId(),
|
||||
similarImageProjection.getUserId());
|
||||
}
|
||||
}
|
||||
|
||||
long fileSize = Files.size(filePath);
|
||||
String originalName = session.getFileName();
|
||||
String storedName = filePath.getFileName().toString();
|
||||
|
||||
FileEntity fileEntity = FileEntity.builder()
|
||||
.userId(session.getUserId())
|
||||
.originalFileName(originalName)
|
||||
.storedFileName(storedName)
|
||||
.filePath(session.getFilePath())
|
||||
.fileSize(fileSize)
|
||||
.mimeType(session.getFileType())
|
||||
.fileExtension(session.getExtension())
|
||||
.checksum(checksum)
|
||||
.uploadSessionId(session.getUploadId())
|
||||
.status(FileStatus.ACTIVE)
|
||||
.build();
|
||||
|
||||
FileEntity saved = fileEntityRepository.save(fileEntity);
|
||||
|
||||
if (!imageHash.isEmpty()) {
|
||||
imageHashService.create(saved, imageHash);
|
||||
}
|
||||
|
||||
return saved;
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to create FileEntity for session {}: {}",
|
||||
session.getUploadId(), e.getMessage(), e);
|
||||
throw new RuntimeException("Failed to create file entity: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public FileEntityResponse getById(String fileId, int version) {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
||||
@@ -145,10 +81,12 @@ public class FileEntityService {
|
||||
for (Long uId : userRepository.findByCompanyId(user.getCompany().getId()).stream()
|
||||
.map(User::getId)
|
||||
.toList()) {
|
||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatus(uId, FileStatus.ACTIVE));
|
||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED)));
|
||||
}
|
||||
} else {
|
||||
allFiles = fileEntityRepository.findByUserIdAndStatus(userId, FileStatus.ACTIVE);
|
||||
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED));
|
||||
}
|
||||
|
||||
return allFiles;
|
||||
@@ -163,12 +101,13 @@ public class FileEntityService {
|
||||
for (Long uId : userRepository.findByCompanyId(user.getCompany().getId()).stream()
|
||||
.map(User::getId)
|
||||
.toList()) {
|
||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatus(uId, FileStatus.ACTIVE));
|
||||
allFiles.addAll(fileEntityRepository.findByUserIdAndStatusIn(uId,
|
||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED)));
|
||||
}
|
||||
|
||||
} else {
|
||||
allFiles = fileEntityRepository.findByUserIdAndStatus(
|
||||
userId, FileStatus.ACTIVE);
|
||||
allFiles = fileEntityRepository.findByUserIdAndStatusIn(userId,
|
||||
List.of(FileStatus.ACTIVE, FileStatus.BLOCKED));
|
||||
}
|
||||
|
||||
int start = (page - 1) * pageSize;
|
||||
@@ -205,45 +144,24 @@ public class FileEntityService {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public FileEntity markAsDeleted(FileEntity fileEntity) throws IOException {
|
||||
fileEntity.setStatus(FileStatus.DELETED);
|
||||
fileEntity.setUpdatedAt(LocalDateTime.now());
|
||||
fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||
|
||||
Path path = Paths.get(fileEntity.getProtectedFilePath());
|
||||
Files.deleteIfExists(path);
|
||||
fileEntity.setProtectedFilePath("");
|
||||
|
||||
return fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void softDeleteFileWithHash(FileEntity fileEntity) throws IOException {
|
||||
if (fileEntity.getImageHash() != null) {
|
||||
fileEntity.setImageHash(null);
|
||||
}
|
||||
|
||||
fileEntity.setStatus(FileStatus.DELETED);
|
||||
fileEntity.setStatus(FileStatus.REMOVED);
|
||||
fileEntity.setUpdatedAt(LocalDateTime.now());
|
||||
fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||
// fileEntity.setProtectionStatus(ProtectionStatus.NOT_PROTECTED);
|
||||
|
||||
Path path = Paths.get(fileEntity.getProtectedFilePath());
|
||||
Files.deleteIfExists(path);
|
||||
fileEntity.setProtectedFilePath("");
|
||||
// Path path = Paths.get(fileEntity.getProtectedFilePath());
|
||||
// Files.deleteIfExists(path);
|
||||
// fileEntity.setProtectedFilePath("");
|
||||
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void deleteFromDisk(FileEntity fileEntity) throws IOException {
|
||||
Path path = Paths.get(fileEntity.getFilePath());
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Files.delete(path);
|
||||
|
||||
fileEntityRepository.delete(fileEntity);
|
||||
public void deleteFromDisk(String deleteFilePath) throws IOException {
|
||||
Files.deleteIfExists(Paths.get(deleteFilePath));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
@@ -252,7 +170,7 @@ public class FileEntityService {
|
||||
return totalSize != null ? totalSize : 0L;
|
||||
}
|
||||
|
||||
public void changeStatus(ProtectionStatus newStatus, String fileId) {
|
||||
public void changeProtectionStatus(ProtectionStatus newStatus, String fileId) {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(fileId));
|
||||
fileEntity.setProtectionStatus(newStatus);
|
||||
@@ -260,8 +178,16 @@ public class FileEntityService {
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void changeFileStatus(FileStatus newStatus, String fileId) {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(fileId)
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(fileId));
|
||||
fileEntity.setStatus(newStatus);
|
||||
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
||||
public FileEntity writeProtectedFile(String id, byte[] data, String fileExt) throws IOException {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id)
|
||||
.orElseThrow(() -> new RuntimeException("File not found: " + id));
|
||||
|
||||
@@ -284,14 +210,14 @@ public class FileEntityService {
|
||||
fileEntity.setFileExtension(extension);
|
||||
fileEntity.setProtectionStatus(ProtectionStatus.PROTECTED);
|
||||
|
||||
fileEntityRepository.save(fileEntity);
|
||||
return fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public void clearTempFiles(long userId) throws IOException {
|
||||
public void clearTempFiles(long userId) {
|
||||
List<FileEntity> fileByUserIdAndStatus = fileEntityRepository.findFileByUserIdAndStatus(userId, FileStatus.TEMP);
|
||||
|
||||
for (FileEntity fileEntity : fileByUserIdAndStatus) {
|
||||
deleteFromDisk(fileEntity);
|
||||
cloudStorageService.deleteFromStorageDisk(fileEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,24 +229,6 @@ public class FileEntityService {
|
||||
return fileEntityRepository.findFileIdByFilePath(filePath);
|
||||
}
|
||||
|
||||
public File getFileById(String id) {
|
||||
try {
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
|
||||
File file = new File(fileEntity.getFilePath());
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException("File not found on disk: " + fileEntity.getFilePath());
|
||||
}
|
||||
|
||||
return file;
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting file: {}", id, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Path prepareProtectedPath(FileEntity fileEntity, String extension) throws IOException {
|
||||
Path originalPath = Paths.get(fileEntity.getFilePath());
|
||||
|
||||
@@ -357,6 +265,18 @@ public class FileEntityService {
|
||||
fileEntityRepository.save(fileEntity);
|
||||
}
|
||||
|
||||
public String formatFileSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format("%.1f KB", size / 1024.0);
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
||||
} else {
|
||||
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkFileExistsOnDisk(String filePath) {
|
||||
try {
|
||||
return Files.exists(Paths.get(filePath));
|
||||
@@ -424,16 +344,4 @@ public class FileEntityService {
|
||||
.thumbnailFileUrl(baseUrl + "/api/files/protected/" + fileEntity.getId() + "/thumbnail")
|
||||
.build();
|
||||
}
|
||||
|
||||
public String formatFileSize(long size) {
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format("%.1f KB", size / 1024.0);
|
||||
} else if (size < 1024 * 1024 * 1024) {
|
||||
return String.format("%.1f MB", size / (1024.0 * 1024.0));
|
||||
} else {
|
||||
return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package ru.soune.nocopy.service.file;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.soune.nocopy.dto.file.UploadProgressResponse;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||
|
||||
@@ -19,5 +20,5 @@ public interface FileUploadService {
|
||||
|
||||
UploadProgressResponse getUploadProgress(String uploadId);
|
||||
|
||||
void completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
||||
FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
package ru.soune.nocopy.service.file.cloud;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.AwsCredentials;
|
||||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.http.apache.ApacheHttpClient;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class CloudStorageService {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
@Value("${yandex.cloud.key}")
|
||||
private String key;
|
||||
|
||||
@Value("${yandex.cloud.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${yandex.cloud.region}")
|
||||
private String region;
|
||||
|
||||
@Value("${yandex.cloud.s3-endpoint}")
|
||||
private String s3endpoint;
|
||||
|
||||
@Value("${yandex.cloud.bucket}")
|
||||
private String bucket;
|
||||
|
||||
public void saveFilesToStorage(String mimeType, String extension, String sendToCloudFilePath) {
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String mimeTypeFromFile = mimeType.equals("document") ? "application" : mimeType;
|
||||
String contentType = mimeTypeFromFile + "/" + extension;
|
||||
String filePath = "files" + sendToCloudFilePath;
|
||||
|
||||
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(filePath)
|
||||
.contentType(contentType)
|
||||
.build();
|
||||
|
||||
try {
|
||||
s3Client.putObject(putObjectRequest,
|
||||
RequestBody.fromBytes(Files.readAllBytes(Paths.get(sendToCloudFilePath))));
|
||||
Files.deleteIfExists(Paths.get(sendToCloudFilePath));
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to read file: {}", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public File readFileFromStorageByPath(String filePath) throws IOException {
|
||||
GetObjectRequest objectRequest = GetObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key("files" + filePath)
|
||||
.build();
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
String fileName = filePath.replace("/", "_");
|
||||
File tempFile = new File(tempDir, fileName);
|
||||
|
||||
s3Client.getObject(objectRequest, tempFile.toPath());
|
||||
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
public void deleteFileFromStorage(String filePath) {
|
||||
if (filePath == null || filePath.isEmpty()) {
|
||||
log.warn("Attempted to delete file with empty path");
|
||||
return;
|
||||
}
|
||||
|
||||
AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
|
||||
|
||||
S3Client s3Client = S3Client.builder()
|
||||
.httpClient(ApacheHttpClient.create())
|
||||
.region(Region.of(region))
|
||||
.endpointOverride(URI.create(s3endpoint))
|
||||
.credentialsProvider(StaticCredentialsProvider.create(credentials))
|
||||
.build();
|
||||
|
||||
String cloudKey = "files" + filePath;
|
||||
|
||||
try {
|
||||
try {
|
||||
s3Client.headObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
||||
} catch (Exception e) {
|
||||
log.warn("File does not exist in cloud storage: {}", cloudKey);
|
||||
return;
|
||||
}
|
||||
|
||||
s3Client.deleteObject(builder -> builder.bucket(bucket).key(cloudKey).build());
|
||||
|
||||
log.info("File successfully deleted from cloud storage: {}", cloudKey);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to delete file from cloud storage: {}", cloudKey, e);
|
||||
throw new RuntimeException("Failed to delete file from cloud storage: " + cloudKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteFromStorageDisk(FileEntity fileEntity) {
|
||||
deleteFileFromStorage(fileEntity.getFilePath());
|
||||
|
||||
if (fileEntity.getProtectedFilePath() != null) {
|
||||
deleteFileFromStorage(fileEntity.getProtectedFilePath());
|
||||
}
|
||||
|
||||
fileEntityRepository.delete(fileEntity);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.vrt.fileprotection.NoCopyCheckResult;
|
||||
import com.vrt.fileprotection.audio.AudioCheckResult;
|
||||
import com.vrt.fileprotection.documents.DocumentCheckResult;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -26,6 +27,8 @@ import ru.soune.nocopy.service.ImageHashService;
|
||||
import ru.soune.nocopy.service.cost.CostService;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
import ru.soune.nocopy.service.tariff.TariffConstants;
|
||||
import ru.soune.nocopy.service.tariff.TariffInfoService;
|
||||
import ru.soune.nocopy.util.FileUtil;
|
||||
|
||||
@@ -84,6 +87,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
|
||||
private final TariffInfoService tariffInfoService;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
@@ -179,7 +184,6 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber,
|
||||
MultipartFile chunkFile, Integer findSimilar) {
|
||||
FileUploadSession session = sessionRepository.findById(uploadId)
|
||||
@@ -221,7 +225,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
@Async("fileUploadTaskExecutor")
|
||||
@Transactional
|
||||
@Override
|
||||
public void completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
||||
public FileEntity completeFileProcessingAsync(FileUploadSession session, FileStatus status) {
|
||||
FileEntity saved = null;
|
||||
try {
|
||||
Path filePath = Paths.get(session.getFilePath());
|
||||
String checksum = calculateChecksum(filePath);
|
||||
@@ -240,7 +245,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
.status(status)
|
||||
.build();
|
||||
|
||||
FileEntity saved = fileEntityRepository.save(fileEntity);
|
||||
saved = fileEntityRepository.save(fileEntity);
|
||||
|
||||
if (session.getFileType().equals("image")) {
|
||||
Map<String, Long> hash = imageHashService.calculateHash(filePath);
|
||||
@@ -257,11 +262,12 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
}
|
||||
|
||||
log.info("File processing completed for session: {}", session.getUploadId());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to complete file processing for session {}: {}",
|
||||
session.getUploadId(), e.getMessage(), e);
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
private void validateSession(FileUploadSession session) {
|
||||
@@ -293,6 +299,9 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
|
||||
private UploadProgressResponse processChunk(FileUploadSession session, Integer chunkNumber, MultipartFile chunkFile,
|
||||
Integer findSimilar) {
|
||||
String chunkPath = null;
|
||||
@@ -313,7 +322,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
chunkNumber, session.getChunksUploaded(), session.getTotalChunks());
|
||||
|
||||
boolean isLastChunk = session.getChunksUploaded().equals(session.getTotalChunks());
|
||||
|
||||
//TODO CHECK
|
||||
if (isLastChunk) {
|
||||
String finalFilePath = assembleFileSynchronously(session);
|
||||
|
||||
@@ -324,7 +333,8 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
fileSimilarityService.hasDuplicatesByHash(finalFilePath ,session.getFileType(),
|
||||
session.getUserId());
|
||||
|
||||
File file = new File(finalFilePath);
|
||||
// File file = new File(finalFilePath);
|
||||
File file = cloudStorageService.readFileFromStorageByPath(finalFilePath);
|
||||
|
||||
if (session.getFileType().startsWith("audio") && !isWavFile(file)) {
|
||||
session.setStatus(UploadStatus.FAILED);
|
||||
@@ -364,14 +374,26 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
session.setStatus(UploadStatus.COMPLETED);
|
||||
session.setFilePath(finalFilePath);
|
||||
|
||||
FileStatus status = findSimilar == 0 ? FileStatus.ACTIVE: FileStatus.TEMP;
|
||||
FileStatus status = findSimilar == 0 ? FileStatus.MODERATION: FileStatus.TEMP;
|
||||
|
||||
if (status == FileStatus.TEMP) {
|
||||
fileEntityService.clearTempFiles(session.getUserId());
|
||||
}
|
||||
|
||||
completeFileProcessingAsync(session, status);
|
||||
FileEntity fileEntity = completeFileProcessingAsync(session, status);
|
||||
|
||||
fileEntityRepository.flush();
|
||||
|
||||
entityManager.clear();
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getFilePath());
|
||||
|
||||
if (status != FileStatus.TEMP) {
|
||||
FileProtector.FileInfo fileInfo = fileUtil.createFileInfo(fileEntity, session.getConvertTo());
|
||||
|
||||
noCopyFileService.addFile(fileInfo);
|
||||
}
|
||||
|
||||
if (status != FileStatus.TEMP) {
|
||||
String fileType = session.getFileType();
|
||||
@@ -511,6 +533,7 @@ public class FileUploadServiceImpl implements FileUploadService {
|
||||
private void checkForDuplicatesSynchronously(String filePath)
|
||||
throws IOException , DuplicateImageException {
|
||||
Path path = Paths.get(filePath);
|
||||
|
||||
Map<String, Long> hash = imageHashService.calculateHash(path);
|
||||
|
||||
List<SimilarImageProjection> duplicates = fileSimilarityService.findDuplicatedByHash(
|
||||
|
||||
@@ -16,21 +16,21 @@ public class NoCopyProcessingListenerImpl implements FileProtector.ProcessingLis
|
||||
|
||||
@Override
|
||||
public void onStartProcessing(FileProtector.FileInfo fileInfo) {
|
||||
fileEntityService.changeStatus(ProtectionStatus.PROCESSING, fileInfo.getId());
|
||||
fileEntityService.changeProtectionStatus(ProtectionStatus.PROCESSING, fileInfo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProcessingFailed(FileProtector.FileInfo fileInfo) {
|
||||
fileEntityService.changeStatus(ProtectionStatus.FAILED, fileInfo.getId());
|
||||
fileEntityService.changeProtectionStatus(ProtectionStatus.FAILED, fileInfo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSavingFailed(FileProtector.FileInfo fileInfo) {
|
||||
fileEntityService.changeStatus(ProtectionStatus.FAILED_SAVE, fileInfo.getId());
|
||||
fileEntityService.changeProtectionStatus(ProtectionStatus.FAILED_SAVE, fileInfo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(FileProtector.FileInfo fileInfo) {
|
||||
fileEntityService.changeStatus(ProtectionStatus.PROTECTED, fileInfo.getId());;
|
||||
fileEntityService.changeProtectionStatus(ProtectionStatus.PROTECTED, fileInfo.getId());;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.repository.FileUploadSessionRepository;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
import ru.soune.nocopy.service.file.FileUploadService;
|
||||
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -32,27 +35,55 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final CloudStorageService cloudStorageService;
|
||||
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getImageFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getVideoFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public File getAudioFile(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable File getDocument(@NotNull String id) {
|
||||
return fileEntityService.getFileById(id);
|
||||
FileEntity fileEntity = fileEntityRepository.findById(id).orElseThrow(() ->
|
||||
new RuntimeException("File not found: " + id));
|
||||
try {
|
||||
return cloudStorageService.readFileFromStorageByPath(fileEntity.getFilePath());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,7 +118,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeAudioFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -99,7 +134,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeVideoFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -111,7 +150,11 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public com.vrt.fileprotection.OperationResult writeImageFile(@NotNull String id, @NotNull byte[] data, @Nullable String fileExt) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, fileExt);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
@@ -122,7 +165,10 @@ public class ProtectionFileProviderImpl implements FileProtector.FileProvider {
|
||||
@Override
|
||||
public @NotNull OperationResult writeDocumentFile(@NotNull String id, @NotNull byte[] data) {
|
||||
try {
|
||||
fileEntityService.writeProtectedFile(id, data, null);
|
||||
FileEntity fileEntity = fileEntityService.writeProtectedFile(id, data, null);
|
||||
|
||||
cloudStorageService.saveFilesToStorage(fileEntity.getMimeType(), fileEntity.getFileExtension(),
|
||||
fileEntity.getProtectedFilePath());
|
||||
return OperationResult.Companion.success();
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to create protected file: {}", id, e);
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package ru.soune.nocopy.service.file.moderation;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import ru.soune.nocopy.dto.file.*;
|
||||
import ru.soune.nocopy.entity.file.*;
|
||||
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
|
||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||
|
||||
import ru.soune.nocopy.exception.InvalidAppealException;
|
||||
import ru.soune.nocopy.repository.FileAppealRepository;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.repository.ModerationLogRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ModerationService {
|
||||
private final FileEntityRepository fileEntityRepository;
|
||||
|
||||
private final FileAppealRepository fileAppealRepository;
|
||||
|
||||
private final ModerationLogRepository moderationLogRepository;
|
||||
|
||||
private final FileEntityService fileEntityService;
|
||||
|
||||
private static final List<FileStatus> MODERATION_NEEDED_STATUSES = Arrays.asList(
|
||||
FileStatus.MODERATION,
|
||||
FileStatus.BLOCKED
|
||||
);
|
||||
|
||||
/**
|
||||
* Модерация файла администратором
|
||||
*/
|
||||
@Transactional
|
||||
public void moderateFile(ModerationActionRequest request, Long moderatorId) {
|
||||
FileEntity file = fileEntityRepository.findById(request.getFileId())
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(request.getFileId()));
|
||||
|
||||
if (!MODERATION_NEEDED_STATUSES.contains(file.getStatus())) {
|
||||
throw new IllegalStateException("File is not in moderation state. Current status: " + file.getStatus());
|
||||
}
|
||||
|
||||
ModerationLog log = ModerationLog.builder()
|
||||
.fileId(file.getId())
|
||||
.moderatorId(moderatorId)
|
||||
.oldStatus(file.getStatus())
|
||||
.newStatus(request.getNewStatus())
|
||||
.reason(request.getComment())
|
||||
.comment(request.getComment())
|
||||
.build();
|
||||
|
||||
moderationLogRepository.save(log);
|
||||
|
||||
file.setStatus(request.getNewStatus());
|
||||
|
||||
fileEntityRepository.save(file);
|
||||
|
||||
if (request.getNewStatus() == FileStatus.BLOCKED ||
|
||||
request.getNewStatus() == FileStatus.VIOLATION) {
|
||||
|
||||
fileAppealRepository.findByFileIdAndStatus(file.getId(), AppealStatus.PENDING)
|
||||
.ifPresent(appeal -> {
|
||||
appeal.setStatus(AppealStatus.REJECTED);
|
||||
appeal.setAdminComment(request.getComment());
|
||||
appeal.setResolvedAt(LocalDateTime.now());
|
||||
fileAppealRepository.save(appeal);
|
||||
});
|
||||
} else if (request.getNewStatus() == FileStatus.REMOVED) {
|
||||
try {
|
||||
fileEntityService.softDeleteFileWithHash(file);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подача апелляции пользователем
|
||||
*/
|
||||
@Transactional
|
||||
public AppealResponse submitAppeal(AppealRequest request, Long userId) {
|
||||
FileEntity file = fileEntityRepository.findById(request.getFileId())
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(request.getFileId()));
|
||||
|
||||
if (!file.getUserId().equals(userId)) {
|
||||
throw new SecurityException("User is not the owner of this file");
|
||||
}
|
||||
|
||||
if (!canAppeal(file)) {
|
||||
throw new InvalidAppealException("Cannot appeal this file. Current status: " + file.getStatus());
|
||||
}
|
||||
|
||||
if (hasActiveAppeal(file.getId())) {
|
||||
throw new InvalidAppealException("Active appeal already exists for this file");
|
||||
}
|
||||
|
||||
FileAppeal appeal = FileAppeal.builder()
|
||||
.fileId(file.getId())
|
||||
.userId(userId)
|
||||
.appealReason(request.getAppealReason())
|
||||
.additionalInfo(request.getAdditionalInfo())
|
||||
.status(AppealStatus.PENDING)
|
||||
.moderationBeforeStatus(file.getStatus())
|
||||
.build();
|
||||
|
||||
FileAppeal saved = fileAppealRepository.save(appeal);
|
||||
|
||||
return convertToResponse(saved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассмотрение апелляции администратором
|
||||
*/
|
||||
@Transactional
|
||||
public void reviewAppeal(String appealId, boolean approve, String comment, Long moderatorId) {
|
||||
FileAppeal appeal = fileAppealRepository.findById(appealId)
|
||||
.orElseThrow(() -> new RuntimeException("Appeal not found: " + appealId));
|
||||
|
||||
if (appeal.getStatus() != AppealStatus.PENDING) {
|
||||
throw new IllegalStateException("Appeal already processed");
|
||||
}
|
||||
|
||||
FileEntity file = fileEntityRepository.findById(appeal.getFileId())
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(appeal.getFileId()));
|
||||
|
||||
appeal.setStatus(approve ? AppealStatus.APPROVED : AppealStatus.REJECTED);
|
||||
appeal.setAdminComment(comment);
|
||||
appeal.setModeratorId(moderatorId);
|
||||
appeal.setResolvedAt(LocalDateTime.now());
|
||||
|
||||
if (approve) {
|
||||
file.setStatus(FileStatus.ACTIVE);
|
||||
}
|
||||
|
||||
ModerationLog log = ModerationLog.builder()
|
||||
.fileId(file.getId())
|
||||
.moderatorId(moderatorId)
|
||||
.oldStatus(file.getStatus())
|
||||
.newStatus(file.getStatus())
|
||||
.comment("Appeal review: " + comment)
|
||||
.build();
|
||||
moderationLogRepository.save(log);
|
||||
|
||||
fileEntityRepository.save(file);
|
||||
|
||||
fileAppealRepository.save(appeal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка, можно ли подать апелляцию на файл
|
||||
*/
|
||||
public boolean canAppeal(FileEntity file) {
|
||||
return file.getStatus() == FileStatus.BLOCKED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия активной апелляции
|
||||
*/
|
||||
public boolean hasActiveAppeal(String fileId) {
|
||||
return fileAppealRepository.existsByFileIdAndStatusIn(
|
||||
fileId,
|
||||
Arrays.asList(AppealStatus.PENDING, AppealStatus.IN_REVIEW)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение информации о файле для модерации
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public FileModerationInfo getFileModerationInfo(String fileId) {
|
||||
FileEntity file = fileEntityRepository.findById(fileId)
|
||||
.orElseThrow(() -> new FileEntityNotFoundException(fileId));
|
||||
|
||||
FileAppeal activeAppeal = fileAppealRepository.findByFileIdAndStatus(
|
||||
fileId, AppealStatus.PENDING).orElse(null);
|
||||
|
||||
return FileModerationInfo.builder()
|
||||
.fileId(file.getId())
|
||||
.fileName(file.getOriginalFileName())
|
||||
.currentStatus(file.getStatus())
|
||||
.uploadDate(file.getCreatedAt())
|
||||
.hasActiveAppeal(activeAppeal != null)
|
||||
.activeAppeal(activeAppeal != null ?
|
||||
AppealInfo.builder()
|
||||
.appealId(activeAppeal.getId())
|
||||
.status(activeAppeal.getStatus().name())
|
||||
.createdAt(activeAppeal.getCreatedAt())
|
||||
.build() : null)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение списка файлов на модерации
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Page<FileEntity> getFilesForModeration(int page, int size) {
|
||||
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
|
||||
return fileEntityRepository.findByStatusIn(
|
||||
Arrays.asList(FileStatus.MODERATION, FileStatus.BLOCKED),
|
||||
pageable
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение апелляций пользователя
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Page<AppealResponse> getUserAppeals(Long userId, int page, int size) {
|
||||
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
|
||||
return fileAppealRepository.findByUserId(userId, pageable)
|
||||
.map(this::convertToResponse);
|
||||
}
|
||||
|
||||
private AppealResponse convertToResponse(FileAppeal appeal) {
|
||||
return AppealResponse.builder()
|
||||
.appealId(appeal.getId())
|
||||
.fileId(appeal.getFileId())
|
||||
.appealReason(appeal.getAppealReason())
|
||||
.additionalInfo(appeal.getAdditionalInfo())
|
||||
.status(appeal.getStatus().name())
|
||||
.adminComment(appeal.getAdminComment())
|
||||
.createdAt(appeal.getCreatedAt())
|
||||
.resolvedAt(appeal.getResolvedAt())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public class MonitoringSearchService {
|
||||
|
||||
private final SearchProperties searchProperties;
|
||||
|
||||
@Transactional
|
||||
@Transactional(noRollbackFor = TariffNotFoundException.class)
|
||||
public void processFileSearch(FileMonitoringEntity monitoring) throws MessagingException, IOException {
|
||||
monitoring.setLastRun(LocalDateTime.now());
|
||||
monitoring.setLastRunStatus("IN_PROGRESS");
|
||||
@@ -141,8 +141,6 @@ public class MonitoringSearchService {
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing monitoring search", e);
|
||||
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
||||
|
||||
} finally {
|
||||
updateNextRun(monitoring);
|
||||
monitoringRepository.save(monitoring);
|
||||
}
|
||||
|
||||
@@ -75,9 +75,6 @@ public class PayoutRequestService {
|
||||
|
||||
balanceService.freezeAmount(user, request.getAmount());
|
||||
|
||||
log.info("Payout requset created: ID={}, user={}, sum={}, status={}",
|
||||
savedRequest.getId(), user.getId(), request.getAmount(), savedRequest.getStatus());
|
||||
|
||||
return savedRequest;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,8 +99,7 @@ public class ReferralRepoImpl implements ReferralRepo {
|
||||
|
||||
@Override
|
||||
public int getAvailableIncome() {
|
||||
Integer holdBalance = entity.getHoldBalance();
|
||||
return entity.getTotalIncome() - (holdBalance != null ? holdBalance : 0);
|
||||
return entity.getAvailableIncome();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,6 +108,11 @@ public class ReferralRepoImpl implements ReferralRepo {
|
||||
return holdBalance != null ? holdBalance : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpentBalance() {
|
||||
return entity.getTotalIncome() - entity.getAvailableIncome() - entity.getHoldBalance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getActive() { return entity.isActive(); }
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ public class GlobalSearchAsyncProcessor {
|
||||
try {
|
||||
for (int i = 0; i < filesToProcess.size(); i++) {
|
||||
FileEntity file = filesToProcess.get(i);
|
||||
|
||||
tariffInfoService.writeOffTokens(userId, TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
||||
GlobalSearchResult result = processFile(file, taskId, userId);
|
||||
globalSearchResultRepository.save(result);
|
||||
|
||||
@@ -171,10 +171,6 @@ public class GlobalSearchAsyncProcessor {
|
||||
log.warn("No results for file {} because all engines are disabled", file.getId());
|
||||
} else {
|
||||
result.setFileStatus(SearchStatus.SUCCESS.name());
|
||||
|
||||
if (!allUniqueImages.isEmpty()) {
|
||||
tariffInfoService.writeOffTokens(userId, TariffConstants.TOKEN_VALUE_FOR_SEARCH);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.search.GlobalSearchStartRequest;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileType;
|
||||
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||
import ru.soune.nocopy.entity.search.GlobalSearchTask;
|
||||
import ru.soune.nocopy.entity.search.SearchStatus;
|
||||
@@ -61,6 +62,7 @@ public class GlobalSearchService {
|
||||
} else {
|
||||
List<String> fileIds = request.getFileIds().isEmpty() ? fileEntityService.getAllUserFiles(userId)
|
||||
.stream()
|
||||
.filter(f -> f.getMimeType().equals(FileType.IMAGE.getDisplayName()))
|
||||
.map(FileEntity::getId)
|
||||
.toList(): request.getFileIds();
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ public class SearchImageService {
|
||||
}
|
||||
|
||||
private OkHttpClient createHttpClient() {
|
||||
boolean useProxy = searchProperties.getProxy().isEnabled();
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder()
|
||||
.connectTimeout(35, TimeUnit.SECONDS)
|
||||
.writeTimeout(35, TimeUnit.SECONDS)
|
||||
@@ -99,12 +100,12 @@ public class SearchImageService {
|
||||
.followSslRedirects(true)
|
||||
.retryOnConnectionFailure(true);
|
||||
|
||||
// if (useProxy && searchProperties.getProxy().isEnabled()) {
|
||||
// Proxy proxy = new Proxy(Proxy.Type.HTTP,
|
||||
// new InetSocketAddress(searchProperties.getProxy().getHost(),
|
||||
// searchProperties.getProxy().getPort()));
|
||||
// builder.proxy(proxy);
|
||||
// }
|
||||
if (useProxy && searchProperties.getProxy().isEnabled()) {
|
||||
Proxy proxy = new Proxy(Proxy.Type.HTTP,
|
||||
new InetSocketAddress(searchProperties.getProxy().getHost(),
|
||||
searchProperties.getProxy().getPort()));
|
||||
builder.proxy(proxy);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@@ -114,9 +115,6 @@ public class SearchImageService {
|
||||
throw new IllegalStateException("SearchAPI key not configured");
|
||||
}
|
||||
|
||||
// boolean useProxy = searchProperties.getProxy().isEnabled();
|
||||
|
||||
// OkHttpClient client = createHttpClient(useProxy);
|
||||
OkHttpClient client = createHttpClient();
|
||||
|
||||
HttpUrl url = HttpUrl.parse("https://www.searchapi.io/api/v1/search")
|
||||
@@ -137,6 +135,7 @@ public class SearchImageService {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
ResponseBody body = response.body();
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
log.info("SearchAPI response code={}, duration={}ms, engine={}",
|
||||
response.code(), duration, engine);
|
||||
@@ -146,7 +145,6 @@ public class SearchImageService {
|
||||
throw new IOException("API error " + response.code() + ": " + errorBody);
|
||||
}
|
||||
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
throw new IOException("Empty response body");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package ru.soune.nocopy.service.tariff;
|
||||
|
||||
import jakarta.mail.MessagingException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffInfoDTO;
|
||||
@@ -13,13 +15,16 @@ import ru.soune.nocopy.exception.TariffNotFoundException;
|
||||
import ru.soune.nocopy.repository.TariffInfoRepository;
|
||||
import ru.soune.nocopy.repository.TariffRepository;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.mail.EmailService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class TariffInfoService {
|
||||
|
||||
private TariffInfoRepository tariffInfoRepository;
|
||||
@@ -28,6 +33,8 @@ public class TariffInfoService {
|
||||
|
||||
private UserRepository userRepository;
|
||||
|
||||
private EmailService emailService;
|
||||
|
||||
public TariffInfo createTariffInfo(LocalDateTime startTariff, LocalDateTime endTariff, TariffStatus tariffStatus,
|
||||
Integer tokens) {
|
||||
TariffInfo tariffInfo = new TariffInfo();
|
||||
@@ -40,8 +47,8 @@ public class TariffInfoService {
|
||||
return tariffInfoRepository.save(tariffInfo);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void writeOffTokens(long userId, Integer value) {
|
||||
// @Transactional
|
||||
public void writeOffTokens(long userId, Integer value) throws MessagingException, IOException {
|
||||
User user = userRepository.findById(userId).orElseThrow();
|
||||
|
||||
TariffInfo activeTariffInfo;
|
||||
@@ -55,7 +62,7 @@ public class TariffInfoService {
|
||||
writeOffTokensForTariff(activeTariffInfo, value);
|
||||
}
|
||||
|
||||
public void writeOffTokensForTariff(TariffInfo activeTariffInfo, Integer value) {
|
||||
public void writeOffTokensForTariff(TariffInfo activeTariffInfo, Integer value) throws MessagingException, IOException {
|
||||
Integer tokens = activeTariffInfo.getTokens();
|
||||
Integer boughtTokens = activeTariffInfo.getBoughtTokens();
|
||||
|
||||
@@ -68,6 +75,12 @@ public class TariffInfoService {
|
||||
|
||||
tariffInfoRepository.save(activeTariffInfo);
|
||||
} else {
|
||||
int needTokens = boughtTokens - value;
|
||||
User user = userRepository.findByPersonalTariffInfo(activeTariffInfo);
|
||||
|
||||
emailService.sendTokensNotFoundEmail(user, tokens, Math.abs(needTokens));
|
||||
log.error("User:" + user.getId() + " not have tokens for protect");
|
||||
|
||||
throw new TariffNotFoundException("User not have tokens for protect");
|
||||
}
|
||||
}
|
||||
@@ -75,8 +88,8 @@ public class TariffInfoService {
|
||||
public TariffInfo createTariffInfo(TariffType tariffType) {
|
||||
Tariff tariff = tariffRepository.findByType(tariffType.toString()).orElseThrow();
|
||||
|
||||
TariffInfo tariffInfo = createTariffInfo(LocalDateTime.now(), LocalDateTime.now().plusDays(30),
|
||||
TariffStatus.ACTIVE, tariff.getTokens());
|
||||
TariffInfo tariffInfo = createTariffInfo(LocalDateTime.now(),
|
||||
LocalDateTime.now().plusDays(tariff.getTariffTerm().getDays()), TariffStatus.ACTIVE, tariff.getTokens());
|
||||
tariffInfo.setTariff(tariff);
|
||||
|
||||
return tariffInfoRepository.save(tariffInfo);
|
||||
|
||||
@@ -7,13 +7,12 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
||||
import ru.soune.nocopy.entity.tarif.TariffTimeTerm;
|
||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||
import ru.soune.nocopy.repository.TariffRepository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -109,6 +108,18 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 100);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case DEMO:
|
||||
tariff.setName("demo");
|
||||
tariff.setPrice(0);
|
||||
tariff.setTokens(30);
|
||||
tariff.setMaxFilesCount(10);
|
||||
tariff.setDiskSize(1024L * 1024 * 100);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case START:
|
||||
@@ -119,6 +130,18 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 500);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case START_YEAR:
|
||||
tariff.setName("start_year");
|
||||
tariff.setPrice(1100);
|
||||
tariff.setTokens(300);
|
||||
tariff.setMaxFilesCount(80);
|
||||
tariff.setDiskSize(1024L * 1024 * 500);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
case PRO:
|
||||
@@ -129,8 +152,21 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case PRO_YEAR:
|
||||
tariff.setName("pro_year");
|
||||
tariff.setPrice(4100);
|
||||
tariff.setTokens(900);
|
||||
tariff.setMaxFilesCount(300);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
||||
tariff.setMaxUsers(1L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
|
||||
case STUDIO:
|
||||
tariff.setName("studio");
|
||||
tariff.setPrice(9750);
|
||||
@@ -139,6 +175,18 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(2L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case STUDIO_YEAR:
|
||||
tariff.setName("studio_year");
|
||||
tariff.setPrice(9500);
|
||||
tariff.setTokens(3000);
|
||||
tariff.setMaxFilesCount(1000);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(2L);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
case BASIC:
|
||||
@@ -149,6 +197,18 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(basicUserCount);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case BASIC_YEAR:
|
||||
tariff.setName("basic_year");
|
||||
tariff.setPrice(basicPrice);
|
||||
tariff.setTokens(basicTokens);
|
||||
tariff.setMaxFilesCount(enterpriceMaxFileCount);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(basicUserCount);
|
||||
tariff.setTariffAccountType("b2c");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
case ENTERPRISE:
|
||||
@@ -159,6 +219,18 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
||||
tariff.setMaxUsers(10L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case ENTERPRISE_YEAR:
|
||||
tariff.setName("enterprise_year");
|
||||
tariff.setPrice(200000);
|
||||
tariff.setTokens(30000);
|
||||
tariff.setMaxFilesCount(100000);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 10L);
|
||||
tariff.setMaxUsers(10L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
case BUISNES:
|
||||
@@ -169,8 +241,21 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(5L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case BUISNES_YEAR:
|
||||
tariff.setName("buisnes_year");
|
||||
tariff.setPrice(29000);
|
||||
tariff.setTokens(5000);
|
||||
tariff.setMaxFilesCount(5000);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(5L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
|
||||
case CORPORAT:
|
||||
tariff.setName("corporat");
|
||||
tariff.setPrice(99000);
|
||||
@@ -179,8 +264,21 @@ public class TariffInitializationService {
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(10L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.MONTHLY);
|
||||
break;
|
||||
|
||||
case CORPORAT_YEAR:
|
||||
tariff.setName("corporat_year");
|
||||
tariff.setPrice(99000);
|
||||
tariff.setTokens(20000);
|
||||
tariff.setMaxFilesCount(50000);
|
||||
tariff.setDiskSize(1024L * 1024 * 1024 * 50L);
|
||||
tariff.setMaxUsers(10L);
|
||||
tariff.setTariffAccountType("b2b");
|
||||
tariff.setTariffTerm(TariffTimeTerm.YEAR);
|
||||
break;
|
||||
|
||||
|
||||
case TOKEN_100:
|
||||
tariff.setName("token_100");
|
||||
tariff.setPrice(299);
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.tarriff.TariffDTO;
|
||||
import ru.soune.nocopy.entity.tarif.Tariff;
|
||||
import ru.soune.nocopy.entity.tarif.TariffTimeTerm;
|
||||
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.exception.TariffNotFoundException;
|
||||
@@ -20,7 +21,7 @@ public class TariffService {
|
||||
private final TariffRepository tariffRepository;
|
||||
|
||||
public void addTariff(String tariffName, double price, String type, int tokens, long diskSize, long maxUsers,
|
||||
int maxFilesCount) {
|
||||
int maxFilesCount, String tariffTerm) {
|
||||
Tariff tariff = new Tariff();
|
||||
tariff.setName(tariffName);
|
||||
tariff.setPrice(price);
|
||||
@@ -29,6 +30,7 @@ public class TariffService {
|
||||
tariff.setDiskSize(diskSize);
|
||||
tariff.setMaxUsers(maxUsers);
|
||||
tariff.setMaxFilesCount(maxFilesCount);
|
||||
tariff.setTariffTerm(TariffTimeTerm.valueOf(tariffTerm.toUpperCase()));
|
||||
|
||||
tariffRepository.save(tariff);
|
||||
}
|
||||
@@ -42,6 +44,15 @@ public class TariffService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<TariffDTO> getTariffByAccountTypeAndTariffTerm(User user, TariffTimeTerm tariffTerm) {
|
||||
String typeAccount = user.canManageCompanySettings() ? "b2b": "b2c";
|
||||
|
||||
return tariffRepository.findByTariffAccountTypeAndTariffTerm(typeAccount, tariffTerm)
|
||||
.stream()
|
||||
.map(this::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<TariffDTO> getTariffByType(String type) {
|
||||
return tariffRepository.findByTariffAccountType(type)
|
||||
.stream()
|
||||
@@ -91,6 +102,8 @@ public class TariffService {
|
||||
}
|
||||
|
||||
private TariffDTO convertToDTO(Tariff tariff) {
|
||||
TariffTimeTerm tariffTerm = tariff.getTariffTerm();
|
||||
|
||||
return new TariffDTO(
|
||||
tariff.getId(),
|
||||
tariff.getType(),
|
||||
@@ -99,7 +112,8 @@ public class TariffService {
|
||||
tariff.getTokens(),
|
||||
tariff.getMaxFilesCount(),
|
||||
tariff.getDiskSize(),
|
||||
tariff.getMaxUsers()
|
||||
tariff.getMaxUsers(),
|
||||
tariffTerm == null ? "" : tariffTerm.name()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,5 +125,6 @@ public class TariffService {
|
||||
tariff.setMaxFilesCount(dto.getMaxFilesCount());
|
||||
tariff.setDiskSize(dto.getDiskSize());
|
||||
tariff.setMaxUsers(dto.getMaxUsers());
|
||||
tariff.setTariffTerm(TariffTimeTerm.valueOf(dto.getTariffTerm().toUpperCase()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package ru.soune.nocopy.service.user;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.entity.user.User;
|
||||
import ru.soune.nocopy.entity.user.UserAdditionalInfo;
|
||||
import ru.soune.nocopy.exception.RegistrationAdditionalInfoNullException;
|
||||
import ru.soune.nocopy.repository.UserAdditionalInfoRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AdditionalInfoService {
|
||||
|
||||
private final UserAdditionalInfoRepository additionalInfoRepository;
|
||||
|
||||
@Transactional
|
||||
public void additionalInfo(String ip, String userAgent, User user) {
|
||||
if (ip == null || userAgent == null) {
|
||||
throw new RegistrationAdditionalInfoNullException("Ip address or user agent is null");
|
||||
}
|
||||
|
||||
UserAdditionalInfo userAdditionalInfo = UserAdditionalInfo.builder()
|
||||
.ipAddress(ip)
|
||||
.userAgent(userAgent)
|
||||
.user(user)
|
||||
.build();
|
||||
|
||||
additionalInfoRepository.save(userAdditionalInfo);
|
||||
}
|
||||
|
||||
public UserAdditionalInfo getAdditionalInfoByUser(Long userId) {
|
||||
return additionalInfoRepository.findById(userId).orElse(null);
|
||||
}
|
||||
|
||||
public List<UserAdditionalInfo> getAllAdditionalInfos() {
|
||||
return additionalInfoRepository.findAll();
|
||||
}
|
||||
|
||||
public void deleteAdditionalInfo(Long userId) {
|
||||
additionalInfoRepository.deleteById(userId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Optional<UserAdditionalInfo> updateAdditionalInfo(Long userId, String ip, String userAgent) {
|
||||
return additionalInfoRepository.findById(userId)
|
||||
.map(info -> {
|
||||
info.setIpAddress(ip);
|
||||
info.setUserAgent(userAgent);
|
||||
return additionalInfoRepository.save(info);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,13 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||
import ru.soune.nocopy.dto.violation.FileViolationSummaryDTO;
|
||||
import ru.soune.nocopy.dto.violation.ViolationInfoDTO;
|
||||
import ru.soune.nocopy.dto.violation.ViolationStatisticsResponse;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.search.GlobalSearchResult;
|
||||
import ru.soune.nocopy.entity.violation.Violation;
|
||||
import ru.soune.nocopy.exception.ViolationNotFoundException;
|
||||
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||
import ru.soune.nocopy.repository.ViolationRepository;
|
||||
import ru.soune.nocopy.service.file.FileEntityService;
|
||||
@@ -51,7 +50,7 @@ public class ViolationService {
|
||||
violation.setPageUrl(imageResult.getPageUrl());
|
||||
violation.setPageTitle(imageResult.getPageTitle());
|
||||
violation.setFileEntity(file);
|
||||
violation.setStatus("CREATED");
|
||||
violation.setStatus(ViolationStatus.NEW.name());
|
||||
|
||||
violationRepository.save(violation);
|
||||
}
|
||||
@@ -94,6 +93,14 @@ public class ViolationService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void changeStatus(ViolationStatus status, Long violationId) {
|
||||
Violation violation = violationRepository.findById(violationId).orElseThrow();
|
||||
violation.setStatus(status.name());
|
||||
|
||||
violationRepository.save(violation);
|
||||
}
|
||||
|
||||
public Page<Violation> getViolationsByFiles(List<FileEntity> files, int page, int size, String sortDirection) {
|
||||
Sort sort = Sort.by(sortDirection.equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "createdDate");
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
@@ -183,22 +190,23 @@ public class ViolationService {
|
||||
violations = violationRepository.findByFileEntityIn(userFiles);
|
||||
}
|
||||
|
||||
Set<String> inProgressStatuses = Set.of(
|
||||
ViolationStatus.SHOWED.name(),
|
||||
ViolationStatus.LEGAL_IN_WORK.name(),
|
||||
ViolationStatus.COMPLAINT_IN_WORK.name(),
|
||||
ViolationStatus.COMPLAINT_AND_LEGAL_IN_WORK.name()
|
||||
);
|
||||
|
||||
long newViolations = violations.stream()
|
||||
.filter(v -> "CREATED".equalsIgnoreCase(v.getStatus()) ||
|
||||
"NEW".equalsIgnoreCase(v.getStatus()))
|
||||
.filter(v -> v.getStatus().equals(ViolationStatus.NEW.name()))
|
||||
.count();
|
||||
|
||||
long inProgressViolations = violations.stream()
|
||||
.filter(v -> "IN_PROGRESS".equalsIgnoreCase(v.getStatus()) ||
|
||||
"PROCESSING".equalsIgnoreCase(v.getStatus()) ||
|
||||
"IN_WORK".equalsIgnoreCase(v.getStatus()))
|
||||
.filter(v -> inProgressStatuses.contains(v.getStatus()))
|
||||
.count();
|
||||
|
||||
long resolvedViolations = violations.stream()
|
||||
.filter(v -> "RESOLVED".equalsIgnoreCase(v.getStatus()) ||
|
||||
"COMPLETED".equalsIgnoreCase(v.getStatus()) ||
|
||||
"SOLVED".equalsIgnoreCase(v.getStatus()) ||
|
||||
"DONE".equalsIgnoreCase(v.getStatus()))
|
||||
.filter(v -> v.getStatus().equals(ViolationStatus.AUTHORIZED_USE.name()))
|
||||
.count();
|
||||
|
||||
return ViolationStatisticsResponse.builder()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.soune.nocopy.service.violation;
|
||||
|
||||
public enum ViolationStatus {
|
||||
NEW , SHOWED, LEGAL_IN_WORK, COMPLAINT_IN_WORK, COMPLAINT_AND_LEGAL_IN_WORK , AUTHORIZED_USE
|
||||
}
|
||||
@@ -102,6 +102,13 @@ yandex:
|
||||
api-key: ${YANDEX_API_KEY:AQVNyaVaUmgUb1GMCtf5zSEqFxy0woXrcMOOB43q}
|
||||
folder-id: ${YANDEX_FOLDER_ID:b1gokpdbm6qfpsou8pcd}
|
||||
search-url: ${YANDEX_SEARCH_URL:https://searchapi.api.cloud.yandex.net/v2/image/search_by_image}
|
||||
cloud:
|
||||
key: "YCAJEmHZcWxzf4oGWvETG3mms"
|
||||
secret-key: "YCPAPZ32XC-LWb8xE0b_xTcBD8NZUCHpSOfu8LUG"
|
||||
region: "ru-central1"
|
||||
s3-endpoint: "https://storage.yandexcloud.net"
|
||||
bucket: "no-copy-storage"
|
||||
|
||||
|
||||
searchapi:
|
||||
api-key: ${SEARCHAPI_API_KEY:5jyYZC8jSaxhZTwjMUhwtAXi}
|
||||
@@ -114,7 +121,7 @@ search:
|
||||
google:
|
||||
enabled: false
|
||||
proxy:
|
||||
enabled: true
|
||||
enabled: false
|
||||
host: "193.46.217.94"
|
||||
port: 3128
|
||||
|
||||
|
||||
Reference in New Issue
Block a user