From 4054578e6eeac80ab2bbec14bcb46aed9a1d2456 Mon Sep 17 00:00:00 2001 From: Hamadi Iddi Date: Tue, 28 Oct 2025 12:24:25 +0300 Subject: [PATCH 1/2] feat: add pagination in FileController --- .../cloudpage/controller/FileController.java | 89 ++++++++++++++++--- .../src/main/java/cloudpage/model/File.java | 28 ++++++ .../cloudpage/repository/FileRepository.java | 21 +++++ .../src/main/resources/application.properties | 2 +- 4 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 backend/src/main/java/cloudpage/model/File.java create mode 100644 backend/src/main/java/cloudpage/repository/FileRepository.java diff --git a/backend/src/main/java/cloudpage/controller/FileController.java b/backend/src/main/java/cloudpage/controller/FileController.java index e92638e..6eaa8c8 100644 --- a/backend/src/main/java/cloudpage/controller/FileController.java +++ b/backend/src/main/java/cloudpage/controller/FileController.java @@ -1,11 +1,19 @@ package cloudpage.controller; +import cloudpage.dto.FileDto; +import cloudpage.model.File; +import cloudpage.repository.FileRepository; import cloudpage.service.FileService; import cloudpage.service.FolderService; import cloudpage.service.UserService; import lombok.RequiredArgsConstructor; + import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -15,6 +23,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; @RestController @RequiredArgsConstructor @@ -24,6 +34,7 @@ public class FileController { private final FileService fileService; private final UserService userService; private final FolderService folderService; + private final FileRepository fileRepository; @PostMapping("/upload") public void uploadFile(@RequestParam String folderPath, @RequestParam MultipartFile file) throws IOException { @@ -32,7 +43,29 @@ public void uploadFile(@RequestParam String folderPath, @RequestParam MultipartF } @GetMapping("/content") - public ResponseEntity getFileContent(@RequestParam String path) throws IOException { + public ResponseEntity getFileContent( + @RequestParam(required = false) String path, + @RequestParam(defaultValue = "0") int pageNumber, + @RequestParam(defaultValue = "10") int pageSize) throws IOException { + + // If path is null or empty, treat as listing directory/files + if (path == null || path.isEmpty()) { + Pageable pageable = PageRequest.of(pageNumber, pageSize, Sort.by("fileId").descending()); + Page filePage = fileRepository.findAllFiles(pageable); + + Map response = new HashMap<>(); + response.put("message", "File(s) retrieved successfully"); + response.put("status", "success"); + response.put("data", filePage.getContent()); + response.put("totalFiles", filePage.getTotalElements()); + response.put("totalPages", filePage.getTotalPages()); + response.put("currentPage", filePage.getNumber()); + response.put("code", 200); + + return ResponseEntity.ok(response); + } + + // Otherwise, treat as a request for a single file's content var user = userService.getCurrentUser(); Path fullPath = Paths.get(user.getRootFolderPath(), path).normalize(); folderService.validatePath(user.getRootFolderPath(), fullPath); @@ -42,8 +75,14 @@ public ResponseEntity getFileContent(@RequestParam String path) throws I } String content = fileService.readFileContent(user.getRootFolderPath(), path); - return ResponseEntity.ok(content); - } + return ResponseEntity.ok(Map.of( + "message", "File content retrieved successfully", + "status", "success", + "data", content, + "code", 200 + )); +} + @DeleteMapping public void deleteFile(@RequestParam String filePath) throws IOException { @@ -57,16 +96,40 @@ public void renameOrMoveFile(@RequestParam String filePath, @RequestParam String fileService.renameOrMoveFile(user.getRootFolderPath(), filePath, newPath); } - @GetMapping("/download") - public ResponseEntity downloadFile(@RequestParam String path) throws IOException { - var user = userService.getCurrentUser(); - Path fullPath = Paths.get(user.getRootFolderPath(), path).normalize(); - folderService.validatePath(user.getRootFolderPath(), fullPath); // ensure security - Resource resource = new UrlResource(fullPath.toUri()); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fullPath.getFileName() + "\"") - .body(resource); - } + @GetMapping("/download") + public ResponseEntity listFilesForDownload( + @RequestParam(required = false) String path, + @RequestParam(defaultValue = "0") int pageNumber, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String sort) { + + Pageable pageable; + if (sort != null && !sort.isEmpty()) { + pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sort)); + } else { + pageable = PageRequest.of(pageNumber, pageSize, Sort.by("fileId").descending()); + } + + Page filePage; + + if (path == null || path.isEmpty()) { + filePage = fileRepository.findAllFiles(pageable); + } else { + filePage = fileRepository.searchAllFiles(pageable, path.toUpperCase()); + } + + Map response = new HashMap<>(); + response.put("message", "File(s) available for download"); + response.put("status", "success"); + response.put("data", filePage.getContent()); + response.put("totalFiles", filePage.getTotalElements()); + response.put("totalPages", filePage.getTotalPages()); + response.put("currentPage", filePage.getNumber()); + response.put("code", 200); + + return ResponseEntity.ok(response); +} + @GetMapping("/view") public ResponseEntity viewFile(@RequestParam String path) throws IOException { diff --git a/backend/src/main/java/cloudpage/model/File.java b/backend/src/main/java/cloudpage/model/File.java new file mode 100644 index 0000000..9cc1662 --- /dev/null +++ b/backend/src/main/java/cloudpage/model/File.java @@ -0,0 +1,28 @@ +package cloudpage.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@Entity +@AllArgsConstructor +@Table(name = "files") +public class File { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String path; + private Long size; + private String mimeType; +} + + + diff --git a/backend/src/main/java/cloudpage/repository/FileRepository.java b/backend/src/main/java/cloudpage/repository/FileRepository.java new file mode 100644 index 0000000..44687c1 --- /dev/null +++ b/backend/src/main/java/cloudpage/repository/FileRepository.java @@ -0,0 +1,21 @@ +package cloudpage.repository; + +import org.hibernate.cache.spi.support.AbstractReadWriteAccess.Item; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import cloudpage.model.File; + +@Repository +public interface FileRepository extends JpaRepository{ + + @Query("select f from File f") + Page findAllFiles(Pageable pageable); + + @Query("select fi from File fi where fi.name like %:path%") + Page searchAllFiles(Pageable pageable, String path); + +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 877e157..c08806b 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -2,7 +2,7 @@ spring.application.name=backend server.port=8090 # PostgreSQL DB Connection spring.datasource.username=postgres -spring.datasource.password=root +spring.datasource.password=12345 spring.datasource.url=jdbc:postgresql://localhost:5432/cloud-db spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true From 524bc3c1693658d05a8ff871b526419717b562ca Mon Sep 17 00:00:00 2001 From: Hamadi Iddi Date: Tue, 28 Oct 2025 12:35:24 +0300 Subject: [PATCH 2/2] fix: adjust pagination logic in FileController --- .../cloudpage/controller/FileController.java | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/backend/src/main/java/cloudpage/controller/FileController.java b/backend/src/main/java/cloudpage/controller/FileController.java index 6eaa8c8..e1ce47a 100644 --- a/backend/src/main/java/cloudpage/controller/FileController.java +++ b/backend/src/main/java/cloudpage/controller/FileController.java @@ -131,24 +131,38 @@ public ResponseEntity listFilesForDownload( } - @GetMapping("/view") - public ResponseEntity viewFile(@RequestParam String path) throws IOException { - var user = userService.getCurrentUser(); - Path fullPath = Paths.get(user.getRootFolderPath(), path).normalize(); - folderService.validatePath(user.getRootFolderPath(), fullPath); + @GetMapping("/view") + public ResponseEntity listFilesForView( + @RequestParam(required = false) String path, + @RequestParam(defaultValue = "0") int pageNumber, + @RequestParam(defaultValue = "10") int pageSize, + @RequestParam(required = false) String sort) { - if (!fullPath.toFile().exists() || !fullPath.toFile().isFile()) { - throw new IllegalArgumentException("File does not exist: " + fullPath); + Pageable pageable; + if (sort != null && !sort.isEmpty()) { + pageable = PageRequest.of(pageNumber, pageSize, Sort.by(sort)); + } else { + pageable = PageRequest.of(pageNumber, pageSize, Sort.by("fileId").descending()); } - Resource resource = new UrlResource(fullPath.toUri()); - String mimeType = Files.probeContentType(fullPath); - if (mimeType == null) { - mimeType = "application/octet-stream"; + Page filePage; + + if (path == null || path.isEmpty()) { + filePage = fileRepository.findAllFiles(pageable); + } else { + filePage = fileRepository.searchAllFiles(pageable, path.toUpperCase()); } - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_TYPE, mimeType) - .body(resource); + Map response = new HashMap<>(); + response.put("message", "File(s) available for viewing"); + response.put("status", "success"); + response.put("data", filePage.getContent()); + response.put("totalFiles", filePage.getTotalElements()); + response.put("totalPages", filePage.getTotalPages()); + response.put("currentPage", filePage.getNumber()); + response.put("code", 200); + + return ResponseEntity.ok(response); } + } \ No newline at end of file