Settings and checks for maximum artifact size #2
13 changed files with 69 additions and 12 deletions
|
@ -16,7 +16,6 @@ import net.friedl.fling.model.json.PathSerializer;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class FlingConfiguration {
|
public class FlingConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordEncoder passwordEncoder() {
|
public PasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
@ -32,6 +33,8 @@ import net.friedl.fling.service.archive.ArchiveService;
|
||||||
@SecurityRequirement(name = "bearer")
|
@SecurityRequirement(name = "bearer")
|
||||||
@Validated
|
@Validated
|
||||||
public class ArtifactController {
|
public class ArtifactController {
|
||||||
|
@Value("${fling.max-artifact-size:-1}")
|
||||||
|
private Long maxArtifactSize;
|
||||||
|
|
||||||
private ArtifactService artifactService;
|
private ArtifactService artifactService;
|
||||||
private ArchiveService archiveService;
|
private ArchiveService archiveService;
|
||||||
|
@ -58,6 +61,10 @@ public class ArtifactController {
|
||||||
@PostMapping(path = "/{id}/data")
|
@PostMapping(path = "/{id}/data")
|
||||||
public void uploadArtifactData(@PathVariable UUID id, HttpServletRequest request)
|
public void uploadArtifactData(@PathVariable UUID id, HttpServletRequest request)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
if(maxArtifactSize >= 0 && maxArtifactSize < request.getContentLengthLong()) {
|
||||||
|
throw new IOException("Maximum artifact size exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
archiveService.storeArtifact(id, request.getInputStream());
|
archiveService.storeArtifact(id, request.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,12 @@
|
||||||
"type": "java.util.Long",
|
"type": "java.util.Long",
|
||||||
"description": "Time until JWT tokens expire",
|
"description": "Time until JWT tokens expire",
|
||||||
"sourceType": "net.friedl.fling.security.FlingWebSecurityConfiguration"
|
"sourceType": "net.friedl.fling.security.FlingWebSecurityConfiguration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fling.max-artifact-size",
|
||||||
|
"type": "java.util.Long",
|
||||||
|
"description": "Maximum artifact size in bytes. -1 to disable.",
|
||||||
|
"sourceType": "net.friedl.fling.controller.ArtifactController"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@ logging.level:
|
||||||
# spring.http.log-request-details: true
|
# spring.http.log-request-details: true
|
||||||
|
|
||||||
fling:
|
fling:
|
||||||
|
max-artifact-size: 209715200 # 200 MB
|
||||||
archive.filesystem.archive-path: /home/armin/Desktop/fling
|
archive.filesystem.archive-path: /home/armin/Desktop/fling
|
||||||
security:
|
security:
|
||||||
allowed-origins:
|
allowed-origins:
|
||||||
|
|
|
@ -16,6 +16,7 @@ logging.level:
|
||||||
root: WARN
|
root: WARN
|
||||||
|
|
||||||
fling:
|
fling:
|
||||||
|
max-artifact-size: 209715200 # 200 MB
|
||||||
archive.filesystem.archive-path: "/var/fling/files"
|
archive.filesystem.archive-path: "/var/fling/files"
|
||||||
security:
|
security:
|
||||||
allowed-origins:
|
allowed-origins:
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
REACT_APP_API=https://fling.friedl.net
|
REACT_APP_API=https://fling.friedl.net
|
||||||
REACT_APP_LOGLEVEL=warn
|
REACT_APP_LOGLEVEL=warn
|
||||||
|
REACT_APP_FILESIZE=209715200
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
REACT_APP_API=http://localhost:8080/
|
REACT_APP_API=http://localhost:8080/
|
||||||
REACT_APP_LOGLEVEL=trace
|
REACT_APP_LOGLEVEL=trace
|
||||||
|
REACT_APP_FILESIZE=209715200
|
||||||
|
|
||||||
|
|
5
web/fling/package-lock.json
generated
5
web/fling/package-lock.json
generated
|
@ -13376,6 +13376,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||||
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
|
||||||
},
|
},
|
||||||
|
"vanillatoasts": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vanillatoasts/-/vanillatoasts-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-DVMPPWVVt/x1B2f4iVpCw3rYDceu1PGRV8ECc3OfUC8By6O14OdXWjKryfaz8UorBuNe8btDIsujp6YP6gDNGA=="
|
||||||
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
"react-scripts": "3.4.1",
|
"react-scripts": "3.4.1",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"spectre.css": "^0.5.8"
|
"spectre.css": "^0.5.8",
|
||||||
|
"vanillatoasts": "^1.4.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|
|
@ -100,8 +100,10 @@ export default function FlingArtifacts() {
|
||||||
.then(result => {
|
.then(result => {
|
||||||
log.debug(`Got ${result.length} artifacts`);
|
log.debug(`Got ${result.length} artifacts`);
|
||||||
for (let artifact of result) {
|
for (let artifact of result) {
|
||||||
|
if(artifact.archived) {
|
||||||
artifacts.push(<FlingArtifactRow key={artifact.id} artifact={artifact} reloadArtifactsFn={getArtifacts} />);
|
artifacts.push(<FlingArtifactRow key={artifact.id} artifact={artifact} reloadArtifactsFn={getArtifacts} />);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setArtifacts(artifacts);
|
setArtifacts(artifacts);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
import VanillaToasts from 'vanillatoasts';
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
|
@ -81,9 +82,21 @@ export default function Upload() {
|
||||||
stopEvent(ev);
|
stopEvent(ev);
|
||||||
ev.persist();
|
ev.persist();
|
||||||
|
|
||||||
let evFiles = ev.dataTransfer.files;
|
let maxSize = process.env.REACT_APP_FILESIZE;
|
||||||
|
let evFiles = fileListToArray(ev.dataTransfer.files);
|
||||||
|
|
||||||
if (!evFiles) {
|
for (let i = evFiles.length - 1; i >= 0; i--) {
|
||||||
|
if (maxSize && maxSize >= 0 && evFiles[i].size > maxSize) {
|
||||||
|
VanillaToasts.create({
|
||||||
|
title: "Maximum file size exceeded",
|
||||||
|
text: `${evFiles[i].name} exceeds the maximum file size of ${prettifyBytes(maxSize)}`,
|
||||||
|
type: "warning"
|
||||||
|
});
|
||||||
|
evFiles.splice(i, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evFiles.lenght === 0) {
|
||||||
console.warn("Dropzone triggered without files");
|
console.warn("Dropzone triggered without files");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -198,6 +211,7 @@ export default function Upload() {
|
||||||
</div>
|
</div>
|
||||||
<div className="upload-command-line m-2">
|
<div className="upload-command-line m-2">
|
||||||
<span className="total-upload">Total Size: {totalSize()}</span>
|
<span className="total-upload">Total Size: {totalSize()}</span>
|
||||||
|
<span className="total-upload">{`Max: ${prettifyBytes(process.env.REACT_APP_FILESIZE)}`}</span>
|
||||||
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
|
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
import VanillaToasts from 'vanillatoasts';
|
||||||
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";
|
||||||
|
@ -17,7 +18,10 @@ function Artifacts(props) {
|
||||||
|
|
||||||
let flingClient = new FlingClient();
|
let flingClient = new FlingClient();
|
||||||
flingClient.getArtifacts(props.fling.id)
|
flingClient.getArtifacts(props.fling.id)
|
||||||
.then(artifacts => setArtifacts(artifacts));
|
.then(artifacts => {
|
||||||
|
artifacts = artifacts.filter(a => a.archived)
|
||||||
|
setArtifacts(artifacts)
|
||||||
|
});
|
||||||
}, [props.fling]);
|
}, [props.fling]);
|
||||||
|
|
||||||
function renderArtifact(artifact) {
|
function renderArtifact(artifact) {
|
||||||
|
@ -119,9 +123,21 @@ function Upload(props) {
|
||||||
stopEvent(ev);
|
stopEvent(ev);
|
||||||
ev.persist();
|
ev.persist();
|
||||||
|
|
||||||
let evFiles = ev.dataTransfer.files;
|
let maxSize = process.env.REACT_APP_FILESIZE;
|
||||||
|
let evFiles = fileListToArray(ev.dataTransfer.files);
|
||||||
|
|
||||||
if (!evFiles) {
|
for (let i = evFiles.length - 1; i >= 0; i--) {
|
||||||
|
if (maxSize && maxSize >= 0 && evFiles[i].size > maxSize) {
|
||||||
|
VanillaToasts.create({
|
||||||
|
title: "Maximum file size exceeded",
|
||||||
|
text: `${evFiles[i].name} exceeds the maximum file size of ${prettifyBytes(maxSize)}`,
|
||||||
|
type: "warning"
|
||||||
|
});
|
||||||
|
evFiles.splice(i, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evFiles.length === 0) {
|
||||||
console.warn("Dropzone triggered without files");
|
console.warn("Dropzone triggered without files");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -236,6 +252,7 @@ function Upload(props) {
|
||||||
</div>
|
</div>
|
||||||
<div className="upload-command-line m-2">
|
<div className="upload-command-line m-2">
|
||||||
<span className="total-upload">Total Size: {totalSize()}</span>
|
<span className="total-upload">Total Size: {totalSize()}</span>
|
||||||
|
<span className="total-upload">{`Max: ${prettifyBytes(process.env.REACT_APP_FILESIZE)}`}</span>
|
||||||
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
|
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -303,7 +320,7 @@ export default function FlingUserList(props) {
|
||||||
<li className={`tab-item ${location.pathname !== path("upload") ? "active" : ""}`}>
|
<li className={`tab-item ${location.pathname !== path("upload") ? "active" : ""}`}>
|
||||||
<Link to={path("files")}>Files</Link>
|
<Link to={path("files")}>Files</Link>
|
||||||
</li>
|
</li>
|
||||||
{ props.fling.allowUpload
|
{props.fling.allowUpload
|
||||||
? <li className={`tab-item ${location.pathname === path("upload") ? "active" : ""}`}>
|
? <li className={`tab-item ${location.pathname === path("upload") ? "active" : ""}`}>
|
||||||
<Link to={path("upload")}>Upload</Link>
|
<Link to={path("upload")}>Upload</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { BrowserRouter } from "react-router-dom";
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
|
import 'vanillatoasts/vanillatoasts.css';
|
||||||
import "./style/fling.scss";
|
import "./style/fling.scss";
|
||||||
|
|
||||||
import * as serviceWorker from './serviceWorker';
|
import * as serviceWorker from './serviceWorker';
|
||||||
|
|
Loading…
Reference in a new issue