database WIP
This commit is contained in:
parent
d1521417cf
commit
e45117b57f
13
pom.xml
13
pom.xml
@ -32,6 +32,19 @@
|
|||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
|||||||
@ -2,10 +2,8 @@ package de.ph87.tools.session;
|
|||||||
|
|
||||||
import de.ph87.tools.user.User;
|
import de.ph87.tools.user.User;
|
||||||
import de.ph87.tools.web.IWebSocketMessage;
|
import de.ph87.tools.web.IWebSocketMessage;
|
||||||
import lombok.Getter;
|
import jakarta.persistence.*;
|
||||||
import lombok.NonNull;
|
import lombok.*;
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -13,36 +11,47 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
public abstract class AbstractSession implements IWebSocketMessage {
|
public abstract class AbstractSession implements IWebSocketMessage {
|
||||||
|
|
||||||
|
@Id
|
||||||
@NonNull
|
@NonNull
|
||||||
public final String uuid = UUID.randomUUID().toString();
|
@Column(nullable = false)
|
||||||
|
private String uuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final User owner;
|
@ManyToOne(optional = false)
|
||||||
|
private User owner;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final ZonedDateTime created = ZonedDateTime.now();
|
@Column(nullable = false)
|
||||||
|
private ZonedDateTime created = ZonedDateTime.now();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ManyToMany
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private final Set<User> users = new HashSet<>();
|
private Set<User> users = new HashSet<>();
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@NonNull
|
@NonNull
|
||||||
public String title = "Spiel ohne Namen";
|
@Column(nullable = false)
|
||||||
|
private String title = "Spiel ohne Namen";
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@NonNull
|
@NonNull
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
|
@Column(nullable = false)
|
||||||
private String password = UUID.randomUUID().toString().substring(0, 4);
|
private String password = UUID.randomUUID().toString().substring(0, 4);
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
|
@Column(nullable = false)
|
||||||
private boolean initial = true;
|
private boolean initial = true;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@Column(nullable = false)
|
||||||
private ZonedDateTime lastAccess = created;
|
private ZonedDateTime lastAccess = created;
|
||||||
|
|
||||||
protected AbstractSession(@NonNull final User user) {
|
protected AbstractSession(@NonNull final User user) {
|
||||||
@ -50,17 +59,13 @@ public abstract class AbstractSession implements IWebSocketMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void join(@NonNull final User user) {
|
public void join(@NonNull final User user) {
|
||||||
synchronized (uuid) {
|
|
||||||
users.add(user);
|
users.add(user);
|
||||||
touch();
|
touch();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void leave(@NonNull final User user) {
|
public void leave(@NonNull final User user) {
|
||||||
synchronized (uuid) {
|
|
||||||
users.remove(user);
|
users.remove(user);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
lastAccess = ZonedDateTime.now();
|
lastAccess = ZonedDateTime.now();
|
||||||
@ -71,4 +76,21 @@ public abstract class AbstractSession implements IWebSocketMessage {
|
|||||||
return List.of("Number", uuid);
|
return List.of("Number", uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOwnedBy(@NonNull final User user) {
|
||||||
|
return owner.equals(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (!(obj instanceof final AbstractSession session)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return session.uuid.equals(this.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return uuid.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,8 @@ public abstract class AbstractSessionController<SESSION extends AbstractSession>
|
|||||||
@Scheduled(timeUnit = TimeUnit.MINUTES, initialDelay = 5, fixedRate = 5)
|
@Scheduled(timeUnit = TimeUnit.MINUTES, initialDelay = 5, fixedRate = 5)
|
||||||
public void cleanUp() {
|
public void cleanUp() {
|
||||||
final ZonedDateTime deadline = ZonedDateTime.now().minusDays(30);
|
final ZonedDateTime deadline = ZonedDateTime.now().minusDays(30);
|
||||||
synchronized (sessions) {
|
|
||||||
sessions.stream().filter(session -> session.getLastAccess().isBefore(deadline)).forEach(this::delete);
|
sessions.stream().filter(session -> session.getLastAccess().isBefore(deadline)).forEach(this::delete);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void delete(@NonNull final SESSION session) {
|
private void delete(@NonNull final SESSION session) {
|
||||||
for (final User user : session.getUsers()) {
|
for (final User user : session.getUsers()) {
|
||||||
@ -63,9 +61,7 @@ public abstract class AbstractSessionController<SESSION extends AbstractSession>
|
|||||||
|
|
||||||
final SESSION session = create(user);
|
final SESSION session = create(user);
|
||||||
log.info("Session CREATED: {}", session);
|
log.info("Session CREATED: {}", session);
|
||||||
synchronized (sessions) {
|
|
||||||
sessions.add(session);
|
sessions.add(session);
|
||||||
}
|
|
||||||
|
|
||||||
return join(session, user);
|
return join(session, user);
|
||||||
}
|
}
|
||||||
@ -110,7 +106,7 @@ public abstract class AbstractSessionController<SESSION extends AbstractSession>
|
|||||||
public AbstractSessionDto changeTitle(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @RequestBody final SessionChangeTitleInbound inbound) {
|
public AbstractSessionDto changeTitle(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @RequestBody final SessionChangeTitleInbound inbound) {
|
||||||
final User user = userService.getByPrivateUuidOrThrow(userUuid);
|
final User user = userService.getByPrivateUuidOrThrow(userUuid);
|
||||||
final SESSION session = getUserSession(user, inbound.uuid);
|
final SESSION session = getUserSession(user, inbound.uuid);
|
||||||
if (session.owner != user) {
|
if (session.isOwnedBy(user)) {
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
session.setTitle(inbound.title);
|
session.setTitle(inbound.title);
|
||||||
@ -122,7 +118,7 @@ public abstract class AbstractSessionController<SESSION extends AbstractSession>
|
|||||||
public AbstractSessionDto changePassword(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @RequestBody final SessionChangePasswordInbound inbound) {
|
public AbstractSessionDto changePassword(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @RequestBody final SessionChangePasswordInbound inbound) {
|
||||||
final User user = userService.getByPrivateUuidOrThrow(userUuid);
|
final User user = userService.getByPrivateUuidOrThrow(userUuid);
|
||||||
final SESSION session = getUserSession(user, inbound.uuid);
|
final SESSION session = getUserSession(user, inbound.uuid);
|
||||||
if (session.owner != user) {
|
if (session.isOwnedBy(user)) {
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
session.setPassword(inbound.password);
|
session.setPassword(inbound.password);
|
||||||
@ -147,15 +143,13 @@ public abstract class AbstractSessionController<SESSION extends AbstractSession>
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Optional<SESSION> findUserSession(@NonNull final User user, @NonNull final String sessionUuid) {
|
private Optional<SESSION> findUserSession(@NonNull final User user, @NonNull final String sessionUuid) {
|
||||||
return user.getSessions().stream().filter(s -> s.uuid.equals(sessionUuid)).filter(sessionClazz::isInstance).map(sessionClazz::cast).findFirst();
|
return user.getSessions().stream().filter(s -> s.getUuid().equals(sessionUuid)).filter(sessionClazz::isInstance).map(sessionClazz::cast).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private SESSION getSessionByUuid(@NonNull final String uuid) {
|
private SESSION getSessionByUuid(@NonNull final String uuid) {
|
||||||
synchronized (sessions) {
|
|
||||||
return sessions.stream().filter(u -> u.getUuid().equals(uuid)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
return sessions.stream().filter(u -> u.getUuid().equals(uuid)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
protected abstract SESSION create(@NonNull final User user);
|
protected abstract SESSION create(@NonNull final User user);
|
||||||
|
|||||||
@ -40,11 +40,11 @@ public abstract class AbstractSessionDto {
|
|||||||
|
|
||||||
protected AbstractSessionDto(@NonNull final AbstractSession session, @NonNull final String type) {
|
protected AbstractSessionDto(@NonNull final AbstractSession session, @NonNull final String type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uuid = session.uuid;
|
this.uuid = session.getUuid();
|
||||||
this.title = session.title;
|
this.title = session.getTitle();
|
||||||
this.created = session.created;
|
this.created = session.getCreated();
|
||||||
this.password = session.getPassword();
|
this.password = session.getPassword();
|
||||||
this.owner = new UserPublicDto(session.owner);
|
this.owner = new UserPublicDto(session.getOwner());
|
||||||
this.users = session.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
|
this.users = session.getUsers().stream().map(UserPublicDto::new).collect(Collectors.toSet());
|
||||||
this.initial = session.isInitial();
|
this.initial = session.isInitial();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
package de.ph87.tools.user;
|
package de.ph87.tools.user;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import de.ph87.tools.session.AbstractSession;
|
import de.ph87.tools.session.AbstractSession;
|
||||||
import de.ph87.tools.web.IWebSocketMessage;
|
import de.ph87.tools.web.IWebSocketMessage;
|
||||||
import lombok.Getter;
|
import jakarta.persistence.Column;
|
||||||
import lombok.NonNull;
|
import jakarta.persistence.Entity;
|
||||||
import lombok.Setter;
|
import jakarta.persistence.Id;
|
||||||
import lombok.ToString;
|
import jakarta.persistence.ManyToMany;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -14,48 +14,67 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Entity
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
public class User implements IWebSocketMessage {
|
public class User implements IWebSocketMessage {
|
||||||
|
|
||||||
|
@Id
|
||||||
@NonNull
|
@NonNull
|
||||||
public final String privateUuid = UUID.randomUUID().toString();
|
@Column(nullable = false)
|
||||||
|
private String privateUuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public final String publicUuid = UUID.randomUUID().toString();
|
@Column(nullable = false)
|
||||||
|
private String publicUuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
public final ZonedDateTime created = ZonedDateTime.now();
|
@NonNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private ZonedDateTime created = ZonedDateTime.now();
|
||||||
|
|
||||||
@JsonIgnore
|
@ManyToMany
|
||||||
@ToString.Exclude
|
@ToString.Exclude
|
||||||
private final Set<AbstractSession> sessions = new HashSet<>();
|
private Set<AbstractSession> sessions = new HashSet<>();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Column(nullable = false)
|
||||||
private ZonedDateTime lastAccess = created;
|
private ZonedDateTime lastAccess = created;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@NonNull
|
@NonNull
|
||||||
public String name = "unnamed";
|
@Column(nullable = false)
|
||||||
|
private String name = "Neuer Benutzer";
|
||||||
|
|
||||||
private void touch() {
|
private void touch() {
|
||||||
lastAccess = ZonedDateTime.now();
|
lastAccess = ZonedDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void join(@NonNull final AbstractSession session) {
|
public void join(@NonNull final AbstractSession session) {
|
||||||
synchronized (privateUuid) {
|
|
||||||
sessions.add(session);
|
sessions.add(session);
|
||||||
touch();
|
touch();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void leave(@NonNull final AbstractSession session) {
|
public void leave(@NonNull final AbstractSession session) {
|
||||||
synchronized (sessions) {
|
|
||||||
sessions.remove(session);
|
sessions.remove(session);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Object> getWebsocketTopic() {
|
public List<Object> getWebsocketTopic() {
|
||||||
return List.of("User", privateUuid);
|
return List.of("User", privateUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (!(obj instanceof final User user)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return user.privateUuid.equals(this.privateUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return privateUuid.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ public class UserPrivateDto {
|
|||||||
public UserPrivateDto(@NonNull final User user) {
|
public UserPrivateDto(@NonNull final User user) {
|
||||||
this.publicUuid = user.getPublicUuid();
|
this.publicUuid = user.getPublicUuid();
|
||||||
this.name = user.getName();
|
this.name = user.getName();
|
||||||
this.privateUuid = user.privateUuid;
|
this.privateUuid = user.getPrivateUuid();
|
||||||
this.created = user.created;
|
this.created = user.getCreated();
|
||||||
this.sessions = user.getSessions().stream().map(AbstractSessionDto::toDto).collect(Collectors.toSet());
|
this.sessions = user.getSessions().stream().map(AbstractSessionDto::toDto).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
src/main/java/de/ph87/tools/user/UserRepository.java
Normal file
7
src/main/java/de/ph87/tools/user/UserRepository.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package de.ph87.tools.user;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.ListCrudRepository;
|
||||||
|
|
||||||
|
public interface UserRepository extends ListCrudRepository<User, String> {
|
||||||
|
|
||||||
|
}
|
||||||
@ -30,12 +30,10 @@ public class UserService {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public User getUserByUuidOrElseCreate(@Nullable final String uuid, @NonNull final HttpServletResponse response) {
|
public User getUserByUuidOrElseCreate(@Nullable final String uuid, @NonNull final HttpServletResponse response) {
|
||||||
synchronized (users) {
|
|
||||||
final User user = Optional.ofNullable(uuid).map(this::findByPrivateUuid).filter(Optional::isPresent).map(Optional::get).orElseGet(this::create);
|
final User user = Optional.ofNullable(uuid).map(this::findByPrivateUuid).filter(Optional::isPresent).map(Optional::get).orElseGet(this::create);
|
||||||
writeUserUuidCookie(response, user);
|
writeUserUuidCookie(response, user);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public UserPrivateDto getUserByUuidOrNull(@Nullable final String userUuid, final @NonNull HttpServletResponse response) {
|
public UserPrivateDto getUserByUuidOrNull(@Nullable final String userUuid, final @NonNull HttpServletResponse response) {
|
||||||
@ -48,27 +46,21 @@ public class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void delete(@NonNull final String userUuid, final @NonNull HttpServletResponse response) {
|
public void delete(@NonNull final String userUuid, final @NonNull HttpServletResponse response) {
|
||||||
synchronized (users) {
|
|
||||||
final User user = findByPrivateUuid(userUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
final User user = findByPrivateUuid(userUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||||
users.remove(user);
|
users.remove(user);
|
||||||
log.info("User DELETED: {}", user);
|
log.info("User DELETED: {}", user);
|
||||||
}
|
|
||||||
writeUserUuidCookie(response, null);
|
writeUserUuidCookie(response, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public User getByPrivateUuidOrThrow(@NonNull final String uuid) {
|
public User getByPrivateUuidOrThrow(@NonNull final String uuid) {
|
||||||
synchronized (users) {
|
|
||||||
return findByPrivateUuid(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
return findByPrivateUuid(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public User getByPublicUuid(@NonNull final String uuid) {
|
public User getByPublicUuid(@NonNull final String uuid) {
|
||||||
synchronized (users) {
|
|
||||||
return findByPublicUuid(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
return findByPublicUuid(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Optional<User> findByPrivateUuid(@NonNull final String uuid) {
|
public Optional<User> findByPrivateUuid(@NonNull final String uuid) {
|
||||||
@ -91,7 +83,7 @@ public class UserService {
|
|||||||
private static void writeUserUuidCookie(@NonNull final HttpServletResponse response, @Nullable final User user) {
|
private static void writeUserUuidCookie(@NonNull final HttpServletResponse response, @Nullable final User user) {
|
||||||
final Cookie cookie = new Cookie(USER_UUID_COOKIE_NAME, "");
|
final Cookie cookie = new Cookie(USER_UUID_COOKIE_NAME, "");
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
cookie.setValue(user.privateUuid);
|
cookie.setValue(user.getPrivateUuid());
|
||||||
}
|
}
|
||||||
cookie.setMaxAge(10 * 365 * 24 * 60 * 60);
|
cookie.setMaxAge(10 * 365 * 24 * 60 * 60);
|
||||||
cookie.setPath("/");
|
cookie.setPath("/");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user