@@ -26,7 +26,9 @@ public class HandlerConfig {
|
|||||||
ReferralHandler referralHandler,
|
ReferralHandler referralHandler,
|
||||||
ResetPasswordHandler resetPasswordHandler,
|
ResetPasswordHandler resetPasswordHandler,
|
||||||
DaDataHandler daDataHandler,
|
DaDataHandler daDataHandler,
|
||||||
PaymentHandler paymentHandler
|
PaymentHandler paymentHandler,
|
||||||
|
GetViolationsHandler getViolationsHandler,
|
||||||
|
MonitoringHandler monitoringHandler
|
||||||
) {
|
) {
|
||||||
Map<Integer, RequestHandler> map = new HashMap<>();
|
Map<Integer, RequestHandler> map = new HashMap<>();
|
||||||
map.put(20001, login);
|
map.put(20001, login);
|
||||||
@@ -44,6 +46,8 @@ public class HandlerConfig {
|
|||||||
map.put(30003, referralHandler);
|
map.put(30003, referralHandler);
|
||||||
map.put(30004, daDataHandler);
|
map.put(30004, daDataHandler);
|
||||||
map.put(30005, paymentHandler);
|
map.put(30005, paymentHandler);
|
||||||
|
map.put(30006, getViolationsHandler);
|
||||||
|
map.put(30007, monitoringHandler);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ public class YandexSearchResponse {
|
|||||||
@JsonProperty("images")
|
@JsonProperty("images")
|
||||||
private List<ImageResult> images;
|
private List<ImageResult> images;
|
||||||
|
|
||||||
// @JsonProperty("images")
|
|
||||||
// public void setImages(List<ImageResult> images) {
|
|
||||||
// this.images = images;
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public static class ImageResult {
|
public static class ImageResult {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package ru.soune.nocopy.dto.monitoring;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GetViolationsRequest {
|
||||||
|
|
||||||
|
@JsonProperty("file_id")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@JsonProperty("page")
|
||||||
|
private Integer page;
|
||||||
|
|
||||||
|
@JsonProperty("page_size")
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
@JsonProperty("mark_as_read")
|
||||||
|
private boolean markAsRead;
|
||||||
|
|
||||||
|
@JsonProperty("auth_token")
|
||||||
|
private String authToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ru.soune.nocopy.dto.monitoring;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response for status monitoring
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class MonitoringStatusResponse {
|
||||||
|
|
||||||
|
@JsonProperty("file_id")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@JsonProperty("file_name")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@JsonProperty("monitoring_type")
|
||||||
|
private MonitoringType monitoringType;
|
||||||
|
|
||||||
|
@JsonProperty("next_run")
|
||||||
|
private LocalDateTime nextRun;
|
||||||
|
|
||||||
|
@JsonProperty("last_run")
|
||||||
|
private LocalDateTime lastRun;
|
||||||
|
|
||||||
|
@JsonProperty("last_run_status")
|
||||||
|
private String lastRunStatus;
|
||||||
|
|
||||||
|
@JsonProperty("is_active")
|
||||||
|
private boolean isActive;
|
||||||
|
|
||||||
|
@JsonProperty("new_violations_count")
|
||||||
|
private long newViolationsCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package ru.soune.nocopy.dto.monitoring;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
/**
|
||||||
|
Set monitoring request
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SetMonitoringRequest {
|
||||||
|
|
||||||
|
@JsonProperty("file_id")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@JsonProperty("monitoring_type")
|
||||||
|
private MonitoringType monitoringType;
|
||||||
|
|
||||||
|
@JsonProperty("auth_token")
|
||||||
|
private String authToken;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package ru.soune.nocopy.dto.monitoring;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.SearchEngine;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class ViolationResponse {
|
||||||
|
|
||||||
|
@JsonProperty("id")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@JsonProperty("file_id")
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@JsonProperty("file_name")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@JsonProperty("found_url")
|
||||||
|
private String foundUrl;
|
||||||
|
|
||||||
|
@JsonProperty("page_url")
|
||||||
|
private String pageUrl;
|
||||||
|
|
||||||
|
@JsonProperty("page_title")
|
||||||
|
private String pageTitle;
|
||||||
|
|
||||||
|
@JsonProperty("host")
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
@JsonProperty("search_engine")
|
||||||
|
private SearchEngine searchEngine;
|
||||||
|
|
||||||
|
@JsonProperty("found_at")
|
||||||
|
private LocalDateTime foundAt;
|
||||||
|
|
||||||
|
@JsonProperty("is_new")
|
||||||
|
private boolean isNew;
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package ru.soune.nocopy.entity.monitoring;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@Table(name = "file_monitoring")
|
||||||
|
public class FileMonitoringEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "file_id", nullable = false)
|
||||||
|
private FileEntity file;
|
||||||
|
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "monitoring_type", nullable = false)
|
||||||
|
private MonitoringType monitoringType;
|
||||||
|
|
||||||
|
@Column(name = "next_run", nullable = false)
|
||||||
|
private LocalDateTime nextRun;
|
||||||
|
|
||||||
|
@Column(name = "last_run")
|
||||||
|
private LocalDateTime lastRun;
|
||||||
|
|
||||||
|
@Column(name = "last_run_status")
|
||||||
|
private String lastRunStatus;
|
||||||
|
|
||||||
|
@Column(name = "is_active", nullable = false)
|
||||||
|
private boolean isActive = true;
|
||||||
|
|
||||||
|
@Column(name = "created_at", nullable = false)
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void prePersist() {
|
||||||
|
this.createdAt = LocalDateTime.now();
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreUpdate
|
||||||
|
public void preUpdate() {
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package ru.soune.nocopy.entity.monitoring;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.*;
|
||||||
|
import ru.soune.nocopy.entity.file.FileEntity;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Entity
|
||||||
|
@Table(name = "found_violations",
|
||||||
|
indexes = {
|
||||||
|
@Index(name = "idx_file_id", columnList = "file_id"),
|
||||||
|
@Index(name = "idx_found_at", columnList = "found_at")
|
||||||
|
})
|
||||||
|
public class FoundViolationEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumn(name = "file_id", nullable = false)
|
||||||
|
private FileEntity file;
|
||||||
|
|
||||||
|
@Column(name = "user_id", nullable = false)
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@Column(name = "file_id_copy", nullable = false)
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
@Column(name = "file_name_copy")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Column(name = "found_url", nullable = false, length = 2048)
|
||||||
|
private String foundUrl;
|
||||||
|
|
||||||
|
@Column(name = "page_url", length = 2048)
|
||||||
|
private String pageUrl;
|
||||||
|
|
||||||
|
@Column(name = "page_title")
|
||||||
|
private String pageTitle;
|
||||||
|
|
||||||
|
@Column(name = "host")
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
@Column(name = "image_width")
|
||||||
|
private Integer imageWidth;
|
||||||
|
|
||||||
|
@Column(name = "image_height")
|
||||||
|
private Integer imageHeight;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "search_engine", nullable = false)
|
||||||
|
private SearchEngine searchEngine;
|
||||||
|
|
||||||
|
@Column(name = "search_session_id")
|
||||||
|
private String searchSessionId;
|
||||||
|
|
||||||
|
@Column(name = "found_at", nullable = false)
|
||||||
|
private LocalDateTime foundAt;
|
||||||
|
|
||||||
|
@Column(name = "is_new", nullable = false)
|
||||||
|
private boolean isNew = true;
|
||||||
|
|
||||||
|
@Column(name = "is_notified", nullable = false)
|
||||||
|
private boolean isNotified = false;
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void prePersist() {
|
||||||
|
this.foundAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.soune.nocopy.entity.monitoring;
|
||||||
|
|
||||||
|
|
||||||
|
public enum MonitoringType {
|
||||||
|
MONITORING_DAILY, MONITORING_WEEKLY, MONITORING_MONTHLY, NONE
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package ru.soune.nocopy.entity.monitoring;
|
||||||
|
|
||||||
|
|
||||||
|
public enum SearchEngine {
|
||||||
|
YANDEX,
|
||||||
|
GOOGLE,
|
||||||
|
BOTH
|
||||||
|
}
|
||||||
@@ -2,5 +2,6 @@ package ru.soune.nocopy.entity.tarif;
|
|||||||
|
|
||||||
|
|
||||||
public enum TariffType {
|
public enum TariffType {
|
||||||
START, BASIC, PRO, ENTERPRISE, DEMO, BUISNES, CORPORAT, STUDIO, FREE, TOKEN_100, TOKEN_500, TOKEN_1000, TOKEN_5000;
|
START, BASIC, PRO, ENTERPRISE, DEMO, BUISNES, CORPORAT, STUDIO, FREE, TOKEN_100, TOKEN_500, TOKEN_1000, TOKEN_5000,
|
||||||
|
MONITORING_DAILY, MONITORING_WEEKLY, MONITORING_MONTHLY;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ru.soune.nocopy.exception;
|
||||||
|
|
||||||
|
public class UserNotHavePermission extends RuntimeException {
|
||||||
|
public UserNotHavePermission(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
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.monitoring.GetViolationsRequest;
|
||||||
|
import ru.soune.nocopy.dto.monitoring.ViolationResponse;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FoundViolationEntity;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
import ru.soune.nocopy.repository.FoundViolationRepository;
|
||||||
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetViolationsHandler implements RequestHandler {
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private final FoundViolationRepository violationRepository;
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
|
GetViolationsRequest violationsRequest = objectMapper.convertValue(
|
||||||
|
request.getMessageBody(), GetViolationsRequest.class);
|
||||||
|
|
||||||
|
String authToken = violationsRequest.getAuthToken();
|
||||||
|
|
||||||
|
//TODO add return for null token
|
||||||
|
if (authToken == null) {
|
||||||
|
return new BaseResponse(0, 0, "", new Object());
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = authService.getAuthToken(authToken).getUser();
|
||||||
|
List<FoundViolationEntity> violations;
|
||||||
|
|
||||||
|
if (violationsRequest.getFileId() != null) {
|
||||||
|
violations = violationRepository.findByFileIdOrderByFoundAtDesc(
|
||||||
|
violationsRequest.getFileId());
|
||||||
|
} else {
|
||||||
|
violations = violationRepository.findByUserIdOrderByFoundAtDesc(user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (violationsRequest.isMarkAsRead()) {
|
||||||
|
violations.stream()
|
||||||
|
.filter(FoundViolationEntity::isNew)
|
||||||
|
.forEach(v -> v.setNew(false));
|
||||||
|
violationRepository.saveAll(violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ViolationResponse> response = violations.stream()
|
||||||
|
.map(this::mapToResponse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return new BaseResponse(
|
||||||
|
request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
"Violations retrieved",
|
||||||
|
response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViolationResponse mapToResponse(FoundViolationEntity entity) {
|
||||||
|
return ViolationResponse.builder()
|
||||||
|
.id(entity.getId())
|
||||||
|
.fileId(entity.getFileId())
|
||||||
|
.fileName(entity.getFileName())
|
||||||
|
.foundUrl(entity.getFoundUrl())
|
||||||
|
.pageUrl(entity.getPageUrl())
|
||||||
|
.pageTitle(entity.getPageTitle())
|
||||||
|
.host(entity.getHost())
|
||||||
|
.searchEngine(entity.getSearchEngine())
|
||||||
|
.foundAt(entity.getFoundAt())
|
||||||
|
.isNew(entity.isNew())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
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.monitoring.MonitoringStatusResponse;
|
||||||
|
import ru.soune.nocopy.dto.monitoring.SetMonitoringRequest;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
import ru.soune.nocopy.entity.tarif.TariffType;
|
||||||
|
import ru.soune.nocopy.entity.user.AuthToken;
|
||||||
|
import ru.soune.nocopy.entity.user.User;
|
||||||
|
import ru.soune.nocopy.repository.TariffRepository;
|
||||||
|
import ru.soune.nocopy.service.monitoring.FileMonitoringService;
|
||||||
|
import ru.soune.nocopy.service.register.AuthService;
|
||||||
|
import ru.soune.nocopy.service.tariff.TariffService;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MonitoringHandler implements RequestHandler {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private final FileMonitoringService monitoringService;
|
||||||
|
|
||||||
|
private final AuthService authService;
|
||||||
|
|
||||||
|
private final TariffService tariffService;
|
||||||
|
|
||||||
|
private final TariffRepository tariffRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseResponse handle(BaseRequest request) throws Exception {
|
||||||
|
SetMonitoringRequest setRequest = objectMapper.convertValue(
|
||||||
|
request.getMessageBody(), SetMonitoringRequest.class);
|
||||||
|
|
||||||
|
String authToken = setRequest.getAuthToken();
|
||||||
|
AuthToken userAuthToken = authService.getAuthToken(authToken);
|
||||||
|
|
||||||
|
if (authToken == null || userAuthToken == null) {
|
||||||
|
return new BaseResponse(30007, MessageCode.AUTH_TOKEN_NOT_FOUND.getCode(),
|
||||||
|
MessageCode.AUTH_TOKEN_NOT_FOUND.getDescription(),
|
||||||
|
Map.of("token", authToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = userAuthToken.getUser();
|
||||||
|
Long userId = user.getId();
|
||||||
|
MonitoringType monitoringType = setRequest.getMonitoringType();
|
||||||
|
|
||||||
|
monitoringService.setMonitoring(userId, setRequest.getFileId(), monitoringType);
|
||||||
|
|
||||||
|
MonitoringStatusResponse status = monitoringService.getMonitoringStatus(
|
||||||
|
setRequest.getFileId(), userId);
|
||||||
|
|
||||||
|
return new BaseResponse(
|
||||||
|
request.getMsgId(),
|
||||||
|
MessageCode.SUCCESS.getCode(),
|
||||||
|
"Monitoring settings updated",
|
||||||
|
status);
|
||||||
|
}
|
||||||
|
//TODO check tokens for apply
|
||||||
|
private boolean checkTokensForMonitoring(User user, MonitoringType monitoringType) {
|
||||||
|
tariffService.getTariffByType(TariffType.valueOf(monitoringType.name()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ru.soune.nocopy.repository;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface FileMonitoringRepository extends JpaRepository<FileMonitoringEntity, String> {
|
||||||
|
|
||||||
|
Optional<FileMonitoringEntity> findByFileId(String fileId);
|
||||||
|
|
||||||
|
List<FileMonitoringEntity> findByUserIdAndIsActiveTrue(Long userId);
|
||||||
|
|
||||||
|
@Query("SELECT fm FROM FileMonitoringEntity fm WHERE fm.isActive = true AND fm.monitoringType != 'NONE' AND fm.nextRun <= :now")
|
||||||
|
List<FileMonitoringEntity> findReadyForRun(@Param("now") LocalDateTime now);
|
||||||
|
|
||||||
|
List<FileMonitoringEntity> findByMonitoringTypeAndIsActiveTrue(MonitoringType type);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package ru.soune.nocopy.repository;
|
||||||
|
|
||||||
|
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 org.springframework.stereotype.Repository;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FoundViolationEntity;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface FoundViolationRepository extends JpaRepository<FoundViolationEntity, String> {
|
||||||
|
|
||||||
|
List<FoundViolationEntity> findByFileIdOrderByFoundAtDesc(String fileId);
|
||||||
|
|
||||||
|
List<FoundViolationEntity> findByUserIdOrderByFoundAtDesc(Long userId);
|
||||||
|
|
||||||
|
@Query("SELECT fv FROM FoundViolationEntity fv WHERE fv.userId = :userId AND fv.isNew = true")
|
||||||
|
List<FoundViolationEntity> findNewViolationsByUser(@Param("userId") Long userId);
|
||||||
|
|
||||||
|
@Query("SELECT fv FROM FoundViolationEntity fv WHERE fv.file.id = :fileId AND fv.foundUrl = :foundUrl")
|
||||||
|
List<FoundViolationEntity> findDuplicate(@Param("fileId") String fileId, @Param("foundUrl") String foundUrl);
|
||||||
|
|
||||||
|
List<FoundViolationEntity> findByFoundAtBetween(LocalDateTime start, LocalDateTime end);
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(fv) FROM FoundViolationEntity fv WHERE fv.userId = :userId AND fv.isNew = true")
|
||||||
|
long countNewByUserId(@Param("userId") Long userId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package ru.soune.nocopy.scheduler;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
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 java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MonitoringScheduler {
|
||||||
|
|
||||||
|
private final FileMonitoringRepository monitoringRepository;
|
||||||
|
|
||||||
|
private final MonitoringSearchService monitoringSearchService;
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 1 2 * * *", zone = "Europe/Moscow")
|
||||||
|
@Transactional
|
||||||
|
public void processScheduledMonitoring() {
|
||||||
|
log.info("Starting scheduled monitoring check at 02:01 MSK");
|
||||||
|
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
List<FileMonitoringEntity> readyForRun = monitoringRepository.findReadyForRun(now);
|
||||||
|
|
||||||
|
log.info("Found {} files ready for monitoring", readyForRun.size());
|
||||||
|
|
||||||
|
//TODO check tokens for apply
|
||||||
|
for (FileMonitoringEntity monitoring : readyForRun) {
|
||||||
|
try {
|
||||||
|
monitoringSearchService.processFileSearch(monitoring);
|
||||||
|
log.info("Successfully queued search for file: {}", monitoring.getFile().getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to process monitoring for file: {}", monitoring.getFile().getId(), e);
|
||||||
|
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
||||||
|
monitoringRepository.save(monitoring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package ru.soune.nocopy.service.monitoring;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.soune.nocopy.dto.BaseResponse;
|
||||||
|
import ru.soune.nocopy.dto.monitoring.MonitoringStatusResponse;
|
||||||
|
import ru.soune.nocopy.entity.file.*;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||||
|
import ru.soune.nocopy.exception.UserNotHavePermission;
|
||||||
|
import ru.soune.nocopy.repository.FileEntityRepository;
|
||||||
|
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||||
|
import ru.soune.nocopy.repository.FoundViolationRepository;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.TemporalAdjusters;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FileMonitoringService {
|
||||||
|
|
||||||
|
private final FileMonitoringRepository monitoringRepository;
|
||||||
|
private final FileEntityRepository fileRepository;
|
||||||
|
private final FoundViolationRepository violationRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public FileMonitoringEntity setMonitoring(Long userId, String fileId, MonitoringType type) throws FileNotFoundException {
|
||||||
|
FileEntity file = fileRepository.findById(fileId)
|
||||||
|
.orElseThrow(() -> new FileNotFoundException(fileId));
|
||||||
|
|
||||||
|
if (!file.getUserId().equals(userId)) {
|
||||||
|
throw new UserNotHavePermission("User not Have permission for file");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileMonitoringEntity monitoring = monitoringRepository.findByFileId(fileId)
|
||||||
|
.orElse(FileMonitoringEntity.builder()
|
||||||
|
.file(file)
|
||||||
|
.userId(userId)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
monitoring.setMonitoringType(type);
|
||||||
|
monitoring.setActive(type != MonitoringType.NONE);
|
||||||
|
monitoring.setNextRun(calculateNextRun(type, LocalDateTime.now()));
|
||||||
|
monitoring.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
if (monitoring.getCreatedAt() == null) {
|
||||||
|
monitoring.setCreatedAt(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
return monitoringRepository.save(monitoring);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void updateNextRun(FileMonitoringEntity monitoring) {
|
||||||
|
if (monitoring.isActive() && monitoring.getMonitoringType() != MonitoringType.NONE) {
|
||||||
|
LocalDateTime nextRun = calculateNextRun(monitoring.getMonitoringType(), LocalDateTime.now());
|
||||||
|
monitoring.setNextRun(nextRun);
|
||||||
|
monitoringRepository.save(monitoring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private LocalDateTime calculateNextRun(MonitoringType type, LocalDateTime from) {
|
||||||
|
LocalDateTime nextRun = from.withHour(2).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
|
||||||
|
if (from.isAfter(nextRun)) {
|
||||||
|
nextRun = nextRun.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (type) {
|
||||||
|
case MONITORING_DAILY -> nextRun;
|
||||||
|
case MONITORING_WEEKLY -> nextRun.with(TemporalAdjusters.nextOrSame(java.time.DayOfWeek.MONDAY));
|
||||||
|
case MONITORING_MONTHLY -> nextRun.with(TemporalAdjusters.firstDayOfNextMonth());
|
||||||
|
case NONE -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public MonitoringStatusResponse getMonitoringStatus(String fileId, Long userId) {
|
||||||
|
FileMonitoringEntity monitoring = monitoringRepository.findByFileId(fileId).orElse(null);
|
||||||
|
FileEntity file = fileRepository.findById(fileId).orElseThrow();
|
||||||
|
|
||||||
|
long newViolations = violationRepository.countNewByUserId(userId);
|
||||||
|
|
||||||
|
return MonitoringStatusResponse.builder()
|
||||||
|
.fileId(fileId)
|
||||||
|
.fileName(file.getOriginalFileName())
|
||||||
|
.monitoringType(monitoring != null ? monitoring.getMonitoringType() : MonitoringType.NONE)
|
||||||
|
.nextRun(monitoring != null ? monitoring.getNextRun() : null)
|
||||||
|
.lastRun(monitoring != null ? monitoring.getLastRun() : null)
|
||||||
|
.lastRunStatus(monitoring != null ? monitoring.getLastRunStatus() : null)
|
||||||
|
.isActive(monitoring != null && monitoring.isActive())
|
||||||
|
.newViolationsCount(newViolations)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package ru.soune.nocopy.service.monitoring;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.soune.nocopy.dto.file.YandexSearchResponse;
|
||||||
|
import ru.soune.nocopy.entity.file.*;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.FoundViolationEntity;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.MonitoringType;
|
||||||
|
import ru.soune.nocopy.entity.monitoring.SearchEngine;
|
||||||
|
import ru.soune.nocopy.repository.FileMonitoringRepository;
|
||||||
|
import ru.soune.nocopy.repository.FoundViolationRepository;
|
||||||
|
import ru.soune.nocopy.service.search.SearchImageService;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MonitoringSearchService {
|
||||||
|
|
||||||
|
private final SearchImageService searchImageService;
|
||||||
|
private final FoundViolationRepository violationRepository;
|
||||||
|
private final FileMonitoringRepository monitoringRepository;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void processFileSearch(FileMonitoringEntity monitoring) {
|
||||||
|
String searchSessionId = UUID.randomUUID().toString();
|
||||||
|
log.info("Starting search session: {} for file: {}", searchSessionId, monitoring.getFile().getId());
|
||||||
|
|
||||||
|
monitoring.setLastRun(LocalDateTime.now());
|
||||||
|
monitoring.setLastRunStatus("IN_PROGRESS");
|
||||||
|
monitoringRepository.save(monitoring);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String yandexResponse = searchImageService.searchReverseByPublicUrl(
|
||||||
|
monitoring.getFile(), "yandex_reverse_image", "visual_matches");
|
||||||
|
|
||||||
|
List<YandexSearchResponse.ImageResult> yandexImages =
|
||||||
|
searchImageService.getAllImagesWithoutPagination(yandexResponse, "visual_matches");
|
||||||
|
|
||||||
|
int savedCount = saveFoundImages(monitoring, yandexImages, SearchEngine.YANDEX, searchSessionId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String googleResponse = searchImageService.searchReverseByPublicUrl(
|
||||||
|
monitoring.getFile(), "google_lens", "exact_matches");
|
||||||
|
|
||||||
|
List<YandexSearchResponse.ImageResult> googleImages =
|
||||||
|
searchImageService.getAllImagesWithoutPagination(googleResponse, "exact_matches");
|
||||||
|
|
||||||
|
savedCount += saveFoundImages(monitoring, googleImages, SearchEngine.GOOGLE, searchSessionId);
|
||||||
|
|
||||||
|
} catch (TimeoutException | IOException e) {
|
||||||
|
log.warn("Google search failed for session: {}", searchSessionId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
monitoring.setLastRunStatus("SUCCESS - Found " + savedCount + " violations");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Search failed for session: {}", searchSessionId, e);
|
||||||
|
monitoring.setLastRunStatus("ERROR: " + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
updateNextRun(monitoring);
|
||||||
|
monitoringRepository.save(monitoring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int saveFoundImages(FileMonitoringEntity monitoring,
|
||||||
|
List<YandexSearchResponse.ImageResult> images,
|
||||||
|
SearchEngine engine,
|
||||||
|
String sessionId) {
|
||||||
|
int savedCount = 0;
|
||||||
|
|
||||||
|
for (YandexSearchResponse.ImageResult image : images) {
|
||||||
|
List<FoundViolationEntity> existing = violationRepository.findDuplicate(
|
||||||
|
monitoring.getFile().getId(), image.getUrl());
|
||||||
|
|
||||||
|
if (existing.isEmpty()) {
|
||||||
|
FoundViolationEntity violation = FoundViolationEntity.builder()
|
||||||
|
.file(monitoring.getFile())
|
||||||
|
.fileId(monitoring.getFile().getId())
|
||||||
|
.fileName(monitoring.getFile().getOriginalFileName())
|
||||||
|
.userId(monitoring.getUserId())
|
||||||
|
.foundUrl(image.getUrl())
|
||||||
|
.pageUrl(image.getPageUrl())
|
||||||
|
.pageTitle(image.getPageTitle())
|
||||||
|
.host(image.getHost())
|
||||||
|
.imageWidth(image.getWidth())
|
||||||
|
.imageHeight(image.getHeight())
|
||||||
|
.searchEngine(engine)
|
||||||
|
.searchSessionId(sessionId)
|
||||||
|
.isNew(true)
|
||||||
|
.isNotified(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
violationRepository.save(violation);
|
||||||
|
savedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return savedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateNextRun(FileMonitoringEntity monitoring) {
|
||||||
|
LocalDateTime nextRun = calculateNextRun(monitoring.getMonitoringType());
|
||||||
|
monitoring.setNextRun(nextRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDateTime calculateNextRun(MonitoringType type) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
LocalDateTime nextRun = now.withHour(2).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
|
||||||
|
if (now.isAfter(nextRun)) {
|
||||||
|
nextRun = nextRun.plusDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (type) {
|
||||||
|
case MONITORING_DAILY -> nextRun;
|
||||||
|
case MONITORING_WEEKLY -> nextRun.plusWeeks(1);
|
||||||
|
case MONITORING_MONTHLY -> nextRun.plusMonths(1);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -160,6 +160,10 @@ public class AuthService {
|
|||||||
return authTokenRepository.save(genereateAuthToken(user));
|
return authTokenRepository.save(genereateAuthToken(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthToken getAuthToken(String token) {
|
||||||
|
return authTokenRepository.findByToken(token).get();
|
||||||
|
}
|
||||||
|
|
||||||
private AuthToken genereateAuthToken(User user) {
|
private AuthToken genereateAuthToken(User user) {
|
||||||
AuthToken authToken = new AuthToken();
|
AuthToken authToken = new AuthToken();
|
||||||
authToken.setToken(generateAuthToken());
|
authToken.setToken(generateAuthToken());
|
||||||
|
|||||||
@@ -220,6 +220,37 @@ public class TariffInitializationService {
|
|||||||
tariff.setMaxUsers(0L);
|
tariff.setMaxUsers(0L);
|
||||||
tariff.setTariffAccountType("token");
|
tariff.setTariffAccountType("token");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case MONITORING_DAILY:
|
||||||
|
tariff.setName("monitoring_daily");
|
||||||
|
tariff.setPrice(0);
|
||||||
|
tariff.setTokens(10);
|
||||||
|
tariff.setMaxFilesCount(0);
|
||||||
|
tariff.setDiskSize(0L);
|
||||||
|
tariff.setMaxUsers(0L);
|
||||||
|
tariff.setTariffAccountType("monitoring");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MONITORING_WEEKLY:
|
||||||
|
tariff.setName("monitoring_weekly");
|
||||||
|
tariff.setPrice(0);
|
||||||
|
tariff.setTokens(25);
|
||||||
|
tariff.setMaxFilesCount(0);
|
||||||
|
tariff.setDiskSize(0L);
|
||||||
|
tariff.setMaxUsers(0L);
|
||||||
|
tariff.setTariffAccountType("monitoring");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MONITORING_MONTHLY:
|
||||||
|
tariff.setName("monitoring_monthly");
|
||||||
|
tariff.setPrice(0);
|
||||||
|
tariff.setTokens(50);
|
||||||
|
tariff.setMaxFilesCount(0);
|
||||||
|
tariff.setDiskSize(0L);
|
||||||
|
tariff.setMaxUsers(0L);
|
||||||
|
tariff.setTariffAccountType("monitoring");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tariffRepository.save(tariff);
|
tariffRepository.save(tariff);
|
||||||
|
|||||||
Reference in New Issue
Block a user