From 6a1e91f5d8e280e11c54b39e623681155b78e134 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 19 Feb 2024 17:24:59 +0100 Subject: [PATCH] Added unique ID generator - Updated data cleaning feature - Added fileID database logic --- .../src/app/download/download.component.ts | 15 ++++++- .../w665/sharepulse/db/RethinkDBService.java | 14 +++++- .../sharepulse/db/repo/FileIdRepository.java | 37 +++++++++++++++ .../sharepulse/rest/mappings/Download.java | 2 +- .../sharepulse/service/FileIdService.java | 45 +++++++++++++------ .../service/FileSecurityService.java | 8 ++-- src/main/resources/application.properties | 6 ++- 7 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 src/main/java/de/w665/sharepulse/db/repo/FileIdRepository.java diff --git a/frontend/src/app/download/download.component.ts b/frontend/src/app/download/download.component.ts index 1cd06e8..bc9e3b5 100644 --- a/frontend/src/app/download/download.component.ts +++ b/frontend/src/app/download/download.component.ts @@ -34,6 +34,7 @@ export class DownloadComponent { fileDownloadFinished: boolean = false; waitingForPassword: boolean = false; downloadProgress: number = 0; + targetUploadProgress: number = 0; downloadDuration: string = ""; passwordWrong: boolean = false; @@ -155,8 +156,8 @@ export class DownloadComponent { // Calculate the percentage of download completed if(progressEvent.total) { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); - this.downloadProgress = percentCompleted; - console.log(percentCompleted + '%'); // Log the percentage or update any progress UI component + this.targetUploadProgress = percentCompleted; + this.smoothProgressUpdate(); } } }) @@ -201,6 +202,16 @@ export class DownloadComponent { private wrongPassword() { this.passwordWrong = true; } + + smoothProgressUpdate() { + if (this.downloadProgress < this.targetUploadProgress) { + this.downloadProgress += 0.01 * (this.targetUploadProgress - this.downloadProgress); + requestAnimationFrame(this.smoothProgressUpdate.bind(this)); + } else if (this.downloadProgress > this.targetUploadProgress) { + // Handle overshoot + this.downloadProgress = this.targetUploadProgress; + } + } } interface DownloadInfo { downloadable: boolean; diff --git a/src/main/java/de/w665/sharepulse/db/RethinkDBService.java b/src/main/java/de/w665/sharepulse/db/RethinkDBService.java index 3e44e4e..da1a8d0 100644 --- a/src/main/java/de/w665/sharepulse/db/RethinkDBService.java +++ b/src/main/java/de/w665/sharepulse/db/RethinkDBService.java @@ -61,7 +61,12 @@ public class RethinkDBService { r.db(config.getDatabase()).tableCreate("id_store").run(connection).stream(); log.debug("Table 'id_store' created successfully."); } catch (ReqlOpFailedError e) { - log.debug("Table 'id_store' already exists. No action needed."); + log.debug("Table 'id_store' already exists."); + if(autoResetOnStartup) { + log.debug("Clearing content..."); + r.db(config.getDatabase()).table("id_store").delete().run(connection); + log.debug("Table 'id_store' cleared successfully."); + } } // rethinkdb check if table expired_file_uploads exists @@ -69,7 +74,12 @@ public class RethinkDBService { r.db(config.getDatabase()).tableCreate("expired_file_uploads").run(connection).stream(); log.debug("Table 'expired_file_uploads' created successfully."); } catch (ReqlOpFailedError e) { - log.debug("Table 'expired_file_uploads' already exists. No action needed."); + log.debug("Table 'expired_file_uploads' already exists."); + if(autoResetOnStartup) { + log.debug("Clearing content..."); + r.db(config.getDatabase()).table("expired_file_uploads").delete().run(connection); + log.debug("Table 'expired_file_uploads' cleared successfully."); + } } log.info("Database ready for operation!"); } diff --git a/src/main/java/de/w665/sharepulse/db/repo/FileIdRepository.java b/src/main/java/de/w665/sharepulse/db/repo/FileIdRepository.java new file mode 100644 index 0000000..6c0043d --- /dev/null +++ b/src/main/java/de/w665/sharepulse/db/repo/FileIdRepository.java @@ -0,0 +1,37 @@ +package de.w665.sharepulse.db.repo; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Result; +import de.w665.sharepulse.db.RethinkDBConnector; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +@Repository +public class FileIdRepository { + + private final RethinkDB r; + private final Connection connection; + private final Gson gson; + + @Autowired + public FileIdRepository(RethinkDBConnector connector) { + this.r = connector.getR(); + this.connection = connector.getConnection(); + this.gson = new GsonBuilder().create(); + } + + public void insertFileId(String fileId) { + r.db("sharepulse").table("id_store").insert(r.hashMap("fileId", fileId)).run(connection); + } + + public boolean fileIdExists(String fileId) { + Result result = r.db("sharepulse").table("id_store") + .filter(r.hashMap("fileId", fileId)) + .run(connection); + + return result.hasNext(); + } +} diff --git a/src/main/java/de/w665/sharepulse/rest/mappings/Download.java b/src/main/java/de/w665/sharepulse/rest/mappings/Download.java index 494e011..4a7a15a 100644 --- a/src/main/java/de/w665/sharepulse/rest/mappings/Download.java +++ b/src/main/java/de/w665/sharepulse/rest/mappings/Download.java @@ -82,7 +82,7 @@ public class Download extends ApiRestController { Optional optionalFileUpload = fileService.getFileUploadByFileId(fileId); FileUpload fileUpload = optionalFileUpload.orElse(null); if(optionalFileUpload.isEmpty()) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } boolean downloadable = !fileUpload.isSingleDownload() || fileUpload.getDownloadCount() == 0; diff --git a/src/main/java/de/w665/sharepulse/service/FileIdService.java b/src/main/java/de/w665/sharepulse/service/FileIdService.java index 457de4b..f7e6aa3 100644 --- a/src/main/java/de/w665/sharepulse/service/FileIdService.java +++ b/src/main/java/de/w665/sharepulse/service/FileIdService.java @@ -1,34 +1,51 @@ package de.w665.sharepulse.service; +import de.w665.sharepulse.db.repo.FileIdRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.security.SecureRandom; import java.text.SimpleDateFormat; import java.util.Date; @Service public class FileIdService { - private static int dailyCounter = 0; - private static String lastDate = ""; + @Value("${sharepulse.fileid.length}") + private int fileIdLength; - // Not safe to use - public static synchronized String generateId() { - String today = new SimpleDateFormat("yyMMdd").format(new Date()); + @Value("${sharepulse.fileid.charset}") + private String CHARSET; - if (!today.equals(lastDate)) { - dailyCounter = 0; - lastDate = today; + private final FileIdRepository fileIdRepository; + private static final SecureRandom RANDOM = new SecureRandom(); + + @Autowired + public FileIdService(FileIdRepository fileIdRepository) { + this.fileIdRepository = fileIdRepository; + } + + private String generateId() { + String uniqueId; + do { + uniqueId = generateRandomCode(fileIdLength); + } while (fileIdRepository.fileIdExists(uniqueId)); + fileIdRepository.insertFileId(uniqueId); + return uniqueId; + } + + private String generateRandomCode(int length) { + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + int index = RANDOM.nextInt(CHARSET.length()); + sb.append(CHARSET.charAt(index)); } - - String counterEncoded = Integer.toString(++dailyCounter, 36).toUpperCase(); - - return today + counterEncoded; + return sb.toString(); } public String generateNewUniqueId() { return generateId(); } - - } diff --git a/src/main/java/de/w665/sharepulse/service/FileSecurityService.java b/src/main/java/de/w665/sharepulse/service/FileSecurityService.java index 4d57dd2..cdb6157 100644 --- a/src/main/java/de/w665/sharepulse/service/FileSecurityService.java +++ b/src/main/java/de/w665/sharepulse/service/FileSecurityService.java @@ -5,17 +5,19 @@ import de.w665.sharepulse.model.FileUpload; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; + +import java.security.SecureRandom; import java.util.Random; @Slf4j @Service public class FileSecurityService { - @Value("${sharepulse.filepassword-length}") + @Value("${sharepulse.filepassword.length}") private int passwordLength; - @Value("${sharepulse.filepassword-charset}") + @Value("${sharepulse.filepassword.charset}") private String passwordCharset; - private final Random random = new Random(); + private final Random random = new SecureRandom(); public boolean verifyDownloadPermission(FileUpload file, String password) throws NoDownloadPermissionException { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 5e0484c..de1047e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,8 +1,10 @@ # Application config sharepulse.temp-filestore-path=/temp-filestore -sharepulse.filepassword-length=6 -sharepulse.filepassword-charset=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 sharepulse.auto-reset-on-startup=true +sharepulse.fileid.length=6 +sharepulse.fileid.charset=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +sharepulse.filepassword.length=6 +sharepulse.filepassword.charset=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 # Static path spring.web.resources.static-locations=classpath:/static/browser/