diff --git a/cmd/root.go b/cmd/root.go
index 4674d168..dc8b57e8 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -334,9 +334,13 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
},
AuthMethod: "",
Branding: settings.Branding{},
- Commands: nil,
- Shell: nil,
- Rules: nil,
+ Tus: settings.Tus{
+ ChunkSize: settings.DefaultTusChunkSize,
+ RetryCount: settings.DefaultTusRetryCount,
+ },
+ Commands: nil,
+ Shell: nil,
+ Rules: nil,
}
var err error
diff --git a/files/file.go b/files/file.go
index 1daf384b..a077b062 100644
--- a/files/file.go
+++ b/files/file.go
@@ -23,6 +23,8 @@ import (
"github.com/filebrowser/filebrowser/v2/rules"
)
+const PERM = 0664
+
// FileInfo describes a file.
type FileInfo struct {
*Listing
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 79db8633..295a369e 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -21,6 +21,7 @@
"noty": "^3.2.0-beta",
"pretty-bytes": "^6.0.0",
"qrcode.vue": "^1.7.0",
+ "tus-js-client": "^3.1.0",
"utif": "^3.1.0",
"vue": "^2.6.10",
"vue-async-computed": "^3.9.0",
@@ -3315,10 +3316,9 @@
}
},
"node_modules/buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
- "dev": true
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/buffer-indexof": {
"version": "1.1.1",
@@ -4088,6 +4088,15 @@
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
"dev": true
},
+ "node_modules/combine-errors": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz",
+ "integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==",
+ "dependencies": {
+ "custom-error-instance": "2.1.1",
+ "lodash.uniqby": "4.5.0"
+ }
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -5042,6 +5051,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/custom-error-instance": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz",
+ "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg=="
+ },
"node_modules/cyclist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -7065,8 +7079,7 @@
"node_modules/graceful-fs": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
- "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
- "dev": true
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
},
"node_modules/gzip-size": {
"version": "5.1.1",
@@ -8847,6 +8860,46 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash._baseiteratee": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz",
+ "integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==",
+ "dependencies": {
+ "lodash._stringtopath": "~4.8.0"
+ }
+ },
+ "node_modules/lodash._basetostring": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz",
+ "integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw=="
+ },
+ "node_modules/lodash._baseuniq": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
+ "integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==",
+ "dependencies": {
+ "lodash._createset": "~4.0.0",
+ "lodash._root": "~3.0.0"
+ }
+ },
+ "node_modules/lodash._createset": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
+ "integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA=="
+ },
+ "node_modules/lodash._root": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
+ "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ=="
+ },
+ "node_modules/lodash._stringtopath": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz",
+ "integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==",
+ "dependencies": {
+ "lodash._basetostring": "~4.12.0"
+ }
+ },
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@@ -8899,6 +8952,15 @@
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
+ "node_modules/lodash.uniqby": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz",
+ "integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==",
+ "dependencies": {
+ "lodash._baseiteratee": "~4.7.0",
+ "lodash._baseuniq": "~4.6.0"
+ }
+ },
"node_modules/log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -11181,6 +11243,16 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
+ "node_modules/proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -11333,8 +11405,7 @@
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
- "dev": true
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/randombytes": {
"version": "2.1.0",
@@ -11681,8 +11752,7 @@
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
- "dev": true
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"node_modules/resolve": {
"version": "1.20.0",
@@ -11751,7 +11821,6 @@
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
- "dev": true,
"engines": {
"node": ">= 4"
}
@@ -12132,8 +12201,7 @@
"node_modules/signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
- "dev": true
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
@@ -13382,6 +13450,36 @@
"node": "*"
}
},
+ "node_modules/tus-js-client": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-3.1.0.tgz",
+ "integrity": "sha512-Hfpc8ho4C9Lhs/OflPUA/nHUHZJUrKD5upoPBq7dYJJ9DQhWocsjJU2RZYfN16Y5n19j9dFDszwCvVZ5sfcogw==",
+ "dependencies": {
+ "buffer-from": "^1.1.2",
+ "combine-errors": "^3.0.3",
+ "is-stream": "^2.0.0",
+ "js-base64": "^3.7.2",
+ "lodash.throttle": "^4.1.1",
+ "proper-lockfile": "^4.1.2",
+ "url-parse": "^1.5.7"
+ }
+ },
+ "node_modules/tus-js-client/node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/tus-js-client/node_modules/js-base64": {
+ "version": "3.7.5",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz",
+ "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
+ },
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
@@ -13709,7 +13807,6 @@
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dev": true,
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -18142,10 +18239,9 @@
}
},
"buffer-from": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
- "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
- "dev": true
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"buffer-indexof": {
"version": "1.1.1",
@@ -18771,6 +18867,15 @@
"integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
"dev": true
},
+ "combine-errors": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz",
+ "integrity": "sha512-C8ikRNRMygCwaTx+Ek3Yr+OuZzgZjduCOfSQBjbM8V3MfgcjSTeto/GXP6PAwKvJz/v15b7GHZvx5rOlczFw/Q==",
+ "requires": {
+ "custom-error-instance": "2.1.1",
+ "lodash.uniqby": "4.5.0"
+ }
+ },
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -19531,6 +19636,11 @@
}
}
},
+ "custom-error-instance": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz",
+ "integrity": "sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg=="
+ },
"cyclist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -21146,8 +21256,7 @@
"graceful-fs": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
- "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
- "dev": true
+ "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ=="
},
"gzip-size": {
"version": "5.1.1",
@@ -22521,6 +22630,46 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "lodash._baseiteratee": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz",
+ "integrity": "sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==",
+ "requires": {
+ "lodash._stringtopath": "~4.8.0"
+ }
+ },
+ "lodash._basetostring": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz",
+ "integrity": "sha512-SwcRIbyxnN6CFEEK4K1y+zuApvWdpQdBHM/swxP962s8HIxPO3alBH5t3m/dl+f4CMUug6sJb7Pww8d13/9WSw=="
+ },
+ "lodash._baseuniq": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
+ "integrity": "sha512-Ja1YevpHZctlI5beLA7oc5KNDhGcPixFhcqSiORHNsp/1QTv7amAXzw+gu4YOvErqVlMVyIJGgtzeepCnnur0A==",
+ "requires": {
+ "lodash._createset": "~4.0.0",
+ "lodash._root": "~3.0.0"
+ }
+ },
+ "lodash._createset": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
+ "integrity": "sha512-GTkC6YMprrJZCYU3zcqZj+jkXkrXzq3IPBcF/fIPpNEAB4hZEtXU8zp/RwKOvZl43NUmwDbyRk3+ZTbeRdEBXA=="
+ },
+ "lodash._root": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
+ "integrity": "sha512-O0pWuFSK6x4EXhM1dhZ8gchNtG7JMqBtrHdoUFUWXD7dJnNSUze1GuyQr5sOs0aCvgGeI3o/OJW8f4ca7FDxmQ=="
+ },
+ "lodash._stringtopath": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz",
+ "integrity": "sha512-SXL66C731p0xPDC5LZg4wI5H+dJo/EO4KTqOMwLYCH3+FmmfAKJEZCm6ohGpI+T1xwsDsJCfL4OnhorllvlTPQ==",
+ "requires": {
+ "lodash._basetostring": "~4.12.0"
+ }
+ },
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@@ -22573,6 +22722,15 @@
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
+ "lodash.uniqby": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz",
+ "integrity": "sha512-IRt7cfTtHy6f1aRVA5n7kT8rgN3N1nH6MOWLcHfpWG2SH19E3JksLK38MktLxZDhlAjCP9jpIXkOnRXlu6oByQ==",
+ "requires": {
+ "lodash._baseiteratee": "~4.7.0",
+ "lodash._baseuniq": "~4.6.0"
+ }
+ },
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -24460,6 +24618,16 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
+ "proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@@ -24592,8 +24760,7 @@
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
- "dev": true
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"randombytes": {
"version": "2.1.0",
@@ -24879,8 +25046,7 @@
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
- "dev": true
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"resolve": {
"version": "1.20.0",
@@ -24932,8 +25098,7 @@
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
- "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
- "dev": true
+ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
},
"rgb-regex": {
"version": "1.0.1",
@@ -25264,8 +25429,7 @@
"signal-exit": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
- "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
- "dev": true
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
},
"simple-swizzle": {
"version": "0.2.2",
@@ -26306,6 +26470,32 @@
"safe-buffer": "^5.0.1"
}
},
+ "tus-js-client": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-3.1.0.tgz",
+ "integrity": "sha512-Hfpc8ho4C9Lhs/OflPUA/nHUHZJUrKD5upoPBq7dYJJ9DQhWocsjJU2RZYfN16Y5n19j9dFDszwCvVZ5sfcogw==",
+ "requires": {
+ "buffer-from": "^1.1.2",
+ "combine-errors": "^3.0.3",
+ "is-stream": "^2.0.0",
+ "js-base64": "^3.7.2",
+ "lodash.throttle": "^4.1.1",
+ "proper-lockfile": "^4.1.2",
+ "url-parse": "^1.5.7"
+ },
+ "dependencies": {
+ "is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+ },
+ "js-base64": {
+ "version": "3.7.5",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz",
+ "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
+ }
+ }
+ },
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
@@ -26575,7 +26765,6 @@
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dev": true,
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
diff --git a/frontend/package.json b/frontend/package.json
index 33ab2ff5..1573f407 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -23,6 +23,7 @@
"noty": "^3.2.0-beta",
"pretty-bytes": "^6.0.0",
"qrcode.vue": "^1.7.0",
+ "tus-js-client": "^3.1.0",
"utif": "^3.1.0",
"vue": "^2.6.10",
"vue-async-computed": "^3.9.0",
diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js
index a91ba4b2..fbe15a73 100644
--- a/frontend/src/api/files.js
+++ b/frontend/src/api/files.js
@@ -1,6 +1,7 @@
import { createURL, fetchURL, removePrefix } from "./utils";
import { baseURL } from "@/utils/constants";
import store from "@/store";
+import { upload as postTus, useTus } from "./tus";
export async function fetch(url) {
url = removePrefix(url);
@@ -78,6 +79,22 @@ export function download(format, ...files) {
}
export async function post(url, content = "", overwrite = false, onupload) {
+ // Use the pre-existing API if:
+ const useResourcesApi =
+ // a folder is being created
+ url.endsWith("/") ||
+ // We're not using http(s)
+ (content instanceof Blob &&
+ !["http:", "https:"].includes(window.location.protocol)) ||
+ // Tus is disabled / not applicable
+ !(await useTus(content));
+
+ return useResourcesApi
+ ? postResources(url, content, overwrite, onupload)
+ : postTus(url, content, overwrite, onupload);
+}
+
+async function postResources(url, content = "", overwrite = false, onupload) {
url = removePrefix(url);
let bufferContent;
diff --git a/frontend/src/api/tus.js b/frontend/src/api/tus.js
new file mode 100644
index 00000000..47e443b4
--- /dev/null
+++ b/frontend/src/api/tus.js
@@ -0,0 +1,85 @@
+import * as tus from "tus-js-client";
+import { tusEndpoint, tusSettings } from "@/utils/constants";
+import store from "@/store";
+import { removePrefix } from "@/api/utils";
+import { fetchURL } from "./utils";
+
+const RETRY_BASE_DELAY = 1000;
+const RETRY_MAX_DELAY = 20000;
+
+export async function upload(url, content = "", overwrite = false, onupload) {
+ if (!tusSettings) {
+ // Shouldn't happen as we check for tus support before calling this function
+ throw new Error("Tus.io settings are not defined");
+ }
+
+ url = removePrefix(url);
+ let resourceUrl = `${tusEndpoint}${url}?override=${overwrite}`;
+
+ await createUpload(resourceUrl);
+
+ return new Promise((resolve, reject) => {
+ let upload = new tus.Upload(content, {
+ uploadUrl: resourceUrl,
+ chunkSize: tusSettings.chunkSize,
+ retryDelays: computeRetryDelays(tusSettings),
+ parallelUploads: 1,
+ storeFingerprintForResuming: false,
+ headers: {
+ "X-Auth": store.state.jwt,
+ },
+ onError: function (error) {
+ reject("Upload failed: " + error);
+ },
+ onProgress: function (bytesUploaded) {
+ // Emulate ProgressEvent.loaded which is used by calling functions
+ // loaded is specified in bytes (https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/loaded)
+ if (typeof onupload === "function") {
+ onupload({ loaded: bytesUploaded });
+ }
+ },
+ onSuccess: function () {
+ resolve();
+ },
+ });
+ upload.start();
+ });
+}
+
+async function createUpload(resourceUrl) {
+ let headResp = await fetchURL(resourceUrl, {
+ method: "POST",
+ });
+ if (headResp.status !== 201) {
+ throw new Error(
+ `Failed to create an upload: ${headResp.status} ${headResp.statusText}`
+ );
+ }
+}
+
+function computeRetryDelays(tusSettings) {
+ if (!tusSettings.retryCount || tusSettings.retryCount < 1) {
+ // Disable retries altogether
+ return null;
+ }
+ // The tus client expects our retries as an array with computed backoffs
+ // E.g.: [0, 3000, 5000, 10000, 20000]
+ const retryDelays = [];
+ let delay = 0;
+
+ for (let i = 0; i < tusSettings.retryCount; i++) {
+ retryDelays.push(Math.min(delay, RETRY_MAX_DELAY));
+ delay =
+ delay === 0 ? RETRY_BASE_DELAY : Math.min(delay * 2, RETRY_MAX_DELAY);
+ }
+
+ return retryDelays;
+}
+
+export async function useTus(content) {
+ return isTusSupported() && content instanceof Blob;
+}
+
+function isTusSupported() {
+ return tus.isSupported === true;
+}
diff --git a/frontend/src/components/prompts/Replace.vue b/frontend/src/components/prompts/Replace.vue
index 82ede1d6..a525bbbc 100644
--- a/frontend/src/components/prompts/Replace.vue
+++ b/frontend/src/components/prompts/Replace.vue
@@ -17,6 +17,14 @@
>
{{ $t("buttons.cancel") }}
+
+
+ {{ $t("settings.tusUploads") }}
+
+ {{ $t("settings.tusUploadsHelp") }}
+
+
@@ -210,11 +241,30 @@ export default {
error: null,
originalSettings: null,
settings: null,
+ debounceTimeout: null,
};
},
computed: {
...mapState(["user", "loading"]),
isExecEnabled: () => enableExec,
+ formattedChunkSize: {
+ get() {
+ return this.formatBytes(this.settings.tus.chunkSize);
+ },
+ set(value) {
+ // Use debouncing to allow the user to type freely without
+ // interruption by the formatter
+ // Clear the previous timeout if it exists
+ if (this.debounceTimeout) {
+ clearTimeout(this.debounceTimeout);
+ }
+
+ // Set a new timeout to apply the format after a short delay
+ this.debounceTimeout = setTimeout(() => {
+ this.settings.tus.chunkSize = this.parseBytes(value);
+ }, 1500);
+ },
+ },
},
async created() {
try {
@@ -275,6 +325,44 @@ export default {
this.$showError(e);
}
},
+ // Parse the user-friendly input (e.g., "20M" or "1T") to bytes
+ parseBytes(input) {
+ const regex = /^(\d+)(\.\d+)?(B|K|KB|M|MB|G|GB|T|TB)?$/i;
+ const matches = input.match(regex);
+ if (matches) {
+ const size = parseFloat(matches[1].concat(matches[2] || ""));
+ let unit = matches[3].toUpperCase();
+ if (!unit.endsWith("B")) {
+ unit += "B";
+ }
+ const units = {
+ KB: 1024,
+ MB: 1024 ** 2,
+ GB: 1024 ** 3,
+ TB: 1024 ** 4,
+ };
+ return size * (units[unit] || 1);
+ } else {
+ return 1024 ** 2;
+ }
+ },
+ // Format the chunk size in bytes to user-friendly format
+ formatBytes(bytes) {
+ const units = ["B", "KB", "MB", "GB", "TB"];
+ let size = bytes;
+ let unitIndex = 0;
+ while (size >= 1024 && unitIndex < units.length - 1) {
+ size /= 1024;
+ unitIndex++;
+ }
+ return `${size}${units[unitIndex]}`;
+ },
+ // Clear the debounce timeout when the component is destroyed
+ beforeDestroy() {
+ if (this.debounceTimeout) {
+ clearTimeout(this.debounceTimeout);
+ }
+ },
},
};
diff --git a/go.mod b/go.mod
index 4ee2a1f6..0b59895c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/filebrowser/filebrowser/v2
-go 1.18
+go 1.20
require (
github.com/asdine/storm/v3 v3.2.1
@@ -20,18 +20,18 @@ require (
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.15.0
- github.com/stretchr/testify v1.8.1
+ github.com/stretchr/testify v1.8.4
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
go.etcd.io/bbolt v1.3.7
- golang.org/x/crypto v0.6.0
+ golang.org/x/crypto v0.10.0
golang.org/x/image v0.5.0
- golang.org/x/text v0.7.0
+ golang.org/x/text v0.10.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
)
require (
- github.com/andybalholm/brotli v1.0.1 // indirect
+ github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d // indirect
@@ -40,15 +40,17 @@ require (
github.com/go-errors/errors v1.1.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
- github.com/golang/snappy v0.0.2 // indirect
+ github.com/golang/protobuf v1.5.3 // indirect
+ github.com/golang/snappy v0.0.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
- github.com/klauspost/compress v1.11.4 // indirect
+ github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
+ github.com/kr/pretty v0.3.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
- github.com/pierrec/lz4/v4 v4.1.2 // indirect
+ github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/spf13/cast v1.5.0 // indirect
@@ -57,8 +59,10 @@ require (
github.com/ulikunitz/xz v0.5.9 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
- golang.org/x/net v0.7.0 // indirect
- golang.org/x/sys v0.5.0 // indirect
+ golang.org/x/net v0.11.0 // indirect
+ golang.org/x/sys v0.9.0 // indirect
+ google.golang.org/protobuf v1.31.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 71a680b7..93842827 100644
--- a/go.sum
+++ b/go.sum
@@ -42,8 +42,9 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
-github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
+github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
+github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac=
github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -55,6 +56,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -123,10 +125,13 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -177,17 +182,21 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
+github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@@ -205,8 +214,10 @@ github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
-github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
+github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -215,7 +226,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
@@ -240,8 +252,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
@@ -279,8 +292,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -352,10 +365,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -417,8 +428,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -428,8 +439,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -574,11 +586,15 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
diff --git a/http/http.go b/http/http.go
index 1c23b242..70d7d9d1 100644
--- a/http/http.go
+++ b/http/http.go
@@ -65,6 +65,10 @@ func NewHandler(
api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT")
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH")
+ api.PathPrefix("/tus").Handler(monkey(tusPostHandler(), "/api/tus")).Methods("POST")
+ api.PathPrefix("/tus").Handler(monkey(tusHeadHandler(), "/api/tus")).Methods("HEAD")
+ api.PathPrefix("/tus").Handler(monkey(tusPatchHandler(), "/api/tus")).Methods("PATCH")
+
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET")
diff --git a/http/settings.go b/http/settings.go
index 78ce9b0d..d0599207 100644
--- a/http/settings.go
+++ b/http/settings.go
@@ -15,6 +15,7 @@ type settingsData struct {
Defaults settings.UserDefaults `json:"defaults"`
Rules []rules.Rule `json:"rules"`
Branding settings.Branding `json:"branding"`
+ Tus settings.Tus `json:"tus"`
Shell []string `json:"shell"`
Commands map[string][]string `json:"commands"`
}
@@ -27,6 +28,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
Defaults: d.settings.Defaults,
Rules: d.settings.Rules,
Branding: d.settings.Branding,
+ Tus: d.settings.Tus,
Shell: d.settings.Shell,
Commands: d.settings.Commands,
}
@@ -47,6 +49,7 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
d.settings.Defaults = req.Defaults
d.settings.Rules = req.Rules
d.settings.Branding = req.Branding
+ d.settings.Tus = req.Tus
d.settings.Shell = req.Shell
d.settings.Commands = req.Commands
diff --git a/http/static.go b/http/static.go
index f438d76c..75c6a4fe 100644
--- a/http/static.go
+++ b/http/static.go
@@ -44,6 +44,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
"EnableThumbs": d.server.EnableThumbnails,
"ResizePreview": d.server.ResizePreview,
"EnableExec": d.server.EnableExec,
+ "TusSettings": d.settings.Tus,
}
if d.settings.Branding.Files != "" {
diff --git a/http/tus_handlers.go b/http/tus_handlers.go
new file mode 100644
index 00000000..8df38d4b
--- /dev/null
+++ b/http/tus_handlers.go
@@ -0,0 +1,155 @@
+package http
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "strconv"
+
+ "github.com/spf13/afero"
+
+ "github.com/filebrowser/filebrowser/v2/files"
+)
+
+func tusPostHandler() handleFunc {
+ return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
+ file, err := files.NewFileInfo(files.FileOptions{
+ Fs: d.user.Fs,
+ Path: r.URL.Path,
+ Modify: d.user.Perm.Modify,
+ Expand: false,
+ ReadHeader: d.server.TypeDetectionByHeader,
+ Checker: d,
+ })
+ switch {
+ case errors.Is(err, afero.ErrFileNotFound):
+ if !d.user.Perm.Create || !d.Check(r.URL.Path) {
+ return http.StatusForbidden, nil
+ }
+ case err != nil:
+ return errToStatus(err), err
+ }
+
+ fileFlags := os.O_CREATE
+ if r.URL.Query().Get("override") == "true" {
+ fileFlags |= os.O_TRUNC
+ }
+
+ // if file exists
+ if file != nil {
+ if file.IsDir {
+ return http.StatusBadRequest, fmt.Errorf("cannot upload to a directory %s", file.RealPath())
+ }
+ }
+
+ openFile, err := d.user.Fs.OpenFile(r.URL.Path, fileFlags, files.PERM)
+ if err != nil {
+ return errToStatus(err), err
+ }
+ if err := openFile.Close(); err != nil {
+ return errToStatus(err), err
+ }
+
+ return http.StatusCreated, nil
+ })
+}
+
+func tusHeadHandler() handleFunc {
+ return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
+ w.Header().Set("Cache-Control", "no-store")
+ if !d.Check(r.URL.Path) {
+ return http.StatusForbidden, nil
+ }
+
+ file, err := files.NewFileInfo(files.FileOptions{
+ Fs: d.user.Fs,
+ Path: r.URL.Path,
+ Modify: d.user.Perm.Modify,
+ Expand: false,
+ ReadHeader: d.server.TypeDetectionByHeader,
+ Checker: d,
+ })
+ if err != nil {
+ return errToStatus(err), err
+ }
+
+ w.Header().Set("Upload-Offset", strconv.FormatInt(file.Size, 10))
+ w.Header().Set("Upload-Length", "-1")
+
+ return http.StatusOK, nil
+ })
+}
+
+func tusPatchHandler() handleFunc {
+ return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
+ if !d.user.Perm.Modify || !d.Check(r.URL.Path) {
+ return http.StatusForbidden, nil
+ }
+ if r.Header.Get("Content-Type") != "application/offset+octet-stream" {
+ return http.StatusUnsupportedMediaType, nil
+ }
+
+ uploadOffset, err := getUploadOffset(r)
+ if err != nil {
+ return http.StatusBadRequest, fmt.Errorf("invalid upload offset: %v", err)
+ }
+
+ file, err := files.NewFileInfo(files.FileOptions{
+ Fs: d.user.Fs,
+ Path: r.URL.Path,
+ Modify: d.user.Perm.Modify,
+ Expand: false,
+ ReadHeader: d.server.TypeDetectionByHeader,
+ Checker: d,
+ })
+
+ switch {
+ case errors.Is(err, afero.ErrFileNotFound):
+ return http.StatusNotFound, nil
+ case err != nil:
+ return errToStatus(err), err
+ }
+
+ switch {
+ case file.IsDir:
+ return http.StatusBadRequest, fmt.Errorf("cannot upload to a directory %s", file.RealPath())
+ case file.Size != uploadOffset:
+ return http.StatusConflict, fmt.Errorf(
+ "%s file size doesn't match the provided offset: %d",
+ file.RealPath(),
+ uploadOffset,
+ )
+ }
+
+ openFile, err := d.user.Fs.OpenFile(r.URL.Path, os.O_WRONLY|os.O_APPEND, files.PERM)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("could not open file: %v", err)
+ }
+ defer openFile.Close()
+
+ _, err = openFile.Seek(uploadOffset, 0)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("could not seek file: %v", err)
+ }
+
+ defer r.Body.Close()
+ bytesWritten, err := io.Copy(openFile, r.Body)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Errorf("could not write to file: %v", err)
+ }
+
+ w.Header().Set("Upload-Offset", strconv.FormatInt(uploadOffset+bytesWritten, 10))
+
+ return http.StatusNoContent, nil
+ })
+}
+
+func getUploadOffset(r *http.Request) (int64, error) {
+ uploadOffset, err := strconv.ParseInt(r.Header.Get("Upload-Offset"), 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("invalid upload offset: %v", err)
+ }
+ return uploadOffset, nil
+}
diff --git a/http/utils.go b/http/utils.go
index c1c3fa65..7ef5f19e 100644
--- a/http/utils.go
+++ b/http/utils.go
@@ -32,9 +32,9 @@ func errToStatus(err error) int {
return http.StatusOK
case os.IsPermission(err):
return http.StatusForbidden
- case os.IsNotExist(err), err == libErrors.ErrNotExist:
+ case os.IsNotExist(err), errors.Is(err, libErrors.ErrNotExist):
return http.StatusNotFound
- case os.IsExist(err), err == libErrors.ErrExist:
+ case os.IsExist(err), errors.Is(err, libErrors.ErrExist):
return http.StatusConflict
case errors.Is(err, libErrors.ErrPermissionDenied):
return http.StatusForbidden
diff --git a/settings/settings.go b/settings/settings.go
index 7df81fc4..35ba1278 100644
--- a/settings/settings.go
+++ b/settings/settings.go
@@ -21,6 +21,7 @@ type Settings struct {
Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"`
Branding Branding `json:"branding"`
+ Tus Tus `json:"tus"`
Commands map[string][]string `json:"commands"`
Shell []string `json:"shell"`
Rules []rules.Rule `json:"rules"`
diff --git a/settings/storage.go b/settings/storage.go
index 8498d3bf..a006a84b 100644
--- a/settings/storage.go
+++ b/settings/storage.go
@@ -33,6 +33,12 @@ func (s *Storage) Get() (*Settings, error) {
if set.UserHomeBasePath == "" {
set.UserHomeBasePath = DefaultUsersHomeBasePath
}
+ if set.Tus == (Tus{}) {
+ set.Tus = Tus{
+ ChunkSize: DefaultTusChunkSize,
+ RetryCount: DefaultTusRetryCount,
+ }
+ }
return set, nil
}
diff --git a/settings/tus.go b/settings/tus.go
new file mode 100644
index 00000000..b869c2e1
--- /dev/null
+++ b/settings/tus.go
@@ -0,0 +1,10 @@
+package settings
+
+const DefaultTusChunkSize = 10 * 1024 * 1024 // 10MB
+const DefaultTusRetryCount = 5
+
+// Tus contains the tus.io settings of the app.
+type Tus struct {
+ ChunkSize uint64 `json:"chunkSize"`
+ RetryCount uint16 `json:"retryCount"`
+}