beidl message

This commit is contained in:
Jonas Weissengruber 2023-12-20 00:41:20 +01:00
parent fc372073f3
commit 85d7162419
28 changed files with 413 additions and 84 deletions

21
http-requests/cart.http Normal file
View file

@ -0,0 +1,21 @@
### add
POST {{url}}/cart/add
Content-Type: application/json
{
"id": 9999999,
"cartEntries": [
{
"id": 2,
"amount": 20
}
]
}
### list
GET {{url}}/user/signin
### checkout
GET {{url}}/user/update
Authorization: Bearer {{token}}

View file

@ -0,0 +1,34 @@
### create
POST {{url}}/category/create
Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Erwachsene",
"price": 25.5,
"stock": 100
}
### update
PUT {{url}}/category/update
Authorization: Bearer {{token}}
Content-Type: application/json
{
"id": 1,
"name": "Erwachsene",
"price": 25.5,
"stock": 100
}
### delete
DELETE {{url}}/category/delete/1
Authorization: Bearer {{token}}
### list
GET {{url}}/category/list
Authorization: Bearer {{token}}
### load
GET {{url}}/category/2
Authorization: Bearer {{token}}

53
http-requests/event.http Normal file
View file

@ -0,0 +1,53 @@
### create
POST {{url}}/event/create
# Authorization: Bearer {{token}}
Content-Type: application/json
{
"name": "Maturaball HTL Steyr 2024",
"from": "2023-03-02T20:00:00.000Z",
"to": "2023-03-03T05:00:00.000Z",
"description": "Maturaball der Abteilungen EL, IT, ME, Y",
"ticketCategories": [
{
"name": "Normal",
"price": 25,
"stock": 500
},
{
"name": "Maturanten",
"price": 0,
"stock": 500
},
{
"name": "Prechtl",
"price": 50,
"stock": 1
}
]
}
### update
PUT {{url}}/event/update
Authorization: Bearer {{token}}
Content-Type: application/json
{
"id": 2,
"name": "Maturaball HTL Steyr 2024",
"from": "2024-03-02T20:00:00.000Z",
"to": "2024-03-03T05:00:00.000Z",
"description": "Fluch der HTL"
}
### delete
DELETE {{url}}/event/delete/3
Authorization: Bearer {{token}}
### list future events
GET {{url}}/event/list
Authorization: Bearer {{token}}
### load
GET {{url}}/event/2
Authorization: Bearer {{token}}

View file

@ -1,5 +1,6 @@
{ {
"dev": { "dev": {
"url": "http://localhost:8080/api/v1" "url": "http://localhost:8080/api/v1",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZXRpY2tldCB1c2VyIHRva2VuIiwiZXhwIjoxNzAzMTAwNzQyLCJ1c2VySWQiOjIsImlhdCI6MTcwMzAxNDM0Mn0.D9HBxWy1vIP82XOh_ocjLO9HB0lK_rQGjgD3a7KQrOE"
} }
} }

View file

@ -10,22 +10,34 @@ Content-Type: application/json
} }
### signin ### signin
GET {{url}}/user/signin POST {{url}}/user/signin
Content-Type: application/json
{
"email": "admin@email.com",
"password": "a"
}
### update ### update
PUT {{url}}/user/update PUT {{url}}/user/update
Authorization: Bearer {{token}}
Content-Type: application/json
{ {
"email": "ratp@htl-steyr.ac.at",
"firstname": "Peter Chef",
"lastname": "Rathgeb"
} }
### delete ### delete
DELETE {{url}}/user/delete/99 DELETE {{url}}/user/delete/1
Authorization: Bearer {{token}}
### list ### list
GET {{url}}/user/list GET {{url}}/user/list
Authorization: Bearer {{token}}
### load ### load
GET {{url}}/user/load/1 GET {{url}}/user/load/2
Authorization: Bearer {{token}}
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZXRpY2tldCB1c2VyIHRva2VuIiwiZXhwIjoxNzAzMDU4MjczLCJ1c2VySWQiOjEsImlhdCI6MTcwMjk3MTg3M30.hC1N7hKYSIT-fqmaZ9bv3-YXOxQWdp-Sb5rZi4rARc0

View file

@ -25,6 +25,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId> <artifactId>java-jwt</artifactId>

View file

@ -0,0 +1,11 @@
package me.jweissen.aeticket.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {
}

View file

@ -0,0 +1,63 @@
package me.jweissen.aeticket.aspect;
import jakarta.servlet.http.HttpServletRequest;
import me.jweissen.aeticket.model.Role;
import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.service.AuthService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Aspect
@Component
public class AuthAspect {
private final HttpServletRequest request;
private final AuthService authService;
public AuthAspect(HttpServletRequest request, AuthService authService) {
this.request = request;
this.authService = authService;
}
@Pointcut("@annotation(UserOnly)")
public void userOnly() { }
@Pointcut("@annotation(AdminOnly)")
public void adminOnly() { }
public Optional<User> authenticate() {
String tokenPrefix = "Bearer ";
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith(tokenPrefix)) {
return Optional.empty();
}
String token = authHeader.substring(tokenPrefix.length());
return authService.authenticate(token);
}
@Around("userOnly()")
public Object checkUser(ProceedingJoinPoint pjp) throws Throwable {
Optional<User> user = authenticate();
if (user.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return pjp.proceed();
}
@Around("adminOnly()")
public Object checkAdmin(ProceedingJoinPoint pjp) throws Throwable {
Optional<User> user = authenticate();
if (user.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} else if (user.get().getRole() != Role.ADMIN) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
return pjp.proceed();
}
}

View file

@ -0,0 +1,11 @@
package me.jweissen.aeticket.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserOnly {
}

View file

@ -1,15 +1,13 @@
package me.jweissen.aeticket.controller; package me.jweissen.aeticket.controller;
import me.jweissen.aeticket.dto.request.CartAddRequestDto; import me.jweissen.aeticket.dto.request.CartAddRequestDto;
import me.jweissen.aeticket.dto.response.CartEntryResponseDto; import me.jweissen.aeticket.dto.response.CartEventResponseDto;
import me.jweissen.aeticket.dto.response.CheckoutResponseDto; import me.jweissen.aeticket.dto.response.CheckoutResponseDto;
import me.jweissen.aeticket.service.AuthService;
import me.jweissen.aeticket.service.CartService; import me.jweissen.aeticket.service.CartService;
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.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@ -17,26 +15,29 @@ import java.util.List;
@RequestMapping("/cart") @RequestMapping("/cart")
public class CartController { public class CartController {
private final CartService cartService; private final CartService cartService;
private final AuthService authService;
public CartController(CartService cartService) { public CartController(CartService cartService, AuthService authService) {
this.cartService = cartService; this.cartService = cartService;
this.authService = authService;
} }
@PostMapping("/add") @PostMapping("/add")
public ResponseEntity<Void> addEntry(CartAddRequestDto dto) { public ResponseEntity<Void> addEntry(@RequestBody CartAddRequestDto dto) {
// TODO if (!cartService.add(dto, authService.getCurrentUser().getCurrentCart())) {
return ResponseEntity.noContent().build(); // user gave invalid category id(s)
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@GetMapping("/list") @GetMapping("/list")
public ResponseEntity<List<CartEntryResponseDto>> getCartEntries() { public ResponseEntity<List<CartEventResponseDto>> getCartEntries() {
// TODO return new ResponseEntity<>(cartService.toDto(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
return new ResponseEntity<>(null, HttpStatus.OK);
} }
@GetMapping("/checkout") @GetMapping("/checkout")
public ResponseEntity<CheckoutResponseDto> checkout() { public ResponseEntity<CheckoutResponseDto> checkout() {
// TODO return new ResponseEntity<>(cartService.checkout(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
return ResponseEntity.badRequest().build();
} }
} }

View file

@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@RestController @RestController
@RequestMapping("/eventcategory") @RequestMapping("/category")
public class CategoryController { public class CategoryController {
private final CategoryService categoryService; private final CategoryService categoryService;
@ -26,35 +26,35 @@ public class CategoryController {
public ResponseEntity<Void> create(@RequestBody CategoryRequestDto dto) { public ResponseEntity<Void> create(@RequestBody CategoryRequestDto dto) {
// TODO admin only // TODO admin only
categoryService.create(dto); categoryService.create(dto);
return new ResponseEntity<>(HttpStatus.CREATED); return ResponseEntity.status(HttpStatus.CREATED).build();
} }
@PutMapping("/update") @PutMapping("/update")
public ResponseEntity<Void> update(@RequestBody CategoryUpdateRequestDto dto) { public ResponseEntity<Void> update(@RequestBody CategoryUpdateRequestDto dto) {
// TODO admin only // TODO admin only
System.out.println(dto);
if (!categoryService.update(dto)) { if (!categoryService.update(dto)) {
return ResponseEntity.notFound().build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} }
return new ResponseEntity<>(HttpStatus.CREATED); return ResponseEntity.status(HttpStatus.CREATED).build();
} }
@DeleteMapping("/delete/{id}") @DeleteMapping("/delete/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) { public ResponseEntity<Void> delete(@PathVariable Long id) {
// TODO admin only // TODO admin only
categoryService.delete(id); categoryService.delete(id);
return ResponseEntity.noContent().build(); return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<CategoryResponseDto> getById(@PathVariable Long id) { public ResponseEntity<CategoryResponseDto> getById(@PathVariable Long id) {
return categoryService.getById(id) return categoryService.getById(id)
.map(categoryResponseDto -> new ResponseEntity<>(categoryResponseDto, HttpStatus.OK)) .map(categoryResponseDto -> new ResponseEntity<>(categoryResponseDto, HttpStatus.OK))
.orElseGet(() -> ResponseEntity.notFound().build()); .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
} }
@GetMapping("/list") @GetMapping("/list")
public ResponseEntity<List<CategoryResponseDto>> getAll() { public ResponseEntity<List<CategoryResponseDto>> getAll() {
return new ResponseEntity<>(categoryService.getAll(), HttpStatus.OK); return new ResponseEntity<>(categoryService.getAll(), HttpStatus.OK);
} }
} }

View file

@ -23,28 +23,28 @@ public class EventController {
public ResponseEntity<Void> create(@RequestBody EventRequestDto event) { public ResponseEntity<Void> create(@RequestBody EventRequestDto event) {
// TODO admin only // TODO admin only
eventService.create(event); eventService.create(event);
return new ResponseEntity<>(HttpStatus.CREATED); return ResponseEntity.status(HttpStatus.CREATED).build();
} }
@PutMapping("/update") @PutMapping("/update")
public ResponseEntity<Void> update(@RequestBody EventUpdateRequestDto event) { public ResponseEntity<Void> update(@RequestBody EventUpdateRequestDto event) {
// TODO admin only // TODO admin only
eventService.update(event); eventService.update(event);
return new ResponseEntity<>(HttpStatus.CREATED); return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@DeleteMapping("/delete/{id}") @DeleteMapping("/delete/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) { public ResponseEntity<Void> delete(@PathVariable Long id) {
// TODO admin only // TODO admin only
eventService.delete(id); eventService.delete(id);
return ResponseEntity.noContent().build(); return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<EventResponseDto> getById(@PathVariable Long id) { public ResponseEntity<EventResponseDto> getById(@PathVariable Long id) {
return eventService.getById(id) return eventService.getById(id)
.map(eventResponseDto -> new ResponseEntity<>(eventResponseDto, HttpStatus.OK)) .map(eventResponseDto -> new ResponseEntity<>(eventResponseDto, HttpStatus.OK))
.orElseGet(() -> ResponseEntity.notFound().build()); .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
} }
@GetMapping("/list") @GetMapping("/list")

View file

@ -1,6 +1,5 @@
package me.jweissen.aeticket.controller; package me.jweissen.aeticket.controller;
import me.jweissen.aeticket.aspect.Permissions;
import me.jweissen.aeticket.dto.request.LoginRequestDto; import me.jweissen.aeticket.dto.request.LoginRequestDto;
import me.jweissen.aeticket.dto.request.SignupRequestDto; import me.jweissen.aeticket.dto.request.SignupRequestDto;
import me.jweissen.aeticket.dto.request.UserUpdateRequestDto; import me.jweissen.aeticket.dto.request.UserUpdateRequestDto;
@ -39,30 +38,29 @@ public class UserController {
public ResponseEntity<Void> update(@RequestBody UserUpdateRequestDto user) { public ResponseEntity<Void> update(@RequestBody UserUpdateRequestDto user) {
// TODO admin only // TODO admin only
if (!userService.update(user)) { if (!userService.update(user)) {
return ResponseEntity.notFound().build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} }
return ResponseEntity.noContent().build(); return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@DeleteMapping("/delete/{id}") @DeleteMapping("/delete/{id}")
public ResponseEntity<Void> delete(@PathVariable Integer id) { public ResponseEntity<Void> delete(@PathVariable Long id) {
// TODO admin only // TODO admin only
userService.delete(id); userService.delete(id);
return ResponseEntity.noContent().build(); return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} }
@GetMapping("/list") @GetMapping("/list")
@Permissions(user = false)
public ResponseEntity<List<UserResponseDto>> getAll() { public ResponseEntity<List<UserResponseDto>> getAll() {
// TODO admin only // TODO admin only
return new ResponseEntity<>(userService.getAll(), HttpStatus.OK); return new ResponseEntity<>(userService.getAll(), HttpStatus.OK);
} }
@GetMapping("/load/{id}") @GetMapping("/load/{id}")
public ResponseEntity<UserResponseDto> getById(@PathVariable Integer id) { public ResponseEntity<UserResponseDto> getById(@PathVariable Long id) {
// TODO admin only // TODO admin only
return userService.getById(id) return userService.getById(id)
.map(userResponseDto -> new ResponseEntity<>(userResponseDto, HttpStatus.OK)) .map(userResponseDto -> new ResponseEntity<>(userResponseDto, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(null, HttpStatus.NOT_FOUND)); .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
} }
} }

View file

@ -1,4 +1,7 @@
package me.jweissen.aeticket.dto.request; package me.jweissen.aeticket.dto.request;
public record EventUpdateRequestDto(Long id) { import java.util.Date;
// no ticketCategories to prevent undefined behaviour
public record EventUpdateRequestDto(Long id, String name, Date from, Date to, String description) {
} }

View file

@ -1,4 +1,4 @@
package me.jweissen.aeticket.dto.request; package me.jweissen.aeticket.dto.request;
public record UserUpdateRequestDto(Integer id, String firstname, String lastname, String email) { public record UserUpdateRequestDto(Long id, String firstname, String lastname, String email) {
} }

View file

@ -15,6 +15,9 @@ public class Cart {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(nullable = false)
private Boolean checkedOut = false;
@OneToMany(mappedBy = "cart") @OneToMany(mappedBy = "cart")
@Column(nullable = false) @Column(nullable = false)
private List<CartEntry> cartEntries; private List<CartEntry> cartEntries;

View file

@ -1,26 +1,30 @@
package me.jweissen.aeticket.model; package me.jweissen.aeticket.model;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.*;
import lombok.Setter;
@Entity @Entity
@Table @Table
@Getter @Getter
@Setter @Setter
@NoArgsConstructor
@RequiredArgsConstructor
public class CartEntry { public class CartEntry {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private int id; private int id;
@ManyToOne @ManyToOne
@NonNull
@JoinColumn(nullable = false) @JoinColumn(nullable = false)
private Cart cart; private Cart cart;
@ManyToOne @ManyToOne
@NonNull
@JoinColumn(nullable = false) @JoinColumn(nullable = false)
private Category category; private Category category;
@Column(nullable = false) @Column(nullable = false)
private int amount; @NonNull
private Integer amount;
} }

View file

@ -13,7 +13,7 @@ import java.util.List;
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder @RequiredArgsConstructor
public class Event { public class Event {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@ -21,7 +21,7 @@ public class Event {
@Column(nullable = false) @Column(nullable = false)
@NonNull @NonNull
private String title; private String name;
@Column(nullable = false) @Column(nullable = false)
@NonNull @NonNull
@ -35,7 +35,7 @@ public class Event {
@NonNull @NonNull
private Date end; private Date end;
@OneToMany(mappedBy = "event") @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
@Column(nullable = false) @Column(nullable = false)
private List<Category> categories = new ArrayList<>(); private List<Category> categories = new ArrayList<>();

View file

@ -3,6 +3,7 @@ package me.jweissen.aeticket.model;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.*; import lombok.*;
import java.util.Date;
import java.util.List; import java.util.List;
@Entity @Entity
@ -35,11 +36,19 @@ public class User {
@Column @Column
private String token; private String token;
@Column
private Date tokenValidUntil;
@Column(nullable = false) @Column(nullable = false)
@Enumerated(EnumType.ORDINAL) @Enumerated(EnumType.ORDINAL)
@NonNull @NonNull
private Role role; private Role role;
@OneToOne
@JoinColumn(nullable = false)
@NonNull
private Cart currentCart;
@OneToMany(mappedBy = "user") @OneToMany(mappedBy = "user")
private List<Cart> carts; private List<Cart> carts;
} }

View file

@ -2,6 +2,10 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.Cart; import me.jweissen.aeticket.model.Cart;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface CartRepository extends JpaRepository<Cart, Integer> { public interface CartRepository extends JpaRepository<Cart, Integer> {
@Query("SELECT sum(ce.amount * c.price) FROM CartEntry ce INNER JOIN Category c ON ce.category = c WHERE ce.cart = :cart")
Integer getCheckoutPrice(@Param("cart") Cart cart);
} }

View file

@ -2,6 +2,10 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.Category; import me.jweissen.aeticket.model.Category;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface CategoryRepository extends JpaRepository<Category, Long> { public interface CategoryRepository extends JpaRepository<Category, Long> {
@Query("SELECT c.stock - sum(ce.amount) FROM Category c INNER JOIN CartEntry ce ON c = ce.category WHERE c = :category GROUP BY c")
Integer availableTickets(@Param("category") Category category);
} }

View file

@ -3,7 +3,7 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.User; import me.jweissen.aeticket.model.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> { public interface UserRepository extends JpaRepository<User, Long> {
User findByToken(String token); User findByToken(String token);
User findByEmail(String email); User findByEmail(String email);
} }

View file

@ -1,29 +1,56 @@
package me.jweissen.aeticket.service; package me.jweissen.aeticket.service;
import lombok.Getter;
import me.jweissen.aeticket.model.Role; import me.jweissen.aeticket.model.Role;
import me.jweissen.aeticket.model.User; import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository; import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Optional; import java.util.Optional;
@Service @Service
public class AuthService { public class AuthService {
private final JwtService jwtService; private final JwtService jwtService;
private final UserRepository userRepository; private final UserRepository userRepository;
private final Long tokenValidForMillis;
@Getter
private User currentUser;
public AuthService(JwtService jwtService, UserRepository userRepository) { public AuthService(
JwtService jwtService,
UserRepository userRepository,
@Value("${token.validForHours}") Long tokenValidForHours) {
this.tokenValidForMillis = 1000L * 3600 * tokenValidForHours;
this.jwtService = jwtService; this.jwtService = jwtService;
this.userRepository = userRepository; this.userRepository = userRepository;
} }
public Optional<Role> getRole(String token) { public void extendToken(User user) {
User user = userRepository.findByToken(token); user.setTokenValidUntil(new Date(System.currentTimeMillis() + tokenValidForMillis));
if (user == null) { userRepository.save(user);
return Optional.empty();
}
return Optional.of(user.getRole());
} }
public Optional<User> authenticate(String token) {
Optional<Long> userIdOptional = jwtService.getUserId(token);
// token not valid or userId
if (userIdOptional.isEmpty()) {
return Optional.empty();
}
Optional<User> userOptional = userRepository.findById(userIdOptional.get());
if (userOptional.isEmpty()) {
// user with id not found
return Optional.empty();
}
User user = userOptional.get();
if (user.getTokenValidUntil().before(new Date())) {
// token expired
return Optional.empty();
}
// success
extendToken(user);
currentUser = user;
return Optional.of(user);
}
} }

View file

@ -1,23 +1,36 @@
package me.jweissen.aeticket.service; package me.jweissen.aeticket.service;
import me.jweissen.aeticket.dto.response.CartEntryResponseDto; import me.jweissen.aeticket.dto.request.CartAddRequestDto;
import me.jweissen.aeticket.dto.request.CartEntryRequestDto;
import me.jweissen.aeticket.dto.response.CartEventResponseDto; import me.jweissen.aeticket.dto.response.CartEventResponseDto;
import me.jweissen.aeticket.dto.response.CheckoutResponseDto;
import me.jweissen.aeticket.model.Cart; import me.jweissen.aeticket.model.Cart;
import me.jweissen.aeticket.model.CartEntry;
import me.jweissen.aeticket.model.Category;
import me.jweissen.aeticket.model.Event; import me.jweissen.aeticket.model.Event;
import me.jweissen.aeticket.repository.CartEntryRepository; import me.jweissen.aeticket.repository.CartEntryRepository;
import me.jweissen.aeticket.repository.CartRepository; import me.jweissen.aeticket.repository.CartRepository;
import me.jweissen.aeticket.repository.CategoryRepository;
import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Optional;
@Service @Service
public class CartService { public class CartService {
private final CartRepository cartRepository; private final CartRepository cartRepository;
private final CartEntryRepository cartEntryRepository; private final CartEntryRepository cartEntryRepository;
private final CategoryRepository categoryRepository;
private final UserRepository userRepository;
public CartService(CartRepository cartRepository, CartEntryRepository cartEntryRepository) { public CartService(CartRepository cartRepository, CartEntryRepository cartEntryRepository,
CategoryRepository categoryRepository,
UserRepository userRepository) {
this.cartRepository = cartRepository; this.cartRepository = cartRepository;
this.cartEntryRepository = cartEntryRepository; this.cartEntryRepository = cartEntryRepository;
this.categoryRepository = categoryRepository;
this.userRepository = userRepository;
} }
public List<CartEventResponseDto> toDto(Cart cart) { public List<CartEventResponseDto> toDto(Cart cart) {
@ -27,7 +40,7 @@ public class CartService {
return distinctEvents.stream().map(event -> return distinctEvents.stream().map(event ->
new CartEventResponseDto( new CartEventResponseDto(
event.getId(), event.getId(),
event.getTitle(), event.getName(),
event.getStart(), event.getStart(),
event.getEnd(), event.getEnd(),
event.getDescription(), event.getDescription(),
@ -35,6 +48,35 @@ public class CartService {
)).toList(); )).toList();
} }
//public List<CartEntryResponseDto> getCartByAuthToken() { public boolean add(CartAddRequestDto dto, Cart cart) {
// posting the eventIds is redundant so, those are ignored
for (CartEntryRequestDto cartEntryDto: dto.cartEntries()) {
Optional<Category> categoryOptional = categoryRepository.findById(cartEntryDto.id());
if (categoryOptional.isEmpty()) {
// category id not found
return false;
}
Category category = categoryOptional.get();
if (cartEntryDto.amount() > categoryRepository.availableTickets(category)) {
// wants to order more tickets than available
return false;
}
cart.getCartEntries().add(new CartEntry(cart, category, cartEntryDto.amount()));
}
cartRepository.save(cart);
return true;
}
public CheckoutResponseDto checkout(Cart cart) {
cart.setCheckedOut(true);
cartRepository.save(cart);
// reset current cart
Cart newCart = new Cart();
newCart = cartRepository.save(newCart);
cart.getUser().setCurrentCart(newCart);
userRepository.save(cart.getUser());
return new CheckoutResponseDto(
CategoryService.centsToEuros(cartRepository.getCheckoutPrice(cart))
);
}
} }

View file

@ -27,6 +27,10 @@ public class CategoryService {
); );
} }
public static List<Category> fromDtos(List<CategoryRequestDto> dtos) {
return dtos.stream().map(CategoryService::fromDto).toList();
}
public static CategoryResponseDto toDto(Category category) { public static CategoryResponseDto toDto(Category category) {
return new CategoryResponseDto( return new CategoryResponseDto(
category.getId(), category.getId(),
@ -54,6 +58,7 @@ public class CategoryService {
} }
public boolean update(CategoryUpdateRequestDto dto) { public boolean update(CategoryUpdateRequestDto dto) {
System.out.println(dto);
return categoryRepository.findById(dto.id()) return categoryRepository.findById(dto.id())
.map(category -> { .map(category -> {
category.setName(dto.name()); category.setName(dto.name());

View file

@ -3,7 +3,9 @@ package me.jweissen.aeticket.service;
import me.jweissen.aeticket.dto.request.EventRequestDto; import me.jweissen.aeticket.dto.request.EventRequestDto;
import me.jweissen.aeticket.dto.request.EventUpdateRequestDto; import me.jweissen.aeticket.dto.request.EventUpdateRequestDto;
import me.jweissen.aeticket.dto.response.EventResponseDto; import me.jweissen.aeticket.dto.response.EventResponseDto;
import me.jweissen.aeticket.model.Category;
import me.jweissen.aeticket.model.Event; import me.jweissen.aeticket.model.Event;
import me.jweissen.aeticket.repository.CategoryRepository;
import me.jweissen.aeticket.repository.EventRepository; import me.jweissen.aeticket.repository.EventRepository;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -13,24 +15,27 @@ import java.util.Optional;
@Service @Service
public class EventService { public class EventService {
private final EventRepository eventRepository; private final EventRepository eventRepository;
private final CategoryRepository categoryRepository;
public EventService(EventRepository eventRepository) { public EventService(EventRepository eventRepository,
CategoryRepository categoryRepository) {
this.eventRepository = eventRepository; this.eventRepository = eventRepository;
this.categoryRepository = categoryRepository;
} }
public static Event fromDto(EventRequestDto dto) { public static Event fromDto(EventRequestDto dto) {
return Event.builder() return new Event(
.title(dto.name()) dto.name(),
.description(dto.description()) dto.description(),
.start(dto.from()) dto.from(),
.end(dto.to()) dto.to()
.build(); );
} }
public static EventResponseDto toDto(Event event) { public static EventResponseDto toDto(Event event) {
return new EventResponseDto( return new EventResponseDto(
event.getId(), event.getId(),
event.getTitle(), event.getName(),
event.getStart(), event.getStart(),
event.getEnd(), event.getEnd(),
event.getDescription(), event.getDescription(),
@ -42,14 +47,23 @@ public class EventService {
return events.stream().map(EventService::toDto).toList(); return events.stream().map(EventService::toDto).toList();
} }
public void create(EventRequestDto event) { public void create(EventRequestDto dto) {
eventRepository.save(EventService.fromDto(event)); Event event = EventService.fromDto(dto);
event = eventRepository.save(event);
Event finalEvent = event;
List<Category> categories = CategoryService.fromDtos(dto.ticketCategories());
categories.forEach(c -> c.setEvent(finalEvent));
categoryRepository.saveAll(categories);
} }
public boolean update(EventUpdateRequestDto dto) { public boolean update(EventUpdateRequestDto dto) {
return eventRepository.findById(dto.id()) return eventRepository.findById(dto.id())
.map(event -> { .map(event -> {
// dto auf object assignen event.setName(dto.name());
event.setDescription(dto.description());
event.setStart(dto.from());
event.setEnd(dto.to());
eventRepository.save(event);
return true; return true;
}) })
.orElse(false); .orElse(false);

View file

@ -6,6 +6,8 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -17,10 +19,8 @@ public class JwtService {
private final JWTVerifier jwtVerifier; private final JWTVerifier jwtVerifier;
private final Algorithm algorithm; private final Algorithm algorithm;
private final String userIdClaimKey = "userId"; private final String userIdClaimKey = "userId";
private final Long tokenValidForMillis;
public JwtService(@Value("${token.secret}") String secret, @Value("${token.validForHours}") Long tokenValidForHours) { public JwtService(@Value("${token.secret}") String secret) {
tokenValidForMillis = 1000L * 3600 * tokenValidForHours;
algorithm = Algorithm.HMAC256(secret); algorithm = Algorithm.HMAC256(secret);
jwtVerifier = JWT.require(algorithm).build(); jwtVerifier = JWT.require(algorithm).build();
} }
@ -29,8 +29,6 @@ public class JwtService {
return JWT.create() return JWT.create()
.withSubject("aeticket user token") .withSubject("aeticket user token")
.withClaim(userIdClaimKey, userId) .withClaim(userIdClaimKey, userId)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + tokenValidForMillis))
.sign(algorithm); .sign(algorithm);
} }
@ -42,11 +40,16 @@ public class JwtService {
// token not valid // token not valid
return Optional.empty(); return Optional.empty();
} }
// token expired Claim userIdClaim = decodedJWT.getClaim(userIdClaimKey);
if (decodedJWT.getExpiresAt().before(new Date())) { if (userIdClaim.isNull()) {
// userId claim not present
return Optional.empty(); return Optional.empty();
} }
Claim claim = decodedJWT.getClaim(userIdClaimKey); Long userId = userIdClaim.asLong();
return Optional.of(claim.asLong()); if (userId == null) {
// userId claim not a long
return Optional.empty();
}
return Optional.of(userId);
} }
} }

View file

@ -5,6 +5,7 @@ import me.jweissen.aeticket.dto.request.SignupRequestDto;
import me.jweissen.aeticket.dto.request.UserUpdateRequestDto; import me.jweissen.aeticket.dto.request.UserUpdateRequestDto;
import me.jweissen.aeticket.dto.response.TokenResponseDto; import me.jweissen.aeticket.dto.response.TokenResponseDto;
import me.jweissen.aeticket.dto.response.UserResponseDto; import me.jweissen.aeticket.dto.response.UserResponseDto;
import me.jweissen.aeticket.model.Cart;
import me.jweissen.aeticket.model.Role; import me.jweissen.aeticket.model.Role;
import me.jweissen.aeticket.model.User; import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository; import me.jweissen.aeticket.repository.UserRepository;
@ -38,7 +39,8 @@ public class UserService {
dto.lastname(), dto.lastname(),
dto.email(), dto.email(),
dto.password(), dto.password(),
Role.USER Role.USER,
new Cart()
); );
} }
@ -62,7 +64,7 @@ public class UserService {
return new TokenResponseDto(generateToken(user)); return new TokenResponseDto(generateToken(user));
} }
public void delete(Integer id) { public void delete(Long id) {
userRepository.deleteById(id); userRepository.deleteById(id);
} }
@ -87,7 +89,7 @@ public class UserService {
return true; return true;
} }
public Optional<UserResponseDto> getById(Integer id) { public Optional<UserResponseDto> getById(Long id) {
return userRepository.findById(id).map(UserService::toDto); return userRepository.findById(id).map(UserService::toDto);
} }
} }