diff --git a/compose.yaml b/compose.yaml index 3c1592d..7cfc102 100644 --- a/compose.yaml +++ b/compose.yaml @@ -10,5 +10,18 @@ services: - '3306:3306' volumes: - mariadb_data:/var/lib/mysql + + oauth2: + build: + context: https://github.com/dsv-su/toker.git + dockerfile: embedded.Dockerfile + + ports: + - '51337:8080' + environment: + - CLIENT_ID=seshat + - CLIENT_SECRET=n0tS3cr3t + - CLIENT_REDIRECT_URI=http://localhost:8181/login/oauth2/code/seshat + volumes: mariadb_data: diff --git a/pom.xml b/pom.xml index ad97abb..19f86ce 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,10 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.boot + spring-boot-starter-oauth2-client + org.springframework.boot spring-boot-starter-validation diff --git a/src/main/java/se/su/dsv/seshat/configuration/SecurityConfig.java b/src/main/java/se/su/dsv/seshat/configuration/SecurityConfig.java index 33a2f7f..35a7647 100644 --- a/src/main/java/se/su/dsv/seshat/configuration/SecurityConfig.java +++ b/src/main/java/se/su/dsv/seshat/configuration/SecurityConfig.java @@ -6,24 +6,32 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; +import org.springframework.web.filter.ForwardedHeaderFilter; +import se.su.dsv.seshat.services.CustomOAuth2loginSuccessHandler; @Configuration public class SecurityConfig { @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + public SecurityFilterChain securityFilterChain( + HttpSecurity httpSecurity, + CustomOAuth2loginSuccessHandler customOAuth2loginSuccessHandler + ) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) + .addFilterBefore(new ForwardedHeaderFilter(), WebAsyncManagerIntegrationFilter.class) .authorizeHttpRequests(authorize -> authorize .requestMatchers("/css/**", "/js/**", "/register", "/login").permitAll() .anyRequest().authenticated() ) - .formLogin(login -> login - .loginPage("/login") - .defaultSuccessUrl("/files/manage", true) - .permitAll() + .oauth2Login(oauth2 -> oauth2 + .successHandler(customOAuth2loginSuccessHandler) ) .logout(logout -> logout - .logoutSuccessUrl("/login?logout") + .logoutSuccessUrl("/") + .invalidateHttpSession(true) + .clearAuthentication(true) + .deleteCookies("JSESSIONID", "auth_code", "refresh_token", "Authorization") .permitAll() ); return httpSecurity.build(); @@ -33,4 +41,5 @@ public class SecurityConfig { public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + } diff --git a/src/main/java/se/su/dsv/seshat/controllers/FileController.java b/src/main/java/se/su/dsv/seshat/controllers/FileController.java index b80a7a2..edee48e 100644 --- a/src/main/java/se/su/dsv/seshat/controllers/FileController.java +++ b/src/main/java/se/su/dsv/seshat/controllers/FileController.java @@ -24,6 +24,8 @@ import se.su.dsv.seshat.services.UserService; import java.io.File; import java.time.LocalDate; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Controller @@ -50,7 +52,19 @@ public class FileController { .filter(file -> file.getJobStatus() != null) .toList(); - model.addAttribute("files", files); + // Map> filesByDirectory = files.stream().collect(Collectors.groupingBy(FileMetadata::getOutputDirectory)); + + Map> filesByDirectory = files.stream() + .collect(Collectors.groupingBy(outputFile -> { + return uploaded.stream() + .filter(uploadedFile -> uploadedFile.getOutputDirectory().equals(outputFile.getOutputDirectory())) + .filter(uploadedFile -> outputFile.getSourceFile().equals(uploadedFile.getFilePath())) + .findFirst() + .orElseThrow(/* Will never happen */); + + })); + + model.addAttribute("filesByDirectory", filesByDirectory); model.addAttribute("statuses", statuses); return "file-management"; @@ -79,7 +93,6 @@ public class FileController { return "redirect:/files/manage"; } - // Browsers do not support DELETE method so for individual file deletion we use GET @GetMapping("files/download/{id}") public ResponseEntity downloadFile(@PathVariable("id") Long id) { try { @@ -116,7 +129,7 @@ public class FileController { } - + // Browsers do not support DELETE method so for individual file deletion we use GET @GetMapping("/files/delete/{id}") public String deleteFile(@PathVariable("id") Long id, Authentication authentication, Model model) { try { diff --git a/src/main/java/se/su/dsv/seshat/controllers/LandingController.java b/src/main/java/se/su/dsv/seshat/controllers/LandingController.java new file mode 100644 index 0000000..f9c712b --- /dev/null +++ b/src/main/java/se/su/dsv/seshat/controllers/LandingController.java @@ -0,0 +1,18 @@ +package se.su.dsv.seshat.controllers; + +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class LandingController { + + @GetMapping("/") + public String showHomePage(Authentication authentication) { + if (authentication != null) { + return "redirect:/files/manage"; + } + return "redirect:/login"; + } + +} diff --git a/src/main/java/se/su/dsv/seshat/controllers/LoginController.java b/src/main/java/se/su/dsv/seshat/controllers/LoginController.java deleted file mode 100644 index 3fed444..0000000 --- a/src/main/java/se/su/dsv/seshat/controllers/LoginController.java +++ /dev/null @@ -1,33 +0,0 @@ -package se.su.dsv.seshat.controllers; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class LoginController { - - @GetMapping("/") - public String showHomePage(Authentication authentication) { - if (authentication != null) { - return "redirect:/files/manage"; - } - return "redirect:/login"; - } - - @GetMapping("/login") - public String showLoginPage(Model model, String error, String logout) { - if (error != null) { - model.addAttribute("error", "Invalid username or password"); - } - if (logout != null) { - model.addAttribute("message", "Logged out successfully"); - } - return "login"; - } - -} diff --git a/src/main/java/se/su/dsv/seshat/controllers/RegistrationController.java b/src/main/java/se/su/dsv/seshat/controllers/RegistrationController.java deleted file mode 100644 index 4a5e83a..0000000 --- a/src/main/java/se/su/dsv/seshat/controllers/RegistrationController.java +++ /dev/null @@ -1,37 +0,0 @@ -package se.su.dsv.seshat.controllers; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import se.su.dsv.seshat.services.UserService; - -@Controller -public class RegistrationController { - - private final UserService userService; - - public RegistrationController(UserService userService) { - this.userService = userService; - } - - @GetMapping("/register") - public String showRegistrationForm() { - return "register"; - } - - @PostMapping("/register") - public String registerUser(@RequestParam String username, - @RequestParam String email, - @RequestParam String password, - Model model) { - try{ - userService.registerUser(username, email, password); - return "redirect:/login"; - } catch (IllegalArgumentException e) { - model.addAttribute("error", "Registration failed: " + e.getMessage()); - return "register"; - } - } -} diff --git a/src/main/java/se/su/dsv/seshat/entities/AppUser.java b/src/main/java/se/su/dsv/seshat/entities/AppUser.java index 9e775ef..4b02f6b 100644 --- a/src/main/java/se/su/dsv/seshat/entities/AppUser.java +++ b/src/main/java/se/su/dsv/seshat/entities/AppUser.java @@ -25,9 +25,6 @@ public class AppUser { @Column(nullable = false, unique = true) private String username; - @Column(nullable = false) - private String password; - @Column(nullable = false, unique = true) private String email; @@ -42,9 +39,8 @@ public class AppUser { public AppUser() {} - public AppUser(String username, String password, String email, String roles) { + public AppUser(String username, String email, String roles) { this.username = username; - this.password = password; this.email = email; this.roles = roles; } @@ -65,14 +61,6 @@ public class AppUser { this.username = username; } - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - public String getEmail() { return email; } @@ -111,14 +99,13 @@ public class AppUser { if (!(o instanceof AppUser appUser)) return false; return Objects.equals(id, appUser.id) && Objects.equals(username, appUser.username) - && Objects.equals(password, appUser.password) && Objects.equals(email, appUser.email) && Objects.equals(roles, appUser.roles); } @Override public int hashCode() { - return Objects.hash(id, username, password, email, roles); + return Objects.hash(id, username, email, roles); } @Override @@ -126,7 +113,6 @@ public class AppUser { return "AppUser{" + "id=" + id + ", username='" + username + '\'' + - ", password='" + password + '\'' + ", email='" + email + '\'' + ", roles='" + roles + '\'' + ", createdAt=" + createdAt + diff --git a/src/main/java/se/su/dsv/seshat/entities/DeletedFile.java b/src/main/java/se/su/dsv/seshat/entities/DeletedFile.java index 22841fa..4e8db25 100644 --- a/src/main/java/se/su/dsv/seshat/entities/DeletedFile.java +++ b/src/main/java/se/su/dsv/seshat/entities/DeletedFile.java @@ -15,7 +15,7 @@ public class DeletedFile { @GeneratedValue private Long id; - @Column(nullable = false) + @Column(nullable = false, length = 1000) private String filePath; @Column(nullable = false) diff --git a/src/main/java/se/su/dsv/seshat/entities/FileMetadata.java b/src/main/java/se/su/dsv/seshat/entities/FileMetadata.java index e7e099c..d0c5e06 100644 --- a/src/main/java/se/su/dsv/seshat/entities/FileMetadata.java +++ b/src/main/java/se/su/dsv/seshat/entities/FileMetadata.java @@ -23,12 +23,15 @@ public class FileMetadata { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) + @Column(nullable = false, length = 512) private String fileName; - @Column(nullable = false) + @Column(nullable = false, length = 1000) private String filePath; + @Column(length = 1000) + private String sourceFile; + private String language; @ManyToOne(fetch = FetchType.EAGER) @@ -36,13 +39,13 @@ public class FileMetadata { private AppUser user; @Column(nullable = false) - private LocalDateTime uploadedAt = LocalDateTime.now(); + private LocalDateTime uploadedAt; @Enumerated(EnumType.STRING) - @Column(name ="job_status", nullable = false, length = 20) + @Column(name ="job_status", length = 20) private JobStatus jobStatus = JobStatus.PENDING; - @Column + @Column(length = 1000) private String outputDirectory; public FileMetadata() {} @@ -76,6 +79,14 @@ public class FileMetadata { this.filePath = filePath; } + public String getSourceFile() { + return sourceFile; + } + + public void setSourceFile(String sourceFile) { + this.sourceFile = sourceFile; + } + public String getLanguage() { return language; } @@ -116,6 +127,8 @@ public class FileMetadata { this.outputDirectory = outputDirectory; } + + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/se/su/dsv/seshat/services/CustomOAuth2loginSuccessHandler.java b/src/main/java/se/su/dsv/seshat/services/CustomOAuth2loginSuccessHandler.java new file mode 100644 index 0000000..b1cb137 --- /dev/null +++ b/src/main/java/se/su/dsv/seshat/services/CustomOAuth2loginSuccessHandler.java @@ -0,0 +1,43 @@ +package se.su.dsv.seshat.services; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class CustomOAuth2loginSuccessHandler implements AuthenticationSuccessHandler { + private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2loginSuccessHandler.class); + + private final String redirectUrl; + private final UserService userService; + + public CustomOAuth2loginSuccessHandler(@Value("${app.onSuccess-homepage}") String redirectUrl, UserService userService) { + this.redirectUrl = redirectUrl; + this.userService = userService; + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); + logger.info("OAuth2 attributes: {}", oAuth2User.getAttributes()); + + String username = oAuth2User.getName(); + // If the user does not have an email, set it to "no-email". We will not send any eamil notifications to this user. + String email = oAuth2User.getAttribute("mail") != null ? oAuth2User.getAttribute("mail") : "no-email"; + + + if(!userService.existsByUsername(oAuth2User.getAttribute("principal"))) { + userService.registerUser(username, email); + } + response.sendRedirect(redirectUrl); + } +} diff --git a/src/main/java/se/su/dsv/seshat/services/CustomUserDetailService.java b/src/main/java/se/su/dsv/seshat/services/CustomUserDetailService.java index d377fbe..2a49956 100644 --- a/src/main/java/se/su/dsv/seshat/services/CustomUserDetailService.java +++ b/src/main/java/se/su/dsv/seshat/services/CustomUserDetailService.java @@ -23,7 +23,6 @@ public class CustomUserDetailService implements UserDetailsService { .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username)); return User.builder() .username(appUser.getUsername()) - .password(appUser.getPassword()) .roles(appUser.getRoles().split(",")) .build(); } diff --git a/src/main/java/se/su/dsv/seshat/services/JobProcessorService.java b/src/main/java/se/su/dsv/seshat/services/JobProcessorService.java index a9c9bd1..9408433 100644 --- a/src/main/java/se/su/dsv/seshat/services/JobProcessorService.java +++ b/src/main/java/se/su/dsv/seshat/services/JobProcessorService.java @@ -3,7 +3,8 @@ package se.su.dsv.seshat.services; import jakarta.annotation.PostConstruct; import jakarta.persistence.EntityNotFoundException; import jakarta.transaction.Transactional; -import org.springframework.beans.factory.annotation.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.springframework.stereotype.Service; import se.su.dsv.seshat.entities.DeletedFile; @@ -15,10 +16,9 @@ import se.su.dsv.seshat.repositories.FileMetadataRepository; import java.io.File; import java.time.LocalDateTime; import java.util.List; +import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Service public class JobProcessorService { @@ -31,9 +31,6 @@ public class JobProcessorService { private final StorageService storageService; private final DeletedFileRepository deletedFileRepository; - @Value("${app.output-root}") - private String outputRoot; - public JobProcessorService(FileMetadataRepository fileMetadataRepository, Transcriber transcriber, StorageService storageService, @@ -88,9 +85,11 @@ public class JobProcessorService { if (transcribe) { logger.info("Transcription successful for file: {}", managedJob.getFileName()); - storageService.addTranscribedFilesToDatabase(managedJob.getUser(), managedJob.getOutputDirectory()); + managedJob.setJobStatus(JobStatus.COMPLETED); + fileMetadataRepository.saveAndFlush(managedJob); + //TODO: This method can just take the filemetadata object managedJob as arugument we will need further information from it. + storageService.addTranscribedFilesToDatabase(managedJob); cleanupFile(managedJob); - fileMetadataRepository.delete(managedJob); // Delete the job after successful transcription break; } else { logger.info("Transcription failed for file: {}", managedJob.getFileName()); @@ -111,20 +110,22 @@ public class JobProcessorService { } - private boolean cleanupFile(FileMetadata jobFile) { + private void cleanupFile(FileMetadata jobFile) { String filePath = jobFile.getFilePath(); File file = new File(filePath); + File parentDir = file.getParentFile(); if(file.exists()) { if(file.delete()) { recordFileDeletion(filePath, "JobProcessorService"); + // Delete the parent directory if it is empty + if(parentDir != null && parentDir.isDirectory() && Objects.requireNonNull(parentDir.list()).length == 0) { + parentDir.delete(); + } logger.info("File deleted successfully: {}", filePath); - return true; } else { logger.error("Failed to delete file: {}", filePath); - return false; } } - return false; } private void recordFileDeletion(String filePath, String jobProcessorService) { diff --git a/src/main/java/se/su/dsv/seshat/services/StorageService.java b/src/main/java/se/su/dsv/seshat/services/StorageService.java index b57bdec..4971a38 100644 --- a/src/main/java/se/su/dsv/seshat/services/StorageService.java +++ b/src/main/java/se/su/dsv/seshat/services/StorageService.java @@ -1,6 +1,8 @@ package se.su.dsv.seshat.services; import jakarta.transaction.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -18,7 +20,10 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -26,6 +31,8 @@ import java.util.zip.ZipOutputStream; @Service public class StorageService { + private final static Logger logger = LoggerFactory.getLogger(StorageService.class); + private final DeletedFileRepository deletedFileRepository; @Value("${app.upload-root}") private String uploadRoot; @@ -41,7 +48,9 @@ public class StorageService { } @Transactional - public void addTranscribedFilesToDatabase(AppUser user, String outputDirectory) { + public void addTranscribedFilesToDatabase(FileMetadata sourceFile) { + String outputDirectory = sourceFile.getOutputDirectory(); + AppUser user = sourceFile.getUser(); File userOutputDirectory = new File(outputDirectory); if(!userOutputDirectory.exists() || !userOutputDirectory.isDirectory()) { return; @@ -64,6 +73,9 @@ public class StorageService { fileMetadata.setFilePath(filePath); fileMetadata.setUser(user); fileMetadata.setJobStatus(null); + fileMetadata.setUploadedAt(sourceFile.getUploadedAt()); + fileMetadata.setOutputDirectory(sourceFile.getOutputDirectory()); + fileMetadata.setSourceFile(sourceFile.getFilePath()); fileMetadataRepository.save(fileMetadata); } } @@ -84,7 +96,7 @@ public class StorageService { String sanitizedFilename = sanitizeFilename(originalFilename); // Users upload directory - Path userUploadDir = Paths.get(uploadRoot, user.getUsername()); + Path userUploadDir = Paths.get(uploadRoot, user.getUsername(), UUID.randomUUID().toString()); try { if(!Files.exists(userUploadDir)) { @@ -107,7 +119,9 @@ public class StorageService { } else { metadata.setLanguage("auto"); } - metadata.setOutputDirectory(outputRoot + File.separator + user.getUsername()); + metadata.setUploadedAt(LocalDateTime.now()); + String fileFolder = fileNameAndUploadedTime(metadata); + metadata.setOutputDirectory(outputRoot + File.separator + user.getUsername() + File.separator + fileFolder); metadata.setUser(user); return fileMetadataRepository.save(metadata); } catch (IOException e) { @@ -126,6 +140,7 @@ public class StorageService { return fileMetadataRepository.findByUserId(user.getId()) .stream() .filter(file -> file.getFilePath().startsWith(uploadRoot)) + .sorted((file1, file2) -> file2.getUploadedAt().compareTo(file1.getUploadedAt())) .collect(Collectors.toList()); } @@ -147,9 +162,21 @@ public class StorageService { deletedFile.setDeletionTime(LocalDateTime.now()); deletedFileRepository.save(deletedFile); fileMetadataRepository.delete(fileMetadata); + + // Delete the directory if it is empty + Path outputDirectory = Paths.get(fileMetadata.getOutputDirectory()); + if (Files.exists(outputDirectory) && Files.isDirectory(outputDirectory)) { + File[] files = outputDirectory.toFile().listFiles(); + if (files != null && files.length == 0) { + Files.delete(outputDirectory); + } + } + return true; } catch (IllegalArgumentException e) { return false; + } catch (IOException e) { + logger.info("Failed to delete file or directory: " + e.getMessage()); } } else { return false; @@ -166,6 +193,9 @@ public class StorageService { } public File createZipFromFiles(List fileIds, AppUser user) throws IOException { + // TODO: Use java.nio FileSystems.newFileSystem(Files.createTempFile("selected_files", ".zip")); + // FileSystems.newFileSystem(Files.createTempFile("selected_files", ".zip")); + // Temporary ZIP file File zipFile = File.createTempFile("selected_files", ".zip"); @@ -183,9 +213,9 @@ public class StorageService { File file = new File(fileMetadata.getFilePath()); if (file.exists()) { Path filePath = file.toPath(); - + String closestParentDir = filePath.getName(filePath.getNameCount() - 2).toString(); // Add new entry for the file in the ZIP - zos.putNextEntry(new ZipEntry(file.getName())); + zos.putNextEntry(new ZipEntry(closestParentDir + File.separator + file.getName())); // Copy file contents to the ZIP Files.copy(filePath, zos); @@ -200,4 +230,19 @@ public class StorageService { private String sanitizeFilename(String filename) { return filename.replaceAll("[^a-zA-Z0-9.-]", "_"); } + + private String fileNameAndUploadedTime(FileMetadata file) { + String fileName = file.getFileName(); + LocalDateTime uploadedAt = file.getUploadedAt(); + String uploadedToSeconds = uploadedAt.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + return fileNameWithoutExtension(file.getFileName()) + "_" + uploadedToSeconds.replaceAll(":|-", ""); + } + + private String fileNameWithoutExtension(String fileName) { + int lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex != -1) { + return fileName.substring(0, lastDotIndex); + } + return fileName; + } } diff --git a/src/main/java/se/su/dsv/seshat/services/Transcriber.java b/src/main/java/se/su/dsv/seshat/services/Transcriber.java index 9d7b7e5..f7fe21e 100644 --- a/src/main/java/se/su/dsv/seshat/services/Transcriber.java +++ b/src/main/java/se/su/dsv/seshat/services/Transcriber.java @@ -1,11 +1,11 @@ package se.su.dsv.seshat.services; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Service public class Transcriber { diff --git a/src/main/java/se/su/dsv/seshat/services/UserService.java b/src/main/java/se/su/dsv/seshat/services/UserService.java index c8e2ef7..e54895c 100644 --- a/src/main/java/se/su/dsv/seshat/services/UserService.java +++ b/src/main/java/se/su/dsv/seshat/services/UserService.java @@ -1,6 +1,5 @@ package se.su.dsv.seshat.services; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import se.su.dsv.seshat.entities.AppUser; import se.su.dsv.seshat.repositories.AppUserRepository; @@ -9,14 +8,12 @@ import se.su.dsv.seshat.repositories.AppUserRepository; public class UserService { private final AppUserRepository appUserRepository; - private final PasswordEncoder passwordEncoder; - public UserService(AppUserRepository appUserRepository, PasswordEncoder passwordEncoder) { + public UserService(AppUserRepository appUserRepository) { this.appUserRepository = appUserRepository; - this.passwordEncoder = passwordEncoder; } - public void registerUser(String username, String email,String password) { + public void registerUser(String username, String email) { if (appUserRepository.existsByUsername(username)) { throw new IllegalArgumentException("Username already exists"); } @@ -24,7 +21,7 @@ public class UserService { throw new IllegalArgumentException("Email already exists"); } - AppUser newUser = new AppUser(username, passwordEncoder.encode(password), email, "USER"); + AppUser newUser = new AppUser(username, email, "USER"); appUserRepository.save(newUser); } @@ -32,4 +29,8 @@ public class UserService { return appUserRepository.findByUsername(username) .orElseThrow(() -> new IllegalArgumentException("User not found")); } + + public boolean existsByUsername(String username) { + return appUserRepository.existsByUsername(username); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 96825b1..2710d11 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,7 +6,7 @@ spring.servlet.multipart.max-request-size=5GB app.upload-root=/seshat/uploads app.output-root=/seshat/outputs -app.api-url=http://localhost:8181/seshat/api +app.onSuccess-homepage=/files/manage # Database properties (local development) spring.datasource.url=jdbc:mariadb://localhost:3306/seshat @@ -16,4 +16,15 @@ spring.datasource.driver-class-name=org.mariadb.jdbc.Driver # JPA properties spring.jpa.hibernate.ddl-auto=update -spring.jpa.show-sql=false \ No newline at end of file +spring.jpa.show-sql=false + +# OAuth2 properties, remember if you change the registration.provider the provider properties must be updated +spring.security.oauth2.client.provider.docker.authorization-uri=http://localhost:51337/authorize +spring.security.oauth2.client.provider.docker.token-uri=http://localhost:51337/exchange +spring.security.oauth2.client.provider.docker.user-info-uri=http://localhost:51337/verify +spring.security.oauth2.client.provider.docker.user-name-attribute=sub +spring.security.oauth2.client.registration.seshat.client-id=seshat +spring.security.oauth2.client.registration.seshat.client-secret=n0tS3cr3t +spring.security.oauth2.client.registration.seshat.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.seshat.provider=docker +spring.security.oauth2.client.registration.seshat.redirect-uri={baseUrl}/login/oauth2/code/{registrationId} \ No newline at end of file diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index f941637..4f81baa 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -1,3 +1,7 @@ +:root { + --bs-primary-rgb: 0, 47, 95; + --bs-btn-bg: #002F5F; +} /* Sticky footer layout */ /* Header Styling */ .header { @@ -7,8 +11,9 @@ } .header .app-title { - font-size: 1.5rem; - font-weight: bold; + font-family: 'PMN Caecilia', serif; + font-size: 2rem; + font-weight: normal; margin: 0; } @@ -23,8 +28,27 @@ font-size: 1.5rem; } +.logo { + width: 12rem; + margin-right: 1rem; +} + +.table-wrapper { + max-height: 160px; +} + +.table thead th { + position: sticky; + top: 0; + z-index: 1; + background-color: #f9fafb; +} +footer { + margin-top: 1rem; +} + html, body { - height: 100%; /* Ensure the height of the body is at least the viewport height */ + min-height: 100dvh; } body { diff --git a/src/main/resources/static/images/SU_logotyp_Landscape_Invert.svg b/src/main/resources/static/images/SU_logotyp_Landscape_Invert.svg new file mode 100644 index 0000000..8cd8db8 --- /dev/null +++ b/src/main/resources/static/images/SU_logotyp_Landscape_Invert.svg @@ -0,0 +1,379 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/file-management.html b/src/main/resources/templates/file-management.html index 2121b01..ff54067 100644 --- a/src/main/resources/templates/file-management.html +++ b/src/main/resources/templates/file-management.html @@ -5,17 +5,20 @@ File Management - Seshat App - +
+

Seshat Audio Transcriber