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