added sending email notifications

This commit is contained in:
Ksan 2025-10-28 20:00:44 +01:00
parent a60a7a99b3
commit 78ba74cd0d
16 changed files with 227 additions and 69 deletions

2
.cred
View File

@ -1,2 +0,0 @@
etfoglasi@gmail.com
wmdz eikm bzrz zavr

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
.db/
.env
todo
.creds
./init/*
*temp.java
./src/main/java/dev/ksan/etfoglasiserver/temp.java

View File

@ -34,6 +34,7 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation("org.simplejavamail:simple-java-mail:8.0.1")
implementation("net.sourceforge.htmlunit:htmlunit:2.70.0")
}

View File

@ -2,6 +2,7 @@ package dev.ksan.etfoglasiserver.controller;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.dto.UserDTO;
import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.service.UserService;
import java.util.List;
@ -27,40 +28,21 @@ public class UserController {
public UserDTO getUserDTO(User user) {
return new UserDTO(user.getId(), user.getEmail());
return new UserDTO(user.getId(), user.getEmail(), user.getSubjectSet());
}
/*
@GetMapping("/users")
public List<User> getUsers() {
return service.getUsers();
}
@GetMapping("/users/{userId}")
public User getUserById(@PathVariable UUID userId) {
return service.getUserById(userId);
}
@PostMapping("/users")
public void addUser(@RequestBody User user) {
service.addUser(user);
}
@PutMapping("/users")
public void updateUser(@RequestBody User user) {
service.updateUser(user);
}
*/
@PutMapping("/users")
public void updateUser(@RequestBody UserCreationDTO user) {
NotificationMethod method = NotificationMethod.valueOf(user.getNotification());
user.setNotificationMethod(method);
service.updateUser(user);
}
@PostMapping("/users")
public void addUser(@RequestBody UserCreationDTO user) {
NotificationMethod method = NotificationMethod.valueOf(user.getNotification());
user.setNotificationMethod(method);
service.addUser(user);
}

View File

@ -1,9 +1,17 @@
package dev.ksan.etfoglasiserver.dto;
import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.Subject;
import java.util.Set;
public class UserCreationDTO {
private String email;
private String newEmail;
private String password;
private Set<Subject> subjectSet;
private String notification;
private NotificationMethod notificationMethod;
public String getEmail() {
return email;
@ -23,4 +31,34 @@ public class UserCreationDTO {
public void setPassword(String password) {
this.password = password;
}
public Set<Subject> getSubjectSet() {
return subjectSet;
}
public void setSubjectSet(Set<Subject> subjectSet) {
this.subjectSet = subjectSet;
}
public String getNotification() {
return notification;
}
public void setNotification(String notificationMethod) {
this.notification = notificationMethod;
}
public void setNewEmail(String newEmail) {
this.newEmail = newEmail;
}
public void setNotificationMethod(NotificationMethod method) {
this.notificationMethod = method;
}
public NotificationMethod getNotificationMethod() {
return notificationMethod;
}
}

View File

@ -1,15 +1,24 @@
package dev.ksan.etfoglasiserver.dto;
import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.Subject;
import java.util.Set;
import java.util.UUID;
public class UserDTO {
private UUID id;
private String email;
private Set<Subject> subjectSet;
private NotificationMethod notificationMethod;
public UserDTO() {}
public UserDTO(UUID id, String email) {
public UserDTO(UUID id, String email, Set<Subject> subjectSet) {
this.id = id;
this.email = email;
this.subjectSet = subjectSet;
}
public UUID getId() {
return id;
}
@ -22,4 +31,20 @@ public class UserDTO {
public void setEmail(String email) {
this.email = email;
}
public Set<Subject> getSubjectSet() {
return subjectSet;
}
public void setSubjectSet(Set<Subject> subjectSet) {
this.subjectSet = subjectSet;
}
public NotificationMethod getNotificationMethod() {
return notificationMethod;
}
public void setNotificationMethod(NotificationMethod notificationMethod) {
this.notificationMethod = notificationMethod;
}
}

View File

@ -0,0 +1,8 @@
package dev.ksan.etfoglasiserver.model;
public enum NotificationMethod {
NO_NOTIFICATION,
EMAIL,
PUSH_NOTIFICATION
}

View File

@ -1,11 +1,8 @@
package dev.ksan.etfoglasiserver.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import java.util.Set;
import java.util.UUID;
@Entity
@ -21,6 +18,8 @@ public class Subject {
@Column(nullable = false)
private String name;
@ManyToMany(mappedBy = "subjectSet", fetch = FetchType.LAZY)
Set<User> users;
public Subject(){
}

View File

@ -3,6 +3,7 @@ package dev.ksan.etfoglasiserver.model;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.Set;
import java.util.UUID;
@Entity
@ -26,6 +27,15 @@ public class User {
columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime regTime;
@Enumerated(EnumType.STRING)
@Column(name = "notification_type", nullable = false)
private NotificationMethod notificationType = NotificationMethod.NO_NOTIFICATION;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "user-subject", joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "subject_id"))
private Set<Subject> subjectSet;
public User() {}
public User(UserCreationDTO user) {
@ -53,6 +63,7 @@ public class User {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@ -60,4 +71,22 @@ public class User {
public LocalDateTime getRegTime() {
return regTime;
}
public Set<Subject> getSubjectSet() {
return subjectSet;
}
public void setSubjectSet(Set<Subject> subjectSet) {
this.subjectSet = subjectSet;
}
public void setNotificationMethod(NotificationMethod notificationMethod) {
this.notificationType = notificationMethod;
}
public NotificationMethod getNotificationMethod() {
return notificationType;
}
}

View File

@ -3,8 +3,10 @@ package dev.ksan.etfoglasiserver.repository;
import dev.ksan.etfoglasiserver.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -13,4 +15,8 @@ public interface UserRepo extends JpaRepository<User, UUID> {
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(String email);
@Query("SELECT u FROM User u JOIN u.subjectSet s WHERE s.id = :subjectId")
List<User> findUsersBySubjectId(@Param("subjectId") UUID id);
}

View File

@ -15,7 +15,7 @@ public class EntryService {
@Autowired EntryRepo entryRepo;
@Autowired SubjectRepo subjectRepo;
@Autowired SubjectService subjectService;
public List<Entry> getEntries() {
return entryRepo.findAll();
@ -28,9 +28,8 @@ public class EntryService {
public void addEntry(EntryDTO entry) {
Subject subject =
subjectRepo
.findById(entry.getSubjectId())
.orElseThrow(() -> new RuntimeException("Subject not found"));
subjectService
.findById(entry.getSubjectId());
Entry newEntry = new Entry(entry);
newEntry.setSubject(subject);
entryRepo.save(newEntry);
@ -42,14 +41,15 @@ public class EntryService {
entryRepo.save(entry);
}catch (Exception e) {
System.out.println("Duplicate");
return;
}
subjectService.notify(entry);
}
public void updateEntry(EntryDTO entry) {
Subject subject =
subjectRepo
.findById(entry.getSubjectId())
.orElseThrow(() -> new RuntimeException("Subject not found"));
subjectService
.findById(entry.getSubjectId());
Entry updateEntry = entryRepo.findById(entry.getId()).orElseThrow(() -> new RuntimeException("Entry not found"));
updateEntry.setSubject(subject);
if (entry.getTitle() != null) {

View File

@ -0,0 +1,41 @@
package dev.ksan.etfoglasiserver.service;
import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.mailer.Mailer;
import org.simplejavamail.api.mailer.config.TransportStrategy;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.MailerBuilder;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class MailService {
String address;
String passwd;
Mailer mailer;
public MailService(String filename) {
try(BufferedReader br = new BufferedReader((new FileReader(filename)))) {
address = br.readLine();
System.out.println(address);
passwd = br.readLine();
}catch(IOException e) {
e.printStackTrace();
}
this.mailer = MailerBuilder
.withSMTPServer("smtp.gmail.com",587,address,passwd)
.withTransportStrategy(TransportStrategy.SMTP)
.withSessionTimeout(10_000).buildMailer();
}
public void sendEmail(String to, String subject, String body) {
Email email = EmailBuilder.startingBlank()
.from(address).to(to)
.withSubject(subject)
.withPlainText(body).buildEmail();
mailer.sendMail(email);
}
}

View File

@ -29,8 +29,6 @@ public class Scraper implements Runnable {
@Autowired SubjectAltService altService;
@Autowired EntryService entryService;
private static List<Entry> entries = new ArrayList<>();
private WebClient webClient;
private volatile boolean running = true;
@ -46,12 +44,6 @@ public class Scraper implements Runnable {
return element == null ? "" : element.asNormalizedText();
}
static List<Entry> getEntries() {
synchronized (entries) {
return new ArrayList<>(entries);
}
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
private void configureHtmlUnitLogging() {
@ -118,7 +110,6 @@ public class Scraper implements Runnable {
entryService.addEntry(entry);
entries.add(entry);
}
ul_idSelection++;

View File

@ -5,6 +5,8 @@ import dev.ksan.etfoglasiserver.model.SubjectAlt;
import dev.ksan.etfoglasiserver.repository.SubjectAltRepo;
import java.util.List;
import java.util.UUID;
import dev.ksan.etfoglasiserver.repository.SubjectRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

View File

@ -1,9 +1,15 @@
package dev.ksan.etfoglasiserver.service;
import dev.ksan.etfoglasiserver.model.Entry;
import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.Subject;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.repository.SubjectRepo;
import java.util.List;
import java.util.UUID;
import dev.ksan.etfoglasiserver.repository.UserRepo;
import org.simplejavamail.api.mailer.Mailer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -11,6 +17,10 @@ import org.springframework.stereotype.Service;
public class SubjectService {
@Autowired SubjectRepo subjectRepo;
@Autowired
UserRepo userRepo;
@Autowired
private UserService userService;
public List<Subject> getSubjects() {
return subjectRepo.findAll();
@ -50,4 +60,32 @@ public class SubjectService {
public Subject getSubject(String name) {
return subjectRepo.findByName(name);
}
public Subject findById(UUID id) {
return subjectRepo.findById(id).orElseThrow(() -> new RuntimeException("Subject not found"));
}
public void notify(Entry entry) {
List<User> users = userService.findUsersBySubjectId(entry.getSubject().getId());
if(users == null || users.isEmpty()) return;
for(User user : users) {
if(user.getNotificationMethod() == NotificationMethod.EMAIL)
this.sendEmailNotification(entry, user);
if(user.getNotificationMethod() == NotificationMethod.PUSH_NOTIFICATION)
this.sendPushNotification(entry, user);
System.out.println(user.getEmail());
}
}
public void sendEmailNotification(Entry entry,User user) {
MailService mail = new MailService(".creds");
mail.sendEmail(user.getEmail(),entry.getTitle(),entry.getParagraph());
}
//todo
public void sendPushNotification(Entry entry,User user) {
System.out.println("Sending push notification");
}
}

View File

@ -1,6 +1,7 @@
package dev.ksan.etfoglasiserver.service;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.model.NotificationMethod;
import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.repository.UserRepo;
import java.util.List;
@ -28,19 +29,12 @@ public class UserService {
if (userRepo.findByEmail(user.getEmail()).isPresent()) {
throw new RuntimeException("User already exists");
}
if (this.isValidEmail(user.getEmail())) {
if (this.isValidEmail(user.getEmail()) && this.isValidPassword(user.getPassword())) {
userRepo.save(new User(user));
}
}
public void addUser(User user) {
if (userRepo.findByEmail(user.getEmail()).isPresent()) {
throw new RuntimeException("User already exists");
}
if (this.isValidEmail(user.getEmail())) {
userRepo.save(user);
}
}
public void updateUser(UserCreationDTO user) {
Optional<User> existingUserOpt = userRepo.findByEmail(user.getEmail());
@ -51,10 +45,14 @@ public class UserService {
if (this.isValidEmail(user.getEmail())) {
if (this.isValidPassword(user.getPassword())) {
User existingUser = existingUserOpt.get();
User existingUser = existingUserOpt.get();
if(user.getPassword() != null && user.getPassword().length() > 0) {
if (this.isValidPassword(user.getPassword())) {
existingUser.setPassword(user.getPassword());
}else throw new RuntimeException("Password too short");
}
if(user.getNewEmail() != null && user.getNewEmail() != existingUser.getEmail()) {
existingUser.setEmail(user.getNewEmail());
@ -64,20 +62,14 @@ public class UserService {
}
existingUser.setPassword(user.getPassword());
existingUser.setSubjectSet(user.getSubjectSet());
existingUser.setNotificationMethod(user.getNotificationMethod());
userRepo.save(existingUser);
} else throw new RuntimeException("Password too short");
} else throw new RuntimeException("Invalid email");
} else throw new RuntimeException("User not found");
}
public void updateUser(User user) {
if (this.isValidEmail(user.getEmail())) {
userRepo.save(user);
}
}
public void deleteUser(UUID userId) {
userRepo.deleteById(userId);
@ -98,4 +90,11 @@ public class UserService {
public boolean isValidPassword(String pass) {
return pass.length() >= 8;
}
public List<User> findUsersBySubjectId(UUID id) {
List<User> users = userRepo.findUsersBySubjectId(id);
if (users.isEmpty()) return null;
return users;
}
}