Compare commits

...

5 Commits

Author SHA1 Message Date
9d9d6d8a60 Funktionierende SecurityConfig 2025-03-18 15:52:06 +01:00
be982193e4 Password hashing und jft 2025-02-28 21:25:24 +01:00
75644d012d Password hashing und jft 2025-02-28 21:24:59 +01:00
fce585e089 gruppen und likes fertig 2025-02-14 11:39:39 +01:00
fdd18bdb6c gruppen und likes 2025-02-12 11:49:46 +01:00
26 changed files with 770 additions and 12 deletions

45
pom.xml
View File

@ -55,6 +55,51 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Spring Security für Passwort-Hashing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JJWT API -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- JJWT Implementierung (zur Laufzeit) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- JJWT Jackson für JSON-Verarbeitung (zur Laufzeit) -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Validation für Request-Validierung -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Lombok (Optional, aber erleichtert die Code-Generierung) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,77 @@
package com.example.hangry;
import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "`group`")
public class Group {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
@ManyToMany
@JoinTable(
name = "group_users",
joinColumns = @JoinColumn(name = "group_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private Set<User> members = new HashSet<>();
@OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<GroupRecipe> sharedRecipes = new HashSet<>();
public Group() {}
public Group(String name, String description) {
this.name = name;
this.description = description;
}
// Getter und Setter
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Set<User> getMembers() {
return members;
}
public Set<GroupRecipe> getSharedRecipes() {
return sharedRecipes;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setDescription(String description) {
this.description = description;
}
public void setMembers(Set<User> members) {
this.members = members;
}
public void setSharedRecipes(Set<GroupRecipe> sharedRecipes) {
this.sharedRecipes = sharedRecipes;
}
}

View File

@ -0,0 +1,52 @@
package com.example.hangry;
import jakarta.persistence.*;
@Entity
public class GroupRecipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "group_id", nullable = false)
private Group group;
@ManyToOne
@JoinColumn(name = "recipe_id", nullable = false)
private Recipe recipe;
public GroupRecipe() {}
public GroupRecipe(Group group, Recipe recipe) {
this.group = group;
this.recipe = recipe;
}
// Getter und Setter
public Long getId() {
return id;
}
public Group getGroup() {
return group;
}
public Recipe getRecipe() {
return recipe;
}
public void setId(Long id) {
this.id = id;
}
public void setGroup(Group group) {
this.group = group;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
}

View File

@ -0,0 +1,6 @@
package com.example.hangry;
import org.springframework.data.jpa.repository.JpaRepository;
public interface GroupRecipeRepository extends JpaRepository<GroupRecipe, Long> {
}

View File

@ -0,0 +1,6 @@
package com.example.hangry;
import org.springframework.data.jpa.repository.JpaRepository;
public interface GroupRepository extends JpaRepository<Group, Long> {
}

View File

@ -0,0 +1,64 @@
package com.example.hangry;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "`likes`")
public class Like {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@ManyToOne
@JoinColumn(name = "recipe_id", nullable = false)
private Recipe recipe;
private LocalDateTime createdAt = LocalDateTime.now();
// Konstruktoren, Getter & Setter#
public Like(){}
public Like( User user, Recipe recipe) {
this.user = user;
this.recipe = recipe;
this.createdAt=LocalDateTime.now();
}
public Long getId() {
return id;
}
public User getUser() {
return user;
}
public Recipe getRecipe() {
return recipe;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setId(Long id) {
this.id = id;
}
public void setUser(User user) {
this.user = user;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
}

View File

@ -0,0 +1,17 @@
package com.example.hangry;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface LikeRepository extends JpaRepository<Like, Long> {
boolean existsByUserAndRecipe(User user, Recipe recipe);
long countByRecipe(Recipe recipe);
List<Like> findAllByUser(User user);
void deleteByUserAndRecipe(User user, Recipe recipe);
}

View File

@ -76,4 +76,6 @@ public class Recipe {
public void setRecipeIngredients(List<RecipeIngredient> recipeIngredients) { public void setRecipeIngredients(List<RecipeIngredient> recipeIngredients) {
this.recipeIngredients = recipeIngredients; this.recipeIngredients = recipeIngredients;
} }
} }

View File

@ -11,6 +11,7 @@ public interface RecipeRepository extends JpaRepository<Recipe, Long> {
Page<Recipe> findByCategoryIgnoreCase(String category, Pageable pageable); Page<Recipe> findByCategoryIgnoreCase(String category, Pageable pageable);
// Sucht nach Rezepten, die eine bestimmte Zutat enthalten // Sucht nach Rezepten, die eine bestimmte Zutat enthalten
@Query("SELECT r FROM Recipe r JOIN r.recipeIngredients i WHERE LOWER(i.name) LIKE LOWER(CONCAT('%', :ingredient, '%'))") @Query("SELECT r FROM Recipe r JOIN r.recipeIngredients i WHERE LOWER(i.ingredient.name) LIKE LOWER(CONCAT('%', :ingredient, '%'))")
Page<Recipe> findByIngredient(@Param("ingredient") String ingredient, Pageable pageable); Page<Recipe> findByIngredient(@Param("ingredient") String ingredient, Pageable pageable);
} }

View File

@ -0,0 +1,45 @@
package com.example.hangry.controller;
import com.example.hangry.User;
import com.example.hangry.services.UserService;
import com.example.hangry.services.JwtService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
@CrossOrigin (origins = "*")
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final UserService userService;
private final JwtService jwtService;
public AuthController(UserService userService, JwtService jwtService) {
this.userService = userService;
this.jwtService = jwtService;
}
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody User user) {
if (userService.getUserByEmail(user.getEmail()) != null) {
return ResponseEntity.badRequest().body("Email bereits vergeben!");
}
if (userService.getUserByUsername(user.getUsername()) != null) {
return ResponseEntity.badRequest().body("Username bereits vergeben!");
}
User newUser = userService.createUser(user);
return ResponseEntity.ok(newUser);
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody User loginRequest) {
User user = userService.getUserByEmail(loginRequest.getEmail());
if (user != null && userService.checkPassword(loginRequest.getPassword(), user.getPassword())) {
String token = jwtService.generateToken(user); // JWT erstellen
return ResponseEntity.ok(Collections.singletonMap("token",token)); // Token zurückgeben
}
return ResponseEntity.status(401).body(Collections.singletonMap("error","Login Fehlgeschlagen!"));
}
}

View File

@ -0,0 +1,44 @@
package com.example.hangry.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
import com.example.hangry.*;
import com.example.hangry.services.GroupService;
@RestController
@RequestMapping("/api/groups")
@CrossOrigin(origins = "*")
public class GroupController {
private final GroupService groupService;
public GroupController(GroupService groupService) {
this.groupService = groupService;
}
// Neue Gruppe erstellen
@PostMapping
public ResponseEntity<Group> createGroup(@RequestParam String name, @RequestParam String description) {
return ResponseEntity.ok(groupService.createGroup(name, description));
}
// Gruppe beitreten
@PostMapping("/{groupId}/join/{userId}")
public ResponseEntity<String> joinGroup(@PathVariable Long groupId, @PathVariable Long userId) {
boolean joined = groupService.joinGroup(userId, groupId);
return joined ? ResponseEntity.ok("Gruppe beigetreten") : ResponseEntity.badRequest().body("Bereits Mitglied");
}
// Gruppe verlassen
@PostMapping("/{groupId}/leave/{userId}")
public ResponseEntity<String> leaveGroup(@PathVariable Long groupId, @PathVariable Long userId) {
boolean left = groupService.leaveGroup(userId, groupId);
return left ? ResponseEntity.ok("Gruppe verlassen") : ResponseEntity.badRequest().body("Nicht in der Gruppe");
}
// Mitglieder einer Gruppe abrufen
@GetMapping("/{groupId}/members")
public ResponseEntity<Set<User>> getGroupMembers(@PathVariable Long groupId) {
return ResponseEntity.ok(groupService.getGroupMembers(groupId));
}
}

View File

@ -0,0 +1,34 @@
package com.example.hangry.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Set;
import com.example.hangry.services.GroupRecipeService;
import com.example.hangry.*;
@RestController
@RequestMapping("/api/group-recipes")
@CrossOrigin(origins = "*")
public class GroupRecipeController {
private final GroupRecipeService groupRecipeService;
public GroupRecipeController(GroupRecipeService groupRecipeService) {
this.groupRecipeService = groupRecipeService;
}
@PostMapping("/{groupId}/share/{recipeId}")
public ResponseEntity<String> shareRecipe(@PathVariable Long groupId, @PathVariable Long recipeId) {
boolean shared = groupRecipeService.shareRecipeWithGroup(groupId, recipeId);
return shared ? ResponseEntity.ok("Rezept geteilt") : ResponseEntity.badRequest().body("Fehler beim Teilen");
}
@GetMapping("/{groupId}")
public ResponseEntity<Set<Recipe>> getGroupRecipes(@PathVariable Long groupId) {
return ResponseEntity.ok(groupRecipeService.getGroupRecipes(groupId));
}
}

View File

@ -1,10 +1,12 @@
package com.example.hangry; package com.example.hangry.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.*; import java.util.*;
import com.example.hangry.*;
import com.example.hangry.services.IngredientService;
@RestController @RestController
@RequestMapping("/api/ingredients") @RequestMapping("/api/ingredients")

View File

@ -0,0 +1,45 @@
package com.example.hangry.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.example.hangry.*;
import com.example.hangry.services.LikeService;
@RestController
@RequestMapping("/api/likes")
@CrossOrigin(origins = "*")
public class LikeController {
private final LikeService likeService;
public LikeController(LikeService likeService) {
this.likeService = likeService;
}
// Rezept liken
@PostMapping("/{userId}/{recipeId}")
public ResponseEntity<String> likeRecipe(@PathVariable Long userId, @PathVariable Long recipeId) {
boolean liked = likeService.likeRecipe(userId, recipeId);
return liked ? ResponseEntity.ok("Rezept geliked") : ResponseEntity.badRequest().body("Bereits geliked");
}
// Rezept entliken
@DeleteMapping("/{userId}/{recipeId}")
public ResponseEntity<String> unlikeRecipe(@PathVariable Long userId, @PathVariable Long recipeId) {
boolean unliked = likeService.unlikeRecipe(userId, recipeId);
return unliked ? ResponseEntity.ok("Like entfernt") : ResponseEntity.badRequest().body("Like existiert nicht");
}
// Anzahl der Likes für ein Rezept abrufen
@GetMapping("/count/{recipeId}")
public ResponseEntity<Long> getLikeCount(@PathVariable Long recipeId) {
return ResponseEntity.ok(likeService.getLikeCount(recipeId));
}
// Alle geliketen Rezepte eines Users abrufen
@GetMapping("/user/{userId}")
public ResponseEntity<List<Recipe>> getLikedRecipes(@PathVariable Long userId) {
return ResponseEntity.ok(likeService.getLikedRecipes(userId));
}
}

View File

@ -1,4 +1,4 @@
package com.example.hangry; package com.example.hangry.controller;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -7,6 +7,8 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import java.util.Optional; import java.util.Optional;
import com.example.hangry.*;
import com.example.hangry.services.RecipeService;
@RestController @RestController
@RequestMapping("/api/recipes") @RequestMapping("/api/recipes")
@CrossOrigin(origins = "*") // Erlaubt CORS für alle Domains, kann angepasst werden @CrossOrigin(origins = "*") // Erlaubt CORS für alle Domains, kann angepasst werden

View File

@ -1,8 +1,9 @@
package com.example.hangry; package com.example.hangry.controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.example.hangry.*;
import com.example.hangry.services.UserService;
@RestController @RestController
@RequestMapping("/users") @RequestMapping("/users")
public class UserController { public class UserController {

View File

@ -0,0 +1,52 @@
package com.example.hangry.security;
import com.example.hangry.User;
import com.example.hangry.UserRepository;
import com.example.hangry.services.JwtService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.ArrayList;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserRepository userRepository;
public JwtAuthenticationFilter(JwtService jwtService, UserRepository userRepository) {
this.jwtService = jwtService;
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
String email = jwtService.extractEmail(token);
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
User user = userRepository.findByEmail(email);
if (user != null && jwtService.validateToken(token, user)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}

View File

@ -0,0 +1,30 @@
package com.example.hangry.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.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity // Aktiviert Spring Security
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
http
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.csrf(AbstractHttpConfigurer::disable) // Neue Syntax für CSRF-Disable
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/register", "/api/auth/login").permitAll() // Registrierung & Login erlauben
.anyRequest().authenticated() // Alles andere erfordert Authentifizierung
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // JWT: Keine Session
return http.build();
}
}

View File

@ -0,0 +1,38 @@
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.stream.Collectors;
import com.example.hangry.*;
@Service
public class GroupRecipeService {
private final GroupRepository groupRepository;
private final RecipeRepository recipeRepository;
private final GroupRecipeRepository groupRecipeRepository;
public GroupRecipeService(GroupRepository groupRepository, RecipeRepository recipeRepository, GroupRecipeRepository groupRecipeRepository) {
this.groupRepository = groupRepository;
this.recipeRepository = recipeRepository;
this.groupRecipeRepository = groupRecipeRepository;
}
// Rezept mit Gruppe teilen
public boolean shareRecipeWithGroup(Long groupId, Long recipeId) {
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new RuntimeException("Gruppe nicht gefunden"));
Recipe recipe = recipeRepository.findById(recipeId)
.orElseThrow(() -> new RuntimeException("Rezept nicht gefunden"));
GroupRecipe groupRecipe = new GroupRecipe(group, recipe);
groupRecipeRepository.save(groupRecipe);
return true;
}
// Alle Rezepte einer Gruppe abrufen
public Set<Recipe> getGroupRecipes(Long groupId) {
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new RuntimeException("Gruppe nicht gefunden"));
return group.getSharedRecipes().stream().map(GroupRecipe::getRecipe).collect(Collectors.toSet());
}
}

View File

@ -0,0 +1,59 @@
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import java.util.Set;
import com.example.hangry.*;
@Service
public class GroupService {
private final GroupRepository groupRepository;
private final UserRepository userRepository;
public GroupService(GroupRepository groupRepository, UserRepository userRepository) {
this.groupRepository = groupRepository;
this.userRepository = userRepository;
}
// Gruppe erstellen
public Group createGroup(String name, String description) {
Group group = new Group(name, description);
return groupRepository.save(group);
}
// Gruppe beitreten
public boolean joinGroup(Long userId, Long groupId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("User nicht gefunden"));
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new RuntimeException("Gruppe nicht gefunden"));
if (!group.getMembers().contains(user)) {
group.getMembers().add(user);
groupRepository.save(group);
return true;
}
return false;
}
// Gruppe verlassen
public boolean leaveGroup(Long userId, Long groupId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("User nicht gefunden"));
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new RuntimeException("Gruppe nicht gefunden"));
if (group.getMembers().contains(user)) {
group.getMembers().remove(user);
groupRepository.save(group);
return true;
}
return false;
}
// Alle Mitglieder einer Gruppe abrufen
public Set<User> getGroupMembers(Long groupId) {
Group group = groupRepository.findById(groupId)
.orElseThrow(() -> new RuntimeException("Gruppe nicht gefunden"));
return group.getMembers();
}
}

View File

@ -1,9 +1,9 @@
package com.example.hangry; package com.example.hangry.services;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
import com.example.hangry.*;
@Service @Service
public class IngredientService { public class IngredientService {

View File

@ -0,0 +1,53 @@
package com.example.hangry.services;
import com.example.hangry.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.cglib.core.internal.Function;
import org.springframework.stereotype.Service;
import java.security.Key;
import java.util.Date;
@Service
public class JwtService {
private static final String SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256).toString();
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 24 Stunden
public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getEmail())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
}
private Key getSigningKey() {
return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
}
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject); // Subject = E-Mail
}
public boolean validateToken(String token, User user) {
final String email = extractEmail(token);
return email.equals(user.getEmail()) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
}

View File

@ -0,0 +1,67 @@
package com.example.hangry.services;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import com.example.hangry.*;
@Service
public class LikeService {
private final LikeRepository likeRepository;
private final RecipeRepository recipeRepository;
private final UserRepository userRepository;
public LikeService(LikeRepository likeRepository, RecipeRepository recipeRepository, UserRepository userRepository) {
this.likeRepository = likeRepository;
this.recipeRepository = recipeRepository;
this.userRepository = userRepository;
}
// Rezept liken
public boolean likeRecipe(Long userId, Long recipeId) {
User user = userRepository.findById(userId).orElseThrow();
Recipe recipe = recipeRepository.findById(recipeId).orElseThrow();
if (likeRepository.existsByUserAndRecipe(user, recipe)) {
return false; // Bereits geliked
}
Like like = new Like();
like.setUser(user);
like.setRecipe(recipe);
likeRepository.save(like);
return true;
}
// Rezept entliken
@Transactional
public boolean unlikeRecipe(Long userId, Long recipeId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new RuntimeException("User nicht gefunden"));
Recipe recipe = recipeRepository.findById(recipeId)
.orElseThrow(() -> new RuntimeException("Rezept nicht gefunden"));
// Prüfen, ob das Like existiert
if (!likeRepository.existsByUserAndRecipe(user, recipe)) {
return false; // Like existiert nicht
}
likeRepository.deleteByUserAndRecipe(user, recipe);
return true;
}
// Anzahl der Likes für ein Rezept abrufen
public long getLikeCount(Long recipeId) {
Recipe recipe = recipeRepository.findById(recipeId).orElseThrow();
return likeRepository.countByRecipe(recipe);
}
// Alle geliketen Rezepte eines Users abrufen
public List<Recipe> getLikedRecipes(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
List<Like> likes = likeRepository.findAllByUser(user);
return likes.stream().map(Like::getRecipe).toList();
}
}

View File

@ -1,11 +1,11 @@
package com.example.hangry; package com.example.hangry.services;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Optional; import java.util.Optional;
import com.example.hangry.*;
@Service @Service
public class RecipeService { public class RecipeService {

View File

@ -1,19 +1,25 @@
package com.example.hangry; package com.example.hangry.services;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.example.hangry.*;
@Service @Service
public class UserService { public class UserService {
private final UserRepository userRepository; private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
// Dependency Injection (Spring kümmert sich darum) // Dependency Injection (Spring kümmert sich darum)
public UserService(UserRepository userRepository) { public UserService(UserRepository userRepository) {
this.userRepository = userRepository; this.userRepository = userRepository;
this.passwordEncoder = new BCryptPasswordEncoder(); // Initialisierung von BCrypt
} }
// Einen neuen Benutzer erstellen // Neuen Benutzer erstellen (mit Passwort-Hashing)
public User createUser(User user) { public User createUser(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword())); // Passwort hashen
return userRepository.save(user); return userRepository.save(user);
} }
@ -26,4 +32,9 @@ public class UserService {
public User getUserByEmail(String email) { public User getUserByEmail(String email) {
return userRepository.findByEmail(email); return userRepository.findByEmail(email);
} }
// Passwort überprüfen
public boolean checkPassword(String rawPassword, String hashedPassword) {
return passwordEncoder.matches(rawPassword, hashedPassword);
}
} }

View File

@ -4,6 +4,11 @@ spring.datasource.username=root
spring.datasource.password= spring.datasource.password=
# Hibernate Einstellungen # Hibernate Einstellungen
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.hibernate.ddl-auto=update
server.port=8080
server.address=0.0.0.0