├── deploy └── db-password.txt ├── client ├── src │ ├── models │ │ ├── IIcon.ts │ │ ├── IUser.ts │ │ ├── response │ │ │ └── Auth.ts │ │ ├── ITheme.ts │ │ └── IGame.ts │ ├── index.css │ ├── constants │ │ ├── baseApiURL.ts │ │ ├── images.ts │ │ ├── themes.ts │ │ ├── filter.ts │ │ └── routes.tsx │ ├── types │ │ └── filter.ts │ ├── assets │ │ └── img │ │ │ ├── Andrew.jpeg │ │ │ ├── logos │ │ │ └── logo.png │ │ │ └── github-icon.svg │ ├── hooks │ │ └── redux.ts │ ├── store │ │ ├── actions │ │ │ ├── gameAction.ts │ │ │ ├── authAction.ts │ │ │ ├── gamesAction.ts │ │ │ └── cartAction.ts │ │ ├── store.ts │ │ └── reducers │ │ │ ├── gamesSlice.ts │ │ │ └── gameSlice.ts │ ├── pages │ │ ├── Games.tsx │ │ └── Error.tsx │ ├── http │ │ └── api.ts │ ├── components │ │ ├── Sidebar.tsx │ │ ├── UI │ │ │ ├── DisabledButton.tsx │ │ │ ├── SecondaryButton.tsx │ │ │ ├── PrimaryButton.tsx │ │ │ ├── Search.tsx │ │ │ └── Input.tsx │ │ ├── games │ │ │ ├── SkeletonGamesList.tsx │ │ │ └── game │ │ │ │ ├── GameSpecificationRow.tsx │ │ │ │ ├── GameCard.tsx │ │ │ │ ├── GamePrice.tsx │ │ │ │ └── GameCardLoader.tsx │ │ ├── Header │ │ │ ├── SmHeaderLink.tsx │ │ │ └── HeaderIcon.tsx │ │ ├── layouts │ │ │ ├── SimpleLayout.tsx │ │ │ └── MainLayout.tsx │ │ ├── AppRouter.tsx │ │ ├── Footer.tsx │ │ └── ThemeSwitcher.tsx │ ├── index.tsx │ ├── App.tsx │ └── services │ │ ├── AuthService.ts │ │ ├── GameService.ts │ │ └── CartService.ts ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ └── logo512.png ├── .prettierrc ├── postcss.config.js ├── docs │ └── img │ │ └── AppPreview.png ├── Dockerfile ├── tsconfig.node.json ├── vite.config.ts ├── Makefile ├── .gitignore ├── tsconfig.json ├── index.html ├── tailwind.config.js └── package.json ├── server ├── games-service │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application.yml │ │ │ │ └── application-test.yml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── fnd │ │ │ │ └── games_store │ │ │ │ ├── ContextLoadsTest.java │ │ │ │ └── service │ │ │ │ └── GetGameByName.java │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── fnd │ │ │ │ └── games_store │ │ │ │ └── games │ │ │ │ ├── service │ │ │ │ ├── GameCatalogueEditorService.java │ │ │ │ ├── SpecificGameService.java │ │ │ │ ├── SpecificGameListService.java │ │ │ │ └── implementation │ │ │ │ │ ├── SpecificGameServiceImpl.java │ │ │ │ │ └── SpecificGameListServiceImpl.java │ │ │ │ ├── exceptions │ │ │ │ ├── GameAlreadyExistException.java │ │ │ │ └── GameNotFoundException.java │ │ │ │ ├── controller │ │ │ │ ├── SpecificGameController.java │ │ │ │ ├── SpecificGameListController.java │ │ │ │ ├── implementation │ │ │ │ │ ├── SpecificGameControllerImpl.java │ │ │ │ │ └── SpecificGameListControllerImpl.java │ │ │ │ └── GameController.java │ │ │ │ ├── annotations │ │ │ │ └── Benchmarked.java │ │ │ │ ├── dto │ │ │ │ ├── ValidationResponseDTO.java │ │ │ │ ├── publisher │ │ │ │ │ └── PublisherResponseDTO.java │ │ │ │ ├── developer │ │ │ │ │ └── DeveloperResponseDTO.java │ │ │ │ ├── genre │ │ │ │ │ └── GenreResponseDTO.java │ │ │ │ ├── feature │ │ │ │ │ └── FeatureResponseDTO.java │ │ │ │ ├── platform │ │ │ │ │ └── PlatformResponseDTO.java │ │ │ │ └── game │ │ │ │ │ └── GameRequestDTO.java │ │ │ │ ├── repository │ │ │ │ └── GameRepository.java │ │ │ │ ├── filter │ │ │ │ ├── ActuatorFilter.java │ │ │ │ ├── StaffAuthorityValidationFilter.java │ │ │ │ └── AdminAuthorityValidationFilter.java │ │ │ │ ├── entity │ │ │ │ ├── Genre.java │ │ │ │ ├── Feature.java │ │ │ │ ├── Platform.java │ │ │ │ ├── Publisher.java │ │ │ │ └── Developer.java │ │ │ │ ├── rest │ │ │ │ └── AuthorityValidationClient.java │ │ │ │ ├── aspects │ │ │ │ └── Benchmarking.java │ │ │ │ └── GamesApplication.java │ │ │ └── resources │ │ │ ├── application-dev.yml │ │ │ ├── application.yml │ │ │ ├── application-prod.yml │ │ │ ├── application-local.yml │ │ │ └── application-standalone.yml │ ├── lombok.config │ └── Dockerfile ├── cart-service │ ├── lombok.config │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application.yml │ │ │ │ └── application-test.yml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── fnd │ │ │ │ └── games_store │ │ │ │ └── cart │ │ │ │ └── test │ │ │ │ ├── configuration │ │ │ │ └── TestSecurityConfiguration.java │ │ │ │ ├── ContextLoadsTest.java │ │ │ │ ├── repository_integration_test │ │ │ │ ├── TestContainerHealth.java │ │ │ │ ├── Repository_UpdateCartTest.java │ │ │ │ └── Repository_GetCartContentTest.java │ │ │ │ ├── filter │ │ │ │ └── TestJwtFilter.java │ │ │ │ └── cart_service │ │ │ │ └── CartService_GetCartContentTest.java │ │ └── main │ │ │ ├── resources │ │ │ ├── application-dev.yml │ │ │ ├── application-prod.yml │ │ │ ├── application.yml │ │ │ ├── application-local.yml │ │ │ └── application-standalone.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── cart │ │ │ ├── dto │ │ │ ├── GameRequestDTO.java │ │ │ ├── ValidationResponseDTO.java │ │ │ ├── OrderResponseDTO.java │ │ │ ├── CartRequestDTO.java │ │ │ ├── OrderDTO.java │ │ │ └── CartResponseDTO.java │ │ │ ├── exception │ │ │ ├── CartNotFoundException.java │ │ │ └── UserValidationFailedException.java │ │ │ ├── service │ │ │ ├── OrderProcessingService.java │ │ │ └── CartCrudService.java │ │ │ ├── repository │ │ │ └── CartRepository.java │ │ │ ├── controller │ │ │ ├── OrderProcessor.java │ │ │ ├── CartCrudController.java │ │ │ └── implementation │ │ │ │ ├── OrderController.java │ │ │ │ └── CartController.java │ │ │ ├── filter │ │ │ ├── ActuatorFilter.java │ │ │ └── JwtFilter.java │ │ │ ├── rest │ │ │ └── UserValidationClient.java │ │ │ ├── CartApplication.java │ │ │ ├── entity │ │ │ ├── Game.java │ │ │ └── Cart.java │ │ │ ├── configuration │ │ │ ├── RedisConfiguration.java │ │ │ └── KafkaConfiguration.java │ │ │ └── serializer │ │ │ └── OrderDTOSerializer.java │ └── Dockerfile ├── gateway-service │ ├── lombok.config │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.yml │ │ │ ├── application-dev.yml │ │ │ ├── application-prod.yml │ │ │ ├── application-local.yml │ │ │ └── application-standalone.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── gateway │ │ │ └── GatewayApplication.java │ ├── Dockerfile │ └── build.gradle ├── login-service │ ├── lombok.config │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── application-dev.yml │ │ │ │ ├── application-prod.yml │ │ │ │ ├── application.yml │ │ │ │ ├── db │ │ │ │ │ └── migration │ │ │ │ │ │ ├── V1__create_account_and_authority_tables.sql │ │ │ │ │ │ └── V2__insert_users.sql │ │ │ │ ├── application-local.yml │ │ │ │ └── application-standalone.yml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── fnd │ │ │ │ └── games_store │ │ │ │ └── login │ │ │ │ ├── service │ │ │ │ ├── ValidationService.java │ │ │ │ ├── AccountRegistration.java │ │ │ │ ├── LoginService.java │ │ │ │ └── implementation │ │ │ │ │ └── ValidationServiceImpl.java │ │ │ │ ├── jwt_utils │ │ │ │ ├── JwtValidator.java │ │ │ │ ├── JwtGenerator.java │ │ │ │ └── implementation │ │ │ │ │ └── JwtGeneratorImpl.java │ │ │ │ ├── exception │ │ │ │ ├── AccountNotFoundException.java │ │ │ │ ├── InvalidPasswordException.java │ │ │ │ ├── JwtVerificationException.java │ │ │ │ ├── BadTokenCredentialsException.java │ │ │ │ ├── AccountAlreadyExistsException.java │ │ │ │ └── JwtAuthenticationEntryPoint.java │ │ │ │ ├── dto │ │ │ │ ├── LoginRequestDTO.java │ │ │ │ ├── LoginResponseDTO.java │ │ │ │ ├── AccountResponseDTO.java │ │ │ │ ├── ValidationRequestDTO.java │ │ │ │ ├── AccountRequestDTO.java │ │ │ │ └── ValidationResponseDTO.java │ │ │ │ ├── repository │ │ │ │ ├── AuthorityRepository.java │ │ │ │ └── AccountRepository.java │ │ │ │ ├── controller │ │ │ │ ├── UserLogin.java │ │ │ │ ├── UserRegistration.java │ │ │ │ ├── UserValidation.java │ │ │ │ └── implementation │ │ │ │ │ ├── RegistrationController.java │ │ │ │ │ ├── LoginController.java │ │ │ │ │ └── ValidationController.java │ │ │ │ ├── configuration │ │ │ │ └── FirewallConfiguration.java │ │ │ │ ├── LoginApplication.java │ │ │ │ └── entity │ │ │ │ └── Authority.java │ │ └── test │ │ │ ├── resources │ │ │ ├── application.yml │ │ │ └── application-test.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── test │ │ │ ├── ContextLoadsTest.java │ │ │ └── service │ │ │ └── JwtValidationServiceTest.java │ └── Dockerfile ├── orders-service │ ├── lombok.config │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application.yml │ │ │ │ └── application-test.yml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── fnd │ │ │ │ └── games_store │ │ │ │ └── orders │ │ │ │ ├── configuration │ │ │ │ └── TestSecurityConfiguration.java │ │ │ │ ├── ContextLoads.java │ │ │ │ ├── filter │ │ │ │ └── TestJwtFilter.java │ │ │ │ └── service │ │ │ │ ├── SaveInitialOrderDataTest.java │ │ │ │ ├── FetchOrderDataTest.java │ │ │ │ └── ServiceTestUtils.java │ │ └── main │ │ │ ├── resources │ │ │ ├── application-dev.yml │ │ │ ├── application-prod.yml │ │ │ ├── application.yml │ │ │ ├── db │ │ │ │ └── migration │ │ │ │ │ └── V1__create_scheme.sql │ │ │ └── application-standalone.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── orders │ │ │ ├── exception │ │ │ ├── OrderNotFoundException.java │ │ │ └── UserValidationFailedException.java │ │ │ ├── service │ │ │ ├── OrderSaver.java │ │ │ ├── OrderMessageListener.java │ │ │ ├── OrderFetcher.java │ │ │ └── implementation │ │ │ │ ├── OrderReceiverService.java │ │ │ │ └── OrderFetcherService.java │ │ │ ├── repository │ │ │ ├── GameRepository.java │ │ │ └── OrderRepository.java │ │ │ ├── controller │ │ │ ├── OrderController.java │ │ │ └── implementation │ │ │ │ └── OrderControllerImpl.java │ │ │ ├── dto │ │ │ ├── ValidationResponseDTO.java │ │ │ ├── OrderResponseDTO.java │ │ │ ├── OrderRequestDTO.java │ │ │ ├── GameRequestDTO.java │ │ │ └── GameResponseDTO.java │ │ │ ├── configuration │ │ │ ├── MiscellaneousConfiguration.java │ │ │ └── KafkaConfiguration.java │ │ │ ├── filter │ │ │ ├── ActuatorFilter.java │ │ │ └── JwtFilter.java │ │ │ ├── OrdersApplication.java │ │ │ ├── rest │ │ │ └── UserValidationClient.java │ │ │ └── entity │ │ │ ├── Order.java │ │ │ └── Game.java │ └── Dockerfile ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── documentation │ └── images │ │ ├── architecture.png │ │ ├── gamesServiceDbScheme.png │ │ ├── loginServiceDbScheme.png │ │ └── ordersServiceDbScheme.png ├── registry-service │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.yml │ │ │ ├── application-prod.yml │ │ │ └── application-dev.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── registry │ │ │ └── RegistryApplication.java │ ├── build.gradle │ └── Dockerfile ├── config-service │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── application.yml │ │ │ ├── application-dev.yml │ │ │ └── application-prod.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── config │ │ │ └── ConfigApplication.java │ ├── Dockerfile │ └── build.gradle ├── .gitignore ├── notification-service │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.yml │ │ │ └── java │ │ │ └── com │ │ │ └── fnd │ │ │ └── games_store │ │ │ └── notification │ │ │ └── NotificationApplication.java │ └── build.gradle ├── settings.gradle └── build.gradle ├── .gitignore └── low_ram_build.sh /deploy/db-password.txt: -------------------------------------------------------------------------------- 1 | root 2 | -------------------------------------------------------------------------------- /client/src/models/IIcon.ts: -------------------------------------------------------------------------------- 1 | export interface IIcon{ 2 | className?:string 3 | } -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /client/src/constants/baseApiURL.ts: -------------------------------------------------------------------------------- 1 | export const BASE_API_URL:string = "http://localhost:8080" 2 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /client/src/models/IUser.ts: -------------------------------------------------------------------------------- 1 | export interface IUser{ 2 | username: string 3 | password: string 4 | } -------------------------------------------------------------------------------- /client/src/types/filter.ts: -------------------------------------------------------------------------------- 1 | export type filterField = { 2 | id: number, 3 | title: string 4 | } 5 | -------------------------------------------------------------------------------- /server/games-service/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: test 4 | -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/public/logo512.png -------------------------------------------------------------------------------- /server/cart-service/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /server/games-service/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /server/gateway-service/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /server/login-service/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /server/orders-service/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | -------------------------------------------------------------------------------- /client/docs/img/AppPreview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/docs/img/AppPreview.png -------------------------------------------------------------------------------- /server/orders-service/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: orders-service 4 | profiles: 5 | active: test -------------------------------------------------------------------------------- /client/src/assets/img/Andrew.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/src/assets/img/Andrew.jpeg -------------------------------------------------------------------------------- /client/src/assets/img/logos/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/client/src/assets/img/logos/logo.png -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | WORKDIR /app 3 | COPY ["package*.json", "./"] 4 | RUN npm i 5 | COPY . . 6 | EXPOSE 5173 7 | CMD [ "npm", "run", "dev" ] 8 | -------------------------------------------------------------------------------- /server/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/server/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /server/documentation/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/server/documentation/images/architecture.png -------------------------------------------------------------------------------- /server/registry-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: prod 4 | # active: dev 5 | application: 6 | name: registry-service -------------------------------------------------------------------------------- /server/cart-service/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: cart-service 4 | profiles: 5 | active: test 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /server/documentation/images/gamesServiceDbScheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/server/documentation/images/gamesServiceDbScheme.png -------------------------------------------------------------------------------- /server/documentation/images/loginServiceDbScheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/server/documentation/images/loginServiceDbScheme.png -------------------------------------------------------------------------------- /server/documentation/images/ordersServiceDbScheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Friendly-neighborhood-development/Fnd_games_store/HEAD/server/documentation/images/ordersServiceDbScheme.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .gradle/ 3 | .DS_Store 4 | .vscode/ 5 | 6 | 7 | 8 | games-service/build 9 | registry-service/build 10 | config-service/build 11 | gateway-service/build 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/src/constants/images.ts: -------------------------------------------------------------------------------- 1 | export const images = { 2 | logo: "../assets/img/logos/logo.png", 3 | andrew: "../assets/img/logos/Andrew.png", 4 | githubLogo: "../assets/img/github-icon" 5 | } 6 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/service/GameCatalogueEditorService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.service; 2 | 3 | public interface GameCatalogueEditorService { 4 | } 5 | -------------------------------------------------------------------------------- /server/gateway-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: gateway-service 4 | profiles: 5 | # active: dev 6 | active: prod 7 | # active: standalone 8 | -------------------------------------------------------------------------------- /client/src/models/response/Auth.ts: -------------------------------------------------------------------------------- 1 | export interface SignInResponse { 2 | token: string 3 | userId: string 4 | } 5 | 6 | export interface SignUpResponse{ 7 | username:string 8 | userId:string 9 | } -------------------------------------------------------------------------------- /server/cart-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | import: optional:configserver:http://localhost:8001 4 | fail-fast: true 5 | password: admin 6 | username: user -------------------------------------------------------------------------------- /server/login-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | import: optional:configserver:http://localhost:8001 4 | fail-fast: true 5 | password: admin 6 | username: user -------------------------------------------------------------------------------- /server/orders-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | import: optional:configserver:http://localhost:8001 4 | fail-fast: true 5 | password: admin 6 | username: user -------------------------------------------------------------------------------- /client/src/models/ITheme.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {IIcon} from "./IIcon"; 3 | 4 | export interface ITheme { 5 | id: string, 6 | title: "light" | "dark" | "system" 7 | Icon: React.FC 8 | } -------------------------------------------------------------------------------- /server/config-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: prod 4 | application: 5 | name: config-service 6 | 7 | server: 8 | port: 8001 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | games-service/build 4 | registry-service/build 5 | config-service/build 6 | gateway-service/build 7 | login-service/build 8 | cart-service/build 9 | notification-service/build 10 | build -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/service/ValidationService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.service; 2 | 3 | public interface ValidationService { 4 | public Boolean validate(String token); 5 | } 6 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/jwt_utils/JwtValidator.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.jwt_utils; 2 | 3 | public interface JwtValidator { 4 | 5 | public Boolean validateJwtToken(String token); 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /server/cart-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: cart-service 4 | config: 5 | import: optional:configserver:http://config-service:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/games-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: games-service 4 | config: 5 | import: optional:configserver:http://localhost:8001 6 | fail-fast: true 7 | password: admin 8 | username: user 9 | -------------------------------------------------------------------------------- /server/games-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: games-service 4 | profiles: 5 | # active: dev 6 | active: prod 7 | # active: standalone 8 | server: 9 | error: 10 | include-message: always -------------------------------------------------------------------------------- /server/gateway-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: gateway-service 4 | config: 5 | import: optional:configserver:http://localhost:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /client/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | server: { 8 | host: true 9 | }, 10 | }) 11 | -------------------------------------------------------------------------------- /server/games-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: games-service 4 | config: 5 | import: optional:configserver:http://config-service:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/gateway-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: gateway-service 4 | config: 5 | import: optional:configserver:http://config-service:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /server/login-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: login-service 4 | config: 5 | import: optional:configserver:http://config-service:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/login-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: login-service 4 | profiles: 5 | # active: dev 6 | active: prod 7 | # active: standalone 8 | server: 9 | error: 10 | include-message: always 11 | -------------------------------------------------------------------------------- /server/notification-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: notification-service 4 | config: 5 | import: optional:configserver:http://localhost:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/orders-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: orders-service 4 | config: 5 | import: optional:configserver:http://config-service:8001 6 | fail-fast: true 7 | password: admin 8 | username: user -------------------------------------------------------------------------------- /server/orders-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: orders-service 4 | profiles: 5 | # active: dev 6 | active: prod 7 | # active: standalone 8 | server: 9 | error: 10 | include-message: always -------------------------------------------------------------------------------- /server/cart-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: cart-service 4 | profiles: 5 | # active: dev 6 | active: prod 7 | # active: standalone 8 | server: 9 | error: 10 | include-message: always 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/GameRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | import lombok.*; 4 | 5 | @NoArgsConstructor 6 | @AllArgsConstructor 7 | @Data 8 | public class GameRequestDTO { 9 | 10 | private String userId; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/exception/CartNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.exception; 2 | 3 | public class CartNotFoundException extends RuntimeException{ 4 | public CartNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/hooks/redux.ts: -------------------------------------------------------------------------------- 1 | import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux"; 2 | import {AppDispatch, RootState} from "../store/store"; 3 | 4 | 5 | export const useAppDispatch = () => useDispatch() 6 | export const useAppSelector: TypedUseSelectorHook = useSelector -------------------------------------------------------------------------------- /server/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'fnd_games_store' 2 | include 'config-service' 3 | include 'registry-service' 4 | include 'gateway-service' 5 | include 'notification-service' 6 | include 'games-service' 7 | include 'orders-service' 8 | include 'login-service' 9 | include 'cart-service' 10 | 11 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/exception/OrderNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.exception; 2 | 3 | public class OrderNotFoundException extends RuntimeException{ 4 | public OrderNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/cart-service/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | server: 8 | servlet: 9 | context-path: /cart 10 | port: 8083 11 | 12 | variables: 13 | redis: 14 | host: localhost 15 | port: 6379 -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/AccountNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | public class AccountNotFoundException extends RuntimeException{ 4 | public AccountNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/InvalidPasswordException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | public class InvalidPasswordException extends RuntimeException{ 4 | public InvalidPasswordException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/service/OrderSaver.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | import com.fnd.games_store.orders.dto.OrderRequestDTO; 4 | 5 | public interface OrderSaver { 6 | 7 | 8 | String saveOrder(OrderRequestDTO incomingOrder); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/exceptions/GameAlreadyExistException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.exceptions; 2 | 3 | public class GameAlreadyExistException extends RuntimeException{ 4 | public GameAlreadyExistException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/LoginRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | import lombok.*; 4 | 5 | @Data 6 | @AllArgsConstructor 7 | public class LoginRequestDTO { 8 | 9 | private String username; 10 | 11 | private String password; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/JwtVerificationException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | public class JwtVerificationException extends RuntimeException{ 4 | 5 | public JwtVerificationException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/service/SpecificGameService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.service; 2 | 3 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 4 | 5 | public interface SpecificGameService { 6 | 7 | GameResponseDTO getGameByName(String name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/BadTokenCredentialsException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | public class BadTokenCredentialsException extends RuntimeException{ 4 | public BadTokenCredentialsException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/service/OrderMessageListener.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | import com.fnd.games_store.orders.dto.OrderRequestDTO; 4 | 5 | public interface OrderMessageListener { 6 | 7 | 8 | void orderMessageListener(String order); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/exception/UserValidationFailedException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.exception; 2 | 3 | public class UserValidationFailedException extends RuntimeException{ 4 | 5 | public UserValidationFailedException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/service/OrderProcessingService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.service; 2 | 3 | import com.fnd.games_store.cart.dto.CartResponseDTO; 4 | 5 | public interface OrderProcessingService { 6 | 7 | CartResponseDTO purchaseGames(String userId); 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/exceptions/GameNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.exceptions; 2 | 3 | public class GameNotFoundException extends RuntimeException{ 4 | 5 | 6 | public GameNotFoundException() { 7 | super("Requested game not found"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/AccountAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | public class AccountAlreadyExistsException extends RuntimeException{ 4 | public AccountAlreadyExistsException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/src/store/actions/gameAction.ts: -------------------------------------------------------------------------------- 1 | import {createAsyncThunk} from "@reduxjs/toolkit"; 2 | import {GameService} from "../../services/GameService"; 3 | 4 | export const fetchOneGame = createAsyncThunk("games/catalogue/gameName", async (name: string) => { 5 | const res = await GameService.fetchOneGame(name) 6 | return res.data 7 | }) -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/exception/UserValidationFailedException.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.exception; 2 | 3 | public class UserValidationFailedException extends RuntimeException{ 4 | 5 | public UserValidationFailedException(String message) { 6 | super(message); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /client/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build -t game-store-client . 3 | 4 | build-multi-arch: 5 | docker buildx build --platform linux/amd64,linux/arm64 -t andrewsemenov/game-store-client:latest --push . 6 | 7 | run: 8 | docker run -d --name game-store-client --rm -p 5173:5173 game-store-client 9 | 10 | stop: 11 | docker stop game-store-client 12 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/service/OrderFetcher.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | import com.fnd.games_store.orders.dto.OrderResponseDTO; 4 | 5 | import java.util.List; 6 | 7 | public interface OrderFetcher { 8 | 9 | List fetchOrderData(String userId); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/LoginResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | import lombok.*; 4 | 5 | @AllArgsConstructor 6 | @Data 7 | @Getter 8 | @Setter 9 | @NoArgsConstructor 10 | public class LoginResponseDTO { 11 | 12 | private String userId; 13 | private String token; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/repository/GameRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.repository; 2 | 3 | import com.fnd.games_store.orders.entity.Game; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface GameRepository extends JpaRepository { 7 | 8 | 9 | } 10 | -------------------------------------------------------------------------------- /client/src/constants/themes.ts: -------------------------------------------------------------------------------- 1 | import {ITheme} from "../models/ITheme"; 2 | import {ComputerDesktopIcon, MoonIcon, SunIcon} from "@heroicons/react/24/outline"; 3 | 4 | export const themes: Array = [ 5 | {id: "1", title: 'light', Icon: SunIcon}, 6 | {id: "2", title: 'dark', Icon: MoonIcon}, 7 | {id: "3", title: 'system', Icon: ComputerDesktopIcon}, 8 | ] -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/jwt_utils/JwtGenerator.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.jwt_utils; 2 | 3 | import org.springframework.security.core.userdetails.UserDetails; 4 | 5 | import java.util.Map; 6 | 7 | public interface JwtGenerator { 8 | 9 | public String generateJwtToken(UserDetails userDetails); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/AccountResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | import lombok.*; 4 | 5 | @Data 6 | @AllArgsConstructor 7 | @NoArgsConstructor 8 | @Getter 9 | @Setter 10 | public class AccountResponseDTO { 11 | 12 | private String username; 13 | 14 | private String userId; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /server/cart-service/src/main/resources/application-standalone.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | kafka: 3 | bootstrap-servers: localhost:9092 4 | cloud: 5 | config: 6 | enabled: false 7 | discovery: 8 | enabled: false 9 | server: 10 | servlet: 11 | context-path: /cart 12 | port: 8083 13 | 14 | variables: 15 | redis: 16 | host: localhost 17 | port: 6379 -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/service/AccountRegistration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.service; 2 | 3 | import com.fnd.games_store.login.dto.AccountRequestDTO; 4 | import com.fnd.games_store.login.dto.AccountResponseDTO; 5 | 6 | public interface AccountRegistration { 7 | AccountResponseDTO register(AccountRequestDTO accountRequestDTO); 8 | } 9 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/ValidationRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | @NoArgsConstructor 10 | public class ValidationRequestDTO { 11 | 12 | private String token; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/ValidationResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 5 | import lombok.*; 6 | 7 | @AllArgsConstructor 8 | @NoArgsConstructor 9 | @Setter 10 | @Getter 11 | public class ValidationResponseDTO { 12 | 13 | private Boolean isTokenValid; 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/AccountRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | import lombok.*; 4 | 5 | @Data 6 | @AllArgsConstructor 7 | @NoArgsConstructor 8 | @Getter 9 | @Setter 10 | public class AccountRequestDTO { 11 | 12 | private String username; 13 | 14 | private String password; 15 | 16 | private String email; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/login-service/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: login-service 4 | profiles: 5 | active: test 6 | 7 | 8 | variables: 9 | security: 10 | access_secret: OTFBRjg3MUMwOUYxODlFNDc3NEQwNTAxMTg0M0M0NzI5QTczNDY1QUU4MTA5MUFERjZFNDIxNTk0QTZDOUYyQg== 11 | access_expiration: 90000000 12 | common: 13 | new_account_expiration_date: 2030-01-01 14 | 15 | -------------------------------------------------------------------------------- /server/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'org.example' 6 | version '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | sourceSets { 12 | 13 | } 14 | dependencies { 15 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' 16 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' 17 | } 18 | 19 | test { 20 | useJUnitPlatform() 21 | } -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/repository/CartRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.repository; 2 | 3 | import com.fnd.games_store.cart.entity.Cart; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface CartRepository extends CrudRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/controller/SpecificGameController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.controller; 2 | 3 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 4 | import org.springframework.http.ResponseEntity; 5 | 6 | public interface SpecificGameController { 7 | 8 | ResponseEntity getSpecifiedGame(String name); 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/dto/ValidationResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class ValidationResponseDTO { 12 | 13 | private Boolean isTokenValid; 14 | 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/service/LoginService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.service; 2 | 3 | import com.fnd.games_store.login.dto.LoginResponseDTO; 4 | import com.fnd.games_store.login.entity.Authority; 5 | 6 | import java.util.List; 7 | 8 | public interface LoginService { 9 | 10 | public LoginResponseDTO login(String username, String password); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/repository/AuthorityRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.repository; 2 | 3 | import com.fnd.games_store.login.entity.Authority; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface AuthorityRepository extends JpaRepository { 9 | 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/registry-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | instance: 3 | preferIpAddress: false 4 | client: 5 | service-url: 6 | defaultZone: http://registry-service:8761/eureka/ 7 | register-with-eureka: true 8 | fetch-registry: false 9 | server: 10 | waitTimeInMsWhenSyncEmpty: 0 11 | server: 12 | peer-node-read-timeout-ms: 500 13 | 14 | server: 15 | port: 8761 16 | 17 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.controller; 2 | 3 | import com.fnd.games_store.orders.dto.OrderResponseDTO; 4 | import org.springframework.http.ResponseEntity; 5 | 6 | import java.util.List; 7 | 8 | public interface OrderController { 9 | 10 | 11 | ResponseEntity> getOrderData(String userId); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /server/cart-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | redis: 3 | kafka: 4 | bootstrap-servers: kafka:9092 5 | cloud: 6 | config: 7 | enabled: false 8 | discovery: 9 | enabled: false 10 | server: 11 | servlet: 12 | context-path: /cart 13 | port: 8083 14 | 15 | variables: 16 | redis: 17 | host: localhost 18 | port: 6379 19 | kafka: 20 | bootstrap_address: localhost:9092 21 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /dist 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | /.vscode 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | -------------------------------------------------------------------------------- /client/src/pages/Games.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {GamesList} from "../components/games/GamesList"; 3 | import {MainLayout} from "../components/layouts/MainLayout"; 4 | 5 | const Games = () => { 6 | return ( 7 | 8 |
9 | 10 |
11 |
12 | ); 13 | }; 14 | 15 | export default Games 16 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/annotations/Benchmarked.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.annotations; 2 | 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target(ElementType.METHOD) 11 | public @interface Benchmarked { 12 | } 13 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/ValidationResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Setter 12 | @Getter 13 | public class ValidationResponseDTO { 14 | 15 | private Boolean isTokenValid; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/repository/GameRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.repository; 2 | 3 | import com.fnd.games_store.games.entity.Game; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Optional; 7 | 8 | public interface GameRepository extends JpaRepository { 9 | 10 | public Optional getGameByName(String name); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/UserLogin.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller; 2 | 3 | import com.fnd.games_store.login.dto.LoginRequestDTO; 4 | import com.fnd.games_store.login.dto.LoginResponseDTO; 5 | import org.springframework.http.ResponseEntity; 6 | 7 | public interface UserLogin { 8 | 9 | public ResponseEntity login(LoginRequestDTO loginRequestDTO); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/controller/OrderProcessor.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.controller; 2 | 3 | import com.fnd.games_store.cart.dto.CartResponseDTO; 4 | import com.fnd.games_store.cart.dto.OrderResponseDTO; 5 | import org.springframework.http.ResponseEntity; 6 | 7 | public interface OrderProcessor { 8 | 9 | public ResponseEntity processOrder(String userId); 10 | 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/dto/ValidationResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.dto; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | @Setter 12 | @Getter 13 | public class ValidationResponseDTO { 14 | 15 | private Boolean isTokenValid; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /client/src/http/api.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | import {BASE_API_URL} from "../constants/baseApiURL"; 4 | 5 | const $api = axios.create({ 6 | baseURL: BASE_API_URL, 7 | }); 8 | 9 | $api.interceptors.request.use((config) => { 10 | if (config.headers === undefined) { 11 | config.headers = {}; 12 | } 13 | config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`; 14 | return config; 15 | }); 16 | 17 | export default $api; 18 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/OrderResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | import com.fnd.games_store.cart.entity.Game; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | @AllArgsConstructor 12 | @NoArgsConstructor 13 | public class OrderResponseDTO { 14 | 15 | private List gameData; 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/config-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | activate: 4 | on-profile: dev 5 | cloud: 6 | config: 7 | server: 8 | git: 9 | uri: file://home/sergey/work/spring/games_store_environment_configurations 10 | skipSslValidation: true 11 | eureka: 12 | instance: 13 | preferIpAddress: true 14 | client: 15 | service-url: 16 | defaultZone: http://localhost:8761/eureka/ 17 | -------------------------------------------------------------------------------- /server/registry-service/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | config: 3 | activate: 4 | on-profile: dev 5 | instance: 6 | preferIpAddress: false 7 | client: 8 | service-url: 9 | defaultZone: http://localhost:8761/eureka/ 10 | register-with-eureka: true 11 | fetch-registry: false 12 | server: 13 | waitTimeInMsWhenSyncEmpty: 0 14 | server: 15 | peer-node-read-timeout-ms: 500 16 | 17 | server: 18 | port: 8761 -------------------------------------------------------------------------------- /client/src/components/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | interface SidebarProps { 4 | children: React.ReactNode; 5 | } 6 | 7 | export const Sidebar: FC = ({ children }) => { 8 | return ( 9 |
14 | {children} 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/UserRegistration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller; 2 | 3 | 4 | import com.fnd.games_store.login.dto.AccountRequestDTO; 5 | import com.fnd.games_store.login.dto.AccountResponseDTO; 6 | import org.springframework.http.ResponseEntity; 7 | 8 | public interface UserRegistration { 9 | 10 | ResponseEntity registerNewUser(AccountRequestDTO accountRequestDTO); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/service/SpecificGameListService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.service; 2 | 3 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 4 | import org.springframework.data.domain.Sort; 5 | 6 | import java.util.List; 7 | 8 | public interface SpecificGameListService { 9 | 10 | 11 | List getSpecifiedGameList(Integer pageNumber, Integer gamesOnPage, Sort sortBy); 12 | 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /server/config-service/src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | config: 3 | activate: 4 | on-profile: prod 5 | cloud: 6 | config: 7 | server: 8 | git: 9 | uri: https://github.com/Friendly-neighborhood-development/games_store_environment_configurations 10 | skipSslValidation: true 11 | eureka: 12 | instance: 13 | preferIpAddress: true 14 | client: 15 | service-url: 16 | defaultZone: http://registry-service:8761/eureka/ -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/repository/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.repository; 2 | 3 | import com.fnd.games_store.orders.entity.Order; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | 9 | public interface OrderRepository extends JpaRepository { 10 | 11 | Optional> findOrdersByUserId(String userId); 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /client/src/components/UI/DisabledButton.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | 3 | interface DisabledButtonProps { 4 | children: string; 5 | } 6 | 7 | export const DisabledButton: FC = ({ children }) => { 8 | return ( 9 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /client/src/components/games/SkeletonGamesList.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | import {GameCardLoader} from "./game/GameCardLoader"; 3 | import {defaultFilterTitles} from "../../constants/filter"; 4 | 5 | export const SkeletonGamesList: FC = () => { 6 | const games = Array(defaultFilterTitles.pageSize).fill({}) 7 | return ( 8 | <> 9 | {games.map((_) => ( 10 | 11 | ))} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /client/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import "./index.css" 5 | import {Provider} from "react-redux"; 6 | import {store} from "./store/store"; 7 | 8 | const root = ReactDOM.createRoot( 9 | document.getElementById('root') as HTMLElement 10 | ); 11 | root.render( 12 | 13 | 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/controller/SpecificGameListController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.controller; 2 | 3 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 4 | import org.springframework.http.ResponseEntity; 5 | 6 | import java.util.List; 7 | 8 | public interface SpecificGameListController { 9 | 10 | ResponseEntity> getEditedList(Integer page, Integer pageSize, String sortField, Boolean descOrder); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/service/CartCrudService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.service; 2 | 3 | import com.fnd.games_store.cart.dto.CartRequestDTO; 4 | import com.fnd.games_store.cart.dto.CartResponseDTO; 5 | import com.fnd.games_store.cart.dto.GameResponseDTO; 6 | 7 | import java.util.List; 8 | 9 | public interface CartCrudService { 10 | 11 | public CartResponseDTO updateCart(CartRequestDTO cart); 12 | public List getCartContent(String userId); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/CartRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | import com.fnd.games_store.cart.entity.Game; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | 9 | import java.util.List; 10 | 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @ToString 15 | public class CartRequestDTO { 16 | 17 | private String userId; 18 | 19 | private List gameData; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/repository/AccountRepository.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.repository; 2 | 3 | import com.fnd.games_store.login.entity.Account; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.Optional; 8 | 9 | @Repository 10 | public interface AccountRepository extends JpaRepository{ 11 | 12 | Optional findAccountByUsername(String username); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /client/src/store/actions/authAction.ts: -------------------------------------------------------------------------------- 1 | import {createAsyncThunk} from "@reduxjs/toolkit"; 2 | import {AuthService, signUpProps} from "../../services/AuthService"; 3 | import {IUser} from "../../models/IUser"; 4 | 5 | 6 | export const signIn = createAsyncThunk("signIn", async (props: IUser) => { 7 | const res = await AuthService.signIn(props) 8 | return res.data 9 | }) 10 | 11 | export const signUp = createAsyncThunk("signUp", async (props: signUpProps) => { 12 | const res = await AuthService.signUp(props) 13 | return res.data 14 | }) -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/configuration/MiscellaneousConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.configuration; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class MiscellaneousConfiguration { 9 | 10 | 11 | 12 | @Bean 13 | public ObjectMapper objectMapper(){ 14 | return new ObjectMapper(); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /client/src/store/actions/gamesAction.ts: -------------------------------------------------------------------------------- 1 | import {createAsyncThunk} from "@reduxjs/toolkit"; 2 | import {GameService} from "../../services/GameService"; 3 | 4 | interface fetchGamesProps { 5 | page: number, 6 | pageSize: number, 7 | sortField: string, 8 | ascOrder: boolean 9 | } 10 | 11 | export const fetchGames = createAsyncThunk("games/catalogue", 12 | async ({page, pageSize, sortField, ascOrder}: fetchGamesProps) => { 13 | const res = await GameService.fetchGames(page, pageSize, sortField, ascOrder) 14 | return res.data 15 | }) 16 | 17 | -------------------------------------------------------------------------------- /server/gateway-service/src/main/java/com/fnd/games_store/gateway/GatewayApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.gateway; 2 | 3 | 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | 8 | @SpringBootApplication 9 | @EnableDiscoveryClient 10 | public class GatewayApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(GatewayApplication.class,args); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/UserValidation.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller; 2 | 3 | import com.fnd.games_store.login.dto.ValidationResponseDTO; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.PostMapping; 6 | import org.springframework.web.bind.annotation.RequestHeader; 7 | 8 | import java.util.Map; 9 | 10 | public interface UserValidation { 11 | 12 | 13 | ResponseEntity validateUser(@RequestHeader Map headers); 14 | } 15 | -------------------------------------------------------------------------------- /client/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect } from 'react'; 2 | import { BrowserRouter as Router } from 'react-router-dom'; 3 | import { AppRouter } from './components/AppRouter'; 4 | import { useAppDispatch } from './hooks/redux'; 5 | import { checkAuth } from './store/reducers/authSlice'; 6 | 7 | const App: FC = () => { 8 | const dispatch = useAppDispatch(); 9 | useEffect(() => { 10 | dispatch(checkAuth()); 11 | }, []); 12 | return ( 13 | 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default App; 20 | -------------------------------------------------------------------------------- /client/src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import gameReducer from "./reducers/gameSlice"; 3 | import gamesReducer from "./reducers/gamesSlice"; 4 | import authReducer from "./reducers/authSlice"; 5 | import cartReducer from "./reducers/cartSlice"; 6 | 7 | export const store = configureStore({ 8 | reducer: { 9 | game: gameReducer, 10 | games: gamesReducer, 11 | auth: authReducer, 12 | cart: cartReducer, 13 | }, 14 | }); 15 | 16 | export type RootState = ReturnType; 17 | export type AppDispatch = typeof store.dispatch; 18 | -------------------------------------------------------------------------------- /client/src/components/Header/SmHeaderLink.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | import {Link} from "react-router-dom"; 3 | 4 | interface IconProps { 5 | className: string 6 | } 7 | 8 | interface SmHeaderLinkProps { 9 | href: string, 10 | title: string, 11 | Icon: React.FC 12 | } 13 | 14 | export const SmHeaderLink: FC = ({href, title, Icon}) => { 15 | return ( 16 | 17 | 18 | {title} 19 | 20 | ); 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/configuration/TestSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.configuration; 2 | 3 | 4 | 5 | import com.fnd.games_store.orders.filter.TestJwtFilter; 6 | import com.fnd.games_store.orders.rest.UserValidationClient; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class TestSecurityConfiguration { 12 | 13 | @Bean 14 | public TestJwtFilter testGlobalFilter(){ 15 | return new TestJwtFilter(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/notification-service/src/main/java/com/fnd/games_store/notification/NotificationApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.notification; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | 7 | import javax.swing.*; 8 | 9 | @SpringBootApplication 10 | @EnableDiscoveryClient 11 | public class NotificationApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(NotificationApplication.class, args); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/configuration/TestSecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.configuration; 2 | 3 | 4 | import com.fnd.games_store.cart.rest.UserValidationClient; 5 | import com.fnd.games_store.cart.test.filter.TestJwtFilter; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Profile; 9 | 10 | @Configuration 11 | public class TestSecurityConfiguration { 12 | 13 | @Bean 14 | public TestJwtFilter testGlobalFilter(){ 15 | return new TestJwtFilter(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /low_ram_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | NC='\033[0m' 4 | GREEN='\033[0;32m' 5 | 6 | 7 | docker build -t registry-service ./server/registry-service; 8 | 9 | docker build -t config-service ./server/config-service; 10 | 11 | docker build -t gateway-service ./server/gateway-service; 12 | 13 | docker build -t games-service ./server/games-service; 14 | 15 | docker build -t login-service ./server/login-service; 16 | 17 | docker build -t orders-service ./server/orders-service; 18 | 19 | docker build -t cart-service ./server/cart-service; 20 | 21 | docker build -t client-application ./client 22 | 23 | 24 | printf ${GREEN}"===Fnd_games_store services build completed==="${NC} 25 | echo 26 | -------------------------------------------------------------------------------- /client/src/services/AuthService.ts: -------------------------------------------------------------------------------- 1 | import {AxiosResponse} from "axios"; 2 | import {SignInResponse, SignUpResponse} from "../models/response/Auth"; 3 | import {IUser} from "../models/IUser"; 4 | import $api from "../http/api"; 5 | 6 | export interface signUpProps extends IUser { 7 | email: string 8 | } 9 | 10 | export class AuthService { 11 | static signIn(user: IUser): Promise> { 12 | return $api.post('/login/v1/authorization', user) 13 | } 14 | 15 | static signUp({password, username, email}: signUpProps): Promise> { 16 | return $api.post('/login/v1/registration', {username, password, email}) 17 | } 18 | } -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | Games store 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /server/games-service/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: true 10 | validate-on-migrate: true 11 | database: POSTGRESQL 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | platform: postgres 18 | url: jdbc:postgresql://localhost:5432/games_service_database 19 | username: postgres 20 | password: root 21 | driver-class-name: org.postgresql.Driver 22 | server: 23 | servlet: 24 | context-path: /games 25 | port: 8081 26 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/OrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | import com.fnd.games_store.cart.entity.Cart; 4 | import com.fnd.games_store.cart.entity.Game; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | 10 | import java.util.List; 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @ToString 15 | public class OrderDTO { 16 | 17 | 18 | private String userId; 19 | 20 | private List gameData; 21 | 22 | public OrderDTO(Cart cart){ 23 | this.userId = cart.getUserId(); 24 | this.gameData = cart.getGameData(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /client/src/components/UI/SecondaryButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | interface SecondaryButtonProps { 4 | children: any; 5 | type?: 'button' | 'submit'; 6 | value?: string; 7 | onClick?: any; 8 | } 9 | 10 | export const SecondaryButton: FC = ({ 11 | children, 12 | ...props 13 | }) => { 14 | return ( 15 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /client/src/components/UI/PrimaryButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | interface PrimaryButtonProps { 4 | children: any; 5 | type?: 'button' | 'submit'; 6 | value?: string; 7 | onClick?: any; 8 | } 9 | 10 | export const PrimaryButton: FC = ({ 11 | children, 12 | ...props 13 | }) => { 14 | return ( 15 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /client/src/services/GameService.ts: -------------------------------------------------------------------------------- 1 | import $api from "../http/api"; 2 | import {AxiosResponse} from "axios"; 3 | import {IGame} from "../models/IGame"; 4 | 5 | export class GameService { 6 | static fetchGames(page: number, pageSize: number, sortField: string, ascOrder: boolean): Promise> { 7 | return $api.get("/games/v1/catalogue/list?", { 8 | params: { 9 | page, 10 | pageSize, 11 | sortField, 12 | ascOrder 13 | } 14 | }) 15 | } 16 | 17 | static fetchOneGame(name: string): Promise> { 18 | return $api.get("/games/v1/catalogue/" + name) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/ContextLoads.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders; 2 | 3 | 4 | import com.fnd.games_store.orders.service.OrderMessageListener; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | @SpringBootTest(classes = OrdersApplication.class) 12 | public class ContextLoads { 13 | 14 | 15 | 16 | @Autowired 17 | private OrderMessageListener service; 18 | 19 | @Test 20 | void contextLoadsTest(){ 21 | assertThat(service).isNotNull(); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/filter/ActuatorFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.filter; 2 | 3 | import org.springframework.web.filter.OncePerRequestFilter; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | public class ActuatorFilter extends OncePerRequestFilter { 12 | 13 | 14 | @Override 15 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 16 | filterChain.doFilter(request,response); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/publisher/PublisherResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.publisher; 2 | 3 | 4 | import com.fnd.games_store.games.entity.Publisher; 5 | import lombok.*; 6 | 7 | @Getter 8 | @Setter 9 | @EqualsAndHashCode 10 | @ToString 11 | @NoArgsConstructor 12 | public class PublisherResponseDTO { 13 | private String id; 14 | 15 | private String name; 16 | 17 | 18 | public PublisherResponseDTO(String id, String name) { 19 | this.id = id; 20 | this.name = name; 21 | } 22 | 23 | public PublisherResponseDTO(Publisher publisher) { 24 | this.id = publisher.getId(); 25 | this.name = publisher.getName(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/controller/CartCrudController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.controller; 2 | 3 | import com.fnd.games_store.cart.dto.CartRequestDTO; 4 | import com.fnd.games_store.cart.dto.CartResponseDTO; 5 | import com.fnd.games_store.cart.dto.GameRequestDTO; 6 | import com.fnd.games_store.cart.dto.GameResponseDTO; 7 | import org.springframework.http.ResponseEntity; 8 | 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | public interface CartCrudController { 13 | 14 | 15 | public ResponseEntity updateCart(CartRequestDTO cartRequestDTO); 16 | 17 | public ResponseEntity> getCartContent(GameRequestDTO gameRequestDTO); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/developer/DeveloperResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.developer; 2 | 3 | import com.fnd.games_store.games.entity.Developer; 4 | import lombok.*; 5 | 6 | @Getter 7 | @Setter 8 | @EqualsAndHashCode 9 | @ToString 10 | @NoArgsConstructor 11 | public class DeveloperResponseDTO { 12 | 13 | 14 | private String id; 15 | 16 | private String name; 17 | 18 | 19 | public DeveloperResponseDTO(String id, String name) { 20 | this.id = id; 21 | this.name = name; 22 | } 23 | 24 | public DeveloperResponseDTO(Developer developer) { 25 | this.id = developer.getId(); 26 | this.name = developer.getName(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/filter/ActuatorFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.filter; 2 | 3 | import org.springframework.web.filter.OncePerRequestFilter; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | public class ActuatorFilter extends OncePerRequestFilter { 12 | 13 | 14 | @Override 15 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 16 | filterChain.doFilter(request,response); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/login-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: true 10 | validate-on-migrate: true 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | server: 27 | servlet: 28 | context-path: /login 29 | port: 8082 -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/dto/OrderResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.dto; 2 | 3 | import com.fnd.games_store.orders.entity.Order; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.ToString; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @ToString 15 | public class OrderResponseDTO { 16 | 17 | 18 | private List gameData; 19 | 20 | 21 | public OrderResponseDTO(Order order){ 22 | this.gameData = order.getGames().stream().map(GameResponseDTO::new).collect(Collectors.toList()); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/filter/ActuatorFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.filter; 2 | 3 | import org.springframework.web.filter.OncePerRequestFilter; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | import java.io.IOException; 10 | 11 | public class ActuatorFilter extends OncePerRequestFilter { 12 | 13 | 14 | @Override 15 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 16 | filterChain.doFilter(request,response); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/games-service/src/test/java/com/fnd/games_store/ContextLoadsTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store; 2 | 3 | import com.fnd.games_store.games.GamesApplication; 4 | import com.fnd.games_store.games.controller.GameController; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | 12 | 13 | @SpringBootTest(classes = GamesApplication.class) 14 | public class ContextLoadsTest { 15 | 16 | @Autowired 17 | GameController controller; 18 | 19 | @Test 20 | void contextLoadsTest(){ 21 | assertThat(controller).isNotNull(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/genre/GenreResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.genre; 2 | 3 | import com.fnd.games_store.games.entity.Genre; 4 | import com.fnd.games_store.games.entity.Publisher; 5 | import lombok.*; 6 | 7 | @Getter 8 | @Setter 9 | @EqualsAndHashCode 10 | @ToString 11 | @NoArgsConstructor 12 | public class GenreResponseDTO { 13 | 14 | 15 | private String id; 16 | 17 | private String name; 18 | 19 | 20 | public GenreResponseDTO(String id, String name) { 21 | this.id = id; 22 | this.name = name; 23 | } 24 | 25 | public GenreResponseDTO(Genre genre) { 26 | this.id = genre.getId(); 27 | this.name = genre.getName(); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/feature/FeatureResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.feature; 2 | 3 | import com.fnd.games_store.games.entity.Feature; 4 | import com.fnd.games_store.games.entity.Platform; 5 | import lombok.*; 6 | 7 | @Getter 8 | @Setter 9 | @EqualsAndHashCode 10 | @ToString 11 | @NoArgsConstructor 12 | public class FeatureResponseDTO { 13 | 14 | private String id; 15 | 16 | private String name; 17 | 18 | 19 | public FeatureResponseDTO(String id, String name) { 20 | this.id = id; 21 | this.name = name; 22 | } 23 | 24 | public FeatureResponseDTO(Feature feature) { 25 | this.id = feature.getId(); 26 | this.name = feature.getName(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/platform/PlatformResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.platform; 2 | 3 | import com.fnd.games_store.games.entity.Genre; 4 | import com.fnd.games_store.games.entity.Platform; 5 | import lombok.*; 6 | 7 | @Getter 8 | @Setter 9 | @EqualsAndHashCode 10 | @ToString 11 | @NoArgsConstructor 12 | public class PlatformResponseDTO { 13 | private String id; 14 | 15 | private String name; 16 | 17 | 18 | public PlatformResponseDTO(String id, String name) { 19 | this.id = id; 20 | this.name = name; 21 | } 22 | 23 | public PlatformResponseDTO(Platform platform) { 24 | this.id = platform.getId(); 25 | this.name = platform.getName(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/models/IGame.ts: -------------------------------------------------------------------------------- 1 | interface IGenre { 2 | id: string, 3 | name: string 4 | } 5 | 6 | interface IDeveloper { 7 | id: string, 8 | name: string 9 | } 10 | 11 | interface IPlatform { 12 | id: string 13 | name: string 14 | } 15 | 16 | interface IFeature { 17 | id: string, 18 | name: string 19 | } 20 | 21 | interface IPublisher { 22 | id: string, 23 | name: string 24 | } 25 | 26 | export interface IGame { 27 | id: number 28 | name: string 29 | base64Image: string 30 | description: string 31 | developer: IDeveloper 32 | discount: number 33 | features: Array 34 | genre: Array 35 | platform: Array 36 | price: number 37 | publisher: IPublisher 38 | releaseDate: string 39 | } -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/configuration/FirewallConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.configuration; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.web.firewall.StrictHttpFirewall; 6 | 7 | @Configuration 8 | public class FirewallConfiguration { 9 | 10 | @Bean 11 | public StrictHttpFirewall httpFirewall() { 12 | StrictHttpFirewall firewall = new StrictHttpFirewall(); 13 | firewall.setAllowedHeaderNames((header) -> true); 14 | firewall.setAllowedHeaderValues((header) -> true); 15 | firewall.setAllowedParameterNames((parameter) -> true); 16 | return firewall; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/login-service/src/test/java/com/fnd/games_store/test/ContextLoadsTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.test; 2 | 3 | 4 | import com.fnd.games_store.login.LoginApplication; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.context.ApplicationContext; 11 | 12 | @SpringBootTest(classes = LoginApplication.class) 13 | public class ContextLoadsTest { 14 | 15 | @Autowired 16 | ApplicationContext context; 17 | 18 | 19 | 20 | @Test 21 | void loads(){ 22 | assertThat(context.getAutowireCapableBeanFactory()).isNotNull(); 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /server/orders-service/src/main/resources/db/migration/V1__create_scheme.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS orders 2 | (order_id varchar(255) PRIMARY KEY UNIQUE NOT NULL, 3 | order_date date NOT NULL, 4 | is_order_processed boolean NOT NULL, 5 | user_id varchar(255)); 6 | 7 | 8 | CREATE TABLE IF NOT EXISTS games 9 | (game_id varchar(255) PRIMARY KEY UNIQUE NOT NULL, 10 | name varchar(255) UNIQUE NOT NULL, 11 | release_date varchar(50) NOT NULL, 12 | price numeric(10,2) NOT NULL, 13 | discount numeric(10,2) NOT NULL, 14 | description varchar NOT NULL, 15 | base64Image varchar NOT NULL); 16 | 17 | 18 | CREATE TABLE IF NOT EXISTS order_game 19 | (order_id varchar(255), 20 | game_id varchar(255), 21 | 22 | FOREIGN KEY(order_id) REFERENCES orders(order_id), 23 | FOREIGN KEY(game_id) REFERENCES games(game_id)); -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/OrdersApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | import org.springframework.cloud.openfeign.EnableFeignClients; 8 | 9 | @SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) 10 | @EnableDiscoveryClient 11 | @EnableFeignClients 12 | public class OrdersApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(OrdersApplication.class,args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /client/src/components/games/game/GameSpecificationRow.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | 3 | interface IValue { 4 | id: string 5 | name: string 6 | } 7 | 8 | interface GameSpecificationRowProps { 9 | title: string, 10 | arrayValue?: IValue[], 11 | value?: string, 12 | } 13 | 14 | export const GameSpecificationRow: FC = ({title, arrayValue, value}) => { 15 | return ( 16 |
17 |
{title}
18 |
{arrayValue?.map((val) => ( 19 | {val.name} 20 | ))}
21 | {value &&
{value}
} 22 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/img/github-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/UI/Search.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | import {Input} from "./Input"; 3 | import {MagnifyingGlassIcon} from "@heroicons/react/24/outline"; 4 | 5 | interface SearchProps { 6 | placeholder: string, 7 | } 8 | 9 | export const Search: FC = ({placeholder}) => { 10 | return ( 11 |
12 | 13 | 14 | 15 | 16 |
17 | ); 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/ContextLoadsTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test; 2 | 3 | 4 | import com.fnd.games_store.cart.CartApplication; 5 | import com.fnd.games_store.cart.controller.implementation.CartController; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | import org.junit.jupiter.api.Test; 9 | 10 | 11 | 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | @SpringBootTest(classes = CartApplication.class) 16 | public class ContextLoadsTest { 17 | 18 | 19 | @Autowired 20 | CartController controller; 21 | 22 | 23 | @Test 24 | void isContextLoads(){ 25 | assertThat(controller).isNotNull(); 26 | } 27 | 28 | 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/dto/OrderRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.dto; 2 | 3 | 4 | 5 | import com.fnd.games_store.orders.entity.Order; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import lombok.ToString; 10 | 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @ToString 18 | public class OrderRequestDTO { 19 | 20 | 21 | private String userId; 22 | 23 | private List gameData; 24 | 25 | 26 | public OrderRequestDTO(Order order){ 27 | this.userId = order.getUserId(); 28 | this.gameData = order.getGames().stream().map(GameRequestDTO::new).collect(Collectors.toList()); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /client/src/components/layouts/SimpleLayout.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC, useEffect} from 'react'; 2 | import {useSwitchTheme} from "../../hooks/useThemeSwitch"; 3 | import {themes} from "../../constants/themes"; 4 | 5 | interface SimpleLayoutProps { 6 | children?: React.ReactNode 7 | } 8 | 9 | export const SimpleLayout: FC = ({children}) => { 10 | const {setSelectedTheme} = useSwitchTheme() 11 | useEffect(() => { 12 | setSelectedTheme(themes.find(theme => theme.title === localStorage.theme) || themes[2]) 13 | }, []) 14 | return ( 15 |
16 |
17 | {children} 18 |
19 |
20 | ); 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/dto/CartResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.dto; 2 | 3 | import com.fnd.games_store.cart.entity.Cart; 4 | import com.fnd.games_store.cart.entity.Game; 5 | import lombok.*; 6 | 7 | import java.util.List; 8 | import java.util.Set; 9 | 10 | @Data 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @ToString 14 | public class CartResponseDTO { 15 | 16 | 17 | private String userId; 18 | 19 | private List gameData; 20 | 21 | public CartResponseDTO(Cart cart){ 22 | this.userId = cart.getUserId(); 23 | this.gameData = cart.getGameData(); 24 | } 25 | 26 | 27 | public CartResponseDTO(CartRequestDTO cartRequestDTO){ 28 | this.userId = cartRequestDTO.getUserId(); 29 | this.gameData = cartRequestDTO.getGameData(); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /server/games-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: true 10 | validate-on-migrate: true 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | server: 27 | servlet: 28 | context-path: /games 29 | port: 8081 30 | 31 | 32 | 33 | logging: 34 | level: 35 | org.springframework.orm.jpa: DEBUG 36 | org.springframework.transaction: DEBUG -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./src/**/*.{js,jsx,ts,tsx}", 4 | ], 5 | theme: { 6 | container: { 7 | padding: { 8 | DEFAULT: "0.75rem", 9 | sm: "1rem", 10 | lg: "3rem", 11 | xl: "3rem", 12 | "2xl": "7rem" 13 | } 14 | }, 15 | fontSize:{ 16 | "2xs": ".6rem", 17 | 'xs': '.75rem', 18 | 'sm': '.875rem', 19 | 'base': '1rem', 20 | 'lg': '1.125rem', 21 | 'xl': '1.25rem', 22 | '2xl': '1.5rem', 23 | '3xl': '1.875rem', 24 | '4xl': '2.25rem', 25 | '5xl': '3rem', 26 | '6xl': '4rem', 27 | '7xl': '5rem', 28 | } 29 | }, 30 | darkMode: 'class', 31 | plugins: [], 32 | } -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/filter/TestJwtFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.filter; 2 | 3 | 4 | import com.fnd.games_store.orders.rest.UserValidationClient; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | @Slf4j 14 | public class TestJwtFilter extends JwtFilter { 15 | 16 | 17 | 18 | @Override 19 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 20 | 21 | filterChain.doFilter(request,response); 22 | log.info(("===== test_request_filtered=====")); 23 | 24 | } 25 | 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/rest/UserValidationClient.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.rest; 2 | 3 | import com.fnd.games_store.orders.dto.ValidationResponseDTO; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.RequestHeader; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | 10 | @FeignClient(name = "login-service", decode404 = true) 11 | public interface UserValidationClient { 12 | @RequestMapping(method = RequestMethod.POST, value = "login/v1/validate", consumes = "application/json", produces = "application/json") 13 | ResponseEntity validateUser(@RequestHeader("Authorization") String AuthorizationHeaderValue); 14 | } 15 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/rest/UserValidationClient.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.rest; 2 | 3 | import com.fnd.games_store.cart.dto.ValidationResponseDTO; 4 | import org.springframework.cloud.openfeign.FeignClient; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.RequestHeader; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | 10 | 11 | @FeignClient(name = "login-service", decode404 = true) 12 | public interface UserValidationClient { 13 | 14 | 15 | @RequestMapping(method = RequestMethod.POST, value = "login/v1/validate", consumes = "application/json", produces = "application/json") 16 | ResponseEntity validateUser(@RequestHeader("Authorization") String AuthorizationHeaderValue); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/filter/StaffAuthorityValidationFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.filter; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.web.filter.OncePerRequestFilter; 6 | 7 | import javax.servlet.*; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | @Slf4j 13 | public class StaffAuthorityValidationFilter extends OncePerRequestFilter { 14 | @Override 15 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 16 | 17 | log.info("==============STAFF==================="); 18 | 19 | filterChain.doFilter(request,response); 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /server/login-service/src/main/resources/db/migration/V1__create_account_and_authority_tables.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS authorities 2 | (authority_id varchar PRIMARY KEY UNIQUE NOT NULL, 3 | authority varchar(50) UNIQUE NOT NULL); 4 | 5 | 6 | CREATE TABLE IF NOT EXISTS accounts 7 | (account_id varchar PRIMARY KEY UNIQUE NOT NULL, 8 | username varchar(50) UNIQUE NOT NULL, 9 | password varchar NOT NULL, 10 | email varchar NOT NULL, 11 | expiration_date date NOT NULL, 12 | is_account_non_locked boolean NOT NULL, 13 | credentials_expiration_date date NOT NULL, 14 | is_account_enabled boolean NOT NULL); 15 | 16 | 17 | CREATE TABLE IF NOT EXISTS accounts_authorities 18 | (account_id varchar NOT NULL, 19 | authority_id varchar NOT NULL, 20 | 21 | 22 | FOREIGN KEY(account_id) REFERENCES accounts(account_id), 23 | FOREIGN KEY(authority_id) REFERENCES authorities(authority_id)); 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/exception/JwtAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.exception; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | import org.springframework.security.web.AuthenticationEntryPoint; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | @Component 13 | public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { 14 | @Override 15 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 16 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/registry-service/src/main/java/com/fnd/games_store/registry/RegistryApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.registry; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 7 | 8 | import java.net.InetAddress; 9 | 10 | @SpringBootApplication 11 | @EnableEurekaServer 12 | public class RegistryApplication implements CommandLineRunner { 13 | public static void main(String[] args) { 14 | SpringApplication.run(RegistryApplication.class, args); 15 | } 16 | 17 | @Override 18 | public void run(String... args) throws Exception { 19 | System.out.println("===================="+ InetAddress.getLocalHost().getHostAddress()+"====================="); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/repository_integration_test/TestContainerHealth.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.repository_integration_test; 2 | 3 | 4 | import com.fnd.games_store.cart.CartApplication; 5 | import com.fnd.games_store.cart.test.utilities.RepositoryTestUtilities; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import static org.junit.Assert.assertTrue; 11 | 12 | @SpringBootTest(classes = CartApplication.class) 13 | @Testcontainers(disabledWithoutDocker = true) 14 | public class TestContainerHealth extends RepositoryTestUtilities { 15 | 16 | @Test 17 | void givenRedisContainerConfiguredWithDynamicProperties_whenCheckingRunningStatus_thenStatusIsRunning() { 18 | assertTrue(REDIS_CONTAINER.isRunning()); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/entity/Genre.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.entity; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import javax.persistence.*; 12 | import java.util.List; 13 | 14 | @Entity 15 | @Table(name = "genres") 16 | @NoArgsConstructor 17 | @Getter 18 | @Setter 19 | @ToString 20 | public class Genre { 21 | 22 | @Id 23 | @GeneratedValue(generator = "uuid") 24 | @GenericGenerator(name ="uuid", strategy ="uuid2") 25 | @Column(name = "genre_id") 26 | private String id; 27 | 28 | @ManyToMany(mappedBy = "genre") 29 | @JsonIgnore 30 | private List game; 31 | 32 | @Column(name = "genre_name") 33 | private String name; 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/filter/TestJwtFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.filter; 2 | 3 | import com.fnd.games_store.cart.filter.JwtFilter; 4 | import com.fnd.games_store.cart.rest.UserValidationClient; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import javax.servlet.FilterChain; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | @Slf4j 14 | public class TestJwtFilter extends JwtFilter { 15 | 16 | 17 | 18 | 19 | @Override 20 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 21 | 22 | filterChain.doFilter(request,response); 23 | log.info(("===== test_request_filtered=====")); 24 | 25 | } 26 | 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/entity/Feature.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.*; 11 | import java.util.List; 12 | 13 | @Entity 14 | @Table(name = "features") 15 | @NoArgsConstructor 16 | @Getter 17 | @Setter 18 | @ToString 19 | public class Feature { 20 | 21 | @Id 22 | @GeneratedValue(generator = "uuid") 23 | @GenericGenerator(name ="uuid", strategy ="uuid2") 24 | @Column(name = "feature_id") 25 | private String id; 26 | 27 | @ManyToMany(mappedBy = "feature") 28 | @JsonIgnore 29 | private List game; 30 | 31 | @Column(name = "feature_name") 32 | private String name; 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/rest/AuthorityValidationClient.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.rest; 2 | 3 | 4 | import com.fnd.games_store.games.dto.ValidationResponseDTO; 5 | import org.springframework.cloud.openfeign.FeignClient; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.RequestHeader; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | 11 | @FeignClient(name = "login-service", decode404 = true) 12 | public interface AuthorityValidationClient { 13 | 14 | 15 | @RequestMapping(method = RequestMethod.POST, value = "login/v1/validation/authority", consumes = "application/json", produces = "application/json") 16 | ResponseEntity validateUserWithAuthority(@RequestHeader("Authorization") String AuthorizationHeaderValue); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/entity/Platform.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import lombok.ToString; 8 | import org.hibernate.annotations.GenericGenerator; 9 | 10 | import javax.persistence.*; 11 | import java.util.List; 12 | 13 | @Entity 14 | @Table(name = "platforms") 15 | @NoArgsConstructor 16 | @Getter 17 | @Setter 18 | @ToString 19 | public class Platform { 20 | 21 | @Id 22 | @GeneratedValue(generator = "uuid") 23 | @GenericGenerator(name ="uuid", strategy ="uuid2") 24 | @Column(name = "platform_id") 25 | private String id; 26 | 27 | @ManyToMany(mappedBy = "platform") 28 | @JsonIgnore 29 | private List game; 30 | 31 | @Column(name = "platform_name") 32 | private String name; 33 | 34 | 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/login-service/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: true 10 | validate-on-migrate: true 11 | database: POSTGRESQL 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | platform: postgres 18 | url: jdbc:postgresql://localhost:5432/login_service_database 19 | username: postgres 20 | password: root 21 | driver-class-name: org.postgresql.Driver 22 | 23 | server: 24 | servlet: 25 | context-path: /login 26 | port: 8082 27 | 28 | variables: 29 | security: 30 | access_secret: OTFBRjg3MUMwOUYxODlFNDc3NEQwNTAxMTg0M0M0NzI5QTczNDY1QUU4MTA5MUFERjZFNDIxNTk0QTZDOUYyQg== 31 | access_expiration: 90000000 32 | common: 33 | new_account_expiration_date: 2030-01-01 -------------------------------------------------------------------------------- /server/orders-service/src/main/resources/application-standalone.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | # cloud: 3 | # config: 4 | # enabled: false 5 | # discovery: 6 | # enabled: false 7 | jpa: 8 | flyway: 9 | enabled: false 10 | validate-on-migrate: false 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | 27 | 28 | server: 29 | servlet: 30 | context-path: /orders 31 | port: 8084 32 | 33 | 34 | variables: 35 | kafka: 36 | port: 9092 37 | 38 | 39 | logging: 40 | level: 41 | org.springframework.orm.jpa: DEBUG 42 | org.springframework.transaction: DEBUG 43 | -------------------------------------------------------------------------------- /server/orders-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: false 10 | validate-on-migrate: false 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | 27 | 28 | server: 29 | servlet: 30 | context-path: /orders 31 | port: 8084 32 | 33 | 34 | variables: 35 | kafka: 36 | bootstrap_address: localhost:9092 37 | 38 | logging: 39 | level: 40 | org.springframework.orm.jpa: DEBUG 41 | org.springframework.transaction: DEBUG -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/aspects/Benchmarking.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.aspects; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.time.Duration; 10 | import java.time.Instant; 11 | 12 | @Component 13 | @Aspect 14 | @Slf4j 15 | public class Benchmarking { 16 | 17 | 18 | @Around("@annotation(com.fnd.games_store.games.annotations.Benchmarked)") 19 | public Object performTImeMeasure(ProceedingJoinPoint joinPoint) throws Throwable { 20 | 21 | Instant startTime = Instant.now(); 22 | 23 | Object proceed = joinPoint.proceed(); 24 | 25 | Instant endTime = Instant.now(); 26 | 27 | log.info(Duration.between(endTime,startTime).toString()); 28 | 29 | return proceed; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /server/login-service/src/main/resources/db/migration/V2__insert_users.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO authorities 2 | (authority_id, authority) 3 | VALUES 4 | ('1', 'super_user'), 5 | ('2', 'staff_user'), 6 | ('3', 'regular_user'); 7 | 8 | INSERT INTO accounts 9 | (account_id, username, password, email, expiration_date, is_account_non_locked, credentials_expiration_date, is_account_enabled) 10 | VALUES 11 | ('1', 'admin', '$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6', 'admin@gmail.com', '2030-01-01', true, '2030-01-01', true), 12 | ('2', 'staff', '$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6', 'staff@gmail.com', '2030-01-01', true, '2030-01-01', true), 13 | ('3', 'regular', '$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6', 'regular@gmail.com', '2030-01-01', true, '2030-01-01', true); 14 | 15 | INSERT INTO accounts_authorities 16 | (account_id, authority_id) 17 | VALUES 18 | ('1','1'), 19 | ('1','2'), 20 | ('1','3'), 21 | ('2','2'), 22 | ('2','3'), 23 | ('3','3'); -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/entity/Publisher.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.hibernate.annotations.GenericGenerator; 8 | 9 | import javax.persistence.*; 10 | import java.util.List; 11 | 12 | @Entity 13 | @Getter 14 | @Setter 15 | @NoArgsConstructor 16 | @Table(name = "publishers") 17 | public class Publisher { 18 | 19 | @Id 20 | @GeneratedValue(generator = "uuid") 21 | @GenericGenerator(name ="uuid", strategy ="uuid2") 22 | @Column(name = "publisher_id") 23 | private String id; 24 | 25 | @Column(name = "publisher_name") 26 | private String name; 27 | 28 | 29 | @OneToMany(mappedBy = "publisher", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) 30 | @JsonIgnore 31 | private List game; 32 | 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /client/src/components/AppRouter.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Routes, Route} from "react-router-dom"; 3 | import {authorizedRoutes, notAuthorizedRoutes} from "../constants/routes"; 4 | import {SimpleLayout} from "./layouts/SimpleLayout"; 5 | import {useAppSelector} from "../hooks/redux"; 6 | 7 | export const AppRouter = () => { 8 | const {isAuth} = useAppSelector(state => state.auth) 9 | let routes 10 | if (isAuth) routes = authorizedRoutes 11 | else routes = notAuthorizedRoutes 12 | return ( 13 | 14 | {routes.map((route) => ( 15 | }> 19 | {route.element} 20 | 21 | } 22 | key={route.path}/> 23 | ))} 24 | 25 | ); 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /server/config-service/src/main/java/com/fnd/games_store/config/ConfigApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.config; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | import org.springframework.cloud.config.server.EnableConfigServer; 8 | 9 | import java.net.InetAddress; 10 | 11 | @SpringBootApplication 12 | @EnableConfigServer 13 | @EnableDiscoveryClient 14 | public class ConfigApplication implements CommandLineRunner { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(ConfigApplication.class, args); 18 | } 19 | 20 | @Override 21 | public void run(String... args) throws Exception { 22 | System.out.println("===================="+ InetAddress.getLocalHost().getHostAddress()+"====================="); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/LoginApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login; 2 | 3 | 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 8 | import org.springframework.cloud.openfeign.EnableFeignClients; 9 | 10 | import java.net.InetAddress; 11 | 12 | @SpringBootApplication 13 | @EnableDiscoveryClient 14 | @EnableFeignClients 15 | public class LoginApplication implements CommandLineRunner { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(LoginApplication.class, args); 19 | 20 | } 21 | 22 | 23 | @Override 24 | public void run(String... args) throws Exception { 25 | System.out.println("===================="+ InetAddress.getLocalHost().getHostAddress()+"====================="); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/service/implementation/ValidationServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.service.implementation; 2 | 3 | import com.fnd.games_store.login.jwt_utils.JwtValidator; 4 | import com.fnd.games_store.login.service.ValidationService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | @Service 11 | @Slf4j 12 | public class ValidationServiceImpl implements ValidationService { 13 | 14 | private final JwtValidator jwtValidator; 15 | 16 | @Autowired 17 | public ValidationServiceImpl(JwtValidator jwtValidator) { 18 | this.jwtValidator = jwtValidator; 19 | } 20 | 21 | @Override 22 | public Boolean validate(String token) { 23 | log.info(token); 24 | return jwtValidator.validateJwtToken(token.substring(7)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/entity/Developer.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.sun.istack.NotNull; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import lombok.ToString; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import javax.persistence.*; 12 | import java.util.List; 13 | 14 | @Entity 15 | @Table(name = "developers") 16 | @NoArgsConstructor 17 | @Getter 18 | @Setter 19 | @ToString 20 | public class Developer { 21 | 22 | @NotNull 23 | @Id 24 | @GeneratedValue(generator = "uuid") 25 | @GenericGenerator(name ="uuid", strategy ="uuid2") 26 | @Column(name = "developer_id") 27 | private String id; 28 | 29 | @Column(name = "developer_name") 30 | private String name; 31 | 32 | @OneToMany(mappedBy = "developer", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) 33 | @JsonIgnore 34 | private List game; 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /client/src/services/CartService.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | import { IGame } from './../models/IGame'; 3 | import $api from '../http/api'; 4 | 5 | export interface accessCartProps { 6 | userId: string; 7 | } 8 | 9 | export interface updateCartGamesProps extends accessCartProps { 10 | games: IGame[]; 11 | } 12 | 13 | export interface deleteCartGameProps extends accessCartProps { 14 | games: IGame[]; 15 | gameToDelete: IGame; 16 | } 17 | 18 | export class CartService { 19 | static fetchCartGames({ 20 | userId, 21 | }: accessCartProps): Promise> { 22 | userId = 'user' + userId; 23 | return $api.post('cart/v1/content', { userId }); 24 | } 25 | static updateCartGames({ 26 | userId, 27 | games, 28 | }: updateCartGamesProps): Promise> { 29 | userId = 'user' + userId; 30 | const bodyParams = { 31 | userId, 32 | gameData: games, 33 | }; 34 | return $api.post('cart/v1/update', bodyParams); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/components/UI/Input.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | 3 | interface CustomInputProps { 4 | value?: string | number; 5 | onChange?: (arg0: React.FormEvent) => any; 6 | type: string; 7 | placeholder?: string; 8 | className?: string 9 | label?: string 10 | autoComplete?: "new-password" | "username" | "password" | "no" | "on" 11 | onFocus?: () => any 12 | required?: boolean 13 | } 14 | 15 | export const Input: FC = ({className, label, required, ...props}) => { 16 | const rootClasses = ["px-2 outline-none w-full dark:text-slate-200 text-sm", className].join(" ") 17 | return ( 18 | <> 19 | {label 20 | ? 25 | : 26 | } 27 | 28 | ) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /client/src/store/reducers/gamesSlice.ts: -------------------------------------------------------------------------------- 1 | import {IGame} from "../../models/IGame"; 2 | import {createSlice} from "@reduxjs/toolkit"; 3 | import {fetchGames} from "../actions/gamesAction"; 4 | 5 | interface GamesState { 6 | games: IGame[], 7 | loading: "idle" | "pending" | "succeeded" | "failed", 8 | error: string 9 | } 10 | 11 | const initialState: GamesState = { 12 | games: [], 13 | loading: "idle", 14 | error: "" 15 | } 16 | 17 | export const gamesSlice = createSlice({ 18 | name: "games", 19 | initialState, 20 | reducers: { 21 | }, 22 | extraReducers:(builder) =>{ 23 | builder.addCase(fetchGames.pending, (state) => { 24 | state.loading = "pending" 25 | }) 26 | .addCase(fetchGames.fulfilled, (state, action) => { 27 | state.loading = "succeeded" 28 | state.games = action.payload 29 | }) 30 | .addCase(fetchGames.rejected, (state, action) => { 31 | state.loading = "failed" 32 | console.log(action.error) 33 | }) 34 | } 35 | }) 36 | 37 | 38 | export default gamesSlice.reducer -------------------------------------------------------------------------------- /client/src/store/actions/cartAction.ts: -------------------------------------------------------------------------------- 1 | import { IGame } from './../../models/IGame'; 2 | import { 3 | CartService, 4 | accessCartProps, 5 | deleteCartGameProps, 6 | updateCartGamesProps, 7 | } from './../../services/CartService'; 8 | import { createAsyncThunk } from '@reduxjs/toolkit'; 9 | 10 | export const fetchCartGames = createAsyncThunk( 11 | 'cart/fetchGames', 12 | async (props: accessCartProps) => { 13 | const res = await CartService.fetchCartGames(props); 14 | return res.data; 15 | } 16 | ); 17 | 18 | export const updateCartGames = createAsyncThunk( 19 | 'cart/updateGames', 20 | async (props: updateCartGamesProps) => { 21 | const res = await CartService.updateCartGames(props); 22 | return res.data; 23 | } 24 | ); 25 | 26 | export const deleteCartGame = createAsyncThunk( 27 | 'cart/deleteGame', 28 | async (props: deleteCartGameProps) => { 29 | props.games = props.games.filter( 30 | (game) => game.id !== props.gameToDelete.id 31 | ); 32 | const res = await CartService.updateCartGames(props); 33 | return res.data; 34 | } 35 | ); 36 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/CartApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart; 2 | 3 | 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; 8 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 9 | import org.springframework.cloud.openfeign.EnableFeignClients; 10 | 11 | import java.net.InetAddress; 12 | 13 | 14 | @SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) 15 | @EnableDiscoveryClient 16 | @EnableFeignClients 17 | public class CartApplication implements CommandLineRunner { 18 | 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(CartApplication.class,args); 22 | } 23 | 24 | @Override 25 | public void run(String... args) throws Exception { 26 | System.out.println("===================="+ InetAddress.getLocalHost().getHostAddress()+"====================="); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /server/gateway-service/src/main/resources/application-local.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | gateway: 8 | routes: 9 | - id: games_service 10 | uri: http://localhost:8081/games 11 | predicates: 12 | - Path=/games/getAll 13 | - id: games_service 14 | uri: http://localhost:8081/games 15 | predicates: 16 | - Path=/games/get/** 17 | - id: cart_service 18 | uri: http://localhost:8083/cart 19 | predicates: 20 | - Path=/cart/v1/update 21 | - id: cart_service 22 | uri: http://localhost:8083/cart 23 | predicates: 24 | - Path=/cart/v1/getContent 25 | - id: cart_service 26 | uri: http://localhost:8083/cart 27 | predicates: 28 | - Path=/cart/v1/orders 29 | - id: login_service 30 | uri: http://localhost:8082/login 31 | predicates: 32 | - Path=/login/v1/login 33 | 34 | eureka: 35 | instance: 36 | preferIpAddress: true 37 | client: 38 | service-url: 39 | defaultZone: http://localhost:8761/eureka/ -------------------------------------------------------------------------------- /client/src/pages/Error.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link} from "react-router-dom"; 3 | import {SimpleLayout} from "../components/layouts/SimpleLayout"; 4 | import {Footer} from "../components/Footer"; 5 | import {PaperAirplaneIcon, ShieldCheckIcon} from "@heroicons/react/20/solid"; 6 | import {NoSymbolIcon} from "@heroicons/react/24/outline"; 7 | 8 | const Error = () => { 9 | return ( 10 | 11 |
12 | 13 |
Something went wrong, please
14 | 15 | 18 | go back to safety 19 | 20 | 21 | 22 |
23 |
24 | 25 | ); 26 | }; 27 | 28 | export default Error 29 | 30 | -------------------------------------------------------------------------------- /client/src/constants/filter.ts: -------------------------------------------------------------------------------- 1 | import {filterField} from "../types/filter"; 2 | 3 | export const fields: filterField[] = [ 4 | {id: 1, title: 'name'}, 5 | {id: 2, title: 'discount'}, 6 | {id: 3, title: 'releaseDate'}, 7 | {id: 4, title: 'platform'}, 8 | {id: 5, title: 'price'}, 9 | ] 10 | 11 | export const pages: filterField[] = [ 12 | {id: 1, title: "1"}, 13 | {id: 2, title: "2"}, 14 | {id: 3, title: "3"}, 15 | {id: 4, title: "4"}, 16 | {id: 5, title: "5"}, 17 | ] 18 | 19 | export const orders: filterField[] = [ 20 | {id: 1, title: "direct"}, 21 | {id: 2, title: "reverse"}, 22 | ] 23 | 24 | export const pageSizes: filterField[] = [ 25 | {id: 1, title: "5"}, 26 | {id: 2, title: "10"}, 27 | {id: 3, title: "20"}, 28 | ] 29 | 30 | // const defaultFilter = { 31 | // page: pages[0], 32 | // pageSize: pageSizes[1], 33 | // sortField: fields[0], 34 | // ascOrder: orders[0] 35 | // } 36 | 37 | export const defaultFilterTitles = { 38 | page: +pages[0].title - 1, 39 | pageSize: +pageSizes[1].title, 40 | sortField: fields[0].title, 41 | ascOrder: orders[0].title === "direct" 42 | } 43 | -------------------------------------------------------------------------------- /server/registry-service/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.fnd.games_store' 8 | version = '1.0' 9 | sourceCompatibility = '1.8' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | jar { 16 | manifest { 17 | attributes( 18 | 'Main-Class': 'com.fnd.games_store.games.RegistryApplication' 19 | ) 20 | 21 | } 22 | } 23 | 24 | ext { 25 | set('springCloudVersion', "2021.0.3") 26 | } 27 | 28 | dependencies { 29 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' 30 | implementation 'org.springframework.boot:spring-boot-starter-web' 31 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 32 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 33 | } 34 | 35 | dependencyManagement { 36 | imports { 37 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 38 | } 39 | } 40 | 41 | //tasks.named('test') { 42 | // useJUnitPlatform() 43 | //} -------------------------------------------------------------------------------- /server/cart-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8083 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar cart-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8083/cart/actuator/health || exit 1 16 | ENTRYPOINT ["java", "-jar", "cart-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8083 29 | #COPY build/libs/cart-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=5 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8083/cart/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java", "-jar", "cart-service-1.0.jar"] -------------------------------------------------------------------------------- /server/config-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8001 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar config-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8001/actuator/health || exit 1 16 | ENTRYPOINT ["java", "-jar", "config-service-1.0.jar"] 17 | 18 | 19 | #Separate gradle build 20 | 21 | #FROM alpine:3.14 22 | # 23 | #RUN apk update \ 24 | # && apk upgrade \ 25 | # && apk add --update openjdk8 tzdata curl unzip bash \ 26 | # && rm -rf /var/cache/apk/* 27 | # 28 | # 29 | #EXPOSE 8001 30 | #COPY build/libs/config-service-1.0.jar . 31 | #HEALTHCHECK --interval=30s --timeout=6s --retries=5 \ 32 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8001/actuator/health || exit 1 33 | # 34 | #ENTRYPOINT ["java", "-jar", "config-service-1.0.jar"] -------------------------------------------------------------------------------- /server/games-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8081 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar games-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8081/games/actuator/health || exit 1 16 | ENTRYPOINT ["java", "-jar", "games-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8081 29 | #COPY build/libs/games-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8081/games/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java", "-jar", "games-service-1.0.jar"] -------------------------------------------------------------------------------- /server/gateway-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8080 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar gateway-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 16 | ENTRYPOINT ["java","-jar","/gateway-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8080 29 | #COPY build/libs/gateway-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=5 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java", "-jar", "gateway-service-1.0.jar"] -------------------------------------------------------------------------------- /server/login-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8082 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar login-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8082/login/actuator/health || exit 1 16 | ENTRYPOINT ["java", "-jar", "login-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8082 29 | #COPY build/libs/login-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8082/login/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java", "-jar", "login-service-1.0.jar"] -------------------------------------------------------------------------------- /server/registry-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8761 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar registry-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8761/actuator/health || exit 1 16 | ENTRYPOINT ["java","-jar","/registry-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8001 29 | #COPY build/libs/config-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=5 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8761/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java","-jar","/registry-service-1.0.jar"] -------------------------------------------------------------------------------- /server/orders-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:jdk8 AS APPLICATION_BUILD_STAGE 2 | COPY --chown=gradle:gradle . /home/gradle 3 | WORKDIR /home/gradle 4 | RUN gradle build || return 1 5 | 6 | 7 | FROM alpine:3.14 AS IMAGE_BUILD_STAGE 8 | RUN apk update \ 9 | && apk upgrade \ 10 | && apk add --update openjdk8 tzdata curl unzip bash \ 11 | && rm -rf /var/cache/apk/* \ 12 | EXPOSE 8084 13 | COPY --from=APPLICATION_BUILD_STAGE /home/gradle/build/libs/*.jar orders-service-1.0.jar 14 | HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 15 | CMD wget --no-verbose --tries=1 --spider http://localhost:8084/orders/actuator/health || exit 1 16 | ENTRYPOINT ["java", "-jar", "orders-service-1.0.jar"] 17 | 18 | #Separate gradle build 19 | 20 | #FROM alpine:3.14 21 | # 22 | #RUN apk update \ 23 | # && apk upgrade \ 24 | # && apk add --update openjdk8 tzdata curl unzip bash \ 25 | # && rm -rf /var/cache/apk/* 26 | # 27 | # 28 | #EXPOSE 8084 29 | #COPY build/libs/orders-service-1.0.jar . 30 | #HEALTHCHECK --interval=30s --timeout=6s --retries=3 \ 31 | # CMD wget --no-verbose --tries=1 --spider http://localhost:8084/orders/actuator/health || exit 1 32 | # 33 | #ENTRYPOINT ["java", "-jar", "orders-service-1.0.jar"] -------------------------------------------------------------------------------- /client/src/components/layouts/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect } from 'react'; 2 | import { Header } from '../Header/Header'; 3 | import { SmHeader } from '../Header/SmHeader'; 4 | import { Footer } from '../Footer'; 5 | 6 | interface MainLayoutProps { 7 | children?: React.ReactNode; 8 | } 9 | 10 | export const MainLayout: FC = ({ children }) => { 11 | useEffect(() => { 12 | // если перейти по ссылке, то сразу убрать блокировку скролла 13 | // т.к. она остаётся при переходе по ссылке из меню для маленьких экранов 14 | // и убирается если кликнуть на страничку, на которой уже находится пользователь 15 | document.documentElement.style.overflow = ''; 16 | }, []); 17 | return ( 18 |
19 |
20 | 21 |
22 |
23 |
{children}
24 |
25 |
26 |
27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/GamesApplication.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; 7 | import org.springframework.cache.annotation.EnableCaching; 8 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 9 | import org.springframework.cloud.openfeign.EnableFeignClients; 10 | 11 | import java.net.InetAddress; 12 | 13 | 14 | @SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) 15 | @EnableDiscoveryClient 16 | @EnableCaching 17 | @EnableFeignClients 18 | public class GamesApplication implements CommandLineRunner { 19 | 20 | public static void main(String[] args) { 21 | SpringApplication.run(GamesApplication.class, args); 22 | } 23 | 24 | 25 | @Override 26 | public void run(String... args) throws Exception { 27 | System.out.println("===================="+ InetAddress.getLocalHost().getHostAddress()+"====================="); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/store/reducers/gameSlice.ts: -------------------------------------------------------------------------------- 1 | import {IGame} from "../../models/IGame"; 2 | import {createSlice} from "@reduxjs/toolkit"; 3 | import {fetchOneGame} from "../actions/gameAction"; 4 | 5 | interface gameState { 6 | game: IGame 7 | loading: "idle" | "pending" | "succeeded" | "failed" 8 | error: string 9 | } 10 | 11 | const initialState: gameState = { 12 | game: {} as IGame, 13 | loading: "idle", 14 | error: "" 15 | } 16 | 17 | const gameSlice = createSlice({ 18 | name: "game", 19 | initialState, 20 | reducers: { 21 | clearGameData:(state) => { 22 | state.game = initialState.game 23 | } 24 | }, 25 | extraReducers: (builder) => { 26 | builder.addCase(fetchOneGame.pending, (state) => { 27 | state.loading = "pending" 28 | }).addCase(fetchOneGame.fulfilled, (state, action) => { 29 | state.loading = "succeeded" 30 | state.game = action.payload 31 | }).addCase(fetchOneGame.rejected, (state, action) => { 32 | state.loading = "failed" 33 | console.log(action.error) 34 | }) 35 | } 36 | }) 37 | 38 | export default gameSlice.reducer 39 | 40 | export const {clearGameData} = gameSlice.actions -------------------------------------------------------------------------------- /server/config-service/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.fnd.games_store' 8 | version = '1.0' 9 | sourceCompatibility = '1.8' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | jar { 16 | manifest { 17 | attributes( 18 | 'Main-Class': 'com.fnd.games_store.games.ConfigApplication' 19 | ) 20 | 21 | } 22 | } 23 | 24 | 25 | ext { 26 | set('springCloudVersion', "2021.0.3") 27 | } 28 | 29 | dependencies { 30 | implementation 'org.springframework.boot:spring-boot-starter' 31 | implementation 'org.springframework.cloud:spring-cloud-config-server' 32 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' 33 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 34 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 35 | } 36 | 37 | dependencyManagement { 38 | imports { 39 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 40 | } 41 | } 42 | 43 | tasks.named('test') { 44 | useJUnitPlatform() 45 | } -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/controller/implementation/OrderControllerImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.controller.implementation; 2 | 3 | import com.fnd.games_store.orders.controller.OrderController; 4 | import com.fnd.games_store.orders.dto.OrderResponseDTO; 5 | import com.fnd.games_store.orders.service.OrderFetcher; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.List; 13 | 14 | 15 | @RestController 16 | public class OrderControllerImpl implements OrderController { 17 | 18 | private final OrderFetcher service; 19 | 20 | @Autowired 21 | public OrderControllerImpl(OrderFetcher service) { 22 | this.service = service; 23 | } 24 | 25 | 26 | @Override 27 | @GetMapping("/v1/purchases/{userId}") 28 | public ResponseEntity> getOrderData(@PathVariable String userId) { 29 | return ResponseEntity.ok(service.fetchOrderData(userId)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/controller/implementation/SpecificGameControllerImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.controller.implementation; 2 | 3 | import com.fnd.games_store.games.controller.SpecificGameController; 4 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 5 | import com.fnd.games_store.games.service.SpecificGameService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | public class SpecificGameControllerImpl implements SpecificGameController { 14 | 15 | 16 | 17 | private final SpecificGameService service; 18 | 19 | @Autowired 20 | public SpecificGameControllerImpl(SpecificGameService service) { 21 | this.service = service; 22 | } 23 | 24 | @Override 25 | @GetMapping("/v1/catalogue/{name}") 26 | public ResponseEntity getSpecifiedGame(@PathVariable String name) { 27 | return ResponseEntity.ok(service.getGameByName(name)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/src/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {images} from "../constants/images"; 3 | 4 | export const Footer = () => { 5 | return ( 6 |
7 |
8 |
9 | Copyright © 2023 Friendly neighborhood development 10 |
11 |
12 | Follow us on Github 13 | 14 | 15 | {"github 16 | 17 | 18 |
19 |
20 |
21 | ); 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /client/src/components/games/game/GameCard.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC, memo} from 'react'; 2 | import {Link} from "react-router-dom"; 3 | import {GamePrice} from "./GamePrice"; 4 | import {IGame} from "../../../models/IGame"; 5 | 6 | 7 | export const GameCard: FC = memo(({name, base64Image, price, discount}) => { 8 | return ( 9 | 11 |
13 | {"game 14 |
15 |
16 |
{name}
18 | 19 |
20 | 21 | ); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /client/src/components/Header/HeaderIcon.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | import {Link} from "react-router-dom"; 3 | 4 | interface NavbarIconProps { 5 | children: React.ReactNode, 6 | link?: string, 7 | onClick?: (arg0:any) => void 8 | } 9 | 10 | export const HeaderIcon: FC = ({children, link, ...props}) => { 11 | return ( 12 | <> 13 | {link ? 14 | 15 | 18 | {children} 19 | 20 | : 21 | 24 | {children} 25 | 26 | } 27 | 28 | 29 | 30 | ); 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /client/src/components/ThemeSwitcher.tsx: -------------------------------------------------------------------------------- 1 | import React, {memo, useEffect} from 'react'; 2 | import {ListBox} from "./UI/ListBox"; 3 | import {MoonIcon, SunIcon} from "@heroicons/react/24/outline"; 4 | import {useSwitchTheme} from "../hooks/useThemeSwitch"; 5 | import {themes} from "../constants/themes"; 6 | 7 | export const ThemeSwitcher = memo(() => { 8 | const {selectedTheme, setSelectedTheme} = useSwitchTheme() 9 | useEffect(() => { 10 | setSelectedTheme(themes.find(theme => theme.title === localStorage.theme) || themes[2]) 11 | }, []) 12 | return ( 13 |
14 | 15 | {localStorage.theme} 16 | {localStorage.theme === "system" 17 | ? window.matchMedia('(prefers-color-scheme: dark)').matches 18 | ? 19 | : 20 | : 21 | } 22 | 23 |
24 | 25 | ); 26 | }); 27 | 28 | export default ThemeSwitcher; -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/entity/Game.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.entity; 2 | 3 | import lombok.*; 4 | 5 | import java.io.Serializable; 6 | import java.math.BigDecimal; 7 | import java.util.Objects; 8 | 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | public class Game implements Serializable { 14 | 15 | private String id; 16 | private String name; 17 | private String releaseDate; 18 | private BigDecimal price; 19 | private BigDecimal discount; 20 | private String description; 21 | private String base64Image; 22 | 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | Game game = (Game) o; 29 | return Objects.equals(id, game.id) && Objects.equals(name, game.name) && Objects.equals(releaseDate, game.releaseDate) && Objects.equals(price, game.price) && Objects.equals(discount, game.discount) && Objects.equals(description, game.description) && Objects.equals(base64Image, game.base64Image); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return Objects.hash(id, name, releaseDate, price, discount, description, base64Image); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/implementation/RegistrationController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller.implementation; 2 | 3 | 4 | import com.fnd.games_store.login.controller.UserRegistration; 5 | import com.fnd.games_store.login.dto.AccountRequestDTO; 6 | import com.fnd.games_store.login.dto.AccountResponseDTO; 7 | import com.fnd.games_store.login.service.AccountRegistration; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | public class RegistrationController implements UserRegistration { 16 | 17 | 18 | private final AccountRegistration service; 19 | 20 | @Autowired 21 | public RegistrationController(AccountRegistration service) { 22 | this.service = service; 23 | } 24 | 25 | @Override 26 | @PostMapping("/v1/registration") 27 | public ResponseEntity registerNewUser(@RequestBody AccountRequestDTO accountRequestDTO) { 28 | return ResponseEntity.ok(service.register(accountRequestDTO)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/controller/implementation/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.controller.implementation; 2 | 3 | 4 | import com.fnd.games_store.cart.controller.OrderProcessor; 5 | import com.fnd.games_store.cart.dto.CartResponseDTO; 6 | import com.fnd.games_store.cart.dto.OrderResponseDTO; 7 | import com.fnd.games_store.cart.service.OrderProcessingService; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @Slf4j 17 | public class OrderController implements OrderProcessor{ 18 | 19 | 20 | private final OrderProcessingService service; 21 | 22 | @Autowired 23 | public OrderController(OrderProcessingService service) { 24 | this.service = service; 25 | } 26 | 27 | 28 | @Override 29 | @PostMapping("/v1/purchase/{userId}") 30 | public ResponseEntity processOrder(@PathVariable String userId) { 31 | 32 | log.info(userId); 33 | 34 | return ResponseEntity.ok(service.purchaseGames(userId)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/filter/AdminAuthorityValidationFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.filter; 2 | 3 | 4 | import com.fnd.games_store.games.rest.AuthorityValidationClient; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.filter.OncePerRequestFilter; 9 | 10 | import javax.servlet.FilterChain; 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | import javax.servlet.http.HttpServletResponse; 14 | import java.io.IOException; 15 | @Slf4j 16 | public class AdminAuthorityValidationFilter extends OncePerRequestFilter { 17 | 18 | 19 | // @Autowired 20 | // private AuthorityValidationClient authorityValidationClient; 21 | 22 | public AdminAuthorityValidationFilter() { 23 | } 24 | 25 | @Override 26 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 27 | 28 | 29 | // authorityValidationClient.validateUserWithAuthority(request.getHeader("authorization")); 30 | 31 | // log.info(request.getHeader("authorization")); 32 | 33 | filterChain.doFilter(request,response); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/filter/JwtFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.filter; 2 | 3 | import com.fnd.games_store.orders.exception.UserValidationFailedException; 4 | import com.fnd.games_store.orders.rest.UserValidationClient; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.filter.OncePerRequestFilter; 8 | 9 | import javax.servlet.FilterChain; 10 | import javax.servlet.ServletException; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | 15 | @Slf4j 16 | public class JwtFilter extends OncePerRequestFilter { 17 | 18 | @Autowired 19 | private UserValidationClient userValidator; 20 | 21 | @Override 22 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 23 | 24 | String token = request.getHeader("authorization"); 25 | 26 | Boolean isUserValid = userValidator.validateUser(token).getBody().getIsTokenValid(); 27 | 28 | if (isUserValid){ 29 | filterChain.doFilter(request, response); 30 | } else throw new UserValidationFailedException("Failed to validate user"); 31 | 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/games-service/src/main/resources/application-standalone.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: false 10 | validate-on-migrate: false 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | server: 27 | servlet: 28 | context-path: /games 29 | port: 8081 30 | 31 | 32 | #spring: 33 | # jpa: 34 | # flyway: 35 | # enabled: true 36 | # validate-on-migrate: true 37 | # database: POSTGRESQL 38 | # properties.hibernate.temp.use_jdbc_metadata_defaults: false 39 | # hibernate: 40 | # ddl-auto: none 41 | # properties: 42 | # datasource: 43 | # platform: postgres 44 | # url: jdbc:postgresql://localhost:5432/games_service_database 45 | # username: postgres 46 | # password: root 47 | # driver-class-name: org.postgresql.Driver 48 | # activate: 49 | # on-profile: "dev" 50 | #server: 51 | # servlet: 52 | # context-path: /games 53 | # port: 8081 -------------------------------------------------------------------------------- /client/src/constants/routes.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Navigate} from "react-router-dom"; 3 | 4 | // lazy loading for pages 5 | const Games = React.lazy(() => import("../pages/Games")); 6 | const GameName = React.lazy(() => import("../pages/GameName")); 7 | const Login = React.lazy(() => import("../pages/Login")); 8 | const Signup = React.lazy(() => import("../pages/Signup")); 9 | const Cart = React.lazy(() => import("../pages/Cart")); 10 | const Error = React.lazy(() => import("../pages/Error")); 11 | 12 | interface routeProps { 13 | path: string; 14 | element: React.ReactElement; 15 | exact?: boolean; 16 | } 17 | 18 | export const authorizedRoutes: Array = [ 19 | {path: "/", element: }, 20 | {path: "/games/:name", element: }, 21 | {path: "/cart", element: }, 22 | {path: "/error", element: }, 23 | {path: "/login", element: }, 24 | {path: "*", element: }, 25 | ]; 26 | 27 | export const notAuthorizedRoutes: Array = [ 28 | {path: "/", element: }, 29 | {path: "/games/:name", element: }, 30 | {path: "/login", element: }, 31 | {path: "/signup", element: }, 32 | {path: "/cart", element: }, 33 | {path: "/error", element: }, 34 | {path: "*", element: }, 35 | ]; 36 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/entity/Cart.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.entity; 2 | 3 | 4 | import com.fnd.games_store.cart.dto.CartResponseDTO; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | import lombok.ToString; 9 | import org.springframework.data.annotation.Id; 10 | import org.springframework.data.redis.core.RedisHash; 11 | 12 | import java.io.Serializable; 13 | import java.math.BigDecimal; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Objects; 17 | import java.util.Set; 18 | 19 | @Data 20 | @AllArgsConstructor 21 | @ToString 22 | @RedisHash("Cart") 23 | public class Cart implements Serializable { 24 | 25 | @Id 26 | private String userId; 27 | 28 | private List gameData; 29 | 30 | 31 | public Cart() { 32 | 33 | this.userId = ""; 34 | this.gameData = new ArrayList<>(); 35 | 36 | } 37 | 38 | @Override 39 | public boolean equals(Object o) { 40 | if (this == o) return true; 41 | if (o == null || getClass() != o.getClass()) return false; 42 | Cart cart = (Cart) o; 43 | return Objects.equals(userId, cart.userId) && Objects.equals(gameData, cart.gameData); 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | return Objects.hash(userId, gameData); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /client/src/components/games/game/GamePrice.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | 3 | interface GamePriceProps { 4 | price: number 5 | discount: number 6 | } 7 | 8 | export const GamePrice: FC = ({price, discount}) => { 9 | return ( 10 |
11 | {price === 0 12 | ?
Бесплатно
13 | : (discount 14 | ?
15 | 18 | -{discount}% 19 | 20 |
21 |
{price} ₽
22 |
23 | {+(price - price * discount / 100).toFixed(2)} ₽ 24 |
25 |
26 |
27 | :
{price} ₽
)} 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/implementation/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller.implementation; 2 | 3 | 4 | import com.fnd.games_store.login.controller.UserLogin; 5 | import com.fnd.games_store.login.dto.LoginRequestDTO; 6 | import com.fnd.games_store.login.dto.LoginResponseDTO; 7 | 8 | import com.fnd.games_store.login.entity.Account; 9 | import com.fnd.games_store.login.repository.AccountRepository; 10 | import com.fnd.games_store.login.service.LoginService; 11 | import com.netflix.discovery.converters.Auto; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.http.ResponseEntity; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import java.util.List; 18 | 19 | @RestController 20 | @Slf4j 21 | public class LoginController implements UserLogin { 22 | 23 | 24 | private final LoginService loginService; 25 | 26 | @Autowired 27 | public LoginController(LoginService loginService) { 28 | this.loginService = loginService; 29 | } 30 | 31 | @Override 32 | @PostMapping("/v1/authorization") 33 | public ResponseEntity login(@RequestBody LoginRequestDTO loginRequestDTO) { 34 | return ResponseEntity.ok(loginService.login(loginRequestDTO.getUsername(), loginRequestDTO.getPassword())); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/configuration/RedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.configuration; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.data.redis.connection.RedisStandaloneConfiguration; 7 | import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; 8 | import org.springframework.data.redis.core.RedisTemplate; 9 | 10 | @Configuration 11 | public class RedisConfiguration { 12 | 13 | @Value("${variables.redis.host}") 14 | private String redisHost; 15 | 16 | @Value("${variables.redis.port}") 17 | private int redisPort; 18 | 19 | @Bean 20 | public LettuceConnectionFactory connectionFactory() { 21 | 22 | RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(); 23 | 24 | redisConfiguration.setPort(redisPort); 25 | redisConfiguration.setHostName(redisHost); 26 | 27 | return new LettuceConnectionFactory(redisConfiguration); 28 | } 29 | 30 | 31 | @Bean 32 | public RedisTemplate redisTemplate() { 33 | RedisTemplate template = new RedisTemplate<>(); 34 | template.setConnectionFactory(connectionFactory()); 35 | return template; 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /client/src/components/games/game/GameCardLoader.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC} from 'react'; 2 | 3 | export const GameCardLoader: FC = () => { 4 | return ( 5 |
8 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /server/gateway-service/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.fnd.games_store' 8 | version = '1.0' 9 | sourceCompatibility = '1.8' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | 16 | jar { 17 | manifest { 18 | attributes( 19 | 'Main-Class': 'com.fnd.games_store.games.GatewayApplication' 20 | ) 21 | 22 | } 23 | } 24 | 25 | ext { 26 | set('springCloudVersion', "2021.0.3") 27 | } 28 | 29 | dependencies { 30 | implementation 'org.springframework.cloud:spring-cloud-starter-gateway' 31 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 32 | implementation 'org.springframework.cloud:spring-cloud-starter-config' 33 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' 34 | implementation 'org.springframework.boot:spring-boot-starter-webflux' 35 | implementation 'io.netty:netty-resolver-dns-native-macos:4.1.85.Final:osx-aarch_64' 36 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 37 | testImplementation 'io.projectreactor:reactor-test' 38 | } 39 | 40 | dependencyManagement { 41 | imports { 42 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 43 | } 44 | } 45 | 46 | tasks.named('test') { 47 | useJUnitPlatform() 48 | } -------------------------------------------------------------------------------- /server/login-service/src/test/java/com/fnd/games_store/test/service/JwtValidationServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.test.service; 2 | 3 | 4 | import com.fnd.games_store.login.jwt_utils.JwtValidator; 5 | import com.fnd.games_store.login.service.implementation.ValidationServiceImpl; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.extension.ExtendWith; 9 | import org.mockito.InjectMocks; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.jupiter.MockitoExtension; 12 | 13 | 14 | import static org.assertj.core.api.Assertions.*; 15 | 16 | import static org.mockito.Mockito.verify; 17 | import static org.mockito.Mockito.when; 18 | 19 | 20 | 21 | @ExtendWith(MockitoExtension.class) 22 | public class JwtValidationServiceTest { 23 | 24 | 25 | @Mock 26 | private JwtValidator jwtValidator; 27 | 28 | @InjectMocks 29 | private ValidationServiceImpl validationService; 30 | 31 | private String incomingTokenHeader = "============================token"; 32 | 33 | private String incomingToken = incomingTokenHeader.substring(7); 34 | 35 | 36 | @Test 37 | void validate_ShouldReturnTrueForCorrectToken(){ 38 | assertThat(validationService.validate(incomingTokenHeader)).isTrue(); 39 | } 40 | 41 | 42 | 43 | @BeforeEach 44 | void testSetup(){ 45 | 46 | when(jwtValidator.validateJwtToken(incomingToken)).thenReturn(true); 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/serializer/OrderDTOSerializer.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.serializer; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fnd.games_store.cart.dto.OrderDTO; 5 | import org.apache.kafka.common.errors.SerializationException; 6 | import org.apache.kafka.common.header.Headers; 7 | import org.apache.kafka.common.serialization.Serializer; 8 | 9 | import java.util.Map; 10 | 11 | 12 | public class OrderDTOSerializer implements Serializer { 13 | 14 | 15 | private final ObjectMapper objectMapper = new ObjectMapper(); 16 | 17 | 18 | @Override 19 | public void configure(Map configs, boolean isKey) { 20 | Serializer.super.configure(configs, isKey); 21 | } 22 | 23 | @Override 24 | public byte[] serialize(String topic, OrderDTO data) { 25 | 26 | try { 27 | if (data == null){ 28 | return null; 29 | } 30 | return objectMapper.writeValueAsBytes(data); 31 | } catch (Exception e) { 32 | throw new SerializationException("Error when serializing OrderDTO to byte[]"); 33 | } 34 | } 35 | 36 | @Override 37 | public byte[] serialize(String topic, Headers headers, OrderDTO data) { 38 | return Serializer.super.serialize(topic, headers, data); 39 | } 40 | 41 | @Override 42 | public void close() { 43 | Serializer.super.close(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/cart_service/CartService_GetCartContentTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.cart_service; 2 | 3 | 4 | import com.fnd.games_store.cart.test.utilities.ServiceTestUtilities; 5 | import org.junit.jupiter.api.*; 6 | import org.junit.jupiter.api.extension.ExtendWith; 7 | 8 | import org.mockito.junit.jupiter.MockitoExtension; 9 | import org.mockito.junit.jupiter.MockitoSettings; 10 | import org.mockito.quality.Strictness; 11 | 12 | import java.util.Optional; 13 | 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | import static org.mockito.Mockito.*; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | @MockitoSettings(strictness = Strictness.LENIENT) 20 | public class CartService_GetCartContentTest extends ServiceTestUtilities { 21 | 22 | 23 | 24 | 25 | @Test 26 | void getCartContent_ShouldReturnProperCartData() { 27 | 28 | assertThat(service.getCartContent(userId)).isEqualTo(testGameSetDTO); 29 | 30 | verify(repository).findById(userId); 31 | 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | @BeforeEach 41 | void testSetup(){ 42 | 43 | testCart.setUserId(userId); 44 | testGameSet.add(createTestGameEntity(1)); 45 | testCart.setGameData(testGameSet); 46 | 47 | testGameSetDTO.add(createTestGameResponse(1)); 48 | 49 | 50 | when(repository.findById(userId)).thenReturn(Optional.ofNullable(testCart)); 51 | } 52 | 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/dto/game/GameRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.dto.game; 2 | 3 | import lombok.*; 4 | import java.math.BigDecimal; 5 | import java.time.OffsetDateTime; 6 | 7 | 8 | 9 | 10 | 11 | @NoArgsConstructor 12 | @Getter 13 | @Setter 14 | @EqualsAndHashCode 15 | @ToString 16 | public class GameRequestDTO { 17 | 18 | private String id; 19 | private String name; 20 | private String genre; 21 | private String releaseDate; 22 | private String developer; 23 | private String publisher; 24 | private String platform; 25 | private String features; 26 | private BigDecimal price; 27 | private BigDecimal discount; 28 | private String description; 29 | private String base64Image; 30 | 31 | public GameRequestDTO(String name, String genre, String releaseDate, String developer, 32 | String publisher, String platform, String features, BigDecimal price, 33 | BigDecimal discount, String description, String base64Image) { 34 | this.name = name; 35 | this.genre = genre; 36 | this.releaseDate = releaseDate; 37 | this.developer = developer; 38 | this.publisher = publisher; 39 | this.platform = platform; 40 | this.features = features; 41 | this.price = price; 42 | this.discount = discount; 43 | this.description = description; 44 | this.base64Image = base64Image; 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/filter/JwtFilter.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.filter; 2 | 3 | import com.fnd.games_store.cart.exception.UserValidationFailedException; 4 | import com.fnd.games_store.cart.rest.UserValidationClient; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.catalina.core.ApplicationContext; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.filter.OncePerRequestFilter; 11 | 12 | import javax.servlet.FilterChain; 13 | import javax.servlet.ServletException; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import java.io.IOException; 17 | import java.util.Arrays; 18 | 19 | @Slf4j 20 | public class JwtFilter extends OncePerRequestFilter { 21 | 22 | @Autowired 23 | private UserValidationClient userValidator; 24 | 25 | @Override 26 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 27 | 28 | String token = request.getHeader("authorization"); 29 | 30 | Boolean isUserValid = userValidator.validateUser(token).getBody().getIsTokenValid(); 31 | 32 | if (isUserValid){ 33 | filterChain.doFilter(request, response); 34 | } else throw new UserValidationFailedException("Failed to validate user"); 35 | 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@headlessui/react": "^1.7.7", 7 | "@heroicons/react": "^2.0.13", 8 | "@reduxjs/toolkit": "^1.9.1", 9 | "axios": "^0.27.2", 10 | "npm": "^9.2.0", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-redux": "^8.0.5", 14 | "react-router-dom": "^6.6.1", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "dev": "vite", 19 | "build": "tsc && vite build", 20 | "preview": "vite preview" 21 | }, 22 | "eslintConfig": { 23 | "extends": [ 24 | "react-app", 25 | "react-app/jest" 26 | ] 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "devDependencies": { 41 | "@types/node": "^16.18.11", 42 | "@types/react": "^18.0.26", 43 | "@types/react-dom": "^18.0.10", 44 | "@types/react-redux": "^7.1.24", 45 | "typescript": "^4.9.4", 46 | "autoprefixer": "^10.4.13", 47 | "postcss": "^8.4.20", 48 | "tailwindcss": "^3.2.4", 49 | "@vitejs/plugin-react": "^3.1.0", 50 | "vite": "^4.1.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/repository_integration_test/Repository_UpdateCartTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.repository_integration_test; 2 | 3 | import com.fnd.games_store.cart.CartApplication; 4 | import com.fnd.games_store.cart.test.utilities.RepositoryTestUtilities; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.testcontainers.junit.jupiter.Testcontainers; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | @Slf4j 14 | @SpringBootTest(classes = CartApplication.class) 15 | @Testcontainers(disabledWithoutDocker = true) 16 | public class Repository_UpdateCartTest extends RepositoryTestUtilities { 17 | 18 | 19 | 20 | @Test 21 | void updateCart_shouldNotReturnUpdatedGameSet(){ 22 | 23 | testCartGameData.add(createTestGameEntity("2")); 24 | testCart.setGameData(testCartGameData); 25 | repository.save(testCart); 26 | 27 | 28 | log.info(repository.findById(userId).get().toString()); 29 | log.info(testCart.toString()); 30 | 31 | assertThat(repository.findById(userId)).get().isEqualTo(testCart); 32 | 33 | } 34 | 35 | @BeforeEach 36 | void testSetup(){ 37 | testCart.setUserId(userId); 38 | testCartGameData.add(createTestGameEntity("1")); 39 | testCart.setGameData(testCartGameData); 40 | 41 | repository.save(testCart); 42 | 43 | } 44 | 45 | 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /server/cart-service/src/test/java/com/fnd/games_store/cart/test/repository_integration_test/Repository_GetCartContentTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.test.repository_integration_test; 2 | 3 | 4 | import com.fnd.games_store.cart.CartApplication; 5 | import com.fnd.games_store.cart.repository.CartRepository; 6 | import com.fnd.games_store.cart.test.utilities.RepositoryTestUtilities; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.testcontainers.junit.jupiter.Testcontainers; 13 | 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | @SpringBootTest(classes = CartApplication.class) 18 | @Slf4j 19 | @Testcontainers(disabledWithoutDocker = true) 20 | public class Repository_GetCartContentTest extends RepositoryTestUtilities { 21 | 22 | @Autowired 23 | CartRepository repository; 24 | 25 | 26 | @Test 27 | void getCartContent_ShouldReturnSavedTestCart(){ 28 | log.info(repository.findById(userId).get().toString()); 29 | log.info(testCart.toString()); 30 | 31 | assertThat(repository.findById(userId)).get().isEqualTo(testCart); 32 | } 33 | 34 | @BeforeEach 35 | void testSetup(){ 36 | testCart.setUserId(userId); 37 | testCartGameData.add(createTestGameEntity("1")); 38 | testCart.setGameData(testCartGameData); 39 | 40 | repository.save(testCart); 41 | 42 | } 43 | 44 | 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/service/implementation/SpecificGameServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.service.implementation; 2 | 3 | import com.fnd.games_store.games.annotations.Benchmarked; 4 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 5 | import com.fnd.games_store.games.exceptions.GameNotFoundException; 6 | import com.fnd.games_store.games.repository.GameRepository; 7 | import com.fnd.games_store.games.service.SpecificGameService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Isolation; 11 | import org.springframework.transaction.annotation.Propagation; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | @Service 15 | public class SpecificGameServiceImpl implements SpecificGameService { 16 | 17 | 18 | private final GameRepository repository; 19 | 20 | @Autowired 21 | public SpecificGameServiceImpl(GameRepository repository) { 22 | this.repository = repository; 23 | } 24 | 25 | @Transactional(label = "specific_gameList_fetch", 26 | propagation = Propagation.REQUIRED, 27 | isolation = Isolation.REPEATABLE_READ, 28 | timeout = 5, 29 | readOnly = true, 30 | rollbackFor = GameNotFoundException.class) 31 | @Benchmarked 32 | @Override 33 | public GameResponseDTO getGameByName(String name) { 34 | return new GameResponseDTO(repository.getGameByName(name).orElseThrow(GameNotFoundException::new)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/service/implementation/OrderReceiverService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service.implementation; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fnd.games_store.orders.dto.OrderRequestDTO; 6 | import com.fnd.games_store.orders.service.OrderMessageListener; 7 | import com.fnd.games_store.orders.service.OrderSaver; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.kafka.annotation.KafkaListener; 11 | import org.springframework.stereotype.Service; 12 | 13 | @Service 14 | @Slf4j 15 | public class OrderReceiverService implements OrderMessageListener { 16 | 17 | 18 | private final ObjectMapper objectMapper; 19 | 20 | private final OrderSaver service; 21 | 22 | @Autowired 23 | public OrderReceiverService(ObjectMapper objectMapper, OrderSaver service) { 24 | this.objectMapper = objectMapper; 25 | this.service = service; 26 | } 27 | @Override 28 | @KafkaListener(topics = "order-stream", groupId = "orders") 29 | public void orderMessageListener(String order) { 30 | 31 | OrderRequestDTO restoredObject; 32 | 33 | try{ 34 | restoredObject = objectMapper.readValue(order, OrderRequestDTO.class); 35 | service.saveOrder(restoredObject); 36 | log.info(restoredObject.toString()); 37 | } catch( JsonProcessingException e){ 38 | e.printStackTrace(); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/notification-service/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.0' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.fnd.games_store' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = '1.8' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | bootJar { 16 | enabled = false 17 | } 18 | 19 | jar { 20 | enabled = true 21 | } 22 | 23 | ext { 24 | set('springCloudVersion', "2021.0.3") 25 | } 26 | 27 | dependencies { 28 | implementation 'org.springframework.boot:spring-boot-starter-mail' 29 | implementation 'org.springframework.boot:spring-boot-starter-web' 30 | implementation 'org.springframework.boot:spring-boot-starter-amqp' 31 | implementation 'org.springframework.cloud:spring-cloud-starter-config' 32 | implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' 33 | implementation 'org.springframework.cloud:spring-cloud-starter-sleuth' 34 | implementation 'org.springframework.boot:spring-boot-starter-actuator' 35 | // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix 36 | implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-hystrix', version: '2.2.10.RELEASE' 37 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 38 | } 39 | 40 | dependencyManagement { 41 | imports { 42 | mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" 43 | } 44 | } 45 | 46 | tasks.named('test') { 47 | useJUnitPlatform() 48 | } -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/controller/GameController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.controller; 2 | 3 | import lombok.NoArgsConstructor; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | @RestController 7 | @NoArgsConstructor 8 | @CrossOrigin 9 | @RequestMapping("/v1/catalogue") 10 | public class GameController { 11 | // 12 | // 13 | // private GameService gameService; 14 | // @Autowired 15 | // public GameController(GameService gameService) { 16 | // this.gameService = gameService; 17 | // } 18 | // 19 | // @GetMapping("/specific/{requestedGameId}") 20 | // public GameResponseDTO getGameById(@PathVariable String requestedGameId){ 21 | // return gameService.getGameById(requestedGameId); 22 | // } 23 | // 24 | // @GetMapping("/all") 25 | // public List getGameCatalogue(){ 26 | // return gameService.getGamesCatalogue(); 27 | // } 28 | // 29 | // @PostMapping("/new") 30 | // public GameResponseDTO createGameEntry(@RequestBody GameRequestDTO creatingGameBody){ 31 | // return gameService.createGameEntry(creatingGameBody); 32 | // } 33 | // 34 | // @PutMapping("/updating/{updatingGameId}") 35 | // public GameResponseDTO updateGameEntry(@PathVariable String updatingGameId, @RequestBody GameRequestDTO newGameBody){ 36 | // return gameService.updateGameEntry(updatingGameId, newGameBody); 37 | // } 38 | // 39 | // @DeleteMapping("/deleting/{deletingGameId}") 40 | // public void deleteGameEntry(@PathVariable String deletingGameId){ 41 | // gameService.deleteGameEntry(deletingGameId); 42 | // } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/configuration/KafkaConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.configuration; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | 7 | import com.fnd.games_store.cart.dto.OrderDTO; 8 | import org.apache.kafka.clients.producer.ProducerConfig; 9 | import org.apache.kafka.common.serialization.StringSerializer; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 14 | import org.springframework.kafka.core.KafkaTemplate; 15 | import org.springframework.kafka.core.ProducerFactory; 16 | import org.springframework.kafka.support.serializer.JsonSerializer; 17 | 18 | @Configuration 19 | public class KafkaConfiguration { 20 | 21 | 22 | @Value("${variables.kafka.bootstrap_address}") 23 | private String kafkaAddress; 24 | 25 | 26 | @Bean 27 | public ProducerFactory producerFactory(){ 28 | Map kafkaConfig = new HashMap<>(); 29 | kafkaConfig.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaAddress); 30 | kafkaConfig.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 31 | kafkaConfig.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); 32 | return new DefaultKafkaProducerFactory<>(kafkaConfig); 33 | } 34 | 35 | 36 | @Bean 37 | public KafkaTemplate kafkaTemplate(){ 38 | return new KafkaTemplate<>(producerFactory()); 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/service/implementation/OrderFetcherService.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service.implementation; 2 | 3 | import com.fnd.games_store.orders.dto.OrderResponseDTO; 4 | import com.fnd.games_store.orders.exception.OrderNotFoundException; 5 | import com.fnd.games_store.orders.repository.OrderRepository; 6 | import com.fnd.games_store.orders.service.OrderFetcher; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Isolation; 10 | import org.springframework.transaction.annotation.Propagation; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.List; 14 | import java.util.stream.Collectors; 15 | 16 | @Service 17 | 18 | public class OrderFetcherService implements OrderFetcher { 19 | 20 | 21 | private final OrderRepository orderRepository; 22 | 23 | @Autowired 24 | public OrderFetcherService(OrderRepository orderRepository) { 25 | this.orderRepository = orderRepository; 26 | } 27 | @Transactional(label = "fetch_orders", 28 | propagation = Propagation.REQUIRED, 29 | isolation = Isolation.REPEATABLE_READ, 30 | timeout = 5, 31 | readOnly = true, 32 | rollbackFor = {OrderNotFoundException.class}) 33 | @Override 34 | public List fetchOrderData(String userId) { 35 | return orderRepository.findOrdersByUserId(userId).orElseThrow(()-> new OrderNotFoundException("Oder data is missing")).stream().map(OrderResponseDTO::new).collect(Collectors.toList()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/cart-service/src/main/java/com/fnd/games_store/cart/controller/implementation/CartController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.cart.controller.implementation; 2 | 3 | import com.fnd.games_store.cart.controller.CartCrudController; 4 | import com.fnd.games_store.cart.controller.OrderProcessor; 5 | import com.fnd.games_store.cart.dto.*; 6 | import com.fnd.games_store.cart.service.CartCrudService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.CrossOrigin; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import java.util.List; 16 | 17 | @RestController 18 | @Slf4j 19 | public class CartController implements CartCrudController { 20 | 21 | 22 | private final CartCrudService service; 23 | 24 | @Autowired 25 | public CartController(CartCrudService service) { 26 | this.service = service; 27 | } 28 | 29 | @Override 30 | @PostMapping("/v1/update") 31 | public ResponseEntity updateCart(@RequestBody CartRequestDTO cartRequestDTO) { 32 | service.updateCart(cartRequestDTO); 33 | return ResponseEntity.ok(new CartResponseDTO(cartRequestDTO)); 34 | } 35 | 36 | @Override 37 | @PostMapping("/v1/content") 38 | public ResponseEntity> getCartContent(@RequestBody GameRequestDTO gameRequestDTO) { 39 | return ResponseEntity.ok(service.getCartContent(gameRequestDTO.getUserId())); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/entity/Order.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.entity; 2 | 3 | import com.sun.istack.NotNull; 4 | import lombok.*; 5 | import org.hibernate.annotations.GenericGenerator; 6 | 7 | import javax.persistence.*; 8 | import java.time.OffsetDateTime; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Objects; 12 | 13 | @Entity 14 | @Table(name = "orders") 15 | @Getter 16 | @Setter 17 | @ToString 18 | @NoArgsConstructor 19 | public class Order { 20 | 21 | @NotNull 22 | @Id 23 | @GeneratedValue(generator = "uuid") 24 | @GenericGenerator(name ="uuid", strategy ="uuid2") 25 | @Column(name = "order_id") 26 | @ToString.Exclude 27 | private String id; 28 | 29 | @Column(name = "user_id") 30 | private String userId; 31 | 32 | @Column(name = "order_date") 33 | private OffsetDateTime orderDate; 34 | 35 | @Column(name = "is_order_processed") 36 | private Boolean isOrderProcessed; 37 | 38 | 39 | @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) 40 | @JoinTable(name = "order_game", 41 | joinColumns = {@JoinColumn(name = "order_id")}, 42 | inverseJoinColumns = {@JoinColumn(name ="game_id")}) 43 | private List games = new ArrayList<>(); 44 | 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (this == o) return true; 49 | if (o == null || getClass() != o.getClass()) return false; 50 | Order order = (Order) o; 51 | return Objects.equals(id, order.id); 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | return Objects.hash(id); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/controller/implementation/SpecificGameListControllerImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.controller.implementation; 2 | 3 | 4 | import com.fnd.games_store.games.controller.SpecificGameListController; 5 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 6 | import com.fnd.games_store.games.service.SpecificGameListService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.data.domain.Sort; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | import java.util.List; 15 | 16 | @RestController 17 | public class SpecificGameListControllerImpl implements SpecificGameListController { 18 | 19 | 20 | private final SpecificGameListService gameService; 21 | 22 | @Autowired 23 | public SpecificGameListControllerImpl(SpecificGameListService gameService) { 24 | this.gameService = gameService; 25 | } 26 | 27 | @Override 28 | @GetMapping("/v1/catalogue/list") 29 | public ResponseEntity> getEditedList(@RequestParam Integer page, @RequestParam Integer pageSize, @RequestParam String sortField, @RequestParam Boolean ascOrder) { 30 | if (ascOrder){ 31 | return ResponseEntity.ok(gameService.getSpecifiedGameList(page,pageSize, Sort.by(sortField))); 32 | } else { 33 | return ResponseEntity.ok(gameService.getSpecifiedGameList(page,pageSize, Sort.by(Sort.Direction.DESC, sortField))); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/jwt_utils/implementation/JwtGeneratorImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.jwt_utils.implementation; 2 | 3 | import com.fnd.games_store.login.entity.Authority; 4 | import com.fnd.games_store.login.jwt_utils.JwtGenerator; 5 | 6 | 7 | import com.auth0.jwt.JWT; 8 | import com.auth0.jwt.JWTCreator.Builder; 9 | import com.auth0.jwt.algorithms.Algorithm; 10 | import com.fnd.games_store.login.repository.AccountRepository; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.context.annotation.Profile; 14 | import org.springframework.security.core.GrantedAuthority; 15 | import org.springframework.security.core.authority.mapping.Attributes2GrantedAuthoritiesMapper; 16 | import org.springframework.security.core.userdetails.UserDetails; 17 | import org.springframework.stereotype.Component; 18 | 19 | import java.util.*; 20 | 21 | @Component 22 | public class JwtGeneratorImpl implements JwtGenerator { 23 | 24 | @Value("${variables.security.access_secret}") 25 | private String jwtAccessSecret; 26 | 27 | @Value("${variables.security.access_expiration}") 28 | private Long accessTokenExpirationDuration; 29 | 30 | 31 | @Override 32 | public String generateJwtToken(UserDetails userDetails) { 33 | 34 | String userName = userDetails.getUsername(); 35 | 36 | 37 | return JWT.create().withSubject(userName) 38 | .withIssuedAt(new Date()).withExpiresAt(new Date(System.currentTimeMillis() + accessTokenExpirationDuration)) 39 | .sign(Algorithm.HMAC256(jwtAccessSecret)); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/dto/GameRequestDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.dto; 2 | 3 | import com.fnd.games_store.orders.entity.Game; 4 | import lombok.*; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.Objects; 8 | 9 | @Getter 10 | @Setter 11 | @NoArgsConstructor 12 | @AllArgsConstructor 13 | @ToString 14 | public class GameRequestDTO { 15 | 16 | 17 | private String id; 18 | private String name; 19 | private String releaseDate; 20 | private BigDecimal price; 21 | private BigDecimal discount; 22 | private String description; 23 | private String base64Image; 24 | 25 | 26 | public GameRequestDTO(Game game) { 27 | this.id = game.getId(); 28 | this.name = game.getName(); 29 | this.releaseDate = game.getReleaseDate(); 30 | this.price = game.getPrice(); 31 | this.discount = game.getDiscount(); 32 | this.description = game.getDescription(); 33 | this.base64Image = game.getBase64Image(); 34 | } 35 | 36 | @Override 37 | public boolean equals(Object o) { 38 | if (this == o) return true; 39 | if (o == null || getClass() != o.getClass()) return false; 40 | GameRequestDTO gameDTO = (GameRequestDTO) o; 41 | return Objects.equals(name, gameDTO.name) && Objects.equals(releaseDate, gameDTO.releaseDate) && Objects.equals(price, gameDTO.price) && Objects.equals(discount, gameDTO.discount) && Objects.equals(description, gameDTO.description) && Objects.equals(base64Image, gameDTO.base64Image); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | return Objects.hash(name, releaseDate, price, discount, description, base64Image); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/dto/GameResponseDTO.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.dto; 2 | 3 | 4 | import com.fnd.games_store.orders.entity.Game; 5 | import lombok.*; 6 | 7 | import java.math.BigDecimal; 8 | import java.util.Objects; 9 | 10 | @Getter 11 | @Setter 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @ToString 15 | public class GameResponseDTO { 16 | 17 | 18 | private String id; 19 | private String name; 20 | private String releaseDate; 21 | private BigDecimal price; 22 | private BigDecimal discount; 23 | private String description; 24 | private String base64Image; 25 | 26 | 27 | 28 | public GameResponseDTO(Game game) { 29 | this.id = game.getId(); 30 | this.name = game.getName(); 31 | this.releaseDate = game.getReleaseDate(); 32 | this.price = game.getPrice(); 33 | this.discount = game.getDiscount(); 34 | this.description = game.getDescription(); 35 | this.base64Image = game.getBase64Image(); 36 | } 37 | 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) return true; 42 | if (o == null || getClass() != o.getClass()) return false; 43 | GameResponseDTO that = (GameResponseDTO) o; 44 | return Objects.equals(name, that.name) && Objects.equals(releaseDate, that.releaseDate) && Objects.equals(price, that.price) && Objects.equals(discount, that.discount) && Objects.equals(description, that.description) && Objects.equals(base64Image, that.base64Image); 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | return Objects.hash(name, releaseDate, price, discount, description, base64Image); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/controller/implementation/ValidationController.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.controller.implementation; 2 | 3 | import com.fnd.games_store.login.controller.UserValidation; 4 | import com.fnd.games_store.login.dto.ValidationResponseDTO; 5 | import com.fnd.games_store.login.service.ValidationService; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.annotation.Profile; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import java.util.Map; 13 | 14 | @RestController 15 | @Slf4j 16 | public class ValidationController implements UserValidation{ 17 | 18 | 19 | private final ValidationService validationService; 20 | 21 | @Autowired 22 | public ValidationController(ValidationService validationService) { 23 | this.validationService = validationService; 24 | } 25 | 26 | @Override 27 | @PostMapping(path = "/v1/validate") 28 | public ResponseEntity validateUser(@RequestHeader Map headers) { 29 | 30 | printHeadersToConsole(headers); 31 | 32 | String incomingUserToken = headers.get("authorization"); 33 | log.info(incomingUserToken); 34 | 35 | Boolean isIncomingTokenValid = validationService.validate(incomingUserToken); 36 | 37 | return ResponseEntity.ok(new ValidationResponseDTO(isIncomingTokenValid)); 38 | } 39 | 40 | 41 | @Profile("test") 42 | private void printHeadersToConsole(Map headers){ 43 | headers.entrySet().stream().forEach(System.out::println); 44 | } 45 | 46 | 47 | 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /server/login-service/src/main/resources/application-standalone.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | jpa: 8 | flyway: 9 | enabled: true 10 | validate-on-migrate: true 11 | database: h2 12 | properties.hibernate.temp.use_jdbc_metadata_defaults: false 13 | hibernate: 14 | ddl-auto: none 15 | properties: 16 | datasource: 17 | database-platform: org.hibernate.dialect.H2Dialect 18 | url: jdbc:h2:mem:testdb 19 | username: sa 20 | password: 21 | driver-class-name: org.h2.Driver 22 | h2: 23 | console: 24 | enabled: true 25 | path: /h2 26 | server: 27 | servlet: 28 | context-path: /login 29 | port: 8082 30 | 31 | variables: 32 | security: 33 | access_secret: OTFBRjg3MUMwOUYxODlFNDc3NEQwNTAxMTg0M0M0NzI5QTczNDY1QUU4MTA5MUFERjZFNDIxNTk0QTZDOUYyQg== 34 | access_expiration: 90000000 35 | common: 36 | new_account_expiration_date: 2030-01-01 37 | 38 | 39 | #spring: 40 | # jpa: 41 | # flyway: 42 | # enabled: true 43 | # validate-on-migrate: true 44 | # database: POSTGRESQL 45 | # properties.hibernate.temp.use_jdbc_metadata_defaults: false 46 | # hibernate: 47 | # ddl-auto: none 48 | # properties: 49 | # datasource: 50 | # platform: postgres 51 | # url: jdbc:postgresql://localhost:5432/login_service_database 52 | # username: postgres 53 | # password: root 54 | # driver-class-name: org.postgresql.Driver 55 | # 56 | #server: 57 | # servlet: 58 | # context-path: /login 59 | # port: 8082 60 | # 61 | # 62 | #variables: 63 | # security: 64 | # access_secret: OTFBRjg3MUMwOUYxODlFNDc3NEQwNTAxMTg0M0M0NzI5QTczNDY1QUU4MTA5MUFERjZFNDIxNTk0QTZDOUYyQg== 65 | # access_expiration: 90000000 -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/service/SaveInitialOrderDataTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | 4 | import com.fnd.games_store.orders.OrdersApplication; 5 | import com.fnd.games_store.orders.dto.OrderRequestDTO; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.time.OffsetDateTime; 13 | 14 | 15 | import static org.assertj.core.api.Assertions.*; 16 | @Transactional 17 | @SpringBootTest(classes = OrdersApplication.class) 18 | @Slf4j 19 | public class SaveInitialOrderDataTest extends ServiceTestUtils{ 20 | 21 | 22 | private OffsetDateTime testCaseInitTime = OffsetDateTime.now(); 23 | 24 | @Test 25 | void saveOrder_ShouldCreateInitialOrderEntry(){ 26 | log.info("saved order: "+savedOrder.toString()); 27 | log.info("expected order: "+expectedOrder.toString()); 28 | assertThat(savedOrder).isEqualTo(expectedOrder); 29 | } 30 | 31 | @BeforeEach 32 | void testSetup(){ 33 | 34 | savedOrder.setIsOrderProcessed(true); 35 | savedOrder.setUserId(userId); 36 | savedOrder.setOrderDate(testCaseInitTime); 37 | 38 | testgameList.add(createTestGameEntity("1")); 39 | 40 | savedOrder.setGames(testgameList); 41 | 42 | OrderRequestDTO incomingOrderDto = wrapOrderToDto(savedOrder); 43 | 44 | String savedOrderId = service.saveOrder(incomingOrderDto); 45 | 46 | savedOrder.setId(savedOrderId); 47 | 48 | expectedOrder = orderRepository.findById(savedOrderId).get(); 49 | 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/service/FetchOrderDataTest.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | import com.fnd.games_store.orders.OrdersApplication; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | import java.time.OffsetDateTime; 11 | 12 | import static org.assertj.core.api.Assertions.*; 13 | 14 | @Transactional 15 | @SpringBootTest(classes = OrdersApplication.class) 16 | @Slf4j 17 | public class FetchOrderDataTest extends ServiceTestUtils { 18 | 19 | 20 | private OffsetDateTime testCaseInitTime = OffsetDateTime.now(); 21 | 22 | 23 | @Test 24 | void saveOrder_ShouldCreateInitialOrderEntry(){ 25 | OffsetDateTime timeNow = OffsetDateTime.now(); 26 | 27 | savedOrder.setOrderDate(timeNow); 28 | expectedOrder.setOrderDate(timeNow); 29 | savedOrder.setId(expectedOrder.getId()); 30 | 31 | log.info("saved order: "+savedOrder.getId()); 32 | log.info("expected order: "+expectedOrder.getId()); 33 | 34 | assertThat(savedOrder).isEqualTo(expectedOrder); 35 | } 36 | 37 | 38 | 39 | 40 | @BeforeEach 41 | void testSetup(){ 42 | 43 | savedOrder.setIsOrderProcessed(true); 44 | savedOrder.setUserId(userId); 45 | savedOrder.setOrderDate(testCaseInitTime); 46 | 47 | testgameList.add(createTestGameEntity("1")); 48 | 49 | savedOrder.setGames(testgameList); 50 | 51 | service.saveOrder(wrapOrderToDto(savedOrder)); 52 | 53 | expectedOrder = orderRepository.findOrdersByUserId(userId).get().get(0); 54 | 55 | } 56 | 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /server/games-service/src/test/java/com/fnd/games_store/service/GetGameByName.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.service; 2 | 3 | import com.fnd.games_store.games.GamesApplication; 4 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 5 | import com.fnd.games_store.games.repository.GameRepository; 6 | import com.fnd.games_store.games.service.SpecificGameService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | 15 | import static org.assertj.core.api.Assertions.*; 16 | 17 | @SpringBootTest(classes = GamesApplication.class) 18 | @Slf4j 19 | @Transactional 20 | public class GetGameByName { 21 | 22 | 23 | @Autowired 24 | private SpecificGameService service; 25 | 26 | @Autowired 27 | private GameRepository repository; 28 | 29 | private GameResponseDTO serviceGameEntity; 30 | 31 | private GameResponseDTO controlGameEntity; 32 | 33 | private String controlGameName = "DOOM Eternal"; 34 | 35 | 36 | @Test 37 | void getGameByName_ShouldReturnProperGameResponseDTO(){ 38 | assertThat(serviceGameEntity).isEqualTo(controlGameEntity); 39 | } 40 | 41 | 42 | @BeforeEach 43 | void testSetup(){ 44 | 45 | controlGameEntity = createAppropriateGameResponseDTO(); 46 | 47 | serviceGameEntity = service.getGameByName(controlGameName); 48 | 49 | } 50 | 51 | private GameResponseDTO createAppropriateGameResponseDTO() { 52 | 53 | GameResponseDTO returnableGame = new GameResponseDTO(repository.getGameByName(controlGameName).get()); 54 | 55 | return returnableGame; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /server/games-service/src/main/java/com/fnd/games_store/games/service/implementation/SpecificGameListServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.games.service.implementation; 2 | 3 | import com.fnd.games_store.games.annotations.Benchmarked; 4 | import com.fnd.games_store.games.dto.game.GameResponseDTO; 5 | import com.fnd.games_store.games.exceptions.GameNotFoundException; 6 | import com.fnd.games_store.games.repository.GameRepository; 7 | import com.fnd.games_store.games.service.SpecificGameListService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.domain.PageRequest; 10 | import org.springframework.data.domain.Sort; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Isolation; 13 | import org.springframework.transaction.annotation.Propagation; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | 20 | @Service 21 | public class SpecificGameListServiceImpl implements SpecificGameListService { 22 | 23 | 24 | private final GameRepository repository; 25 | 26 | @Autowired 27 | public SpecificGameListServiceImpl(GameRepository repository) { 28 | this.repository = repository; 29 | } 30 | 31 | @Transactional(label = "specific_gameList_fetch", 32 | propagation = Propagation.REQUIRED, 33 | isolation = Isolation.REPEATABLE_READ, 34 | timeout = 5, 35 | readOnly = true) 36 | @Benchmarked 37 | @Override 38 | public List getSpecifiedGameList(Integer page, Integer pageSize, Sort sortBy) { 39 | return repository.findAll(PageRequest.of(page,pageSize, sortBy)).stream().map(GameResponseDTO::new).collect(Collectors.toList()); 40 | } 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /server/login-service/src/main/java/com/fnd/games_store/login/entity/Authority.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.login.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.sun.istack.NotNull; 5 | import lombok.*; 6 | import org.hibernate.annotations.GenericGenerator; 7 | import org.springframework.security.core.GrantedAuthority; 8 | 9 | 10 | import javax.persistence.*; 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | @Entity 15 | @Table(name = "authorities") 16 | @NoArgsConstructor 17 | @Setter 18 | @Getter 19 | public class Authority implements GrantedAuthority{ 20 | 21 | @NotNull 22 | @Id 23 | @GeneratedValue(generator = "uuid") 24 | @GenericGenerator(name ="uuid", strategy ="uuid2") 25 | @Column(name = "authority_id") 26 | private String id; 27 | 28 | private String authority; 29 | 30 | 31 | @ManyToMany 32 | @JsonIgnore 33 | private List account; 34 | 35 | 36 | public Authority(String id, String authority) { 37 | this.id = id; 38 | this.authority = authority; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Authority{" + 44 | "id='" + id + '\'' + 45 | ", authority='" + authority + '\'' + 46 | // ", accounts=" + accounts + 47 | '}'; 48 | } 49 | 50 | 51 | @Override 52 | public boolean equals(Object o) { 53 | if (this == o) return true; 54 | if (o == null || getClass() != o.getClass()) return false; 55 | Authority authority1 = (Authority) o; 56 | return Objects.equals(id, authority1.id) && Objects.equals(authority, authority1.authority) && Objects.equals(account, authority1.account); 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | return Objects.hash(id, authority, account); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /server/orders-service/src/test/java/com/fnd/games_store/orders/service/ServiceTestUtils.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.service; 2 | 3 | import com.fnd.games_store.orders.dto.OrderRequestDTO; 4 | import com.fnd.games_store.orders.entity.Game; 5 | import com.fnd.games_store.orders.entity.Order; 6 | import com.fnd.games_store.orders.repository.OrderRepository; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | 10 | import java.math.BigDecimal; 11 | import java.time.OffsetDateTime; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @Slf4j 16 | public class ServiceTestUtils { 17 | 18 | @Autowired 19 | protected OrderRepository orderRepository; 20 | 21 | @Autowired 22 | protected OrderSaver service; 23 | 24 | protected String userId = "user1"; 25 | 26 | protected Order savedOrder = new Order(); 27 | 28 | protected Order expectedOrder = new Order(); 29 | 30 | protected List testgameList = new ArrayList<>(); 31 | 32 | protected Game createTestGameEntity(String differenceParameter){ 33 | 34 | Game testGameEntity = new Game(); 35 | testGameEntity.setId(differenceParameter); 36 | testGameEntity.setName("Doom Eternal" + differenceParameter); 37 | testGameEntity.setReleaseDate("2009"+differenceParameter); 38 | testGameEntity.setPrice(BigDecimal.valueOf(1000)); 39 | testGameEntity.setDiscount(BigDecimal.valueOf(20)); 40 | testGameEntity.setDescription(""+differenceParameter); 41 | testGameEntity.setBase64Image(""+differenceParameter); 42 | 43 | return testGameEntity; 44 | } 45 | 46 | protected OrderRequestDTO wrapOrderToDto(Order wrapableOrder){ 47 | log.info(wrapableOrder.getOrderDate().toString()); 48 | return new OrderRequestDTO(wrapableOrder); 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/configuration/KafkaConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.configuration; 2 | 3 | 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | 8 | 9 | import org.apache.kafka.clients.consumer.ConsumerConfig; 10 | import org.apache.kafka.common.serialization.StringDeserializer; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.kafka.annotation.EnableKafka; 15 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 16 | import org.springframework.kafka.core.ConsumerFactory; 17 | import org.springframework.kafka.core.DefaultKafkaConsumerFactory; 18 | 19 | @EnableKafka 20 | @Configuration 21 | public class KafkaConfiguration { 22 | 23 | @Value("${variables.kafka.bootstrap_address}") 24 | private String kafkaAddress; 25 | 26 | @Bean 27 | public ConsumerFactory consumerFactory(){ 28 | Map kafkaConfig = new HashMap<>(); 29 | kafkaConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaAddress); 30 | kafkaConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 31 | kafkaConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 32 | kafkaConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "orders"); 33 | return new DefaultKafkaConsumerFactory<>(kafkaConfig); 34 | } 35 | 36 | @Bean 37 | public ConcurrentKafkaListenerContainerFactory kafkaListener(){ 38 | ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); 39 | factory.setConsumerFactory(consumerFactory()); 40 | return factory; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /server/orders-service/src/main/java/com/fnd/games_store/orders/entity/Game.java: -------------------------------------------------------------------------------- 1 | package com.fnd.games_store.orders.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fnd.games_store.orders.dto.GameRequestDTO; 5 | import com.sun.istack.NotNull; 6 | import lombok.*; 7 | import org.hibernate.annotations.GenericGenerator; 8 | 9 | import javax.persistence.*; 10 | import java.io.Serializable; 11 | import java.math.BigDecimal; 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | @Entity 16 | @Table(name = "games") 17 | @NoArgsConstructor 18 | @Getter 19 | @Setter 20 | @ToString 21 | public class Game { 22 | @NotNull 23 | @Id 24 | @Column(name = "game_id") 25 | @ToString.Exclude 26 | private String id; 27 | @ManyToMany(mappedBy = "games") 28 | @JsonIgnore 29 | @ToString.Exclude 30 | private List order; 31 | private String name; 32 | private String releaseDate; 33 | private BigDecimal price; 34 | private BigDecimal discount; 35 | private String description; 36 | private String base64Image; 37 | 38 | public Game(GameRequestDTO gameRequestDTO){ 39 | this.id = gameRequestDTO.getId(); 40 | this.name = gameRequestDTO.getName(); 41 | this.releaseDate = gameRequestDTO.getReleaseDate(); 42 | this.price = gameRequestDTO.getPrice(); 43 | this.discount = gameRequestDTO.getDiscount(); 44 | this.description = gameRequestDTO.getDescription(); 45 | this.base64Image = gameRequestDTO.getBase64Image(); 46 | } 47 | 48 | 49 | @Override 50 | public boolean equals(Object o) { 51 | if (this == o) return true; 52 | if (o == null || getClass() != o.getClass()) return false; 53 | Game game = (Game) o; 54 | return Objects.equals(id, game.id); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(id); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /server/gateway-service/src/main/resources/application-standalone.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | gateway: 8 | default-filters: 9 | - DedupeResponseHeader=Access-Control-Allow-Origin 10 | globalcors: 11 | add-to-simple-url-handler-mapping: true 12 | corsConfigurations: 13 | '[/**]': 14 | allowed-origins: "http://localhost:3000" 15 | allowed-methods: 16 | - GET 17 | - POST 18 | - OPTIONS 19 | allowed-headers: "*" 20 | allow-credentials: true 21 | routes: 22 | - id: games_service 23 | uri: http://localhost:8081/games 24 | predicates: 25 | - Path=/games/v1/catalogue/list/**" 26 | - id: games_service 27 | uri: http://localhost:8081/games 28 | predicates: 29 | - Path=/games/v1/catalogue/** 30 | 31 | 32 | 33 | - id: cart_service 34 | uri: http://localhost:8083/cart 35 | predicates: 36 | - Path=/cart/v1/update 37 | - id: cart_service 38 | uri: http://localhost:8083/cart 39 | predicates: 40 | - Path=/cart/v1/content 41 | - id: cart_service 42 | uri: http://localhost:8083/cart 43 | predicates: 44 | - Path=/cart/v1/purchase/** 45 | 46 | 47 | 48 | - id: login_service 49 | uri: http://localhost:8082/login 50 | predicates: 51 | - Path=/login/v1/authorization 52 | - id: login_service 53 | uri: http://localhost:8082/login 54 | predicates: 55 | - Path=/login/v1/registration 56 | 57 | 58 | eureka: 59 | instance: 60 | preferIpAddress: true 61 | client: 62 | service-url: 63 | defaultZone: http://localhost:8761/eureka/ 64 | 65 | 66 | variables: 67 | webclient: 68 | timeout: 1000 --------------------------------------------------------------------------------