Directory Upload & Auto UUID (#60)
This commit is contained in:
parent
f4760e4481
commit
96034cf10d
|
@ -19,6 +19,7 @@
|
||||||
"gatsby-transformer-sharp": "^2.3.17",
|
"gatsby-transformer-sharp": "^2.3.17",
|
||||||
"jsonp": "^0.2.1",
|
"jsonp": "^0.2.1",
|
||||||
"node-sass": "^4.13.1",
|
"node-sass": "^4.13.1",
|
||||||
|
"path-browserify": "^1.0.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^16.13.0",
|
"react": "^16.13.0",
|
||||||
"react-countup": "^4.3.3",
|
"react-countup": "^4.3.3",
|
||||||
|
@ -29,8 +30,7 @@
|
||||||
"react-mailchimp-subscribe": "^2.1.0",
|
"react-mailchimp-subscribe": "^2.1.0",
|
||||||
"react-reveal": "^1.2.2",
|
"react-reveal": "^1.2.2",
|
||||||
"react-syntax-highlighter": "^12.2.1",
|
"react-syntax-highlighter": "^12.2.1",
|
||||||
"react-visibility-sensor": "^5.1.1",
|
"react-visibility-sensor": "^5.1.1"
|
||||||
"shortid": "^2.2.15"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cypress": "^4.1.0",
|
"cypress": "^4.1.0",
|
||||||
|
|
|
@ -10,6 +10,11 @@ server {
|
||||||
listen [::]:443 ssl http2;
|
listen [::]:443 ssl http2;
|
||||||
server_name siasky.net www.siasky.net; # replace with actual server names
|
server_name siasky.net www.siasky.net; # replace with actual server names
|
||||||
|
|
||||||
|
# Enable the following line if you want to have auto uuid support. This
|
||||||
|
# means users are able to upload Skyfiles without having to provide a uuid
|
||||||
|
# themselves.
|
||||||
|
# rewrite ^/skynet/skyfile/?$ /skynet/skyfile/$request_id$1;
|
||||||
|
|
||||||
# NOTE: make sure to enable any additional configuration you might need like gzip
|
# NOTE: make sure to enable any additional configuration you might need like gzip
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
@ -32,10 +37,11 @@ server {
|
||||||
proxy_set_header Authorization "Basic BASE64_AUTHENTICATION";
|
proxy_set_header Authorization "Basic BASE64_AUTHENTICATION";
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ "^/([a-zA-Z0-9-_]{46})$" {
|
location ~ "^/([a-zA-Z0-9-_]{46}(/.*)?)$" {
|
||||||
proxy_read_timeout 600;
|
proxy_read_timeout 600;
|
||||||
# proxy this call to siad /skynet/skylink/ endpoint (make sure the ip is correct)
|
# proxy this call to siad /skynet/skylink/ endpoint (make sure the ip is
|
||||||
proxy_pass http://127.0.0.1:9980/skynet/skylink/$1;
|
# correct)
|
||||||
|
proxy_pass http://127.0.0.1:9980/skynet/skylink/$1$is_args$args;
|
||||||
proxy_set_header Access-Control-Allow-Origin: *;
|
proxy_set_header Access-Control-Allow-Origin: *;
|
||||||
# make sure to override user agent header - siad requirement
|
# make sure to override user agent header - siad requirement
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
@ -45,11 +51,12 @@ server {
|
||||||
#proxy_buffers 4 128k;
|
#proxy_buffers 4 128k;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ "^/file/([a-zA-Z0-9-_]{46})$" {
|
location ~ "^/file/([a-zA-Z0-9-_]{46}(/.*)?)$" {
|
||||||
proxy_read_timeout 600;
|
proxy_read_timeout 600;
|
||||||
# proxy this call to siad /skunet/skylink/ endpoint (make sure the ip is correct)
|
# proxy this call to siad /skunet/skylink/ endpoint (make sure the ip is
|
||||||
# this alias also adds attachment=true url param to force download the file
|
# correct) this alias also adds attachment=true url param to force
|
||||||
proxy_pass http://127.0.0.1:9980/skynet/skylink/$1?attachment=true;
|
# download the file
|
||||||
|
proxy_pass http://127.0.0.1:9980/skynet/skylink/$1?attachment=true&$args;
|
||||||
proxy_set_header Access-Control-Allow-Origin: *;
|
proxy_set_header Access-Control-Allow-Origin: *;
|
||||||
# make sure to override user agent header - siad requirement
|
# make sure to override user agent header - siad requirement
|
||||||
proxy_set_header User-Agent: Sia-Agent;
|
proxy_set_header User-Agent: Sia-Agent;
|
||||||
|
|
|
@ -9,7 +9,7 @@ Skynet.download_file("./dst.jpg", skylink)
|
||||||
print("Download successful")`;
|
print("Download successful")`;
|
||||||
|
|
||||||
export const curl = `# upload
|
export const curl = `# upload
|
||||||
curl -X POST "https://siasky.net/skynet/skyfile/[uuid]" -F file=@src.jpg
|
curl -X POST "https://siasky.net/skynet/skyfile" -F file=@src.jpg
|
||||||
|
|
||||||
# download
|
# download
|
||||||
curl "https://siasky.net/[skylink]" -o dst.jpg`;
|
curl "https://siasky.net/[skylink]" -o dst.jpg`;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useState, useContext } from "react";
|
import React, { useState, useContext, useEffect } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Dropzone from "react-dropzone";
|
import path from "path-browserify";
|
||||||
|
import { useDropzone } from "react-dropzone";
|
||||||
import Reveal from "react-reveal/Reveal";
|
import Reveal from "react-reveal/Reveal";
|
||||||
import shortid from "shortid";
|
|
||||||
import { Button, UploadFile } from "../";
|
import { Button, UploadFile } from "../";
|
||||||
import { Deco3, Deco4, Deco5, Folder, DownArrow } from "../../svg";
|
import { Deco3, Deco4, Deco5, Folder, DownArrow } from "../../svg";
|
||||||
import "./HomeUpload.scss";
|
import "./HomeUpload.scss";
|
||||||
|
@ -12,8 +12,47 @@ import axios from "axios";
|
||||||
export default function HomeUpload() {
|
export default function HomeUpload() {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
const { apiUrl } = useContext(AppContext);
|
const { apiUrl } = useContext(AppContext);
|
||||||
|
const [directoryMode, setDirectoryMode] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (directoryMode) {
|
||||||
|
inputRef.current.setAttribute("webkitdirectory", "true");
|
||||||
|
} else {
|
||||||
|
inputRef.current.removeAttribute("webkitdirectory");
|
||||||
|
}
|
||||||
|
}, [directoryMode]);
|
||||||
|
|
||||||
|
const getFilePath = (file) => file.webkitRelativePath || file.path || file.name;
|
||||||
|
|
||||||
|
const getRelativeFilePath = (file) => {
|
||||||
|
const filePath = getFilePath(file);
|
||||||
|
const { root, dir, base } = path.parse(filePath);
|
||||||
|
const relative = path
|
||||||
|
.normalize(dir)
|
||||||
|
.slice(root.length)
|
||||||
|
.split(path.sep)
|
||||||
|
.slice(1);
|
||||||
|
|
||||||
|
return path.join(...relative, base);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRootDirectory = (file) => {
|
||||||
|
const filePath = getFilePath(file);
|
||||||
|
const { root, dir } = path.parse(filePath);
|
||||||
|
|
||||||
|
return path
|
||||||
|
.normalize(dir)
|
||||||
|
.slice(root.length)
|
||||||
|
.split(path.sep)[0];
|
||||||
|
};
|
||||||
|
|
||||||
const handleDrop = async (acceptedFiles) => {
|
const handleDrop = async (acceptedFiles) => {
|
||||||
|
if (directoryMode && acceptedFiles.length) {
|
||||||
|
const rootDir = getRootDirectory(acceptedFiles[0]); // get the file path from the first file
|
||||||
|
|
||||||
|
acceptedFiles = [{ name: rootDir, directory: true, files: acceptedFiles }];
|
||||||
|
}
|
||||||
|
|
||||||
setFiles((previousFiles) => [...acceptedFiles.map((file) => ({ file, status: "uploading" })), ...previousFiles]);
|
setFiles((previousFiles) => [...acceptedFiles.map((file) => ({ file, status: "uploading" })), ...previousFiles]);
|
||||||
|
|
||||||
const onFileStateChange = (file, state) => {
|
const onFileStateChange = (file, state) => {
|
||||||
|
@ -31,30 +70,45 @@ export default function HomeUpload() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onProgress = (file, { loaded, total }) => {
|
const upload = async (formData, directory, file) => {
|
||||||
const progress = loaded / total;
|
const uploadUrl = `${apiUrl}/skynet/skyfile/${directory ? `?filename=${encodeURIComponent(directory)}` : ""}`;
|
||||||
const status = progress === 1 ? "processing" : "uploading";
|
const { data } = await axios.post(uploadUrl, formData, {
|
||||||
|
onUploadProgress: ({ loaded, total }) => {
|
||||||
|
const progress = loaded / total;
|
||||||
|
const status = progress === 1 ? "processing" : "uploading";
|
||||||
|
|
||||||
onFileStateChange(file, { status, progress });
|
onFileStateChange(file, { status, progress });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
acceptedFiles.forEach(async (file) => {
|
acceptedFiles.forEach(async (file) => {
|
||||||
try {
|
try {
|
||||||
const fd = new FormData();
|
const formData = new FormData();
|
||||||
fd.append("file", file);
|
|
||||||
|
|
||||||
const uuid = shortid.generate();
|
if (file.directory) {
|
||||||
const { data } = await axios.post(`${apiUrl}/skynet/skyfile/${uuid}`, fd, {
|
file.files.forEach((directoryFile) => {
|
||||||
onUploadProgress: (event) => onProgress(file, event)
|
const relativeFilePath = getRelativeFilePath(directoryFile);
|
||||||
});
|
|
||||||
|
|
||||||
onFileStateChange(file, { status: "complete", url: `${apiUrl}/${data.skylink}` });
|
formData.append("files[]", directoryFile, relativeFilePath);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
formData.append("file", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { skylink } = await upload(formData, directoryMode && file.name, file);
|
||||||
|
|
||||||
|
onFileStateChange(file, { status: "complete", url: `${apiUrl}/${skylink}` });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
onFileStateChange(file, { status: "error" });
|
onFileStateChange(file, { status: "error" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { getRootProps, getInputProps, isDragActive, inputRef } = useDropzone({ onDrop: handleDrop });
|
||||||
|
|
||||||
const handleSkylink = (event) => {
|
const handleSkylink = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
@ -71,28 +125,44 @@ export default function HomeUpload() {
|
||||||
<div className="home-upload-white fadeInUp delay4">
|
<div className="home-upload-white fadeInUp delay4">
|
||||||
<div className="home-upload-split">
|
<div className="home-upload-split">
|
||||||
<div className="home-upload-box ">
|
<div className="home-upload-box ">
|
||||||
<Dropzone onDrop={handleDrop}>
|
<div
|
||||||
{({ getRootProps, getInputProps, isDragActive }) => (
|
className={classNames("home-upload-dropzone", {
|
||||||
<>
|
"drop-active": isDragActive
|
||||||
<div
|
})}
|
||||||
className={classNames("home-upload-dropzone", {
|
{...getRootProps()}
|
||||||
"drop-active": isDragActive
|
>
|
||||||
})}
|
<span className="home-upload-text">
|
||||||
{...getRootProps()}
|
<h3>Upload your {directoryMode ? "Directory" : "Files"}</h3>
|
||||||
>
|
Drop your {directoryMode ? "directory" : "files"} here to pin to Skynet
|
||||||
<span className="home-upload-text">
|
</span>
|
||||||
<h3>Upload your Files</h3>
|
<Button iconLeft>
|
||||||
Drop your files here to pin to Skynet
|
<Folder />
|
||||||
</span>
|
Browse
|
||||||
<Button iconLeft>
|
</Button>
|
||||||
<Folder />
|
</div>
|
||||||
Browse
|
<input {...getInputProps()} className="offscreen" />
|
||||||
</Button>
|
<button
|
||||||
</div>
|
type="button"
|
||||||
<input {...getInputProps()} className="offscreen" />
|
className="home-upload-mode-switch link"
|
||||||
</>
|
onClick={() => setDirectoryMode(!directoryMode)}
|
||||||
)}
|
>
|
||||||
</Dropzone>
|
{directoryMode ? "⇐ Switch back to uploading files" : "Do you want to upload entire directory?"}
|
||||||
|
</button>
|
||||||
|
{directoryMode && (
|
||||||
|
<p className="home-upload-directory-mode-notice">
|
||||||
|
Please note that directory upload is not a standard browser feature and the browser support is
|
||||||
|
limited. To check whether your browser is compatible, visit{" "}
|
||||||
|
<a
|
||||||
|
href="https://caniuse.com/#feat=mdn-api_htmlinputelement_webkitdirectory"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="link"
|
||||||
|
>
|
||||||
|
caniuse.com
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="home-upload-retrieve">
|
<div className="home-upload-retrieve">
|
||||||
|
|
|
@ -89,6 +89,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-upload-mode-switch {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
margin: 10px auto 0;
|
||||||
|
|
||||||
|
@media (min-width: $largebp) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-upload-directory-mode-notice {
|
||||||
|
color: $lightGray;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2em;
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.home-upload-text {
|
.home-upload-text {
|
||||||
color: $gray;
|
color: $gray;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -128,6 +128,15 @@ svg {
|
||||||
color: $darkGray;
|
color: $darkGray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
color: $green;
|
||||||
|
transition: 0.2s color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.truncate {
|
.truncate {
|
||||||
width: 250px;
|
width: 250px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
17
yarn.lock
17
yarn.lock
|
@ -8492,11 +8492,6 @@ nan@^2.12.1, nan@^2.13.2, nan@^2.14.0:
|
||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||||
|
|
||||||
nanoid@^2.1.0:
|
|
||||||
version "2.1.11"
|
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
|
|
||||||
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
|
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
|
||||||
|
@ -9380,6 +9375,11 @@ path-browserify@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
|
||||||
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
|
||||||
|
|
||||||
|
path-browserify@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
|
||||||
|
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
|
||||||
|
|
||||||
path-dirname@^1.0.0:
|
path-dirname@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||||
|
@ -11333,13 +11333,6 @@ shell-quote@1.6.1:
|
||||||
array-reduce "~0.0.0"
|
array-reduce "~0.0.0"
|
||||||
jsonify "~0.0.0"
|
jsonify "~0.0.0"
|
||||||
|
|
||||||
shortid@^2.2.15:
|
|
||||||
version "2.2.15"
|
|
||||||
resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.15.tgz#2b902eaa93a69b11120373cd42a1f1fe4437c122"
|
|
||||||
integrity sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==
|
|
||||||
dependencies:
|
|
||||||
nanoid "^2.1.0"
|
|
||||||
|
|
||||||
side-channel@^1.0.2:
|
side-channel@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
|
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
|
||||||
|
|
Reference in New Issue