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"}
|
{"adminName": "admin", "adminPassword":"123"}
|
||||||
-> run-hook (restclient-set-var ":token" (buffer-substring-no-properties 1 (line-end-position)))
|
-> 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 all flings
|
||||||
GET http://localhost:8080/api/fling
|
GET http://localhost:8080/api/fling
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
:token
|
: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
|
# Add a new fling
|
||||||
POST http://localhost:8080/api/fling
|
POST http://localhost:8080/api/fling
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
|
|
|
@ -101,11 +101,14 @@ public class FlingController {
|
||||||
public ResponseEntity<Resource> getFlingData(@PathVariable UUID id) throws IOException {
|
public ResponseEntity<Resource> getFlingData(@PathVariable UUID id) throws IOException {
|
||||||
FlingDto flingDto = flingService.getById(id);
|
FlingDto flingDto = flingService.getById(id);
|
||||||
InputStreamResource data = new InputStreamResource(archiveService.getFling(id));
|
InputStreamResource data = new InputStreamResource(archiveService.getFling(id));
|
||||||
|
Long length = data.contentLength();
|
||||||
|
|
||||||
|
data = new InputStreamResource(archiveService.getFling(id));
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||||
"attachment;filename=\"" + flingDto.getName() + ".zip" + "\"")
|
"attachment;filename=\"" + flingDto.getName() + ".zip" + "\"")
|
||||||
.contentLength(200L) // FIXME
|
.contentLength(length)
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.body(data);
|
.body(data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import io.jsonwebtoken.JwtParser;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.friedl.fling.model.dto.AdminAuthDto;
|
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.model.dto.UserAuthDto;
|
||||||
import net.friedl.fling.persistence.entities.FlingEntity;
|
import net.friedl.fling.persistence.entities.FlingEntity;
|
||||||
import net.friedl.fling.persistence.entities.TokenEntity;
|
import net.friedl.fling.persistence.entities.TokenEntity;
|
||||||
|
|
|
@ -122,7 +122,7 @@ public class FlingControllerTest {
|
||||||
@Test
|
@Test
|
||||||
public void replaceFling_ok() throws Exception {
|
public void replaceFling_ok() throws Exception {
|
||||||
FlingDto flingDto = new FlingDto(flingId, "new-name", Instant.EPOCH, "shareId", "new-authCode",
|
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)
|
mockMvc.perform(put("/api/fling/{id}", flingId)
|
||||||
.content(mapper.writeValueAsString(flingDto))
|
.content(mapper.writeValueAsString(flingDto))
|
||||||
|
@ -275,7 +275,11 @@ public class FlingControllerTest {
|
||||||
byte[] testZip = new byte[testZipInt.length];
|
byte[] testZip = new byte[testZipInt.length];
|
||||||
for (int idx = 0; idx < testZip.length; idx++) testZip[idx] = (byte) testZipInt[idx];
|
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))
|
mockMvc.perform(get("/api/fling/{id}/data", flingId))
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||||
|
|
|
@ -26,7 +26,7 @@ function FlingArtifactControl(props) {
|
||||||
log.trace(`Generated download url: ${url}`);
|
log.trace(`Generated download url: ${url}`);
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
iframeContainer.current.appendChild(frame);
|
iframeContainer.current.appendChild(frame);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -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) {
|
export default function FlingUser(props) {
|
||||||
let iframeContainer = useRef(null);
|
let iframeContainer = useRef(null);
|
||||||
let [packaging, setPackaging] = useState(true);
|
let [packaging, setPackaging] = useState(true);
|
||||||
let [waitingMessage, setWaitingMessage] = useState("");
|
let [done, setDone] = useState(false);
|
||||||
let [downloadUrl, setDownloadUrl] = useState("");
|
let [waitingMessage, setWaitingMessage] = useState("");
|
||||||
|
let [downloadUrl, setDownloadUrl] = useState("");
|
||||||
|
|
||||||
useEffect(handleDownload, []);
|
useEffect(handleDownload, []);
|
||||||
|
|
||||||
function handleDownload() {
|
function handleDownload() {
|
||||||
flingClient.packageFling(props.fling.id)
|
let authClient = new AuthClient();
|
||||||
.then(downloadUrl => {
|
authClient.deriveToken({ singleUse: true })
|
||||||
setPackaging(false);
|
.then(token => {
|
||||||
// We need this iframe hack because with a regular href, while
|
let url = `${process.env.REACT_APP_API.replace(/\/+$/, '')}/api/fling/${props.fling.id}/data?derivedToken=${token}`;
|
||||||
// the browser downloads the file fine, it also reloads the page, hence
|
log.trace(`Generated download url for link: ${url}`);
|
||||||
// loosing all logs and state
|
setDownloadUrl(url);
|
||||||
let frame = document.createElement("iframe");
|
})
|
||||||
frame.src = downloadUrl;
|
.then(
|
||||||
iframeContainer.current.appendChild(frame);
|
authClient.deriveToken({ singleUse: true })
|
||||||
setDownloadUrl(downloadUrl);
|
.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...",
|
let randMsg = ["Please stay patient...",
|
||||||
"Your download will be ready soon...",
|
"Your download will be ready soon...",
|
||||||
"Packaging up your files...",
|
"Packaging up your files...",
|
||||||
"Almost there..."];
|
"Almost there..."];
|
||||||
setInterval(() => setWaitingMessage(randMsg[Math.floor(Math.random() * randMsg.length)]), 10000);
|
setInterval(() => setWaitingMessage(randMsg[Math.floor(Math.random() * randMsg.length)]), 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return(
|
function invalidateLink(ev) {
|
||||||
<div>
|
setDone(true);
|
||||||
<div className="container-center">
|
window.location.href = downloadUrl;
|
||||||
<div className="card direct-download-card">
|
}
|
||||||
<div className="card-body ">
|
|
||||||
{packaging
|
function reloadPage(ev) {
|
||||||
? <><div className="loading loading-lg" />
|
window.location.reload();
|
||||||
{waitingMessage ? waitingMessage: "Packaging up your files..."}
|
}
|
||||||
</>
|
|
||||||
: <>
|
return (
|
||||||
<h5>Your download is <span className="text-primary">ready!</span></h5>
|
<div>
|
||||||
<i className="icon icon-check icon-2x text-primary" /><br/>
|
<div className="container-center">
|
||||||
<span className="text-dark">Download doesn't start? <br/><a href={downloadUrl}>Click here</a></span>
|
<div className="card direct-download-card">
|
||||||
</>
|
<div className="card-body ">
|
||||||
}
|
{packaging
|
||||||
</div>
|
? <><div className="loading loading-lg" />
|
||||||
</div>
|
{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 />
|
||||||
|
<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>
|
</div>
|
||||||
<div className="d-hide" ref={iframeContainer} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
</div>
|
||||||
|
<div className="d-hide" ref={iframeContainer} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,18 +10,27 @@ import FlingUserList from './FlingUserList';
|
||||||
export default function FlingUser() {
|
export default function FlingUser() {
|
||||||
let { shareId } = useParams();
|
let { shareId } = useParams();
|
||||||
let [fling, setFling] = useState({});
|
let [fling, setFling] = useState({});
|
||||||
|
let [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let flingClient = new FlingClient();
|
let flingClient = new FlingClient();
|
||||||
flingClient.getFlingByShareId(shareId)
|
flingClient.getFlingByShareId(shareId)
|
||||||
.then(f => setFling(f));
|
.then(f => {
|
||||||
|
setFling(f);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
}, [shareId]);
|
}, [shareId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
{fling.sharing && fling.sharing.directDownload
|
{loading
|
||||||
? <DirectDownload fling={fling} />
|
? <div></div>
|
||||||
: <FlingUserList fling={fling} />}
|
: <div>
|
||||||
</div>
|
{fling.shared && fling.directDownload
|
||||||
|
? <DirectDownload fling={fling} />
|
||||||
|
: <FlingUserList fling={fling} />}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export default function Unlock() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let authClient = new AuthClient();
|
let authClient = new AuthClient();
|
||||||
let userAuth = new fc.UserAuth(location.state.shareId, "")
|
let userAuth = new fc.UserAuth(location.state.shareId, "");
|
||||||
|
|
||||||
authClient.authenticateUser({ 'userAuth': userAuth })
|
authClient.authenticateUser({ 'userAuth': userAuth })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
@ -20,7 +20,7 @@ export default function Unlock() {
|
||||||
sessionStorage.setItem('token', response);
|
sessionStorage.setItem('token', response);
|
||||||
history.replace(location.state.from);
|
history.replace(location.state.from);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
log.info("Fling protected. Could not unlock without code.")
|
log.info("Fling protected. Could not unlock without code.");
|
||||||
});
|
});
|
||||||
}, [location, history]);
|
}, [location, history]);
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export default function Unlock() {
|
||||||
function handleSubmit(ev) {
|
function handleSubmit(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
let authClient = new AuthClient();
|
let authClient = new AuthClient();
|
||||||
let userAuth = new fc.UserAuth(shareId, authCode)
|
let userAuth = new fc.UserAuth(shareId, authCode);
|
||||||
|
|
||||||
authClient.authenticateUser({ 'userAuth': userAuth })
|
authClient.authenticateUser({ 'userAuth': userAuth })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
Loading…
Reference in a new issue