Use new API in DirectDownload, improve redirection
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
DirectDownload now uses the new Fling API, including one-time tokens for download. When accessing a fling, while loading the fling, instead of showing the ArtifactList an empty page is shown. This way the redirection does not leak through so visibly when using direct download and the loading of the fling takes a bit longer.
This commit is contained in:
parent
0824d8367f
commit
e9ad499850
8 changed files with 119 additions and 59 deletions
|
@ -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
|
||||
|
|
|
@ -101,11 +101,14 @@ public class FlingController {
|
|||
public ResponseEntity<Resource> 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -26,7 +26,7 @@ function FlingArtifactControl(props) {
|
|||
log.trace(`Generated download url: ${url}`);
|
||||
frame.src = url;
|
||||
iframeContainer.current.appendChild(frame);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
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 [done, setDone] = useState(false);
|
||||
let [waitingMessage, setWaitingMessage] = useState("");
|
||||
let [downloadUrl, setDownloadUrl] = useState("");
|
||||
|
||||
useEffect(handleDownload, []);
|
||||
|
||||
function handleDownload() {
|
||||
flingClient.packageFling(props.fling.id)
|
||||
.then(downloadUrl => {
|
||||
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");
|
||||
frame.src = downloadUrl;
|
||||
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);
|
||||
setDownloadUrl(downloadUrl);
|
||||
});
|
||||
}));
|
||||
|
||||
let randMsg = ["Please stay patient...",
|
||||
"Your download will be ready soon...",
|
||||
|
@ -30,6 +41,15 @@ export default function FlingUser(props) {
|
|||
setInterval(() => setWaitingMessage(randMsg[Math.floor(Math.random() * randMsg.length)]), 10000);
|
||||
}
|
||||
|
||||
function invalidateLink(ev) {
|
||||
setDone(true);
|
||||
window.location.href = downloadUrl;
|
||||
}
|
||||
|
||||
function reloadPage(ev) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="container-center">
|
||||
|
@ -39,10 +59,18 @@ export default function FlingUser(props) {
|
|||
? <><div className="loading loading-lg" />
|
||||
{waitingMessage ? waitingMessage : "Packaging up your files..."}
|
||||
</>
|
||||
: <>
|
||||
: !done
|
||||
? <>
|
||||
<h5>Your download is <span className="text-primary">ready!</span></h5>
|
||||
<i className="icon icon-check icon-2x text-primary" /><br />
|
||||
<span className="text-dark">Download doesn't start? <br/><a href={downloadUrl}>Click here</a></span>
|
||||
<span className="text-dark">Download doesn't start? <br />
|
||||
<button className="btn btn-link" onClick={invalidateLink}>Click here</button></span>
|
||||
</>
|
||||
: <>
|
||||
<h5>Thanks for <span className="text-primary">downloading!</span></h5>
|
||||
<i className="icon icon-check icon-2x text-primary" /><br />
|
||||
<span className="text-dark">Want to download again? <br />
|
||||
<button className="btn btn-link" onClick={reloadPage}>Reload page</button></span>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
{fling.sharing && fling.sharing.directDownload
|
||||
<>
|
||||
{loading
|
||||
? <div></div>
|
||||
: <div>
|
||||
{fling.shared && fling.directDownload
|
||||
? <DirectDownload fling={fling} />
|
||||
: <FlingUserList fling={fling} />}
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
Loading…
Reference in a new issue