From aec991374f68dfe5715411b1af6a41621530297b Mon Sep 17 00:00:00 2001 From: Cedric Date: Sun, 22 Dec 2024 23:53:36 +0100 Subject: [PATCH 01/32] db connected --- .../controller/ExpenseListController.java | 38 +++++++++++++++++++ .../app/XpenselyServer/model/AppUser.java | 22 +++++++++++ .../app/XpenselyServer/model/Expense.java | 26 +++++++++++++ .../app/XpenselyServer/model/ExpenseList.java | 34 +++++++++++++++++ .../repo/ExpenseListRepository.java | 11 ++++++ .../services/ExpenseListService.java | 27 +++++++++++++ 6 files changed, 158 insertions(+) create mode 100644 src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/model/Expense.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java diff --git a/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java b/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java new file mode 100644 index 0000000..a41b469 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java @@ -0,0 +1,38 @@ +package de.zendric.app.XpenselyServer.controller; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.zendric.app.XpenselyServer.model.ExpenseList; +import de.zendric.app.XpenselyServer.services.ExpenseListService; + +@RestController +@RequestMapping("/api/lists") +public class ExpenseListController { + + @Autowired + private ExpenseListService service; + + @GetMapping + public List getAllLists() { + return service.getAllLists(); + } + + @PostMapping + public ExpenseList createList(@RequestBody ExpenseList list) { + return service.createList(list); + } + + @DeleteMapping("/{id}") + public void deleteList(@PathVariable Long id) { + service.deleteList(id); + } +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java b/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java new file mode 100644 index 0000000..17b3e10 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java @@ -0,0 +1,22 @@ +package de.zendric.app.XpenselyServer.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +public class AppUser { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String username; + private String password; + + +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java b/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java new file mode 100644 index 0000000..b3618a6 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java @@ -0,0 +1,26 @@ +package de.zendric.app.XpenselyServer.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Entity +public class Expense { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String description; + private Double amount; + + + @ManyToOne + private ExpenseList expenseList; +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java b/src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java new file mode 100644 index 0000000..c7a2a5a --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java @@ -0,0 +1,34 @@ +package de.zendric.app.XpenselyServer.model; + +import java.util.List; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import lombok.Getter; +import lombok.Setter; + +@Entity +@Getter +@Setter +public class ExpenseList { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @ManyToOne + private AppUser owner; + + @ManyToMany + private List sharedWith; + + @OneToMany(mappedBy = "expenseList", cascade = CascadeType.ALL) + private List expenses; +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java b/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java new file mode 100644 index 0000000..ad2301b --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java @@ -0,0 +1,11 @@ +package de.zendric.app.XpenselyServer.repo; + +import org.springframework.data.jpa.repository.JpaRepository; + +import de.zendric.app.XpenselyServer.model.ExpenseList; + +import java.util.List; + +public interface ExpenseListRepository extends JpaRepository { + List findByOwnerId(Long ownerId); +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java b/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java new file mode 100644 index 0000000..d08c3a1 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java @@ -0,0 +1,27 @@ +package de.zendric.app.XpenselyServer.services; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.zendric.app.XpenselyServer.model.ExpenseList; +import de.zendric.app.XpenselyServer.repo.ExpenseListRepository; + +@Service +public class ExpenseListService { + @Autowired + private ExpenseListRepository repository; + + public List getAllLists() { + return repository.findAll(); + } + + public ExpenseList createList(ExpenseList list) { + return repository.save(list); + } + + public void deleteList(Long id) { + repository.deleteById(id); + } +} \ No newline at end of file From a3a89abc349d1431eea34ab5e6a611090fb2133a Mon Sep 17 00:00:00 2001 From: Cedric Date: Mon, 23 Dec 2024 11:44:02 +0100 Subject: [PATCH 02/32] user endpoints working --- .../controller/AppUserController.java | 45 +++++++++++++++++++ .../app/XpenselyServer/model/AppUser.java | 6 ++- .../repo/ExpenseRepository.java | 9 ++++ .../XpenselyServer/repo/UserRepository.java | 9 ++++ .../security/SecurityConfig.java | 19 ++++++++ .../XpenselyServer/services/UserService.java | 43 ++++++++++++++++++ 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java create mode 100644 src/main/java/de/zendric/app/XpenselyServer/services/UserService.java diff --git a/src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java b/src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java new file mode 100644 index 0000000..5d03e05 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java @@ -0,0 +1,45 @@ +package de.zendric.app.XpenselyServer.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.zendric.app.XpenselyServer.model.AppUser; +import de.zendric.app.XpenselyServer.services.UserService; + +@RestController +@RequestMapping("/api/users") +public class AppUserController { + + private UserService userService; + + @Autowired + public AppUserController(UserService userService){ + this.userService= userService; + } + + @GetMapping + public AppUser getUser(@RequestParam Long id){ + return userService.getUser(id); + } + + @PostMapping + public ResponseEntity createUser(@RequestBody AppUser user){ + AppUser appUser = userService.createUser(user); + return ResponseEntity.status(HttpStatus.CREATED).body(appUser); + } + + + @DeleteMapping + public String deleteUser(@RequestParam Long id){ + AppUser user = userService.deleteUserById(id); + return "User deleted : "+ user.getUsername(); + } +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java b/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java index 17b3e10..ff060d9 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java +++ b/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java @@ -4,12 +4,16 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Entity @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -17,6 +21,6 @@ public class AppUser { private String username; private String password; + private String email; - } diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java b/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java new file mode 100644 index 0000000..3590d36 --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java @@ -0,0 +1,9 @@ +package de.zendric.app.XpenselyServer.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import de.zendric.app.XpenselyServer.model.Expense; + +@Repository +public interface ExpenseRepository extends JpaRepository {} diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java b/src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java new file mode 100644 index 0000000..926b70a --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java @@ -0,0 +1,9 @@ +package de.zendric.app.XpenselyServer.repo; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import de.zendric.app.XpenselyServer.model.AppUser; + +@Repository +public interface UserRepository extends JpaRepository {} diff --git a/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java b/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java new file mode 100644 index 0000000..fa24b6b --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java @@ -0,0 +1,19 @@ +package de.zendric.app.XpenselyServer.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf().disable() // Disable CSRF for testing + .authorizeHttpRequests() + .anyRequest().permitAll(); // Allow all requests + return http.build(); + } +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/services/UserService.java b/src/main/java/de/zendric/app/XpenselyServer/services/UserService.java new file mode 100644 index 0000000..68a3cec --- /dev/null +++ b/src/main/java/de/zendric/app/XpenselyServer/services/UserService.java @@ -0,0 +1,43 @@ +package de.zendric.app.XpenselyServer.services; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import de.zendric.app.XpenselyServer.model.AppUser; +import de.zendric.app.XpenselyServer.repo.UserRepository; + +@Service +public class UserService { + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public List getAllUsers() { + return userRepository.findAll(); + } + + public AppUser createUser(AppUser user) { + return userRepository.save(user); + } + + public AppUser getUser(Long id) { + Optional user= userRepository.findById(id); + if (user.isPresent()) { + return user.get(); + } + else return null; + } + + public AppUser deleteUserById(Long id) { + Optional user= userRepository.findById(id); + if (user.isPresent()) { + userRepository.deleteById(id); + return user.get(); + } + else return null; + } +} \ No newline at end of file From aa4ed91b9ddcfb2e6801202240bf83a5884727de Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 24 Dec 2024 23:04:35 +0100 Subject: [PATCH 03/32] expense List logic --- apiDesign.drawio | 199 ++++++++++++++++++ .../controller/ExpenseListController.java | 38 ---- .../app/XpenselyServer/model/Expense.java | 26 --- .../security/SecurityConfig.java | 19 -- .../services/ExpenseListService.java | 27 --- .../XpenselyServerApplication.java | 2 +- .../controller/AppUserController.java | 36 ++-- .../controller/ExpenseListController.java | 132 ++++++++++++ .../model/AppUser.java | 10 +- .../app/xpensely_server/model/Expense.java | 43 ++++ .../model/ExpenseList.java | 24 ++- .../repo/ExpenseListRepository.java | 12 +- .../repo/ExpenseRepository.java | 7 +- .../repo/UserRepository.java | 10 +- .../security/SecurityConfig.java | 32 +++ .../services/ExpenseListService.java | 137 ++++++++++++ .../services/ExpenseService.java | 19 ++ .../services/UserService.java | 26 ++- src/main/resources/application.properties | 15 ++ .../XpenselyServerApplicationTests.java | 2 +- 20 files changed, 659 insertions(+), 157 deletions(-) create mode 100644 apiDesign.drawio delete mode 100644 src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java delete mode 100644 src/main/java/de/zendric/app/XpenselyServer/model/Expense.java delete mode 100644 src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java delete mode 100644 src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/XpenselyServerApplication.java (88%) rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/controller/AppUserController.java (57%) create mode 100644 src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/model/AppUser.java (72%) create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/Expense.java rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/model/ExpenseList.java (58%) rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/repo/ExpenseListRepository.java (57%) rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/repo/ExpenseRepository.java (63%) rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/repo/UserRepository.java (50%) create mode 100644 src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java create mode 100644 src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java create mode 100644 src/main/java/de/zendric/app/xpensely_server/services/ExpenseService.java rename src/main/java/de/zendric/app/{XpenselyServer => xpensely_server}/services/UserService.java (55%) rename src/test/java/de/zendric/app/{XpenselyServer => xpensely_Server}/XpenselyServerApplicationTests.java (82%) diff --git a/apiDesign.drawio b/apiDesign.drawio new file mode 100644 index 0000000..e276b67 --- /dev/null +++ b/apiDesign.drawio @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java b/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java deleted file mode 100644 index a41b469..0000000 --- a/src/main/java/de/zendric/app/XpenselyServer/controller/ExpenseListController.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.zendric.app.XpenselyServer.controller; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import de.zendric.app.XpenselyServer.model.ExpenseList; -import de.zendric.app.XpenselyServer.services.ExpenseListService; - -@RestController -@RequestMapping("/api/lists") -public class ExpenseListController { - - @Autowired - private ExpenseListService service; - - @GetMapping - public List getAllLists() { - return service.getAllLists(); - } - - @PostMapping - public ExpenseList createList(@RequestBody ExpenseList list) { - return service.createList(list); - } - - @DeleteMapping("/{id}") - public void deleteList(@PathVariable Long id) { - service.deleteList(id); - } -} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java b/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java deleted file mode 100644 index b3618a6..0000000 --- a/src/main/java/de/zendric/app/XpenselyServer/model/Expense.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.zendric.app.XpenselyServer.model; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -public class Expense { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String description; - private Double amount; - - - @ManyToOne - private ExpenseList expenseList; -} diff --git a/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java b/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java deleted file mode 100644 index fa24b6b..0000000 --- a/src/main/java/de/zendric/app/XpenselyServer/security/SecurityConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.zendric.app.XpenselyServer.security; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.SecurityFilterChain; - -@Configuration -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf().disable() // Disable CSRF for testing - .authorizeHttpRequests() - .anyRequest().permitAll(); // Allow all requests - return http.build(); - } -} diff --git a/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java b/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java deleted file mode 100644 index d08c3a1..0000000 --- a/src/main/java/de/zendric/app/XpenselyServer/services/ExpenseListService.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.zendric.app.XpenselyServer.services; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import de.zendric.app.XpenselyServer.model.ExpenseList; -import de.zendric.app.XpenselyServer.repo.ExpenseListRepository; - -@Service -public class ExpenseListService { - @Autowired - private ExpenseListRepository repository; - - public List getAllLists() { - return repository.findAll(); - } - - public ExpenseList createList(ExpenseList list) { - return repository.save(list); - } - - public void deleteList(Long id) { - repository.deleteById(id); - } -} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/XpenselyServer/XpenselyServerApplication.java b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java similarity index 88% rename from src/main/java/de/zendric/app/XpenselyServer/XpenselyServerApplication.java rename to src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java index 0fe260e..420f3e5 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/XpenselyServerApplication.java +++ b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java @@ -1,4 +1,4 @@ -package de.zendric.app.XpenselyServer; +package de.zendric.app.xpensely_server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java similarity index 57% rename from src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java rename to src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java index 5d03e05..4714ecc 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/controller/AppUserController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java @@ -1,4 +1,4 @@ -package de.zendric.app.XpenselyServer.controller; +package de.zendric.app.xpensely_server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -11,35 +11,39 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import de.zendric.app.XpenselyServer.model.AppUser; -import de.zendric.app.XpenselyServer.services.UserService; +import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.services.UserService; @RestController @RequestMapping("/api/users") public class AppUserController { - + private UserService userService; - + @Autowired - public AppUserController(UserService userService){ - this.userService= userService; + public AppUserController(UserService userService) { + this.userService = userService; } - + @GetMapping - public AppUser getUser(@RequestParam Long id){ + public AppUser getUser(@RequestParam Long id) { return userService.getUser(id); } - @PostMapping - public ResponseEntity createUser(@RequestBody AppUser user){ - AppUser appUser = userService.createUser(user); - return ResponseEntity.status(HttpStatus.CREATED).body(appUser); + @GetMapping("/byName") + public AppUser getUserByName(@RequestParam String username) { + return userService.getUserByName(username); } + @PostMapping + public ResponseEntity createUser(@RequestBody AppUser user) { + AppUser appUser = userService.createUser(user); + return ResponseEntity.status(HttpStatus.CREATED).body(appUser); + } @DeleteMapping - public String deleteUser(@RequestParam Long id){ - AppUser user = userService.deleteUserById(id); - return "User deleted : "+ user.getUsername(); + public String deleteUser(@RequestParam Long id) { + AppUser user = userService.deleteUserById(id); + return "User deleted : " + user.getUsername(); } } diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java new file mode 100644 index 0000000..b39ccc1 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -0,0 +1,132 @@ +package de.zendric.app.xpensely_server.controller; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import de.zendric.app.xpensely_server.model.Expense; +import de.zendric.app.xpensely_server.model.ExpenseList; +import de.zendric.app.xpensely_server.services.ExpenseListService; + +@RestController +@RequestMapping("/api/expenselist") +class ExpenseListController { + + private ExpenseListService expenseListService; + + @Autowired + public ExpenseListController(ExpenseListService expenseListService) { + this.expenseListService = expenseListService; + } + + @GetMapping("/all") + public ResponseEntity> getAll() { + try { + List items = new ArrayList<>(); + + expenseListService.findAll().forEach(items::add); + + if (items.isEmpty()) + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + + return new ResponseEntity<>(items, HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @GetMapping("/byUser") + public ResponseEntity> getByUser(@RequestParam Long userId) { + try { + List items = expenseListService.findByUserId(userId); + + if (items.isEmpty()) + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + + return new ResponseEntity<>(items, HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @GetMapping("/byUsername") + public ResponseEntity> getByUser(@RequestParam String username) { + try { + List items = expenseListService.findByUsername(username); + + if (items.isEmpty()) + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + + return new ResponseEntity<>(items, HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @GetMapping("/byId") + public ResponseEntity getById(@RequestParam Long id) { + Optional existingItemOptional = expenseListService.findById(id); + + if (existingItemOptional.isPresent()) { + return new ResponseEntity<>(existingItemOptional.get(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @PostMapping + public ResponseEntity create(@RequestBody ExpenseList expenseList) { + try { + ExpenseList savedItem = (ExpenseList) expenseListService.save(expenseList); + return new ResponseEntity<>(savedItem, HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); + } + } + + @DeleteMapping("{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + try { + expenseListService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); + } + } + + @PostMapping("/{id}/add") + public ResponseEntity addExpenseToList( + @PathVariable("id") Long expenseListId, + @RequestBody Expense expense) { + try { + Expense addedExpense = expenseListService.addExpenseToList(expenseListId, expense); + return new ResponseEntity<>(addedExpense, HttpStatus.CREATED); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + @DeleteMapping("/{id}/delete") + public ResponseEntity deleteExpenseFromList( + @PathVariable("id") Long expenseListId, + @RequestParam("expenseId") Long expenseId) { + try { + expenseListService.deleteExpenseFromList(expenseListId, expenseId); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); + } + } +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java b/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java similarity index 72% rename from src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java rename to src/main/java/de/zendric/app/xpensely_server/model/AppUser.java index ff060d9..2b495e2 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/model/AppUser.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java @@ -1,26 +1,24 @@ -package de.zendric.app.XpenselyServer.model; +package de.zendric.app.xpensely_server.model; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -@Entity @Getter @Setter -@AllArgsConstructor @NoArgsConstructor +@Entity public class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "username", nullable = false, unique = true) private String username; - private String password; - private String email; } diff --git a/src/main/java/de/zendric/app/xpensely_server/model/Expense.java b/src/main/java/de/zendric/app/xpensely_server/model/Expense.java new file mode 100644 index 0000000..828eaae --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/Expense.java @@ -0,0 +1,43 @@ +package de.zendric.app.xpensely_server.model; + +import java.time.LocalDate; + +import com.fasterxml.jackson.annotation.JsonBackReference; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Entity +@AllArgsConstructor +@NoArgsConstructor +public class Expense { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + @ManyToOne + private AppUser owner; + + private Double amount; + private Double deviation; + + private LocalDate date; + + @ManyToOne + @JoinColumn(name = "expense_list_id", nullable = false) + @JsonBackReference + private ExpenseList expenseList; +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java similarity index 58% rename from src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java rename to src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java index c7a2a5a..49a742d 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/model/ExpenseList.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java @@ -1,7 +1,9 @@ -package de.zendric.app.XpenselyServer.model; +package de.zendric.app.xpensely_server.model; import java.util.List; +import com.fasterxml.jackson.annotation.JsonManagedReference; + import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -10,17 +12,21 @@ import jakarta.persistence.Id; import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Entity @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class ExpenseList { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - + private String name; @ManyToOne @@ -29,6 +35,18 @@ public class ExpenseList { @ManyToMany private List sharedWith; - @OneToMany(mappedBy = "expenseList", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "expenseList", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference private List expenses; + + public void addExpense(Expense expense) { + expense.setExpenseList(this); + expenses.add(expense); + + } + + public void removeExpense(Expense expense) { + expenses.remove(expense); + expense.setExpenseList(null); + } } diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java similarity index 57% rename from src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java rename to src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java index ad2301b..613e091 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseListRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java @@ -1,11 +1,13 @@ -package de.zendric.app.XpenselyServer.repo; - -import org.springframework.data.jpa.repository.JpaRepository; - -import de.zendric.app.XpenselyServer.model.ExpenseList; +package de.zendric.app.xpensely_server.repo; import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import de.zendric.app.xpensely_server.model.ExpenseList; + +@Repository public interface ExpenseListRepository extends JpaRepository { List findByOwnerId(Long ownerId); } \ No newline at end of file diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java similarity index 63% rename from src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java rename to src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java index 3590d36..c79b08b 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/repo/ExpenseRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java @@ -1,9 +1,10 @@ -package de.zendric.app.XpenselyServer.repo; +package de.zendric.app.xpensely_server.repo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import de.zendric.app.XpenselyServer.model.Expense; +import de.zendric.app.xpensely_server.model.Expense; @Repository -public interface ExpenseRepository extends JpaRepository {} +public interface ExpenseRepository extends JpaRepository { +} diff --git a/src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java similarity index 50% rename from src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java rename to src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java index 926b70a..23bb4ee 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/repo/UserRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java @@ -1,9 +1,13 @@ -package de.zendric.app.XpenselyServer.repo; +package de.zendric.app.xpensely_server.repo; + +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import de.zendric.app.XpenselyServer.model.AppUser; +import de.zendric.app.xpensely_server.model.AppUser; @Repository -public interface UserRepository extends JpaRepository {} +public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); +} diff --git a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java new file mode 100644 index 0000000..8843cb7 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java @@ -0,0 +1,32 @@ +package de.zendric.app.xpensely_server.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(authorize -> authorize + .anyRequest().permitAll() // Allow all requests without authentication + ) + .csrf().disable(); // Disable CSRF for development purposes + + return http.build(); + } + + // @Bean + // public SecurityFilterChain securityFilterChain(HttpSecurity http) throws + // Exception { + // return http.authorizeHttpRequests(auth -> { + // auth.requestMatchers("/").permitAll(); + // auth.anyRequest().permitAll(); + // // auth.anyRequest().authenticated(); + // }).oauth2Login(Customizer.withDefaults()) + // .build(); + // } +} diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java new file mode 100644 index 0000000..055d496 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -0,0 +1,137 @@ +package de.zendric.app.xpensely_server.services; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.Expense; +import de.zendric.app.xpensely_server.model.ExpenseList; +import de.zendric.app.xpensely_server.repo.ExpenseListRepository; +import de.zendric.app.xpensely_server.repo.ExpenseRepository; +import jakarta.persistence.EntityManager; + +@Service +@Transactional +public class ExpenseListService { + + private ExpenseListRepository repository; + private final ExpenseRepository expenseRepository; + + @Autowired + private EntityManager entityManager; + + @Autowired + public ExpenseListService(ExpenseListRepository repository, ExpenseRepository expenseRepository) { + this.repository = repository; + this.expenseRepository = expenseRepository; + } + + public List getAllLists() { + return repository.findAll(); + } + + public ExpenseList createList(ExpenseList list) { + return repository.save(list); + } + + public void deleteList(Long id) { + repository.deleteById(id); + } + + public void deleteById(Long id) { + repository.deleteById(id); + } + + public Optional findById(Long id) { + return repository.findById(id); + } + + public Iterable findAll() { + return repository.findAll(); + } + + public ExpenseList save(ExpenseList expenseList) { + return repository.save(expenseList); + } + + public List findByUserId(Long id) { + List allLists = repository.findAll(); + List userSpecificList = new ArrayList<>(); + for (ExpenseList expenseList : allLists) { + List sharedWith = expenseList.getSharedWith(); + + if (expenseList.getOwner().getId().equals(id)) { + userSpecificList.add(expenseList); + } else { + for (AppUser user : sharedWith) { + if (user.getId().equals(id)) { + userSpecificList.add(expenseList); + } + } + } + } + return userSpecificList; + } + + public List findByUsername(String username) { + List allLists = repository.findAll(); + List userSpecificList = new ArrayList<>(); + for (ExpenseList expenseList : allLists) { + List sharedWith = expenseList.getSharedWith(); + + if (expenseList.getOwner().getUsername().equals(username)) { + userSpecificList.add(expenseList); + } else { + for (AppUser user : sharedWith) { + if (user.getUsername().equals(username)) { + userSpecificList.add(expenseList); + } + } + } + } + return userSpecificList; + } + + public Expense addExpenseToList(Long expenseListId, Expense expense) { + ExpenseList expenseList = repository.findById(expenseListId) + .orElseThrow(() -> new RuntimeException("ExpenseList not found with id: " + expenseListId)); + HashSet existingId = new HashSet<>(); + for (Expense e : expenseList.getExpenses()) { + existingId.add(e.getId()); + } + expenseList.addExpense(expense); + repository.save(expenseList); + Expense newExpense = expense; + for (Expense e : expenseList.getExpenses()) { + if (!existingId.contains(e.getId())) { + newExpense = e; + break; + } + } + return newExpense; + } + + public void deleteExpenseFromList(Long expenseListId, Long expenseId) { + ExpenseList expenseList = repository.findById(expenseListId) + .orElseThrow(() -> new RuntimeException("ExpenseList not found with id: " + expenseListId)); + Expense expenseToRemove = null; + for (Expense expense : expenseList.getExpenses()) { + if (expense.getId().equals(expenseId)) { + expenseToRemove = expense; + break; + } + } + if (expenseToRemove != null) { + expenseList.removeExpense(expenseToRemove); + } else { + throw new RuntimeException("Expense not found with id: " + expenseId); + } + repository.save(expenseList); + } +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseService.java new file mode 100644 index 0000000..3d45353 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseService.java @@ -0,0 +1,19 @@ +package de.zendric.app.xpensely_server.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import de.zendric.app.xpensely_server.repo.ExpenseRepository; + +@Service +@Transactional +public class ExpenseService { + + @Autowired + private ExpenseRepository repository; + + public ExpenseService(ExpenseRepository repository) { + this.repository = repository; + } +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/XpenselyServer/services/UserService.java b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java similarity index 55% rename from src/main/java/de/zendric/app/XpenselyServer/services/UserService.java rename to src/main/java/de/zendric/app/xpensely_server/services/UserService.java index 68a3cec..66371ae 100644 --- a/src/main/java/de/zendric/app/XpenselyServer/services/UserService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java @@ -1,12 +1,12 @@ -package de.zendric.app.XpenselyServer.services; +package de.zendric.app.xpensely_server.services; import java.util.List; import java.util.Optional; import org.springframework.stereotype.Service; -import de.zendric.app.XpenselyServer.model.AppUser; -import de.zendric.app.XpenselyServer.repo.UserRepository; +import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.repo.UserRepository; @Service public class UserService { @@ -25,19 +25,27 @@ public class UserService { } public AppUser getUser(Long id) { - Optional user= userRepository.findById(id); + Optional user = userRepository.findById(id); if (user.isPresent()) { return user.get(); - } - else return null; + } else + return null; } public AppUser deleteUserById(Long id) { - Optional user= userRepository.findById(id); + Optional user = userRepository.findById(id); if (user.isPresent()) { userRepository.deleteById(id); return user.get(); - } - else return null; + } else + return null; + } + + public AppUser getUserByName(String username) { + Optional optUser = userRepository.findByUsername(username); + if (optUser.isPresent()) { + return optUser.get(); + } else + return null; } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bc19518..5591bdb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,16 @@ +#Server spring.application.name=XpenselyServer + +#Security +spring.security.enabled=false +#logging.level.org.springframework.security=TRACE + +# PostgreSQL Configuration +spring.datasource.url=jdbc:postgresql://localhost:5432/Xpensely +spring.datasource.username=${XpenselyDBUser} +spring.datasource.password=${XpenselyDBPW} +spring.datasource.driver-class-name=org.postgresql.Driver + +# Hibernate configuration +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect diff --git a/src/test/java/de/zendric/app/XpenselyServer/XpenselyServerApplicationTests.java b/src/test/java/de/zendric/app/xpensely_Server/XpenselyServerApplicationTests.java similarity index 82% rename from src/test/java/de/zendric/app/XpenselyServer/XpenselyServerApplicationTests.java rename to src/test/java/de/zendric/app/xpensely_Server/XpenselyServerApplicationTests.java index 5f228b2..3c78751 100644 --- a/src/test/java/de/zendric/app/XpenselyServer/XpenselyServerApplicationTests.java +++ b/src/test/java/de/zendric/app/xpensely_Server/XpenselyServerApplicationTests.java @@ -1,4 +1,4 @@ -package de.zendric.app.XpenselyServer; +package de.zendric.app.xpensely_Server; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; From e20be63e5e34c71f63e1cabd8191b396d4585638 Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 25 Dec 2024 01:04:05 +0100 Subject: [PATCH 04/32] Oauth setup --- pom.xml | 4 ++++ .../controller/HomeController.java | 13 +++++++++++ .../security/SecurityConfig.java | 22 +++++-------------- 3 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 src/main/java/de/zendric/app/xpensely_server/controller/HomeController.java diff --git a/pom.xml b/pom.xml index fdac1ff..d9decdd 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,10 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.boot + spring-boot-starter-oauth2-client + org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/HomeController.java b/src/main/java/de/zendric/app/xpensely_server/controller/HomeController.java new file mode 100644 index 0000000..12a8948 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/controller/HomeController.java @@ -0,0 +1,13 @@ +package de.zendric.app.xpensely_server.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +class HomeController { + + @GetMapping("/") + public String getAll() { + return "Welcome"; + } +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java index 8843cb7..1d7caae 100644 --- a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java +++ b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java @@ -2,31 +2,19 @@ package de.zendric.app.xpensely_server.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration +@EnableWebSecurity public class SecurityConfig { - @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(authorize -> authorize - .anyRequest().permitAll() // Allow all requests without authentication - ) - .csrf().disable(); // Disable CSRF for development purposes + http.authorizeHttpRequests(auth -> auth + .anyRequest().authenticated()).oauth2Login(Customizer.withDefaults()); return http.build(); } - - // @Bean - // public SecurityFilterChain securityFilterChain(HttpSecurity http) throws - // Exception { - // return http.authorizeHttpRequests(auth -> { - // auth.requestMatchers("/").permitAll(); - // auth.anyRequest().permitAll(); - // // auth.anyRequest().authenticated(); - // }).oauth2Login(Customizer.withDefaults()) - // .build(); - // } } From 4df0b36f45e5fbd1d46c8ac72d8cf19c0612af5b Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 28 Dec 2024 01:35:50 +0100 Subject: [PATCH 05/32] Sharing Lists logic --- .../XpenselyServerApplication.java | 2 + .../controller/AppUserController.java | 9 +++++ .../controller/ExpenseListController.java | 38 ++++++++++++++++++- .../xpensely_server/model/ExpenseList.java | 11 ++++-- .../xpensely_server/model/InviteRequest.java | 13 +++++++ .../repo/ExpenseListRepository.java | 2 + .../services/CleanupService.java | 34 +++++++++++++++++ .../services/ExpenseListService.java | 37 +++++++++++++----- .../xpensely_server/services/UserService.java | 1 + 9 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/InviteRequest.java create mode 100644 src/main/java/de/zendric/app/xpensely_server/services/CleanupService.java diff --git a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java index 420f3e5..ae78652 100644 --- a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java +++ b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java @@ -2,8 +2,10 @@ package de.zendric.app.xpensely_server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class XpenselyServerApplication { public static void main(String[] args) { diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java index 4714ecc..4332c3c 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java @@ -3,6 +3,7 @@ package de.zendric.app.xpensely_server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -41,6 +42,14 @@ public class AppUserController { return ResponseEntity.status(HttpStatus.CREATED).body(appUser); } + @PostMapping("/createUser") + public ResponseEntity createUsername(@RequestBody AppUser user, Authentication authentication) { + String googleUserId = authentication.getName(); + // Validate and store the username with googleUserId + + return ResponseEntity.ok("Username created successfully"); + } + @DeleteMapping public String deleteUser(@RequestParam Long id) { AppUser user = userService.deleteUserById(id); diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index b39ccc1..0bdc51a 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -1,5 +1,6 @@ package de.zendric.app.xpensely_server.controller; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -16,19 +17,24 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import de.zendric.app.xpensely_server.model.AppUser; import de.zendric.app.xpensely_server.model.Expense; import de.zendric.app.xpensely_server.model.ExpenseList; +import de.zendric.app.xpensely_server.model.InviteRequest; import de.zendric.app.xpensely_server.services.ExpenseListService; +import de.zendric.app.xpensely_server.services.UserService; @RestController @RequestMapping("/api/expenselist") class ExpenseListController { private ExpenseListService expenseListService; + private UserService userService; @Autowired - public ExpenseListController(ExpenseListService expenseListService) { + public ExpenseListController(ExpenseListService expenseListService, UserService userService) { this.expenseListService = expenseListService; + this.userService = userService; } @GetMapping("/all") @@ -129,4 +135,34 @@ class ExpenseListController { return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); } } + + @PostMapping("/{listId}/invite") + public ResponseEntity generateInvite(@PathVariable Long listId) { + String inviteCode = expenseListService.generateInviteCode(listId); + return ResponseEntity.ok(inviteCode); + } + + @PostMapping("/accept-invite") + + public ResponseEntity acceptInvite(@RequestBody InviteRequest inviteRequest) { + ExpenseList list = expenseListService.findByInviteCode(inviteRequest.getInviteCode()); + + if (list == null || list.getInviteCodeExpiration() == null || + list.getInviteCodeExpiration().isBefore(LocalDateTime.now())) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Invalid or expired invite code"); + } + AppUser user = null; + try { + user = userService.getUser(inviteRequest.getUserId()); + } catch (Exception e) { + throw new RuntimeException("User not found"); + } + if (user != null) { + list.setSharedWith(user); + expenseListService.save(list); + } else { + throw new RuntimeException("User not found"); + } + return ResponseEntity.ok("User added to the list"); + } } diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java index 49a742d..56e2aee 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java @@ -1,7 +1,9 @@ package de.zendric.app.xpensely_server.model; +import java.time.LocalDateTime; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.CascadeType; @@ -9,7 +11,6 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import lombok.AllArgsConstructor; @@ -29,11 +30,15 @@ public class ExpenseList { private String name; + private String inviteCode; + @JsonIgnore + private LocalDateTime inviteCodeExpiration; + @ManyToOne private AppUser owner; - @ManyToMany - private List sharedWith; + @ManyToOne + private AppUser sharedWith; @OneToMany(mappedBy = "expenseList", cascade = CascadeType.ALL, orphanRemoval = true) @JsonManagedReference diff --git a/src/main/java/de/zendric/app/xpensely_server/model/InviteRequest.java b/src/main/java/de/zendric/app/xpensely_server/model/InviteRequest.java new file mode 100644 index 0000000..81ac35a --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/InviteRequest.java @@ -0,0 +1,13 @@ +package de.zendric.app.xpensely_server.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class InviteRequest { + private String inviteCode; + private Long userId; +} diff --git a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java index 613e091..c42b820 100644 --- a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseListRepository.java @@ -10,4 +10,6 @@ import de.zendric.app.xpensely_server.model.ExpenseList; @Repository public interface ExpenseListRepository extends JpaRepository { List findByOwnerId(Long ownerId); + + ExpenseList findByInviteCode(String inviteCode); } \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/services/CleanupService.java b/src/main/java/de/zendric/app/xpensely_server/services/CleanupService.java new file mode 100644 index 0000000..cbfd06c --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/services/CleanupService.java @@ -0,0 +1,34 @@ +package de.zendric.app.xpensely_server.services; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import de.zendric.app.xpensely_server.model.ExpenseList; +import de.zendric.app.xpensely_server.repo.ExpenseListRepository; + +@Service + +public class CleanupService { + + @Autowired + private ExpenseListRepository expenseListRepository; + + @Scheduled(cron = "0 0 0 * * ?") // Runs daily at midnight + public void cleanupExpiredInvites() { + List expiredLists = expenseListRepository.findAll().stream() + .filter(list -> list.getInviteCodeExpiration() != null && + list.getInviteCodeExpiration().isBefore(LocalDateTime.now())) + .collect(Collectors.toList()); + + for (ExpenseList list : expiredLists) { + list.setInviteCode(null); + list.setInviteCodeExpiration(null); + expenseListRepository.save(list); + } + } +} diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java index 055d496..cad04d2 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -1,9 +1,11 @@ package de.zendric.app.xpensely_server.services; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -64,15 +66,13 @@ public class ExpenseListService { List allLists = repository.findAll(); List userSpecificList = new ArrayList<>(); for (ExpenseList expenseList : allLists) { - List sharedWith = expenseList.getSharedWith(); + AppUser sharedWith = expenseList.getSharedWith(); if (expenseList.getOwner().getId().equals(id)) { userSpecificList.add(expenseList); } else { - for (AppUser user : sharedWith) { - if (user.getId().equals(id)) { - userSpecificList.add(expenseList); - } + if (sharedWith.getId().equals(id)) { + userSpecificList.add(expenseList); } } } @@ -83,19 +83,18 @@ public class ExpenseListService { List allLists = repository.findAll(); List userSpecificList = new ArrayList<>(); for (ExpenseList expenseList : allLists) { - List sharedWith = expenseList.getSharedWith(); + AppUser sharedWith = expenseList.getSharedWith(); if (expenseList.getOwner().getUsername().equals(username)) { userSpecificList.add(expenseList); } else { - for (AppUser user : sharedWith) { - if (user.getUsername().equals(username)) { - userSpecificList.add(expenseList); - } + if (sharedWith.getUsername().equals(username)) { + userSpecificList.add(expenseList); } } } return userSpecificList; + } public Expense addExpenseToList(Long expenseListId, Expense expense) { @@ -134,4 +133,22 @@ public class ExpenseListService { } repository.save(expenseList); } + + public String generateInviteCode(Long listId) { + ExpenseList list = repository.findById(listId) + .orElseThrow(() -> new RuntimeException("List not found")); + + String inviteCode = UUID.randomUUID().toString().substring(0, 6).toUpperCase(); + LocalDateTime expirationTime = LocalDateTime.now().plusWeeks(1); + + list.setInviteCode(inviteCode); + list.setInviteCodeExpiration(expirationTime); + repository.save(list); + + return inviteCode; + } + + public ExpenseList findByInviteCode(String inviteCode) { + return repository.findByInviteCode(inviteCode); + } } \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java index 66371ae..6475a49 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java @@ -48,4 +48,5 @@ public class UserService { } else return null; } + } \ No newline at end of file From 0ee56e4e52ce4676faefd827d1418e8a8ddd8169 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sun, 29 Dec 2024 00:47:10 +0100 Subject: [PATCH 06/32] Fixed bug in finding ExpenseLists --- .../app/xpensely_server/controller/ExpenseListController.java | 4 +++- .../app/xpensely_server/services/ExpenseListService.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index 0bdc51a..d0329fa 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -143,7 +143,6 @@ class ExpenseListController { } @PostMapping("/accept-invite") - public ResponseEntity acceptInvite(@RequestBody InviteRequest inviteRequest) { ExpenseList list = expenseListService.findByInviteCode(inviteRequest.getInviteCode()); @@ -151,6 +150,9 @@ class ExpenseListController { list.getInviteCodeExpiration().isBefore(LocalDateTime.now())) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Invalid or expired invite code"); } + if (list.getSharedWith() != null) { + return ResponseEntity.status(HttpStatus.IM_USED).body("List has already been shared"); + } AppUser user = null; try { user = userService.getUser(inviteRequest.getUserId()); diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java index cad04d2..f80ee3b 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -71,7 +71,7 @@ public class ExpenseListService { if (expenseList.getOwner().getId().equals(id)) { userSpecificList.add(expenseList); } else { - if (sharedWith.getId().equals(id)) { + if (sharedWith != null && sharedWith.getId().equals(id)) { userSpecificList.add(expenseList); } } @@ -88,7 +88,7 @@ public class ExpenseListService { if (expenseList.getOwner().getUsername().equals(username)) { userSpecificList.add(expenseList); } else { - if (sharedWith.getUsername().equals(username)) { + if (sharedWith != null && sharedWith.getUsername().equals(username)) { userSpecificList.add(expenseList); } } From b3d5b5ad115759c88a8c49e8da8495a2b883ceeb Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 31 Dec 2024 01:14:28 +0100 Subject: [PATCH 07/32] Bugfix, support for Expense without AppUser id --- .../controller/ExpenseListController.java | 25 +++++++++- .../xpensely_server/model/ExpenseInput.java | 50 +++++++++++++++++++ .../services/ExpenseListService.java | 23 ++++++--- 3 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index d0329fa..1fc6a4e 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController; import de.zendric.app.xpensely_server.model.AppUser; import de.zendric.app.xpensely_server.model.Expense; +import de.zendric.app.xpensely_server.model.ExpenseInput; import de.zendric.app.xpensely_server.model.ExpenseList; import de.zendric.app.xpensely_server.model.InviteRequest; import de.zendric.app.xpensely_server.services.ExpenseListService; @@ -95,9 +96,23 @@ class ExpenseListController { @PostMapping public ResponseEntity create(@RequestBody ExpenseList expenseList) { try { - ExpenseList savedItem = (ExpenseList) expenseListService.save(expenseList); + // Fetch or persist the `owner` AppUser + if (expenseList.getOwner() != null) { + AppUser existingOwner = userService.getUser(expenseList.getOwner().getId()); + if (existingOwner == null) { + throw new IllegalArgumentException("Owner does not exist."); + } + expenseList.setOwner(existingOwner); + } else { + throw new IllegalArgumentException("Owner is required."); + } + + expenseList.setSharedWith(null); + + ExpenseList savedItem = expenseListService.createList(expenseList); return new ResponseEntity<>(savedItem, HttpStatus.CREATED); } catch (Exception e) { + e.printStackTrace(); return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); } } @@ -115,8 +130,11 @@ class ExpenseListController { @PostMapping("/{id}/add") public ResponseEntity addExpenseToList( @PathVariable("id") Long expenseListId, - @RequestBody Expense expense) { + @RequestBody ExpenseInput expenseInput) { try { + AppUser expenseOwner = userService.getUserByName(expenseInput.getOwner()); + Expense expense = expenseInput.convertToExpense(expenseOwner.getId()); + Expense addedExpense = expenseListService.addExpenseToList(expenseListId, expense); return new ResponseEntity<>(addedExpense, HttpStatus.CREATED); } catch (Exception e) { @@ -153,6 +171,9 @@ class ExpenseListController { if (list.getSharedWith() != null) { return ResponseEntity.status(HttpStatus.IM_USED).body("List has already been shared"); } + if (list.getOwner().getId() == inviteRequest.getUserId()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("You cant join your own List"); + } AppUser user = null; try { user = userService.getUser(inviteRequest.getUserId()); diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java new file mode 100644 index 0000000..5d8418f --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java @@ -0,0 +1,50 @@ +package de.zendric.app.xpensely_server.model; + +import java.time.LocalDate; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class ExpenseInput { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + private String owner; + + private Double amount; + private Double deviation; + + private LocalDate date; + + private ExpenseList expenseList; + + public Expense convertToExpense(Long userId) { + AppUser appUser = new AppUser(); + appUser.setId(userId); + appUser.setUsername(owner); + + Expense expense = new Expense(); + expense.setAmount(amount); + expense.setDate(date); + expense.setDeviation(deviation); + expense.setExpenseList(expenseList); + expense.setId(id); + expense.setOwner(appUser); + expense.setTitle(title); + + return expense; + } +} diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java index f80ee3b..c37733f 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -98,15 +98,20 @@ public class ExpenseListService { } public Expense addExpenseToList(Long expenseListId, Expense expense) { + // find expenseList ExpenseList expenseList = repository.findById(expenseListId) .orElseThrow(() -> new RuntimeException("ExpenseList not found with id: " + expenseListId)); + // get all added expenses HashSet existingId = new HashSet<>(); for (Expense e : expenseList.getExpenses()) { existingId.add(e.getId()); } + // add the new expense expenseList.addExpense(expense); + // save repository.save(expenseList); - Expense newExpense = expense; + + Expense newExpense = new Expense(); for (Expense e : expenseList.getExpenses()) { if (!existingId.contains(e.getId())) { newExpense = e; @@ -137,14 +142,18 @@ public class ExpenseListService { public String generateInviteCode(Long listId) { ExpenseList list = repository.findById(listId) .orElseThrow(() -> new RuntimeException("List not found")); + String inviteCode; + if (list.getInviteCode() == null || list.getInviteCodeExpiration().isBefore(LocalDateTime.now())) { - String inviteCode = UUID.randomUUID().toString().substring(0, 6).toUpperCase(); - LocalDateTime expirationTime = LocalDateTime.now().plusWeeks(1); - - list.setInviteCode(inviteCode); - list.setInviteCodeExpiration(expirationTime); - repository.save(list); + inviteCode = UUID.randomUUID().toString().substring(0, 6).toUpperCase(); + LocalDateTime expirationTime = LocalDateTime.now().plusWeeks(1); + list.setInviteCode(inviteCode); + list.setInviteCodeExpiration(expirationTime); + repository.save(list); + } else { + inviteCode = list.getInviteCode(); + } return inviteCode; } From f26f365b3c52b37c372e055aabe0d13e46f93054 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sun, 5 Jan 2025 01:30:28 +0100 Subject: [PATCH 08/32] +Data Structure Changes +Api Functionality --- pom.xml | 4 ++ .../controller/ExpenseListController.java | 22 +++++++++++ .../app/xpensely_server/model/Expense.java | 3 +- .../model/ExpenseChangeRequest.java | 39 +++++++++++++++++++ .../xpensely_server/model/ExpenseInput.java | 5 ++- .../xpensely_server/model/ExpenseList.java | 1 + .../repo/ExpenseRepository.java | 3 ++ .../security/SecurityConfig.java | 21 +++++++++- .../services/ExpenseListService.java | 21 ++++++++++ 9 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/ExpenseChangeRequest.java diff --git a/pom.xml b/pom.xml index d9decdd..6abd81e 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,10 @@ spring-boot-starter-security + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + org.springframework.boot spring-boot-starter-oauth2-client diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index 1fc6a4e..705b306 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController; import de.zendric.app.xpensely_server.model.AppUser; import de.zendric.app.xpensely_server.model.Expense; +import de.zendric.app.xpensely_server.model.ExpenseChangeRequest; import de.zendric.app.xpensely_server.model.ExpenseInput; import de.zendric.app.xpensely_server.model.ExpenseList; import de.zendric.app.xpensely_server.model.InviteRequest; @@ -142,6 +144,26 @@ class ExpenseListController { } } + @PutMapping("/{id}/update") + public ResponseEntity updateExpenseInList( + @PathVariable("id") Long expenseListId, + @RequestBody ExpenseChangeRequest expenseChangeRequest) { + try { + AppUser expenseOwner = userService.getUserByName(expenseChangeRequest.getOwnerName()); + Optional expenseList = expenseListService.findById(expenseListId); + if (expenseList.isPresent()) { + Expense expense = expenseChangeRequest.convertToExpense(expenseOwner.getId(), expenseList.get()); + + Expense addedExpense = expenseListService.updateExpense(expenseListId, expense); + return new ResponseEntity<>(addedExpense, HttpStatus.CREATED); + } + return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + } + } + @DeleteMapping("/{id}/delete") public ResponseEntity deleteExpenseFromList( @PathVariable("id") Long expenseListId, diff --git a/src/main/java/de/zendric/app/xpensely_server/model/Expense.java b/src/main/java/de/zendric/app/xpensely_server/model/Expense.java index 828eaae..ab7bab7 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/Expense.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/Expense.java @@ -32,7 +32,8 @@ public class Expense { private AppUser owner; private Double amount; - private Double deviation; + private Double personalUseAmount; + private Double otherPersonAmount; private LocalDate date; diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseChangeRequest.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseChangeRequest.java new file mode 100644 index 0000000..c028b00 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseChangeRequest.java @@ -0,0 +1,39 @@ +package de.zendric.app.xpensely_server.model; + +import java.time.LocalDate; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ExpenseChangeRequest { + + private Long id; + private String title; + private String ownerName; + private Double amount; + private Double personalUseAmount; + private Double otherPersonAmount; + private LocalDate date; + + public Expense convertToExpense(Long userId, ExpenseList expenseList) { + AppUser appUser = new AppUser(); + appUser.setId(userId); + appUser.setUsername(ownerName); + + Expense expense = new Expense(); + expense.setAmount(amount); + expense.setDate(date); + expense.setPersonalUseAmount(personalUseAmount); + expense.setOtherPersonAmount(otherPersonAmount); + expense.setExpenseList(expenseList); + expense.setId(id); + expense.setOwner(appUser); + expense.setTitle(title); + + return expense; + } +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java index 5d8418f..a36c7cf 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java @@ -25,7 +25,8 @@ public class ExpenseInput { private String owner; private Double amount; - private Double deviation; + private Double personalUseAmount; + private Double otherPersonAmount; private LocalDate date; @@ -39,7 +40,7 @@ public class ExpenseInput { Expense expense = new Expense(); expense.setAmount(amount); expense.setDate(date); - expense.setDeviation(deviation); + expense.setPersonalUseAmount(personalUseAmount); expense.setExpenseList(expenseList); expense.setId(id); expense.setOwner(appUser); diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java index 56e2aee..a31f9de 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseList.java @@ -42,6 +42,7 @@ public class ExpenseList { @OneToMany(mappedBy = "expenseList", cascade = CascadeType.ALL, orphanRemoval = true) @JsonManagedReference + @jakarta.persistence.OrderBy("date ASC, id ASC") private List expenses; public void addExpense(Expense expense) { diff --git a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java index c79b08b..fe7a610 100644 --- a/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/ExpenseRepository.java @@ -1,5 +1,7 @@ package de.zendric.app.xpensely_server.repo; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -7,4 +9,5 @@ import de.zendric.app.xpensely_server.model.Expense; @Repository public interface ExpenseRepository extends JpaRepository { + List findAllByOrderByDateAsc(); } diff --git a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java index 1d7caae..72d7e71 100644 --- a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java +++ b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java @@ -10,10 +10,27 @@ import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { + // @Bean + // public SecurityFilterChain securityFilterChain(HttpSecurity http) throws + // Exception { + // http.authorizeHttpRequests(auth -> auth + // .anyRequest().permitAll()).csrf().disable(); + // ; + + // return http.build(); + // } + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http.authorizeHttpRequests(auth -> auth - .anyRequest().authenticated()).oauth2Login(Customizer.withDefaults()); + http + .authorizeHttpRequests(auth -> auth + .anyRequest().authenticated() // Require authentication for all requests + ) + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(Customizer.withDefaults()) // Enable JWT validation + ) + .oauth2Login(Customizer.withDefaults()) // Optional if you want OAuth2 login + .csrf().disable(); // Disable CSRF for simplicity in APIs (consider enabling it for forms) return http.build(); } diff --git a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java index c37733f..445ab1f 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/ExpenseListService.java @@ -160,4 +160,25 @@ public class ExpenseListService { public ExpenseList findByInviteCode(String inviteCode) { return repository.findByInviteCode(inviteCode); } + + public Expense updateExpense(Long expenseListId, Expense updatedExpense) { + ExpenseList expenseList = repository.findById(expenseListId) + .orElseThrow(() -> new IllegalArgumentException("ExpenseList not found")); + + if (!expenseList.getExpenses().stream() + .anyMatch(expense -> expense.getId().equals(updatedExpense.getId()))) { + throw new IllegalArgumentException("Expense does not belong to the specified ExpenseList"); + } + + Expense existingExpense = expenseRepository.findById(updatedExpense.getId()) + .orElseThrow(() -> new IllegalArgumentException("Expense not found")); + existingExpense.setTitle(updatedExpense.getTitle()); + existingExpense.setAmount(updatedExpense.getAmount()); + existingExpense.setPersonalUseAmount(updatedExpense.getPersonalUseAmount()); + existingExpense.setOtherPersonAmount(updatedExpense.getOtherPersonAmount()); + existingExpense.setDate(updatedExpense.getDate()); + existingExpense.setOwner(updatedExpense.getOwner()); + + return expenseRepository.save(existingExpense); + } } \ No newline at end of file From 53a262ddb9de8e75d0ca5a811f614cf64b925506 Mon Sep 17 00:00:00 2001 From: Cedric Date: Mon, 6 Jan 2025 00:28:36 +0100 Subject: [PATCH 09/32] Never had a stupid bug dont look --- .../app/xpensely_server/controller/ExpenseListController.java | 1 - .../java/de/zendric/app/xpensely_server/model/ExpenseInput.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index 705b306..dfec7ae 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -98,7 +98,6 @@ class ExpenseListController { @PostMapping public ResponseEntity create(@RequestBody ExpenseList expenseList) { try { - // Fetch or persist the `owner` AppUser if (expenseList.getOwner() != null) { AppUser existingOwner = userService.getUser(expenseList.getOwner().getId()); if (existingOwner == null) { diff --git a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java index a36c7cf..ac2e7a6 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/ExpenseInput.java @@ -41,6 +41,7 @@ public class ExpenseInput { expense.setAmount(amount); expense.setDate(date); expense.setPersonalUseAmount(personalUseAmount); + expense.setOtherPersonAmount(otherPersonAmount); expense.setExpenseList(expenseList); expense.setId(id); expense.setOwner(appUser); From 5546b0ba3b93d1818e0a5cd0420dcc7371ac1b25 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 7 Jan 2025 23:40:00 +0100 Subject: [PATCH 10/32] better secret handling docker api upgrade --- .gitignore | 1 + docker-compose.yml | 30 ++++++++++++++ dockerfile | 7 ++++ pom.xml | 10 +++-- .../XpenselyServerApplication.java | 9 +++++ .../controller/AppUserController.java | 33 ++++++++++----- .../controller/ExpenseListController.java | 2 +- .../app/xpensely_server/model/AppUser.java | 16 ++++++++ .../model/AppUserCreateRequest.java | 21 ++++++++++ .../model/UsernameAlreadyExistsException.java | 11 +++++ .../xpensely_server/repo/UserRepository.java | 4 ++ .../security/SecurityConfig.java | 40 +++++++++---------- .../xpensely_server/services/UserService.java | 12 ++++++ src/main/resources/application.properties | 10 +++-- 14 files changed, 168 insertions(+), 38 deletions(-) create mode 100644 docker-compose.yml create mode 100644 dockerfile create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/AppUserCreateRequest.java create mode 100644 src/main/java/de/zendric/app/xpensely_server/model/UsernameAlreadyExistsException.java diff --git a/.gitignore b/.gitignore index f8185d3..37fdfda 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ +.env ### STS ### .apt_generated diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..59136de --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3" + +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: XpenselyServer + ports: + - "8080:8080" + environment: + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + DB_PORT: 5432 + DB_CONTAINER: ${DB_P_CONTAINER} + DB_NAME: ${DB_P_NAME} + DB_USERNAME: ${DB_USERNAME} + DB_PASSWORD: ${DB_PASSWORD} + depends_on: + - postgresdb + + postgresdb: + image: postgres:14 + container_name: postgresdb + ports: + - "5432:5432" + environment: + POSTGRES_DB: ${DB_P_NAME} + POSTGRES_USER: ${DB_USERNAME} + POSTGRES_PASSWORD: ${DB_PASSWORD} diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..f7e9a76 --- /dev/null +++ b/dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:17-jdk-slim + +COPY ./target/*.jar app.jar + +EXPOSE 8080 + +ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6abd81e..f7c275f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,9 +10,9 @@ de.zendric.app XpenselyServer - 0.0.1-SNAPSHOT + 1.0.0 XpenselyServer - Demo project for Spring Boot + XpenselyServer used to handle the Xpensely App @@ -50,7 +50,11 @@ org.springframework.boot spring-boot-starter-web - + + io.github.cdimascio + dotenv-java + 3.1.0 + org.springframework.boot spring-boot-devtools diff --git a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java index ae78652..d9678e8 100644 --- a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java +++ b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java @@ -4,11 +4,20 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; +import io.github.cdimascio.dotenv.Dotenv; + @SpringBootApplication @EnableScheduling public class XpenselyServerApplication { public static void main(String[] args) { + Dotenv dotenv = Dotenv.load(); // Loads the .env file + System.setProperty("GOOGLE_CLIENT_ID", dotenv.get("GOOGLE_CLIENT_ID")); + System.setProperty("GOOGLE_CLIENT_SECRET", dotenv.get("GOOGLE_CLIENT_SECRET")); + System.setProperty("DB_USERNAME", dotenv.get("DB_USERNAME")); + System.setProperty("DB_PASSWORD", dotenv.get("DB_PASSWORD")); + System.setProperty("DB_DEV_CONTAINER", dotenv.get("DB_DEV_CONTAINER")); + System.setProperty("DB_DEV_NAME", dotenv.get("DB_DEV_NAME")); SpringApplication.run(XpenselyServerApplication.class, args); } diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java index 4332c3c..e0b9994 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/AppUserController.java @@ -3,7 +3,6 @@ package de.zendric.app.xpensely_server.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -13,6 +12,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.AppUserCreateRequest; +import de.zendric.app.xpensely_server.model.UsernameAlreadyExistsException; import de.zendric.app.xpensely_server.services.UserService; @RestController @@ -36,18 +37,32 @@ public class AppUserController { return userService.getUserByName(username); } - @PostMapping - public ResponseEntity createUser(@RequestBody AppUser user) { - AppUser appUser = userService.createUser(user); - return ResponseEntity.status(HttpStatus.CREATED).body(appUser); + @GetMapping("/byGoogleId") + public ResponseEntity getUserByGoogleId(@RequestParam String id) { + try { + AppUser userByGoogleId = userService.getUserByGoogleId(id); + return new ResponseEntity<>(userByGoogleId, HttpStatus.OK); + + } catch (IllegalArgumentException e) { + return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + } } @PostMapping("/createUser") - public ResponseEntity createUsername(@RequestBody AppUser user, Authentication authentication) { - String googleUserId = authentication.getName(); - // Validate and store the username with googleUserId + public ResponseEntity createUser(@RequestBody AppUserCreateRequest userRequest) { + try { + AppUser convertedUser = userRequest.convertToAppUser(); + + AppUser nUser = userService.createUser(convertedUser); + return new ResponseEntity<>(nUser, HttpStatus.CREATED); + } catch (UsernameAlreadyExistsException e) { + return new ResponseEntity<>(null, HttpStatus.CONFLICT); + } catch (Exception e) { + return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + } - return ResponseEntity.ok("Username created successfully"); } @DeleteMapping diff --git a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java index dfec7ae..3a407fb 100644 --- a/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java +++ b/src/main/java/de/zendric/app/xpensely_server/controller/ExpenseListController.java @@ -95,7 +95,7 @@ class ExpenseListController { } } - @PostMapping + @PostMapping("/create") public ResponseEntity create(@RequestBody ExpenseList expenseList) { try { if (expenseList.getOwner() != null) { diff --git a/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java b/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java index 2b495e2..90a0cd9 100644 --- a/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java +++ b/src/main/java/de/zendric/app/xpensely_server/model/AppUser.java @@ -1,10 +1,17 @@ package de.zendric.app.xpensely_server.model; +import java.time.LocalDateTime; + +import org.hibernate.annotations.CreationTimestamp; + +import com.fasterxml.jackson.annotation.JsonIgnore; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,6 +20,7 @@ import lombok.Setter; @Setter @NoArgsConstructor @Entity +@EqualsAndHashCode public class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -21,4 +29,12 @@ public class AppUser { @Column(name = "username", nullable = false, unique = true) private String username; + @JsonIgnore + private String googleId; + + @Column(updatable = false) + @CreationTimestamp + @JsonIgnore + private LocalDateTime createdAt; + } diff --git a/src/main/java/de/zendric/app/xpensely_server/model/AppUserCreateRequest.java b/src/main/java/de/zendric/app/xpensely_server/model/AppUserCreateRequest.java new file mode 100644 index 0000000..3ae678d --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/AppUserCreateRequest.java @@ -0,0 +1,21 @@ +package de.zendric.app.xpensely_server.model; + +import jakarta.persistence.Column; +import lombok.Data; + +@Data +public class AppUserCreateRequest { + + @Column(name = "username", nullable = false, unique = true) + private String username; + + private String googleId; + + public AppUser convertToAppUser() { + AppUser appUser = new AppUser(); + appUser.setGoogleId(googleId); + appUser.setUsername(username); + + return appUser; + } +} diff --git a/src/main/java/de/zendric/app/xpensely_server/model/UsernameAlreadyExistsException.java b/src/main/java/de/zendric/app/xpensely_server/model/UsernameAlreadyExistsException.java new file mode 100644 index 0000000..b0645d8 --- /dev/null +++ b/src/main/java/de/zendric/app/xpensely_server/model/UsernameAlreadyExistsException.java @@ -0,0 +1,11 @@ +package de.zendric.app.xpensely_server.model; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.CONFLICT) +public class UsernameAlreadyExistsException extends RuntimeException { + public UsernameAlreadyExistsException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java b/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java index 23bb4ee..a35ba4f 100644 --- a/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java +++ b/src/main/java/de/zendric/app/xpensely_server/repo/UserRepository.java @@ -10,4 +10,8 @@ import de.zendric.app.xpensely_server.model.AppUser; @Repository public interface UserRepository extends JpaRepository { Optional findByUsername(String username); + + Optional findByGoogleId(String id); + + Boolean existsByUsername(String username); } diff --git a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java index 72d7e71..b4883a3 100644 --- a/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java +++ b/src/main/java/de/zendric/app/xpensely_server/security/SecurityConfig.java @@ -10,28 +10,26 @@ import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { - // @Bean - // public SecurityFilterChain securityFilterChain(HttpSecurity http) throws - // Exception { - // http.authorizeHttpRequests(auth -> auth - // .anyRequest().permitAll()).csrf().disable(); - // ; + // @Bean + // public SecurityFilterChain securityFilterChain(HttpSecurity http) throws + // Exception { + // http.authorizeHttpRequests(auth -> auth + // .anyRequest().permitAll()).csrf().disable(); + // ; - // return http.build(); - // } + // return http.build(); + // } - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(auth -> auth - .anyRequest().authenticated() // Require authentication for all requests - ) - .oauth2ResourceServer(oauth2 -> oauth2 - .jwt(Customizer.withDefaults()) // Enable JWT validation - ) - .oauth2Login(Customizer.withDefaults()) // Optional if you want OAuth2 login - .csrf().disable(); // Disable CSRF for simplicity in APIs (consider enabling it for forms) + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(auth -> auth + .anyRequest().authenticated()) + .oauth2ResourceServer(oauth2 -> oauth2 + .jwt(Customizer.withDefaults())) + .oauth2Login(Customizer.withDefaults()) + .csrf().disable(); - return http.build(); - } + return http.build(); + } } diff --git a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java index 6475a49..58298dc 100644 --- a/src/main/java/de/zendric/app/xpensely_server/services/UserService.java +++ b/src/main/java/de/zendric/app/xpensely_server/services/UserService.java @@ -6,6 +6,7 @@ import java.util.Optional; import org.springframework.stereotype.Service; import de.zendric.app.xpensely_server.model.AppUser; +import de.zendric.app.xpensely_server.model.UsernameAlreadyExistsException; import de.zendric.app.xpensely_server.repo.UserRepository; @Service @@ -21,6 +22,9 @@ public class UserService { } public AppUser createUser(AppUser user) { + if (Boolean.TRUE.equals(userRepository.existsByUsername(user.getUsername()))) { + throw new UsernameAlreadyExistsException("Username already exists"); + } return userRepository.save(user); } @@ -49,4 +53,12 @@ public class UserService { return null; } + public AppUser getUserByGoogleId(String id) { + Optional optUser = userRepository.findByGoogleId(id); + if (optUser.isPresent()) { + return optUser.get(); + } else + return null; + } + } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5591bdb..6983f6d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,12 +3,14 @@ spring.application.name=XpenselyServer #Security spring.security.enabled=false -#logging.level.org.springframework.security=TRACE +spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID} +spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET} +spring.security.oauth2.resourceserver.jwt.issuer-uri=https://accounts.google.com # PostgreSQL Configuration -spring.datasource.url=jdbc:postgresql://localhost:5432/Xpensely -spring.datasource.username=${XpenselyDBUser} -spring.datasource.password=${XpenselyDBPW} +spring.datasource.url=jdbc:postgresql://${DB_DEV_CONTAINER}:5432/${DB_DEV_NAME} +spring.datasource.username=${DB_USERNAME} +spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver # Hibernate configuration From 49401a8d091e64078050988bdc2448448751b02c Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 10 Jan 2025 23:49:34 +0100 Subject: [PATCH 11/32] test workflow --- .gitea/workflows/test.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .gitea/workflows/test.yml diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml new file mode 100644 index 0000000..c537cc6 --- /dev/null +++ b/.gitea/workflows/test.yml @@ -0,0 +1,19 @@ +name: Gitea Actions Demo +run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀 +on: [push] + +jobs: + Explore-Gitea-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!" + - run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}." + - name: Check out repository code + uses: actions/checkout@v4 + - run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner." + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: List files in the repository + run: | + ls ${{ gitea.workspace }} + - run: echo "🍏 This job's status is ${{ job.status }}." From 823b1182be8fc11f16510e441257678841692788 Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 10 Jan 2025 23:49:34 +0100 Subject: [PATCH 12/32] test workflow --- .gitea/workflows/test.yml | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .gitea/workflows/test.yml diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml new file mode 100644 index 0000000..7964a53 --- /dev/null +++ b/.gitea/workflows/test.yml @@ -0,0 +1,46 @@ +name: Build and Deploy Spring Boot Server + +on: + push: + branches: + - dev + +jobs: + build: + runs-on: self-hosted + + steps: + # 1. Checkout the code + - name: Checkout code + uses: actions/checkout@v2 + + # 2. Set up Java and Maven + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: "17" + + - name: Verify Maven installation + run: | + mvn -version + + # 3. Build the Spring Boot application + - name: Build Spring Boot Application + run: | + mvn clean package -DskipTests + + # 4. Set up Docker + - name: Set up Docker + run: | + docker --version + docker-compose --version + + # 5. Build the Docker image using docker-compose + - name: Build and Package Docker Image + run: | + docker-compose build + + # 6. Start the application with dependencies + - name: Start the Docker Compose Application + run: | + docker-compose up -d From 1fd1e8ae7557e7c033153a5ebf495820d98ab763 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:13:25 +0100 Subject: [PATCH 13/32] test workflow --- .gitea/workflows/build.yml | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .gitea/workflows/build.yml diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..30bdd06 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,46 @@ +name: Build and Deploy Spring Boot Server + +on: + push: + branches: + - dev + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. Checkout the code + - name: Checkout code + uses: actions/checkout@v2 + + # 2. Set up Java and Maven + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: "17" + + - name: Verify Maven installation + run: | + mvn -version + + # 3. Build the Spring Boot application + - name: Build Spring Boot Application + run: | + mvn clean package -DskipTests + + # 4. Set up Docker + - name: Set up Docker + run: | + docker --version + docker-compose --version + + # 5. Build the Docker image using docker-compose + - name: Build and Package Docker Image + run: | + docker-compose build + + # 6. Start the application with dependencies + - name: Start the Docker Compose Application + run: | + docker-compose up -d From 38bb0f131ca75f6bd6f89be0d6393bd0a659095f Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:24:49 +0100 Subject: [PATCH 14/32] test --- .gitea/workflows/build.yml | 3 ++- .gitea/workflows/test.yml | 46 -------------------------------------- 2 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 .gitea/workflows/test.yml diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 30bdd06..c10f71b 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -18,6 +18,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: + distribution: "oracle" java-version: "17" - name: Verify Maven installation @@ -38,7 +39,7 @@ jobs: # 5. Build the Docker image using docker-compose - name: Build and Package Docker Image run: | - docker-compose build + docker build -t tea.zendric.de/zendric/XpenselyServer:1.0.0 . # 6. Start the application with dependencies - name: Start the Docker Compose Application diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml deleted file mode 100644 index 7964a53..0000000 --- a/.gitea/workflows/test.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Build and Deploy Spring Boot Server - -on: - push: - branches: - - dev - -jobs: - build: - runs-on: self-hosted - - steps: - # 1. Checkout the code - - name: Checkout code - uses: actions/checkout@v2 - - # 2. Set up Java and Maven - - name: Set up JDK - uses: actions/setup-java@v3 - with: - java-version: "17" - - - name: Verify Maven installation - run: | - mvn -version - - # 3. Build the Spring Boot application - - name: Build Spring Boot Application - run: | - mvn clean package -DskipTests - - # 4. Set up Docker - - name: Set up Docker - run: | - docker --version - docker-compose --version - - # 5. Build the Docker image using docker-compose - - name: Build and Package Docker Image - run: | - docker-compose build - - # 6. Start the application with dependencies - - name: Start the Docker Compose Application - run: | - docker-compose up -d From 96b9989a2a5256a4a4b1e5777cbb1952f8ec26c6 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:27:25 +0100 Subject: [PATCH 15/32] fix maven --- .gitea/workflows/build.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c10f71b..c0b568b 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -15,33 +15,32 @@ jobs: uses: actions/checkout@v2 # 2. Set up Java and Maven - - name: Set up JDK + - name: Set up JDK and Maven uses: actions/setup-java@v3 with: distribution: "oracle" java-version: "17" + cache: maven - - name: Verify Maven installation + # 3. Verify Maven installation + - name: Install Maven run: | + sudo apt-get update + sudo apt-get install -y maven mvn -version - # 3. Build the Spring Boot application + # 4. Build the Spring Boot application - name: Build Spring Boot Application run: | mvn clean package -DskipTests - # 4. Set up Docker + # 5. Set up Docker - name: Set up Docker run: | docker --version docker-compose --version - # 5. Build the Docker image using docker-compose + # 6. Build the Docker image - name: Build and Package Docker Image run: | docker build -t tea.zendric.de/zendric/XpenselyServer:1.0.0 . - - # 6. Start the application with dependencies - - name: Start the Docker Compose Application - run: | - docker-compose up -d From 77073ddba647a9bab65e61fb560b28f923e39c61 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:34:43 +0100 Subject: [PATCH 16/32] openjdk --- .gitea/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index c0b568b..3258471 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Set up JDK and Maven uses: actions/setup-java@v3 with: - distribution: "oracle" + distribution: "adoptopenjdk" java-version: "17" cache: maven @@ -38,7 +38,6 @@ jobs: - name: Set up Docker run: | docker --version - docker-compose --version # 6. Build the Docker image - name: Build and Package Docker Image From 6d806fbc206271c82488b23f26da84df5822960d Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:35:56 +0100 Subject: [PATCH 17/32] fix adoptium --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 3258471..8676ae2 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Set up JDK and Maven uses: actions/setup-java@v3 with: - distribution: "adoptopenjdk" + distribution: "adoptium" java-version: "17" cache: maven From 01aa12e8a2a721aad5c8238b16211901202fc2a6 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:37:32 +0100 Subject: [PATCH 18/32] temurin --- .gitea/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 8676ae2..b56a02f 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -15,10 +15,10 @@ jobs: uses: actions/checkout@v2 # 2. Set up Java and Maven - - name: Set up JDK and Maven + - name: Set up JDK (Eclipse Temurin) uses: actions/setup-java@v3 with: - distribution: "adoptium" + distribution: "temurin" java-version: "17" cache: maven From 197e40dfd5ed6bb2fa4f5d93ab5679dac2cfc6bf Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:41:19 +0100 Subject: [PATCH 19/32] fix --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index b56a02f..992ad96 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v2 # 2. Set up Java and Maven - - name: Set up JDK (Eclipse Temurin) + - name: Set up JDK (Eclipse Temurin) uses: actions/setup-java@v3 with: distribution: "temurin" From bed8a2e0f546148e565d7f18832032ca25673959 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:50:18 +0100 Subject: [PATCH 20/32] docker fix --- .gitea/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 992ad96..4d459fd 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -42,4 +42,9 @@ jobs: # 6. Build the Docker image - name: Build and Package Docker Image run: | - docker build -t tea.zendric.de/zendric/XpenselyServer:1.0.0 . + docker build -t tea.zendric.de/zendric/xpenselyServer:latest. + + # 7. Push Docker image + - name: Push the Docker Image to registry + run: | + docker push tea.zendric.de/zendric/xpenselyServer:latest. From 4fbee3852a8f465419adcfcc7fa75152dda5967d Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 00:58:27 +0100 Subject: [PATCH 21/32] docker fix --- .gitea/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 4d459fd..39c7e01 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -42,9 +42,9 @@ jobs: # 6. Build the Docker image - name: Build and Package Docker Image run: | - docker build -t tea.zendric.de/zendric/xpenselyServer:latest. + docker build -t tea.zendric.de/zendric/xpenselyServer:latest . # 7. Push Docker image - name: Push the Docker Image to registry run: | - docker push tea.zendric.de/zendric/xpenselyServer:latest. + docker push tea.zendric.de/zendric/xpenselyServer:latest . From 82cdca6f0af65ef491a7f10585f569d927ae4f68 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 01:04:20 +0100 Subject: [PATCH 22/32] aaaaahhhh --- .gitea/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 39c7e01..25e6fe8 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -42,9 +42,9 @@ jobs: # 6. Build the Docker image - name: Build and Package Docker Image run: | - docker build -t tea.zendric.de/zendric/xpenselyServer:latest . + docker build -t tea.zendric.de/zendric/xpensely-server:latest . # 7. Push Docker image - name: Push the Docker Image to registry run: | - docker push tea.zendric.de/zendric/xpenselyServer:latest . + docker push tea.zendric.de/zendric/xpensely-server:latest . From 76cfaecdda2211bcce6e8a242a07fd9e93bbc057 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 01:12:46 +0100 Subject: [PATCH 23/32] push docker --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 25e6fe8..535e586 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -47,4 +47,4 @@ jobs: # 7. Push Docker image - name: Push the Docker Image to registry run: | - docker push tea.zendric.de/zendric/xpensely-server:latest . + docker push tea.zendric.de/zendric/xpensely-server:latest From 0b624f1562b1f7cc3d822c43c55545f69a82f1cf Mon Sep 17 00:00:00 2001 From: Cedric Hornberger Date: Sat, 11 Jan 2025 11:46:19 +0100 Subject: [PATCH 24/32] docker login --- .gitea/workflows/build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 535e586..3c7eef3 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -44,7 +44,11 @@ jobs: run: | docker build -t tea.zendric.de/zendric/xpensely-server:latest . - # 7. Push Docker image + # 7. Docker login + - name: Login to Docker Registry + run: | + run: echo "${{ secrets.TEAPASSWORD }}" | docker login tea.zendric.de -u ${{ secrets.TEAUSER }} --password-stdin + # 8. Push Docker image - name: Push the Docker Image to registry run: | docker push tea.zendric.de/zendric/xpensely-server:latest From e12e8067ce45c9df5a021f36285a284779d5b0b5 Mon Sep 17 00:00:00 2001 From: Cedric Hornberger Date: Sat, 11 Jan 2025 11:58:07 +0100 Subject: [PATCH 25/32] ich dreh am login --- .gitea/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 3c7eef3..afcb658 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -47,7 +47,7 @@ jobs: # 7. Docker login - name: Login to Docker Registry run: | - run: echo "${{ secrets.TEAPASSWORD }}" | docker login tea.zendric.de -u ${{ secrets.TEAUSER }} --password-stdin + echo "${{ secrets.TEAPASSWORD }}" | docker login tea.zendric.de -u ${{ secrets.TEAUSER }} --password-stdin # 8. Push Docker image - name: Push the Docker Image to registry run: | From 12f6733b48d1318c190ed13cc6b851c515d4af43 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 14:16:49 +0100 Subject: [PATCH 26/32] fix user --- .gitea/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index afcb658..0aa927d 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -42,7 +42,7 @@ jobs: # 6. Build the Docker image - name: Build and Package Docker Image run: | - docker build -t tea.zendric.de/zendric/xpensely-server:latest . + docker build -t tea.zendric.de/cedric/xpensely-server:latest . # 7. Docker login - name: Login to Docker Registry @@ -51,4 +51,4 @@ jobs: # 8. Push Docker image - name: Push the Docker Image to registry run: | - docker push tea.zendric.de/zendric/xpensely-server:latest + docker push tea.zendric.de/cedric/xpensely-server:latest From d39b5e875c5e8150b01e626ee371e7851742571d Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 20:24:33 +0100 Subject: [PATCH 27/32] docker compose sample --- docker-compose.yml | 25 +++++++++++++------ .../XpenselyServerApplication.java | 1 + src/main/resources/application.properties | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 59136de..a4f4356 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,30 +1,41 @@ -version: "3" +version: "3.8" services: app: - build: - context: . - dockerfile: Dockerfile + image: tea.zendric.de/cedric/xpensely-server:latest container_name: XpenselyServer ports: - - "8080:8080" + - "3636:8080" environment: GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - DB_PORT: 5432 + DB_PORT: 5434 DB_CONTAINER: ${DB_P_CONTAINER} DB_NAME: ${DB_P_NAME} DB_USERNAME: ${DB_USERNAME} DB_PASSWORD: ${DB_PASSWORD} depends_on: - postgresdb + networks: + - xpensely-network postgresdb: image: postgres:14 container_name: postgresdb ports: - - "5432:5432" + - "5434:5432" environment: POSTGRES_DB: ${DB_P_NAME} POSTGRES_USER: ${DB_USERNAME} POSTGRES_PASSWORD: ${DB_PASSWORD} + networks: + - xpensely-network + volumes: + - db_data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + db_data: + +networks: + xpensely-network: diff --git a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java index d9678e8..ecd2d57 100644 --- a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java +++ b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java @@ -18,6 +18,7 @@ public class XpenselyServerApplication { System.setProperty("DB_PASSWORD", dotenv.get("DB_PASSWORD")); System.setProperty("DB_DEV_CONTAINER", dotenv.get("DB_DEV_CONTAINER")); System.setProperty("DB_DEV_NAME", dotenv.get("DB_DEV_NAME")); + System.setProperty("DB_PORT", dotenv.get("DB_PORT")); SpringApplication.run(XpenselyServerApplication.class, args); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6983f6d..833a90b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_ spring.security.oauth2.resourceserver.jwt.issuer-uri=https://accounts.google.com # PostgreSQL Configuration -spring.datasource.url=jdbc:postgresql://${DB_DEV_CONTAINER}:5432/${DB_DEV_NAME} +spring.datasource.url=jdbc:postgresql://${DB_DEV_CONTAINER}:${DB_PORT}/${DB_DEV_NAME} spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver From ece3e1d697953e6c3a2f656d61da241955d291d7 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 20:48:53 +0100 Subject: [PATCH 28/32] fix app_props --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 833a90b..cfb0d66 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_ spring.security.oauth2.resourceserver.jwt.issuer-uri=https://accounts.google.com # PostgreSQL Configuration -spring.datasource.url=jdbc:postgresql://${DB_DEV_CONTAINER}:${DB_PORT}/${DB_DEV_NAME} +spring.datasource.url=jdbc:postgresql://${DB_P_CONTAINER}:${DB_PORT}/${DB_P_NAME} spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver From 85e4a2b12571e79caee519394a170b95f20fd63a Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 21:20:58 +0100 Subject: [PATCH 29/32] remove dotenv --- pom.xml | 5 ----- .../app/xpensely_server/XpenselyServerApplication.java | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/pom.xml b/pom.xml index f7c275f..df740a9 100644 --- a/pom.xml +++ b/pom.xml @@ -50,11 +50,6 @@ org.springframework.boot spring-boot-starter-web - - io.github.cdimascio - dotenv-java - 3.1.0 - org.springframework.boot spring-boot-devtools diff --git a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java index ecd2d57..ae78652 100644 --- a/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java +++ b/src/main/java/de/zendric/app/xpensely_server/XpenselyServerApplication.java @@ -4,21 +4,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; -import io.github.cdimascio.dotenv.Dotenv; - @SpringBootApplication @EnableScheduling public class XpenselyServerApplication { public static void main(String[] args) { - Dotenv dotenv = Dotenv.load(); // Loads the .env file - System.setProperty("GOOGLE_CLIENT_ID", dotenv.get("GOOGLE_CLIENT_ID")); - System.setProperty("GOOGLE_CLIENT_SECRET", dotenv.get("GOOGLE_CLIENT_SECRET")); - System.setProperty("DB_USERNAME", dotenv.get("DB_USERNAME")); - System.setProperty("DB_PASSWORD", dotenv.get("DB_PASSWORD")); - System.setProperty("DB_DEV_CONTAINER", dotenv.get("DB_DEV_CONTAINER")); - System.setProperty("DB_DEV_NAME", dotenv.get("DB_DEV_NAME")); - System.setProperty("DB_PORT", dotenv.get("DB_PORT")); SpringApplication.run(XpenselyServerApplication.class, args); } From c4534114443ab0f415eb4ae0609073c14b91b1b9 Mon Sep 17 00:00:00 2001 From: Cedric Date: Sat, 11 Jan 2025 21:37:11 +0100 Subject: [PATCH 30/32] fixes --- docker-compose.yml | 25 ++++++++++++----------- src/main/resources/application.properties | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a4f4356..886656c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,29 +1,27 @@ version: "3.8" - services: - app: + xpensely-server: image: tea.zendric.de/cedric/xpensely-server:latest - container_name: XpenselyServer + container_name: xpensely-server ports: - - "3636:8080" + - 3636:8080 environment: GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} DB_PORT: 5434 - DB_CONTAINER: ${DB_P_CONTAINER} DB_NAME: ${DB_P_NAME} DB_USERNAME: ${DB_USERNAME} DB_PASSWORD: ${DB_PASSWORD} depends_on: - - postgresdb + postgresdb: + condition: service_healthy networks: - xpensely-network - postgresdb: image: postgres:14 container_name: postgresdb ports: - - "5434:5432" + - 5434:5432 environment: POSTGRES_DB: ${DB_P_NAME} POSTGRES_USER: ${DB_USERNAME} @@ -33,9 +31,12 @@ services: volumes: - db_data:/var/lib/postgresql/data restart: unless-stopped - + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"] + interval: 10s + timeout: 5s + retries: 5 volumes: - db_data: - + db_data: null networks: - xpensely-network: + xpensely-network: null diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index cfb0d66..61116e5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_ spring.security.oauth2.resourceserver.jwt.issuer-uri=https://accounts.google.com # PostgreSQL Configuration -spring.datasource.url=jdbc:postgresql://${DB_P_CONTAINER}:${DB_PORT}/${DB_P_NAME} +spring.datasource.url=jdbc:postgresql://postgresdb:${DB_PORT}/${DB_P_NAME} spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=org.postgresql.Driver From 4fca98dc72600c7d6dd1c9d707c7ab6399169fed Mon Sep 17 00:00:00 2001 From: Cedric Date: Sun, 12 Jan 2025 10:11:57 +0100 Subject: [PATCH 31/32] add logos --- src/main/resources/static/xpensely_icon.png | Bin 0 -> 2748 bytes src/main/resources/static/xpensely_logo.png | Bin 0 -> 8389 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/static/xpensely_icon.png create mode 100644 src/main/resources/static/xpensely_logo.png diff --git a/src/main/resources/static/xpensely_icon.png b/src/main/resources/static/xpensely_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b90a600ecd644acef4c7e1d82d773f78dfe9438f GIT binary patch literal 2748 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 Date: Sun, 12 Jan 2025 10:35:50 +0100 Subject: [PATCH 32/32] tag releases --- .gitea/workflows/tag_release.yml | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .gitea/workflows/tag_release.yml diff --git a/.gitea/workflows/tag_release.yml b/.gitea/workflows/tag_release.yml new file mode 100644 index 0000000..7a4092c --- /dev/null +++ b/.gitea/workflows/tag_release.yml @@ -0,0 +1,62 @@ +name: Build and Deploy Versioned Spring Boot Server + +on: + push: + tags: # Match all tags + - "*" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. Checkout the code + - name: Checkout code + uses: actions/checkout@v2 + + # 2. Set up Java and Maven + - name: Set up JDK (Eclipse Temurin) + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + cache: maven + + # 3. Verify Maven installation + - name: Install Maven + run: | + sudo apt-get update + sudo apt-get install -y maven + mvn -version + + # 4. Build the Spring Boot application + - name: Build Spring Boot Application + run: | + mvn clean package -DskipTests + + # 5. Set up Docker + - name: Set up Docker + run: | + docker --version + + # 6. Extract the tag name + - name: Extract Tag Version + id: extract_version + run: | + TAG_VERSION=${GITEA_REF#refs/tags/} + echo "TAG_VERSION=$TAG_VERSION" >> $GITHUB_ENV + + # 7. Build the Docker image with the tag + - name: Build and Package Docker Image + run: | + docker build -t tea.zendric.de/cedric/xpensely-server:${{ env.TAG_VERSION }} . + + # 8. Docker login + - name: Login to Docker Registry + run: | + echo "${{ secrets.TEAPASSWORD }}" | docker login tea.zendric.de -u ${{ secrets.TEAUSER }} --password-stdin + + # 9. Push the Docker image with the tag + - name: Push the Docker Image to registry + run: | + docker push tea.zendric.de/cedric/xpensely-server:${{ env.TAG_VERSION }}