diff --git a/service/fling/src/main/java/net/friedl/fling/service/AuthorizationService.java b/service/fling/src/main/java/net/friedl/fling/service/AuthorizationService.java index 1e58c2d..8f8fb75 100644 --- a/service/fling/src/main/java/net/friedl/fling/service/AuthorizationService.java +++ b/service/fling/src/main/java/net/friedl/fling/service/AuthorizationService.java @@ -2,6 +2,7 @@ package net.friedl.fling.service; import java.util.UUID; import javax.persistence.EntityNotFoundException; +import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.stereotype.Service; @@ -13,6 +14,7 @@ import net.friedl.fling.security.authentication.FlingToken; @Slf4j @Service +@Transactional public class AuthorizationService { private FlingRepository flingRepository; @@ -32,7 +34,8 @@ public class AuthorizationService { return true; } - if (!flingRepository.getOne(flingId).getAllowUpload()) { + FlingEntity flingEntity = flingRepository.getOne(flingId); + if (flingEntity.getAllowUpload() == null || !flingEntity.getAllowUpload()) { log.debug("Fling[.id={}] does not not allow uploads"); return false; } diff --git a/web/fling/src/components/user/FlingUserList.jsx b/web/fling/src/components/user/FlingUserList.jsx index 8985435..9d213c3 100644 --- a/web/fling/src/components/user/FlingUserList.jsx +++ b/web/fling/src/components/user/FlingUserList.jsx @@ -1,380 +1,341 @@ import log from 'loglevel'; -import React, {useState, useEffect, useRef} from 'react'; +import React, { useState, useEffect, useRef } from 'react'; -import {Switch, Route, useLocation, Link} from "react-router-dom"; +import { Switch, Route, useLocation, Link } from "react-router-dom"; -import {flingClient, artifactClient} from '../../util/flingclient'; +import { FlingClient, AuthClient, ArtifactClient, fc } from '../../util/fc'; +import { prettifyTimestamp, prettifyBytes } from '../../util/fn'; import upload from '../resources/upload.svg'; import drop from '../resources/drop.svg'; function Artifacts(props) { - let [artifacts, setArtifacts] = useState([]); + let [artifacts, setArtifacts] = useState([]); - useEffect(() => { - if(!props.fling) return; + useEffect(() => { + if (!props.fling) return; - artifactClient.getArtifacts(props.fling.id) - .then((artifacts) => setArtifacts(artifacts)); - }, [props.fling]); + let flingClient = new FlingClient(); + flingClient.getArtifacts(props.fling.id) + .then(artifacts => setArtifacts(artifacts)); + }, [props.fling]); - function renderArtifact(artifact) { - function readableBytes(bytes) { - if(bytes <= 0) return "0 KB"; - - var i = Math.floor(Math.log(bytes) / Math.log(1024)), - sizes = ['Byte', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - - return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; - } - - function localizedDate(t) { - let d = new Date(t); - return d.toLocaleDateString(); - } - - return( -
-
-
-
- {artifact.name}
-
-
-
{readableBytes(artifact.size)}
-
-
-
{localizedDate(artifact.uploadTime)}
-
-
-
-
- ); - } + function renderArtifact(artifact) { return ( -
- {artifacts.map(renderArtifact)} +
+
+
+
+ {artifact.path}
+
+
+
+
+
+
{prettifyTimestamp(artifact.creationTime)}
+
+
+
); + } + + return ( +
+ {artifacts.map(renderArtifact)} +
+ ); } function Upload(props) { - let fileInputRef = useRef(null); - let [files, setFiles] = useState([]); - let [dragging, setDragging] = useState(false); - let [dragCount, setDragCount] = useState(0); + let fileInputRef = useRef(null); + let [files, setFiles] = useState([]); + let [dragging, setDragging] = useState(false); + let [dragCount, setDragCount] = useState(0); - useEffect(() => { - // prevent browser from trying to open the file when drag event - // not recognized properly - window.addEventListener("dragover",function(e){ - e.preventDefault(); - },false); - window.addEventListener("drop",function(e){ - e.preventDefault(); - },false); - }); - - function fileList() { - function readableBytes(bytes) { - if(bytes <= 0) return "0 KB"; - - var i = Math.floor(Math.log(bytes) / Math.log(1024)), - sizes = ['Byte', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - - return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; - } - - let fileList = []; - files.forEach((file,idx) => { - if(!file.uploaded) { - fileList.push( -
-
-
- deleteFile(idx)}/> -
{file.name}
-
{(new Date(file.lastModified)).toLocaleString()+", "+readableBytes(file.size)}
-
-
-
- ); - } - }); - - return fileList; - } - - function deleteFile(idx) { - let f = [...files]; - f.splice(idx, 1); - setFiles(f); - } - - function totalSize() { - function readableBytes(bytes) { - if(bytes <= 0) return "0 KB"; - - var i = Math.floor(Math.log(bytes) / Math.log(1024)), - sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - - return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; - } - - let totalSize = 0; - for(let file of files) { - totalSize += file.size; - } - - return readableBytes(totalSize); - } - - function handleClick(ev) { - fileInputRef.current.click(); - } - - function handleFileInputChange(ev) { - let fileInputFiles = fileInputRef.current.files; - if (!fileInputFiles) { - console.warn("No files selected"); - return; - } - - setFiles([...files, ...fileInputFiles]); - } - - function handleDrop(ev) { - stopEvent(ev); - ev.persist(); - - let evFiles = ev.dataTransfer.files; - - if (!evFiles) { - console.warn("Dropzone triggered without files"); - return; - } - - setFiles([...files, ...fileListToArray(evFiles)]); - setDragging(false); - setDragCount(0); - } - - function fileListToArray(fileList) { - if(fileList === undefined || fileList === null) { - return []; - } - - let arr = []; - for (let i=0; i i.name).join(',')+"]"); - } - - function setFileUploaded(idx) { - let f = [...files]; - f[idx].uploaded = true; - setFiles(f); - } - - function handleUpload() { - files.forEach((file, idx) => { - artifactClient.postArtifact(props.fling.id, file) - .then(response => { - setFileUploaded(idx); - }); - }); - } - - function zoneContent(dragging) { - if(dragging){ - return( - <> - dropzone icon -
Drop now!
- - ); - }else { - return( - <> - dropzone icon -
Click or Drop
- - ); - } - } - - return( -
- {logFiles()} -
-
-
- - - {zoneContent(dragging)} + useEffect(() => { + // prevent browser from trying to open the file when drag event + // not recognized properly + window.addEventListener("dragover", function(e) { + e.preventDefault(); + }, false); + window.addEventListener("drop", function(e) { + e.preventDefault(); + }, false); + }); + function fileList() { + let fileList = []; + files.forEach((file, idx) => { + if (!file.uploaded) { + fileList.push( +
+
+
+ deleteFile(idx)} /> +
{file.name}
+
{(new Date(file.lastModified)).toLocaleString() + ", " + prettifyBytes(file.size)}
+
+ ); + } + }); -
-
-
-
-
- {fileList()} -
-
-
-
- Total Size: {totalSize()} - + return fileList; + } + + function deleteFile(idx) { + let f = [...files]; + f.splice(idx, 1); + setFiles(f); + } + + function totalSize() { + let totalSize = 0; + for (let file of files) { + totalSize += file.size; + } + + return prettifyBytes(totalSize); + } + + function handleClick(ev) { + fileInputRef.current.click(); + } + + function handleFileInputChange(ev) { + let fileInputFiles = fileInputRef.current.files; + if (!fileInputFiles) { + console.warn("No files selected"); + return; + } + + setFiles([...files, ...fileInputFiles]); + } + + function handleDrop(ev) { + stopEvent(ev); + ev.persist(); + + let evFiles = ev.dataTransfer.files; + + if (!evFiles) { + console.warn("Dropzone triggered without files"); + return; + } + + setFiles([...files, ...fileListToArray(evFiles)]); + setDragging(false); + setDragCount(0); + } + + function fileListToArray(fileList) { + if (fileList === undefined || fileList === null) { + return []; + } + + let arr = []; + for (let i = 0; i < fileList.length; i++) { arr.push(fileList[i]); } + + return arr; + } + + function handleOnDragEnter(ev) { + stopEvent(ev); + if (dragCount === 0) setDragging(true); + + setDragCount(dragCount + 1); + } + + function handleOnDragLeave(ev) { + stopEvent(ev); + let dc = dragCount; + + dc -= 1; + setDragCount(dc); + + if (dc === 0) setDragging(false); + } + + function stopEvent(ev) { + ev.preventDefault(); + ev.stopPropagation(); + } + + function logFiles() { + log.info("Files so far: [" + files.map((i) => i.name).join(',') + "]"); + } + + function setFileUploaded(idx) { + let f = [...files]; + f[idx].uploaded = true; + setFiles(f); + } + + function handleUpload() { + const flingClient = new FlingClient(); + const artifactClient = new ArtifactClient(); + + files.forEach((file, idx) => { + let artifact = new fc.Artifact(file.name) + + flingClient.postArtifact(props.fling.id, { artifact: artifact }) + .then(artifact => { + artifactClient.uploadArtifactData(artifact.id, { body: file }); + setFileUploaded(idx); + }); + }); + } + + function zoneContent(dragging) { + if (dragging) { + return ( + <> + dropzone icon +
Drop now!
+ + ); + } else { + return ( + <> + dropzone icon +
Click or Drop
+ + ); + } + } + + return ( +
+ {logFiles()} +
+
+
+ + + {zoneContent(dragging)} + +
+
+ +
+
+
+
+
+ {fileList()}
+
+ Total Size: {totalSize()} + +
- ); +
+
+ ); } export default function FlingUserList(props) { - let location = useLocation(); + let location = useLocation(); - let iframeContainer = useRef(null); - let [infoText, setInfoText] = useState(""); - let [inProgress, setInProgress] = useState(false); + let iframeContainer = useRef(null); + let [infoText, setInfoText] = useState(""); + let [inProgress, setInProgress] = useState(false); - useEffect((flingId) => { - if(!flingId) return; + useEffect(() => { + if (!props.fling.id) return; - function readableBytes(bytes) { - if(bytes <= 0) return "0 KB"; + let flingClient = new FlingClient(); + flingClient.getArtifacts(props.fling.id) + .then((artifacts) => { + setInfoText(`${prettifyTimestamp(props.fling.creationTime)} - ${artifacts.length} files`); + }); + }, [props.fling.id, props.fling.creationTime]); - var i = Math.floor(Math.log(bytes) / Math.log(1024)), - sizes = ['Byte', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + function handleDownload(ev) { + ev.preventDefault(); - return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i]; - } + setInProgress(true); + let authClient = new AuthClient(); + authClient.deriveToken({ singleUse: true }) + .then(token => { + // 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; + setInProgress(false); + iframeContainer.current.appendChild(frame); + }); + } - function localizedDate(t) { - let d = new Date(t); - return d.toLocaleDateString(); - } - - artifactClient.getArtifacts(flingId) - .then((artifacts) => { - let totalSize = 0; - let countArtifacts = 0; - - for(let artifact of artifacts) { - totalSize += artifact.size; - countArtifacts++; - } - - setInfoText(`${localizedDate(props.fling.creationTime)} - ${countArtifacts} files - ${readableBytes(totalSize)}`); - }); - }, [props.fling.id, props.fling.creationTime]); - - function handleDownload(ev) { - ev.preventDefault(); - - setInProgress(true); - - flingClient.packageFling(props.fling.id) - .then(downloadUrl => { - // 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); - setInProgress(false); - }); + function path(tail) { + if (props.fling && props.fling.shareId) { + return `/f/${props.fling.shareId}/${tail}`; } - function path(tail) { - if(props.fling && props.fling.sharing) { - return `/f/${props.fling.sharing.shareUrl}/${tail}`; - } + return ""; + } - return ""; - } + return ( + <> +
- return( - <> -
+
-
+

{props.fling.name}

+
{infoText}
-

{props.fling.name}

-
{infoText}
+
+
    +
  • + Files +
  • + { props.fling.allowUpload + ?
  • + Upload +
  • + : <> + } -
    -
      -
    • - Files -
    • -
    • - Upload -
    • - -
    • -
      - {inProgress - ? - : - } -
      -
    • -
    - -
    - - - - - -
    -
    - -
    + : + } +
    + +
+
+ + + + +
- - ); + +
+ +
+
+ + ); } diff --git a/web/fling/src/components/user/FlingUserUpload.jsx b/web/fling/src/components/user/FlingUserUpload.jsx deleted file mode 100644 index 6f7c18a..0000000 --- a/web/fling/src/components/user/FlingUserUpload.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import log from 'loglevel'; -import React, {useState, useEffect} from 'react'; - -import {useParams, BrowserRouter} from 'react-router-dom'; - -import {flingClient} from '../../util/flingclient'; - -import DirectDownload from './DirectDownload'; - -export default function FlingUserUpload(props) { - let { shareId } = useParams(); - let [fling, setFling] = useState({}); - - useEffect(() => { - flingClient.getFlingByShareId(shareId) - .then(f => setFling(f)); - }, [shareId]); - - return( -
- {fling.sharing && fling.sharing.directDownload ? : ""} -
- ); -} -