diff --git a/pom.xml b/pom.xml
index 4718743..89a54e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.2.4
+ 3.4.5
diff --git a/src/main/java/de/bstly/board/businesslogic/NotificationsManager.java b/src/main/java/de/bstly/board/businesslogic/NotificationsManager.java
new file mode 100755
index 0000000..2290955
--- /dev/null
+++ b/src/main/java/de/bstly/board/businesslogic/NotificationsManager.java
@@ -0,0 +1,85 @@
+package de.bstly.board.businesslogic;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Lists;
+
+import de.bstly.board.businesslogic.notification.NotificationProvider;
+import de.bstly.board.model.LocalUser;
+import de.bstly.board.model.Notification;
+import de.bstly.board.model.QNotification;
+import de.bstly.board.model.support.Notificationable;
+import de.bstly.board.repository.NotificationRepository;
+
+@Component
+public class NotificationsManager implements SmartInitializingSingleton {
+
+ private Logger logger = LoggerFactory.getLogger(NotificationsManager.class);
+
+ @Autowired
+ private NotificationRepository notificationRepository;
+ @Autowired
+ private UserManager userManager;
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ private List providers;
+
+ private QNotification qNotification;
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ providers = Lists.newArrayList(applicationContext.getBeansOfType(NotificationProvider.class).values());
+ }
+
+ public void processNotificationable(Notificationable notificationable) {
+ logger.info("Created new " + notificationable.getType() + " [" + notificationable.getId() + "] by "
+ + notificationable.getAuthor() + " at " + notificationable.getCreated());
+ }
+
+ public Notification geNotification(String username, Notificationable notificationable) {
+ return notificationRepository.findOne(qNotification.targetType.eq(notificationable.getType())
+ .and(qNotification.owner.eq(username))
+ .and(qNotification.target.eq(notificationable.getId()).or(qNotification.target.isNull()))).orElse(null);
+ }
+
+ public Notification createNotification(Notification notification) {
+ return notificationRepository.save(notification);
+ }
+
+ public Notification updateNotification(Notification notification) {
+ return notificationRepository.save(notification);
+ }
+
+ public void deleteNotification(Long id) {
+ notificationRepository.deleteById(id);
+ }
+
+ protected List getUsernames(Notificationable notificationable) {
+ List notifications = Lists
+ .newArrayList(notificationRepository.findAll(qNotification.targetType.eq(notificationable.getType())
+ .and(qNotification.target.eq(notificationable.getId()).or(qNotification.target.isNull())).and(qNotification.disabled.isFalse())));
+
+ return notifications.stream().map(Notification::getOwner).collect(Collectors.toList());
+ }
+
+ @Async
+ protected void processNotificationable(String username, Notificationable notificationable) {
+ LocalUser user = userManager.getByUsername(username);
+ for (NotificationProvider provider : providers) {
+ if (user.getSettings() != null && user.getSettings().get(provider.getId()) == "true") {
+ provider.processNotificationable(user, notificationable);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/de/bstly/board/businesslogic/UserManager.java b/src/main/java/de/bstly/board/businesslogic/UserManager.java
index a492f0a..7f5229d 100644
--- a/src/main/java/de/bstly/board/businesslogic/UserManager.java
+++ b/src/main/java/de/bstly/board/businesslogic/UserManager.java
@@ -92,7 +92,7 @@ public class UserManager implements UserDetailsService, SmartInitializingSinglet
public void afterSingletonsInstantiated() {
if (!localUserRepository.exists(qLocalUser.roles.contains("ROLE_ADMIN"))) {
if (!StringUtils.hasText(adminPassword)) {
- adminPassword = RandomStringUtils.random(24, true, true);
+ adminPassword = RandomStringUtils.secure().next(24, true, true);
logger.error("password for 'admin': " + adminPassword);
}
LocalUser admin = new LocalUser();
diff --git a/src/main/java/de/bstly/board/businesslogic/notification/EmailNotificationProvider.java b/src/main/java/de/bstly/board/businesslogic/notification/EmailNotificationProvider.java
new file mode 100755
index 0000000..ebc1006
--- /dev/null
+++ b/src/main/java/de/bstly/board/businesslogic/notification/EmailNotificationProvider.java
@@ -0,0 +1,40 @@
+package de.bstly.board.businesslogic.notification;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import de.bstly.board.model.LocalUser;
+import de.bstly.board.model.support.Notificationable;
+
+@Component
+public class EmailNotificationProvider implements NotificationProvider{
+
+
+ @Autowired
+ private JavaMailSender javaMailSender;
+
+ @Value("bstly.board.email.from")
+ private String from;
+
+ @Override
+ public String getId() {
+ return "notificationsEmail";
+ }
+
+ @Async
+ @Override
+ public void processNotificationable(LocalUser user, Notificationable notificationable) {
+ SimpleMailMessage message = new SimpleMailMessage();
+ message.setFrom(from);
+ message.setTo(user.getEmail());
+ message.setSubject("New " + notificationable.getType());
+ message.setText("Created new " + notificationable.getType() +" by "
+ + notificationable.getAuthor() + " at " + notificationable.getCreated());
+ javaMailSender.send(message);
+ }
+
+}
diff --git a/src/main/java/de/bstly/board/businesslogic/notification/NotificationProvider.java b/src/main/java/de/bstly/board/businesslogic/notification/NotificationProvider.java
new file mode 100755
index 0000000..fb6c609
--- /dev/null
+++ b/src/main/java/de/bstly/board/businesslogic/notification/NotificationProvider.java
@@ -0,0 +1,14 @@
+package de.bstly.board.businesslogic.notification;
+
+import org.springframework.scheduling.annotation.Async;
+
+import de.bstly.board.model.LocalUser;
+import de.bstly.board.model.support.Notificationable;
+
+public interface NotificationProvider {
+
+ String getId();
+
+ @Async
+ void processNotificationable(LocalUser user, Notificationable notificationable);
+}
diff --git a/src/main/java/de/bstly/board/businesslogic/support/NotificationEntityListener.java b/src/main/java/de/bstly/board/businesslogic/support/NotificationEntityListener.java
new file mode 100755
index 0000000..3d0c7d1
--- /dev/null
+++ b/src/main/java/de/bstly/board/businesslogic/support/NotificationEntityListener.java
@@ -0,0 +1,26 @@
+package de.bstly.board.businesslogic.support;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.bstly.board.businesslogic.NotificationsManager;
+import de.bstly.board.model.support.Notificationable;
+import jakarta.persistence.PostPersist;
+
+@Component
+public class NotificationEntityListener {
+
+ private static NotificationsManager notificationsManager;
+
+ @Autowired
+ public void setEntityProviderManager(NotificationsManager notificationsManager) {
+ NotificationEntityListener.notificationsManager = notificationsManager;
+ }
+
+ @PostPersist
+ public void postPersist(Notificationable notificationable) {
+ if (notificationsManager != null) {
+ notificationsManager.processNotificationable(notificationable);
+ }
+ }
+}
diff --git a/src/main/java/de/bstly/board/controller/DebugController.java b/src/main/java/de/bstly/board/controller/DebugController.java
index 77acddf..9df34c5 100644
--- a/src/main/java/de/bstly/board/controller/DebugController.java
+++ b/src/main/java/de/bstly/board/controller/DebugController.java
@@ -124,8 +124,8 @@ public class DebugController extends BaseController {
entry.setEntryType(EntryType.INTERN);
entry.setAuthor(username);
entry.setCreated(Instant.now().minus(splittableRandom.nextLong(0, entryAge), ChronoUnit.SECONDS));
- entry.setTitle(RandomStringUtils.randomAscii(splittableRandom.nextInt(10, 250)));
- entry.setText(RandomStringUtils.randomAscii(splittableRandom.nextInt(0, 2500)));
+ entry.setTitle(RandomStringUtils.secure().nextAscii(splittableRandom.nextInt(10, 250)));
+ entry.setText(RandomStringUtils.secure().nextAscii(splittableRandom.nextInt(0, 2500)));
entry.setEntryStatus(EntryStatus.NORMAL);
entry.setFlaggedStatus(FlaggedStatus.NORMAL);
entry = entryManager.save(entry);
@@ -150,7 +150,7 @@ public class DebugController extends BaseController {
Comment comment = new Comment();
comment.setTarget(target);
comment.setAuthor("user" + splittableRandom.nextLong(0, userCount));
- comment.setText(RandomStringUtils.randomAscii(splittableRandom.nextInt(0, 2500)));
+ comment.setText(RandomStringUtils.secure().nextAscii(splittableRandom.nextInt(0, 2500)));
comment.setFlaggedStatus(FlaggedStatus.NORMAL);
comment.setCreated(Instant.now().minus(
splittableRandom.nextLong(0, (Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
@@ -183,7 +183,7 @@ public class DebugController extends BaseController {
comment.setTarget(target);
comment.setParent(parent);
comment.setAuthor("user" + splittableRandom.nextLong(0, userCount));
- comment.setText(RandomStringUtils.randomAscii(splittableRandom.nextInt(0, 2500)));
+ comment.setText(RandomStringUtils.secure().nextAscii(splittableRandom.nextInt(0, 2500)));
comment.setFlaggedStatus(FlaggedStatus.NORMAL);
comment.setCreated(Instant.now().minus(
splittableRandom.nextLong(0, (Instant.now().toEpochMilli() - date.toEpochMilli()) / 1000),
diff --git a/src/main/java/de/bstly/board/controller/NotificationController.java b/src/main/java/de/bstly/board/controller/NotificationController.java
new file mode 100755
index 0000000..9aecd3f
--- /dev/null
+++ b/src/main/java/de/bstly/board/controller/NotificationController.java
@@ -0,0 +1,50 @@
+/**
+ *
+ */
+package de.bstly.board.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import de.bstly.board.businesslogic.NotificationsManager;
+import de.bstly.board.model.Notification;
+
+/**
+ * The Class NotificationController.
+ */
+@RestController
+@RequestMapping("/notifications")
+public class NotificationController extends BaseController {
+
+ @Autowired
+ private NotificationsManager notificationsManager;
+
+
+ @PreAuthorize("isAuthenticated()")
+ @PostMapping()
+ public Notification createNotification(@RequestBody Notification notification) {
+ notification.setOwner(getCurrentUsername());
+ return notificationsManager.createNotification(notification);
+ }
+
+ @PreAuthorize("isAuthenticated()")
+ @PatchMapping()
+ public Notification updateNotification(@RequestBody Notification notification) {
+ notification.setOwner(getCurrentUsername());
+ return notificationsManager.updateNotification(notification);
+ }
+
+ @PreAuthorize("isAuthenticated()")
+ @DeleteMapping("{id}")
+ public void deleteNotification(@PathVariable("id") Long id) {
+ notificationsManager.deleteNotification(id);
+ }
+
+}
diff --git a/src/main/java/de/bstly/board/model/Notification.java b/src/main/java/de/bstly/board/model/Notification.java
new file mode 100755
index 0000000..fe29150
--- /dev/null
+++ b/src/main/java/de/bstly/board/model/Notification.java
@@ -0,0 +1,129 @@
+/**
+ *
+ */
+package de.bstly.board.model;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EntityListeners;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import de.bstly.board.model.support.Types;
+
+/**
+ * The Class Flag.
+ */
+@Entity
+@Table(name = "notifications")
+@EntityListeners({ AuditingEntityListener.class })
+public class Notification {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "id")
+ private Long id;
+ @Column(name = "target")
+ private Long target;
+ @Column(name = "parent")
+ private Long parent;
+ @Column(name = "target_type", nullable = false)
+ private Types targetType;
+ @Column(name = "owner", nullable = false)
+ private String owner;
+ @Column(name = "disabled", nullable = false)
+ private boolean disabled;
+
+ /**
+ * Gets the id.
+ *
+ * @return the id
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the id.
+ *
+ * @param id the new id
+ */
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * Gets the target.
+ *
+ * @return the target
+ */
+ public Long getTarget() {
+ return target;
+ }
+
+ public Long getParent() {
+ return parent;
+ }
+
+ public void setParent(Long parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Sets the target.
+ *
+ * @param target the new target
+ */
+ public void setTarget(Long target) {
+ this.target = target;
+ }
+
+ /**
+ * Gets the target type.
+ *
+ * @return the target type
+ */
+ public Types getTargetType() {
+ return targetType;
+ }
+
+ /**
+ * Sets the target type.
+ *
+ * @param targetType the new target type
+ */
+ public void setTargetType(Types targetType) {
+ this.targetType = targetType;
+ }
+
+ /**
+ * Gets the owner.
+ *
+ * @return the owner
+ */
+ public String getOwner() {
+ return owner;
+ }
+
+ /**
+ * Sets the owner.
+ *
+ * @param owner the new owner
+ */
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public boolean isDisabled() {
+ return disabled;
+ }
+
+ public void setDisabled(boolean disabled) {
+ this.disabled = disabled;
+ }
+
+}
diff --git a/src/main/java/de/bstly/board/model/support/Notificationable.java b/src/main/java/de/bstly/board/model/support/Notificationable.java
new file mode 100755
index 0000000..c765b33
--- /dev/null
+++ b/src/main/java/de/bstly/board/model/support/Notificationable.java
@@ -0,0 +1,15 @@
+package de.bstly.board.model.support;
+
+import java.time.Instant;
+
+public interface Notificationable {
+
+ Long getId();
+
+ String getAuthor();
+
+ Instant getCreated();
+
+ Types getType();
+
+}
diff --git a/src/main/java/de/bstly/board/repository/NotificationRepository.java b/src/main/java/de/bstly/board/repository/NotificationRepository.java
new file mode 100755
index 0000000..0ae691d
--- /dev/null
+++ b/src/main/java/de/bstly/board/repository/NotificationRepository.java
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package de.bstly.board.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.stereotype.Repository;
+
+import de.bstly.board.model.Notification;
+
+/**
+ * The Interface NotifcationRepository.
+ */
+@Repository
+public interface NotificationRepository extends JpaRepository, QuerydslPredicateExecutor {
+
+}