diff --git a/examples/querysheet.http b/examples/querysheet.http index 5e08215..5995cff 100644 --- a/examples/querysheet.http +++ b/examples/querysheet.http @@ -16,13 +16,30 @@ Content-Type: application/json {"adminName": "admin", "adminPassword":"123"} -> run-hook (restclient-set-var ":token" (buffer-substring-no-properties 1 (line-end-position))) -:token = Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTUxNzY4OTksImV4cCI6MTU5NTM1Njg5OSwic3ViIjoiYWRtaW4ifQ.uRh_xBCrBiLQEBah9I8bYWM-Zph-V_pzQVdaGSU5Mlc +:token = Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1OTU2NzUxMjAsImV4cCI6MTU5NTg1NTEyMCwic3ViIjoiYWRtaW4ifQ.WzrGTTZTYHYOw8SskHQ2_sob2tzLIF6q8y8_2oyuafs # Get all flings GET http://localhost:8080/api/fling Content-Type: application/json :token +:flingid = 9f7353a3-efaa-41af-9f93-61e02dc5e440 + +# Put a fling +PUT http://localhost:8080/api/fling/:flingid +Content-Type: application/json +:token + { + "id": "9f7353a3-efaa-41af-9f93-61e02dc5e440", + "name": "Shared Fling from querysheetsdfasfd", + "creationTime": 1595253659362, + "shareId": "WWgTlNZJPZDQ6oowUYfxcQqq", + "directDownload": false, + "allowUpload": false, + "shared": true, + "expirationClicks": 12 + } + # Add a new fling POST http://localhost:8080/api/fling Content-Type: application/json diff --git a/service/fling/src/main/java/net/friedl/fling/controller/FlingController.java b/service/fling/src/main/java/net/friedl/fling/controller/FlingController.java index db4dddc..584b248 100644 --- a/service/fling/src/main/java/net/friedl/fling/controller/FlingController.java +++ b/service/fling/src/main/java/net/friedl/fling/controller/FlingController.java @@ -101,11 +101,14 @@ public class FlingController { public ResponseEntity getFlingData(@PathVariable UUID id) throws IOException { FlingDto flingDto = flingService.getById(id); InputStreamResource data = new InputStreamResource(archiveService.getFling(id)); + Long length = data.contentLength(); + + data = new InputStreamResource(archiveService.getFling(id)); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=\"" + flingDto.getName() + ".zip" + "\"") - .contentLength(200L) // FIXME + .contentLength(length) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(data); } diff --git a/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java b/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java index 2617716..9849d3b 100644 --- a/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java +++ b/service/fling/src/main/java/net/friedl/fling/service/AuthenticationService.java @@ -22,7 +22,6 @@ import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; import net.friedl.fling.model.dto.AdminAuthDto; -import net.friedl.fling.model.dto.FlingDto; import net.friedl.fling.model.dto.UserAuthDto; import net.friedl.fling.persistence.entities.FlingEntity; import net.friedl.fling.persistence.entities.TokenEntity; diff --git a/service/fling/src/test/java/net/friedl/fling/controller/FlingControllerTest.java b/service/fling/src/test/java/net/friedl/fling/controller/FlingControllerTest.java index 22c95c6..22340ba 100644 --- a/service/fling/src/test/java/net/friedl/fling/controller/FlingControllerTest.java +++ b/service/fling/src/test/java/net/friedl/fling/controller/FlingControllerTest.java @@ -122,7 +122,7 @@ public class FlingControllerTest { @Test public void replaceFling_ok() throws Exception { FlingDto flingDto = new FlingDto(flingId, "new-name", Instant.EPOCH, "shareId", "new-authCode", - false, true, true, 1, null); + false, true, true, 1, null); mockMvc.perform(put("/api/fling/{id}", flingId) .content(mapper.writeValueAsString(flingDto)) @@ -275,7 +275,11 @@ public class FlingControllerTest { byte[] testZip = new byte[testZipInt.length]; for (int idx = 0; idx < testZip.length; idx++) testZip[idx] = (byte) testZipInt[idx]; - when(archiveService.getFling(any())).thenReturn(new ByteArrayInputStream(testZip)); + when(archiveService.getFling(any())) + .thenAnswer((invocation) -> { + // need to use thenAnswer here to always return a fresh new (unclosed) input stream + return new ByteArrayInputStream(testZip); + }); mockMvc.perform(get("/api/fling/{id}/data", flingId)) .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM)) diff --git a/web/fling/src/components/admin/FlingArtifacts.jsx b/web/fling/src/components/admin/FlingArtifacts.jsx index ebe2684..0e71e5b 100644 --- a/web/fling/src/components/admin/FlingArtifacts.jsx +++ b/web/fling/src/components/admin/FlingArtifacts.jsx @@ -26,7 +26,7 @@ function FlingArtifactControl(props) { log.trace(`Generated download url: ${url}`); frame.src = url; iframeContainer.current.appendChild(frame); - }) + }); } return ( diff --git a/web/fling/src/components/user/DirectDownload.jsx b/web/fling/src/components/user/DirectDownload.jsx index 1055018..9981a38 100644 --- a/web/fling/src/components/user/DirectDownload.jsx +++ b/web/fling/src/components/user/DirectDownload.jsx @@ -1,54 +1,82 @@ -import React, {useRef, useState, useEffect} from 'react'; +import log from 'loglevel'; +import React, { useRef, useState, useEffect } from 'react'; -import {flingClient} from '../../util/flingclient'; +import { AuthClient } from '../../util/fc'; export default function FlingUser(props) { - let iframeContainer = useRef(null); - let [packaging, setPackaging] = useState(true); - let [waitingMessage, setWaitingMessage] = useState(""); - let [downloadUrl, setDownloadUrl] = useState(""); + let iframeContainer = useRef(null); + let [packaging, setPackaging] = useState(true); + let [done, setDone] = useState(false); + let [waitingMessage, setWaitingMessage] = useState(""); + let [downloadUrl, setDownloadUrl] = useState(""); - useEffect(handleDownload, []); + useEffect(handleDownload, []); - function handleDownload() { - flingClient.packageFling(props.fling.id) - .then(downloadUrl => { - setPackaging(false); - // We need this iframe hack because with a regular href, while - // the browser downloads the file fine, it also reloads the page, hence - // loosing all logs and state - let frame = document.createElement("iframe"); - frame.src = downloadUrl; - iframeContainer.current.appendChild(frame); - setDownloadUrl(downloadUrl); - }); + function handleDownload() { + let authClient = new AuthClient(); + authClient.deriveToken({ singleUse: true }) + .then(token => { + let url = `${process.env.REACT_APP_API.replace(/\/+$/, '')}/api/fling/${props.fling.id}/data?derivedToken=${token}`; + log.trace(`Generated download url for link: ${url}`); + setDownloadUrl(url); + }) + .then( + authClient.deriveToken({ singleUse: true }) + .then(token => { + setPackaging(false); + // We need this iframe hack because with a regular href, while + // the browser downloads the file fine, it also reloads the page, hence + // loosing all logs and state + let frame = document.createElement("iframe"); + let url = `${process.env.REACT_APP_API.replace(/\/+$/, '')}/api/fling/${props.fling.id}/data?derivedToken=${token}`; + log.trace(`Generated download url: ${url}`); + frame.src = url; + iframeContainer.current.appendChild(frame); + })); - let randMsg = ["Please stay patient...", - "Your download will be ready soon...", - "Packaging up your files...", - "Almost there..."]; - setInterval(() => setWaitingMessage(randMsg[Math.floor(Math.random() * randMsg.length)]), 10000); - } + let randMsg = ["Please stay patient...", + "Your download will be ready soon...", + "Packaging up your files...", + "Almost there..."]; + setInterval(() => setWaitingMessage(randMsg[Math.floor(Math.random() * randMsg.length)]), 10000); + } - return( -
-
-
-
- {packaging - ? <>
- {waitingMessage ? waitingMessage: "Packaging up your files..."} - - : <> -
Your download is ready!
-
- Download doesn't start?
Click here
- - } -
-
+ function invalidateLink(ev) { + setDone(true); + window.location.href = downloadUrl; + } + + function reloadPage(ev) { + window.location.reload(); + } + + return ( +
+
+
+
+ {packaging + ? <>
+ {waitingMessage ? waitingMessage : "Packaging up your files..."} + + : !done + ? <> +
Your download is ready!
+
+ Download doesn't start?
+
+ + : <> +
Thanks for downloading!
+
+ Want to download again?
+
+ + }
-
- ); +
+
+
+ ); } diff --git a/web/fling/src/components/user/FlingUser.jsx b/web/fling/src/components/user/FlingUser.jsx index b76a37f..96bf1dd 100644 --- a/web/fling/src/components/user/FlingUser.jsx +++ b/web/fling/src/components/user/FlingUser.jsx @@ -10,18 +10,27 @@ import FlingUserList from './FlingUserList'; export default function FlingUser() { let { shareId } = useParams(); let [fling, setFling] = useState({}); + let [loading, setLoading] = useState(true); useEffect(() => { let flingClient = new FlingClient(); flingClient.getFlingByShareId(shareId) - .then(f => setFling(f)); + .then(f => { + setFling(f); + setLoading(false); + }); }, [shareId]); return ( -
- {fling.sharing && fling.sharing.directDownload - ? - : } -
+ <> + {loading + ?
+ :
+ {fling.shared && fling.directDownload + ? + : } +
+ } + ); } diff --git a/web/fling/src/components/user/Unlock.jsx b/web/fling/src/components/user/Unlock.jsx index 799be63..b30bfe0 100644 --- a/web/fling/src/components/user/Unlock.jsx +++ b/web/fling/src/components/user/Unlock.jsx @@ -12,7 +12,7 @@ export default function Unlock() { useEffect(() => { let authClient = new AuthClient(); - let userAuth = new fc.UserAuth(location.state.shareId, "") + let userAuth = new fc.UserAuth(location.state.shareId, ""); authClient.authenticateUser({ 'userAuth': userAuth }) .then(response => { @@ -20,7 +20,7 @@ export default function Unlock() { sessionStorage.setItem('token', response); history.replace(location.state.from); }).catch(error => { - log.info("Fling protected. Could not unlock without code.") + log.info("Fling protected. Could not unlock without code."); }); }, [location, history]); @@ -46,7 +46,7 @@ export default function Unlock() { function handleSubmit(ev) { ev.preventDefault(); let authClient = new AuthClient(); - let userAuth = new fc.UserAuth(shareId, authCode) + let userAuth = new fc.UserAuth(shareId, authCode); authClient.authenticateUser({ 'userAuth': userAuth }) .then(response => {