diff --git a/back-end/pom.xml b/back-end/pom.xml
index cb737f5..924a828 100644
--- a/back-end/pom.xml
+++ b/back-end/pom.xml
@@ -22,10 +22,10 @@
3.6.0
8
8
+ 0.11.5
-
org.springframework.boot
@@ -47,6 +47,17 @@
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
org.springframework.boot
spring-boot-starter-web
@@ -74,16 +85,17 @@
liquibase-core
+
+ org.springdoc
+ springdoc-openapi-ui
+ ${org.springdoc.version}
+
+
org.projectlombok
lombok
true
-
- org.springdoc
- springdoc-openapi-ui
- 1.6.4
-
org.springframework.boot
spring-boot-starter-test
@@ -95,77 +107,77 @@
test
-
-
-
-
+
org.mapstruct
mapstruct
1.4.2.Final
-
- io.swagger.core.v3
- swagger-models
- 2.1.11
-
-
- io.swagger.core.v3
- swagger-annotations
- 2.1.11
-
-
org.postgresql
postgresql
runtime
+
+
+
org.springframework.boot
spring-boot-starter-log4j2
-
- org.springframework.boot
- spring-boot-starter-security
- compile
-
-
- org.springframework.boot
- spring-boot-starter-logging
-
-
+ com.lmax
+ disruptor
+ 3.4.0
+
+
+
io.jsonwebtoken
jjwt-api
- 0.11.5
+ ${io.jsonwebtoken.version}
io.jsonwebtoken
jjwt-impl
- 0.11.5
+ ${io.jsonwebtoken.version}
+ runtime
io.jsonwebtoken
jjwt-jackson
- 0.11.5
+ ${io.jsonwebtoken.version}
+ runtime
+
+
+
- org.liquibase
- liquibase-maven-plugin
- 4.5.0
+ org.passay
+ passay
+ 1.3.1
+
+
+
+
+
+ net.sf.jasperreports
+ jasperreports
+ 6.20.0
+
+
-
+
-
org.springframework.boot
spring-boot-maven-plugin
@@ -174,10 +186,10 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ ${org.apache.maven.plugins.version}
- 1.8
- 1.8
+ ${java.version}
+ ${java.version}
org.projectlombok
@@ -189,13 +201,10 @@
mapstruct-processor
${org.mapstruct.version}
-
-
-
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/config/SecurityAuditorAware.java b/back-end/src/main/java/com/example/demo/config/SecurityAuditorAware.java
new file mode 100644
index 0000000..3b56961
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/config/SecurityAuditorAware.java
@@ -0,0 +1,29 @@
+package com.example.demo.config;
+
+import lombok.extern.log4j.Log4j2;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Optional;
+
+@Configuration
+@Log4j2
+public class SecurityAuditorAware implements AuditorAware {
+ @Override
+ public Optional getCurrentAuditor() {
+ Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+
+ if (principal instanceof User) {
+ User user = (User) principal;
+ log.debug("Current Auditor {}", user.getUsername());
+ return Optional.of(user.getUsername());
+ } else {
+ log.debug("Current Auditor {}", principal);
+ return Optional.of(principal);
+ }
+
+
+ }
+}
diff --git a/back-end/src/main/java/com/example/demo/config/SecurityConfig.java b/back-end/src/main/java/com/example/demo/config/SecurityConfig.java
deleted file mode 100644
index 545002d..0000000
--- a/back-end/src/main/java/com/example/demo/config/SecurityConfig.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.example.demo.config;
-
-import com.example.demo.security.RestAuthenticationEntryPoint;
-import com.example.demo.security.SecurityUserService;
-import com.example.demo.security.TokenAuthenticationFilter;
-import lombok.AllArgsConstructor;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.config.http.SessionCreationPolicy;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-
-@Configuration
-@EnableWebSecurity
-@AllArgsConstructor
-public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- private final SecurityUserService securityUserService;
-
- private final TokenAuthenticationFilter tokenAuthenticationFilter;
-
- private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
-
-
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
-
-
- http.csrf().disable().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
- http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
- http.authorizeRequests().antMatchers("/auth/**").permitAll();
- http.authorizeRequests().antMatchers("/v3/api-docs/*", "/v3/api-docs", "/swagger-ui.html", "/swagger-ui/*").permitAll();
- http.authorizeRequests().anyRequest().authenticated();
- http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
- }
-
-
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(securityUserService).passwordEncoder(passwordEncoder());
- }
-
- @Bean
- public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
- return authenticationConfiguration.getAuthenticationManager();
- }
-
-
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
-}
diff --git a/back-end/src/main/java/com/example/demo/config/WebSecurityConfig.java b/back-end/src/main/java/com/example/demo/config/WebSecurityConfig.java
new file mode 100644
index 0000000..7cb452f
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/config/WebSecurityConfig.java
@@ -0,0 +1,109 @@
+package com.example.demo.config;
+import com.example.demo.security.CustomUserDetailsService;
+import com.example.demo.security.RestAuthenticationEntryPoint;
+import com.example.demo.security.TokenAuthenticationFilter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import java.util.List;
+
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+@RequiredArgsConstructor
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ private final CustomUserDetailsService customUserDetailsService;
+
+ private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
+
+ @Value("${app.allowed-origins}")
+ private List allowedOrigins;
+
+ @Override
+ public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
+ authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
+ }
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public TokenAuthenticationFilter authenticationJwtTokenFilter() {
+ return new TokenAuthenticationFilter();
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+
+ http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable().exceptionHandling()
+ .authenticationEntryPoint(restAuthenticationEntryPoint);
+ http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+ http.authorizeRequests().antMatchers("/auth/**").permitAll()
+
+ .antMatchers(HttpMethod.POST,"/interview").hasRole("EMPLOYEE")
+ .antMatchers(HttpMethod.GET,"/interview/*").hasRole("EMPLOYEE")
+ .antMatchers(HttpMethod.PUT,"/interview/*").hasRole("EMPLOYEE")
+ .antMatchers(HttpMethod.DELETE,"/interview/*").hasRole("EMPLOYEE")
+ .antMatchers(HttpMethod.GET,"/user/current/vacancy").hasRole("EMPLOYEE")
+
+
+ // All Permissions
+
+ .antMatchers("/employee/**", "/vacancy/**","/role/**").hasRole("ADMIN")
+ .antMatchers("/vacancy/**").hasRole("EMPLOYEE");
+
+
+
+ //for swagger
+ http.authorizeRequests().antMatchers("/v3/api-docs/*", "/v3/api-docs", "/swagger-ui.html", "/swagger-ui/*").permitAll();
+ http.authorizeRequests().anyRequest().authenticated();
+ http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
+ }
+
+
+ @Override
+ public void configure(WebSecurity web) throws Exception {
+
+ }
+
+ @Bean
+ CorsConfigurationSource corsConfigurationSource() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
+ corsConfiguration.setAllowedOrigins(allowedOrigins);
+ corsConfiguration.setAllowCredentials(true);
+ corsConfiguration.addAllowedMethod(HttpMethod.PUT);
+ corsConfiguration.addAllowedMethod(HttpMethod.DELETE);
+ corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS);
+ corsConfiguration.addAllowedMethod(HttpMethod.PATCH);
+ source.registerCorsConfiguration("/**", corsConfiguration);
+ return source;
+ }
+
+
+}
diff --git a/back-end/src/main/java/com/example/demo/entity/Application.java b/back-end/src/main/java/com/example/demo/entity/Application.java
index df302b3..e092260 100644
--- a/back-end/src/main/java/com/example/demo/entity/Application.java
+++ b/back-end/src/main/java/com/example/demo/entity/Application.java
@@ -9,16 +9,16 @@
@Entity
public class Application extends JPAEntity {
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "VACANCY_ID")
private Vacancy vacancy;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "CANDIDATE_ID")
private Candidate candidate;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "QUESTION_ID")
private Question question;
diff --git a/back-end/src/main/java/com/example/demo/entity/Candidate.java b/back-end/src/main/java/com/example/demo/entity/Candidate.java
index 2b6783f..ab5e03f 100644
--- a/back-end/src/main/java/com/example/demo/entity/Candidate.java
+++ b/back-end/src/main/java/com/example/demo/entity/Candidate.java
@@ -25,7 +25,7 @@ public class Candidate extends JPAEntity {
@Column
private Integer documentId;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "position_id")
private Position position;
diff --git a/back-end/src/main/java/com/example/demo/entity/Document.java b/back-end/src/main/java/com/example/demo/entity/Document.java
index 72c84f5..80bde2f 100644
--- a/back-end/src/main/java/com/example/demo/entity/Document.java
+++ b/back-end/src/main/java/com/example/demo/entity/Document.java
@@ -3,11 +3,15 @@
import com.example.demo.entity.common.JPAEntity;
import com.example.demo.support.DocumentType;
import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
import javax.persistence.*;
@Data
@Entity
+@EqualsAndHashCode(of = "id", callSuper = false)
+@ToString(of = "id", callSuper = false)
public class Document extends JPAEntity {
@Column
diff --git a/back-end/src/main/java/com/example/demo/entity/FollowUp.java b/back-end/src/main/java/com/example/demo/entity/FollowUp.java
new file mode 100644
index 0000000..bfc4ef5
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/entity/FollowUp.java
@@ -0,0 +1,28 @@
+package com.example.demo.entity;
+
+import com.example.demo.entity.common.JPAEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.persistence.*;
+import java.sql.Date;
+import java.time.LocalDateTime;
+
+@Data
+@Entity
+@EqualsAndHashCode(of = "id", callSuper = false)
+@ToString(of = "id", callSuper = false)
+@Table(name = "followUp")
+public class FollowUp extends JPAEntity {
+ @Column(name = "date")
+ private Date date;
+
+ @Column(name = "description")
+ private String description;
+
+ @ManyToOne
+ @JoinColumn(name = "interviewId")
+ private Interview interview;
+
+}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/entity/Vacancy.java b/back-end/src/main/java/com/example/demo/entity/Vacancy.java
index 420a8f5..bbda503 100644
--- a/back-end/src/main/java/com/example/demo/entity/Vacancy.java
+++ b/back-end/src/main/java/com/example/demo/entity/Vacancy.java
@@ -23,9 +23,22 @@ public class Vacancy extends JPAEntity {
private LocalDateTime expireDate;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "position_id")
private Position position;
+ @Column(name = "vacancies")
+ private String vacancies;
+
+ @Column(name = "post_date", updatable = false)
+ private LocalDateTime postDate;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "post_by")
+ private User postBy;
+
+
+
+
}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/repository/FollowUpRepository.java b/back-end/src/main/java/com/example/demo/repository/FollowUpRepository.java
new file mode 100644
index 0000000..59a6396
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/repository/FollowUpRepository.java
@@ -0,0 +1,28 @@
+package com.example.demo.repository;
+
+
+import com.example.demo.entity.FollowUp;
+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 org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface FollowUpRepository extends JpaRepository {
+
+
+
+
+ @Query(value = "SELECT f FROM FollowUp f JOIN FETCH f.interview WHERE f.id = :id")
+ Optional findById(@Param("id") Integer id);
+
+ @Query(value = " SELECT f FROM FollowUp f JOIN FETCH f.interview WHERE f.interview.id= :interviewId",
+ countQuery = "SELECT COUNT(f) FROM FollowUp f WHERE f.interview.id= :interviewId")
+ Page findAllByInterview(@Param(value = "interviewId") Integer interviewId, Pageable pageable);
+
+}
diff --git a/back-end/src/main/java/com/example/demo/repository/InterviewRepository.java b/back-end/src/main/java/com/example/demo/repository/InterviewRepository.java
index 434b4e9..24884a3 100644
--- a/back-end/src/main/java/com/example/demo/repository/InterviewRepository.java
+++ b/back-end/src/main/java/com/example/demo/repository/InterviewRepository.java
@@ -2,6 +2,9 @@
import com.example.demo.entity.Interview;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+@Repository
public interface InterviewRepository extends JpaRepository {
+
}
diff --git a/back-end/src/main/java/com/example/demo/repository/PositionRepository.java b/back-end/src/main/java/com/example/demo/repository/PositionRepository.java
index 6dcc8e7..886c8cc 100644
--- a/back-end/src/main/java/com/example/demo/repository/PositionRepository.java
+++ b/back-end/src/main/java/com/example/demo/repository/PositionRepository.java
@@ -2,7 +2,8 @@
import com.example.demo.entity.Position;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
-
+@Repository
public interface PositionRepository extends JpaRepository {
}
diff --git a/back-end/src/main/java/com/example/demo/repository/TopicLevelRepository.java b/back-end/src/main/java/com/example/demo/repository/TopicLevelRepository.java
index 50ced10..96df4df 100644
--- a/back-end/src/main/java/com/example/demo/repository/TopicLevelRepository.java
+++ b/back-end/src/main/java/com/example/demo/repository/TopicLevelRepository.java
@@ -3,7 +3,8 @@
import com.example.demo.entity.TopicLevel;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
-
+@Repository
public interface TopicLevelRepository extends JpaRepository {
}
diff --git a/back-end/src/main/java/com/example/demo/repository/UserRepository.java b/back-end/src/main/java/com/example/demo/repository/UserRepository.java
index c49dee3..4162127 100644
--- a/back-end/src/main/java/com/example/demo/repository/UserRepository.java
+++ b/back-end/src/main/java/com/example/demo/repository/UserRepository.java
@@ -3,12 +3,17 @@
import com.example.demo.entity.User;
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 java.util.Optional;
-//@Repository
+@Repository
public interface UserRepository extends JpaRepository {
@Query("SELECT u FROM User u WHERE u.username = :username")
Optional findUserByUsername(String username);
+
+ @Query(value = "Select u FROM User u LEFT JOIN FETCH u.roles WHERE u.username= :userName ")
+ Optional findRolesByUserName(@Param("userName") String userName);
}
diff --git a/back-end/src/main/java/com/example/demo/repository/VacancyRepository.java b/back-end/src/main/java/com/example/demo/repository/VacancyRepository.java
index e1a0684..8d985d6 100644
--- a/back-end/src/main/java/com/example/demo/repository/VacancyRepository.java
+++ b/back-end/src/main/java/com/example/demo/repository/VacancyRepository.java
@@ -1,8 +1,19 @@
package com.example.demo.repository;
import com.example.demo.entity.Vacancy;
+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 org.springframework.stereotype.Repository;
@Repository
-public interface VacancyRepository extends JpaRepository{}
+public interface VacancyRepository extends JpaRepository{
+
+
+
+ @Query(value = "SELECT v from Vacancy v JOIN FETCH v.postBy as A WHERE A.username= :userName",
+ countQuery = "SELECT count(v) FROM Vacancy v WHERE v.postBy.username= :userName ")
+ Page getVacationsByUser(@Param("userName") String userName, Pageable pageable);
+}
diff --git a/back-end/src/main/java/com/example/demo/rest/controller/FollowUpController.java b/back-end/src/main/java/com/example/demo/rest/controller/FollowUpController.java
new file mode 100644
index 0000000..7f85a01
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/rest/controller/FollowUpController.java
@@ -0,0 +1,46 @@
+package com.example.demo.rest.controller;
+
+import com.example.demo.entity.FollowUp;
+import com.example.demo.rest.dto.EmployeeDto;
+import com.example.demo.rest.dto.FollowUpDto;
+import com.example.demo.rest.handler.FollowUpHandler;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/followUp")
+@AllArgsConstructor
+@Tag(name = "followUp", description = "REST API for followUp")
+public class FollowUpController {
+ private FollowUpHandler followUpHandler;
+
+ @GetMapping("/{id}")
+ @Operation(summary = "Get followUp By Id")
+ public ResponseEntity> getById(@PathVariable(value = "id") Integer id) {
+ return followUpHandler.getById(id);
+ }
+
+ @GetMapping("/{id}/followUp-interview")
+ @Operation(summary = "get All interview by FollowUp")
+ public ResponseEntity getInterviews(@PathVariable(value = "id") Integer id, @RequestParam(value = "page", defaultValue = "0") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ return followUpHandler.getAllByInterview(id, page, size);}
+
+ @PostMapping
+ @Operation(summary = "Add New followUp")
+ public ResponseEntity save(@RequestBody FollowUpDto dto) {
+ return followUpHandler.save(dto);
+ }
+
+
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "Delete A followUp")
+ public ResponseEntity delete(@PathVariable Integer id) {
+ return followUpHandler.delete(id);
+ }
+
+}
diff --git a/back-end/src/main/java/com/example/demo/rest/controller/UserController.java b/back-end/src/main/java/com/example/demo/rest/controller/UserController.java
index 00b659c..64c599f 100644
--- a/back-end/src/main/java/com/example/demo/rest/controller/UserController.java
+++ b/back-end/src/main/java/com/example/demo/rest/controller/UserController.java
@@ -1,7 +1,9 @@
package com.example.demo.rest.controller;
+import com.example.demo.entity.Vacancy;
import com.example.demo.rest.dto.UserDto;
import com.example.demo.rest.handler.UserHandler;
+import com.example.demo.rest.handler.VacancyHandler;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
@@ -14,6 +16,7 @@
@Tag(name = "user", description = "REST API for user")
public class UserController {
private final UserHandler userHandler;
+ private VacancyHandler vacancyHandler;
@GetMapping
@Operation(summary = "Get All Users Paged")
@@ -45,4 +48,12 @@ public ResponseEntity> delete(@PathVariable Integer id) {
public ResponseEntity> update(@PathVariable Integer id,@RequestBody UserDto usersDto) {
return userHandler.update(id, usersDto);
}
+ //----------------------------------Vacancy----------------------------------------------------
+
+ @GetMapping("/current/vacancy")
+ @Operation(summary = "get all Vacancies that posted by user ", description = "this API to get all Vacancies that posted by user in pages")
+ public ResponseEntity> getAllByUserName(@RequestParam(value = "page", defaultValue = "0") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ return vacancyHandler.getAllByUser(page, size);
+ }
}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/FollowUpDto.java b/back-end/src/main/java/com/example/demo/rest/dto/FollowUpDto.java
new file mode 100644
index 0000000..ae0bca0
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/rest/dto/FollowUpDto.java
@@ -0,0 +1,14 @@
+package com.example.demo.rest.dto;
+import com.example.demo.rest.dto.common.RestDto;
+import lombok.Data;
+
+import java.sql.Date;
+import java.time.LocalDateTime;
+import java.util.LinkedHashSet;
+import java.util.Set;
+@Data
+public class FollowUpDto extends RestDto {
+ private Date date;
+ private String description;
+ private InterviewDto interview;
+}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/InterviewDto.java b/back-end/src/main/java/com/example/demo/rest/dto/InterviewDto.java
index 19b4810..ae41847 100644
--- a/back-end/src/main/java/com/example/demo/rest/dto/InterviewDto.java
+++ b/back-end/src/main/java/com/example/demo/rest/dto/InterviewDto.java
@@ -4,16 +4,17 @@
import com.example.demo.entity.Employee;
import com.example.demo.entity.Topics;
import com.example.demo.rest.dto.common.RestDto;
+import lombok.Data;
import java.sql.Date;
import java.util.LinkedHashSet;
import java.util.Set;
-
+@Data
public class InterviewDto extends RestDto {
private Date date;
private String evaluation;
private Boolean status;
private Set topicses = new LinkedHashSet<>();
- private Employee employee;
+ private EmployeeDto employee;
private Set applications = new LinkedHashSet<>();
}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/PositionDto.java b/back-end/src/main/java/com/example/demo/rest/dto/PositionDto.java
index c398477..97bfa28 100644
--- a/back-end/src/main/java/com/example/demo/rest/dto/PositionDto.java
+++ b/back-end/src/main/java/com/example/demo/rest/dto/PositionDto.java
@@ -10,4 +10,6 @@ public class PositionDto extends RestDto {
private String name;
private String description;
+ private CompanyDto company;
+
}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/TopicLevelDto.java b/back-end/src/main/java/com/example/demo/rest/dto/TopicLevelDto.java
index 5887b95..8fccbf5 100644
--- a/back-end/src/main/java/com/example/demo/rest/dto/TopicLevelDto.java
+++ b/back-end/src/main/java/com/example/demo/rest/dto/TopicLevelDto.java
@@ -1,11 +1,16 @@
package com.example.demo.rest.dto;
+import com.example.demo.entity.Topics;
import com.example.demo.rest.dto.common.RestDto;
import lombok.Data;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
@Data
public class TopicLevelDto extends RestDto {
private String describtion;
+ private PositionDto position;
}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/TopicsDto.java b/back-end/src/main/java/com/example/demo/rest/dto/TopicsDto.java
index fc4a431..ca96cae 100644
--- a/back-end/src/main/java/com/example/demo/rest/dto/TopicsDto.java
+++ b/back-end/src/main/java/com/example/demo/rest/dto/TopicsDto.java
@@ -3,8 +3,14 @@
import com.example.demo.rest.dto.common.RestDto;
import lombok.Data;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
@Data
public class TopicsDto extends RestDto {
private String name;
private String description;
+ private PositionDto position;
+ private Set topicLevels = new LinkedHashSet<>();
+
}
diff --git a/back-end/src/main/java/com/example/demo/rest/dto/VacancyDto.java b/back-end/src/main/java/com/example/demo/rest/dto/VacancyDto.java
index 11c1030..81bcca8 100644
--- a/back-end/src/main/java/com/example/demo/rest/dto/VacancyDto.java
+++ b/back-end/src/main/java/com/example/demo/rest/dto/VacancyDto.java
@@ -13,4 +13,12 @@ public class VacancyDto extends RestDto {
private LocalDateTime expireDate;
private PositionDto position;
+
+ private String vacancies;
+
+ private LocalDateTime postDate;
+
+ private UserDto postBy;
+
+
}
diff --git a/back-end/src/main/java/com/example/demo/rest/entitymapper/DocumentMapper.java b/back-end/src/main/java/com/example/demo/rest/entitymapper/DocumentMapper.java
index d882908..02991f1 100644
--- a/back-end/src/main/java/com/example/demo/rest/entitymapper/DocumentMapper.java
+++ b/back-end/src/main/java/com/example/demo/rest/entitymapper/DocumentMapper.java
@@ -9,7 +9,7 @@
import java.util.Base64;
import java.util.List;
-@Mapper(componentModel = "spring")
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class DocumentMapper {
// @Mappings({@Mapping(source = "content", target = "content", ignore = true) })
diff --git a/back-end/src/main/java/com/example/demo/rest/entitymapper/FollowUpMapper.java b/back-end/src/main/java/com/example/demo/rest/entitymapper/FollowUpMapper.java
new file mode 100644
index 0000000..cf48362
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/rest/entitymapper/FollowUpMapper.java
@@ -0,0 +1,60 @@
+package com.example.demo.rest.entitymapper;
+import com.example.demo.entity.FollowUp;
+
+import com.example.demo.entity.Interview;
+import com.example.demo.rest.dto.FollowUpDto;
+import com.example.demo.rest.dto.InterviewDto;
+import com.example.demo.rest.entitymapper.common.JPAEntityMapper;
+import com.example.demo.service.InterviewService;
+import com.example.demo.utils.HibernateUtils;
+import org.mapstruct.*;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+
+public abstract class FollowUpMapper {
+ @Autowired
+ private InterviewMapper interviewMapper;
+ @Autowired
+ private InterviewService interviewService;
+
+
+ @Mappings({
+ @Mapping(source = "interview", target = "interview", ignore = true)
+ })
+
+ public abstract FollowUpDto toDto(FollowUp followUp);
+
+ public abstract List toDto(List followUps);
+
+
+ @AfterMapping
+ public void toDtoAfterMapping(FollowUp entity, @MappingTarget FollowUpDto dto) {
+ if (HibernateUtils.isConvertible(entity.getInterview())) {
+ dto.setInterview(interviewMapper.toDto(entity.getInterview()));
+ }
+
+
+ }
+ @InheritInverseConfiguration
+ public abstract FollowUp toEntity(FollowUpDto followUpDto);
+
+ public abstract List toEntity(List list);
+ @AfterMapping
+ public void toEntityAfterMapping(FollowUpDto dto, @MappingTarget FollowUp entity) {
+ if (dto.getInterview() != null) {
+ entity.setInterview(interviewService.getById(dto.getInterview().getId()).get());
+ }
+
+
+ }
+ @InheritInverseConfiguration
+ public abstract FollowUp updateEntityFromDto(FollowUpDto dto, @MappingTarget FollowUp entity);
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/rest/entitymapper/VacancyMapper.java b/back-end/src/main/java/com/example/demo/rest/entitymapper/VacancyMapper.java
index 23db19c..a8a7bee 100644
--- a/back-end/src/main/java/com/example/demo/rest/entitymapper/VacancyMapper.java
+++ b/back-end/src/main/java/com/example/demo/rest/entitymapper/VacancyMapper.java
@@ -3,8 +3,75 @@
import com.example.demo.entity.Vacancy;
import com.example.demo.rest.dto.VacancyDto;
import com.example.demo.rest.entitymapper.common.JPAEntityMapper;
-import org.mapstruct.Mapper;
+import com.example.demo.service.PositionService;
+import com.example.demo.service.UserService;
+import com.example.demo.utils.HibernateUtils;
+import org.mapstruct.*;
+import org.springframework.beans.factory.annotation.Autowired;
+
+
+import java.util.List;
+
+
+@Mapper(componentModel = "spring", nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public abstract class VacancyMapper {
+
+
+ @Autowired
+ private PositionMapper positionMapper;
+ @Autowired
+ private PositionService positionService;
+ @Autowired
+ private UserMapper userMapper;
+ @Autowired
+ private UserService userService;
+
+
+
+ @Mappings({
+ @Mapping(source = "position", target = "position", ignore = true),
+ @Mapping(source = "postBy", target = "postBy", ignore = true),
+
+
+ })
+
+ public abstract VacancyDto toDto(Vacancy vacancy);
+
+ public abstract List toDto(List vacancies);
+
+ @AfterMapping
+ public void toDtoAfterMapping(Vacancy entity, @MappingTarget VacancyDto dto) {
+ if (HibernateUtils.isConvertible(entity.getPosition())) {
+ dto.setPosition(positionMapper.toDto(entity.getPosition()));
+ }
+ if (HibernateUtils.isConvertible(entity.getPostBy())) {
+ dto.setPostBy(userMapper.toDto(entity.getPostBy()));
+ }
+
+
+ }
+ @InheritInverseConfiguration
+ public abstract Vacancy toEntity(VacancyDto billDetailsDto);
+
+ public abstract List toEntity(List list);
+ @AfterMapping
+ public void toEntityAfterMapping(VacancyDto dto, @MappingTarget Vacancy entity) {
+ if (dto.getPosition() != null) {
+ entity.setPosition(positionService.getById(dto.getPosition().getId()).get());
+ }
+
+ if (dto.getPostBy() != null) {
+ entity.setPostBy(userService.getById(dto.getPostBy().getId()).get());
+ }
+
+
+ }
+ @InheritInverseConfiguration
+ public abstract Vacancy updateEntityFromDto(VacancyDto billDetailsDto, @MappingTarget Vacancy billDetails);
+
+
+
+
+}
-@Mapper(componentModel = "spring")
-public interface VacancyMapper extends JPAEntityMapper {}
diff --git a/back-end/src/main/java/com/example/demo/rest/handler/FollowUpHandler.java b/back-end/src/main/java/com/example/demo/rest/handler/FollowUpHandler.java
new file mode 100644
index 0000000..aa3f266
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/rest/handler/FollowUpHandler.java
@@ -0,0 +1,63 @@
+package com.example.demo.rest.handler;
+
+import com.example.demo.entity.FollowUp;
+import com.example.demo.entity.Interview;
+import com.example.demo.rest.dto.FollowUpDto;
+import com.example.demo.rest.dto.InterviewDto;
+import com.example.demo.rest.dto.common.PaginatedResultDto;
+import com.example.demo.rest.entitymapper.EmployeeMapper;
+import com.example.demo.rest.entitymapper.FollowUpMapper;
+import com.example.demo.rest.entitymapper.common.PaginationMapper;
+import com.example.demo.rest.exception.ResourceNotFoundException;
+import com.example.demo.service.EmployeeService;
+import com.example.demo.service.FollowUpService;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.net.URI;
+import java.util.List;
+
+@Component
+@AllArgsConstructor
+public class FollowUpHandler {
+
+ @Autowired
+ private FollowUpService followUpService;
+ @Autowired
+ private FollowUpMapper followUpMapper;
+ private PaginationMapper paginationMapper;
+ public ResponseEntity> save(FollowUpDto dto) {
+ FollowUp followUp = followUpMapper.toEntity(dto);
+ followUpService.save(followUp);
+ FollowUpDto followUpDto = followUpMapper.toDto(followUp);
+ return ResponseEntity.created(URI.create("/FollowUp/" + followUp.getId())).body(followUpDto);
+ }
+ public ResponseEntity> getById(Integer id){
+ FollowUp followUp = followUpService.getById(id).
+ orElseThrow(() -> new ResourceNotFoundException(FollowUp.class.getSimpleName(), id));
+ FollowUpDto followUpDto = followUpMapper.toDto(followUp);
+ return ResponseEntity.ok(followUpDto);
+ }
+
+ public ResponseEntity> delete(Integer id){
+ FollowUp followUp = followUpService.getById(id).
+ orElseThrow(() -> new ResourceNotFoundException(FollowUp.class.getSimpleName(), id));
+ followUpService.delete(followUp);
+ return ResponseEntity.noContent().build();
+ }
+
+
+ public ResponseEntity> getAllByInterview(Integer interviewId, Integer page, Integer size) {
+ Page followUpPage = followUpService.getAllByInterview(interviewId, page, size);
+ List followUpDtoList = followUpMapper.toDto(followUpPage.getContent());
+ PaginatedResultDto paginatedResultDto = new PaginatedResultDto<>();
+ paginatedResultDto.setData(followUpDtoList);
+ paginatedResultDto.setPagination(paginationMapper.toPaginationDto(followUpPage));
+ return ResponseEntity.ok(paginatedResultDto);
+ }
+
+}
+
diff --git a/back-end/src/main/java/com/example/demo/rest/handler/VacancyHandler.java b/back-end/src/main/java/com/example/demo/rest/handler/VacancyHandler.java
index c0880b7..87b970c 100644
--- a/back-end/src/main/java/com/example/demo/rest/handler/VacancyHandler.java
+++ b/back-end/src/main/java/com/example/demo/rest/handler/VacancyHandler.java
@@ -3,18 +3,24 @@
import com.example.demo.entity.Vacancy;
import com.example.demo.rest.dto.VacancyDto;
import com.example.demo.rest.dto.common.PaginatedResultDto;
+import com.example.demo.rest.entitymapper.UserMapper;
import com.example.demo.rest.entitymapper.VacancyMapper;
import com.example.demo.rest.entitymapper.common.PaginationMapper;
import com.example.demo.rest.exception.ResourceNotFoundException;
+import com.example.demo.service.UserService;
import com.example.demo.service.VacancyService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.util.List;
+import java.util.Optional;
@Component
@AllArgsConstructor
@@ -23,6 +29,8 @@ public class VacancyHandler {
private VacancyService vacancyService;
private VacancyMapper vacancyMapper;
private PaginationMapper paginationMapper;
+ private UserService userService;
+ private UserMapper userMapper;
public ResponseEntity> getAll(Integer page, Integer size) {
Page vacancyPage = vacancyService.getAll(page, size);
@@ -61,4 +69,14 @@ public ResponseEntity> delete(Integer id){
vacancyService.delete(vacancy);
return ResponseEntity.noContent().build();
}
+
+ public ResponseEntity> getAllByUser(Integer page, Integer size) {
+ String userName = SecurityContextHolder.getContext().getAuthentication().getName();
+ Page vacancies = vacancyService.getByUser(userName, page, size);
+ List vacancyDtoList = vacancyMapper.toDto(vacancies.getContent());
+ PaginatedResultDto paginatedResultDto = new PaginatedResultDto<>();
+ paginatedResultDto.setData(vacancyDtoList);
+ paginatedResultDto.setPagination(paginationMapper.toPaginationDto(vacancies));
+ return ResponseEntity.ok(paginatedResultDto);
+ }
}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/security/AuthController.java b/back-end/src/main/java/com/example/demo/security/AuthController.java
index c6690b7..3964e47 100644
--- a/back-end/src/main/java/com/example/demo/security/AuthController.java
+++ b/back-end/src/main/java/com/example/demo/security/AuthController.java
@@ -11,6 +11,11 @@
import javax.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.web.bind.annotation.*;
+
@RestController
@RequestMapping("/auth")
@AllArgsConstructor
@@ -18,21 +23,24 @@
@Log4j2
public class AuthController {
+ private AuthHandler authHandler;
- private final AuthHandler authHandler;
+ @PostMapping(value = "/login")
+ public ResponseEntity> login(@Valid @RequestBody LoginRequestDto loginRequest) {
+ //TODO:add encryption/decryption
+ return authHandler.login(loginRequest);
+ }
- @PostMapping("/login")
- public ResponseEntity> login(@Valid @RequestBody AuthRequest authRequest)
- {
- return authHandler.login(authRequest);
+ @PostMapping(value = "/refresh")
+ public ResponseEntity> refreshToken(@RequestBody RefreshTokenRequestDto refreshToken) {
+ return authHandler.refresh(refreshToken);
}
- @PostMapping("/refresh")
- public ResponseEntity> login(@Valid @RequestBody RefreshTokenRequest refreshTokenRequest)
- {
- return authHandler.refresh(refreshTokenRequest.getRefreshToken());
+ @GetMapping(value = "/user-info")
+ @PreAuthorize("isAuthenticated()")
+ public ResponseEntity> getUserInfo(@AuthenticationPrincipal UserDetails userDetails) {
+ return authHandler.getUserInfo(userDetails);
}
-
-
}
+
diff --git a/back-end/src/main/java/com/example/demo/security/AuthHandler.java b/back-end/src/main/java/com/example/demo/security/AuthHandler.java
index 9682749..eda890f 100644
--- a/back-end/src/main/java/com/example/demo/security/AuthHandler.java
+++ b/back-end/src/main/java/com/example/demo/security/AuthHandler.java
@@ -1,5 +1,6 @@
package com.example.demo.security;
+import com.example.demo.rest.handler.UserHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
@@ -13,47 +14,91 @@
import java.util.HashMap;
import java.util.Map;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+
@Component
@RequiredArgsConstructor
@Log4j2
public class AuthHandler {
-
private final AuthenticationManager authenticationManager;
+
private final TokenProvider tokenProvider;
- public ResponseEntity> login(AuthRequest authRequest)
- {
- Authentication authentication= authenticationManager
- .authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()));
- SecurityContextHolder.getContext().setAuthentication(authentication);
+ private final CookieUtil cookieUtil;
- String accessToken = tokenProvider.generateToken(authRequest.getUsername(),TokenType.ACCESS_TOKEN);
- String refreshToken = tokenProvider.generateToken(authRequest.getUsername(),TokenType.REFRESH_TOKEN);
- Map tokens = new HashMap<>();
- tokens.put("access token", accessToken);
- tokens.put("refresh token", refreshToken);
+ @Value("${app.jwt.token.expiration-in-ms}")
+ private Long tokenExpirationMillis;
- return ResponseEntity.ok(tokens);
+ @Value("${app.jwt.refresh.expiration-in-ms}")
+ private Long refreshTokenExpirationMillis;
+
+// private UsersService usersService;
+
+ @Autowired
+ private UserHandler userHandler;
+
+
+ public ResponseEntity> login(LoginRequestDto loginRequest) {
+ log.info("start login for user {}", loginRequest.getUsername());
+ Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ log.info("create cookies for user {}", loginRequest.getUsername());
+ HttpHeaders responseHeaders = new HttpHeaders();
+ String accessToken = tokenProvider.generateAccessToken(loginRequest.getUsername());
+ String refreshToken = tokenProvider.generateRefreshToken(loginRequest.getUsername());
+ addAccessTokenCookie(responseHeaders, accessToken);
+ addRefreshTokenCookie(responseHeaders, refreshToken);
+ return ResponseEntity.ok().body(new LoginResponseDto(accessToken, refreshToken));
}
- public ResponseEntity> refresh(String refreshToken) {
- boolean refreshTokenValid = tokenProvider.validateToken(refreshToken);
+
+ public ResponseEntity> refresh(RefreshTokenRequestDto dto) {
+ Boolean refreshTokenValid = tokenProvider.validateToken(dto.getRefreshToken());
if (!refreshTokenValid) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
- String currentUsername = tokenProvider.getUsernameFromToken(refreshToken);
+ String currentUsername = tokenProvider.getUsernameFromToken(dto.getRefreshToken());
- String newAccessToken = tokenProvider.generateToken(currentUsername,TokenType.ACCESS_TOKEN);
- String newRefreshToken = tokenProvider.generateToken(currentUsername,TokenType.REFRESH_TOKEN);
+ String newAccessToken = tokenProvider.generateAccessToken(currentUsername);
+ HttpHeaders responseHeaders = new HttpHeaders();
+ addAccessTokenCookie(responseHeaders, newAccessToken);
- Map tokens = new HashMap<>();
- tokens.put("access token", newAccessToken);
- tokens.put("refresh token", newRefreshToken);
+ return ResponseEntity.ok().body(new LoginResponseDto(newAccessToken, dto.getRefreshToken()));
+ }
- return ResponseEntity.ok(tokens);
+
+ public ResponseEntity> getUserInfo(UserDetails userDetails) {
+ UserDto userDto = UserDto.builder().username(userDetails.getUsername()).authorities((Set) userDetails.getAuthorities()).build();
+
+ return ResponseEntity.ok(userDto);
+ }
+
+ private void addAccessTokenCookie(HttpHeaders httpHeaders, String token) {
+ httpHeaders.add(HttpHeaders.SET_COOKIE, cookieUtil.createAccessTokenCookie(token, tokenExpirationMillis).toString());
}
-}
+ private void deleteAccessTokenCookie(HttpHeaders httpHeaders) {
+ httpHeaders.add(HttpHeaders.SET_COOKIE, cookieUtil.deleteAccessTokenCookie().toString());
+ }
+ private void addRefreshTokenCookie(HttpHeaders httpHeaders, String token) {
+ httpHeaders.add(HttpHeaders.SET_COOKIE, cookieUtil.createRefreshTokenCookie(token, refreshTokenExpirationMillis).toString());
+ }
+}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/security/CookieUtil.java b/back-end/src/main/java/com/example/demo/security/CookieUtil.java
new file mode 100644
index 0000000..79da7a3
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/security/CookieUtil.java
@@ -0,0 +1,37 @@
+package com.example.demo.security;
+
+import org.springframework.http.HttpCookie;
+import org.springframework.http.ResponseCookie;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CookieUtil {
+
+ public HttpCookie createAccessTokenCookie(String token, Long duration) {
+ //TODO:add encryption/decryption
+ //TODO:add secure= true
+ return ResponseCookie.from("accessToken", token)
+ .maxAge(duration / 1000)
+ .httpOnly(true)
+ .path("/")
+ .build();
+ }
+
+ public HttpCookie createRefreshTokenCookie(String token, Long duration) {
+ //TODO:add encryption/decryption
+ //TODO:add secure= true
+ return ResponseCookie.from("refreshToken", token)
+ .maxAge(duration / 1000)
+ .httpOnly(true)
+ .path("/")
+ .build();
+ }
+
+ public HttpCookie deleteAccessTokenCookie() {
+ return ResponseCookie.from("accessToken", null)
+ .maxAge(0)
+ .httpOnly(true)
+ .path("/")
+ .build();
+ }
+}
diff --git a/back-end/src/main/java/com/example/demo/security/CustomUserDetailsService.java b/back-end/src/main/java/com/example/demo/security/CustomUserDetailsService.java
new file mode 100644
index 0000000..c2dfa36
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/security/CustomUserDetailsService.java
@@ -0,0 +1,48 @@
+package com.example.demo.security;
+
+import com.example.demo.entity.User;
+import com.example.demo.repository.UserRepository;
+import lombok.AllArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+@Log4j2
+public class CustomUserDetailsService implements UserDetailsService {
+
+ private UserRepository userRepository;
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ log.info("load user by username");
+ User user = userRepository.findRolesByUserName(username)
+ .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
+
+ if (user.getRoles().isEmpty()) {
+ throw new AccessDeniedException("User Doesn't have any Roles.");
+ }
+
+ List authorities = user.getRoles().stream()
+ .map(role -> new SimpleGrantedAuthority(role.getName()))
+ .collect(Collectors.toList());
+ return org.springframework.security.core.userdetails.User
+ .withUsername(username)
+ .password(user.getPassword())
+ .authorities(authorities)
+ .accountExpired(false)
+ .accountLocked(false)
+ .credentialsExpired(false)
+ .disabled(false)
+ .build();
+ }
+}
diff --git a/back-end/src/main/java/com/example/demo/security/LoginRequestDto.java b/back-end/src/main/java/com/example/demo/security/LoginRequestDto.java
new file mode 100644
index 0000000..0e68cc2
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/security/LoginRequestDto.java
@@ -0,0 +1,19 @@
+package com.example.demo.security;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Data
+public class LoginRequestDto {
+
+ @NotBlank(message = "username is mandatory")
+ @Size(max = 100, message = "username's max length allowed is 100 characters")
+ private String username;
+
+ @NotBlank(message = "password is mandatory")
+ @Size(max = 100, message = "password's max length allowed is 100 characters")
+ private String password;
+
+}
diff --git a/back-end/src/main/java/com/example/demo/security/LoginResponseDto.java b/back-end/src/main/java/com/example/demo/security/LoginResponseDto.java
new file mode 100644
index 0000000..5b4d9ea
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/security/LoginResponseDto.java
@@ -0,0 +1,17 @@
+package com.example.demo.security;
+
+import lombok.Data;
+
+@Data
+public class LoginResponseDto {
+ private String accessToken;
+ private String refreshToken;
+
+ public LoginResponseDto(String accessToken, String refreshToken) {
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+
+ public LoginResponseDto() {
+ }
+}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/security/RefreshTokenRequest.java b/back-end/src/main/java/com/example/demo/security/RefreshTokenRequestDto.java
similarity index 84%
rename from back-end/src/main/java/com/example/demo/security/RefreshTokenRequest.java
rename to back-end/src/main/java/com/example/demo/security/RefreshTokenRequestDto.java
index 6847951..b6228fb 100644
--- a/back-end/src/main/java/com/example/demo/security/RefreshTokenRequest.java
+++ b/back-end/src/main/java/com/example/demo/security/RefreshTokenRequestDto.java
@@ -5,7 +5,7 @@
import javax.validation.constraints.NotBlank;
@Data
-public class RefreshTokenRequest {
+public class RefreshTokenRequestDto {
@NotBlank(message = "refresh token is mandatory")
private String refreshToken;
diff --git a/back-end/src/main/java/com/example/demo/security/RestAuthenticationEntryPoint.java b/back-end/src/main/java/com/example/demo/security/RestAuthenticationEntryPoint.java
index f35cacb..feb8c1e 100644
--- a/back-end/src/main/java/com/example/demo/security/RestAuthenticationEntryPoint.java
+++ b/back-end/src/main/java/com/example/demo/security/RestAuthenticationEntryPoint.java
@@ -1,6 +1,8 @@
package com.example.demo.security;
import lombok.extern.log4j.Log4j2;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@@ -11,11 +13,14 @@
import java.io.IOException;
@Component
-@Log4j2
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
+ private static final Logger logger = LoggerFactory.getLogger(RestAuthenticationEntryPoint.class);
+
@Override
- public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
- log.error("Unauthorized error: {}", authException.getMessage());
+ public void commence(HttpServletRequest request, HttpServletResponse response,
+ AuthenticationException authException) throws IOException, ServletException {
+ logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized :D :D :D ");
}
-}
+
+}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/security/TokenAuthenticationFilter.java b/back-end/src/main/java/com/example/demo/security/TokenAuthenticationFilter.java
index 40898fb..db5ed91 100644
--- a/back-end/src/main/java/com/example/demo/security/TokenAuthenticationFilter.java
+++ b/back-end/src/main/java/com/example/demo/security/TokenAuthenticationFilter.java
@@ -2,54 +2,86 @@
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@AllArgsConstructor
@Log4j2
-@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
- private final TokenProvider tokenProvider;
- private final SecurityUserService securityUserService;
+ @Autowired
+ private TokenProvider tokenProvider;
+ @Autowired
+ private UserDetailsService customUserDetailsService;
+
+
+ public TokenAuthenticationFilter() {
+ }
@Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try {
- log.info("validate token");
- String jwt = getJwtToken(request);
+ String jwt = getJwtToken(httpServletRequest, false);
if (tokenProvider.validateToken(jwt)) {
- log.info("token is valid ! ");
String username = tokenProvider.getUsernameFromToken(jwt);
- UserDetails userDetails = securityUserService.loadUserByUsername(username);
+ UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
- authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
- log.error("error in validating token", ex);
+ ex.printStackTrace();
}
- filterChain.doFilter(request, response);
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ }
+
+ private String getJwtFromRequest(HttpServletRequest request) {
+ String bearerToken = request.getHeader("Authorization");
+ if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
+ String accessToken = bearerToken.substring(7);
+ if (accessToken == null) return null;
+
+ // return SecurityCipher.decrypt(accessToken);
+ return accessToken;
+ }
+ return null;
}
- private String getJwtToken(HttpServletRequest request) {
- String header = request.getHeader("Authorization");
- if (header != null && header.startsWith("Bearer"))
- return header.replace("Bearer ", "");
+ private String getJwtFromCookie(HttpServletRequest request) {
+ Cookie[] cookies = request.getCookies();
+ for (Cookie cookie : cookies) {
+ if ("accessToken".equals(cookie.getName())) {
+ String accessToken = cookie.getValue();
+ if (accessToken == null) return null;
+
+ // return SecurityCipher.decrypt(accessToken);
+ return accessToken;
+ }
+ }
return null;
}
+
+ private String getJwtToken(HttpServletRequest request, boolean fromCookie) {
+ if (fromCookie) return getJwtFromCookie(request);
+
+ return getJwtFromRequest(request);
+ }
}
diff --git a/back-end/src/main/java/com/example/demo/security/TokenProvider.java b/back-end/src/main/java/com/example/demo/security/TokenProvider.java
index d290e51..d93c811 100644
--- a/back-end/src/main/java/com/example/demo/security/TokenProvider.java
+++ b/back-end/src/main/java/com/example/demo/security/TokenProvider.java
@@ -5,40 +5,46 @@
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
@Component
-@Log4j2
public class TokenProvider {
+ private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
@Value("${app.jwt.token.expiration-in-ms}")
private Long tokenExpirationMillis;
-
@Value("${app.jwt.refresh.expiration-in-ms}")
private Long refreshTokenExpirationMillis;
- private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
-
- public String generateToken(String subject,TokenType tokenType) {
-
- Long expireTime =(tokenType == TokenType.ACCESS_TOKEN) ? tokenExpirationMillis : refreshTokenExpirationMillis;
-
-
+ public String generateAccessToken(String subject) {
Date now = new Date();
- long duration = now.getTime() + expireTime;
+ Long duration = now.getTime() + tokenExpirationMillis;
Date expiryDate = new Date(duration);
+ String token = Jwts.builder()
+ .setSubject(subject)
+ .setIssuedAt(now)
+ .setExpiration(expiryDate)
+ .signWith(key)
+ .compact();
+ return token;
+ }
-
- return Jwts.builder()
+ public String generateRefreshToken(String subject) {
+ Date now = new Date();
+ Long duration = now.getTime() + refreshTokenExpirationMillis;
+ Date expiryDate = new Date(duration);
+ String token = Jwts.builder()
.setSubject(subject)
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(key)
.compact();
+ return token;
}
public String getUsernameFromToken(String token) {
@@ -53,22 +59,22 @@ public LocalDateTime getExpiryDateFromToken(String token) {
public boolean validateToken(String token) {
try {
+ if (!StringUtils.hasText(token))
+ return false;
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
- } catch (ExpiredJwtException ex) {
- log.error("JWT expired: {}", ex.getMessage());
- } catch (IllegalArgumentException ex) {
- log.error("Token is null, empty or only whitespace: {}", ex.getMessage());
+ } catch (SignatureException ex) {
+ ex.printStackTrace();
} catch (MalformedJwtException ex) {
- log.error("JWT is invalid: {}", ex.getMessage());
+ ex.printStackTrace();
+ } catch (ExpiredJwtException ex) {
+ ex.printStackTrace();
} catch (UnsupportedJwtException ex) {
- log.error("JWT is not supported: {}", ex.getMessage());
+ ex.printStackTrace();
+ } catch (IllegalArgumentException ex) {
+ ex.printStackTrace();
}
-
return false;
-
-
}
-
-
}
+
diff --git a/back-end/src/main/java/com/example/demo/security/UserDto.java b/back-end/src/main/java/com/example/demo/security/UserDto.java
new file mode 100644
index 0000000..2b6f278
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/security/UserDto.java
@@ -0,0 +1,16 @@
+package com.example.demo.security;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Set;
+
+@Builder
+@Getter
+public class UserDto {
+
+ private String username;
+ private Set authorities;
+
+}
diff --git a/back-end/src/main/java/com/example/demo/service/FollowUpService.java b/back-end/src/main/java/com/example/demo/service/FollowUpService.java
new file mode 100644
index 0000000..dbd9697
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/service/FollowUpService.java
@@ -0,0 +1,38 @@
+package com.example.demo.service;
+
+import com.example.demo.entity.Answer;
+import com.example.demo.entity.FollowUp;
+import com.example.demo.repository.AnswerRepository;
+import com.example.demo.repository.FollowUpRepository;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+@Service
+@AllArgsConstructor
+public class FollowUpService {
+
+ private FollowUpRepository followUpRepository;
+
+
+
+ public Optional getById(Integer id){
+ return followUpRepository.findById(id);
+ }
+
+ public FollowUp save(FollowUp followUp) {
+ return followUpRepository.save(followUp);
+ }
+
+
+ public Page getAllByInterview(Integer interviewId, Integer page, Integer size) {
+ return followUpRepository.findAllByInterview(interviewId, PageRequest.of(page, size));
+ }
+
+ public void delete(FollowUp followUp) {
+
+ followUpRepository.delete(followUp);
+ }
+}
diff --git a/back-end/src/main/java/com/example/demo/service/PositionService.java b/back-end/src/main/java/com/example/demo/service/PositionService.java
index 1e74f78..002ade6 100644
--- a/back-end/src/main/java/com/example/demo/service/PositionService.java
+++ b/back-end/src/main/java/com/example/demo/service/PositionService.java
@@ -4,6 +4,7 @@
import com.example.demo.entity.Position;
import com.example.demo.repository.PositionRepository;
+import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -12,6 +13,7 @@
import java.util.Optional;
@Service
+@AllArgsConstructor
public class PositionService {
@Autowired
PositionRepository positionRepository;
diff --git a/back-end/src/main/java/com/example/demo/service/VacancyService.java b/back-end/src/main/java/com/example/demo/service/VacancyService.java
index d6da76e..3c67e67 100644
--- a/back-end/src/main/java/com/example/demo/service/VacancyService.java
+++ b/back-end/src/main/java/com/example/demo/service/VacancyService.java
@@ -38,4 +38,8 @@ public void delete(Vacancy vacancy) {
public Optional getById(Integer id) {
return vacancyRepository.findById(id);
}
+
+ public Page getByUser(String userName, Integer page, Integer size) {
+ return vacancyRepository.getVacationsByUser(userName, PageRequest.of(page, size));
+ }
}
\ No newline at end of file
diff --git a/back-end/src/main/java/com/example/demo/utils/HibernateUtils.java b/back-end/src/main/java/com/example/demo/utils/HibernateUtils.java
new file mode 100644
index 0000000..961bcbe
--- /dev/null
+++ b/back-end/src/main/java/com/example/demo/utils/HibernateUtils.java
@@ -0,0 +1,22 @@
+package com.example.demo.utils;
+
+import org.hibernate.Hibernate;
+
+import java.util.List;
+import java.util.Set;
+
+public class HibernateUtils {
+ public static boolean isConvertible(Set> set) {
+ if (set == null)
+ return false;
+ return Hibernate.isInitialized(set);
+ }
+
+ public static boolean isConvertible(List> list) {
+ return Hibernate.isInitialized(list);
+ }
+
+ public static boolean isConvertible(Object obj) {
+ return Hibernate.isInitialized(obj);
+ }
+}
diff --git a/back-end/src/main/resources/application.properties b/back-end/src/main/resources/application.properties
deleted file mode 100644
index d333a82..0000000
--- a/back-end/src/main/resources/application.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
-spring.jpa.hibernate.ddl-auto=none
-spring.jpa.hibernate.show-sql=true
-spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
-spring.datasource.username=postgres
-spring.datasource.password=root
-spring.liquibase.change-log=classpath:db/root-changelog.xml
-spring.doc.swagger-ui.path=/swagger-ui.html
-spring.servlet.multipart.enabled=true
-spring.servlet.multipart.file-size-threshold=2KB
-spring.servlet.multipart.max-file-size=200MB
-spring.servlet.multipart.max-request-size=200MB
-
-app.jwt.token.expiration-in-ms=30000
-app.jwt.refresh.expiration-in-ms=60000
\ No newline at end of file
diff --git a/back-end/src/main/resources/application.yaml b/back-end/src/main/resources/application.yaml
new file mode 100644
index 0000000..240492d
--- /dev/null
+++ b/back-end/src/main/resources/application.yaml
@@ -0,0 +1,28 @@
+
+server:
+ port: 8085
+spring:
+ jpa:
+ hibernate:
+ ddl-auto: none
+ show-sql: true
+ datasource:
+ driver-class-name: org.postgresql.Driver
+ url: jdbc:postgresql://localhost:5432/Candi-Meter
+ username: postgres
+ password: 12345
+ liquibase:
+ change-log: classpath:db/root-changelog.xml
+
+
+app:
+ allowed-origins:
+ http://41.32.3.215:8085/,http://localhost:4200
+
+ jwt:
+ token:
+ expiration-in-ms: 86400000
+ refresh:
+ expiration-in-ms: 108000000
+ secret: my-very-secret-key
+
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-01-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-01-changelog.xml
new file mode 100644
index 0000000..8d7a57b
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-01-changelog.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-02-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-02-changelog.xml
new file mode 100644
index 0000000..9ec145c
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-02-changelog.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-03-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-03-changelog.xml
new file mode 100644
index 0000000..95bef2a
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-03-changelog.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-04-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-04-changelog.xml
new file mode 100644
index 0000000..4b62ea2
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-04-changelog.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-05-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-05-changelog.xml
new file mode 100644
index 0000000..4bf6836
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-05-changelog.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/back-end/src/main/resources/db/changelog/2022/08/01-06-changelog.xml b/back-end/src/main/resources/db/changelog/2022/08/01-06-changelog.xml
new file mode 100644
index 0000000..a656fcb
--- /dev/null
+++ b/back-end/src/main/resources/db/changelog/2022/08/01-06-changelog.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/back-end/src/main/resources/db/root-changelog.xml b/back-end/src/main/resources/db/root-changelog.xml
index 4a3b1ed..60f3f96 100644
--- a/back-end/src/main/resources/db/root-changelog.xml
+++ b/back-end/src/main/resources/db/root-changelog.xml
@@ -12,6 +12,13 @@
+
+
+
+
+
+
+