diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index c99b2c54..9b63df43 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '16' - run: make lint-frontend lint-backend: runs-on: ubuntu-latest @@ -24,9 +24,9 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18.1 - run: make lint-backend - lint-commints: + lint-commits: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -34,11 +34,11 @@ jobs: fetch-depth: 0 - uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '16' - run: make lint-commits lint: runs-on: ubuntu-latest - needs: [lint-frontend, lint-backend, lint-commints] + needs: [lint-frontend, lint-backend, lint-commits] steps: - run: echo "done" @@ -49,7 +49,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '16' - run: make test-frontend test-backend: runs-on: ubuntu-latest @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18.1 - run: make test-backend test: runs-on: ubuntu-latest @@ -76,15 +76,15 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.18.1 - uses: actions/setup-node@v2 with: - node-version: '14' + node-version: '16' - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Build fronetend + - name: Build frontend run: make build-frontend - name: Login to Docker Hub uses: docker/login-action@v1 @@ -97,4 +97,4 @@ jobs: version: latest args: release --rm-dist env: - GITHUB_TOKEN: ${{ secrets.GH_PAT }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GH_PAT }} diff --git a/.golangci.yml b/.golangci.yml index 54ecb4a0..c946c73a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -109,6 +109,7 @@ issues: - gomnd run: + go: '1.18' skip-dirs: - frontend/ skip-files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a784ef..54faa62d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,47 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.21.1](https://github.com/filebrowser/filebrowser/compare/v2.21.0...v2.21.1) (2022-02-22) + + +### Bug Fixes + +* display user scope for admin users ([#1834](https://github.com/filebrowser/filebrowser/issues/1834)) ([6366cf0](https://github.com/filebrowser/filebrowser/commit/6366cf0b181f13eac38f69f1760d6f6f0586a5d1)) + +## [2.21.0](https://github.com/filebrowser/filebrowser/compare/v2.20.1...v2.21.0) (2022-02-21) + + +### Features + +* add colorized file type icons ([2948589](https://github.com/filebrowser/filebrowser/commit/2948589fcde6d1dca7f3ea52a621d8213fa3300c)) +* add gallery view mode ([8888b9f](https://github.com/filebrowser/filebrowser/commit/8888b9f44640394df9e3583db4392472d7027a4b)) +* add Ukrainian translation / update Russian translation ([#1753](https://github.com/filebrowser/filebrowser/issues/1753)) ([665e458](https://github.com/filebrowser/filebrowser/commit/665e45889cd333f1e3500e4bf38d15d229c9fe2a)) +* add upload file list with progress ([#1825](https://github.com/filebrowser/filebrowser/issues/1825)) ([cf85404](https://github.com/filebrowser/filebrowser/commit/cf85404dd25cd7fdd73aa32878b4dc5f85ee3e96)) +* smaller column width to fit 2 columns in landscape mobiles ([7870e89](https://github.com/filebrowser/filebrowser/commit/7870e89bc04f1494f2705795476b5f1c9d621e38)) +* use real image path to calculate cache key ([c198723](https://github.com/filebrowser/filebrowser/commit/c1987237d05adcce77c614e5247a181ae5cdfacd)) + + +### Bug Fixes + +* correctly handle non-ascii passwords for shared resources ([c782f21](https://github.com/filebrowser/filebrowser/commit/c782f21b0fa4511a15e7015117d075eaf5ea332c)) +* don't expose scope for non-admin users ([0942fc7](https://github.com/filebrowser/filebrowser/commit/0942fc7042fd949cce91855169d0bcf16eb75771)) +* open all the pdf files correctly ([#1742](https://github.com/filebrowser/filebrowser/issues/1742)) ([949f0f2](https://github.com/filebrowser/filebrowser/commit/949f0f277f6004904b3edfa716a8365ec93fa0fa)) + + +### Build + +* **deps:** bump browserslist from 4.16.3 to 4.19.1 in /frontend ([8089007](https://github.com/filebrowser/filebrowser/commit/80890075e802e2a4217edbb01d6417122d702f5e)) +* **deps:** bump dns-packet from 1.3.1 to 1.3.4 in /frontend ([a73d7f1](https://github.com/filebrowser/filebrowser/commit/a73d7f14b787935c6ebe525dba64b65f8ed733e2)) +* **deps:** bump follow-redirects from 1.13.3 to 1.14.8 in /frontend ([f1f7f17](https://github.com/filebrowser/filebrowser/commit/f1f7f17ade8d40fc6cfb22c79960bce299876b56)) +* **deps:** bump hosted-git-info from 2.8.8 to 2.8.9 in /frontend ([e7659ea](https://github.com/filebrowser/filebrowser/commit/e7659ea36bdf780ce17005f7170a2fef02a2d5e5)) +* **deps:** bump path-parse from 1.0.6 to 1.0.7 in /frontend ([c014966](https://github.com/filebrowser/filebrowser/commit/c01496624a7ebfc8a7c256bd919a400367281cbb)) +* **deps:** bump postcss from 7.0.35 to 7.0.39 in /frontend ([9182d33](https://github.com/filebrowser/filebrowser/commit/9182d33e1cc375473fb18989a92d20252884f096)) +* **deps:** bump ssri from 6.0.1 to 6.0.2 in /frontend ([3717186](https://github.com/filebrowser/filebrowser/commit/371718634b11f32e68165f31c51b6b1139c829ec)) +* **deps:** bump tar from 6.1.0 to 6.1.11 in /frontend ([010d16f](https://github.com/filebrowser/filebrowser/commit/010d16fc1d8f0200e5662943aef17ee89c5877b7)) +* **deps:** bump url-parse from 1.5.1 to 1.5.4 in /frontend ([8906408](https://github.com/filebrowser/filebrowser/commit/8906408a8f0ed86d1e11ea90fc573b36815c9c0d)) +* **deps:** bump url-parse from 1.5.4 to 1.5.7 in /frontend ([228ebea](https://github.com/filebrowser/filebrowser/commit/228ebea66cc871b33459406590a80ef906298e7d)) +* **deps:** bump ws from 6.2.1 to 6.2.2 in /frontend ([73c8073](https://github.com/filebrowser/filebrowser/commit/73c80732d934bc8802a6d7c7a559cad37df405f0)) + ### [2.20.1](https://github.com/filebrowser/filebrowser/compare/v2.20.0...v2.20.1) (2021-12-21) diff --git a/README.md b/README.md index c7967da4..7e39024d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Version](https://img.shields.io/github/release/filebrowser/filebrowser.svg?style=flat-square)](https://github.com/filebrowser/filebrowser/releases/latest) [![Chat IRC](https://img.shields.io/badge/freenode-%23filebrowser-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23filebrowser) -filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware. +filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app. ## Features diff --git a/cmd/utils.go b/cmd/utils.go index a7b706b5..dbdb64d8 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/asdine/storm" + "github.com/asdine/storm/v3" "github.com/spf13/cobra" "github.com/spf13/pflag" yaml "gopkg.in/yaml.v2" diff --git a/files/file.go b/files/file.go index 187606ad..c92f730b 100644 --- a/files/file.go +++ b/files/file.go @@ -188,6 +188,19 @@ func (i *FileInfo) Checksum(algo string) error { return nil } +func (i *FileInfo) RealPath() string { + if realPathFs, ok := i.Fs.(interface { + RealPath(name string) (fPath string, err error) + }); ok { + realPath, err := realPathFs.RealPath(i.Path) + if err == nil { + return realPath + } + } + + return i.Path +} + //nolint:goconst //TODO: use constants func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { @@ -222,6 +235,9 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { case strings.HasPrefix(mimetype, "image"): i.Type = "image" return nil + case strings.HasSuffix(mimetype, "pdf"): + i.Type = "pdf" + return nil case (strings.HasPrefix(mimetype, "text") || !isBinary(buffer)) && i.Size <= 10*1024*1024: // 10 MB i.Type = "text" @@ -310,13 +326,16 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { } symlink := "" - isSymlink := IsSymlink(f.Mode()) - if isSymlink { + isSymlink, isInvalidLink := false, false + if IsSymlink(f.Mode()) { + isSymlink = true // It's a symbolic link. We try to follow it. If it doesn't work, // we stay with the link information instead of the target's. info, err := i.Fs.Stat(fPath) if err == nil { f = info + } else { + isInvalidLink = true } // Try to read the link's target @@ -343,9 +362,13 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { } else { listing.NumFiles++ - err := file.detectType(true, false, readHeader) - if err != nil { - return err + if isInvalidLink { + file.Type = "invalid_link" + } else { + err := file.detectType(true, false, readHeader) + if err != nil { + return err + } } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ad4009d1..56062700 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,8 +15,8 @@ "js-base64": "^2.5.1", "lodash.clonedeep": "^4.5.0", "lodash.throttle": "^4.1.1", - "material-design-icons": "^3.0.1", - "moment": "^2.24.0", + "material-icons": "^1.10.5", + "moment": "^2.29.2", "normalize.css": "^8.0.1", "noty": "^3.2.0-beta", "qrcode.vue": "^1.7.0", @@ -2745,9 +2745,9 @@ } }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "dependencies": { "lodash": "^4.17.14" @@ -3272,16 +3272,16 @@ } }, "node_modules/browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" }, "bin": { "browserslist": "cli.js" @@ -3294,6 +3294,12 @@ "url": "https://opencollective.com/browserslist" } }, + "node_modules/browserslist/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, "node_modules/buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -3368,9 +3374,9 @@ } }, "node_modules/cacache/node_modules/ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "dependencies": { "figgy-pudding": "^3.5.1" @@ -3503,10 +3509,14 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001203", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001203.tgz", - "integrity": "sha512-/I9tvnzU/PHMH7wBPrfDMSuecDeUKerjCPX7D0xBbaJZPxoT9m+yYxt0zCTkcijCkjTdim3H56Zm0i5Adxch4w==", - "dev": true + "version": "1.0.30001309", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", + "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -5415,9 +5425,9 @@ "dev": true }, "node_modules/dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dev": true, "dependencies": { "ip": "^1.1.0", @@ -5592,9 +5602,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.3.693", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.693.tgz", - "integrity": "sha512-vUdsE8yyeu30RecppQtI+XTz2++LWLVEIYmzeCaCRLSdtKZ2eXqdJcrs85KwLiPOPVc6PELgWyXBsfqIvzGZag==", + "version": "1.4.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.67.tgz", + "integrity": "sha512-A6a2jEPLueEDfb7kvh7/E94RKKnIb01qL+4I7RFxtajmo+G9F5Ei7HgY5PRbQ4RDrh6DGDW66P0hD5XI2nRAcg==", "dev": true }, "node_modules/elliptic": { @@ -6733,9 +6743,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", - "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "dev": true, "funding": [ { @@ -7287,9 +7297,9 @@ } }, "node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "node_modules/hpack.js": { @@ -8962,10 +8972,10 @@ "node": ">=0.10.0" } }, - "node_modules/material-design-icons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz", - "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78=" + "node_modules/material-icons": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.10.5.tgz", + "integrity": "sha512-+4l0xbfKlA2eSXJrhISYvCvT8e9HqQk3BLdOCPGal5qPYAQoTI8TTacYI0yXyf7WS6BpKrhQemUenhmR0IoS8A==" }, "node_modules/md5.js": { "version": "1.3.5", @@ -9193,9 +9203,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "node_modules/minipass": { @@ -9318,9 +9328,9 @@ } }, "node_modules/moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==", "engines": { "node": "*" } @@ -9507,9 +9517,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", "dev": true }, "node_modules/normalize-package-data": { @@ -10256,9 +10266,9 @@ } }, "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "node_modules/path-to-regexp": { @@ -10310,6 +10320,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "node_modules/picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -10409,14 +10425,13 @@ } }, "node_modules/postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "dependencies": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "engines": { "node": ">=6.0.0" @@ -11070,18 +11085,6 @@ "node": ">=0.10.0" } }, - "node_modules/postcss/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -12558,9 +12561,9 @@ } }, "node_modules/ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz", + "integrity": "sha512-w+daCzXN89PseTL99MkA+fxJEcU3wfaE/ah0i0lnOlpG1CYLJ2ZjzEry68YBKfLs4JfoTShrTEsJkAZuNZ/stw==", "dev": true, "dependencies": { "figgy-pudding": "^3.5.1", @@ -12973,9 +12976,9 @@ } }, "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -13689,9 +13692,9 @@ } }, "node_modules/url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "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", @@ -15146,9 +15149,9 @@ } }, "node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dev": true, "dependencies": { "async-limiter": "~1.0.0" @@ -16835,6 +16838,7 @@ "integrity": "sha512-8q67ORQ9O0Ms0nlqsXTVhaBefRBaLrzPxOewAZhdcO7onHwcO5/wRdWtHhZgfpCZlhY7NogkU16z3WnorSSkEA==", "dev": true, "requires": { + "@babel/core": "^7.11.0", "@babel/helper-compilation-targets": "^7.9.6", "@babel/helper-module-imports": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3", @@ -16847,6 +16851,7 @@ "@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-preset-jsx": "^1.2.4", "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", "core-js-compat": "^3.6.5", "semver": "^6.1.0" } @@ -17641,9 +17646,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -18078,16 +18083,24 @@ } }, "browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "dependencies": { + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + } } }, "buffer": { @@ -18161,9 +18174,9 @@ }, "dependencies": { "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -18271,9 +18284,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001203", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001203.tgz", - "integrity": "sha512-/I9tvnzU/PHMH7wBPrfDMSuecDeUKerjCPX7D0xBbaJZPxoT9m+yYxt0zCTkcijCkjTdim3H56Zm0i5Adxch4w==", + "version": "1.0.30001309", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", + "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", "dev": true }, "case-sensitive-paths-webpack-plugin": { @@ -19799,9 +19812,9 @@ "dev": true }, "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", "dev": true, "requires": { "ip": "^1.1.0", @@ -19952,9 +19965,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.693", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.693.tgz", - "integrity": "sha512-vUdsE8yyeu30RecppQtI+XTz2++LWLVEIYmzeCaCRLSdtKZ2eXqdJcrs85KwLiPOPVc6PELgWyXBsfqIvzGZag==", + "version": "1.4.67", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.67.tgz", + "integrity": "sha512-A6a2jEPLueEDfb7kvh7/E94RKKnIb01qL+4I7RFxtajmo+G9F5Ei7HgY5PRbQ4RDrh6DGDW66P0hD5XI2nRAcg==", "dev": true }, "elliptic": { @@ -20863,9 +20876,9 @@ } }, "follow-redirects": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", - "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz", + "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==", "dev": true }, "for-in": { @@ -21282,9 +21295,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "hpack.js": { @@ -22587,10 +22600,10 @@ "object-visit": "^1.0.0" } }, - "material-design-icons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz", - "integrity": "sha1-mnHEh0chjrylHlGmbaaCA4zct78=" + "material-icons": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.10.5.tgz", + "integrity": "sha512-+4l0xbfKlA2eSXJrhISYvCvT8e9HqQk3BLdOCPGal5qPYAQoTI8TTacYI0yXyf7WS6BpKrhQemUenhmR0IoS8A==" }, "md5.js": { "version": "1.3.5", @@ -22779,9 +22792,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "minipass": { @@ -22884,9 +22897,9 @@ } }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "version": "2.29.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz", + "integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==" }, "move-concurrently": { "version": "1.0.1", @@ -23057,9 +23070,9 @@ } }, "node-releases": { - "version": "1.1.71", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", - "integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", "dev": true }, "normalize-package-data": { @@ -23647,9 +23660,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-to-regexp": { @@ -23694,6 +23707,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -23768,14 +23787,13 @@ "dev": true }, "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" + "picocolors": "^0.2.1", + "source-map": "^0.6.1" }, "dependencies": { "source-map": { @@ -23783,15 +23801,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } } } }, @@ -25615,9 +25624,9 @@ } }, "ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.1.tgz", + "integrity": "sha512-w+daCzXN89PseTL99MkA+fxJEcU3wfaE/ah0i0lnOlpG1CYLJ2ZjzEry68YBKfLs4JfoTShrTEsJkAZuNZ/stw==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -25952,9 +25961,9 @@ "dev": true }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -26531,9 +26540,9 @@ } }, "url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "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", @@ -27694,9 +27703,9 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", "dev": true, "requires": { "async-limiter": "~1.0.0" diff --git a/frontend/package.json b/frontend/package.json index 1fe0477f..c267a34b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,8 +17,8 @@ "js-base64": "^2.5.1", "lodash.clonedeep": "^4.5.0", "lodash.throttle": "^4.1.1", - "material-design-icons": "^3.0.1", - "moment": "^2.24.0", + "material-icons": "^1.10.5", + "moment": "^2.29.2", "normalize.css": "^8.0.1", "noty": "^3.2.0-beta", "qrcode.vue": "^1.7.0", diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js index 4ab3183c..6b3a71e3 100644 --- a/frontend/src/api/files.js +++ b/frontend/src/api/files.js @@ -1,4 +1,4 @@ -import { fetchURL, removePrefix } from "./utils"; +import { fetchURL, removePrefix, createURL } from "./utils"; import { baseURL } from "@/utils/constants"; import store from "@/store"; @@ -7,28 +7,24 @@ export async function fetch(url) { const res = await fetchURL(`/api/resources${url}`, {}); - if (res.status === 200) { - let data = await res.json(); - data.url = `/files${url}`; + let data = await res.json(); + data.url = `/files${url}`; - if (data.isDir) { - if (!data.url.endsWith("/")) data.url += "/"; - data.items = data.items.map((item, index) => { - item.index = index; - item.url = `${data.url}${encodeURIComponent(item.name)}`; + if (data.isDir) { + if (!data.url.endsWith("/")) data.url += "/"; + data.items = data.items.map((item, index) => { + item.index = index; + item.url = `${data.url}${encodeURIComponent(item.name)}`; - if (item.isDir) { - item.url += "/"; - } + if (item.isDir) { + item.url += "/"; + } - return item; - }); - } - - return data; - } else { - throw new Error(res.status); + return item; + }); } + + return data; } async function resourceAction(url, method, content) { @@ -42,11 +38,7 @@ async function resourceAction(url, method, content) { const res = await fetchURL(`/api/resources${url}`, opts); - if (res.status !== 200) { - throw new Error(await res.text()); - } else { - return res; - } + return res; } export async function remove(url, skipTrash = true) { @@ -119,8 +111,8 @@ export async function post(url, content = "", overwrite = false, onupload) { } }; - request.onerror = (error) => { - reject(error); + request.onerror = () => { + reject(new Error("001 Connection aborted")); }; request.send(bufferContent || content); @@ -194,3 +186,33 @@ export async function chmod(path, perms, recursive, recursionType) { } return resourceAction(url, "PATCH"); } + +export function getDownloadURL(file, inline) { + const params = { + ...(inline && { inline: "true" }), + }; + + return createURL("api/raw" + file.path, params); +} + +export function getPreviewURL(file, size) { + const params = { + inline: "true", + key: Date.parse(file.modified), + }; + + return createURL("api/preview/" + size + file.path, params); +} + +export function getSubtitlesURL(file) { + const params = { + inline: "true", + }; + + const subtitles = []; + for (const sub of file.subtitles) { + subtitles.push(createURL("api/raw" + sub, params)); + } + + return subtitles; +} diff --git a/frontend/src/api/pub.js b/frontend/src/api/pub.js index 99642309..1511143d 100644 --- a/frontend/src/api/pub.js +++ b/frontend/src/api/pub.js @@ -1,35 +1,35 @@ -import { fetchURL, removePrefix } from "./utils"; +import { fetchURL, removePrefix, createURL } from "./utils"; import { baseURL } from "@/utils/constants"; export async function fetch(url, password = "") { url = removePrefix(url); - const res = await fetchURL(`/api/public/share${url}`, { - headers: { "X-SHARE-PASSWORD": password }, - }); + const res = await fetchURL( + `/api/public/share${url}`, + { + headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) }, + }, + false + ); - if (res.status === 200) { - let data = await res.json(); - data.url = `/share${url}`; + let data = await res.json(); + data.url = `/share${url}`; - if (data.isDir) { - if (!data.url.endsWith("/")) data.url += "/"; - data.items = data.items.map((item, index) => { - item.index = index; - item.url = `${data.url}${encodeURIComponent(item.name)}`; + if (data.isDir) { + if (!data.url.endsWith("/")) data.url += "/"; + data.items = data.items.map((item, index) => { + item.index = index; + item.url = `${data.url}${encodeURIComponent(item.name)}`; - if (item.isDir) { - item.url += "/"; - } + if (item.isDir) { + item.url += "/"; + } - return item; - }); - } - - return data; - } else { - throw new Error(res.status); + return item; + }); } + + return data; } export function download(format, hash, token, ...files) { @@ -59,3 +59,12 @@ export function download(format, hash, token, ...files) { window.open(url); } + +export function getDownloadURL(share, inline = false) { + const params = { + ...(inline && { inline: "true" }), + ...(share.token && { token: share.token }), + }; + + return createURL("api/public/dl/" + share.hash + share.path, params, false); +} diff --git a/frontend/src/api/search.js b/frontend/src/api/search.js index 08be5c1d..42846880 100644 --- a/frontend/src/api/search.js +++ b/frontend/src/api/search.js @@ -11,21 +11,17 @@ export default async function search(base, query) { let res = await fetchURL(`/api/search${base}?query=${query}`, {}); - if (res.status === 200) { - let data = await res.json(); + let data = await res.json(); - data = data.map((item) => { - item.url = `/files${base}` + url.encodePath(item.path); + data = data.map((item) => { + item.url = `/files${base}` + url.encodePath(item.path); - if (item.dir) { - item.url += "/"; - } + if (item.dir) { + item.url += "/"; + } - return item; - }); + return item; + }); - return data; - } else { - throw Error(res.status); - } + return data; } diff --git a/frontend/src/api/settings.js b/frontend/src/api/settings.js index 8abe1f1e..e03b0db1 100644 --- a/frontend/src/api/settings.js +++ b/frontend/src/api/settings.js @@ -5,12 +5,8 @@ export function get() { } export async function update(settings) { - const res = await fetchURL(`/api/settings`, { + await fetchURL(`/api/settings`, { method: "PUT", body: JSON.stringify(settings), }); - - if (res.status !== 200) { - throw new Error(res.status); - } } diff --git a/frontend/src/api/share.js b/frontend/src/api/share.js index 54bbc460..1ac4473a 100644 --- a/frontend/src/api/share.js +++ b/frontend/src/api/share.js @@ -1,4 +1,4 @@ -import { fetchURL, fetchJSON, removePrefix } from "./utils"; +import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils"; export async function list() { return fetchJSON("/api/shares"); @@ -10,13 +10,9 @@ export async function get(url) { } export async function remove(hash) { - const res = await fetchURL(`/api/share/${hash}`, { + await fetchURL(`/api/share/${hash}`, { method: "DELETE", }); - - if (res.status !== 200) { - throw new Error(res.status); - } } export async function create(url, password = "", expires = "", unit = "hours") { @@ -34,3 +30,7 @@ export async function create(url, password = "", expires = "", unit = "hours") { body: body, }); } + +export function getShareURL(share) { + return createURL("share/" + share.hash, {}, false); +} diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js index 1f12c503..dae2daa9 100644 --- a/frontend/src/api/users.js +++ b/frontend/src/api/users.js @@ -20,13 +20,11 @@ export async function create(user) { if (res.status === 201) { return res.headers.get("Location"); - } else { - throw new Error(res.status); } } export async function update(user, which = ["all"]) { - const res = await fetchURL(`/api/users/${user.id}`, { + await fetchURL(`/api/users/${user.id}`, { method: "PUT", body: JSON.stringify({ what: "user", @@ -34,20 +32,12 @@ export async function update(user, which = ["all"]) { data: user, }), }); - - if (res.status !== 200) { - throw new Error(res.status); - } } export async function remove(id) { - const res = await fetchURL(`/api/users/${id}`, { + await fetchURL(`/api/users/${id}`, { method: "DELETE", }); - - if (res.status !== 200) { - throw new Error(res.status); - } } export async function getQuota() { diff --git a/frontend/src/api/utils.js b/frontend/src/api/utils.js index 65c6740a..ddddcfa7 100644 --- a/frontend/src/api/utils.js +++ b/frontend/src/api/utils.js @@ -1,8 +1,9 @@ import store from "@/store"; -import { renew } from "@/utils/auth"; +import { renew, logout } from "@/utils/auth"; import { baseURL } from "@/utils/constants"; +import { encodePath } from "@/utils/url"; -export async function fetchURL(url, opts) { +export async function fetchURL(url, opts, auth = true) { opts = opts || {}; opts.headers = opts.headers || {}; @@ -17,14 +18,28 @@ export async function fetchURL(url, opts) { }, ...rest, }); - } catch (error) { - return { status: 0 }; + } catch { + const error = new Error("000 No connection"); + error.status = 0; + + throw error; } - if (res.headers.get("X-Renew-Token") === "true") { + if (auth && res.headers.get("X-Renew-Token") === "true") { await renew(store.state.jwt); } + if (res.status < 200 || res.status > 299) { + const error = new Error(await res.text()); + error.status = res.status; + + if (auth && res.status == 401) { + logout(); + } + + throw error; + } + return res; } @@ -45,3 +60,18 @@ export function removePrefix(url) { if (url[0] !== "/") url = "/" + url; return url; } + +export function createURL(endpoint, params = {}, auth = true) { + const url = new URL(encodePath(endpoint), origin + baseURL); + + const searchParams = { + ...(auth && { auth: store.state.jwt }), + ...params, + }; + + for (const key in searchParams) { + url.searchParams.set(key, searchParams[key]); + } + + return url.toString(); +} diff --git a/frontend/src/components/Quota.vue b/frontend/src/components/Quota.vue index 7f6404c8..15044d54 100644 --- a/frontend/src/components/Quota.vue +++ b/frontend/src/components/Quota.vue @@ -1,21 +1,27 @@ diff --git a/frontend/src/css/_variables.css b/frontend/src/css/_variables.css index e0c039ef..94e59664 100644 --- a/frontend/src/css/_variables.css +++ b/frontend/src/css/_variables.css @@ -4,4 +4,11 @@ --red: #F44336; --dark-red: #D32F2F; --moon-grey: #f2f2f2; + + --icon-red: #da4453; + --icon-orange: #f47750; + --icon-yellow: #fdbc4b; + --icon-green: #2ecc71; + --icon-blue: #1d99f3; + --icon-violet: #9b59b6; } diff --git a/frontend/src/css/base.css b/frontend/src/css/base.css index 53235d9c..5e3b273c 100644 --- a/frontend/src/css/base.css +++ b/frontend/src/css/base.css @@ -1,5 +1,5 @@ body { - font-family: 'Roboto', sans-serif; + font-family: "Roboto", sans-serif; padding-top: 4em; background-color: #fafafa; color: #333333; @@ -13,7 +13,7 @@ body { *:hover, *:active, *:focus { - outline: 0 + outline: 0; } a { @@ -60,7 +60,7 @@ i.pulse-spin { } #app { - transition: .2s ease padding; + transition: 0.2s ease padding; } #app.multiple { @@ -79,17 +79,17 @@ nav .action { display: block; border-radius: 0; font-size: 1.1em; - padding: .5em; + padding: 0.5em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -nav>div { +nav > div { border-top: 1px solid rgba(0, 0, 0, 0.05); } -nav .action>* { +nav .action > * { vertical-align: middle; } @@ -113,19 +113,25 @@ main { .breadcrumbs a { color: inherit; - transition: .1s ease-in; - border-radius: .125em; + transition: 0.1s ease-in; + border-radius: 0.125em; } .breadcrumbs a:hover { - background-color: rgba(0,0,0, 0.05); + background-color: rgba(0, 0, 0, 0.05); } .breadcrumbs span a { - padding: .2em; + padding: 0.2em; } -#progress { +.files { + position: absolute; + bottom: 30px; + width: 100%; +} + +.progress { position: fixed; top: 0; left: 0; @@ -134,11 +140,11 @@ main { z-index: 9999999999; } -#progress div { +.progress div { height: 100%; background-color: #40c4ff; width: 0; - transition: .2s ease width; + transition: 0.2s ease width; } .break-word { @@ -153,23 +159,23 @@ main { margin-top: .8em; } -#quota .label { +#quota .quota-label { color: #546E7A; font-weight: 500; } -#quota .label .metric { +#quota .quota-label .quota-metric { font-weight: 100; margin: .2em 0 } -#quota .bar { +#quota .quota-bar { width: 100%; height: 10px; background-color: rgba(0,0,0, 0.1); } -#quota .bar .progress { +#quota .quota-bar .quota-progress { width: 0; height: 100%; transition: .2s ease width; diff --git a/frontend/src/css/fonts.css b/frontend/src/css/fonts.css index cd1474f7..f57a98c1 100644 --- a/frontend/src/css/fonts.css +++ b/frontend/src/css/fonts.css @@ -166,7 +166,7 @@ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } -@import "~material-design-icons/iconfont/material-icons.css"; +@import '~material-icons/iconfont/filled.css'; .material-icons { font-size: 1.5rem; diff --git a/frontend/src/css/listing-icons.css b/frontend/src/css/listing-icons.css new file mode 100644 index 00000000..32c87755 --- /dev/null +++ b/frontend/src/css/listing-icons.css @@ -0,0 +1,205 @@ +/* Icons */ + +/* General */ + +.file-icons [aria-label^="."] { opacity: 0.33 } +.file-icons [aria-label$=".bak"] { opacity: 0.33 } + +.file-icons [data-type=audio] i::before { content: 'volume_up' } +.file-icons [data-type=blob] i::before { content: 'insert_drive_file' } +.file-icons [data-type=image] i::before { content: 'image' } +.file-icons [data-type=pdf] i::before { content: 'description' } +.file-icons [data-type=text] i::before { content: 'description' } +.file-icons [data-type=video] i::before { content: 'movie' } +.file-icons [data-type=invalid_link] i::before { content: 'link_off' } + +/* #f90 - Image */ + +.file-icons [aria-label$=".ai"] i::before, +.file-icons [aria-label$=".odg"] i::before, +.file-icons [aria-label$=".xcf"] i::before +{ content: 'image' } + +/* #f90 - Presentation */ + +.file-icons [aria-label$=".odp"] i::before, +.file-icons [aria-label$=".ppt"] i::before, +.file-icons [aria-label$=".pptx"] i::before +{ content: 'slideshow' } + +/* #0f0 - Spreadsheet/Database */ + +.file-icons [aria-label$=".csv"] i::before, +.file-icons [aria-label$=".db"] i::before, +.file-icons [aria-label$=".odb"] i::before, +.file-icons [aria-label$=".ods"] i::before, +.file-icons [aria-label$=".xls"] i::before, +.file-icons [aria-label$=".xlsx"] i::before +{ content: 'border_all' } + +/* #00f - Document */ + +.file-icons [aria-label$=".doc"] i::before, +.file-icons [aria-label$=".docx"] i::before, +.file-icons [aria-label$=".log"] i::before, +.file-icons [aria-label$=".odt"] i::before, +.file-icons [aria-label$=".rtf"] i::before +{ content: 'description' } + +/* #999 - Code */ + +.file-icons [aria-label$=".c"] i::before, +.file-icons [aria-label$=".cpp"] i::before, +.file-icons [aria-label$=".cs"] i::before, +.file-icons [aria-label$=".css"] i::before, +.file-icons [aria-label$=".go"] i::before, +.file-icons [aria-label$=".h"] i::before, +.file-icons [aria-label$=".html"] i::before, +.file-icons [aria-label$=".java"] i::before, +.file-icons [aria-label$=".js"] i::before, +.file-icons [aria-label$=".json"] i::before, +.file-icons [aria-label$=".kt"] i::before, +.file-icons [aria-label$=".php"] i::before, +.file-icons [aria-label$=".py"] i::before, +.file-icons [aria-label$=".rb"] i::before, +.file-icons [aria-label$=".rs"] i::before, +.file-icons [aria-label$=".vue"] i::before, +.file-icons [aria-label$=".xml"] i::before, +.file-icons [aria-label$=".yml"] i::before +{ content: 'code' } + +/* #999 - Executable */ + +.file-icons [aria-label$=".apk"] i::before, +.file-icons [aria-label$=".bat"] i::before, +.file-icons [aria-label$=".exe"] i::before, +.file-icons [aria-label$=".jar"] i::before, +.file-icons [aria-label$=".ps1"] i::before, +.file-icons [aria-label$=".sh"] i::before +{ content: 'web_asset' } + +/* #999 - Installer */ + +.file-icons [aria-label$=".deb"] i::before, +.file-icons [aria-label$=".msi"] i::before, +.file-icons [aria-label$=".pkg"] i::before, +.file-icons [aria-label$=".rpm"] i::before +{ content: 'archive' } + +/* #999 - Compressed */ + +.file-icons [aria-label$=".7z"] i::before, +.file-icons [aria-label$=".bz2"] i::before, +.file-icons [aria-label$=".cab"] i::before, +.file-icons [aria-label$=".gz"] i::before, +.file-icons [aria-label$=".rar"] i::before, +.file-icons [aria-label$=".tar"] i::before, +.file-icons [aria-label$=".xz"] i::before, +.file-icons [aria-label$=".zip"] i::before, +.file-icons [aria-label$=".zst"] i::before +{ content: 'folder_zip' } + +/* #999 - Disk */ + +.file-icons [aria-label$=".ccd"] i::before, +.file-icons [aria-label$=".dmg"] i::before, +.file-icons [aria-label$=".iso"] i::before, +.file-icons [aria-label$=".mdf"] i::before, +.file-icons [aria-label$=".vdi"] i::before, +.file-icons [aria-label$=".vhd"] i::before, +.file-icons [aria-label$=".vmdk"] i::before, +.file-icons [aria-label$=".wim"] i::before +{ content: 'album' } + +/* #999 - Font */ + +.file-icons [aria-label$=".otf"] i::before, +.file-icons [aria-label$=".ttf"] i::before, +.file-icons [aria-label$=".woff"] i::before, +.file-icons [aria-label$=".woff2"] i::before +{ content: 'font_download' } + +/* Colors */ + +/* General */ + +.file-icons [data-type=audio] i { color: var(--icon-yellow) } +.file-icons [data-type=image] i { color: var(--icon-orange) } +.file-icons [data-type=video] i { color: var(--icon-violet) } +.file-icons [data-type=invalid_link] i { color: var(--icon-red) } + +/* #f00 - Adobe/Oracle */ + +.file-icons [aria-label$=".ai"] i, +.file-icons [aria-label$=".java"] i, +.file-icons [aria-label$=".jar"] i, +.file-icons [aria-label$=".psd"] i, +.file-icons [aria-label$=".rb"] i, +.file-icons [data-type=pdf] i +{ color: var(--icon-red) } + +/* #f90 - Image/Presentation */ + +.file-icons [aria-label$=".html"] i, +.file-icons [aria-label$=".odg"] i, +.file-icons [aria-label$=".odp"] i, +.file-icons [aria-label$=".ppt"] i, +.file-icons [aria-label$=".pptx"] i, +.file-icons [aria-label$=".vue"] i, +.file-icons [aria-label$=".xcf"] i +{ color: var(--icon-orange) } + +/* #ff0 - Various */ + +.file-icons [aria-label$=".css"] i, +.file-icons [aria-label$=".js"] i, +.file-icons [aria-label$=".json"] i, +.file-icons [aria-label$=".zip"] i +{ color: var(--icon-yellow) } + +/* #0f0 - Spreadsheet/Google */ + +.file-icons [aria-label$=".apk"] i, +.file-icons [aria-label$=".dex"] i, +.file-icons [aria-label$=".go"] i, +.file-icons [aria-label$=".ods"] i, +.file-icons [aria-label$=".xls"] i, +.file-icons [aria-label$=".xlsx"] i +{ color: var(--icon-green) } + +/* #00f - Document/Microsoft/Apple/Closed */ + +.file-icons [aria-label$=".aac"] i, +.file-icons [aria-label$=".bat"] i, +.file-icons [aria-label$=".cab"] i, +.file-icons [aria-label$=".cs"] i, +.file-icons [aria-label$=".dmg"] i, +.file-icons [aria-label$=".doc"] i, +.file-icons [aria-label$=".docx"] i, +.file-icons [aria-label$=".emf"] i, +.file-icons [aria-label$=".exe"] i, +.file-icons [aria-label$=".ico"] i, +.file-icons [aria-label$=".mp2"] i, +.file-icons [aria-label$=".mp3"] i, +.file-icons [aria-label$=".mp4"] i, +.file-icons [aria-label$=".mpg"] i, +.file-icons [aria-label$=".msi"] i, +.file-icons [aria-label$=".odt"] i, +.file-icons [aria-label$=".ps1"] i, +.file-icons [aria-label$=".rtf"] i, +.file-icons [aria-label$=".vob"] i, +.file-icons [aria-label$=".wim"] i +{ color: var(--icon-blue) } + +/* #60f - Various */ + +.file-icons [aria-label$=".iso"] i, +.file-icons [aria-label$=".php"] i, +.file-icons [aria-label$=".rar"] i +{ color: var(--icon-violet) } + +/* Overrides */ + +.file-icons [data-dir=true] i { color: var(--icon-blue) } +.file-icons [data-dir=true] i::before { content: 'folder' } +.file-icons [aria-selected=true] i { color: var(--item-selected) } diff --git a/frontend/src/css/listing.css b/frontend/src/css/listing.css index f4e27228..5cb26863 100644 --- a/frontend/src/css/listing.css +++ b/frontend/src/css/listing.css @@ -1,3 +1,7 @@ +#listing { + --item-selected: white; +} + #listing h2 { margin: 0 0 0 0.5em; font-size: .9em; @@ -115,6 +119,41 @@ width: calc(100% - 5vw); } +#listing.mosaic.gallery .item div:first-of-type { + width: 100%; + height: 12em; +} + +#listing.mosaic.gallery .item div:last-of-type { + position: absolute; + bottom: 0.5em; + padding: 1em; + width: calc(100% - 1em); + text-align: center; +} + +#listing.mosaic.gallery .item[data-type=image] div:last-of-type { + color: white; + background: linear-gradient(#0000, #0009); +} + +#listing.mosaic.gallery .item i { + width: 100%; + margin-right: 0; + font-size: 8em; + text-align: center; +} + +#listing.mosaic.gallery .item img { + width: 100%; + height: 100%; +} + +#listing.gallery .size, +#listing.gallery .modified { + display: none; +} + #listing.list { flex-direction: column; width: 100%; @@ -136,7 +175,7 @@ #listing .item[aria-selected=true] { background: var(--blue) !important; - color: #fff !important; + color: var(--item-selected) !important; } #listing.list .item div:first-of-type { @@ -259,7 +298,7 @@ #listing #multiple-selection p, #listing #multiple-selection i { - color: #fff; + color: var(--item-selected); } #listing .item .symlink-icon { diff --git a/frontend/src/css/styles.css b/frontend/src/css/styles.css index 5bcfe703..8b3b608c 100644 --- a/frontend/src/css/styles.css +++ b/frontend/src/css/styles.css @@ -10,6 +10,8 @@ @import "./base.css"; @import "./header.css"; @import "./listing.css"; +@import "./listing-icons.css"; +@import "./upload-files.css"; @import "./dashboard.css"; @import "./login.css"; @@ -365,7 +367,7 @@ main .spinner .bounce2 { @keyframes spin { 100% { - transform: rotate(-360deg); + transform: rotate(360deg); } } diff --git a/frontend/src/css/upload-files.css b/frontend/src/css/upload-files.css new file mode 100644 index 00000000..ce864182 --- /dev/null +++ b/frontend/src/css/upload-files.css @@ -0,0 +1,61 @@ +.upload-files .card.floating { + left: auto; + top: auto; + margin: 0; + right: 0; + bottom: 0; + transform: none; +} + +.upload-files .file { + margin-bottom: 8px; +} + +.upload-files .file .file-name { + font-size: 1.1em; + display: flex; + align-items: center; +} + +.upload-files .file .file-name i { + margin-right: 5px; +} + +.upload-files .file .file-progress { + margin-top: 2px; + width: 100%; + height: 5px; +} + +.upload-files .file .file-progress div { + height: 100%; + background-color: #40c4ff; + width: 0; + transition: 0.2s ease width; + border-radius: 10px; +} + +.upload-files.closed .card-content { + display: none; + padding: 0em 1em 1em 1em; +} + +.upload-files .card .card-title { + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8em; + padding: 1em 1em 0em; +} + +.upload-files.closed .card-title { + font-size: 0.7em; + padding: 0.5em 1em; +} + +@media (max-width: 450px) { + .upload-files .card.floating { + max-width: 100%; + width: 100%; + } +} diff --git a/frontend/src/i18n/en_GB.json b/frontend/src/i18n/en_GB.json index 0a79d385..6de5e5ff 100644 --- a/frontend/src/i18n/en_GB.json +++ b/frontend/src/i18n/en_GB.json @@ -179,6 +179,7 @@ "upload": "Upload", "uploadFile": "File", "uploadFolder": "Folder", + "uploadFiles": "Uploading {files} files...", "uploadMessage": "Select an option to upload.", "write": "Write" }, diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 0b77cdf2..6af06f33 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -12,10 +12,26 @@ import ProfileSettings from "@/views/settings/Profile"; import Shares from "@/views/settings/Shares"; import Errors from "@/views/Errors"; import store from "@/store"; -import { baseURL } from "@/utils/constants"; +import { baseURL, name } from "@/utils/constants"; +import i18n from "@/i18n"; Vue.use(Router); +const titles = { + Login: "sidebar.login", + Share: "buttons.share", + Files: "files.files", + Settings: "sidebar.settings", + ProfileSettings: "settings.profileSettings", + Shares: "settings.shareManagement", + GlobalSettings: "settings.globalSettings", + Users: "settings.users", + User: "settings.user", + Forbidden: "errors.forbidden", + NotFound: "errors.notFound", + InternalServerError: "errors.internal", +}; + const router = new Router({ base: baseURL, mode: "history", @@ -29,7 +45,6 @@ const router = new Router({ return next({ path: "/files" }); } - document.title = "Login"; next(); }, }, @@ -63,7 +78,7 @@ const router = new Router({ children: [ { path: "/settings/profile", - name: "Profile Settings", + name: "ProfileSettings", component: ProfileSettings, }, { @@ -73,7 +88,7 @@ const router = new Router({ }, { path: "/settings/global", - name: "Global Settings", + name: "GlobalSettings", component: GlobalSettings, meta: { requiresAdmin: true, @@ -108,7 +123,7 @@ const router = new Router({ }, { path: "/404", - name: "Not Found", + name: "NotFound", component: Errors, props: { errorCode: 404, @@ -117,7 +132,7 @@ const router = new Router({ }, { path: "/500", - name: "Internal Server Error", + name: "InternalServerError", component: Errors, props: { errorCode: 500, @@ -140,7 +155,8 @@ const router = new Router({ }); router.beforeEach((to, from, next) => { - document.title = to.name; + const title = i18n.t(titles[to.name]); + document.title = title + " - " + name; if (to.matched.some((record) => record.meta.requiresAuth)) { if (!store.getters.isLogged) { diff --git a/frontend/src/store/getters.js b/frontend/src/store/getters.js index 8a08e392..ea520a6b 100644 --- a/frontend/src/store/getters.js +++ b/frontend/src/store/getters.js @@ -10,8 +10,40 @@ const getters = { return 0; } + let totalSize = state.upload.sizes.reduce((a, b) => a + b, 0); + let sum = state.upload.progress.reduce((acc, val) => acc + val); - return Math.ceil((sum / state.upload.size) * 100); + return Math.ceil((sum / totalSize) * 100); + }, + filesInUploadCount: (state) => { + let total = + Object.keys(state.upload.uploads).length + state.upload.queue.length; + return total; + }, + filesInUpload: (state) => { + let files = []; + + for (let index in state.upload.uploads) { + let upload = state.upload.uploads[index]; + let id = upload.id; + let type = upload.type; + let name = upload.file.name; + let size = state.upload.sizes[id]; + let isDir = upload.file.isDir; + let progress = isDir + ? 100 + : Math.ceil((state.upload.progress[id] / size) * 100); + + files.push({ + id, + name, + progress, + type, + isDir, + }); + } + + return files.sort((a, b) => a.progress - b.progress); }, onlyArchivesSelected: (state, getters) => { let extensions = [".zip", ".tar", ".gz", ".bz2", ".xz", ".lz4", ".sz"]; diff --git a/frontend/src/store/modules/upload.js b/frontend/src/store/modules/upload.js index b1a59bc8..4110cb33 100644 --- a/frontend/src/store/modules/upload.js +++ b/frontend/src/store/modules/upload.js @@ -7,7 +7,7 @@ const UPLOADS_LIMIT = 5; const state = { id: 0, - size: 0, + sizes: [], progress: [], queue: [], uploads: {}, @@ -19,12 +19,12 @@ const mutations = { }, reset: (state) => { state.id = 0; - state.size = 0; + state.sizes = []; state.progress = []; }, addJob: (state, item) => { state.queue.push(item); - state.size += item.file.size; + state.sizes[state.id] = item.file.size; state.id++; }, moveJob(state) { @@ -33,6 +33,7 @@ const mutations = { Vue.set(state.uploads, item.id, item); }, removeJob(state, id) { + Vue.delete(state.uploads, id); delete state.uploads[id]; }, }; diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index c5554475..02c8bd2d 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -18,6 +18,7 @@ const theme = window.FileBrowser.Theme; const enableThumbs = window.FileBrowser.EnableThumbs; const resizePreview = window.FileBrowser.ResizePreview; const enableExec = window.FileBrowser.EnableExec; +const origin = window.location.origin; export { name, @@ -39,4 +40,5 @@ export { enableThumbs, resizePreview, enableExec, + origin, }; diff --git a/frontend/src/utils/upload.js b/frontend/src/utils/upload.js index 1ab4bb65..2184072f 100644 --- a/frontend/src/utils/upload.js +++ b/frontend/src/utils/upload.js @@ -70,6 +70,7 @@ export function scanFiles(dt) { isDir: true, size: 0, fullPath: `${directory}${entry.name}`, + name: entry.name, }; contents.push(dir); @@ -99,6 +100,15 @@ export function scanFiles(dt) { }); } +function detectType(mimetype) { + if (mimetype.startsWith("video")) return "video"; + if (mimetype.startsWith("audio")) return "audio"; + if (mimetype.startsWith("image")) return "image"; + if (mimetype.startsWith("pdf")) return "pdf"; + if (mimetype.startsWith("text")) return "text"; + return "blob"; +} + export function handleFiles(files, base, overwrite = false) { for (let i = 0; i < files.length; i++) { let id = store.state.upload.id; @@ -120,6 +130,7 @@ export function handleFiles(files, base, overwrite = false) { path, file, overwrite, + ...(!file.isDir && { type: detectType(file.type) }), }; store.dispatch("upload/upload", item); diff --git a/frontend/src/utils/url.js b/frontend/src/utils/url.js index 8346bb03..33d124a2 100644 --- a/frontend/src/utils/url.js +++ b/frontend/src/utils/url.js @@ -1,4 +1,4 @@ -function removeLastDir(url) { +export function removeLastDir(url) { var arr = url.split("/"); if (arr.pop() === "") { arr.pop(); @@ -9,7 +9,7 @@ function removeLastDir(url) { // this code borrow from mozilla // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples -function encodeRFC5987ValueChars(str) { +export function encodeRFC5987ValueChars(str) { return ( encodeURIComponent(str) // Note that although RFC3986 reserves "!", RFC5987 does not, @@ -22,7 +22,7 @@ function encodeRFC5987ValueChars(str) { ); } -function encodePath(str) { +export function encodePath(str) { return str .split("/") .map((v) => encodeURIComponent(v)) @@ -30,7 +30,7 @@ function encodePath(str) { } export default { - encodeRFC5987ValueChars: encodeRFC5987ValueChars, - removeLastDir: removeLastDir, - encodePath: encodePath, + encodeRFC5987ValueChars, + removeLastDir, + encodePath, }; diff --git a/frontend/src/views/Errors.vue b/frontend/src/views/Errors.vue index b010906d..43746105 100644 --- a/frontend/src/views/Errors.vue +++ b/frontend/src/views/Errors.vue @@ -38,15 +38,8 @@ export default { }, props: ["errorCode", "showHeader"], computed: { - code() { - return this.errorCode === "0" || - this.errorCode === "404" || - this.errorCode === "403" - ? parseInt(this.errorCode) - : 500; - }, info() { - return errors[this.code]; + return errors[this.errorCode] ? errors[this.errorCode] : errors[500]; }, }, }; diff --git a/frontend/src/views/Files.vue b/frontend/src/views/Files.vue index 1808d218..e12ac1dc 100644 --- a/frontend/src/views/Files.vue +++ b/frontend/src/views/Files.vue @@ -4,7 +4,7 @@ - +
-
+
@@ -10,6 +10,8 @@ + +
@@ -19,6 +21,7 @@ import Sidebar from "@/components/Sidebar"; import Prompts from "@/components/prompts/Prompts"; import ContextMenu from "@/components/files/ContextMenu"; import Shell from "@/components/Shell"; +import UploadFiles from "../components/prompts/UploadFiles"; import { enableExec } from "@/utils/constants"; export default { @@ -28,6 +31,7 @@ export default { Prompts, ContextMenu, Shell, + UploadFiles, }, computed: { ...mapGetters(["isLogged", "isVisibleContext", "progress"]), diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue index 50d4392d..27bb5847 100644 --- a/frontend/src/views/Login.vue +++ b/frontend/src/views/Login.vue @@ -8,6 +8,8 @@ @@ -69,6 +71,8 @@ export default { }; }, mounted() { + this.focusUsername(); + if (!recaptcha) return; window.grecaptcha.ready(function () { @@ -78,6 +82,9 @@ export default { }); }, methods: { + focusUsername() { + this.$refs.username.focus(); + }, toggleMode() { this.createMode = !this.createMode; }, diff --git a/frontend/src/views/Share.vue b/frontend/src/views/Share.vue index c1b79e65..5e9feb2d 100644 --- a/frontend/src/views/Share.vue +++ b/frontend/src/views/Share.vue @@ -30,7 +30,7 @@
-
+
- +
-
+
import { mapState, mapMutations, mapGetters } from "vuex"; import { pub as api } from "@/api"; -import { baseURL } from "@/utils/constants"; import filesize from "filesize"; import moment from "moment"; @@ -231,21 +230,10 @@ export default { return "insert_drive_file"; }, link: function () { - let queryArg = ""; - if (this.token !== "") { - queryArg = `?token=${this.token}`; - } - - const path = this.$route.path.split("/").splice(2).join("/"); - return `${baseURL}/api/public/dl/${path}${queryArg}`; + return api.getDownloadURL(this.req); }, inlineLink: function () { - let url = new URL(this.fullLink); - url.searchParams.set("inline", "true"); - return url.href; - }, - fullLink: function () { - return window.location.origin + this.link; + return api.getDownloadURL(this.req, true); }, humanSize: function () { if (this.req.isDir) { @@ -287,11 +275,12 @@ export default { try { let file = await api.fetch(url, this.password); + file.hash = this.hash; this.token = file.token || ""; this.updateRequest(file); - document.title = `${file.name} - ${this.$route.name}`; + document.title = `${file.name} - ${document.title}`; } catch (e) { this.error = e; } finally { diff --git a/frontend/src/views/files/Listing.vue b/frontend/src/views/files/Listing.vue index 7d541903..4c777115 100644 --- a/frontend/src/views/files/Listing.vue +++ b/frontend/src/views/files/Listing.vue @@ -74,7 +74,7 @@ @action="$store.commit('toggleShell')" /> @@ -173,7 +173,12 @@ multiple />
-
+
@@ -234,6 +239,7 @@ v-bind:modified="item.modified" v-bind:type="item.type" v-bind:size="item.size" + v-bind:path="item.path" >
@@ -253,6 +259,7 @@ v-bind:modified="item.modified" v-bind:type="item.type" v-bind:size="item.size" + v-bind:path="item.path" >
@@ -317,6 +324,7 @@ export default { data: function () { return { showLimit: 50, + columnWidth: 280, dragCounter: 0, width: window.innerWidth, itemWeight: 0, @@ -390,6 +398,14 @@ export default { return "arrow_upward"; }, + viewIcon() { + const icons = { + list: "view_module", + mosaic: "grid_view", + "mosaic gallery": "view_list", + }; + return icons[this.user.viewMode]; + }, headerButtons() { return { upload: this.user.perm.create, @@ -632,7 +648,7 @@ export default { colunmsResize() { // Update the columns size based on the window width. let columns = Math.floor( - document.querySelector("main").offsetWidth / 300 + document.querySelector("main").offsetWidth / this.columnWidth ); let items = css(["#listing.mosaic .item", ".mosaic#listing .item"]); if (columns === 0) columns = 1; @@ -886,9 +902,15 @@ export default { switchView: async function () { this.$store.commit("closeHovers"); + const modes = { + list: "mosaic", + mosaic: "mosaic gallery", + "mosaic gallery": "list", + }; + const data = { id: this.user.id, - viewMode: this.user.viewMode === "mosaic" ? "list" : "mosaic", + viewMode: modes[this.user.viewMode] || "list", }; users.update(data, ["viewMode"]).catch(this.$showError); diff --git a/frontend/src/views/files/Preview.vue b/frontend/src/views/files/Preview.vue index 4e699dca..d08ac5aa 100644 --- a/frontend/src/views/files/Preview.vue +++ b/frontend/src/views/files/Preview.vue @@ -103,7 +103,7 @@ @@ -145,7 +145,7 @@