From f528cbaa2b437ae7be98c3f91f3a42ed39fe763c Mon Sep 17 00:00:00 2001 From: Mattrixwv Date: Tue, 4 Mar 2025 21:14:22 -0500 Subject: [PATCH] Games tab on admin page working --- db/1.0.0/5. createGame.sql | 11 ++ db/1.0.0/6. createGamePermission.sql | 17 +++ .../annotation/GameAuthorization.java | 16 ++ .../aspect/GameAuthorizationAspect.java | 95 ++++++++++++ .../raidbuilder/config/SecurityConfig.java | 1 + .../raidbuilder/config/TokenService.java | 3 + .../controller/AccountController.java | 1 + .../controller/GameController.java | 137 ++++++++++++++++++ .../controller/IconController.java | 39 +++++ .../raidbuilder/entity/AuditableEntity.java | 11 +- .../entity/AuditableEntityListener.java | 24 ++- .../mattrixwv/raidbuilder/entity/Game.java | 35 +++++ .../raidbuilder/entity/GamePermission.java | 40 +++++ .../repository/game/GameCustomRepository.java | 5 + .../repository/game/GameRepository.java | 16 ++ .../repository/game/GameRepositoryImpl.java | 9 ++ .../GamePermissionCustomRepository.java | 5 + .../GamePermissionRepository.java | 14 ++ .../GamePermissionRepositoryImpl.java | 9 ++ .../raidbuilder/service/AccountService.java | 5 +- .../service/GamePermissionService.java | 41 ++++++ .../raidbuilder/service/GameService.java | 113 +++++++++++++++ .../raidbuilder/util/DatabaseTypeUtil.java | 6 +- src/main/resources/application.properties | 3 + ...e9169149--Screenshot 2024-10-19 162110.png | Bin 0 -> 5292 bytes 25 files changed, 631 insertions(+), 25 deletions(-) create mode 100644 db/1.0.0/5. createGame.sql create mode 100644 db/1.0.0/6. createGamePermission.sql create mode 100644 src/main/java/com/mattrixwv/raidbuilder/annotation/GameAuthorization.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/aspect/GameAuthorizationAspect.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/controller/GameController.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/controller/IconController.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/entity/Game.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/entity/GamePermission.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game/GameCustomRepository.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepository.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepositoryImpl.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionCustomRepository.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepository.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepositoryImpl.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/service/GamePermissionService.java create mode 100644 src/main/java/com/mattrixwv/raidbuilder/service/GameService.java create mode 100644 temp/raidBuilderIcons/gameIcons/070d1ad3-48bb-438e-af01-0559e9169149--Screenshot 2024-10-19 162110.png diff --git a/db/1.0.0/5. createGame.sql b/db/1.0.0/5. createGame.sql new file mode 100644 index 0000000..2285216 --- /dev/null +++ b/db/1.0.0/5. createGame.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS raid_builder.game( + game_id uuid PRIMARY KEY, + game_name text UNIQUE NOT NULL, + game_icon text, + modified_by uuid, + modified_date timestamptz, + created_by uuid NOT NULL, + created_date timestamptz NOT NULL +); + +GRANT ALL ON TABLE raid_builder.game TO raid_builder; diff --git a/db/1.0.0/6. createGamePermission.sql b/db/1.0.0/6. createGamePermission.sql new file mode 100644 index 0000000..298b0fe --- /dev/null +++ b/db/1.0.0/6. createGamePermission.sql @@ -0,0 +1,17 @@ +CREATE TYPE raid_builder.game_permission_type AS ENUM ( 'ADMIN' ); + + +CREATE TABLE IF NOT EXISTS raid_builder.game_permission( + game_permission_id uuid PRIMARY KEY, + account_id uuid REFERENCES raid_builder.account(account_id) NOT NULL, + game_id uuid REFERENCES raid_builder.game(game_id) NOT NULL, + game_permission_type raid_builder.game_permission_type NOT NULL, + + --Auditing + modified_by uuid REFERENCES raid_builder.account(account_id) NOT NULL, + modified_date timestamptz NOT NULL, + created_by uuid REFERENCES raid_builder.account(account_id) NOT NULL, + created_date timestamptz NOT NULL +); + +GRANT ALL ON TABLE raid_builder.game_permission TO raid_builder; diff --git a/src/main/java/com/mattrixwv/raidbuilder/annotation/GameAuthorization.java b/src/main/java/com/mattrixwv/raidbuilder/annotation/GameAuthorization.java new file mode 100644 index 0000000..5602ca8 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/annotation/GameAuthorization.java @@ -0,0 +1,16 @@ +package com.mattrixwv.raidbuilder.annotation; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType; + + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface GameAuthorization{ + public GamePermissionType[] permissions(); +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/aspect/GameAuthorizationAspect.java b/src/main/java/com/mattrixwv/raidbuilder/aspect/GameAuthorizationAspect.java new file mode 100644 index 0000000..ac29ad9 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/aspect/GameAuthorizationAspect.java @@ -0,0 +1,95 @@ +package com.mattrixwv.raidbuilder.aspect; + + +import java.util.List; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authorization.AuthorizationDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; + +import com.mattrixwv.raidbuilder.annotation.GameAuthorization; +import com.mattrixwv.raidbuilder.entity.Account; +import com.mattrixwv.raidbuilder.entity.AccountPermission; +import com.mattrixwv.raidbuilder.entity.GamePermission; +import com.mattrixwv.raidbuilder.service.AccountPermissionService; +import com.mattrixwv.raidbuilder.service.AccountService; +import com.mattrixwv.raidbuilder.service.GamePermissionService; +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType; +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountStatus; +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@Aspect +@Configuration +@RequiredArgsConstructor +public class GameAuthorizationAspect{ + private final AccountService accountService; + private final AccountPermissionService accountPermissionService; + private final GamePermissionService gamePermissionService; + + + @Pointcut("@annotation(com.mattrixwv.raidbuilder.annotation.GameAuthorizationAspect)") + public void gameAuthorizationAnnotation(){ + //Intentionally blank + } + + + @Before("gameAuthorizationAnnotation()") + public void authorizeGame(JoinPoint joinPoint){ + log.debug("Authorizing game"); + + + //Get the annotation + GameAuthorization gameAuthorization = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(GameAuthorization.class); + //Return if there are no required permissions + if(gameAuthorization.permissions().length == 0){ + log.debug("No required permissions"); + return; + } + + //Get the account + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + String username = ((Jwt)auth.getPrincipal()).getClaimAsString("sub"); + Account account = accountService.getByUsername(username); + if(account.getAccountStatus() != AccountStatus.ACTIVE){ + throw new AuthorizationDeniedException("Account is not active", () -> false); + } + + //Return if the user is a site admin + List accountPermissions = accountPermissionService.getByAccountId(account.getAccountId()); + for(AccountPermission permission : accountPermissions){ + if(permission.getAccountPermissionType() == AccountPermissionType.ADMIN){ + log.debug("User is a site admin"); + return; + } + } + + //Return if the account has a matching permission + List gamePermissions = gamePermissionService.getByAccountId(account.getAccountId()); + for(GamePermission permission : gamePermissions){ + for(GamePermissionType permissionType : gameAuthorization.permissions()){ + if(permission.getGamePermissionType() == permissionType){ + log.debug("User is authorized"); + return; + } + } + } + + + log.debug("User is not authorized"); + + //If the user doesn't have a matching permission, throw an authorization exception + throw new AuthorizationDeniedException("User is not authorized to perform this action", () -> false); + } +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java b/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java index f98c1b6..b924272 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java +++ b/src/main/java/com/mattrixwv/raidbuilder/config/SecurityConfig.java @@ -44,6 +44,7 @@ public class SecurityConfig{ .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> { auth.requestMatchers(HttpMethod.OPTIONS).permitAll() + .requestMatchers("/icons/**").permitAll() .requestMatchers("/auth/refresh", "/auth/test").permitAll() //Permit refresh tokens .requestMatchers(HttpMethod.POST, "/auth/signup", "/auth/confirm").permitAll() //Permit signup operations .requestMatchers("/auth/forgot", "/auth/forgot/*").permitAll() //Permit forgot password operations diff --git a/src/main/java/com/mattrixwv/raidbuilder/config/TokenService.java b/src/main/java/com/mattrixwv/raidbuilder/config/TokenService.java index 2bd4b23..52dca96 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/config/TokenService.java +++ b/src/main/java/com/mattrixwv/raidbuilder/config/TokenService.java @@ -43,6 +43,9 @@ public class TokenService{ .expiresAt(now.plus(accessTokenDuration)) .subject(account.getUsername()) .claim("scope", scope) + .claim("accountId", account.getAccountId().toString()) + //Game Permissions + //Raid Group Permissions .build(); return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue(); diff --git a/src/main/java/com/mattrixwv/raidbuilder/controller/AccountController.java b/src/main/java/com/mattrixwv/raidbuilder/controller/AccountController.java index 45648eb..3bbc4ec 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/controller/AccountController.java +++ b/src/main/java/com/mattrixwv/raidbuilder/controller/AccountController.java @@ -186,6 +186,7 @@ public class AccountController{ log.info("Updating account {}", accountId); + //TODO: Existing account verification Account oldAccount = accountService.getByAccountId(accountId); ObjectNode returnNode = mapper.createObjectNode(); if(oldAccount == null){ diff --git a/src/main/java/com/mattrixwv/raidbuilder/controller/GameController.java b/src/main/java/com/mattrixwv/raidbuilder/controller/GameController.java new file mode 100644 index 0000000..30eaf5d --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/controller/GameController.java @@ -0,0 +1,137 @@ +package com.mattrixwv.raidbuilder.controller; + + +import java.util.List; +import java.util.UUID; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.mattrixwv.raidbuilder.annotation.AccountAuthorization; +import com.mattrixwv.raidbuilder.annotation.GameAuthorization; +import com.mattrixwv.raidbuilder.entity.Game; +import com.mattrixwv.raidbuilder.service.GameService; +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.AccountPermissionType; +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@RestController +@RequestMapping("/game") +@RequiredArgsConstructor +public class GameController{ + private final ObjectMapper mapper; + private final GameService gameService; + + + @GetMapping + @AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER}) + public List getGames(@RequestParam("page") int page, @RequestParam("pageSize") int pageSize, @RequestParam(value = "searchTerm", required = false) String searchTerm){ + log.info("Getting games page {} of size {} with search term {}", page, pageSize, searchTerm); + + + List games; + if((searchTerm == null) || (searchTerm.isBlank())){ + games = gameService.getGames(page, pageSize); + } + else{ + games = gameService.getGames(page, pageSize, searchTerm); + } + + + return games; + } + + + @GetMapping("/count") + @AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER}) + public ObjectNode getGamesCount(@RequestParam(value = "searchTerm", required = false) String searchTerm){ + log.info("Getting games count"); + + + Long gamesCount; + if((searchTerm == null) || (searchTerm.isBlank())){ + gamesCount = gameService.getGamesCount(); + } + else{ + gamesCount = gameService.getGamesCount(searchTerm); + } + + ObjectNode countNode = mapper.createObjectNode(); + countNode.put("count", gamesCount); + countNode.put("status", "success"); + + + return countNode; + } + + + @PostMapping + @AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER}) + @GameAuthorization(permissions = {GamePermissionType.ADMIN}) + public ObjectNode createGame(@RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameName") String gameName){ + log.info("Creating game {}", gameName); + + + //TODO: New game verification + ObjectNode returnNode = mapper.createObjectNode(); + Game game = new Game(); + game.setGameName(gameName); + game = gameService.createGame(game, file); + returnNode.put("gameId", game.getGameId().toString()); + returnNode.put("status", "success"); + + log.info("Successfully created game: {}", game.getGameId()); + + return returnNode; + } + + @PutMapping("/{gameId}") + @AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER}) + @GameAuthorization(permissions = {GamePermissionType.ADMIN}) + public ObjectNode updateGame(@PathVariable("gameId") UUID gameId, @RequestParam(value = "iconFile", required = false) MultipartFile file, @RequestParam("gameName") String gameName, @RequestParam(name = "gameIcon", required = false) String gameIcon){ + log.info("Updating game {}", gameName); + + + ObjectNode returnNode = mapper.createObjectNode(); + Game game = new Game(); + game.setGameId(gameId); + game.setGameName(gameName); + game.setGameIcon(gameIcon); + game = gameService.updateGame(game, file); + returnNode.put("gameId", game.getGameId().toString()); + returnNode.put("status", "success"); + + log.info("Successfully updated game: {}", game.getGameId()); + + return returnNode; + } + + @DeleteMapping("/{gameId}") + @AccountAuthorization(permissions = {AccountPermissionType.ADMIN, AccountPermissionType.USER}) + @GameAuthorization(permissions = {GamePermissionType.ADMIN}) + public ObjectNode deleteGame(@PathVariable("gameId") UUID gameId){ + log.info("Deleting game {}", gameId); + + + ObjectNode returnNode = mapper.createObjectNode(); + gameService.deleteById(gameId); + returnNode.put("status", "success"); + + log.info("Successfully deleted game: {}", gameId); + + return returnNode; + } +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/controller/IconController.java b/src/main/java/com/mattrixwv/raidbuilder/controller/IconController.java new file mode 100644 index 0000000..8a5683a --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/controller/IconController.java @@ -0,0 +1,39 @@ +package com.mattrixwv.raidbuilder.controller; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.mattrixwv.raidbuilder.annotation.AccountAuthorization; + +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@RestController +@RequestMapping("/icons") +public class IconController{ + @Value("${uploadFileDirectory}") + private String uploadFileDirectory; + + + @GetMapping("/gameIcons/{gameIconName}") + @AccountAuthorization(permissions = {}) + public ResponseEntity getGameClassIcons(@PathVariable("gameIconName") String gameIconName) throws IOException{ + log.info("Getting game icon {}", gameIconName); + + + byte[] resource = Files.readAllBytes(Path.of(uploadFileDirectory + "/gameIcons/" + gameIconName)); + + + return ResponseEntity.ok().body(resource); + } +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntity.java b/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntity.java index 2774c46..e1b9d0b 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntity.java +++ b/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntity.java @@ -4,33 +4,26 @@ package com.mattrixwv.raidbuilder.entity; import java.time.ZonedDateTime; import java.util.UUID; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; - import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; import lombok.Data; @Data +@MappedSuperclass public abstract class AuditableEntity{ @JsonIgnore - @LastModifiedBy @Column(name = "modified_by") protected UUID modifiedBy; @JsonIgnore - @LastModifiedDate @Column(name = "modified_date") protected ZonedDateTime modifiedDate; @JsonIgnore - @CreatedBy @Column(name = "created_by", updatable = false) protected UUID createdBy; @JsonIgnore - @CreatedDate @Column(name = "created_date", updatable = false) protected ZonedDateTime createdDate; } diff --git a/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntityListener.java b/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntityListener.java index dc78207..76d78fa 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntityListener.java +++ b/src/main/java/com/mattrixwv/raidbuilder/entity/AuditableEntityListener.java @@ -4,12 +4,11 @@ package com.mattrixwv.raidbuilder.entity; import java.time.ZonedDateTime; import java.util.UUID; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Component; -import com.mattrixwv.raidbuilder.service.AccountService; - import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import lombok.RequiredArgsConstructor; @@ -20,19 +19,16 @@ import lombok.extern.slf4j.Slf4j; @Component @RequiredArgsConstructor public class AuditableEntityListener{ - private final AccountService accountService; - - @PrePersist - public void prePersist(AuditableEntity entity){ - entity.setCreatedBy(getCurrentUserId()); - entity.setCreatedDate(ZonedDateTime.now()); + public void prePersist(AuditableEntity entity) throws NoSuchFieldException, IllegalAccessException{ + entity.createdBy = getCurrentUserId(); + entity.createdDate = ZonedDateTime.now(); } @PreUpdate public void preUpdate(AuditableEntity entity){ - entity.setModifiedBy(getCurrentUserId()); - entity.setModifiedDate(ZonedDateTime.now()); + entity.modifiedBy = getCurrentUserId(); + entity.modifiedDate = ZonedDateTime.now(); } @@ -43,11 +39,11 @@ public class AuditableEntityListener{ UUID returnUUID; try{ - UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - returnUUID = accountService.getByUsername(userDetails.getUsername()).getAccountId(); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + returnUUID = UUID.fromString(((Jwt)auth.getPrincipal()).getClaimAsString("accountId")); } catch(Exception e){ - returnUUID = UUID.fromString("382b1ed8-7d5a-4683-a25d-1f462e9cd921"); + returnUUID = new UUID(0, 0); log.debug("No user logged in: {}", returnUUID); } diff --git a/src/main/java/com/mattrixwv/raidbuilder/entity/Game.java b/src/main/java/com/mattrixwv/raidbuilder/entity/Game.java new file mode 100644 index 0000000..efe925c --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/entity/Game.java @@ -0,0 +1,35 @@ +package com.mattrixwv.raidbuilder.entity; + + +import java.util.UUID; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + + +@Entity +@Table(name = "game", schema = "raid_builder") +@EntityListeners(AuditableEntityListener.class) +@Data +@EqualsAndHashCode(callSuper = false) +@ToString(callSuper = true) +@NoArgsConstructor +public class Game extends AuditableEntity{ + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(name = "game_id") + private UUID gameId; + @Column(name = "game_name") + private String gameName; + @Column(name = "game_icon") + private String gameIcon; +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/entity/GamePermission.java b/src/main/java/com/mattrixwv/raidbuilder/entity/GamePermission.java new file mode 100644 index 0000000..703e4b4 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/entity/GamePermission.java @@ -0,0 +1,40 @@ +package com.mattrixwv.raidbuilder.entity; + + +import java.util.UUID; + +import com.mattrixwv.raidbuilder.util.DatabaseTypeUtil.GamePermissionType; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + + +@Entity +@Table(name = "game_permission", schema = "raid_builder") +@EntityListeners(AuditableEntityListener.class) +@Data +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor +public class GamePermission extends AuditableEntity{ + @Id + @Column(name = "game_permission_id") + @GeneratedValue(strategy = GenerationType.UUID) + private UUID gamePermissionId; + @Column(name = "account_id") + private UUID accountId; + @Column(name = "game_id") + private UUID gameId; + @Enumerated(EnumType.STRING) + @Column(name = "game_permission_type") + private GamePermissionType gamePermissionType; +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameCustomRepository.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameCustomRepository.java new file mode 100644 index 0000000..addfbdb --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameCustomRepository.java @@ -0,0 +1,5 @@ +package com.mattrixwv.raidbuilder.repository.game; + + +public interface GameCustomRepository{ +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepository.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepository.java new file mode 100644 index 0000000..60463d7 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepository.java @@ -0,0 +1,16 @@ +package com.mattrixwv.raidbuilder.repository.game; + + +import java.util.List; +import java.util.UUID; + +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mattrixwv.raidbuilder.entity.Game; + + +public interface GameRepository extends GameCustomRepository, JpaRepository{ + public List findAllByGameNameContainingIgnoreCase(String searchTerm, PageRequest pageRequest); + public long countAllByGameNameContainingIgnoreCase(String searchTerm); +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepositoryImpl.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepositoryImpl.java new file mode 100644 index 0000000..7fbc155 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game/GameRepositoryImpl.java @@ -0,0 +1,9 @@ +package com.mattrixwv.raidbuilder.repository.game; + + +import org.springframework.stereotype.Repository; + + +@Repository +public class GameRepositoryImpl implements GameCustomRepository{ +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionCustomRepository.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionCustomRepository.java new file mode 100644 index 0000000..bf2a76c --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionCustomRepository.java @@ -0,0 +1,5 @@ +package com.mattrixwv.raidbuilder.repository.game_permission; + + +public interface GamePermissionCustomRepository{ +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepository.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepository.java new file mode 100644 index 0000000..0d3c772 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepository.java @@ -0,0 +1,14 @@ +package com.mattrixwv.raidbuilder.repository.game_permission; + + +import java.util.List; +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.mattrixwv.raidbuilder.entity.GamePermission; + + +public interface GamePermissionRepository extends GamePermissionCustomRepository, JpaRepository{ + public List findAllByAccountId(UUID accountId); +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepositoryImpl.java b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepositoryImpl.java new file mode 100644 index 0000000..d2989ea --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/repository/game_permission/GamePermissionRepositoryImpl.java @@ -0,0 +1,9 @@ +package com.mattrixwv.raidbuilder.repository.game_permission; + + +import org.springframework.stereotype.Repository; + + +@Repository +public class GamePermissionRepositoryImpl implements GamePermissionCustomRepository{ +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java b/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java index c263336..56b1e9d 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java +++ b/src/main/java/com/mattrixwv/raidbuilder/service/AccountService.java @@ -127,7 +127,10 @@ public class AccountService implements UserDetailsService{ } public List getAccounts(int page, int pageSize, String searchTerm){ - return accountRepository.findAllByUsernameContainingIgnoreCase(searchTerm, PageRequest.of(page, pageSize, Sort.by("username").ascending())); + return accountRepository.findAllByUsernameContainingIgnoreCase( + searchTerm, + PageRequest.of(page, pageSize, Sort.by("username").ascending()) + ); } public long getAccountsCount(){ diff --git a/src/main/java/com/mattrixwv/raidbuilder/service/GamePermissionService.java b/src/main/java/com/mattrixwv/raidbuilder/service/GamePermissionService.java new file mode 100644 index 0000000..1f1ed58 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/service/GamePermissionService.java @@ -0,0 +1,41 @@ +package com.mattrixwv.raidbuilder.service; + + +import java.util.List; +import java.util.UUID; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.mattrixwv.raidbuilder.entity.GamePermission; +import com.mattrixwv.raidbuilder.repository.game_permission.GamePermissionRepository; + +import lombok.RequiredArgsConstructor; + + +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor +public class GamePermissionService{ + private final GamePermissionRepository gamePermissionRepository; + + + //Write + public GamePermission createGamePermission(GamePermission gamePermission){ + return gamePermissionRepository.save(gamePermission); + } + + public GamePermission updateGamePermission(GamePermission gamePermission){ + return gamePermissionRepository.save(gamePermission); + } + + public void deleteGamePermission(GamePermission gamePermission){ + gamePermissionRepository.delete(gamePermission); + } + + + //Read + public List getByAccountId(UUID accountId){ + return gamePermissionRepository.findAllByAccountId(accountId); + } +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/service/GameService.java b/src/main/java/com/mattrixwv/raidbuilder/service/GameService.java new file mode 100644 index 0000000..9c5b329 --- /dev/null +++ b/src/main/java/com/mattrixwv/raidbuilder/service/GameService.java @@ -0,0 +1,113 @@ +package com.mattrixwv.raidbuilder.service; + + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import com.mattrixwv.raidbuilder.entity.Game; +import com.mattrixwv.raidbuilder.repository.game.GameRepository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor +public class GameService{ + private final GameRepository gameRepository; + @Value("${uploadFileDirectory}") + private String uploadFileDirectory; + + + //Write + public Game createGame(Game game, MultipartFile file){ + if(file != null){ + String fileName = UUID.randomUUID().toString() + "--" + file.getOriginalFilename(); + Path filePath = Paths.get(uploadFileDirectory + "/gameIcons").resolve(fileName); + try{ + file.transferTo(filePath); + game.setGameIcon(fileName); + } + catch(Exception error){ + log.error("Error uploading file: " + error.getMessage(), error); + throw new RuntimeException("Error uploading file: " + error.getMessage(), error); + } + } + return gameRepository.save(game); + } + + public Game updateGame(Game game, MultipartFile file){ + Game existingGame = gameRepository.findById(game.getGameId()).orElse(null); + + //Delete the old file if one exists + if((existingGame != null) && (existingGame.getGameIcon() != null) && (game.getGameIcon() == null)){ + log.debug("Deleting old file: {}", existingGame.getGameIcon()); + File existingFile = new File(uploadFileDirectory + "/gameIcons/" + existingGame.getGameIcon()); + if(existingFile.exists()){ + existingFile.delete(); + } + } + + if(file != null){ + //Upload the new file + String fileName = UUID.randomUUID().toString() + "--" + file.getOriginalFilename(); + Path filePath = Paths.get(uploadFileDirectory + "/gameIcons").resolve(fileName); + try{ + file.transferTo(filePath); + game.setGameIcon(fileName); + } + catch(Exception error){ + log.error("Error uploading file: " + error.getMessage(), error); + throw new RuntimeException("Error uploading file: " + error.getMessage(), error); + } + } + + return gameRepository.save(game); + } + + public void deleteById(UUID gameId){ + Game game = gameRepository.findById(gameId).orElse(null); + if(game != null){ + if(game.getGameIcon() != null){ + File existingFile = new File(uploadFileDirectory + "/gameIcons/" + game.getGameIcon()); + if(existingFile.exists()){ + existingFile.delete(); + } + } + gameRepository.deleteById(gameId); + } + } + + + //Read + public List getGames(int page, int pageSize){ + return gameRepository.findAll(PageRequest.of(page, pageSize, Sort.by("gameName").ascending())).getContent(); + } + + public List getGames(int page, int pageSize, String searchTerm){ + return gameRepository.findAllByGameNameContainingIgnoreCase( + searchTerm, + PageRequest.of(page, pageSize, Sort.by("gameName").ascending()) + ); + } + + public long getGamesCount(){ + return gameRepository.count(); + } + + public long getGamesCount(String searchTerm){ + return gameRepository.countAllByGameNameContainingIgnoreCase(searchTerm); + } +} diff --git a/src/main/java/com/mattrixwv/raidbuilder/util/DatabaseTypeUtil.java b/src/main/java/com/mattrixwv/raidbuilder/util/DatabaseTypeUtil.java index 5ab6956..7f8cd8b 100644 --- a/src/main/java/com/mattrixwv/raidbuilder/util/DatabaseTypeUtil.java +++ b/src/main/java/com/mattrixwv/raidbuilder/util/DatabaseTypeUtil.java @@ -17,7 +17,11 @@ public class DatabaseTypeUtil{ public static enum AccountPermissionType { ADMIN, USER - } + }; + + public static enum GamePermissionType { + ADMIN + }; public static enum TutorialStatus { COMPLETED, diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index b3962c6..31128cc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,3 +19,6 @@ rsa.privateKey=classpath:certs/private.pem rsa.publicKey=classpath:certs/public.pem jwt.accessTokenDuration=15m jwt.refreshTokenDuration=30d + +#Files +uploadFileDirectory=../raidBuilderIcons diff --git a/temp/raidBuilderIcons/gameIcons/070d1ad3-48bb-438e-af01-0559e9169149--Screenshot 2024-10-19 162110.png b/temp/raidBuilderIcons/gameIcons/070d1ad3-48bb-438e-af01-0559e9169149--Screenshot 2024-10-19 162110.png new file mode 100644 index 0000000000000000000000000000000000000000..304e84719e261e2ec4eeda941718ab72d71d8f23 GIT binary patch literal 5292 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