Merge branch 'main' of github.com:FlorianSpeicher04/webshop

This commit is contained in:
FlorianSpeicher
2025-06-11 14:20:41 +02:00
29 changed files with 345 additions and 97 deletions

View File

@@ -16,6 +16,9 @@ public class ControllerPathConfig {
public static final String ACCOUNT_BASE = "/account"; public static final String ACCOUNT_BASE = "/account";
public static final String EMAIL_BASE = "/email"; public static final String EMAIL_BASE = "/email";
//SessionController
public static final String SESSION_BASE = "/session";
//ArticleController //ArticleController
public static final String ARTICLE_BASE = "/article"; public static final String ARTICLE_BASE = "/article";
public static final String ARTICLE_GET_ALL = ARTICLE_BASE + "/all"; public static final String ARTICLE_GET_ALL = ARTICLE_BASE + "/all";
@@ -34,9 +37,4 @@ public class ControllerPathConfig {
//ReviewController //ReviewController
public static final String REVIEW_BASE = "/review"; public static final String REVIEW_BASE = "/review";
public static final String REVIEW_GET_ALL = REVIEW_BASE + "/all"; public static final String REVIEW_GET_ALL = REVIEW_BASE + "/all";
//ImageHardcodeController
private static final String HARDCODE_IMAGE = "/images";
public static final String HARDCODE_IMAGE_DPS_STICKER = HARDCODE_IMAGE + "/dps.webp";
} }

View File

@@ -15,7 +15,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static de.htwsaar.webshop.config.ControllerPathConfig.*; import static de.htwsaar.webshop.config.ControllerPathConfig.IMAGE_ALL;
import static de.htwsaar.webshop.config.ControllerPathConfig.IMAGE_BASE;
import static de.htwsaar.webshop.config.ParameterConfig.*; import static de.htwsaar.webshop.config.ParameterConfig.*;
import static de.htwsaar.webshop.util.LoggerUtil.logRequest; import static de.htwsaar.webshop.util.LoggerUtil.logRequest;

View File

@@ -1,30 +0,0 @@
package de.htwsaar.webshop.controller;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import static de.htwsaar.webshop.config.ControllerPathConfig.HARDCODE_IMAGE_DPS_STICKER;
import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
@RestController
@Slf4j
public class ImageHardcodeController {
private final ResourceLoader resourceLoader;
@Autowired
public ImageHardcodeController(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@RequestMapping(value = HARDCODE_IMAGE_DPS_STICKER, method = RequestMethod.GET)
Resource dpsSticker(HttpServletRequest request) {
logRequest(request);
return resourceLoader.getResource("classpath:images/dps_sticker.webp");
}
}

View File

@@ -14,7 +14,8 @@ import java.util.List;
import static de.htwsaar.webshop.config.ControllerPathConfig.ORDER_BASE; import static de.htwsaar.webshop.config.ControllerPathConfig.ORDER_BASE;
import static de.htwsaar.webshop.config.ControllerPathConfig.ORDER_GET_ALL; import static de.htwsaar.webshop.config.ControllerPathConfig.ORDER_GET_ALL;
import static de.htwsaar.webshop.config.ParameterConfig.*; import static de.htwsaar.webshop.config.ParameterConfig.PARAM_CUSTOMER_ID;
import static de.htwsaar.webshop.config.ParameterConfig.PARAM_ID;
import static de.htwsaar.webshop.util.LoggerUtil.logRequest; import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
@RestController @RestController

View File

@@ -13,7 +13,8 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static de.htwsaar.webshop.config.ControllerPathConfig.*; import static de.htwsaar.webshop.config.ControllerPathConfig.REVIEW_BASE;
import static de.htwsaar.webshop.config.ControllerPathConfig.REVIEW_GET_ALL;
import static de.htwsaar.webshop.config.ParameterConfig.*; import static de.htwsaar.webshop.config.ParameterConfig.*;
import static de.htwsaar.webshop.util.LoggerUtil.logRequest; import static de.htwsaar.webshop.util.LoggerUtil.logRequest;

View File

@@ -0,0 +1,80 @@
package de.htwsaar.webshop.controller;
import de.htwsaar.webshop.model.SessionModel;
import de.htwsaar.webshop.repository.entities.Account;
import de.htwsaar.webshop.repository.entities.Session;
import de.htwsaar.webshop.service.AccountService;
import de.htwsaar.webshop.service.SessionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import static de.htwsaar.webshop.config.ControllerPathConfig.SESSION_BASE;
@RestController
@Slf4j
public class SessionController {
private final SessionService sessionService;
private final AccountService accountService;
@Autowired
public SessionController(SessionService sessionService, AccountService accountService) {
this.sessionService = sessionService;
this.accountService = accountService;
}
@RequestMapping(value = SESSION_BASE, method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<SessionModel> create(@RequestParam String email, @RequestParam String password) {
if (email == null || password == null) {
log.warn("Got bad request, email and password");
return ResponseEntity.badRequest().build();
}
Account acc = accountService.isValidLogin(email, password);
if (acc == null) {
log.warn("Got bad request, Account does not exist");
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(sessionService.create(acc).toModel());
}
@RequestMapping(value = SESSION_BASE, method = RequestMethod.DELETE, produces = "application/json")
public ResponseEntity<Boolean> delete(@RequestParam String email, @RequestParam UUID token) {
if (email == null || token == null) {
log.warn("Got bad request, email and token");
return ResponseEntity.badRequest().body(false);
}
Session session = sessionService.getByToken(token);
if (!sessionService.isValid(session, email)) {
log.warn("Got bad request, session is invalid");
return ResponseEntity.badRequest().body(false);
}
sessionService.delete(session);
return ResponseEntity.ok(true);
}
@RequestMapping(value = SESSION_BASE, method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<Boolean> isValid(@RequestParam String email, @RequestParam UUID token) {
if (email == null || token == null) {
log.warn("Got bad request, email and token");
return ResponseEntity.badRequest().body(false);
}
Session session = sessionService.getByToken(token);
if (session == null) {
log.warn("Got bad request, session is invalid");
return ResponseEntity.notFound().build();
}
if (sessionService.isValid(session, email)) {
return ResponseEntity.ok(true);
}
return ResponseEntity.notFound().build();
}
}

View File

@@ -0,0 +1,24 @@
package de.htwsaar.webshop.model;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.UUID;
import static de.htwsaar.webshop.util.TimeUtil.VALID_MIN_MILLIS_TIMESTAMP;
@Getter
@Setter
@ToString
@AllArgsConstructor
public class SessionModel {
@NotNull
UUID uuid;
@Min(VALID_MIN_MILLIS_TIMESTAMP)
long timeout;
}

View File

@@ -3,7 +3,6 @@ package de.htwsaar.webshop.repository;
import de.htwsaar.webshop.repository.entities.Article; import de.htwsaar.webshop.repository.entities.Article;
import lombok.NonNull; import lombok.NonNull;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Optional; import java.util.Optional;

View File

@@ -1,15 +1,14 @@
package de.htwsaar.webshop.repository; package de.htwsaar.webshop.repository;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import de.htwsaar.webshop.repository.entities.Review; import de.htwsaar.webshop.repository.entities.Review;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Positive;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
@Repository @Repository
public interface ReviewRepository extends JpaRepository<Review, Long> { public interface ReviewRepository extends JpaRepository<Review, Long> {

View File

@@ -0,0 +1,16 @@
package de.htwsaar.webshop.repository;
import de.htwsaar.webshop.repository.entities.Account;
import de.htwsaar.webshop.repository.entities.Session;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface SessionRepository extends JpaRepository<Session, Long> {
Session findByAccount(Account account);
Session getSessionByToken(UUID token);
}

View File

@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.hibernate.type.NumericBooleanConverter;
@Getter @Getter
@Setter @Setter
@@ -29,4 +30,9 @@ public class Account {
@Column(name = "lang_i18n", nullable = false) @Column(name = "lang_i18n", nullable = false)
private String langI18n; private String langI18n;
@Convert(converter = NumericBooleanConverter.class)
@Column(name = "admin", nullable = false)
private Boolean admin;
} }

View File

@@ -3,11 +3,7 @@ package de.htwsaar.webshop.repository.entities;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import lombok.AllArgsConstructor; import lombok.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@@ -0,0 +1,37 @@
package de.htwsaar.webshop.repository.entities;
import de.htwsaar.webshop.model.SessionModel;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "Sessions")
public class Session {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@JoinColumn(name = "token", nullable = false)
private UUID token;
@Column(name = "timeout", nullable = false)
private Long timeout;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false)
private Account account;
public SessionModel toModel() {
return new SessionModel(token, timeout);
}
}

View File

@@ -4,8 +4,12 @@ import de.htwsaar.webshop.repository.entities.Account;
public interface AccountService { public interface AccountService {
Account saveNew(Account account); Account saveNew(Account account);
Account save(Account account); Account save(Account account);
boolean deleteIfExists(Account account); boolean deleteIfExists(Account account);
Account isValidLogin(String email, String password); Account isValidLogin(String email, String password);
boolean existsWithEmail(String email); boolean existsWithEmail(String email);
} }

View File

@@ -4,6 +4,8 @@ import de.htwsaar.webshop.repository.entities.Customer;
public interface CustomerService { public interface CustomerService {
Customer save(Customer customer); Customer save(Customer customer);
void delete(Customer customer); void delete(Customer customer);
Customer findById(Long id); Customer findById(Long id);
} }

View File

@@ -6,7 +6,10 @@ import java.util.List;
public interface OrderService { public interface OrderService {
Order save(Order order); Order save(Order order);
void delete(Long orderId); void delete(Long orderId);
Order getOrderById(Long orderId); Order getOrderById(Long orderId);
List<Order> getAllOrders(Long customerId); List<Order> getAllOrders(Long customerId);
} }

View File

@@ -2,5 +2,6 @@ package de.htwsaar.webshop.service;
public interface PasswordService { public interface PasswordService {
String hashPassword(String password); String hashPassword(String password);
boolean verifyPassword(String password, String hashedPassword); boolean verifyPassword(String password, String hashedPassword);
} }

View File

@@ -1,16 +1,21 @@
package de.htwsaar.webshop.service; package de.htwsaar.webshop.service;
import java.util.List;
import java.util.UUID;
import de.htwsaar.webshop.model.ReviewModel; import de.htwsaar.webshop.model.ReviewModel;
import de.htwsaar.webshop.repository.entities.Review; import de.htwsaar.webshop.repository.entities.Review;
import java.util.List;
import java.util.UUID;
public interface ReviewService { public interface ReviewService {
Review save(Review review); Review save(Review review);
Review save(UUID articleUuid, int rating, String content); Review save(UUID articleUuid, int rating, String content);
void delete(Long reviewId); void delete(Long reviewId);
Review getReviewById(Long id); Review getReviewById(Long id);
List<Review> getAllByUUID(UUID uuid); List<Review> getAllByUUID(UUID uuid);
ReviewModel toModel(Review review); ReviewModel toModel(Review review);
} }

View File

@@ -0,0 +1,20 @@
package de.htwsaar.webshop.service;
import de.htwsaar.webshop.repository.entities.Account;
import de.htwsaar.webshop.repository.entities.Session;
import java.util.UUID;
public interface SessionService {
Session create(Account account);
void delete(Session session);
Session findByAccount(Account account);
Session getByToken(UUID token);
boolean isValid(Session session, String email);
boolean isValid(UUID token, String email);
}

View File

@@ -0,0 +1,73 @@
package de.htwsaar.webshop.service.impl;
import de.htwsaar.webshop.repository.AccountRepository;
import de.htwsaar.webshop.repository.SessionRepository;
import de.htwsaar.webshop.repository.entities.Account;
import de.htwsaar.webshop.repository.entities.Session;
import de.htwsaar.webshop.service.SessionService;
import de.htwsaar.webshop.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
@Slf4j
public class SessionServiceImpl implements SessionService {
private final SessionRepository sessionRepository;
private final AccountRepository accountRepository;
@Autowired
public SessionServiceImpl(SessionRepository sessionRepository, AccountRepository accountRepository) {
this.sessionRepository = sessionRepository;
this.accountRepository = accountRepository;
}
@Override
public Session create(Account account) {
long timeout = TimeUtil.nowPlusDays(7);
UUID token = UUID.randomUUID();
log.info("Creating session with account {} and token {} with timeout {} ", account, token, timeout);
return sessionRepository.save(new Session(0L, token, timeout, account));
}
@Override
public void delete(Session session) {
sessionRepository.delete(session);
}
@Override
public Session findByAccount(Account account) {
return sessionRepository.findByAccount(account);
}
@Override
public Session getByToken(UUID token) {
return sessionRepository.getSessionByToken(token);
}
@Override
public boolean isValid(Session session, String email) {
if (session == null || email == null) {
return false;
}
Account accountEmail = accountRepository.getAccountByEmail(email);
if (!session.getAccount().equals(accountEmail)) {
return false;
}
if (session.getTimeout() >= System.currentTimeMillis()) {
log.info("Session with email {} is expired", email);
delete(session);
return false;
}
log.info("Session with email {} is valid", email);
return true;
}
@Override
public boolean isValid(UUID token, String email) {
return isValid(getByToken(token), email);
}
}

View File

@@ -60,6 +60,7 @@ CREATE TABLE IF NOT EXISTS Accounts
email TEXT NOT NULL, email TEXT NOT NULL,
password TEXT NOT NULL, password TEXT NOT NULL,
lang_i18n TEXT NOT NULL, lang_i18n TEXT NOT NULL,
admin INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (customer_id) REFERENCES Customers (id) FOREIGN KEY (customer_id) REFERENCES Customers (id)
ON DELETE CASCADE ON DELETE CASCADE
ON UPDATE CASCADE ON UPDATE CASCADE
@@ -81,3 +82,14 @@ CREATE TABLE IF NOT EXISTS OrderItems
ON DELETE SET NULL ON DELETE SET NULL
ON UPDATE CASCADE ON UPDATE CASCADE
); );
CREATE TABLE IF NOT EXISTS Sessions
(
id INTEGER NOT NULL PRIMARY KEY,
token TEXT UNIQUE NOT NULL,
timeout INTEGER NOT NULL,
account_id INTEGER NOT NULL,
FOREIGN KEY (account_id) REFERENCES Accounts (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);

Binary file not shown.