From 6b322825cdf4cc2300462dad4df6e6f0e83cfc73 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 14:41:29 +0100 Subject: [PATCH 1/4] Added sample details UI to upload --- frontend/src/app/upload/upload.component.html | 53 +++++++++++++++++-- frontend/src/app/upload/upload.component.ts | 6 ++- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/upload/upload.component.html b/frontend/src/app/upload/upload.component.html index f3616be..9053a93 100644 --- a/frontend/src/app/upload/upload.component.html +++ b/frontend/src/app/upload/upload.component.html @@ -1,5 +1,5 @@
-

Upload Your File

+

Upload Your File

@@ -52,11 +52,54 @@

Uploading {{ fileToUpload.name }}...

-
- -

File uploaded successfully!

- +
+
+ Success +
+ +

File uploaded successfully!

+ +
+
+
+
File ID:
+
166554
+ +
Material:
+
Aluminum & Plastic
+ +
Color:
+
Black/Silver
+ +
Dimensions:
+
15 x 15 x 25 cm
+ +
Weight:
+
1.2 kg
+ +
Power:
+
10W LED (included)
+ +
Brightness:
+
700 lumens
+ +
Warranty:
+
2 years
+
+
+
+ + + +

Your file is now securely stored on our servers.

+ +
+ + +
+ Manage your uploads
+
diff --git a/frontend/src/app/upload/upload.component.ts b/frontend/src/app/upload/upload.component.ts index 316b99e..b4e9d00 100644 --- a/frontend/src/app/upload/upload.component.ts +++ b/frontend/src/app/upload/upload.component.ts @@ -1,5 +1,5 @@ import {Component, ElementRef, ViewChild} from '@angular/core'; -import {DecimalPipe, NgIf} from "@angular/common"; +import {DecimalPipe, NgClass, NgIf} from "@angular/common"; import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe"; import {FormsModule} from "@angular/forms"; import axios, {AxiosProgressEvent} from "axios"; @@ -12,7 +12,8 @@ import { DevelopmentStore } from '../../store/DevelopmentStore'; NgIf, FormatFileSizePipePipe, FormsModule, - DecimalPipe + DecimalPipe, + NgClass ], templateUrl: './upload.component.html', styleUrl: './upload.component.scss' @@ -96,6 +97,7 @@ export class UploadComponent { axios.post(this.developmentStore.getBaseUrl() + 'api/v1/upload', formData, config) .then(response => { console.log('Upload completed successfully:', response.data); + console.log(response.data); this.uploadFinished = true; }) .catch(error => { From 43e0eb1766b5339dc3573ed92debea2ad548433a Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 17:27:06 +0100 Subject: [PATCH 2/4] Added file details display - Added password generator logic to backend - Added UI for file details --- frontend/src/app/upload/upload.component.html | 54 +++++++++++-------- frontend/src/app/upload/upload.component.ts | 29 +++++++++- .../w665/sharepulse/rest/mappings/Upload.java | 6 ++- .../service/FileSecurityService.java | 22 +++++++- .../w665/sharepulse/service/FileService.java | 11 ++-- src/main/resources/application.properties | 10 ++-- 6 files changed, 95 insertions(+), 37 deletions(-) diff --git a/frontend/src/app/upload/upload.component.html b/frontend/src/app/upload/upload.component.html index 9053a93..9b09151 100644 --- a/frontend/src/app/upload/upload.component.html +++ b/frontend/src/app/upload/upload.component.html @@ -59,43 +59,51 @@

File uploaded successfully!

-
+
+
File Name:
+
{{uploadData.fileName}}
+ +
Is password protected:
+ + +
Is single download:
+ + +
Download Password:
+
{{uploadData.password || 'N/A'}}
+
File ID:
-
166554
+
{{uploadData.fileId}}
-
Material:
-
Aluminum & Plastic
+ +
Download URL:
+ -
Color:
-
Black/Silver
- -
Dimensions:
-
15 x 15 x 25 cm
- -
Weight:
-
1.2 kg
- -
Power:
-
10W LED (included)
- -
Brightness:
-
700 lumens
- -
Warranty:
-
2 years
+
File Status URL:
+
+

Your file is now securely stored on our servers.

- - + + +
Manage your uploads
diff --git a/frontend/src/app/upload/upload.component.ts b/frontend/src/app/upload/upload.component.ts index 7419cee..82568e4 100644 --- a/frontend/src/app/upload/upload.component.ts +++ b/frontend/src/app/upload/upload.component.ts @@ -27,7 +27,6 @@ export class UploadComponent { singleDownload: boolean = false; fileDescription: string = ''; passwordProtected: boolean = false; - password: string = ''; // Generated by the server for only this file uploadStarted: boolean = false; uploadProgress = 0; // Real progress @@ -35,6 +34,8 @@ export class UploadComponent { uploadFinished = false; uploadSpeedBps: number = 0; + uploadData: FileDetails | null = null; + fileUrls: { downloadUrl: string, statusUrl: string, deleteUrl: string } | null = null; constructor(private developmentStore: DevelopmentStore) { @@ -98,6 +99,8 @@ export class UploadComponent { .then(response => { console.log('Upload completed successfully!'); console.log(response.data); + this.fileUrls = this.buildFileUrls(response.data); + this.uploadData = response.data; this.uploadFinished = true; }) .catch(error => { @@ -106,13 +109,26 @@ export class UploadComponent { } } + buildFileUrls(fileDetails: FileDetails) { + const baseUrl = this.developmentStore.getBaseUrl(); + const fileId = fileDetails.fileId; + const downloadUrl = `${baseUrl}download?fileId=${fileId}`; + const deleteUrl = `${baseUrl}api/v1/deletefile?fileId=${fileId}`; + const statusUrl = `${baseUrl}status?fileId=${fileId}`; + return { + downloadUrl, + statusUrl, + deleteUrl, + }; } + buildFormDataObject(): FormData { const formData = new FormData(); formData.append('file', this.fileToUpload as Blob); - formData.append('password', this.passwordProtected ? this.password : ''); + formData.append('passwordProtected', this.passwordProtected ? 'true' : 'false'); formData.append('singleDownload', this.singleDownload.toString()); formData.append('shortStorage', this.shortStorage.toString()); formData.append('fileDescription', this.fileDescription); + console.log(formData.get("passwordProtected")); return formData; } @@ -169,3 +185,12 @@ export class UploadComponent { } } } +interface FileDetails { + fileId: string; + fileName: string; + message: string; + passwordProtected: boolean; + singleDownload: boolean; + uploadDate: string; + password: string; +} diff --git a/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java b/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java index 3515dfb..803c3be 100644 --- a/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java +++ b/src/main/java/de/w665/sharepulse/rest/mappings/Upload.java @@ -29,7 +29,7 @@ public class Upload extends ApiRestController { // Currently testing @PostMapping("/upload") public ResponseEntity getUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request, - @RequestParam(value = "password", required = false) String password, + @RequestParam(value = "passwordProtected", required = false) Boolean passwordProtected, @RequestParam(value = "singleDownload", defaultValue = "false") boolean singleDownload, @RequestParam(value = "fileDescription", required = false) String fileDescription) { @@ -40,15 +40,17 @@ public class Upload extends ApiRestController { return new ResponseEntity<>("Please select a file to upload.", HttpStatus.NOT_ACCEPTABLE); } - FileUpload fileUpload = fileService.processUploadedFile(file, request.getRemoteAddr(), password, singleDownload, fileDescription); + FileUpload fileUpload = fileService.processUploadedFile(file, request.getRemoteAddr(), passwordProtected, singleDownload, fileDescription); log.debug("User uploaded file " + file.getOriginalFilename() + " from IP " + request.getRemoteAddr() + " successfully."); JsonObject response = new JsonObject(); response.addProperty("fileId", fileUpload.getFileId()); + response.addProperty("fileName", fileUpload.getFileName()); response.addProperty("message", "File " + file.getOriginalFilename() + " uploaded successfully!"); response.addProperty("passwordProtected", fileUpload.isPasswordProtected()); response.addProperty("singleDownload", fileUpload.isSingleDownload()); response.addProperty("uploadDate", fileUpload.getUploadDate().toString()); + response.addProperty("password", fileUpload.getDownloadPassword()); return new ResponseEntity<>(response.toString(), HttpStatus.OK); } diff --git a/src/main/java/de/w665/sharepulse/service/FileSecurityService.java b/src/main/java/de/w665/sharepulse/service/FileSecurityService.java index f55903e..4d57dd2 100644 --- a/src/main/java/de/w665/sharepulse/service/FileSecurityService.java +++ b/src/main/java/de/w665/sharepulse/service/FileSecurityService.java @@ -3,14 +3,20 @@ package de.w665.sharepulse.service; import de.w665.sharepulse.exception.NoDownloadPermissionException; 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.io.File; +import java.util.Random; @Slf4j @Service public class FileSecurityService { + @Value("${sharepulse.filepassword-length}") + private int passwordLength; + @Value("${sharepulse.filepassword-charset}") + private String passwordCharset; + private final Random random = new Random(); + public boolean verifyDownloadPermission(FileUpload file, String password) throws NoDownloadPermissionException { @@ -34,4 +40,16 @@ public class FileSecurityService { throw new NoDownloadPermissionException("Password protected file can only be downloaded with correct password."); } + + public String generateFilePassword() { + StringBuilder password = new StringBuilder(passwordLength); + + for (int i = 0; i < passwordLength; i++) { + int randomIndex = random.nextInt(passwordCharset.length()); + char randomChar = passwordCharset.charAt(randomIndex); + password.append(randomChar); + } + + return password.toString(); + } } diff --git a/src/main/java/de/w665/sharepulse/service/FileService.java b/src/main/java/de/w665/sharepulse/service/FileService.java index 044a629..7196442 100644 --- a/src/main/java/de/w665/sharepulse/service/FileService.java +++ b/src/main/java/de/w665/sharepulse/service/FileService.java @@ -23,15 +23,16 @@ import java.util.Date; public class FileService { private final FileIdService fileIdService; - + private final FileSecurityService fileSecurityService; private final FileUploadRepository fileUploadRepository; @Value("${sharepulse.temp-filestore-path}") private String tempDirPath; @Autowired - public FileService(FileIdService fileIdService, FileUploadRepository fileUploadRepository) { + public FileService(FileIdService fileIdService, FileSecurityService fileSecurityService, FileUploadRepository fileUploadRepository) { this.fileIdService = fileIdService; + this.fileSecurityService = fileSecurityService; this.fileUploadRepository = fileUploadRepository; } @@ -53,10 +54,12 @@ public class FileService { } } - public FileUpload processUploadedFile(MultipartFile file, String uploaderIp, String password, boolean singleDownload, String fileDescription) { + public FileUpload processUploadedFile(MultipartFile file, String uploaderIp, boolean passwordProtected, boolean singleDownload, String fileDescription) { String fileId = fileIdService.generateNewUniqueId(); + String password = fileSecurityService.generateFilePassword(); // TODO: generate password + FileUpload fileUpload = FileUpload.builder() .fileId(fileId) .fileName(file.getOriginalFilename()) @@ -66,7 +69,7 @@ public class FileService { .uploadedByIpAddress(uploaderIp) .downloadCount(0) .fileDescription(fileDescription) - .passwordProtected(password != null && !password.isEmpty()) + .passwordProtected(passwordProtected) .downloadPassword(password) .build(); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ce36a92..99e3d04 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,7 @@ # Application config sharepulse.temp-filestore-path=/temp-filestore +sharepulse.filepassword-length=6 +sharepulse.filepassword-charset=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 # Static path spring.web.resources.static-locations=classpath:/static/browser/ @@ -11,9 +13,6 @@ spring.data.rest.base-path=/api/v1 spring.servlet.multipart.max-file-size=1GB spring.servlet.multipart.max-request-size=1GB -# Logging -logging.level.de.w665.sharepulse=DEBUG - # Database rethinkdb.host=localhost rethinkdb.port=28015 @@ -24,4 +23,7 @@ server.port=80 spring.application.name=sharepulse # Spring profiles (Options: development, production) (Controls cors) -spring.profiles.active=development \ No newline at end of file +spring.profiles.active=development + +# Logging +logging.level.de.w665.sharepulse=DEBUG \ No newline at end of file From b607424d8aef9533e54dc1afda1eeef3c0780def Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 17:55:20 +0100 Subject: [PATCH 3/4] Update upload.component.html - Updated mobile design --- frontend/src/app/upload/upload.component.html | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/upload/upload.component.html b/frontend/src/app/upload/upload.component.html index 9b09151..335478d 100644 --- a/frontend/src/app/upload/upload.component.html +++ b/frontend/src/app/upload/upload.component.html @@ -1,6 +1,6 @@

Upload Your File

-
+
Uploading {{ fileToUpload.name }}...

-
+
Success
@@ -61,35 +61,31 @@
-
-
File Name:
+
+
File Name:
{{uploadData.fileName}}
-
Is password protected:
+
Is password protected:
-
Is single download:
+
Is single download:
- -
Download Password:
+
Download Password:
{{uploadData.password || 'N/A'}}
-
File ID:
+
File ID:
{{uploadData.fileId}}
- -
Download URL:
+
Download URL:
-
File Status URL:
+
File Status URL:
@@ -98,6 +94,7 @@ +

Your file is now securely stored on our servers.

@@ -105,7 +102,6 @@
- Manage your uploads
From cd9a800bf0718ef0c996f9df24bc7dd7b85f93d0 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 18:32:59 +0100 Subject: [PATCH 4/4] Finished upload logic --- frontend/src/app/upload/upload.component.html | 10 +++++++--- frontend/src/app/upload/upload.component.ts | 16 +++++++++++++++- frontend/src/assets/check-solid.svg | 1 + .../de/w665/sharepulse/service/FileService.java | 5 ++++- 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 frontend/src/assets/check-solid.svg diff --git a/frontend/src/app/upload/upload.component.html b/frontend/src/app/upload/upload.component.html index 335478d..f31c556 100644 --- a/frontend/src/app/upload/upload.component.html +++ b/frontend/src/app/upload/upload.component.html @@ -98,9 +98,12 @@

Your file is now securely stored on our servers.

- - - + + +
@@ -109,5 +112,6 @@ diff --git a/frontend/src/app/upload/upload.component.ts b/frontend/src/app/upload/upload.component.ts index 82568e4..fdc2c96 100644 --- a/frontend/src/app/upload/upload.component.ts +++ b/frontend/src/app/upload/upload.component.ts @@ -4,6 +4,7 @@ import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe"; import {FormsModule} from "@angular/forms"; import axios, {AxiosProgressEvent} from "axios"; import { DevelopmentStore } from '../../store/DevelopmentStore'; +import {RouterLink} from "@angular/router"; @Component({ selector: 'app-upload', @@ -13,7 +14,8 @@ import { DevelopmentStore } from '../../store/DevelopmentStore'; FormatFileSizePipePipe, FormsModule, DecimalPipe, - NgClass + NgClass, + RouterLink ], templateUrl: './upload.component.html', styleUrl: './upload.component.scss' @@ -36,6 +38,7 @@ export class UploadComponent { uploadData: FileDetails | null = null; fileUrls: { downloadUrl: string, statusUrl: string, deleteUrl: string } | null = null; + urlCopied: boolean = false; constructor(private developmentStore: DevelopmentStore) { @@ -184,6 +187,17 @@ export class UploadComponent { this.uploadProgress = this.targetUploadProgress; } } + + copyUrlToClipboard(url: string | undefined) { + if(url) { + navigator.clipboard.writeText(url).then(() => { + this.urlCopied = true; + console.log('Text successfully copied to clipboard'); + }).catch(err => { + console.error('Failed to copy text to clipboard', err); + }); + } + } } interface FileDetails { fileId: string; diff --git a/frontend/src/assets/check-solid.svg b/frontend/src/assets/check-solid.svg new file mode 100644 index 0000000..10da547 --- /dev/null +++ b/frontend/src/assets/check-solid.svg @@ -0,0 +1 @@ + diff --git a/src/main/java/de/w665/sharepulse/service/FileService.java b/src/main/java/de/w665/sharepulse/service/FileService.java index 7196442..57207e2 100644 --- a/src/main/java/de/w665/sharepulse/service/FileService.java +++ b/src/main/java/de/w665/sharepulse/service/FileService.java @@ -58,7 +58,10 @@ public class FileService { String fileId = fileIdService.generateNewUniqueId(); - String password = fileSecurityService.generateFilePassword(); // TODO: generate password + String password = ""; + if (passwordProtected) { + password = fileSecurityService.generateFilePassword(); + } FileUpload fileUpload = FileUpload.builder() .fileId(fileId)