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
-
+