changed some stuff lol

This commit is contained in:
Ksan 2026-05-12 21:25:30 +02:00
parent dadbfde802
commit 4df33663d8
13 changed files with 130 additions and 61 deletions

View File

@ -8,6 +8,6 @@ services:
- 'POSTGRES_USER=test' - 'POSTGRES_USER=test'
volumes: volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql - ./init.sql:/docker-entrypoint-initdb.d/init.sql
- .db:/var/lib/postgresql/data - ./.db:/var/lib/postgresql/data
ports: ports:
- '5432:5432' - '5432:5432'

View File

@ -1,5 +1,6 @@
-- This script was generated by the ERD tool in pgAdmin 4. -- This script was generated by the ERD tool in pgAdmin 4.
-- Please log an issue at https://github.com/pgadmin-org/pgadmin4/issues/new/choose if you find any bugs, including reproduction steps. -- Please log an issue at https://github.com/pgadmin-org/pgadmin4/issues/new/choose if you find any bugs, including reproduction steps.
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
BEGIN; BEGIN;
@ -12,16 +13,19 @@ CREATE TABLE IF NOT EXISTS public.alt_subject
CONSTRAINT alt_subject_name_key UNIQUE (name) CONSTRAINT alt_subject_name_key UNIQUE (name)
); );
CREATE TABLE IF NOT EXISTS public.entries CREATE TABLE IF NOT EXISTS public.entries
( (
id text COLLATE pg_catalog."default" NOT NULL, id uuid NOT NULL,
subject_id uuid NOT NULL, subject_id uuid NOT NULL,
title text COLLATE pg_catalog."default", title text,
info_entry text COLLATE pg_catalog."default", info_entry text,
paragraph text COLLATE pg_catalog."default", paragraph text,
time_published timestamp without time zone NOT NULL, time_published timestamp without time zone NOT NULL,
filepath text COLLATE pg_catalog."default", filepath text,
group_name text COLLATE pg_catalog."default", group_name text,
CONSTRAINT entries_pkey PRIMARY KEY (id) CONSTRAINT entries_pkey PRIMARY KEY (id)
); );

View File

@ -3,6 +3,7 @@ package dev.ksan.etfoglasiserver;
import dev.ksan.etfoglasiserver.service.Scraper; import dev.ksan.etfoglasiserver.service.Scraper;
import java.util.Scanner; import java.util.Scanner;
import dev.ksan.etfoglasiserver.service.SubjectService;
import dev.ksan.etfoglasiserver.util.SubjectLoader; import dev.ksan.etfoglasiserver.util.SubjectLoader;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
@ -21,16 +22,18 @@ public class EtfoglasiServerApplication {
Scraper scraper = context.getBean(Scraper.class); Scraper scraper = context.getBean(Scraper.class);
/* SubjectService subjectService = context.getBean(SubjectService.class);
SubjectLoader subjectLoader = context.getBean(SubjectLoader.class);
subjectLoader.loadSubjects(); if (subjectService.count() <= 1) {
SubjectLoader subjectLoader = context.getBean(SubjectLoader.class);
System.out.println("Loading..."); subjectLoader.loadSubjects();
subjectLoader.loadAlts();
System.out.println("Loading...");
*/ subjectLoader.loadAlts();
} else {
System.out.println("Subjects already loaded, skipping...");
}
Thread webClientThread = new Thread(scraper, "WebClientThread"); Thread webClientThread = new Thread(scraper, "WebClientThread");
Scanner scanner = new Scanner(System.in); Scanner scanner = new Scanner(System.in);

View File

@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
public class SubjectController { public class SubjectController {
@Autowired SubjectService service; @Autowired SubjectService service;
@GetMapping("/subject") @GetMapping("/subjects")
public List<Subject> getSubjects() { public List<Subject> getSubjects() {
return service.getSubjects(); return service.getSubjects();
} }

View File

@ -18,12 +18,12 @@ public class UserController {
@Autowired UserService service; @Autowired UserService service;
@GetMapping("/users") // @GetMapping("/users")
public ResponseEntity<List<UserDTO>> getUsers() { // public ResponseEntity<List<UserDTO>> getUsers() {
//
return new ResponseEntity(service.getUsers(), HttpStatus.OK); // return new ResponseEntity(service.getUsers(), HttpStatus.OK);
} // }
//
@GetMapping("users/{userId}") @GetMapping("users/{userId}")
public ResponseEntity<UserDTO> getUserById(@PathVariable UUID userId) { public ResponseEntity<UserDTO> getUserById(@PathVariable UUID userId) {
@ -38,8 +38,6 @@ public class UserController {
@PutMapping("/users") @PutMapping("/users")
public ResponseEntity<UserDTO> updateUser(@RequestBody UserCreationDTO user) { public ResponseEntity<UserDTO> updateUser(@RequestBody UserCreationDTO user) {
NotificationMethod method = NotificationMethod.valueOf(user.getNotification());
user.setNotificationMethod(method);
UserDTO userUpdated = service.updateUser(user); UserDTO userUpdated = service.updateUser(user);
if (userUpdated != null) { if (userUpdated != null) {
return new ResponseEntity<>(userUpdated, HttpStatus.OK); return new ResponseEntity<>(userUpdated, HttpStatus.OK);
@ -47,6 +45,27 @@ public class UserController {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
@PutMapping("/users/{userId}/subjects/{subjectId}")
public ResponseEntity<UserDTO> addSubject(
@PathVariable UUID userId,
@PathVariable UUID subjectId
) {
UserDTO updatedUser = service.addSubject(userId, subjectId);
return ResponseEntity.ok(updatedUser);
}
@DeleteMapping("/users/{userId}/subjects/{subjectId}")
public ResponseEntity<UserDTO> removeSubject(
@PathVariable UUID userId,
@PathVariable UUID subjectId
) {
UserDTO updatedUser = service.removeSubject(userId, subjectId);
return ResponseEntity.ok(updatedUser);
}
@PostMapping("/login") @PostMapping("/login")
public ResponseEntity<JwtResponseDTO> login(@RequestBody UserLoginDTO user) { public ResponseEntity<JwtResponseDTO> login(@RequestBody UserLoginDTO user) {
@ -63,11 +82,11 @@ public class UserController {
if (newUser != null) return new ResponseEntity<>(newUser, HttpStatus.CREATED); if (newUser != null) return new ResponseEntity<>(newUser, HttpStatus.CREATED);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} }
//
@DeleteMapping("/users/{userId}") // @DeleteMapping("/users/{userId}")
public void deleteUser(@PathVariable UUID userId) { // public void deleteUser(@PathVariable UUID userId) {
service.deleteUser(userId); // service.deleteUser(userId);
} // }
/* /*
@PostMapping("/users") @PostMapping("/users")

View File

@ -4,15 +4,21 @@ public class JwtResponseDTO {
private String token; private String token;
private String email; private String email;
private String userId;
private String message; private String message;
public JwtResponseDTO(String token, String email, String message) { public JwtResponseDTO(String token, String email, String userId, String message) {
this.token = token; this.token = token;
this.email = email; this.email = email;
this.userId = userId;
this.message = message; this.message = message;
} }
public String getToken() { public String getUserId() {
return userId;
}
public String getToken() {
return token; return token;
} }

View File

@ -11,7 +11,6 @@ public class UserCreationDTO {
private String password; private String password;
private Set<Subject> subjectSet; private Set<Subject> subjectSet;
private String notification; private String notification;
private NotificationMethod notificationMethod;
public String getEmail() { public String getEmail() {
return email; return email;
@ -54,11 +53,4 @@ public class UserCreationDTO {
} }
public void setNotificationMethod(NotificationMethod method) {
this.notificationMethod = method;
}
public NotificationMethod getNotificationMethod() {
return notificationMethod;
}
} }

View File

@ -9,7 +9,6 @@ public class UserDTO {
private UUID id; private UUID id;
private String email; private String email;
private Set<Subject> subjectSet; private Set<Subject> subjectSet;
private NotificationMethod notificationMethod;
public UserDTO() {} public UserDTO() {}
@ -44,11 +43,5 @@ public class UserDTO {
this.subjectSet = subjectSet; this.subjectSet = subjectSet;
} }
public NotificationMethod getNotificationMethod() {
return notificationMethod;
}
public void setNotificationMethod(NotificationMethod notificationMethod) {
this.notificationMethod = notificationMethod;
}
} }

View File

@ -31,7 +31,7 @@ public class User {
@Column(name = "notification_type", nullable = false) @Column(name = "notification_type", nullable = false)
private NotificationMethod notificationType = NotificationMethod.NO_NOTIFICATION; private NotificationMethod notificationType = NotificationMethod.NO_NOTIFICATION;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @ManyToMany(fetch = FetchType.LAZY )
@JoinTable(name = "user-subject", joinColumns = @JoinColumn(name = "user_id"), @JoinTable(name = "user-subject", joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "subject_id")) inverseJoinColumns = @JoinColumn(name = "subject_id"))
private Set<Subject> subjectSet; private Set<Subject> subjectSet;

View File

@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
@Service @Service
public class JWTService { public class JWTService {
//TODO add persistent token env variable
private String secretKey = ""; private String secretKey = "";
private JWTService() { private JWTService() {

View File

@ -20,6 +20,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
// This exists because i made it a while ago and I just kept it just because
// should probably move to just hitting the endpoints directly instead of digging through the html manually
@Component @Component
public class Scraper implements Runnable { public class Scraper implements Runnable {

View File

@ -90,4 +90,9 @@ public class SubjectService {
public void sendPushNotification(Entry entry,User user) { public void sendPushNotification(Entry entry,User user) {
System.out.println("Sending push notification"); System.out.println("Sending push notification");
} }
public long count() {
return subjectRepo.count();
}
} }

View File

@ -4,8 +4,9 @@ import dev.ksan.etfoglasiserver.dto.JwtResponseDTO;
import dev.ksan.etfoglasiserver.dto.UserCreationDTO; import dev.ksan.etfoglasiserver.dto.UserCreationDTO;
import dev.ksan.etfoglasiserver.dto.UserDTO; import dev.ksan.etfoglasiserver.dto.UserDTO;
import dev.ksan.etfoglasiserver.dto.UserLoginDTO; import dev.ksan.etfoglasiserver.dto.UserLoginDTO;
import dev.ksan.etfoglasiserver.model.Subject;
import dev.ksan.etfoglasiserver.model.User; import dev.ksan.etfoglasiserver.model.User;
import dev.ksan.etfoglasiserver.model.UserPrincipal; import dev.ksan.etfoglasiserver.repository.SubjectRepo;
import dev.ksan.etfoglasiserver.repository.UserRepo; import dev.ksan.etfoglasiserver.repository.UserRepo;
import java.util.List; import java.util.List;
@ -19,8 +20,6 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -28,12 +27,18 @@ import org.springframework.transaction.annotation.Transactional;
@Service @Service
@Transactional @Transactional
public class UserService { public class UserService {
@Autowired
SubjectRepo subjectRepo;
@Autowired UserRepo userRepo; @Autowired UserRepo userRepo;
@Autowired private AuthenticationManager authManager; @Autowired private AuthenticationManager authManager;
@Autowired private JWTService jwtService; @Autowired private JWTService jwtService;
private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
public List<UserDTO> getUsers() { public List<UserDTO> getUsers() {
@ -70,10 +75,10 @@ public class UserService {
if (user.getPassword() != null && user.getPassword().length() > 0) { if (user.getPassword() != null && user.getPassword().length() > 0) {
if (this.isValidPassword(user.getPassword())) { if (this.isValidPassword(user.getPassword())) {
existingUser.setPassword(user.getPassword()); existingUser.setPassword(encoder.encode(user.getPassword()));
} else throw new RuntimeException("Password too short"); } else throw new RuntimeException("Password too short");
} }
if (user.getNewEmail() != null && user.getNewEmail() != existingUser.getEmail()) { if (user.getNewEmail() != null && !user.getNewEmail().equals(existingUser.getEmail())) {
existingUser.setEmail(user.getNewEmail()); existingUser.setEmail(user.getNewEmail());
} else { } else {
@ -83,8 +88,6 @@ public class UserService {
existingUser.setSubjectSet(user.getSubjectSet()); existingUser.setSubjectSet(user.getSubjectSet());
existingUser.setNotificationMethod(user.getNotificationMethod());
userRepo.save(existingUser); userRepo.save(existingUser);
return getUserDTO(existingUser); return getUserDTO(existingUser);
@ -139,24 +142,62 @@ public class UserService {
throw new RuntimeException("Invalid email"); throw new RuntimeException("Invalid email");
} }
public JwtResponseDTO verify(UserLoginDTO user) { public JwtResponseDTO verify(UserLoginDTO user) {
Authentication authentication = authManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())
);
Authentication authentication = authManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())); if (!authentication.isAuthenticated()) {
throw new BadCredentialsException("Invalid username or password");
}
if (authentication.isAuthenticated()) { String token = jwtService.generateToken(user.getEmail());
String token = jwtService.generateToken(user.getEmail()); User foundUser = userRepo.findByEmail(user.getEmail())
return new JwtResponseDTO(token, user.getEmail(), HttpStatus.OK.toString()); .orElseThrow(() -> new RuntimeException("User not found after authentication"));
} else {
throw new BadCredentialsException("Invalid username or password"); return new JwtResponseDTO(
token,
foundUser.getEmail(),
foundUser.getId().toString(),
HttpStatus.OK.toString()
);
} }
}
public Optional<User> getAccountByEmail(String email) { public Optional<User> getAccountByEmail(String email) {
return userRepo.findByEmail(email); return userRepo.findByEmail(email);
} }
public UserDTO addSubject(UUID userId, UUID subjectId) {
User user = userRepo.findById(userId)
.orElseThrow(() -> new RuntimeException("User not found"));
Subject subject = subjectRepo.findById(subjectId)
.orElseThrow(() -> new RuntimeException("Subject not found"));
user.getSubjectSet().add(subject);
userRepo.save(user);
return getUserDTO(user);
}
public UserDTO removeSubject(UUID userId, UUID subjectId) {
User user = userRepo.findById(userId)
.orElseThrow(() -> new RuntimeException("User not found"));
Subject subject = subjectRepo.findById(subjectId)
.orElseThrow(() -> new RuntimeException("Subject not found"));
user.getSubjectSet().remove(subject);
userRepo.save(user);
return getUserDTO(user);
}
} }