From c2f1423c02e4736f4c243c3164dc671879e065f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 11:47:56 +0200 Subject: [PATCH 01/63] build(deps): bump semver from 5.7.1 to 5.7.2 in /tools (#2546) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/yarn.lock | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tools/yarn.lock b/tools/yarn.lock index 670aef3a..e1a88a43 100644 --- a/tools/yarn.lock +++ b/tools/yarn.lock @@ -1351,11 +1351,11 @@ safe-buffer@~5.2.0: integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.3.5, semver@^7.1.1, semver@^7.3.4: +semver@7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -1363,9 +1363,16 @@ semver@7.3.5, semver@^7.1.1, semver@^7.3.4: lru-cache "^6.0.0" semver@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.1.1, semver@^7.3.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" shebang-command@^2.0.0: version "2.0.0" From 9bcfa900f904fe683c8d9085947f57932bfe22a0 Mon Sep 17 00:00:00 2001 From: Daniel Li Date: Thu, 20 Jul 2023 23:38:53 +0800 Subject: [PATCH 02/63] fix: filter ANSI color for shell (#2529) * feat: filter ANSI color for shell * chore: fix formatting --------- Co-authored-by: Oleg Lobanov --- frontend/src/components/Shell.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Shell.vue b/frontend/src/components/Shell.vue index 47c3d67c..dd5bf319 100644 --- a/frontend/src/components/Shell.vue +++ b/frontend/src/components/Shell.vue @@ -113,7 +113,10 @@ export default { this.scroll(); }, () => { - results.text = results.text.trimEnd(); + results.text = results.text + // eslint-disable-next-line no-control-regex + .replace(/\u001b\[[0-9;]+m/g, "") // Filter ANSI color for now + .trimEnd(); this.canInput = true; this.$refs.input.focus(); this.scroll(); From ecfcbfd2160f44798cd4f2302c52a5293645c9a9 Mon Sep 17 00:00:00 2001 From: ChengDaqi2023 <131479795+ChengDaqi2023@users.noreply.github.com> Date: Sun, 23 Jul 2023 03:24:28 +0800 Subject: [PATCH 03/63] chore: update golang.org/x/net v0.6.0 to 0.7.0 (#2559) --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b50ba09c..4ee2a1f6 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ 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.6.0 // indirect + golang.org/x/net v0.7.0 // indirect golang.org/x/sys v0.5.0 // 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 3b5fde8a..71a680b7 100644 --- a/go.sum +++ b/go.sum @@ -354,6 +354,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v 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/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= From bb3486286c0da112ad97456ad258ddcdfe17c154 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:24:50 +0200 Subject: [PATCH 04/63] build(deps-dev): bump word-wrap from 1.2.3 to 1.2.4 in /frontend (#2556) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cbf1136b..79db8633 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15049,9 +15049,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -27647,9 +27647,9 @@ "dev": true }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "worker-farm": { From a664ba1f9df45c7f6d03492c85466c5aa07c740e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Jul 2023 22:38:34 +0200 Subject: [PATCH 05/63] build(deps): bump minimatch from 3.0.4 to 3.1.2 in /tools (#2561) Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2) --- updated-dependencies: - dependency-name: minimatch dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tools/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/yarn.lock b/tools/yarn.lock index e1a88a43..1802db10 100644 --- a/tools/yarn.lock +++ b/tools/yarn.lock @@ -1034,9 +1034,9 @@ min-indent@^1.0.0: integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" From ff4375cf6ce849459889f892dd91304703c52dcd Mon Sep 17 00:00:00 2001 From: Andrew Kennedy Date: Sat, 22 Jul 2023 14:07:15 -0700 Subject: [PATCH 06/63] feat: add a healthcheck script that works with a dynamic port (#2510) --- Dockerfile | 8 ++++++-- healthcheck.sh | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 healthcheck.sh diff --git a/Dockerfile b/Dockerfile index ab826705..40a91a06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,14 @@ FROM alpine:latest RUN apk --update add ca-certificates \ mailcap \ - curl + curl \ + jq + +COPY healthcheck.sh /healthcheck.sh +RUN chmod +x /healthcheck.sh # Make the script executable HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \ - CMD curl -f http://localhost/health || exit 1 + CMD /healthcheck.sh || exit 1 VOLUME /srv EXPOSE 80 diff --git a/healthcheck.sh b/healthcheck.sh new file mode 100644 index 00000000..43eb2176 --- /dev/null +++ b/healthcheck.sh @@ -0,0 +1,3 @@ +#!/bin/sh +PORT=$(jq .port /.filebrowser.json) +curl -f http://localhost:$PORT/health || exit 1 From b508ac3d4f7f0f75d6b49c99bdc661a6d2173f30 Mon Sep 17 00:00:00 2001 From: Anchit Bajaj Date: Thu, 27 Jul 2023 15:12:27 +0530 Subject: [PATCH 07/63] fix: xss vulnerability in /api/raw (#2570) (#2572) --- http/raw.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http/raw.go b/http/raw.go index 1312510b..9bf982e9 100644 --- a/http/raw.go +++ b/http/raw.go @@ -207,7 +207,7 @@ func rawFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo defer fd.Close() setContentDisposition(w, r, file) - + w.Header().Add("Content-Security-Policy", `script-src 'none';`) w.Header().Set("Cache-Control", "private") http.ServeContent(w, r, file.Name, file.ModTime, fd) return 0, nil From 2744f7d5b9106c7c2eec69010e550e0939c23d80 Mon Sep 17 00:00:00 2001 From: ArthurMousatov <57199800+ArthurMousatov@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:03:49 -0400 Subject: [PATCH 08/63] fix: added an early return on non-existent items (#2571) --- frontend/src/views/files/Listing.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/files/Listing.vue b/frontend/src/views/files/Listing.vue index bdf4806b..b3d88e8e 100644 --- a/frontend/src/views/files/Listing.vue +++ b/frontend/src/views/files/Listing.vue @@ -606,10 +606,12 @@ export default { }, colunmsResize() { // Update the columns size based on the window width. + let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]); + if (!items) return; + let columns = Math.floor( document.querySelector("main").offsetWidth / this.columnWidth ); - let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]); if (columns === 0) columns = 1; items.style.width = `calc(${100 / columns}% - 1em)`; }, From 7b35815754690540f76e3ffe114eedb47cfd5c7e Mon Sep 17 00:00:00 2001 From: Tobias Goerke Date: Fri, 28 Jul 2023 18:15:44 +0200 Subject: [PATCH 09/63] feat: integrate tus.io for resumable and chunked uploads (#2145) --- cmd/root.go | 10 +- files/file.go | 2 + frontend/package-lock.json | 247 +++++++++++++++++--- frontend/package.json | 1 + frontend/src/api/files.js | 17 ++ frontend/src/api/tus.js | 85 +++++++ frontend/src/components/prompts/Replace.vue | 10 +- frontend/src/css/styles.css | 10 + frontend/src/i18n/en.json | 9 +- frontend/src/store/index.js | 1 + frontend/src/store/mutations.js | 4 + frontend/src/utils/constants.js | 4 + frontend/src/views/files/Listing.vue | 10 + frontend/src/views/settings/Global.vue | 88 +++++++ go.mod | 24 +- go.sum | 54 +++-- http/http.go | 4 + http/settings.go | 3 + http/static.go | 1 + http/tus_handlers.go | 155 ++++++++++++ http/utils.go | 4 +- settings/settings.go | 1 + settings/storage.go | 6 + settings/tus.go | 10 + 24 files changed, 694 insertions(+), 66 deletions(-) create mode 100644 frontend/src/api/tus.js create mode 100644 http/tus_handlers.go create mode 100644 settings/tus.go 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") }} + - +
+ +
+ + +
@@ -46,7 +63,7 @@ export default { dest: null, }; }, - computed: mapState(["req", "selected"]), + computed: mapState(["req", "selected", "user"]), methods: { copy: async function (event) { event.preventDefault(); diff --git a/frontend/src/components/prompts/Delete.vue b/frontend/src/components/prompts/Delete.vue index 0540b1d8..8e319846 100644 --- a/frontend/src/components/prompts/Delete.vue +++ b/frontend/src/components/prompts/Delete.vue @@ -37,8 +37,8 @@ import buttons from "@/utils/buttons"; export default { name: "delete", computed: { - ...mapGetters(["isListing", "selectedCount"]), - ...mapState(["req", "selected", "showConfirm"]), + ...mapGetters(["isListing", "selectedCount", "currentPrompt"]), + ...mapState(["req", "selected"]), }, methods: { ...mapMutations(["closeHovers"]), @@ -50,7 +50,7 @@ export default { await api.remove(this.$route.path); buttons.success("delete"); - this.showConfirm(); + this.currentPrompt?.confirm(); this.closeHovers(); return; } diff --git a/frontend/src/components/prompts/Download.vue b/frontend/src/components/prompts/Download.vue index 8c5a570d..1c6e63b1 100644 --- a/frontend/src/components/prompts/Download.vue +++ b/frontend/src/components/prompts/Download.vue @@ -11,7 +11,7 @@ v-for="(ext, format) in formats" :key="format" class="button button--block" - @click="showConfirm(format)" + @click="currentPrompt.confirm(format)" v-focus > {{ ext }} @@ -21,7 +21,7 @@ diff --git a/frontend/src/components/prompts/FileList.vue b/frontend/src/components/prompts/FileList.vue index 7a5c9f56..653752a0 100644 --- a/frontend/src/components/prompts/FileList.vue +++ b/frontend/src/components/prompts/FileList.vue @@ -133,6 +133,17 @@ export default { this.selected = event.currentTarget.dataset.url; this.$emit("update:selected", this.selected); }, + createDir: async function () { + this.$store.commit("showHover", { + prompt: "newDir", + action: null, + confirm: null, + props: { + redirect: false, + base: this.current === this.$route.path ? null : this.current, + }, + }); + }, }, }; diff --git a/frontend/src/components/prompts/Move.vue b/frontend/src/components/prompts/Move.vue index 1e0cf868..bbc19d36 100644 --- a/frontend/src/components/prompts/Move.vue +++ b/frontend/src/components/prompts/Move.vue @@ -5,27 +5,44 @@
- + +
-
- - +
+ +
+ + +
@@ -46,7 +63,7 @@ export default { dest: null, }; }, - computed: mapState(["req", "selected"]), + computed: mapState(["req", "selected", "user"]), methods: { move: async function (event) { event.preventDefault(); diff --git a/frontend/src/components/prompts/NewDir.vue b/frontend/src/components/prompts/NewDir.vue index bdcae64a..653ae953 100644 --- a/frontend/src/components/prompts/NewDir.vue +++ b/frontend/src/components/prompts/NewDir.vue @@ -43,6 +43,16 @@ import url from "@/utils/url"; export default { name: "new-dir", + props: { + redirect: { + type: Boolean, + default: true, + }, + base: { + type: [String, null], + default: null, + }, + }, data: function () { return { name: "", @@ -57,7 +67,11 @@ export default { if (this.new === "") return; // Build the path of the new directory. - let uri = this.isFiles ? this.$route.path + "/" : "/"; + let uri; + + if (this.base) uri = this.base; + else if (this.isFiles) uri = this.$route.path + "/"; + else uri = "/"; if (!this.isListing) { uri = url.removeLastDir(uri) + "/"; @@ -65,10 +79,14 @@ export default { uri += encodeURIComponent(this.name) + "/"; uri = uri.replace("//", "/"); - try { await api.post(uri); - this.$router.push({ path: uri }); + if (this.redirect) { + this.$router.push({ path: uri }); + } else if (!this.base) { + const res = await api.fetch(url.removeLastDir(uri) + "/"); + this.$store.commit("updateRequest", res); + } } catch (e) { this.$showError(e); } diff --git a/frontend/src/components/prompts/Prompts.vue b/frontend/src/components/prompts/Prompts.vue index 4a96f471..3305be9a 100644 --- a/frontend/src/components/prompts/Prompts.vue +++ b/frontend/src/components/prompts/Prompts.vue @@ -1,6 +1,12 @@ @@ -20,7 +26,8 @@ import ReplaceRename from "./ReplaceRename.vue"; import Share from "./Share.vue"; import Upload from "./Upload.vue"; import ShareDelete from "./ShareDelete.vue"; -import { mapState } from "vuex"; +import Sidebar from "../Sidebar.vue"; +import { mapGetters, mapState } from "vuex"; import buttons from "@/utils/buttons"; export default { @@ -40,6 +47,7 @@ export default { ReplaceRename, Upload, ShareDelete, + Sidebar }, data: function () { return { @@ -52,7 +60,7 @@ export default { }, created() { window.addEventListener("keydown", (event) => { - if (this.show == null) return; + if (this.currentPrompt == null) return; let prompt = this.$refs.currentComponent; @@ -64,7 +72,7 @@ export default { // Enter if (event.keyCode == 13) { - switch (this.show) { + switch (this.currentPrompt.prompt) { case "delete": prompt.submit(); break; @@ -82,31 +90,13 @@ export default { }); }, computed: { - ...mapState(["show", "plugins"]), - currentComponent: function () { - const matched = - [ - "info", - "help", - "delete", - "rename", - "move", - "copy", - "newFile", - "newDir", - "download", - "replace", - "replace-rename", - "share", - "upload", - "share-delete", - ].indexOf(this.show) >= 0; - - return (matched && this.show) || null; - }, + ...mapState(["plugins"]), + ...mapGetters(["currentPrompt", "currentPromptName"]), showOverlay: function () { return ( - this.show !== null && this.show !== "search" && this.show !== "more" + this.currentPrompt !== null && + this.currentPrompt.prompt !== "search" && + this.currentPrompt.prompt !== "more" ); }, }, diff --git a/frontend/src/components/prompts/Replace.vue b/frontend/src/components/prompts/Replace.vue index a525bbbc..bece1772 100644 --- a/frontend/src/components/prompts/Replace.vue +++ b/frontend/src/components/prompts/Replace.vue @@ -19,7 +19,7 @@