diff --git a/README.md b/README.md index 4784024..0f53e80 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # QuickDrop -QuickDrop is a secure, easy-to-use file sharing application that allows users to upload files without an account, generate download links, and manage file availability, all with built-in malware scanning and optional password protection. +QuickDrop is a secure, easy-to-use file sharing application that allows users to upload files without an account, +generate download links, and manage file availability, all with built-in malware scanning and optional password +protection. ## This project is still under development, and all but the most basic features are still missing. + **Available Features:** - - **File Upload**: Users can upload files without needing to create an account. - - **File list**: Users can view a list of uploaded files. - - **File Download**: Users can download files from the file list. + +- **File Upload**: Users can upload files without needing to create an account. +- **File list**: Users can view a list of uploaded files. +- **File Download**: Users can download files from the file list. +- **Download Links**: Generate download links for easy sharing. ## Features diff --git a/src/main/java/org/rostislav/quickdrop/config/SecurityConfig.java b/src/main/java/org/rostislav/quickdrop/config/SecurityConfig.java index 096fae2..bf63774 100644 --- a/src/main/java/org/rostislav/quickdrop/config/SecurityConfig.java +++ b/src/main/java/org/rostislav/quickdrop/config/SecurityConfig.java @@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; @Configuration @EnableWebSecurity @@ -15,6 +16,9 @@ public class SecurityConfig { http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest().anonymous() ); + http.csrf(csrf -> + csrf.csrfTokenRepository(new CookieCsrfTokenRepository()) + ); return http.build(); } diff --git a/src/main/java/org/rostislav/quickdrop/controller/FileRestController.java b/src/main/java/org/rostislav/quickdrop/controller/FileRestController.java index a74c06e..cdbe1f6 100644 --- a/src/main/java/org/rostislav/quickdrop/controller/FileRestController.java +++ b/src/main/java/org/rostislav/quickdrop/controller/FileRestController.java @@ -20,7 +20,8 @@ public class FileRestController { } @PostMapping("/upload") - public ResponseEntity saveFile(@RequestParam("file") MultipartFile file, FileUploadRequest fileUploadRequest) { + public ResponseEntity saveFile(@RequestParam("file") MultipartFile file, + FileUploadRequest fileUploadRequest) { FileEntity fileEntity = fileService.saveFile(file, fileUploadRequest); if (fileEntity != null) { return ResponseEntity.ok(fileEntity); diff --git a/src/main/java/org/rostislav/quickdrop/controller/FileViewController.java b/src/main/java/org/rostislav/quickdrop/controller/FileViewController.java index fb0b05a..ed95d3b 100644 --- a/src/main/java/org/rostislav/quickdrop/controller/FileViewController.java +++ b/src/main/java/org/rostislav/quickdrop/controller/FileViewController.java @@ -1,5 +1,6 @@ package org.rostislav.quickdrop.controller; +import jakarta.servlet.http.HttpServletRequest; import org.rostislav.quickdrop.model.FileEntity; import org.rostislav.quickdrop.model.FileUploadRequest; import org.rostislav.quickdrop.service.FileService; @@ -30,16 +31,13 @@ public class FileViewController { public String saveFile(@RequestParam("file") MultipartFile file, @RequestParam("description") String description, @RequestParam(value = "keepIndefinitely", defaultValue = "false") boolean keepIndefinitely, - Model model) { + Model model, HttpServletRequest request) { FileUploadRequest fileUploadRequest = new FileUploadRequest(description, keepIndefinitely); - FileEntity fileEntity = fileService.saveFile(file, fileUploadRequest); - if (fileEntity != null) { - model.addAttribute("message", "File uploaded successfully"); - } else { - model.addAttribute("message", "File upload failed"); - } + if (fileEntity != null) { + return filePage(fileEntity.uuid, model, request); + } return "upload"; } @@ -50,6 +48,16 @@ public class FileViewController { return "listFiles"; } + @GetMapping("/{UUID}") + public String filePage(@PathVariable String UUID, Model model, HttpServletRequest request) { + FileEntity file = fileService.getFile(UUID); + model.addAttribute("file", file); + String downloadLink = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/file/" + file.uuid; + model.addAttribute("downloadLink", downloadLink); + + return "fileUploaded"; + } + @GetMapping("/download/{id}") public ResponseEntity downloadFile(@PathVariable Long id) { return fileService.downloadFile(id); diff --git a/src/main/java/org/rostislav/quickdrop/model/FileEntity.java b/src/main/java/org/rostislav/quickdrop/model/FileEntity.java index 95a0204..be31518 100644 --- a/src/main/java/org/rostislav/quickdrop/model/FileEntity.java +++ b/src/main/java/org/rostislav/quickdrop/model/FileEntity.java @@ -11,7 +11,7 @@ public class FileEntity { public Long id; public String name; - public String UUID; + public String uuid; public String description; public long size; public boolean keepIndefinitely; diff --git a/src/main/java/org/rostislav/quickdrop/repository/FileRepository.java b/src/main/java/org/rostislav/quickdrop/repository/FileRepository.java index 29822a9..81618f6 100644 --- a/src/main/java/org/rostislav/quickdrop/repository/FileRepository.java +++ b/src/main/java/org/rostislav/quickdrop/repository/FileRepository.java @@ -2,7 +2,12 @@ package org.rostislav.quickdrop.repository; import org.rostislav.quickdrop.model.FileEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; public interface FileRepository extends JpaRepository { - + @Query("SELECT f FROM FileEntity f WHERE f.uuid = :uuid") + public Optional findByUUID(@Param("uuid") String uuid); } diff --git a/src/main/java/org/rostislav/quickdrop/service/FileService.java b/src/main/java/org/rostislav/quickdrop/service/FileService.java index 4cd217d..cf24309 100644 --- a/src/main/java/org/rostislav/quickdrop/service/FileService.java +++ b/src/main/java/org/rostislav/quickdrop/service/FileService.java @@ -3,6 +3,8 @@ package org.rostislav.quickdrop.service; import org.rostislav.quickdrop.model.FileEntity; import org.rostislav.quickdrop.model.FileUploadRequest; import org.rostislav.quickdrop.repository.FileRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; @@ -20,6 +22,7 @@ import java.util.UUID; @Service public class FileService { + private static final Logger logger = LoggerFactory.getLogger(FileService.class); private final FileRepository fileRepository; @Value("${file.save.path}") private String fileSavePath; @@ -34,17 +37,19 @@ public class FileService { try { Files.createFile(path); Files.write(path, file.getBytes()); + logger.info("File saved: {}", path); } catch (Exception e) { return null; } FileEntity fileEntity = new FileEntity(); fileEntity.name = file.getOriginalFilename(); - fileEntity.UUID = uuid; + fileEntity.uuid = uuid; fileEntity.description = fileUploadRequest.description; fileEntity.size = file.getSize(); fileEntity.keepIndefinitely = fileUploadRequest.keepIndefinitely; + logger.info("FileEntity saved: {}", fileEntity); return fileRepository.save(fileEntity); } @@ -62,7 +67,7 @@ public class FileService { return ResponseEntity.notFound().build(); } - Path path = Path.of(fileSavePath, referenceById.get().UUID); + Path path = Path.of(fileSavePath, referenceById.get().uuid); try { Resource resource = new UrlResource(path.toUri()); return ResponseEntity.ok() @@ -76,4 +81,8 @@ public class FileService { public FileEntity getFile(Long id) { return fileRepository.findById(id).orElse(null); } + + public FileEntity getFile(String uuid) { + return fileRepository.findByUUID(uuid).orElse(null); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 39d99f6..ae3ecaa 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,4 +9,5 @@ spring.thymeleaf.suffix=.html spring.thymeleaf.cache=false spring.servlet.multipart.max-file-size=1024MB spring.servlet.multipart.max-request-size=1024MB +server.tomcat.connection-timeout=60000 file.save.path=/files \ No newline at end of file diff --git a/src/main/resources/templates/fileUploaded.html b/src/main/resources/templates/fileUploaded.html new file mode 100644 index 0000000..f132948 --- /dev/null +++ b/src/main/resources/templates/fileUploaded.html @@ -0,0 +1,31 @@ + + + + + File has been uploaded + + +

File has been uploaded

+
+

File Name

+

+

File description

+

+

Uploaded At

+

+

Keep Indefinitely

+

+

File Size

+

+

Link:

+

+

Download

+

+ Download +

+
+

+ View all files +

+ + \ No newline at end of file diff --git a/src/main/resources/templates/listFiles.html b/src/main/resources/templates/listFiles.html index 36d52da..06ab55e 100644 --- a/src/main/resources/templates/listFiles.html +++ b/src/main/resources/templates/listFiles.html @@ -23,7 +23,7 @@ - Download + Go to file page diff --git a/src/main/resources/templates/upload.html b/src/main/resources/templates/upload.html index 296291e..1d35762 100644 --- a/src/main/resources/templates/upload.html +++ b/src/main/resources/templates/upload.html @@ -6,7 +6,7 @@

Upload a File

-
+
@@ -24,5 +24,55 @@
+ + - \ No newline at end of file + + +