0.1 #1
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