From c808d2cc10e67ed9241c686f008acda7778f5508 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 19:56:29 +0100 Subject: [PATCH 1/8] Updated animations --- frontend/src/app/download/download.component.html | 9 ++++----- frontend/src/app/upload/upload.component.html | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/download/download.component.html b/frontend/src/app/download/download.component.html index 6ba7fc4..3a5511b 100644 --- a/frontend/src/app/download/download.component.html +++ b/frontend/src/app/download/download.component.html @@ -1,18 +1,17 @@ -
+

Download File

Access your files quickly and securely

- +
- +
-

Files are available for a limited time. Ensure the code or link is correct.

diff --git a/frontend/src/app/upload/upload.component.html b/frontend/src/app/upload/upload.component.html index f31c556..a862c5b 100644 --- a/frontend/src/app/upload/upload.component.html +++ b/frontend/src/app/upload/upload.component.html @@ -1,4 +1,4 @@ -
+

Upload Your File

From 29755a70f436c7a2e8dac17ee49ed68ea7371c8f Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 16 Feb 2024 22:16:10 +0100 Subject: [PATCH 2/8] Update download.component.ts --- frontend/src/app/download/download.component.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/app/download/download.component.ts b/frontend/src/app/download/download.component.ts index f55e6a8..e57fc18 100644 --- a/frontend/src/app/download/download.component.ts +++ b/frontend/src/app/download/download.component.ts @@ -10,10 +10,16 @@ import axios from "axios"; }) export class DownloadComponent { + fileId: string = ""; + constructor() { this.speedTest(); } + checkFile() { + console.log("Moin") + } + private speedTest() { const start = new Date().getTime(); // Start timer From f30961f1f0bb3581fa78c2ba4139f1838f25f162 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 17 Feb 2024 00:14:43 +0100 Subject: [PATCH 3/8] Added password field - Added download error modal - Added password field show logic - Redesigned UI --- .../src/app/download/download.component.html | 42 +++++++++----- .../src/app/download/download.component.ts | 57 +++++++++++++++++-- frontend/src/app/upload/upload.component.ts | 2 +- frontend/src/assets/poop-solid.svg | 1 + .../sharepulse/rest/mappings/Download.java | 3 + 5 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 frontend/src/assets/poop-solid.svg diff --git a/frontend/src/app/download/download.component.html b/frontend/src/app/download/download.component.html index 4b678f2..9f0eba2 100644 --- a/frontend/src/app/download/download.component.html +++ b/frontend/src/app/download/download.component.html @@ -3,29 +3,41 @@

Access your files quickly and securely

-
- -
- - - -
-
- +

Files are available for a limited time. Ensure the code or link is correct and the file is still downloadable.

- + diff --git a/frontend/src/app/download/download.component.ts b/frontend/src/app/download/download.component.ts index e57fc18..be619ae 100644 --- a/frontend/src/app/download/download.component.ts +++ b/frontend/src/app/download/download.component.ts @@ -1,23 +1,43 @@ -import { Component } from '@angular/core'; +import {Component, ElementRef, ViewChild} from '@angular/core'; import axios from "axios"; +import {DevelopmentStore} from "../../store/DevelopmentStore"; +import {FormsModule} from "@angular/forms"; +import {NgIf} from "@angular/common"; @Component({ selector: 'app-download', standalone: true, - imports: [], + imports: [ + FormsModule, + NgIf + ], templateUrl: './download.component.html', styleUrl: './download.component.scss' }) export class DownloadComponent { - fileId: string = ""; + @ViewChild('download_not_possible') download_not_possible: ElementRef | undefined; - constructor() { + inputFileId: string = ""; + fileId: string = ""; + filePassword: string = ""; + downloadInfo: DownloadInfo | null = null; + + + constructor(private developmentStore: DevelopmentStore) { this.speedTest(); } checkFile() { - console.log("Moin") + + console.log(this.inputFileId); + this.fileId = this.inputFileId; // TODO: Implement link extraction logic + + this.getDownloadInfo(); + } + + checkPassword() { + } private speedTest() { @@ -25,7 +45,7 @@ export class DownloadComponent { axios({ method: 'get', - url: 'http://localhost:/api/v1/speed-test', + url: this.developmentStore.getBaseUrl() + 'api/v1/speed-test', responseType: 'arraybuffer', headers: { 'Access-Control-Allow-Origin': '*', // Allow CORS @@ -44,4 +64,29 @@ export class DownloadComponent { console.error('Error during download test:', error); }); } + + private getDownloadInfo() { + axios({ + method: 'get', + url: this.developmentStore.getBaseUrl() + 'api/v1/download-info?fileId=' + this.fileId, + responseType: 'json', + headers: { + 'Access-Control-Allow-Origin': '*', // Allow CORS + } + }) + .then(response => { + this.downloadInfo = response.data; + console.log(response.data); + return response.data; + }) + .catch(error => { + console.error('Error during download info request:', error); + }); + } +} +interface DownloadInfo { + downloadable: boolean; + passwordProtected: boolean; + singleDownload: boolean; + fileId: string; } diff --git a/frontend/src/app/upload/upload.component.ts b/frontend/src/app/upload/upload.component.ts index fdc2c96..8251840 100644 --- a/frontend/src/app/upload/upload.component.ts +++ b/frontend/src/app/upload/upload.component.ts @@ -147,7 +147,7 @@ export class UploadComponent { axios({ method: 'post', - url: 'http://localhost/api/v1/upload-speed-test', + url: this.developmentStore.getBaseUrl() + 'api/v1/upload-speed-test', data: uint8View, headers: { 'Content-Type': 'application/octet-stream', diff --git a/frontend/src/assets/poop-solid.svg b/frontend/src/assets/poop-solid.svg new file mode 100644 index 0000000..e38eabb --- /dev/null +++ b/frontend/src/assets/poop-solid.svg @@ -0,0 +1 @@ + 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 b534780..392d7e5 100644 --- a/src/main/java/de/w665/sharepulse/rest/mappings/Download.java +++ b/src/main/java/de/w665/sharepulse/rest/mappings/Download.java @@ -75,10 +75,13 @@ public class Download extends ApiRestController { public ResponseEntity getDownloadInfo(@RequestParam String fileId) { FileUpload fileUpload = fileService.getFileUploadByFileId(fileId); + boolean downloadable = !fileUpload.isSingleDownload() || fileUpload.getDownloadCount() == 0; + Map response = new HashMap<>(); response.put("fileId", fileUpload.getFileId()); response.put("passwordProtected", fileUpload.isPasswordProtected()); response.put("singleDownload", fileUpload.isSingleDownload()); + response.put("downloadable", downloadable); return new ResponseEntity<>(response, HttpStatus.OK); } From 66d33e7e56a6c920ad7c173f8defd4159da9088c Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 17 Feb 2024 17:04:46 +0100 Subject: [PATCH 4/8] Refactored to optional type --- .../db/repo/FileUploadRepository.java | 18 +++++++++++++----- .../w665/sharepulse/rest/TestRestResource.java | 6 +++--- .../sharepulse/rest/mappings/Download.java | 13 +++++++++++-- .../w665/sharepulse/service/FileService.java | 3 ++- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/w665/sharepulse/db/repo/FileUploadRepository.java b/src/main/java/de/w665/sharepulse/db/repo/FileUploadRepository.java index e82c99a..24278ae 100644 --- a/src/main/java/de/w665/sharepulse/db/repo/FileUploadRepository.java +++ b/src/main/java/de/w665/sharepulse/db/repo/FileUploadRepository.java @@ -14,6 +14,8 @@ import org.springframework.stereotype.Repository; import java.lang.reflect.Type; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; @Repository @RequiredArgsConstructor @@ -44,11 +46,17 @@ public class FileUploadRepository { r.db("sharepulse").table("file_uploads").insert(map).run(connection); } - public FileUpload retrieveFileUploadByFileId(String fileId) { - return r.db("sharepulse").table("file_uploads") - .filter(r.hashMap("fileId", fileId)) - .run(connection, FileUpload.class) - .next(); + public Optional retrieveFileUploadByFileId(String fileId) { + try { + FileUpload fileUpload = r.db("sharepulse").table("file_uploads") + .filter(r.hashMap("fileId", fileId)) + .run(connection, FileUpload.class) + .next(); + return Optional.ofNullable(fileUpload); + } catch (NoSuchElementException e) { + return Optional.empty(); + } + } public void updateFileUpload(FileUpload updatedFileUpload) { diff --git a/src/main/java/de/w665/sharepulse/rest/TestRestResource.java b/src/main/java/de/w665/sharepulse/rest/TestRestResource.java index ce94c50..790c454 100644 --- a/src/main/java/de/w665/sharepulse/rest/TestRestResource.java +++ b/src/main/java/de/w665/sharepulse/rest/TestRestResource.java @@ -28,9 +28,9 @@ public class TestRestResource extends ApiRestController { } @GetMapping("test") - public String test(@RequestParam String id) { - FileUpload fileUpload = fileUploadRepository.retrieveFileUploadByFileId(id); - return fileUpload != null ? fileUpload.toString() : "FileUpload not found for id: " + id; + public FileUpload test(@RequestParam String id) { + FileUpload fileUpload = fileUploadRepository.retrieveFileUploadByFileId(id).orElse(null); + return fileUpload; } @GetMapping("test/download") 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 392d7e5..0a8c602 100644 --- a/src/main/java/de/w665/sharepulse/rest/mappings/Download.java +++ b/src/main/java/de/w665/sharepulse/rest/mappings/Download.java @@ -22,6 +22,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Slf4j @RestController @@ -41,7 +42,11 @@ public class Download extends ApiRestController { @RequestParam(value = "password", required = false) String password) throws IOException { - FileUpload fileUpload = fileService.getFileUploadByFileId(fileId); + Optional optionalFileUpload = fileService.getFileUploadByFileId(fileId); + FileUpload fileUpload = optionalFileUpload.orElse(null); + if(optionalFileUpload.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } try { fileSecurityService.verifyDownloadPermission(fileUpload, password); @@ -73,7 +78,11 @@ public class Download extends ApiRestController { @GetMapping("/download-info") public ResponseEntity getDownloadInfo(@RequestParam String fileId) { - FileUpload fileUpload = fileService.getFileUploadByFileId(fileId); + Optional optionalFileUpload = fileService.getFileUploadByFileId(fileId); + FileUpload fileUpload = optionalFileUpload.orElse(null); + if(optionalFileUpload.isEmpty()) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } boolean downloadable = !fileUpload.isSingleDownload() || fileUpload.getDownloadCount() == 0; diff --git a/src/main/java/de/w665/sharepulse/service/FileService.java b/src/main/java/de/w665/sharepulse/service/FileService.java index e3e0184..d8211d4 100644 --- a/src/main/java/de/w665/sharepulse/service/FileService.java +++ b/src/main/java/de/w665/sharepulse/service/FileService.java @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; +import java.util.Optional; @Slf4j @Service @@ -104,7 +105,7 @@ public class FileService { return new File(getTempDirPath() + File.separator + fileId); } - public FileUpload getFileUploadByFileId(String fileId) { + public Optional getFileUploadByFileId(String fileId) { return fileUploadRepository.retrieveFileUploadByFileId(fileId); } From c084af461f1c55442c917b879c1926526686e41b Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 17 Feb 2024 20:10:03 +0100 Subject: [PATCH 5/8] Updated Upload error - Added header expose - Updated download UI logic --- .../src/app/download/download.component.html | 2 +- .../src/app/download/download.component.ts | 67 +++++++++++++++++-- .../sharepulse/rest/mappings/Download.java | 1 + .../w665/sharepulse/rest/mappings/Upload.java | 8 +++ .../w665/sharepulse/service/FileService.java | 9 ++- 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/download/download.component.html b/frontend/src/app/download/download.component.html index 9f0eba2..1989abb 100644 --- a/frontend/src/app/download/download.component.html +++ b/frontend/src/app/download/download.component.html @@ -9,7 +9,7 @@ type="password" class="input input-bordered text-center w-full max-w-md mb-6" placeholder="Enter file password..." [(ngModel)]="filePassword"/> - - -

Files are available for a limited time. Ensure the code or link is correct and the file is still downloadable.

+ + +

Files are available for a limited time. Ensure the code or link is correct and the file is still downloadable.

+ + +
+
+
{{ downloadProgress | number:'1.0-0' }}%
+

Downloading...

+

Your file is being downloaded in the background. Please wait...

+

{{ downloadDuration }} seconds passed.

+
+
+
+
+ Success +

Download complete!

+

File {{ fileName }} has been downloaded and saved to your drive.

+

The download took {{ downloadDuration }} seconds.

+ + +
+ +
+
+
+

Did you know?

+

{{ funfact }}

+
+
+
+ + + diff --git a/frontend/src/app/download/download.component.ts b/frontend/src/app/download/download.component.ts index 3a2ae41..795f329 100644 --- a/frontend/src/app/download/download.component.ts +++ b/frontend/src/app/download/download.component.ts @@ -2,14 +2,18 @@ import {Component, ElementRef, ViewChild} from '@angular/core'; import axios from "axios"; import {DevelopmentStore} from "../../store/DevelopmentStore"; import {FormsModule} from "@angular/forms"; -import {NgIf} from "@angular/common"; +import {DecimalPipe, NgIf} from "@angular/common"; +import funfacts from "../../assets/funfacts"; +import {RouterLink} from "@angular/router"; @Component({ selector: 'app-download', standalone: true, imports: [ FormsModule, - NgIf + NgIf, + DecimalPipe, + RouterLink ], templateUrl: './download.component.html', styleUrl: './download.component.scss' @@ -18,15 +22,23 @@ export class DownloadComponent { @ViewChild('download_not_possible') download_not_possible: ElementRef | undefined; - inputFileId: string = ""; + inputFileId: string = "2402171"; fileId: string = ""; filePassword: string = ""; + fileName: string = ""; downloadInfo: DownloadInfo | null = null; + fileDownloadStarted: boolean = false; + fileDownloadFinished: boolean = false; waitingForPassword: boolean = false; + downloadProgress: number = 0; + downloadDuration: string = ""; + + funfact: string = ""; constructor(private developmentStore: DevelopmentStore) { + this.funfact = funfacts[Math.floor(Math.random() * funfacts.length)]; this.speedTest(); } @@ -98,6 +110,8 @@ export class DownloadComponent { } private downloadFile() { + const startTime = new Date().getTime(); + this.fileDownloadStarted = true; axios({ method: 'get', url: this.developmentStore.getBaseUrl() + 'api/v1/download?fileId=' + this.fileId, @@ -105,15 +119,24 @@ export class DownloadComponent { headers: { 'Access-Control-Allow-Origin': '*', // Allow CORS }, - onDownloadProgress: function(progressEvent) { + onDownloadProgress: (progressEvent) => { + const endTime = new Date().getTime(); + const duration = (endTime - startTime) / 1000; + this.downloadDuration = duration.toFixed(0); // 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 } } }) .then(response => { + const endTime = new Date().getTime(); + const duration = (endTime - startTime) / 1000; + this.downloadDuration = duration.toFixed(2); + this.fileDownloadFinished = true; + const contentDisposition = response.headers['content-disposition']; let filename = "default_filename"; // Default filename in case parsing fails @@ -124,6 +147,7 @@ export class DownloadComponent { filename = matches[1]; } } + this.fileName = filename; const blob = new Blob([response.data], {type: 'application/octet-stream'}); const url = window.URL.createObjectURL(blob); diff --git a/frontend/src/assets/funfacts.ts b/frontend/src/assets/funfacts.ts new file mode 100644 index 0000000..7edf729 --- /dev/null +++ b/frontend/src/assets/funfacts.ts @@ -0,0 +1,24 @@ + +let funfacts = [ + "Honey never spoils. Archaeologists have found pots of honey in ancient Egyptian tombs that are over 3,000 years old and still edible.", + "Octopuses have three hearts and blue blood.", + "Bananas are berries, but strawberries are not.", + "The shortest war in history was between Britain and Zanzibar on August 27, 1896. Zanzibar surrendered after 38 minutes.", + "A day on Venus is longer than a year on Venus.", + "The Eiffel Tower can be 15 cm taller during the summer when the iron heats up and expands.", + "Cows have best friends and can become stressed if they are separated.", + "A group of flamingos is called a flamboyance.", + "The unicorn is the national animal of Scotland.", + "More people are killed each year by cows than by sharks.", + "The total weight of all the ants on Earth is about the same as the weight of all the humans on Earth.", + "Wombat poop is cube-shaped.", + "The inventor of the frisbee was turned into a frisbee after he died.", + "There are more possible iterations of a game of chess than there are atoms in the known universe.", + "The heart of a blue whale is so large that a human can swim through the arteries.", + "Vending machines kill 4 times as many people as sharks per year.", + "Butterflies taste with their feet.", + "In Switzerland, it is illegal to own just one guinea pig because they are prone to loneliness.", + "Snails can sleep for up to three years.", + "The old Twitter bird actually had a name - Larry." +]; +export default funfacts; From 690fe5289f02ef3572bf8d79f786126af2ad57a6 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 18 Feb 2024 11:18:48 +0100 Subject: [PATCH 7/8] Implemented password verification --- .../src/app/download/download.component.html | 21 +++++++++++++---- .../src/app/download/download.component.ts | 23 +++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/download/download.component.html b/frontend/src/app/download/download.component.html index 72990e2..ba98c11 100644 --- a/frontend/src/app/download/download.component.html +++ b/frontend/src/app/download/download.component.html @@ -6,13 +6,24 @@

Access your files quickly and securely

- - + +
+ +
+ Password wrong | Check capslock +
+
- + + + + + + diff --git a/frontend/src/app/download/download.component.ts b/frontend/src/app/download/download.component.ts index e70eb89..8d889a5 100644 --- a/frontend/src/app/download/download.component.ts +++ b/frontend/src/app/download/download.component.ts @@ -22,6 +22,7 @@ import {RouterLink} from "@angular/router"; export class DownloadComponent { @ViewChild('download_not_possible') download_not_possible: ElementRef | undefined; + @ViewChild('file_not_found_modal') file_not_found_modal: ElementRef | undefined; inputFileId: string = "2402183"; fileId: string = ""; @@ -114,6 +115,7 @@ export class DownloadComponent { this.processDownloadInfo(); }) .catch(error => { + this.file_not_found_modal?.nativeElement.showModal(); console.error('Error during download info request:', error); }); }