perf: azure deployment optimizations
This commit is contained in:
62
.github/workflows/deploy.yml
vendored
62
.github/workflows/deploy.yml
vendored
@@ -9,21 +9,53 @@ env:
|
|||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
build-backend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
id-token: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Set image names (lowercase)
|
- name: Set image name (lowercase)
|
||||||
run: |
|
run: |
|
||||||
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||||
echo "BACKEND_IMAGE=ghcr.io/${OWNER}/petshop-backend" >> $GITHUB_ENV
|
echo "BACKEND_IMAGE=ghcr.io/${OWNER}/petshop-backend" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3.3.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build and push backend image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: ./backend
|
||||||
|
push: true
|
||||||
|
tags: ${{ env.BACKEND_IMAGE }}:latest
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
build-frontend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
|
- name: Set image name (lowercase)
|
||||||
|
run: |
|
||||||
|
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||||
echo "FRONTEND_IMAGE=ghcr.io/${OWNER}/petshop-web" >> $GITHUB_ENV
|
echo "FRONTEND_IMAGE=ghcr.io/${OWNER}/petshop-web" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry
|
- name: Log in to GitHub Container Registry
|
||||||
@@ -33,12 +65,8 @@ jobs:
|
|||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push backend image
|
- name: Set up Docker Buildx
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/setup-buildx-action@v3
|
||||||
with:
|
|
||||||
context: ./backend
|
|
||||||
push: true
|
|
||||||
tags: ${{ env.BACKEND_IMAGE }}:latest
|
|
||||||
|
|
||||||
- name: Build and push frontend image
|
- name: Build and push frontend image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
@@ -48,6 +76,22 @@ jobs:
|
|||||||
tags: ${{ env.FRONTEND_IMAGE }}:latest
|
tags: ${{ env.FRONTEND_IMAGE }}:latest
|
||||||
build-args: |
|
build-args: |
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${{ secrets.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${{ secrets.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build-backend, build-frontend]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Set image names (lowercase)
|
||||||
|
run: |
|
||||||
|
OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
echo "BACKEND_IMAGE=ghcr.io/${OWNER}/petshop-backend" >> $GITHUB_ENV
|
||||||
|
echo "FRONTEND_IMAGE=ghcr.io/${OWNER}/petshop-web" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Log in to Azure
|
- name: Log in to Azure
|
||||||
uses: azure/login@v2
|
uses: azure/login@v2
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ FROM eclipse-temurin:25-jre
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /app/target/*.jar app.jar
|
COPY --from=build /app/target/*.jar app.jar
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT ["java","-jar","app.jar"]
|
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-XX:+UseG1GC", "-jar", "app.jar"]
|
||||||
|
|||||||
@@ -38,6 +38,16 @@
|
|||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-validation</artifactId>
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import com.petshop.backend.config.FlywayContextInitializer;
|
|||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
|
@EnableAsync
|
||||||
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
||||||
public class BackendApplication {
|
public class BackendApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CacheManager cacheManager() {
|
||||||
|
CaffeineCacheManager manager = new CaffeineCacheManager("userAuthCache");
|
||||||
|
manager.setCaffeine(Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(60, TimeUnit.SECONDS)
|
||||||
|
.maximumSize(1000));
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import com.petshop.backend.entity.StoreLocation;
|
|||||||
import com.petshop.backend.entity.User;
|
import com.petshop.backend.entity.User;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import com.petshop.backend.security.JwtUtil;
|
import com.petshop.backend.security.JwtUtil;
|
||||||
|
import com.petshop.backend.security.UserAuthCacheService;
|
||||||
import com.petshop.backend.service.ActivityLogService;
|
import com.petshop.backend.service.ActivityLogService;
|
||||||
import com.petshop.backend.service.AvatarStorageService;
|
import com.petshop.backend.service.AvatarStorageService;
|
||||||
import com.petshop.backend.service.EmailService;
|
import com.petshop.backend.service.EmailService;
|
||||||
@@ -58,8 +59,9 @@ public class AuthController {
|
|||||||
private final ActivityLogService activityLogService;
|
private final ActivityLogService activityLogService;
|
||||||
private final PasswordResetService passwordResetService;
|
private final PasswordResetService passwordResetService;
|
||||||
private final EmailService emailService;
|
private final EmailService emailService;
|
||||||
|
private final UserAuthCacheService userAuthCacheService;
|
||||||
|
|
||||||
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService, ActivityLogService activityLogService, PasswordResetService passwordResetService, EmailService emailService) {
|
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService, ActivityLogService activityLogService, PasswordResetService passwordResetService, EmailService emailService, UserAuthCacheService userAuthCacheService) {
|
||||||
this.authenticationManager = authenticationManager;
|
this.authenticationManager = authenticationManager;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.jwtUtil = jwtUtil;
|
this.jwtUtil = jwtUtil;
|
||||||
@@ -68,6 +70,7 @@ public class AuthController {
|
|||||||
this.activityLogService = activityLogService;
|
this.activityLogService = activityLogService;
|
||||||
this.passwordResetService = passwordResetService;
|
this.passwordResetService = passwordResetService;
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
|
this.userAuthCacheService = userAuthCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
@@ -263,6 +266,7 @@ public class AuthController {
|
|||||||
error.put("message", "Username, email, or phone already exists");
|
error.put("message", "Username, email, or phone already exists");
|
||||||
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
|
||||||
}
|
}
|
||||||
|
userAuthCacheService.evict(updatedUser.getId());
|
||||||
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
|
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.petshop.backend.security;
|
|||||||
|
|
||||||
import com.petshop.backend.entity.User;
|
import com.petshop.backend.entity.User;
|
||||||
import com.petshop.backend.exception.ApiErrorResponder;
|
import com.petshop.backend.exception.ApiErrorResponder;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
|
||||||
import io.jsonwebtoken.JwtException;
|
import io.jsonwebtoken.JwtException;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
@@ -16,16 +15,18 @@ import org.springframework.stereotype.Component;
|
|||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final JwtUtil jwtUtil;
|
private final JwtUtil jwtUtil;
|
||||||
private final UserRepository userRepository;
|
private final UserAuthCacheService userAuthCacheService;
|
||||||
private final ApiErrorResponder apiErrorResponder;
|
private final ApiErrorResponder apiErrorResponder;
|
||||||
|
|
||||||
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserRepository userRepository, ApiErrorResponder apiErrorResponder) {
|
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserAuthCacheService userAuthCacheService, ApiErrorResponder apiErrorResponder) {
|
||||||
this.jwtUtil = jwtUtil;
|
this.jwtUtil = jwtUtil;
|
||||||
this.userRepository = userRepository;
|
this.userAuthCacheService = userAuthCacheService;
|
||||||
this.apiErrorResponder = apiErrorResponder;
|
this.apiErrorResponder = apiErrorResponder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,30 +45,44 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
jwt = authHeader.substring(7);
|
jwt = authHeader.substring(7);
|
||||||
Long userId;
|
Long userId;
|
||||||
|
String username;
|
||||||
|
String roleStr;
|
||||||
|
Integer jwtTokenVersion;
|
||||||
try {
|
try {
|
||||||
userId = jwtUtil.extractUserId(jwt);
|
userId = jwtUtil.extractUserId(jwt);
|
||||||
|
username = jwtUtil.extractUsername(jwt);
|
||||||
|
roleStr = jwtUtil.extractRole(jwt);
|
||||||
|
jwtTokenVersion = jwtUtil.extractTokenVersion(jwt);
|
||||||
} catch (JwtException | IllegalArgumentException ex) {
|
} catch (JwtException | IllegalArgumentException ex) {
|
||||||
writeUnauthorized(request, response, "Invalid or expired token", ex);
|
writeUnauthorized(request, response, "Invalid or expired token", ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
User user = userRepository.findById(userId).orElse(null);
|
if (jwtUtil.extractExpiration(jwt).before(new Date())) {
|
||||||
if (user == null || user.getActive() == null || !user.getActive()) {
|
|
||||||
writeUnauthorized(request, response, "User account is inactive", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!jwtUtil.validateToken(jwt, user)) {
|
|
||||||
writeUnauthorized(request, response, "Invalid or expired token", null);
|
writeUnauthorized(request, response, "Invalid or expired token", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppPrincipal principal = new AppPrincipal(
|
UserAuthCacheService.UserAuthData authData = userAuthCacheService.loadAuthData(userId);
|
||||||
user.getId(),
|
if (authData == null || !Boolean.TRUE.equals(authData.active())) {
|
||||||
user.getUsername(),
|
writeUnauthorized(request, response, "User account is inactive", null);
|
||||||
user.getRole(),
|
return;
|
||||||
user.getTokenVersion()
|
}
|
||||||
);
|
if (!authData.tokenVersion().equals(jwtTokenVersion)) {
|
||||||
|
writeUnauthorized(request, response, "Invalid or expired token", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
User.Role role;
|
||||||
|
try {
|
||||||
|
role = User.Role.valueOf(roleStr);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
writeUnauthorized(request, response, "Invalid or expired token", ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppPrincipal principal = new AppPrincipal(userId, username, role, jwtTokenVersion);
|
||||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
principal,
|
principal,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.petshop.backend.security;
|
||||||
|
|
||||||
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserAuthCacheService {
|
||||||
|
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public UserAuthCacheService(UserRepository userRepository) {
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record UserAuthData(Boolean active, Integer tokenVersion) {}
|
||||||
|
|
||||||
|
@Cacheable(value = "userAuthCache", key = "#userId")
|
||||||
|
public UserAuthData loadAuthData(Long userId) {
|
||||||
|
return userRepository.findById(userId)
|
||||||
|
.map(u -> new UserAuthData(u.getActive(), u.getTokenVersion()))
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@CacheEvict(value = "userAuthCache", key = "#userId")
|
||||||
|
public void evict(Long userId) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import com.petshop.backend.entity.User;
|
|||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -18,6 +19,7 @@ public class ActivityLogService {
|
|||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
public void record(Long userId, String activity) {
|
public void record(Long userId, String activity) {
|
||||||
if (userId == null || activity == null || activity.isBlank()) {
|
if (userId == null || activity == null || activity.isBlank()) {
|
||||||
return;
|
return;
|
||||||
@@ -36,6 +38,7 @@ public class ActivityLogService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
public void record(User user, String activity) {
|
public void record(User user, String activity) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.petshop.backend.entity.User;
|
|||||||
import com.petshop.backend.exception.BusinessException;
|
import com.petshop.backend.exception.BusinessException;
|
||||||
import com.petshop.backend.repository.PasswordResetTokenRepository;
|
import com.petshop.backend.repository.PasswordResetTokenRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.security.UserAuthCacheService;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -29,16 +30,19 @@ public class PasswordResetService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final EmailService emailService;
|
private final EmailService emailService;
|
||||||
|
private final UserAuthCacheService userAuthCacheService;
|
||||||
private final SecureRandom secureRandom = new SecureRandom();
|
private final SecureRandom secureRandom = new SecureRandom();
|
||||||
|
|
||||||
public PasswordResetService(PasswordResetTokenRepository passwordResetTokenRepository,
|
public PasswordResetService(PasswordResetTokenRepository passwordResetTokenRepository,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
PasswordEncoder passwordEncoder,
|
PasswordEncoder passwordEncoder,
|
||||||
EmailService emailService) {
|
EmailService emailService,
|
||||||
|
UserAuthCacheService userAuthCacheService) {
|
||||||
this.passwordResetTokenRepository = passwordResetTokenRepository;
|
this.passwordResetTokenRepository = passwordResetTokenRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.emailService = emailService;
|
this.emailService = emailService;
|
||||||
|
this.userAuthCacheService = userAuthCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -97,6 +101,7 @@ public class PasswordResetService {
|
|||||||
user.setPassword(passwordEncoder.encode(newPassword));
|
user.setPassword(passwordEncoder.encode(newPassword));
|
||||||
user.setTokenVersion(user.getTokenVersion() + 1);
|
user.setTokenVersion(user.getTokenVersion() + 1);
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
userAuthCacheService.evict(user.getId());
|
||||||
|
|
||||||
token.setUsedAt(now);
|
token.setUsedAt(now);
|
||||||
passwordResetTokenRepository.save(token);
|
passwordResetTokenRepository.save(token);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.petshop.backend.entity.User;
|
|||||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.security.UserAuthCacheService;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -33,11 +34,13 @@ public class UserService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
|
private final UserAuthCacheService userAuthCacheService;
|
||||||
|
|
||||||
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, StoreRepository storeRepository) {
|
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, StoreRepository storeRepository, UserAuthCacheService userAuthCacheService) {
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
|
this.userAuthCacheService = userAuthCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<UserResponse> getAllUsers(String query, String role, Pageable pageable) {
|
public Page<UserResponse> getAllUsers(String query, String role, Pageable pageable) {
|
||||||
@@ -147,6 +150,7 @@ public class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
user = userRepository.save(user);
|
user = userRepository.save(user);
|
||||||
|
userAuthCacheService.evict(user.getId());
|
||||||
return mapToResponse(user);
|
return mapToResponse(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ spring:
|
|||||||
username: ${SPRING_DATASOURCE_USERNAME:petshop}
|
username: ${SPRING_DATASOURCE_USERNAME:petshop}
|
||||||
password: ${SPRING_DATASOURCE_PASSWORD:petshop}
|
password: ${SPRING_DATASOURCE_PASSWORD:petshop}
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
hikari:
|
||||||
|
maximum-pool-size: 20
|
||||||
|
minimum-idle: 5
|
||||||
|
connection-timeout: 30000
|
||||||
|
|
||||||
sql:
|
sql:
|
||||||
init:
|
init:
|
||||||
@@ -42,6 +46,10 @@ server:
|
|||||||
address: ${SERVER_ADDRESS:0.0.0.0}
|
address: ${SERVER_ADDRESS:0.0.0.0}
|
||||||
servlet:
|
servlet:
|
||||||
context-path: /
|
context-path: /
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
|
mime-types: application/json,application/javascript,text/css,text/plain
|
||||||
|
min-response-size: 1024
|
||||||
|
|
||||||
springdoc:
|
springdoc:
|
||||||
api-docs:
|
api-docs:
|
||||||
|
|||||||
Reference in New Issue
Block a user