diff --git a/pom.xml b/pom.xml
index 85cb069..409f5d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,10 @@
17
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
org.springdoc
diff --git a/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java b/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java
index 926e991..54bd25f 100644
--- a/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java
+++ b/src/main/java/com/provedcode/aws/controller/AWSS3BucketController.java
@@ -21,10 +21,11 @@ public class AWSS3BucketController {
@PostSetNewUserImageApiDoc
@PreAuthorize("hasRole('TALENT')")
- @PostMapping("/image/upload")
+ @PostMapping("/talents/{talent-id}/image/upload")
public void setNewUserImage(@RequestParam("file") MultipartFile file,
- Authentication authentication) {
- fileService.setNewUserImage(file, authentication);
+ @PathVariable("talent-id") Long talentId,
+ Authentication authentication) {
+ fileService.setNewUserImage(file, talentId, authentication);
}
@GetAllAWSBucketFilesDevApiDoc
diff --git a/src/main/java/com/provedcode/aws/service/FileService.java b/src/main/java/com/provedcode/aws/service/FileService.java
index 1c1d9e6..bb771c2 100644
--- a/src/main/java/com/provedcode/aws/service/FileService.java
+++ b/src/main/java/com/provedcode/aws/service/FileService.java
@@ -16,7 +16,7 @@ public interface FileService {
List listAllFiles();
- void setNewUserImage(MultipartFile file, Authentication authentication);
+ void setNewUserImage(MultipartFile file, Long talentId, Authentication authentication);
URL generetePresingedUrlFor7Days(String fileFullPath);
}
diff --git a/src/main/java/com/provedcode/aws/service/S3Service.java b/src/main/java/com/provedcode/aws/service/S3Service.java
index a7a4dc9..5c753ef 100644
--- a/src/main/java/com/provedcode/aws/service/S3Service.java
+++ b/src/main/java/com/provedcode/aws/service/S3Service.java
@@ -6,6 +6,7 @@
import com.amazonaws.services.s3.model.*;
import com.amazonaws.util.IOUtils;
import com.provedcode.config.AWSProperties;
+import com.provedcode.talent.model.entity.Talent;
import com.provedcode.talent.repo.TalentRepository;
import com.provedcode.user.model.entity.UserInfo;
import com.provedcode.user.repo.UserInfoRepository;
@@ -75,7 +76,7 @@ public List listAllFiles() {
}
@Override
- public void setNewUserImage(MultipartFile file, Authentication authentication) {
+ public void setNewUserImage(MultipartFile file, Long talentId, Authentication authentication) {
if (file.isEmpty()) {
throw new ResponseStatusException(BAD_REQUEST, "file must be not empty, actual file-size: %s".formatted(file.getSize()));
}
@@ -84,6 +85,11 @@ public void setNewUserImage(MultipartFile file, Authentication authentication) {
}
UserInfo user = userInfoRepository.findByLogin(authentication.getName())
.orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "user with login = {%s} not found".formatted(authentication.getName())));
+ Talent talent = talentRepository.findById(talentId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND, "talent with id = {%s} not found".formatted(talentId)));
+ if (!user.getTalent().equals(talent)) {
+ throw new ResponseStatusException(FORBIDDEN, "You cannot change another talent");
+ }
try {
String fileType = getFileType(file);
diff --git a/src/main/java/com/provedcode/config/EmailConfig.java b/src/main/java/com/provedcode/config/EmailConfig.java
new file mode 100644
index 0000000..d1a0073
--- /dev/null
+++ b/src/main/java/com/provedcode/config/EmailConfig.java
@@ -0,0 +1,15 @@
+package com.provedcode.config;
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+@Configuration
+@AllArgsConstructor
+public class EmailConfig {
+ private Environment env;
+
+ public String getDefaultEmail() {
+ return env.getProperty("EMAIL_USER");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/config/EmailDefaultProps.java b/src/main/java/com/provedcode/config/EmailDefaultProps.java
new file mode 100644
index 0000000..f6adf63
--- /dev/null
+++ b/src/main/java/com/provedcode/config/EmailDefaultProps.java
@@ -0,0 +1,19 @@
+package com.provedcode.config;
+
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+@Validated
+@ConfigurationProperties(prefix = "default-email")
+@Slf4j
+public record EmailDefaultProps(
+ String userDeleted,
+ String userDeletedSubject
+) {
+ @PostConstruct
+ void logging() {
+ log.info("email-default-props = {}", this);
+ }
+}
diff --git a/src/main/java/com/provedcode/config/SecurityConfig.java b/src/main/java/com/provedcode/config/SecurityConfig.java
index 4459d42..8bfb464 100644
--- a/src/main/java/com/provedcode/config/SecurityConfig.java
+++ b/src/main/java/com/provedcode/config/SecurityConfig.java
@@ -9,6 +9,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@@ -59,6 +60,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(antMatcher("/v3/api-docs/**")).permitAll() // for openAPI
.requestMatchers(antMatcher("/swagger-ui/**")).permitAll() // for openAPI
.requestMatchers(antMatcher("/swagger-ui.html")).permitAll() // for openAPI
+ .requestMatchers(antMatcher(HttpMethod.GET, "/api/v5/activate")).permitAll()// for email account recovery
.anyRequest().authenticated()
);
diff --git a/src/main/java/com/provedcode/config/ServerInfoConfig.java b/src/main/java/com/provedcode/config/ServerInfoConfig.java
new file mode 100644
index 0000000..ba483f4
--- /dev/null
+++ b/src/main/java/com/provedcode/config/ServerInfoConfig.java
@@ -0,0 +1,29 @@
+package com.provedcode.config;
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+@AllArgsConstructor
+@Configuration
+public class ServerInfoConfig {
+ private Environment env;
+
+ public String getServerPort() {
+ return env.getProperty("server.port");
+ }
+ public String getIpAddress() {
+ InetAddress ip = null;
+ try {
+ return ip.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ public String getFullServerAddress() {
+ return getIpAddress() + ":" + getServerPort();
+ }
+}
diff --git a/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java b/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java
new file mode 100644
index 0000000..1d59e1d
--- /dev/null
+++ b/src/main/java/com/provedcode/handlers/GlobalControllerAdvice.java
@@ -0,0 +1,33 @@
+package com.provedcode.handlers;
+
+import com.provedcode.user.model.entity.UserInfo;
+import com.provedcode.user.repo.UserInfoRepository;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.server.ResponseStatusException;
+
+import static org.springframework.http.HttpStatus.FORBIDDEN;
+import static org.springframework.http.HttpStatus.NOT_FOUND;
+
+@ControllerAdvice
+@AllArgsConstructor
+@Slf4j
+public class GlobalControllerAdvice {
+ UserInfoRepository userInfoRepository;
+
+ @ModelAttribute
+ public void handleAuthentication(Authentication authentication) {
+ if (authentication != null) {
+ String login = authentication.getName();
+ UserInfo user = userInfoRepository.findByLogin(login)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login {%s} not found".formatted(login)));
+ if (Boolean.TRUE.equals(user.getIsLocked())) {
+ throw new ResponseStatusException(FORBIDDEN, "your account is blocked");
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/provedcode/kudos/controller/KudosController.java b/src/main/java/com/provedcode/kudos/controller/KudosController.java
index f065d39..78fa3e1 100644
--- a/src/main/java/com/provedcode/kudos/controller/KudosController.java
+++ b/src/main/java/com/provedcode/kudos/controller/KudosController.java
@@ -36,7 +36,7 @@ KudosAmount getKudosForSponsor(@PathVariable("sponsor-id") long sponsorId, Authe
}
@GetAmountOfKudosApiDoc
- @PreAuthorize("hasRole('TALENT')")
+ @PreAuthorize("hasAnyRole('TALENT', 'SPONSOR')")
@GetMapping("/proofs/{proof-id}/kudos")
KudosAmountWithSponsor getProofKudos(@PathVariable("proof-id") long proofId, Authentication authentication) {
return kudosService.getProofKudos(proofId, authentication);
diff --git a/src/main/java/com/provedcode/kudos/service/KudosService.java b/src/main/java/com/provedcode/kudos/service/KudosService.java
index 39ae8b5..793441a 100644
--- a/src/main/java/com/provedcode/kudos/service/KudosService.java
+++ b/src/main/java/com/provedcode/kudos/service/KudosService.java
@@ -38,156 +38,185 @@
@AllArgsConstructor
@Transactional
public class KudosService {
- KudosRepository kudosRepository;
- TalentProofRepository talentProofRepository;
- UserInfoRepository userInfoRepository;
- SponsorRepository sponsorRepository;
- TalentRepository talentRepository;
- ProofSkillRepository proofSkillRepository;
- SponsorMapper sponsorMapper;
-
- @Transactional(readOnly = true)
- public KudosAmount getKudosForSponsor(long sponsorId, Authentication authentication) {
- String login = authentication.getName();
- UserInfo userInfo = userInfoRepository.findByLogin(login)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "User with login = %s not found".formatted(login)));
- if (!userInfo.getSponsor().getId().equals(sponsorId)) {
- throw new ResponseStatusException(FORBIDDEN,
- "Only the account owner can view the number of kudos");
- }
- Sponsor sponsor = sponsorRepository.findById(sponsorId).orElseThrow(
- () -> new ResponseStatusException(NOT_FOUND,
- String.format("Sponsor with id = %d not found", sponsorId)));
- return new KudosAmount(sponsor.getAmountKudos());
+ KudosRepository kudosRepository;
+ TalentProofRepository talentProofRepository;
+ UserInfoRepository userInfoRepository;
+ SponsorRepository sponsorRepository;
+ TalentRepository talentRepository;
+ ProofSkillRepository proofSkillRepository;
+ SponsorMapper sponsorMapper;
+
+ @Transactional(readOnly = true)
+ public KudosAmount getKudosForSponsor(long sponsorId, Authentication authentication) {
+ String login = authentication.getName();
+ UserInfo userInfo = userInfoRepository.findByLogin(login)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login = %s not found".formatted(
+ login)));
+ if (!userInfo.getSponsor().getId().equals(sponsorId)) {
+ throw new ResponseStatusException(FORBIDDEN, "Only the account owner can view the number of kudos");
}
-
- @Transactional(readOnly = true)
- public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authentication) {
- String login = authentication.getName();
- UserInfo userInfo = userInfoRepository.findByLogin(login)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "User with login = %s not found".formatted(login)));
- Talent talent = talentRepository.findById(userInfo.getTalent().getId())
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Talent with login = %s not found".formatted(login)));
- TalentProof talentProof = talentProofRepository.findById(proofId)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Proof with id = %s not found".formatted(proofId)));
-
- Long countOfAllKudos = talentProof.getProofSkills()
- .stream().flatMap(proofSkills -> proofSkills.getKudos()
- .stream().map(Kudos::getAmount))
- .reduce(0L, (prev, next) -> prev + next);
-
- if (talent.getId().equals(talentProof.getTalent().getId())) {
- Map> skillsMap = new HashMap<>();
- talentProof.getProofSkills().forEach(proofSkill -> { // I dnk wtf is this piece of shit, but it
- // works.
- String skill = proofSkill.getSkill().getSkill();
- Map kudosFromSponsor = talentProof.getProofSkills().stream()
- .filter(proofSkills -> proofSkills.getSkill().getSkill().equals(skill))
- .flatMap(proofSkills -> proofSkills.getKudos().stream())
- .collect(Collectors.toMap(
- Kudos::getAmount,
- proof -> proof.getSponsor() != null
- ? sponsorMapper.toDto(
- proof.getSponsor())
- : SponsorDTO.builder().build(),
- (prev, next) -> next,
- HashMap::new));
- skillsMap.put(skill, kudosFromSponsor);
- });
- return KudosAmountWithSponsor.builder()
- .allKudosOnProof(countOfAllKudos)
- .kudosFromSponsor(skillsMap)
- .build();
- } else {
- return KudosAmountWithSponsor.builder()
- .allKudosOnProof(countOfAllKudos)
- .kudosFromSponsor(null).build();
- }
+ Sponsor sponsor = sponsorRepository.findById(sponsorId).orElseThrow(
+ () -> new ResponseStatusException(NOT_FOUND,
+ String.format("Sponsor with id = %d not found", sponsorId)));
+ return new KudosAmount(sponsor.getAmountKudos());
+ }
+
+ @Transactional(readOnly = true)
+ public KudosAmountWithSponsor getProofKudos(long proofId, Authentication authentication) {
+ String login = authentication.getName();
+ UserInfo userInfo = userInfoRepository.findByLogin(login)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login = %s not found".formatted(login)));
+ TalentProof talentProof = talentProofRepository.findById(proofId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Proof with id = %s not found".formatted(proofId)));
+
+ Long countOfAllKudos = talentProof.getProofSkills()
+ .stream().flatMap(proofSkills -> proofSkills.getKudos()
+ .stream().map(Kudos::getAmount))
+ .reduce(0L, Long::sum);
+ Map> skillsMap = new HashMap<>();
+
+ if (userInfo.getSponsor() != null && userInfo.getTalent() == null) {
+ Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId())
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login = %s not found".formatted(
+ login)));
+ talentProof.getProofSkills().forEach(proofSkill -> {
+ String skill = proofSkill.getSkill().getSkill();
+ Map kudosFromSponsor = talentProof.getProofSkills().stream()
+ .map(proofSkills -> {
+ proofSkills.setKudos(proofSkills.getKudos().stream()
+ .filter(kudos -> sponsor.equals(kudos.getSponsor())).toList());
+ return proofSkills;
+ })
+ .filter(proofSkills -> proofSkills.getSkill().getSkill().equals(skill))
+ .flatMap(proofSkills -> proofSkills.getKudos().stream())
+ .collect(Collectors.toMap(
+ Kudos::getAmount,
+ proof -> proof.getSponsor() != null
+ ? sponsorMapper.toDto(proof.getSponsor())
+ : SponsorDTO.builder().build(),
+ (prev, next) -> next,
+ HashMap::new));
+ skillsMap.put(skill, kudosFromSponsor);
+ });
+
+ return KudosAmountWithSponsor.builder()
+ .allKudosOnProof(countOfAllKudos)
+ .kudosFromSponsor(skillsMap)
+ .build();
}
- public void addKudosToProof(long proofId, SetAmountKudos amountOfKudoses, Authentication authentication) {
- String login = authentication.getName();
- UserInfo userInfo = userInfoRepository.findByLogin(login)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "User with login = %s not found".formatted(login)));
- Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow(
- () -> new ResponseStatusException(NOT_FOUND,
- "Sponsor with login = %s not found".formatted(login)));
- TalentProof talentProof = talentProofRepository.findById(proofId)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Proof with id = %d not found".formatted(proofId)));
- if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
- throw new ResponseStatusException(FORBIDDEN,
- "Proof that was kudosed does not have the PUBLISHED status");
- long obtainedAmount = amountOfKudoses.amount();
-
- if (sponsor.getAmountKudos() < obtainedAmount) {
- throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has");
- }
- Long modula = obtainedAmount % talentProof.getProofSkills().size();
- if (modula != 0) {
- obtainedAmount -= modula;
- }
- sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount);
-
- Long addKudoses = obtainedAmount / talentProof.getProofSkills().size();
-
- talentProof.getProofSkills().forEach(proofSkill -> {
- Kudos kudos = Kudos.builder()
- .sponsor(sponsor)
- .skill(proofSkill)
- .amount(addKudoses)
- .build();
- proofSkill.getKudos().add(kudosRepository.save(kudos));
- });
+ Talent talent = talentRepository.findById(userInfo.getTalent().getId())
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Talent with login = %s not found".formatted(login)));
+ if (talent.getId().equals(talentProof.getTalent().getId())) {
+ talentProof.getProofSkills().forEach(proofSkill -> { // I dnk wtf is this piece of shit, but it works.
+ String skill = proofSkill.getSkill().getSkill();
+ Map kudosFromSponsor = talentProof.getProofSkills().stream()
+ .filter(proofSkills -> proofSkills.getSkill().getSkill().equals(skill))
+ .flatMap(proofSkills -> proofSkills.getKudos().stream())
+ .collect(Collectors.toMap(
+ Kudos::getAmount,
+ proof -> proof.getSponsor() != null
+ ? sponsorMapper.toDto(proof.getSponsor())
+ : SponsorDTO.builder().build(),
+ (prev, next) -> next,
+ HashMap::new));
+ skillsMap.put(skill, kudosFromSponsor);
+ });
+ return KudosAmountWithSponsor.builder()
+ .allKudosOnProof(countOfAllKudos)
+ .kudosFromSponsor(skillsMap)
+ .build();
+ } else {
+ return KudosAmountWithSponsor.builder()
+ .allKudosOnProof(countOfAllKudos)
+ .kudosFromSponsor(null).build();
}
-
- public void addKudosToSkill(long proofId, long skillId, SetAmountKudos amountOfKudos,
- Authentication authentication) {
- String login = authentication.getName();
- UserInfo userInfo = userInfoRepository.findByLogin(login)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "User with login = %s not found".formatted(
- login)));
- Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow(
- () -> new ResponseStatusException(NOT_FOUND,
- "Sponsor with login = %s not found".formatted(login)));
- TalentProof talentProof = talentProofRepository.findById(proofId)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Proof with id = %d not found".formatted(proofId)));
- if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
- throw new ResponseStatusException(FORBIDDEN,
- "Skill on proof that was kudosed does not have the PUBLISHED status");
- long obtainedAmount = amountOfKudos.amount();
- if (sponsor.getAmountKudos() < obtainedAmount) {
- throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has");
- }
- sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount);
- ProofSkill proofSkill = talentProof.getProofSkills().stream()
- .filter(s -> s.getSkill().getId().equals(skillId))
- .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Skill with id = %d not found".formatted(skillId)));
- kudosRepository.save(Kudos.builder().amount(obtainedAmount).sponsor(sponsor).skill(proofSkill).build());
+ }
+
+ public void addKudosToProof(long proofId, SetAmountKudos amountOfKudoses, Authentication authentication) {
+ String login = authentication.getName();
+ UserInfo userInfo = userInfoRepository.findByLogin(login)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login = %s not found".formatted(login)));
+ Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow(
+ () -> new ResponseStatusException(NOT_FOUND,
+ "Sponsor with login = %s not found".formatted(login)));
+ TalentProof talentProof = talentProofRepository.findById(proofId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Proof with id = %d not found".formatted(proofId)));
+ if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
+ throw new ResponseStatusException(FORBIDDEN,
+ "Proof that was kudosed does not have the PUBLISHED status");
+ long obtainedAmount = amountOfKudoses.amount();
+
+ if (sponsor.getAmountKudos() < obtainedAmount) {
+ throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has");
}
-
- @Transactional(readOnly = true)
- public KudosAmount getSkillKudos(long proofId, long skillId) {
- TalentProof talentProof = talentProofRepository.findById(proofId)
- .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Proof with id = %d not found".formatted(proofId)));
- if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
- throw new ResponseStatusException(FORBIDDEN,
- "The skill from the proof that was referred to does not have a PUBLISHED status");
- ProofSkill proofSkill = talentProof.getProofSkills().stream()
- .filter(s -> s.getSkill().getId().equals(skillId))
- .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
- "Skill with id = %d not found".formatted(skillId)));
- List kudos = kudosRepository.findBySkill(proofSkill);
- long amountOfKudos = kudos.stream().map(Kudos::getAmount).reduce(0L, Long::sum);
- return new KudosAmount(amountOfKudos);
+ Long modula = obtainedAmount % talentProof.getProofSkills().size();
+ if (modula != 0) {
+ obtainedAmount -= modula;
+ }
+ sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount);
+
+ Long addKudoses = obtainedAmount / talentProof.getProofSkills().size();
+
+ talentProof.getProofSkills().forEach(proofSkill -> {
+ Kudos kudos = Kudos.builder()
+ .sponsor(sponsor)
+ .skill(proofSkill)
+ .amount(addKudoses)
+ .build();
+ proofSkill.getKudos().add(kudosRepository.save(kudos));
+ });
+ }
+
+ public void addKudosToSkill(long proofId, long skillId, SetAmountKudos amountOfKudos,
+ Authentication authentication) {
+ String login = authentication.getName();
+ UserInfo userInfo = userInfoRepository.findByLogin(login)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "User with login = %s not found".formatted(
+ login)));
+ Sponsor sponsor = sponsorRepository.findById(userInfo.getSponsor().getId()).orElseThrow(
+ () -> new ResponseStatusException(NOT_FOUND,
+ "Sponsor with login = %s not found".formatted(login)));
+ TalentProof talentProof = talentProofRepository.findById(proofId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Proof with id = %d not found".formatted(proofId)));
+ if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
+ throw new ResponseStatusException(FORBIDDEN,
+ "Skill on proof that was kudosed does not have the PUBLISHED status");
+ long obtainedAmount = amountOfKudos.amount();
+ if (sponsor.getAmountKudos() < obtainedAmount) {
+ throw new ResponseStatusException(FORBIDDEN, "The sponsor cannot give more kudos than he has");
}
+ sponsor.setAmountKudos(sponsor.getAmountKudos() - obtainedAmount);
+ ProofSkill proofSkill = talentProof.getProofSkills().stream()
+ .filter(s -> s.getSkill().getId().equals(skillId))
+ .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Skill with id = %d not found".formatted(skillId)));
+ kudosRepository.save(Kudos.builder().amount(obtainedAmount).sponsor(sponsor).skill(proofSkill).build());
+ }
+
+ @Transactional(readOnly = true)
+ public KudosAmount getSkillKudos(long proofId, long skillId) {
+ TalentProof talentProof = talentProofRepository.findById(proofId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Proof with id = %d not found".formatted(proofId)));
+ if (!talentProof.getStatus().equals(ProofStatus.PUBLISHED))
+ throw new ResponseStatusException(FORBIDDEN,
+ "The skill from the proof that was referred to does not have a PUBLISHED status");
+ ProofSkill proofSkill = talentProof.getProofSkills().stream()
+ .filter(s -> s.getSkill().getId().equals(skillId))
+ .findFirst().orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "Skill with id = %d not found".formatted(skillId)));
+ List kudos = kudosRepository.findBySkill(proofSkill);
+ long amountOfKudos = kudos.stream().map(Kudos::getAmount).reduce(0L, Long::sum);
+ return new KudosAmount(amountOfKudos);
+ }
}
diff --git a/src/main/java/com/provedcode/sponsor/controller/SponsorController.java b/src/main/java/com/provedcode/sponsor/controller/SponsorController.java
index 39c5bad..09aa4d0 100644
--- a/src/main/java/com/provedcode/sponsor/controller/SponsorController.java
+++ b/src/main/java/com/provedcode/sponsor/controller/SponsorController.java
@@ -23,13 +23,13 @@
@RestController
@AllArgsConstructor
@Validated
-@RequestMapping("/api/v3")
+@RequestMapping("/api")
public class SponsorController {
SponsorService sponsorService;
SponsorMapper sponsorMapper;
@GetAllSponsorsApiDoc
- @GetMapping("/sponsors")
+ @GetMapping("/v3/sponsors")
Page getSponsors(@RequestParam(value = "page", defaultValue = "0") @PositiveOrZero Integer page,
@RequestParam(value = "size", defaultValue = "5") @Min(1) @Max(1000) Integer size) {
return sponsorService.getAllSponsors(page, size).map(sponsorMapper::toDto);
@@ -37,14 +37,14 @@ Page getSponsors(@RequestParam(value = "page", defaultValue = "0") @
@GetSponsorApiDoc
@PreAuthorize("hasRole('SPONSOR')")
- @GetMapping("/sponsors/{id}")
+ @GetMapping("/v3/sponsors/{id}")
SponsorDTO getSponsor(@PathVariable("id") long id, Authentication authentication) {
return sponsorMapper.toDto(sponsorService.getSponsorById(id, authentication));
}
@PatchEditSponsorApiDoc
@PreAuthorize("hasRole('SPONSOR')")
- @PatchMapping("/sponsors/{id}")
+ @PatchMapping("/v3/sponsors/{id}")
SponsorDTO editSponsor(@PathVariable("id") long id,
@RequestBody EditSponsor editSponsor,
Authentication authentication) {
@@ -53,8 +53,14 @@ SponsorDTO editSponsor(@PathVariable("id") long id,
@DeleteSponsorApiDoc
@PreAuthorize("hasRole('SPONSOR')")
- @DeleteMapping("/sponsors/{id}")
+ @DeleteMapping("/v3/sponsors/{id}")
void deleteSponsor(@PathVariable("id") long id, Authentication authentication) {
sponsorService.deleteSponsor(id, authentication);
}
+
+ @PreAuthorize("hasRole('SPONSOR')")
+ @DeleteMapping("/v5/sponsors/{sponsor-id}")
+ void deactivateSponsor(@PathVariable("sponsor-id") long sponsorId, Authentication authentication) {
+ sponsorService.deactivateSponsor(sponsorId, authentication);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/sponsor/service/SponsorService.java b/src/main/java/com/provedcode/sponsor/service/SponsorService.java
index 8beaba7..40bff99 100644
--- a/src/main/java/com/provedcode/sponsor/service/SponsorService.java
+++ b/src/main/java/com/provedcode/sponsor/service/SponsorService.java
@@ -1,13 +1,20 @@
package com.provedcode.sponsor.service;
+import com.provedcode.config.EmailDefaultProps;
import com.provedcode.config.PageProperties;
+import com.provedcode.config.ServerInfoConfig;
import com.provedcode.kudos.model.entity.Kudos;
import com.provedcode.sponsor.model.entity.Sponsor;
import com.provedcode.sponsor.model.request.EditSponsor;
import com.provedcode.sponsor.repository.SponsorRepository;
import com.provedcode.sponsor.utill.ValidateSponsorForCompliance;
+import com.provedcode.user.model.entity.DeletedUser;
import com.provedcode.user.model.entity.UserInfo;
+import com.provedcode.user.repo.DeletedUserRepository;
import com.provedcode.user.repo.UserInfoRepository;
+import com.provedcode.user.service.impl.EmailService;
+import com.provedcode.user.util.UsersSchedulerService;
+import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -16,11 +23,13 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
+import java.util.UUID;
-import static org.springframework.http.HttpStatus.BAD_REQUEST;
-import static org.springframework.http.HttpStatus.FORBIDDEN;
+import static org.springframework.http.HttpStatus.*;
@Service
@AllArgsConstructor
@@ -30,6 +39,10 @@ public class SponsorService {
SponsorRepository sponsorRepository;
UserInfoRepository userInfoRepository;
ValidateSponsorForCompliance validateSponsorForCompliance;
+ DeletedUserRepository deletedUserRepository;
+ ServerInfoConfig serverInfoConfig;
+ EmailService emailService;
+ EmailDefaultProps emailDefaultProps;
@Transactional(readOnly = true)
public Page getAllSponsors(Integer page, Integer size) {
@@ -76,17 +89,49 @@ public void deleteSponsor(long id, Authentication authentication) {
validateSponsorForCompliance.userVerification(sponsor, user, id);
Sponsor deletableSponsor = sponsor.get();
+ deleteSponsorByUser(user.get(), deletableSponsor);
+ }
+
+ private void deleteSponsorByUser(UserInfo user, @NotNull Sponsor deletableSponsor) {
List kudosList = deletableSponsor.getKudoses().stream().map(i -> {
i.setSponsor(null);
return i;
}).toList();
deletableSponsor.setKudoses(kudosList);
- userInfoRepository.delete(user.get());
+ userInfoRepository.delete(user);
}
private void checkEditSponsorNull(EditSponsor editSponsor) {
if (editSponsor.firstName() == null && editSponsor.lastName() == null && editSponsor.image() == null &&
- editSponsor.countOfKudos() == null)
+ editSponsor.countOfKudos() == null)
throw new ResponseStatusException(FORBIDDEN, "you did not provide information to make changes");
}
+
+ public void deactivateSponsor(long sponsorId, Authentication authentication) {
+ UserInfo user = userInfoRepository.findByLogin(authentication.getName())
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "user with login = %s not found".formatted(authentication.getName())));
+ Sponsor sponsor = sponsorRepository.findById(sponsorId)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND,
+ "sponsor with id = %s not found".formatted(sponsorId)));
+ if (!sponsor.equals(user.getSponsor())) {
+ throw new ResponseStatusException(FORBIDDEN, "you cannot update/delete another sponsor");
+ }
+ user.setIsLocked(true);
+
+ DeletedUser deletedUser = DeletedUser.builder()
+ .deletedUser(user)
+ .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS))
+ .uuidForActivate(UUID.randomUUID().toString())
+ .build();
+
+ String userActivateAccountLink = serverInfoConfig.getFullServerAddress() +
+ "/api/v5/activate?uuid=" + deletedUser.getUuidForActivate();
+
+ deletedUserRepository.save(deletedUser);
+
+ emailService.sendEmail(user.getLogin(),
+ emailDefaultProps.userDeletedSubject(),
+ emailDefaultProps.userDeleted().formatted(userActivateAccountLink));
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/talent/controller/TalentController.java b/src/main/java/com/provedcode/talent/controller/TalentController.java
index 3c0b319..157cc17 100644
--- a/src/main/java/com/provedcode/talent/controller/TalentController.java
+++ b/src/main/java/com/provedcode/talent/controller/TalentController.java
@@ -97,4 +97,11 @@ StatisticsDTO getStatisticsForTalent(@PathVariable("talent-id") long talentId,
Authentication authentication) {
return talentService.getStatisticsForTalent(talentId, authentication);
}
+
+ @PreAuthorize("hasRole('TALENT')")
+ @DeleteMapping("v5/talents/{talent-id}")
+ void deactivateTalentById(@PathVariable("talent-id") long talentId,
+ Authentication authentication) {
+ talentService.deactivateTalentById(talentId, authentication);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/talent/service/TalentService.java b/src/main/java/com/provedcode/talent/service/TalentService.java
index d89b82d..e0e8e54 100644
--- a/src/main/java/com/provedcode/talent/service/TalentService.java
+++ b/src/main/java/com/provedcode/talent/service/TalentService.java
@@ -1,10 +1,11 @@
package com.provedcode.talent.service;
+import com.provedcode.config.EmailDefaultProps;
import com.provedcode.config.PageProperties;
+import com.provedcode.config.ServerInfoConfig;
import com.provedcode.kudos.model.entity.Kudos;
import com.provedcode.talent.mapper.TalentProofMapper;
import com.provedcode.talent.model.dto.ProofDTO;
-import com.provedcode.talent.model.dto.SkillDTO;
import com.provedcode.talent.model.dto.SkillIdDTO;
import com.provedcode.talent.model.dto.StatisticsDTO;
import com.provedcode.talent.model.entity.*;
@@ -14,9 +15,12 @@
import com.provedcode.talent.repo.TalentRepository;
import com.provedcode.talent.utill.ValidateTalentForCompliance;
import com.provedcode.user.model.dto.SessionInfoDTO;
+import com.provedcode.user.model.entity.DeletedUser;
import com.provedcode.user.model.entity.UserInfo;
import com.provedcode.user.repo.AuthorityRepository;
+import com.provedcode.user.repo.DeletedUserRepository;
import com.provedcode.user.repo.UserInfoRepository;
+import com.provedcode.user.service.impl.EmailService;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.PositiveOrZero;
@@ -29,6 +33,8 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
@@ -47,6 +53,10 @@ public class TalentService {
ValidateTalentForCompliance validateTalentForCompliance;
SkillsRepository skillsRepository;
TalentProofMapper talentProofMapper;
+ DeletedUserRepository deletedUserRepository;
+ ServerInfoConfig serverInfoConfig;
+ EmailService emailService;
+ EmailDefaultProps emailDefaultProps;
@Transactional(readOnly = true)
public Page getTalentsPage(Integer page, Integer size) {
@@ -143,15 +153,34 @@ public SessionInfoDTO deleteTalentById(long id, Authentication authentication) {
validateTalentForCompliance.userVerification(talent, userInfo, id);
UserInfo user = userInfo.get();
- Talent entity = talent.get();
-
- user.getAuthorities().clear();
userInfoRepository.delete(user);
- talentRepository.delete(entity);
return new SessionInfoDTO("deleted", "null");
}
+ public void deactivateTalentById(long id, Authentication authentication) {
+ Optional talent = talentRepository.findById(id);
+ Optional userInfo = userInfoRepository.findByLogin(authentication.getName());
+
+ validateTalentForCompliance.userVerification(talent, userInfo, id);
+
+ UserInfo user = userInfo.get();
+ user.setIsLocked(true);
+ DeletedUser deletedUser = DeletedUser.builder()
+ .deletedUser(user)
+ .timeToDelete(Instant.now().plus(3, ChronoUnit.DAYS))
+ .uuidForActivate(UUID.randomUUID().toString())
+ .build();
+ deletedUserRepository.save(deletedUser);
+ userInfoRepository.save(user);
+
+ String userActivateAccountLink = serverInfoConfig.getFullServerAddress() + "/api/v5/activate?uuid=" + deletedUser.getUuidForActivate();
+
+ emailService.sendEmail(user.getLogin(),
+ emailDefaultProps.userDeletedSubject(),
+ emailDefaultProps.userDeleted().formatted(userActivateAccountLink));
+ }
+
private void checkEditTalentNull(EditTalent editTalent) {
if (editTalent.firstName() == null && editTalent.lastName() == null && editTalent.image() == null &&
editTalent.specialization() == null && editTalent.additionalInfo() == null && editTalent.bio() == null &&
diff --git a/src/main/java/com/provedcode/user/controller/AuthenticationController.java b/src/main/java/com/provedcode/user/controller/AuthenticationController.java
index b30ee13..e25f35b 100644
--- a/src/main/java/com/provedcode/user/controller/AuthenticationController.java
+++ b/src/main/java/com/provedcode/user/controller/AuthenticationController.java
@@ -1,5 +1,6 @@
package com.provedcode.user.controller;
+import com.provedcode.talent.service.TalentService;
import com.provedcode.user.model.dto.SponsorRegistrationDTO;
import com.provedcode.user.model.dto.TalentRegistrationDTO;
import com.provedcode.user.model.dto.UserInfoDTO;
@@ -40,4 +41,9 @@ UserInfoDTO register(@RequestBody @Valid TalentRegistrationDTO user) {
UserInfoDTO register(@RequestBody @Valid SponsorRegistrationDTO user) {
return authenticationService.register(user);
}
+
+ @GetMapping("/v5/activate")
+ void activateAccount(@RequestParam("uuid") String uuid) {
+ authenticationService.activateAccount(uuid);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/user/model/entity/DeletedUser.java b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java
new file mode 100644
index 0000000..105f920
--- /dev/null
+++ b/src/main/java/com/provedcode/user/model/entity/DeletedUser.java
@@ -0,0 +1,25 @@
+package com.provedcode.user.model.entity;
+
+import jakarta.persistence.*;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.Instant;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@Slf4j
+@Builder
+@Entity
+public class DeletedUser {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private Instant timeToDelete;
+ @OneToOne
+ @JoinColumn(name = "user_id", referencedColumnName = "id")
+ private UserInfo deletedUser;
+ private String uuidForActivate;
+}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/user/model/entity/UserInfo.java b/src/main/java/com/provedcode/user/model/entity/UserInfo.java
index 19dc0da..7250e83 100644
--- a/src/main/java/com/provedcode/user/model/entity/UserInfo.java
+++ b/src/main/java/com/provedcode/user/model/entity/UserInfo.java
@@ -55,4 +55,7 @@ public class UserInfo {
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_authorities", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "authority_id"))
private Set authorities = new LinkedHashSet<>();
+
+ @Column(columnDefinition = "FALSE")
+ private Boolean isLocked;
}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java
new file mode 100644
index 0000000..de3478a
--- /dev/null
+++ b/src/main/java/com/provedcode/user/repo/DeletedUserRepository.java
@@ -0,0 +1,15 @@
+package com.provedcode.user.repo;
+
+import com.provedcode.user.model.entity.DeletedUser;
+import com.provedcode.user.model.entity.UserInfo;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.Optional;
+
+public interface DeletedUserRepository extends JpaRepository {
+ @Query("select d from DeletedUser d where d.uuidForActivate = ?1")
+ Optional findByUUID(String uuidForActivate);
+ Optional findByUuidForActivate(String uuidForActivate);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/provedcode/user/service/AuthenticationService.java b/src/main/java/com/provedcode/user/service/AuthenticationService.java
index 57b7bcd..277af5d 100644
--- a/src/main/java/com/provedcode/user/service/AuthenticationService.java
+++ b/src/main/java/com/provedcode/user/service/AuthenticationService.java
@@ -9,6 +9,10 @@
public interface AuthenticationService {
UserInfoDTO login(String name, Collection extends GrantedAuthority> authorities);
+
UserInfoDTO register(TalentRegistrationDTO user);
+
UserInfoDTO register(SponsorRegistrationDTO user);
+
+ void activateAccount(String uuid);
}
diff --git a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java
index dbcce3e..2adc01f 100644
--- a/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java
+++ b/src/main/java/com/provedcode/user/service/impl/AuthenticationServiceImpl.java
@@ -9,13 +9,14 @@
import com.provedcode.user.model.dto.TalentRegistrationDTO;
import com.provedcode.user.model.dto.UserInfoDTO;
import com.provedcode.user.model.entity.Authority;
+import com.provedcode.user.model.entity.DeletedUser;
import com.provedcode.user.model.entity.UserInfo;
import com.provedcode.user.repo.AuthorityRepository;
+import com.provedcode.user.repo.DeletedUserRepository;
import com.provedcode.user.repo.UserInfoRepository;
import com.provedcode.user.service.AuthenticationService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.catalina.User;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -47,6 +48,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
SponsorRepository sponsorRepository;
AuthorityRepository authorityRepository;
PasswordEncoder passwordEncoder;
+ DeletedUserRepository deletedUserRepository;
@Transactional(readOnly = true)
public UserInfoDTO login(String name, Collection extends GrantedAuthority> authorities) {
@@ -80,6 +82,7 @@ public UserInfoDTO register(TalentRegistrationDTO user) {
.login(user.login())
.password(passwordEncoder.encode(user.password()))
.authorities(Set.of(authorityRepository.findByAuthority(Role.TALENT).orElseThrow()))
+ .isLocked(false)
.build();
userInfoRepository.save(userInfo);
@@ -111,6 +114,7 @@ public UserInfoDTO register(SponsorRegistrationDTO user) {
.password(passwordEncoder.encode(user.password()))
.authorities(
Set.of(authorityRepository.findByAuthority(Role.SPONSOR).orElseThrow()))
+ .isLocked(false)
.build();
userInfoRepository.save(userInfo);
@@ -123,6 +127,15 @@ public UserInfoDTO register(SponsorRegistrationDTO user) {
return new UserInfoDTO(generateJWTToken(sponsor.getId(), userLogin, userAuthorities).getTokenValue());
}
+ public void activateAccount(String uuid) {
+ DeletedUser deletedUser = deletedUserRepository.findByUUID(uuid)
+ .orElseThrow(() -> new ResponseStatusException(NOT_FOUND));
+ UserInfo user = deletedUser.getDeletedUser();
+ user.setIsLocked(false);
+ deletedUserRepository.deleteById(deletedUser.getId());
+ userInfoRepository.save(user);
+ }
+
private Jwt generateJWTToken(Long id, String login, Collection extends GrantedAuthority> authorities) {
log.info("=== POST /login === auth.login = {}", login);
log.info("=== POST /login === auth = {}", authorities);
diff --git a/src/main/java/com/provedcode/user/service/impl/EmailService.java b/src/main/java/com/provedcode/user/service/impl/EmailService.java
new file mode 100644
index 0000000..c497427
--- /dev/null
+++ b/src/main/java/com/provedcode/user/service/impl/EmailService.java
@@ -0,0 +1,25 @@
+package com.provedcode.user.service.impl;
+
+import com.provedcode.config.EmailConfig;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+@Slf4j
+public class EmailService {
+ private JavaMailSender javaMailSender;
+ private EmailConfig emailConfig;
+
+ public void sendEmail(String to, String subject, String text) {
+ SimpleMailMessage message = new SimpleMailMessage();
+ message.setFrom(emailConfig.getDefaultEmail());
+ message.setTo(to);
+ message.setSubject(subject);
+ message.setText(text);
+ javaMailSender.send(message);
+ }
+}
diff --git a/src/main/java/com/provedcode/user/util/UsersSchedulerService.java b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java
new file mode 100644
index 0000000..63285dd
--- /dev/null
+++ b/src/main/java/com/provedcode/user/util/UsersSchedulerService.java
@@ -0,0 +1,64 @@
+package com.provedcode.user.util;
+
+import com.provedcode.user.repo.DeletedUserRepository;
+import com.provedcode.user.repo.UserInfoRepository;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+
+@Service
+@EnableScheduling
+@AllArgsConstructor
+@Transactional
+@Slf4j
+public class UsersSchedulerService {
+ UserInfoRepository userInfoRepository;
+ DeletedUserRepository deletedUsersRepository;
+
+ @Scheduled(timeUnit = TimeUnit.HOURS, fixedRate = 1)
+ void deleteUser() {
+ deletedUsersRepository.findAll()
+ .forEach(userToDelete -> {
+ if (userToDelete.getTimeToDelete().isAfter(Instant.now())) {
+ log.info("deleting user: id = {}", userToDelete.getDeletedUser().getId());
+ deletedUsersRepository.delete(userToDelete);
+ userInfoRepository.delete(userToDelete.getDeletedUser());
+ }
+ });
+ }
+
+// @Async
+// public void scheduleDeletingMethod(String uuid) {
+// try {
+// TimeUnit.MINUTES.sleep(3);
+// log.info("scheduleDeletingMethod = {}", uuid);
+// deletedUsersRepository.findByUUID(uuid)
+// .ifPresent(deleted -> {
+// deletedUsersRepository.delete(deleted);
+// deleteUser(deleted.getDeletedUser());
+// });
+// } catch (InterruptedException e) {
+// Thread.currentThread().interrupt();
+// }
+// }
+//
+// private void deleteUser(UserInfo user) {
+// if (user.getSponsor() != null) {
+// List kudosList = user.getSponsor().getKudoses().stream().map(i -> {
+// i.setSponsor(null);
+// return i;
+// }).toList();
+// user.getSponsor().setKudoses(kudosList);
+// userInfoRepository.delete(user);
+// }
+// if (user.getTalent() != null) {
+// userInfoRepository.delete(user);
+// }
+// }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index f91aa56..d3d2982 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -38,4 +38,14 @@ page-config.default-page-num=0
page-config.default-page-size=5
page-config.default-sort-by=created
##
-spring.liquibase.enabled=true
\ No newline at end of file
+spring.liquibase.enabled=true
+##
+spring.mail.host=smtp.gmail.com
+spring.mail.port=587
+spring.mail.username=${EMAIL_USER}
+spring.mail.password=${EMAIL_PASSWORD}
+spring.mail.properties.mail.smtp.auth=true
+spring.mail.properties.mail.smtp.starttls.enable=true
+# Default email response
+default-email.user-deleted=Your account has been suspended, it will be deleted after 24 hours, click on the link to restore it:\n%s\nBest wishes from the test team.
+default-email.user-deleted-subject=Your account has been suspended, TestTeam.
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/changeset/V5/data-V5.sql b/src/main/resources/db/changelog/changeset/V5/data-V5.sql
index b897501..2d63889 100644
--- a/src/main/resources/db/changelog/changeset/V5/data-V5.sql
+++ b/src/main/resources/db/changelog/changeset/V5/data-V5.sql
@@ -5,33 +5,7 @@ insert into authorities (id, authority)
values (1, 'TALENT');
insert into authorities (id, authority)
values (2, 'SPONSOR');
--- Skill
-insert into skills (id, skill)
-values (1, 'Java Core');
-insert into skills (id, skill)
-values (2, 'Spring Core');
-insert into skills (id, skill)
-values (3, 'Spring boot');
-insert into skills (id, skill)
-values (4, 'H2 Database');
-insert into skills (id, skill)
-values (5, 'Spring Security');
-insert into skills (id, skill)
-values (6, 'REST API');
-insert into skills (id, skill)
-values (7, 'Git');
-insert into skills (id, skill)
-values (8, 'Docker');
-insert into skills (id, skill)
-values (9, 'Jira');
-insert into skills (id, skill)
-values (10, 'JavaScript Core');
-insert into skills (id, skill)
-values (11, 'React');
-insert into skills (id, skill)
-values (12, 'Node.js');
-insert into skills (id, skill)
-values (13, 'Angular');
+
-- Talent
-- Serhii Soloviov
insert into talents (first_name, last_name, specialization, image)
diff --git a/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql
new file mode 100644
index 0000000..7f19179
--- /dev/null
+++ b/src/main/resources/db/changelog/changeset/V5/deleted-user-schema.sql
@@ -0,0 +1,14 @@
+--liquibase formatted sql
+--changeset Maslyna:5
+
+CREATE TABLE deleted_user
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
+ time_to_delete TIMESTAMP WITHOUT TIME ZONE,
+ user_id BIGINT,
+ uuid_for_activate VARCHAR(255),
+ CONSTRAINT pk_deleteduser PRIMARY KEY (id)
+);
+
+ALTER TABLE deleted_user
+ ADD CONSTRAINT FK_DELETEDUSER_ON_USER FOREIGN KEY (user_id) REFERENCES users_info (id);
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/changeset/V5/skills-data.sql b/src/main/resources/db/changelog/changeset/V5/skills-data.sql
new file mode 100644
index 0000000..cd7848c
--- /dev/null
+++ b/src/main/resources/db/changelog/changeset/V5/skills-data.sql
@@ -0,0 +1,223 @@
+--liquibase formatted sql
+--changeset Maslyna:0
+
+-- Skill
+insert into skills (id, skill)
+values (1, 'Java Core');
+insert into skills (id, skill)
+values (2, 'Spring Core');
+insert into skills (id, skill)
+values (3, 'Spring boot');
+insert into skills (id, skill)
+values (4, 'H2 Database');
+insert into skills (id, skill)
+values (5, 'Spring Security');
+insert into skills (id, skill)
+values (6, 'REST API');
+insert into skills (id, skill)
+values (7, 'Git');
+insert into skills (id, skill)
+values (8, 'Docker');
+insert into skills (id, skill)
+values (9, 'Jira');
+insert into skills (id, skill)
+values (10, 'JavaScript Core');
+insert into skills (id, skill)
+values (11, 'React');
+insert into skills (id, skill)
+values (12, 'Node.js');
+insert into skills (id, skill)
+values (13, 'Angular');
+--
+INSERT INTO skills (id, skill)
+VALUES (14, 'Java Development Kit (JDK)'),
+ (15, 'Servlet API'),
+ (16, 'JavaServer Pages (JSP)'),
+ (17, 'JavaServer Faces (JSF)'),
+ (18, 'Spring Framework'),
+ (19, 'Hibernate'),
+ (20, 'Apache Struts'),
+ (21, 'MySQL'),
+ (22, 'PostgreSQL'),
+ (23, 'Oracle'),
+ (24, 'SQL'),
+ (25, 'Eclipse'),
+ (26, 'IntelliJ IDEA'),
+ (27, 'NetBeans'),
+ (28, 'Apache Tomcat'),
+ (29, 'Jetty'),
+ (30, 'JBoss'),
+ (31, 'Git'),
+ (32, 'SVN'),
+ (33, 'Mercurial'),
+ (34, 'Algorithms and data structures'),
+ (35, 'SOAP'),
+ (36, 'REST'),
+ (37, 'JSON'),
+ (38, 'XML'),
+ (39, 'Multithreading and parallelism');
+INSERT INTO skills (id, skill)
+VALUES (40, 'Django'),
+ (41, 'Flask'),
+ (42, 'Pyramid'),
+ (43, 'SQLite'),
+ (44, 'NoSQL'),
+ (45, 'MongoDB'),
+ (46, 'Redis'),
+ (47, 'BeautifulSoup'),
+ (48, 'Scrapy'),
+ (49, 'asyncio'),
+ (50, 'Twisted'),
+ (51, 'NumPy'),
+ (52, 'SciPy'),
+ (53, 'Pandas'),
+ (54, 'scikit-learn'),
+ (55, 'TensorFlow'),
+ (56, 'PyTorch'),
+ (57, 'JavaScript'),
+ (58, 'HTML'),
+ (59, 'CSS'),
+ (60, 'DOM'),
+ (61, 'React'),
+ (62, 'Angular'),
+ (63, 'Vue.js'),
+ (64, 'jQuery'),
+ (65, 'AJAX'),
+ (66, 'XSS (Cross-Site Scripting)'),
+ (67, 'CSRF (Cross-Site Request Forgery)'),
+ (68, 'Swift'),
+ (69, 'UIKit'),
+ (70, 'AppKit'),
+ (71, 'Core Data'),
+ (72, 'Core Animation'),
+ (73, 'Core Location'),
+ (74, 'Core Graphics'),
+ (75, 'Storyboards'),
+ (76, 'Interface Builder'),
+ (77, 'Auto Layout'),
+ (78, 'CoreData'),
+ (79, 'Asynchronous programming'),
+ (80, 'MVC (Model-View-Controller)'),
+ (81, 'MVVM (Model-View-ViewModel)'),
+ (82, 'Clean Architecture'),
+ (83, 'Kotlin'),
+ (84, 'Android SDK'),
+ (85, 'Firebase'),
+ (86, 'Jenkins'),
+ (87, 'Docker'),
+ (88, 'UNIX'),
+ (89, 'Linux OS'),
+ (90, 'JPA'),
+ (91, 'JUnit'),
+ (92, 'OOP'),
+ (93, 'PHP'),
+ (94, 'Microservices'),
+ (95, 'Azure Service Bus'),
+ (96, 'Typescript'),
+ (97, 'WinForms'),
+ (98, 'Firebase Analytics'),
+ (99, 'Appsflyer'),
+ (100, 'Amplitude');
+INSERT INTO skills (id, skill)
+VALUES (101, 'Marketing'),
+ (102, 'Sales'),
+ (103, 'Content Writing'),
+ (104, 'Graphic Design'),
+ (105, 'Video Editing'),
+ (106, 'Project Management'),
+ (107, 'Leadership'),
+ (108, 'Negotiation'),
+ (109, 'Public Speaking'),
+ (110, 'Time Management'),
+ (111, 'Critical Thinking'),
+ (112, 'Problem Solving'),
+ (113, 'Customer Service'),
+ (114, 'Data Analysis'),
+ (115, 'Financial Planning'),
+ (116, 'Event Planning'),
+ (117, 'Foreign Languages'),
+ (118, 'Photography'),
+ (119, 'Cooking'),
+ (120, 'Yoga'),
+ (121, 'Creative Writing');
+INSERT INTO skills (id, skill)
+VALUES (122, 'Leadership Development'),
+ (123, 'Emotional Intelligence'),
+ (124, 'Teamwork'),
+ (125, 'Conflict Resolution'),
+ (126, 'Decision Making'),
+ (127, 'Presentation Skills'),
+ (128, 'Networking'),
+ (129, 'Research Skills'),
+ (130, 'Problem-solving'),
+ (131, 'Analytical Thinking'),
+ (132, 'Creativity'),
+ (133, 'Attention to Detail'),
+ (134, 'Organizational Skills'),
+ (135, 'Time Management'),
+ (136, 'Adaptability'),
+ (137, 'Stress Management'),
+ (138, 'Interpersonal Skills'),
+ (139, 'Communication Skills'),
+ (140, 'Customer Relationship Management'),
+ (141, 'Sales Techniques'),
+ (142, 'Market Research'),
+ (143, 'Digital Marketing'),
+ (144, 'Social Media Marketing'),
+ (145, 'Search Engine Optimization'),
+ (146, 'Content Marketing'),
+ (147, 'Data Analytics'),
+ (148, 'Financial Analysis'),
+ (149, 'Budgeting'),
+ (150, 'Project Planning'),
+ (151, 'Quality Assurance'),
+ (152, 'Risk Management'),
+ (153, 'Supply Chain Management'),
+ (154, 'Logistics'),
+ (155, 'Business Strategy'),
+ (156, 'Entrepreneurship'),
+ (157, 'Innovation'),
+ (158, 'Customer Service'),
+ (159, 'Hospitality'),
+ (160, 'Event Management');
+INSERT INTO skills (id, skill)
+VALUES (161, 'Teaching'),
+ (162, 'Tutoring'),
+ (163, 'Curriculum Development'),
+ (164, 'Instructional Design'),
+ (165, 'Classroom Management'),
+ (166, 'Educational Technology'),
+ (167, 'Language Teaching'),
+ (168, 'Music'),
+ (169, 'Art'),
+ (170, 'Sports'),
+ (171, 'Fitness'),
+ (172, 'Nutrition'),
+ (173, 'Counseling'),
+ (174, 'Life Coaching'),
+ (175, 'Meditation'),
+ (176, 'Mindfulness'),
+ (177, 'Research'),
+ (178, 'Data Entry'),
+ (179, 'Virtual Assistance'),
+ (180, 'Project Coordination'),
+ (181, 'Event Coordination'),
+ (182, 'Office Management'),
+ (183, 'Translation'),
+ (184, 'Transcription'),
+ (185, 'Proofreading'),
+ (186, 'Editing'),
+ (187, 'Copywriting'),
+ (188, 'Content Creation'),
+ (189, 'Social Media Management'),
+ (190, 'Graphic Design'),
+ (191, 'Video Editing'),
+ (192, 'Photography'),
+ (193, 'Web Development'),
+ (194, 'Mobile App Development'),
+ (195, 'Game Development'),
+ (196, 'UI/UX Design'),
+ (197, 'Product Management'),
+ (198, 'Agile Methodology'),
+ (199, 'Scrum'),
+ (200, 'Lean Manufacturing');
\ No newline at end of file
diff --git a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql
index 97b1109..3028fe7 100644
--- a/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql
+++ b/src/main/resources/db/changelog/changeset/V5/user-info-schema-V5.sql
@@ -8,6 +8,7 @@ CREATE TABLE users_info (
sponsor_id BIGINT REFERENCES sponsors,
login VARCHAR(100) NOT NULL,
password VARCHAR(255) NOT NULL,
+ is_locked BOOLEAN,
PRIMARY KEY (id)
);
CREATE TABLE authorities (
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 77eb9b2..49cbdae 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -23,7 +23,12 @@ databaseChangeLog:
file: db/changelog/changeset/V5/user-info-schema-V5.sql
- include:
file: db/changelog/changeset/V5/data-V5.sql
+ - include:
+ file: db/changelog/changeset/V5/skills-data.sql
- include:
file: db/changelog/changeset/V5/dataSkill-V5.sql
- include:
file: db/changelog/changeset/V5/dataKudos-V5.sql
+ - include:
+ file: db/changelog/changeset/V5/deleted-user-schema.sql
+