Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4cb813ddf | ||
|
|
4d0a68e787 | ||
|
|
a744bd224f | ||
|
|
da1fe7c9d7 | ||
|
|
7fabadc871 | ||
|
|
c3079d30e2 | ||
|
|
6a31af6c0a | ||
|
|
21d361ad30 | ||
|
|
d574fb6d1a | ||
|
|
bb4bb508a9 | ||
|
|
edd808f124 | ||
|
|
cdcd9a313a | ||
|
|
d0c3aeace1 | ||
|
|
9484454584 | ||
|
|
bd3c1941ff | ||
|
|
01f7842a18 | ||
|
|
38f7788255 | ||
|
|
c1fb4004f7 | ||
|
|
584b706b1e | ||
|
|
ecdd684bf1 | ||
|
|
36af01daa6 | ||
|
|
d0c3b8033d | ||
|
|
aa00c1c89c | ||
|
|
a404fb043d | ||
|
|
95fec7f694 | ||
|
|
5994224468 | ||
|
|
374bbd3ec1 | ||
|
|
2c97573301 | ||
|
|
70eba7ecc9 | ||
|
|
7a4d0c0c08 | ||
|
|
8838a09cf5 |
@@ -1,4 +1,5 @@
|
|||||||
*
|
*
|
||||||
!docker/*
|
!docker/*
|
||||||
|
!healthcheck.sh
|
||||||
!docker_config.json
|
!docker_config.json
|
||||||
!filebrowser
|
!filebrowser
|
||||||
16
.github/workflows/main.yaml
vendored
16
.github/workflows/main.yaml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
- run: make lint-frontend
|
- run: make lint-frontend
|
||||||
lint-backend:
|
lint-backend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.20.6
|
go-version: 1.21.0
|
||||||
- run: make lint-backend
|
- run: make lint-backend
|
||||||
lint-commits:
|
lint-commits:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
- run: make lint-commits
|
- run: make lint-commits
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
- run: make test-frontend
|
- run: make test-frontend
|
||||||
test-backend:
|
test-backend:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.20.6
|
go-version: 1.21.0
|
||||||
- run: make test-backend
|
- run: make test-backend
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -76,10 +76,10 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.20.6
|
go-version: 1.21.0
|
||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '18'
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
@@ -95,6 +95,6 @@ jobs:
|
|||||||
uses: goreleaser/goreleaser-action@v2
|
uses: goreleaser/goreleaser-action@v2
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: release --rm-dist
|
args: release --clean
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -29,3 +29,6 @@ yarn-error.log*
|
|||||||
*.sw*
|
*.sw*
|
||||||
bin/
|
bin/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
/frontend/dist/*
|
||||||
|
!/frontend/dist/.gitkeep
|
||||||
|
|||||||
@@ -3,33 +3,33 @@ project_name: filebrowser
|
|||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
|
||||||
build:
|
builds:
|
||||||
env:
|
- env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/filebrowser/filebrowser/v2/version.Version={{ .Version }} -X github.com/filebrowser/filebrowser/v2/version.CommitSHA={{ .ShortCommit }}
|
- -s -w -X github.com/filebrowser/filebrowser/v2/version.Version={{ .Version }} -X github.com/filebrowser/filebrowser/v2/version.CommitSHA={{ .ShortCommit }}
|
||||||
main: main.go
|
main: main.go
|
||||||
binary: filebrowser
|
binary: filebrowser
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
- linux
|
- linux
|
||||||
- windows
|
- windows
|
||||||
- freebsd
|
- freebsd
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- 386
|
- 386
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
- riscv64
|
- riscv64
|
||||||
goarm:
|
goarm:
|
||||||
- 5
|
- 5
|
||||||
- 6
|
- 6
|
||||||
- 7
|
- 7
|
||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: 386
|
goarch: 386
|
||||||
- goos: freebsd
|
- goos: freebsd
|
||||||
goarch: arm
|
goarch: arm
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
-
|
-
|
||||||
@@ -186,7 +186,7 @@ docker_manifests:
|
|||||||
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
||||||
brews:
|
brews:
|
||||||
- name: filebrowser
|
- name: filebrowser
|
||||||
tap:
|
repository:
|
||||||
owner: filebrowser
|
owner: filebrowser
|
||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
folder: Formula
|
folder: Formula
|
||||||
|
|||||||
59
CHANGELOG.md
59
CHANGELOG.md
@@ -2,6 +2,65 @@
|
|||||||
|
|
||||||
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.
|
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.26.0](https://github.com/filebrowser/filebrowser/compare/v2.25.0...v2.26.0) (2023-11-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add modern greek translation ([#2778](https://github.com/filebrowser/filebrowser/issues/2778)) ([c3079d3](https://github.com/filebrowser/filebrowser/commit/c3079d30e22385d7e677f172324cd9cbab6487ce))
|
||||||
|
* make user session timeout configurable ([#2753](https://github.com/filebrowser/filebrowser/issues/2753)) ([7fabadc](https://github.com/filebrowser/filebrowser/commit/7fabadc871ea91ea22fe9454e2ca4b33e5c211be))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* avoid the front-end calling api/renew loop ([#2792](https://github.com/filebrowser/filebrowser/issues/2792)) ([edd808f](https://github.com/filebrowser/filebrowser/commit/edd808f124f4ada99bcbe4bca98ddbe20e5a424c))
|
||||||
|
* disable static resource files listing ([da1fe7c](https://github.com/filebrowser/filebrowser/commit/da1fe7c9d76a9c6a25bfa19ebd6cf8023eff5d62))
|
||||||
|
* display file size as base 2 (KiB instead of KB) ([#2779](https://github.com/filebrowser/filebrowser/issues/2779)) ([cdcd9a3](https://github.com/filebrowser/filebrowser/commit/cdcd9a313aa50c2e6806a182b6838462d42dcafe))
|
||||||
|
* goreleaser yaml ([4d0a68e](https://github.com/filebrowser/filebrowser/commit/4d0a68e7875274f4c939f2bfa15739a9b0ecf70a))
|
||||||
|
* revert fetchURL changes in auth (Fixes [#2729](https://github.com/filebrowser/filebrowser/issues/2729)) ([#2739](https://github.com/filebrowser/filebrowser/issues/2739)) ([bd3c194](https://github.com/filebrowser/filebrowser/commit/bd3c1941ff8289a5dae877e08f7e25fa9b2a92c5))
|
||||||
|
* solve docker build failed issue ([#2797](https://github.com/filebrowser/filebrowser/issues/2797)) ([6a31af6](https://github.com/filebrowser/filebrowser/commit/6a31af6c0a144128af865d802c8039fa5250e946))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **deps-dev:** bump postcss from 8.4.27 to 8.4.31 in /frontend ([#2749](https://github.com/filebrowser/filebrowser/issues/2749)) ([21d361a](https://github.com/filebrowser/filebrowser/commit/21d361ad308d109d2a6b323597019aaa09ce1781))
|
||||||
|
* **deps:** bump @babel/traverse in /frontend ([#2775](https://github.com/filebrowser/filebrowser/issues/2775)) ([bb4bb50](https://github.com/filebrowser/filebrowser/commit/bb4bb508a9d71516e8fa80b3a6285fe002a059d2))
|
||||||
|
* **deps:** bump golang.org/x/image from 0.5.0 to 0.10.0 ([#2800](https://github.com/filebrowser/filebrowser/issues/2800)) ([a744bd2](https://github.com/filebrowser/filebrowser/commit/a744bd224f0ff1efc53ab94481fa76ef68788df1))
|
||||||
|
* **deps:** bump golang.org/x/net from 0.11.0 to 0.17.0 ([#2758](https://github.com/filebrowser/filebrowser/issues/2758)) ([d574fb6](https://github.com/filebrowser/filebrowser/commit/d574fb6d1af41ec31778b0f402674e5111a7875d))
|
||||||
|
* fix deprecated goreleaser config options ([38f7788](https://github.com/filebrowser/filebrowser/commit/38f77882559133b9ff330cfb955a9d4ea4728cf8))
|
||||||
|
|
||||||
|
## [2.25.0](https://github.com/filebrowser/filebrowser/compare/v2.24.2...v2.25.0) (2023-09-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add new folder button to move/create dialogs ([#2667](https://github.com/filebrowser/filebrowser/issues/2667)) ([5994224](https://github.com/filebrowser/filebrowser/commit/599422446849fa37d5ab448bbf464afb7304b99d))
|
||||||
|
* added shell resizing ([#2648](https://github.com/filebrowser/filebrowser/issues/2648)) ([584b706](https://github.com/filebrowser/filebrowser/commit/584b706b1e310297acc2580c60442ff5c11ae432))
|
||||||
|
* implement abort upload functionality ([#2673](https://github.com/filebrowser/filebrowser/issues/2673)) ([a404fb0](https://github.com/filebrowser/filebrowser/commit/a404fb043da2573bf04385863b2d34b1f918b8e1))
|
||||||
|
* implement upload speed calculation and ETA estimation ([#2677](https://github.com/filebrowser/filebrowser/issues/2677)) ([ecdd684](https://github.com/filebrowser/filebrowser/commit/ecdd684bf1d537a4591caa38348102b61dd51e5d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* refactor path resolution logic for project root ([#2674](https://github.com/filebrowser/filebrowser/issues/2674)) ([95fec7f](https://github.com/filebrowser/filebrowser/commit/95fec7f69430c108e5cf95c428db9d671cd97a94))
|
||||||
|
* tus upload with cloudflare proxy ([36af01d](https://github.com/filebrowser/filebrowser/commit/36af01daa6e04005ce3d18985eebaeef06f7393d)), closes [#2593](https://github.com/filebrowser/filebrowser/issues/2593)
|
||||||
|
|
||||||
|
|
||||||
|
### Refactorings
|
||||||
|
|
||||||
|
* migrate frontend tooling to vite 4 ([#2645](https://github.com/filebrowser/filebrowser/issues/2645)) ([8838a09](https://github.com/filebrowser/filebrowser/commit/8838a09cf5104deac22b6143050588040c6825e6))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* bump go version to 1.21.0 ([#2672](https://github.com/filebrowser/filebrowser/issues/2672)) ([2c97573](https://github.com/filebrowser/filebrowser/commit/2c97573301a1b13179678fb7f9bd8316539ecdff))
|
||||||
|
* bump node version to 18 ([#2671](https://github.com/filebrowser/filebrowser/issues/2671)) ([70eba7e](https://github.com/filebrowser/filebrowser/commit/70eba7ecc9d19545c0899ae40eb3897a7c48562f))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance improvements
|
||||||
|
|
||||||
|
* **backend:** optimize subtitles detection performance ([#2637](https://github.com/filebrowser/filebrowser/issues/2637)) ([374bbd3](https://github.com/filebrowser/filebrowser/commit/374bbd3ec199fddbe491ab2b74e520a10a73e54b))
|
||||||
|
|
||||||
### [2.24.2](https://github.com/filebrowser/filebrowser/compare/v2.24.1...v2.24.2) (2023-08-08)
|
### [2.24.2](https://github.com/filebrowser/filebrowser/compare/v2.24.1...v2.24.2) (2023-08-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,12 @@
|
|||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
url: https://demo.filebrowser.org/
|
||||||
|
|
||||||
|
credentials: `demo`/`demo`
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Please refer to our docs at [https://filebrowser.org/features](https://filebrowser.org/features)
|
Please refer to our docs at [https://filebrowser.org/features](https://filebrowser.org/features)
|
||||||
|
|||||||
@@ -29,20 +29,21 @@ const PermDir = 0755
|
|||||||
// FileInfo describes a file.
|
// FileInfo describes a file.
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
*Listing
|
*Listing
|
||||||
Fs afero.Fs `json:"-"`
|
Fs afero.Fs `json:"-"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Size int64 `json:"size"`
|
Size int64 `json:"size"`
|
||||||
Extension string `json:"extension"`
|
Extension string `json:"extension"`
|
||||||
ModTime time.Time `json:"modified"`
|
ModTime time.Time `json:"modified"`
|
||||||
Mode os.FileMode `json:"mode"`
|
Mode os.FileMode `json:"mode"`
|
||||||
IsDir bool `json:"isDir"`
|
IsDir bool `json:"isDir"`
|
||||||
IsSymlink bool `json:"isSymlink"`
|
IsSymlink bool `json:"isSymlink"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Subtitles []string `json:"subtitles,omitempty"`
|
Subtitles []string `json:"subtitles,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
Checksums map[string]string `json:"checksums,omitempty"`
|
Checksums map[string]string `json:"checksums,omitempty"`
|
||||||
Token string `json:"token,omitempty"`
|
Token string `json:"token,omitempty"`
|
||||||
|
currentDir []os.FileInfo `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileOptions are the options when getting a file info.
|
// FileOptions are the options when getting a file info.
|
||||||
@@ -294,13 +295,21 @@ func (i *FileInfo) detectSubtitles() {
|
|||||||
// detect multiple languages. Base*.vtt
|
// detect multiple languages. Base*.vtt
|
||||||
// TODO: give subtitles descriptive names (lang) and track attributes
|
// TODO: give subtitles descriptive names (lang) and track attributes
|
||||||
parentDir := strings.TrimRight(i.Path, i.Name)
|
parentDir := strings.TrimRight(i.Path, i.Name)
|
||||||
dir, err := afero.ReadDir(i.Fs, parentDir)
|
var dir []os.FileInfo
|
||||||
if err == nil {
|
if len(i.currentDir) > 0 {
|
||||||
base := strings.TrimSuffix(i.Name, ext)
|
dir = i.currentDir
|
||||||
for _, f := range dir {
|
} else {
|
||||||
if !f.IsDir() && strings.HasPrefix(f.Name(), base) && strings.HasSuffix(f.Name(), ".vtt") {
|
var err error
|
||||||
i.Subtitles = append(i.Subtitles, path.Join(parentDir, f.Name()))
|
dir, err = afero.ReadDir(i.Fs, parentDir)
|
||||||
}
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base := strings.TrimSuffix(i.Name, ext)
|
||||||
|
for _, f := range dir {
|
||||||
|
if !f.IsDir() && strings.HasPrefix(f.Name(), base) && strings.HasSuffix(f.Name(), ".vtt") {
|
||||||
|
i.Subtitles = append(i.Subtitles, path.Join(parentDir, f.Name()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,15 +349,16 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
file := &FileInfo{
|
file := &FileInfo{
|
||||||
Fs: i.Fs,
|
Fs: i.Fs,
|
||||||
Name: name,
|
Name: name,
|
||||||
Size: f.Size(),
|
Size: f.Size(),
|
||||||
ModTime: f.ModTime(),
|
ModTime: f.ModTime(),
|
||||||
Mode: f.Mode(),
|
Mode: f.Mode(),
|
||||||
IsDir: f.IsDir(),
|
IsDir: f.IsDir(),
|
||||||
IsSymlink: isSymlink,
|
IsSymlink: isSymlink,
|
||||||
Extension: filepath.Ext(name),
|
Extension: filepath.Ext(name),
|
||||||
Path: fPath,
|
Path: fPath,
|
||||||
|
currentDir: dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.IsDir {
|
if file.IsDir {
|
||||||
|
|||||||
20
frontend/.eslintrc.json
Normal file
20
frontend/.eslintrc.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/eslint-config-prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"vue/multi-word-component-names": "off",
|
||||||
|
"vue/no-reserved-component-names": "warn",
|
||||||
|
"vue/no-mutating-props": "warn"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
frontend/.prettierignore
Normal file
2
frontend/.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Ignore artifacts:
|
||||||
|
dist
|
||||||
3
frontend/.prettierrc.json
Normal file
3
frontend/.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ["@vue/app"],
|
|
||||||
};
|
|
||||||
4
frontend/dist/.gitignore
vendored
4
frontend/dist/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
# Ignore everything in this directory
|
|
||||||
*
|
|
||||||
# Except this file
|
|
||||||
!.gitignore
|
|
||||||
0
frontend/dist/.gitkeep
vendored
Normal file
0
frontend/dist/.gitkeep
vendored
Normal file
192
frontend/index.html
Normal file
192
frontend/index.html
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>File Browser</title>
|
||||||
|
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/img/icons/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/img/icons/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||||
|
<link
|
||||||
|
rel="manifest"
|
||||||
|
id="manifestPlaceholder"
|
||||||
|
crossorigin="use-credentials"
|
||||||
|
/>
|
||||||
|
<meta name="theme-color" content="#2979ff" />
|
||||||
|
|
||||||
|
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="assets" />
|
||||||
|
<link rel="apple-touch-icon" href="/img/icons/apple-touch-icon.png" />
|
||||||
|
|
||||||
|
<!-- Add to home screen for Windows -->
|
||||||
|
<meta
|
||||||
|
name="msapplication-TileImage"
|
||||||
|
content="/img/icons/mstile-144x144.png"
|
||||||
|
/>
|
||||||
|
<meta name="msapplication-TileColor" content="#2979ff" />
|
||||||
|
|
||||||
|
<!-- Inject Some Variables and generate the manifest json -->
|
||||||
|
<script>
|
||||||
|
// We can assign JSON directly
|
||||||
|
window.FileBrowser = {
|
||||||
|
AuthMethod: "json",
|
||||||
|
BaseURL: "",
|
||||||
|
CSS: false,
|
||||||
|
Color: "",
|
||||||
|
DisableExternal: false,
|
||||||
|
DisableUsedPercentage: false,
|
||||||
|
EnableExec: true,
|
||||||
|
EnableThumbs: true,
|
||||||
|
LoginPage: true,
|
||||||
|
Name: "",
|
||||||
|
NoAuth: false,
|
||||||
|
ReCaptcha: false,
|
||||||
|
ResizePreview: true,
|
||||||
|
Signup: false,
|
||||||
|
StaticURL: "",
|
||||||
|
Theme: "",
|
||||||
|
TusSettings: { chunkSize: 10485760, retryCount: 5 },
|
||||||
|
Version: "(untracked)",
|
||||||
|
};
|
||||||
|
// Global function to prepend static url
|
||||||
|
window.__prependStaticUrl = (url) => {
|
||||||
|
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
|
||||||
|
};
|
||||||
|
var dynamicManifest = {
|
||||||
|
name: window.FileBrowser.Name || "File Browser",
|
||||||
|
short_name: window.FileBrowser.Name || "File Browser",
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: window.__prependStaticUrl(
|
||||||
|
"/img/icons/android-chrome-192x192.png"
|
||||||
|
),
|
||||||
|
sizes: "192x192",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: window.__prependStaticUrl(
|
||||||
|
"/img/icons/android-chrome-512x512.png"
|
||||||
|
),
|
||||||
|
sizes: "512x512",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
start_url: window.location.origin + window.FileBrowser.BaseURL,
|
||||||
|
display: "standalone",
|
||||||
|
background_color: "#ffffff",
|
||||||
|
theme_color: window.FileBrowser.Color || "#455a64",
|
||||||
|
};
|
||||||
|
|
||||||
|
const stringManifest = JSON.stringify(dynamicManifest);
|
||||||
|
const blob = new Blob([stringManifest], { type: "application/json" });
|
||||||
|
const manifestURL = URL.createObjectURL(blob);
|
||||||
|
document
|
||||||
|
.querySelector("#manifestPlaceholder")
|
||||||
|
.setAttribute("href", manifestURL);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#loading {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: 0.1s ease opacity;
|
||||||
|
-webkit-transition: 0.1s ease opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading.done {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner {
|
||||||
|
width: 70px;
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner > div {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce1 {
|
||||||
|
-webkit-animation-delay: -0.32s;
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce2 {
|
||||||
|
-webkit-animation-delay: -0.16s;
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes sk-bouncedelay {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sk-bouncedelay {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<div id="loading">
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="bounce1"></div>
|
||||||
|
<div class="bounce2"></div>
|
||||||
|
<div class="bounce3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
frontend/jsconfig.json
Normal file
10
frontend/jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
29113
frontend/package-lock.json
generated
29113
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,71 +2,59 @@
|
|||||||
"name": "filebrowser-frontend",
|
"name": "filebrowser-frontend",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"dev": "vite dev",
|
||||||
"build": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --no-clean",
|
"serve": "vite serve",
|
||||||
"lint": "npx vue-cli-service lint --no-fix --max-warnings=0",
|
"build": "vite build",
|
||||||
"fix": "npx vue-cli-service lint",
|
"watch": "vite build --watch",
|
||||||
"watch": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --watch --no-clean"
|
"clean": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitkeep' -exec rm -r {} +",
|
||||||
|
"lint": "eslint --ext .vue,.js src/",
|
||||||
|
"lint:fix": "eslint --ext .vue,.js --fix src/",
|
||||||
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ace-builds": "^1.4.7",
|
"ace-builds": "^1.23.4",
|
||||||
"clipboard": "^2.0.4",
|
"clipboard": "^2.0.11",
|
||||||
"core-js": "^3.9.1",
|
"core-js": "^3.32.0",
|
||||||
"css-vars-ponyfill": "^2.4.3",
|
"css-vars-ponyfill": "^2.4.8",
|
||||||
"js-base64": "^2.5.1",
|
"filesize": "^10.0.8",
|
||||||
|
"js-base64": "^3.7.5",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"material-icons": "^1.10.5",
|
"material-icons": "^1.13.9",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
"noty": "^3.2.0-beta",
|
"noty": "^3.2.0-beta",
|
||||||
"pretty-bytes": "^6.0.0",
|
"pretty-bytes": "^6.1.1",
|
||||||
"qrcode.vue": "^1.7.0",
|
"qrcode.vue": "^1.7.0",
|
||||||
"tus-js-client": "^3.1.0",
|
"tus-js-client": "^3.1.1",
|
||||||
"utif": "^3.1.0",
|
"utif": "^3.1.0",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.7.14",
|
||||||
"vue-async-computed": "^3.9.0",
|
"vue-async-computed": "^3.9.0",
|
||||||
"vue-i18n": "^8.15.3",
|
"vue-i18n": "^8.28.2",
|
||||||
"vue-lazyload": "^1.3.3",
|
"vue-lazyload": "^1.3.5",
|
||||||
"vue-router": "^3.1.3",
|
"vue-router": "^3.6.5",
|
||||||
"vue-simple-progress": "^1.1.1",
|
"vue-simple-progress": "^1.1.1",
|
||||||
"vuex": "^3.1.2",
|
"vuex": "^3.6.2",
|
||||||
"vuex-router-sync": "^5.0.0",
|
"vuex-router-sync": "^5.0.0",
|
||||||
"whatwg-fetch": "^3.6.2"
|
"whatwg-fetch": "^3.6.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.1.2",
|
"@vitejs/plugin-legacy": "^4.1.1",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
"@vitejs/plugin-vue2": "^2.2.0",
|
||||||
"@vue/cli-service": "^4.1.2",
|
"@vue/eslint-config-prettier": "^8.0.0",
|
||||||
"@vue/eslint-config-prettier": "^6.0.0",
|
"autoprefixer": "^10.4.14",
|
||||||
"babel-eslint": "^10.1.0",
|
"eslint": "^8.46.0",
|
||||||
"compression-webpack-plugin": "^6.0.3",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"eslint": "^6.7.2",
|
"eslint-plugin-vue": "^9.16.1",
|
||||||
"eslint-plugin-prettier": "^3.3.1",
|
"jsdom": "^22.1.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"postcss": "^8.4.31",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.6.10"
|
"terser": "^5.19.2",
|
||||||
},
|
"vite": "^4.4.9",
|
||||||
"eslintConfig": {
|
"vite-plugin-compression2": "^0.10.3",
|
||||||
"root": true,
|
"vite-plugin-rewrite-all": "^1.0.1"
|
||||||
"env": {
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"plugin:vue/essential",
|
|
||||||
"eslint:recommended",
|
|
||||||
"@vue/prettier"
|
|
||||||
],
|
|
||||||
"rules": {},
|
|
||||||
"parserOptions": {
|
|
||||||
"parser": "babel-eslint"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"postcss": {
|
|
||||||
"plugins": {
|
|
||||||
"autoprefixer": {}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
|
|||||||
5
frontend/postcss.config.cjs
Normal file
5
frontend/postcss.config.cjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
0
frontend/public/.gitkeep
Normal file
0
frontend/public/.gitkeep
Normal file
@@ -1,144 +1,193 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||||
|
/>
|
||||||
|
|
||||||
[{[ if .ReCaptcha -]}]
|
[{[ if .ReCaptcha -]}]
|
||||||
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
||||||
[{[ end ]}]
|
[{[ end ]}]
|
||||||
|
|
||||||
<title>[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]</title>
|
<title>
|
||||||
|
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]
|
||||||
|
</title>
|
||||||
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png">
|
<link
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png">
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||||
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials">
|
<link
|
||||||
<meta name="theme-color" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
rel="manifest"
|
||||||
|
id="manifestPlaceholder"
|
||||||
|
crossorigin="use-credentials"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="theme-color"
|
||||||
|
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||||
<meta name="apple-mobile-web-app-title" content="assets">
|
<meta name="apple-mobile-web-app-title" content="assets" />
|
||||||
<link rel="apple-touch-icon" href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png">
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Add to home screen for Windows -->
|
<!-- Add to home screen for Windows -->
|
||||||
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png">
|
<meta
|
||||||
<meta name="msapplication-TileColor" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
name="msapplication-TileImage"
|
||||||
|
content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="msapplication-TileColor"
|
||||||
|
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Inject Some Variables and generate the manifest json -->
|
<!-- Inject Some Variables and generate the manifest json -->
|
||||||
<script>
|
<script>
|
||||||
window.FileBrowser = JSON.parse('[{[ .Json ]}]');
|
// We can assign JSON directly
|
||||||
|
window.FileBrowser = [{[ .Json ]}];
|
||||||
|
// Global function to prepend static url
|
||||||
|
window.__prependStaticUrl = (url) => {
|
||||||
|
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
|
||||||
|
};
|
||||||
|
var dynamicManifest = {
|
||||||
|
name: window.FileBrowser.Name || "File Browser",
|
||||||
|
short_name: window.FileBrowser.Name || "File Browser",
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: window.__prependStaticUrl("/img/icons/android-chrome-192x192.png"),
|
||||||
|
sizes: "192x192",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: window.__prependStaticUrl("/img/icons/android-chrome-512x512.png"),
|
||||||
|
sizes: "512x512",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
start_url: window.location.origin + window.FileBrowser.BaseURL,
|
||||||
|
display: "standalone",
|
||||||
|
background_color: "#ffffff",
|
||||||
|
theme_color: window.FileBrowser.Color || "#455a64",
|
||||||
|
};
|
||||||
|
|
||||||
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
const stringManifest = JSON.stringify(dynamicManifest);
|
||||||
var dynamicManifest = {
|
const blob = new Blob([stringManifest], { type: "application/json" });
|
||||||
"name": window.FileBrowser.Name || 'File Browser',
|
const manifestURL = URL.createObjectURL(blob);
|
||||||
"short_name": window.FileBrowser.Name || 'File Browser',
|
document
|
||||||
"icons": [
|
.querySelector("#manifestPlaceholder")
|
||||||
{
|
.setAttribute("href", manifestURL);
|
||||||
"src": fullStaticURL + "/img/icons/android-chrome-192x192.png",
|
</script>
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png"
|
<style>
|
||||||
},
|
#loading {
|
||||||
{
|
position: fixed;
|
||||||
"src": fullStaticURL + "/img/icons/android-chrome-512x512.png",
|
top: 0;
|
||||||
"sizes": "512x512",
|
left: 0;
|
||||||
"type": "image/png"
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: 0.1s ease opacity;
|
||||||
|
-webkit-transition: 0.1s ease opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading.done {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner {
|
||||||
|
width: 70px;
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner > div {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce1 {
|
||||||
|
-webkit-animation-delay: -0.32s;
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce2 {
|
||||||
|
-webkit-animation-delay: -0.16s;
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes sk-bouncedelay {
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
-webkit-transform: scale(0);
|
||||||
}
|
}
|
||||||
],
|
40% {
|
||||||
"start_url": window.location.origin + window.FileBrowser.BaseURL,
|
-webkit-transform: scale(1);
|
||||||
"display": "standalone",
|
}
|
||||||
"background_color": "#ffffff",
|
}
|
||||||
"theme_color": window.FileBrowser.Color || "#455a64"
|
|
||||||
}
|
|
||||||
|
|
||||||
const stringManifest = JSON.stringify(dynamicManifest);
|
@keyframes sk-bouncedelay {
|
||||||
const blob = new Blob([stringManifest], {type: 'application/json'});
|
0%,
|
||||||
const manifestURL = URL.createObjectURL(blob);
|
80%,
|
||||||
document.querySelector('#manifestPlaceholder').setAttribute('href', manifestURL);
|
100% {
|
||||||
</script>
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
<style>
|
<div id="loading">
|
||||||
#loading {
|
<div class="spinner">
|
||||||
position: fixed;
|
<div class="bounce1"></div>
|
||||||
top: 0;
|
<div class="bounce2"></div>
|
||||||
left: 0;
|
<div class="bounce3"></div>
|
||||||
width: 100%;
|
</div>
|
||||||
height: 100%;
|
|
||||||
background: #fff;
|
|
||||||
z-index: 9999;
|
|
||||||
transition: .1s ease opacity;
|
|
||||||
-webkit-transition: .1s ease opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading.done {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading .spinner {
|
|
||||||
width: 70px;
|
|
||||||
text-align: center;
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translate(-50%, -50%);
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading .spinner > div {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
background-color: #333;
|
|
||||||
border-radius: 100%;
|
|
||||||
display: inline-block;
|
|
||||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
|
||||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading .spinner .bounce1 {
|
|
||||||
-webkit-animation-delay: -0.32s;
|
|
||||||
animation-delay: -0.32s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#loading .spinner .bounce2 {
|
|
||||||
-webkit-animation-delay: -0.16s;
|
|
||||||
animation-delay: -0.16s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes sk-bouncedelay {
|
|
||||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
|
||||||
40% { -webkit-transform: scale(1.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes sk-bouncedelay {
|
|
||||||
0%, 80%, 100% {
|
|
||||||
-webkit-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
} 40% {
|
|
||||||
-webkit-transform: scale(1.0);
|
|
||||||
transform: scale(1.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
<div id="loading">
|
|
||||||
<div class="spinner">
|
|
||||||
<div class="bounce1"></div>
|
|
||||||
<div class="bounce2"></div>
|
|
||||||
<div class="bounce3"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
[{[ if .Theme -]}]
|
<script type="module" src="/src/main.js"></script>
|
||||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" />
|
|
||||||
[{[ end ]}]
|
[{[ if .Theme -]}]
|
||||||
[{[ if .CSS -]}]
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css"
|
||||||
|
/>
|
||||||
|
[{[ end ]}] [{[ if .CSS -]}]
|
||||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||||
[{[ end ]}]
|
[{[ end ]}]
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -174,6 +174,12 @@ table th {
|
|||||||
background: var(--surfacePrimary);
|
background: var(--surfacePrimary);
|
||||||
color: var(--textPrimary);
|
color: var(--textPrimary);
|
||||||
}
|
}
|
||||||
|
.shell__divider {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
.shell__divider:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
.shell__result {
|
.shell__result {
|
||||||
border-top: 1px solid var(--divider);
|
border-top: 1px solid var(--divider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
// __webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "app",
|
name: "app",
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ import { fetchURL } from "./utils";
|
|||||||
|
|
||||||
const RETRY_BASE_DELAY = 1000;
|
const RETRY_BASE_DELAY = 1000;
|
||||||
const RETRY_MAX_DELAY = 20000;
|
const RETRY_MAX_DELAY = 20000;
|
||||||
|
const SPEED_UPDATE_INTERVAL = 1000;
|
||||||
|
const ALPHA = 0.2;
|
||||||
|
const ONE_MINUS_ALPHA = 1 - ALPHA;
|
||||||
|
const RECENT_SPEEDS_LIMIT = 5;
|
||||||
|
const MB_DIVISOR = 1024 * 1024;
|
||||||
|
const CURRENT_UPLOAD_LIST = {};
|
||||||
|
|
||||||
export async function upload(
|
export async function upload(
|
||||||
filePath,
|
filePath,
|
||||||
@@ -34,19 +40,47 @@ export async function upload(
|
|||||||
"X-Auth": store.state.jwt,
|
"X-Auth": store.state.jwt,
|
||||||
},
|
},
|
||||||
onError: function (error) {
|
onError: function (error) {
|
||||||
|
if (CURRENT_UPLOAD_LIST[filePath].interval) {
|
||||||
|
clearInterval(CURRENT_UPLOAD_LIST[filePath].interval);
|
||||||
|
}
|
||||||
|
delete CURRENT_UPLOAD_LIST[filePath];
|
||||||
reject("Upload failed: " + error);
|
reject("Upload failed: " + error);
|
||||||
},
|
},
|
||||||
onProgress: function (bytesUploaded) {
|
onProgress: function (bytesUploaded) {
|
||||||
// Emulate ProgressEvent.loaded which is used by calling functions
|
let fileData = CURRENT_UPLOAD_LIST[filePath];
|
||||||
// loaded is specified in bytes (https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/loaded)
|
fileData.currentBytesUploaded = bytesUploaded;
|
||||||
|
|
||||||
|
if (!fileData.hasStarted) {
|
||||||
|
fileData.hasStarted = true;
|
||||||
|
fileData.lastProgressTimestamp = Date.now();
|
||||||
|
|
||||||
|
fileData.interval = setInterval(() => {
|
||||||
|
calcProgress(filePath);
|
||||||
|
}, SPEED_UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
if (typeof onupload === "function") {
|
if (typeof onupload === "function") {
|
||||||
onupload({ loaded: bytesUploaded });
|
onupload({ loaded: bytesUploaded });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSuccess: function () {
|
onSuccess: function () {
|
||||||
|
if (CURRENT_UPLOAD_LIST[filePath].interval) {
|
||||||
|
clearInterval(CURRENT_UPLOAD_LIST[filePath].interval);
|
||||||
|
}
|
||||||
|
delete CURRENT_UPLOAD_LIST[filePath];
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
CURRENT_UPLOAD_LIST[filePath] = {
|
||||||
|
upload: upload,
|
||||||
|
recentSpeeds: [],
|
||||||
|
initialBytesUploaded: 0,
|
||||||
|
currentBytesUploaded: 0,
|
||||||
|
currentAverageSpeed: 0,
|
||||||
|
lastProgressTimestamp: null,
|
||||||
|
sumOfRecentSpeeds: 0,
|
||||||
|
hasStarted: false,
|
||||||
|
interval: null,
|
||||||
|
};
|
||||||
upload.start();
|
upload.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -88,3 +122,74 @@ export async function useTus(content) {
|
|||||||
function isTusSupported() {
|
function isTusSupported() {
|
||||||
return tus.isSupported === true;
|
return tus.isSupported === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function computeETA(state) {
|
||||||
|
if (state.speedMbyte === 0) {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
const totalSize = state.sizes.reduce((acc, size) => acc + size, 0);
|
||||||
|
const uploadedSize = state.progress.reduce(
|
||||||
|
(acc, progress) => acc + progress,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const remainingSize = totalSize - uploadedSize;
|
||||||
|
const speedBytesPerSecond = state.speedMbyte * 1024 * 1024;
|
||||||
|
return remainingSize / speedBytesPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeGlobalSpeedAndETA() {
|
||||||
|
let totalSpeed = 0;
|
||||||
|
let totalCount = 0;
|
||||||
|
|
||||||
|
for (let filePath in CURRENT_UPLOAD_LIST) {
|
||||||
|
totalSpeed += CURRENT_UPLOAD_LIST[filePath].currentAverageSpeed;
|
||||||
|
totalCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalCount === 0) return { speed: 0, eta: Infinity };
|
||||||
|
|
||||||
|
const averageSpeed = totalSpeed / totalCount;
|
||||||
|
const averageETA = computeETA(store.state.upload, averageSpeed);
|
||||||
|
|
||||||
|
return { speed: averageSpeed, eta: averageETA };
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcProgress(filePath) {
|
||||||
|
let fileData = CURRENT_UPLOAD_LIST[filePath];
|
||||||
|
|
||||||
|
let elapsedTime = (Date.now() - fileData.lastProgressTimestamp) / 1000;
|
||||||
|
let bytesSinceLastUpdate =
|
||||||
|
fileData.currentBytesUploaded - fileData.initialBytesUploaded;
|
||||||
|
let currentSpeed = bytesSinceLastUpdate / MB_DIVISOR / elapsedTime;
|
||||||
|
|
||||||
|
if (fileData.recentSpeeds.length >= RECENT_SPEEDS_LIMIT) {
|
||||||
|
fileData.sumOfRecentSpeeds -= fileData.recentSpeeds.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
fileData.recentSpeeds.push(currentSpeed);
|
||||||
|
fileData.sumOfRecentSpeeds += currentSpeed;
|
||||||
|
|
||||||
|
let avgRecentSpeed =
|
||||||
|
fileData.sumOfRecentSpeeds / fileData.recentSpeeds.length;
|
||||||
|
fileData.currentAverageSpeed =
|
||||||
|
ALPHA * avgRecentSpeed + ONE_MINUS_ALPHA * fileData.currentAverageSpeed;
|
||||||
|
|
||||||
|
const { speed, eta } = computeGlobalSpeedAndETA();
|
||||||
|
store.commit("setUploadSpeed", speed);
|
||||||
|
store.commit("setETA", eta);
|
||||||
|
|
||||||
|
fileData.initialBytesUploaded = fileData.currentBytesUploaded;
|
||||||
|
fileData.lastProgressTimestamp = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function abortAllUploads() {
|
||||||
|
for (let filePath in CURRENT_UPLOAD_LIST) {
|
||||||
|
if (CURRENT_UPLOAD_LIST[filePath].interval) {
|
||||||
|
clearInterval(CURRENT_UPLOAD_LIST[filePath].interval);
|
||||||
|
}
|
||||||
|
if (CURRENT_UPLOAD_LIST[filePath].upload) {
|
||||||
|
CURRENT_UPLOAD_LIST[filePath].upload.abort(true);
|
||||||
|
}
|
||||||
|
delete CURRENT_UPLOAD_LIST[filePath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
show(val, old) {
|
currentPrompt(val, old) {
|
||||||
this.active = val === "search";
|
this.active = val?.prompt === "search";
|
||||||
|
|
||||||
if (old === "search" && !this.active) {
|
if (old?.prompt === "search" && !this.active) {
|
||||||
if (this.reload) {
|
if (this.reload) {
|
||||||
this.setReload(true);
|
this.setReload(true);
|
||||||
}
|
}
|
||||||
@@ -116,8 +116,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["user", "show"]),
|
...mapState(["user"]),
|
||||||
...mapGetters(["isListing"]),
|
...mapGetters(["isListing", "currentPrompt"]),
|
||||||
boxes() {
|
boxes() {
|
||||||
return boxes;
|
return boxes;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,37 +1,54 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
@click="focus"
|
|
||||||
class="shell"
|
class="shell"
|
||||||
ref="scrollable"
|
|
||||||
:class="{ ['shell--hidden']: !showShell }"
|
:class="{ ['shell--hidden']: !showShell }"
|
||||||
|
:style="{ height: `${this.shellHeight}em` }"
|
||||||
>
|
>
|
||||||
<div v-for="(c, index) in content" :key="index" class="shell__result">
|
<div
|
||||||
<div class="shell__prompt">
|
@pointerdown="startDrag()"
|
||||||
<i class="material-icons">chevron_right</i>
|
@pointerup="stopDrag()"
|
||||||
|
class="shell__divider"
|
||||||
|
:style="this.shellDrag ? { background: `${checkTheme()}` } : ''"
|
||||||
|
></div>
|
||||||
|
<div @click="focus" class="shell__content" ref="scrollable">
|
||||||
|
<div v-for="(c, index) in content" :key="index" class="shell__result">
|
||||||
|
<div class="shell__prompt">
|
||||||
|
<i class="material-icons">chevron_right</i>
|
||||||
|
</div>
|
||||||
|
<pre class="shell__text">{{ c.text }}</pre>
|
||||||
</div>
|
</div>
|
||||||
<pre class="shell__text">{{ c.text }}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="shell__result" :class="{ 'shell__result--hidden': !canInput }">
|
<div
|
||||||
<div class="shell__prompt">
|
class="shell__result"
|
||||||
<i class="material-icons">chevron_right</i>
|
:class="{ 'shell__result--hidden': !canInput }"
|
||||||
|
>
|
||||||
|
<div class="shell__prompt">
|
||||||
|
<i class="material-icons">chevron_right</i>
|
||||||
|
</div>
|
||||||
|
<pre
|
||||||
|
tabindex="0"
|
||||||
|
ref="input"
|
||||||
|
class="shell__text"
|
||||||
|
contenteditable="true"
|
||||||
|
@keydown.prevent.38="historyUp"
|
||||||
|
@keydown.prevent.40="historyDown"
|
||||||
|
@keypress.prevent.enter="submit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<pre
|
|
||||||
tabindex="0"
|
|
||||||
ref="input"
|
|
||||||
class="shell__text"
|
|
||||||
contenteditable="true"
|
|
||||||
@keydown.prevent.38="historyUp"
|
|
||||||
@keydown.prevent.40="historyDown"
|
|
||||||
@keypress.prevent.enter="submit"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
@pointerup="stopDrag()"
|
||||||
|
class="shell__overlay"
|
||||||
|
v-show="this.shellDrag"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapMutations, mapState, mapGetters } from "vuex";
|
import { mapMutations, mapState, mapGetters } from "vuex";
|
||||||
import { commands } from "@/api";
|
import { commands } from "@/api";
|
||||||
|
import { throttle } from "lodash";
|
||||||
|
import { theme } from "@/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "shell",
|
name: "shell",
|
||||||
@@ -51,9 +68,55 @@ export default {
|
|||||||
history: [],
|
history: [],
|
||||||
historyPos: 0,
|
historyPos: 0,
|
||||||
canInput: true,
|
canInput: true,
|
||||||
|
shellDrag: false,
|
||||||
|
shellHeight: 25,
|
||||||
|
fontsize: parseFloat(getComputedStyle(document.documentElement).fontSize),
|
||||||
}),
|
}),
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener("resize", this.resize);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener("resize", this.resize);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["toggleShell"]),
|
...mapMutations(["toggleShell"]),
|
||||||
|
checkTheme() {
|
||||||
|
if (theme == "dark") {
|
||||||
|
return "rgba(255, 255, 255, 0.4)";
|
||||||
|
}
|
||||||
|
return "rgba(127, 127, 127, 0.4)";
|
||||||
|
},
|
||||||
|
startDrag() {
|
||||||
|
document.addEventListener("pointermove", this.handleDrag);
|
||||||
|
this.shellDrag = true;
|
||||||
|
},
|
||||||
|
stopDrag() {
|
||||||
|
document.removeEventListener("pointermove", this.handleDrag);
|
||||||
|
this.shellDrag = false;
|
||||||
|
},
|
||||||
|
handleDrag: throttle(function (event) {
|
||||||
|
const top = window.innerHeight / this.fontsize - 4;
|
||||||
|
const userPos = (window.innerHeight - event.clientY) / this.fontsize;
|
||||||
|
const bottom =
|
||||||
|
2.25 +
|
||||||
|
document.querySelector(".shell__divider").offsetHeight / this.fontsize;
|
||||||
|
|
||||||
|
if (userPos <= top && userPos >= bottom) {
|
||||||
|
this.shellHeight = userPos.toFixed(2);
|
||||||
|
}
|
||||||
|
}, 32),
|
||||||
|
resize: throttle(function () {
|
||||||
|
const top = window.innerHeight / this.fontsize - 4;
|
||||||
|
const bottom =
|
||||||
|
2.25 +
|
||||||
|
document.querySelector(".shell__divider").offsetHeight / this.fontsize;
|
||||||
|
|
||||||
|
if (this.shellHeight > top) {
|
||||||
|
this.shellHeight = top;
|
||||||
|
} else if (this.shellHeight < bottom) {
|
||||||
|
this.shellHeight = bottom;
|
||||||
|
}
|
||||||
|
}, 32),
|
||||||
scroll: function () {
|
scroll: function () {
|
||||||
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight;
|
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -133,9 +133,9 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["user"]),
|
...mapState(["user"]),
|
||||||
...mapGetters(["isLogged"]),
|
...mapGetters(["isLogged", "currentPrompt"]),
|
||||||
active() {
|
active() {
|
||||||
return this.$store.state.show === "sidebar";
|
return this.currentPrompt?.prompt === "sidebar";
|
||||||
},
|
},
|
||||||
signup: () => signup,
|
signup: () => signup,
|
||||||
version: () => version,
|
version: () => version,
|
||||||
|
|||||||
@@ -10,12 +10,7 @@
|
|||||||
@mouseup="mouseUp"
|
@mouseup="mouseUp"
|
||||||
@wheel="wheelMove"
|
@wheel="wheelMove"
|
||||||
>
|
>
|
||||||
<img
|
<img class="image-ex-img image-ex-img-center" ref="imgex" @load="onLoad" />
|
||||||
src=""
|
|
||||||
class="image-ex-img image-ex-img-center"
|
|
||||||
ref="imgex"
|
|
||||||
@load="onLoad"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { enableThumbs } from "@/utils/constants";
|
import { enableThumbs } from "@/utils/constants";
|
||||||
import { mapMutations, mapGetters, mapState } from "vuex";
|
import { mapMutations, mapGetters, mapState } from "vuex";
|
||||||
import filesize from "filesize";
|
import { filesize } from "@/utils";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import * as upload from "@/utils/upload";
|
import * as upload from "@/utils/upload";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
<div id="dropdown" :class="{ active: this.$store.state.show === 'more' }">
|
<div id="dropdown" :class="{ active: this.currentPromptName === 'more' }">
|
||||||
<slot name="actions" />
|
<slot name="actions" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="overlay"
|
class="overlay"
|
||||||
v-show="this.$store.state.show == 'more'"
|
v-show="this.currentPromptName == 'more'"
|
||||||
@click="$store.commit('closeHovers')"
|
@click="$store.commit('closeHovers')"
|
||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
@@ -34,7 +34,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import { logoURL } from "@/utils/constants";
|
import { logoURL } from "@/utils/constants";
|
||||||
|
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "header-bar",
|
name: "header-bar",
|
||||||
@@ -52,6 +53,9 @@ export default {
|
|||||||
this.$store.commit("showHover", "sidebar");
|
this.$store.commit("showHover", "sidebar");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(["currentPromptName"]),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -6,33 +6,50 @@
|
|||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>{{ $t("prompts.copyMessage") }}</p>
|
<p>{{ $t("prompts.copyMessage") }}</p>
|
||||||
<file-list @update:selected="(val) => (dest = val)"></file-list>
|
<file-list ref="fileList" @update:selected="(val) => (dest = val)">
|
||||||
|
</file-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div
|
||||||
<button
|
class="card-action"
|
||||||
class="button button--flat button--grey"
|
style="display: flex; align-items: center; justify-content: space-between;"
|
||||||
@click="$store.commit('closeHovers')"
|
>
|
||||||
:aria-label="$t('buttons.cancel')"
|
<template v-if="user.perm.create">
|
||||||
:title="$t('buttons.cancel')"
|
<button
|
||||||
>
|
class="button button--flat"
|
||||||
{{ $t("buttons.cancel") }}
|
@click="$refs.fileList.createDir()"
|
||||||
</button>
|
:aria-label="$t('sidebar.newFolder')"
|
||||||
<button
|
:title="$t('sidebar.newFolder')"
|
||||||
class="button button--flat"
|
style="justify-self: left;"
|
||||||
@click="copy"
|
>
|
||||||
:aria-label="$t('buttons.copy')"
|
<span>{{ $t("sidebar.newFolder") }}</span>
|
||||||
:title="$t('buttons.copy')"
|
</button>
|
||||||
>
|
</template>
|
||||||
{{ $t("buttons.copy") }}
|
<div>
|
||||||
</button>
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
:aria-label="$t('buttons.cancel')"
|
||||||
|
:title="$t('buttons.cancel')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
|
@click="copy"
|
||||||
|
:aria-label="$t('buttons.copy')"
|
||||||
|
:title="$t('buttons.copy')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.copy") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import FileList from "./FileList";
|
import FileList from "./FileList.vue";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import buttons from "@/utils/buttons";
|
import buttons from "@/utils/buttons";
|
||||||
import * as upload from "@/utils/upload";
|
import * as upload from "@/utils/upload";
|
||||||
@@ -46,7 +63,7 @@ export default {
|
|||||||
dest: null,
|
dest: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(["req", "selected"]),
|
computed: mapState(["req", "selected", "user"]),
|
||||||
methods: {
|
methods: {
|
||||||
copy: async function (event) {
|
copy: async function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ import buttons from "@/utils/buttons";
|
|||||||
export default {
|
export default {
|
||||||
name: "delete",
|
name: "delete",
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["isListing", "selectedCount"]),
|
...mapGetters(["isListing", "selectedCount", "currentPrompt"]),
|
||||||
...mapState(["req", "selected", "showConfirm"]),
|
...mapState(["req", "selected"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["closeHovers"]),
|
...mapMutations(["closeHovers"]),
|
||||||
@@ -50,7 +50,7 @@ export default {
|
|||||||
await api.remove(this.$route.path);
|
await api.remove(this.$route.path);
|
||||||
buttons.success("delete");
|
buttons.success("delete");
|
||||||
|
|
||||||
this.showConfirm();
|
this.currentPrompt?.confirm();
|
||||||
this.closeHovers();
|
this.closeHovers();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
v-for="(ext, format) in formats"
|
v-for="(ext, format) in formats"
|
||||||
:key="format"
|
:key="format"
|
||||||
class="button button--block"
|
class="button button--block"
|
||||||
@click="showConfirm(format)"
|
@click="currentPrompt.confirm(format)"
|
||||||
v-focus
|
v-focus
|
||||||
>
|
>
|
||||||
{{ ext }}
|
{{ ext }}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "download",
|
name: "download",
|
||||||
@@ -38,6 +38,8 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(["showConfirm"]),
|
computed: {
|
||||||
|
...mapGetters(["currentPrompt"]),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -133,6 +133,17 @@ export default {
|
|||||||
this.selected = event.currentTarget.dataset.url;
|
this.selected = event.currentTarget.dataset.url;
|
||||||
this.$emit("update:selected", this.selected);
|
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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from "vuex";
|
import { mapState, mapGetters } from "vuex";
|
||||||
import filesize from "filesize";
|
import { filesize } from "@/utils";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const hash = await api.checksum(link, algo);
|
const hash = await api.checksum(link, algo);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
event.target.innerHTML = hash
|
event.target.innerHTML = hash;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.$showError(e);
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,34 +5,51 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<file-list @update:selected="(val) => (dest = val)"></file-list>
|
<file-list ref="fileList" @update:selected="(val) => (dest = val)">
|
||||||
|
</file-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-action">
|
<div
|
||||||
<button
|
class="card-action"
|
||||||
class="button button--flat button--grey"
|
style="display: flex; align-items: center; justify-content: space-between;"
|
||||||
@click="$store.commit('closeHovers')"
|
>
|
||||||
:aria-label="$t('buttons.cancel')"
|
<template v-if="user.perm.create">
|
||||||
:title="$t('buttons.cancel')"
|
<button
|
||||||
>
|
class="button button--flat"
|
||||||
{{ $t("buttons.cancel") }}
|
@click="$refs.fileList.createDir()"
|
||||||
</button>
|
:aria-label="$t('sidebar.newFolder')"
|
||||||
<button
|
:title="$t('sidebar.newFolder')"
|
||||||
class="button button--flat"
|
style="justify-self: left;"
|
||||||
@click="move"
|
>
|
||||||
:disabled="$route.path === dest"
|
<span>{{ $t("sidebar.newFolder") }}</span>
|
||||||
:aria-label="$t('buttons.move')"
|
</button>
|
||||||
:title="$t('buttons.move')"
|
</template>
|
||||||
>
|
<div>
|
||||||
{{ $t("buttons.move") }}
|
<button
|
||||||
</button>
|
class="button button--flat button--grey"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
:aria-label="$t('buttons.cancel')"
|
||||||
|
:title="$t('buttons.cancel')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
|
@click="move"
|
||||||
|
:disabled="$route.path === dest"
|
||||||
|
:aria-label="$t('buttons.move')"
|
||||||
|
:title="$t('buttons.move')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.move") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
import FileList from "./FileList";
|
import FileList from "./FileList.vue";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import buttons from "@/utils/buttons";
|
import buttons from "@/utils/buttons";
|
||||||
import * as upload from "@/utils/upload";
|
import * as upload from "@/utils/upload";
|
||||||
@@ -46,7 +63,7 @@ export default {
|
|||||||
dest: null,
|
dest: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: mapState(["req", "selected"]),
|
computed: mapState(["req", "selected", "user"]),
|
||||||
methods: {
|
methods: {
|
||||||
move: async function (event) {
|
move: async function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -43,6 +43,16 @@ import url from "@/utils/url";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "new-dir",
|
name: "new-dir",
|
||||||
|
props: {
|
||||||
|
redirect: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
base: {
|
||||||
|
type: [String, null],
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
name: "",
|
name: "",
|
||||||
@@ -57,7 +67,11 @@ export default {
|
|||||||
if (this.new === "") return;
|
if (this.new === "") return;
|
||||||
|
|
||||||
// Build the path of the new directory.
|
// 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) {
|
if (!this.isListing) {
|
||||||
uri = url.removeLastDir(uri) + "/";
|
uri = url.removeLastDir(uri) + "/";
|
||||||
@@ -65,10 +79,14 @@ export default {
|
|||||||
|
|
||||||
uri += encodeURIComponent(this.name) + "/";
|
uri += encodeURIComponent(this.name) + "/";
|
||||||
uri = uri.replace("//", "/");
|
uri = uri.replace("//", "/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.post(uri);
|
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) {
|
} catch (e) {
|
||||||
this.$showError(e);
|
this.$showError(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<component ref="currentComponent" :is="currentComponent"></component>
|
<component
|
||||||
|
v-if="showOverlay"
|
||||||
|
:ref="currentPromptName"
|
||||||
|
:is="currentPromptName"
|
||||||
|
v-bind="currentPrompt.props"
|
||||||
|
>
|
||||||
|
</component>
|
||||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Help from "./Help";
|
import Help from "./Help.vue";
|
||||||
import Info from "./Info";
|
import Info from "./Info.vue";
|
||||||
import Delete from "./Delete";
|
import Delete from "./Delete.vue";
|
||||||
import Rename from "./Rename";
|
import Rename from "./Rename.vue";
|
||||||
import Download from "./Download";
|
import Download from "./Download.vue";
|
||||||
import Move from "./Move";
|
import Move from "./Move.vue";
|
||||||
import Copy from "./Copy";
|
import Copy from "./Copy.vue";
|
||||||
import NewFile from "./NewFile";
|
import NewFile from "./NewFile.vue";
|
||||||
import NewDir from "./NewDir";
|
import NewDir from "./NewDir.vue";
|
||||||
import Replace from "./Replace";
|
import Replace from "./Replace.vue";
|
||||||
import ReplaceRename from "./ReplaceRename";
|
import ReplaceRename from "./ReplaceRename.vue";
|
||||||
import Share from "./Share";
|
import Share from "./Share.vue";
|
||||||
import Upload from "./Upload";
|
import Upload from "./Upload.vue";
|
||||||
import ShareDelete from "./ShareDelete";
|
import ShareDelete from "./ShareDelete.vue";
|
||||||
import { mapState } from "vuex";
|
import Sidebar from "../Sidebar.vue";
|
||||||
|
import { mapGetters, mapState } from "vuex";
|
||||||
import buttons from "@/utils/buttons";
|
import buttons from "@/utils/buttons";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -40,6 +47,7 @@ export default {
|
|||||||
ReplaceRename,
|
ReplaceRename,
|
||||||
Upload,
|
Upload,
|
||||||
ShareDelete,
|
ShareDelete,
|
||||||
|
Sidebar
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
@@ -52,7 +60,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
window.addEventListener("keydown", (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
if (this.show == null) return;
|
if (this.currentPrompt == null) return;
|
||||||
|
|
||||||
let prompt = this.$refs.currentComponent;
|
let prompt = this.$refs.currentComponent;
|
||||||
|
|
||||||
@@ -64,7 +72,7 @@ export default {
|
|||||||
|
|
||||||
// Enter
|
// Enter
|
||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
switch (this.show) {
|
switch (this.currentPrompt.prompt) {
|
||||||
case "delete":
|
case "delete":
|
||||||
prompt.submit();
|
prompt.submit();
|
||||||
break;
|
break;
|
||||||
@@ -82,31 +90,13 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["show", "plugins"]),
|
...mapState(["plugins"]),
|
||||||
currentComponent: function () {
|
...mapGetters(["currentPrompt", "currentPromptName"]),
|
||||||
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;
|
|
||||||
},
|
|
||||||
showOverlay: function () {
|
showOverlay: function () {
|
||||||
return (
|
return (
|
||||||
this.show !== null && this.show !== "search" && this.show !== "more"
|
this.currentPrompt !== null &&
|
||||||
|
this.currentPrompt.prompt !== "search" &&
|
||||||
|
this.currentPrompt.prompt !== "more"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--blue"
|
class="button button--flat button--blue"
|
||||||
@click="showAction"
|
@click="currentPrompt.action"
|
||||||
:aria-label="$t('buttons.continue')"
|
:aria-label="$t('buttons.continue')"
|
||||||
:title="$t('buttons.continue')"
|
:title="$t('buttons.continue')"
|
||||||
>
|
>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
@click="showConfirm"
|
@click="currentPrompt.confirm"
|
||||||
:aria-label="$t('buttons.replace')"
|
:aria-label="$t('buttons.replace')"
|
||||||
:title="$t('buttons.replace')"
|
:title="$t('buttons.replace')"
|
||||||
>
|
>
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "replace",
|
name: "replace",
|
||||||
computed: mapState(["showConfirm", "showAction"]),
|
computed: mapGetters(["currentPrompt"]),
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--blue"
|
class="button button--flat button--blue"
|
||||||
@click="(event) => showConfirm(event, 'rename')"
|
@click="(event) => currentPrompt.confirm(event, 'rename')"
|
||||||
:aria-label="$t('buttons.rename')"
|
:aria-label="$t('buttons.rename')"
|
||||||
:title="$t('buttons.rename')"
|
:title="$t('buttons.rename')"
|
||||||
>
|
>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="button button--flat button--red"
|
class="button button--flat button--red"
|
||||||
@click="(event) => showConfirm(event, 'overwrite')"
|
@click="(event) => currentPrompt.confirm(event, 'overwrite')"
|
||||||
:aria-label="$t('buttons.replace')"
|
:aria-label="$t('buttons.replace')"
|
||||||
:title="$t('buttons.replace')"
|
:title="$t('buttons.replace')"
|
||||||
>
|
>
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "replace-rename",
|
name: "replace-rename",
|
||||||
computed: mapState(["showConfirm"]),
|
computed: mapGetters(["currentPrompt"]),
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,16 +25,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapGetters } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "share-delete",
|
name: "share-delete",
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["showConfirm"]),
|
...mapGetters(["currentPrompt"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submit: function () {
|
submit: function () {
|
||||||
this.showConfirm();
|
this.currentPrompt?.confirm();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,18 @@
|
|||||||
<div class="card floating">
|
<div class="card floating">
|
||||||
<div class="card-title">
|
<div class="card-title">
|
||||||
<h2>{{ $t("prompts.uploadFiles", { files: filesInUploadCount }) }}</h2>
|
<h2>{{ $t("prompts.uploadFiles", { files: filesInUploadCount }) }}</h2>
|
||||||
|
<div class="upload-info">
|
||||||
|
<div class="upload-speed">{{ uploadSpeed.toFixed(2) }} MB/s</div>
|
||||||
|
<div class="upload-eta">{{ formattedETA }} remaining</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="action"
|
||||||
|
@click="abortAll"
|
||||||
|
aria-label="Abort upload"
|
||||||
|
title="Abort upload"
|
||||||
|
>
|
||||||
|
<i class="material-icons">{{ "cancel" }}</i>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="action"
|
class="action"
|
||||||
@click="toggle"
|
@click="toggle"
|
||||||
@@ -42,7 +53,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from "vuex";
|
import { mapGetters, mapMutations } from "vuex";
|
||||||
|
import { abortAllUploads } from "@/api/tus";
|
||||||
|
import buttons from "@/utils/buttons";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "uploadFiles",
|
name: "uploadFiles",
|
||||||
@@ -52,12 +65,42 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["filesInUpload", "filesInUploadCount"]),
|
...mapGetters([
|
||||||
|
"filesInUpload",
|
||||||
|
"filesInUploadCount",
|
||||||
|
"uploadSpeed",
|
||||||
|
"eta",
|
||||||
|
]),
|
||||||
|
...mapMutations(["resetUpload"]),
|
||||||
|
formattedETA() {
|
||||||
|
if (!this.eta || this.eta === Infinity) {
|
||||||
|
return "--:--:--";
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalSeconds = this.eta;
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
totalSeconds %= 3600;
|
||||||
|
const minutes = Math.floor(totalSeconds / 60);
|
||||||
|
const seconds = Math.round(totalSeconds % 60);
|
||||||
|
|
||||||
|
return `${hours.toString().padStart(2, "0")}:${minutes
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggle: function () {
|
toggle: function () {
|
||||||
this.open = !this.open;
|
this.open = !this.open;
|
||||||
},
|
},
|
||||||
|
abortAll() {
|
||||||
|
if (confirm(this.$t("upload.abortUpload"))) {
|
||||||
|
abortAllUploads();
|
||||||
|
buttons.done("upload");
|
||||||
|
this.open = false;
|
||||||
|
this.$store.commit("resetUpload");
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default {
|
|||||||
hu: "hu",
|
hu: "hu",
|
||||||
ar: "ar",
|
ar: "ar",
|
||||||
de: "de",
|
de: "de",
|
||||||
|
el: "el",
|
||||||
en: "en",
|
en: "en",
|
||||||
es: "es",
|
es: "es",
|
||||||
fr: "fr",
|
fr: "fr",
|
||||||
|
|||||||
@@ -67,10 +67,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Languages from "./Languages";
|
import Languages from "./Languages.vue";
|
||||||
import Rules from "./Rules";
|
import Rules from "./Rules.vue";
|
||||||
import Permissions from "./Permissions";
|
import Permissions from "./Permissions.vue";
|
||||||
import Commands from "./Commands";
|
import Commands from "./Commands.vue";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -2,21 +2,49 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 25em;
|
|
||||||
max-height: calc(100% - 4em);
|
max-height: calc(100% - 4em);
|
||||||
background: white;
|
background: white;
|
||||||
color: #212121;
|
color: #212121;
|
||||||
z-index: 9999;
|
z-index: 9997;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: monospace;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 1rem;
|
|
||||||
cursor: text;
|
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||||
transition: .2s ease transform;
|
transition: .2s ease transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.rtl .shell {
|
.shell__divider {
|
||||||
|
position: relative;
|
||||||
|
height: 8px;
|
||||||
|
z-index: 9999;
|
||||||
|
background: rgba(127, 127, 127, 0.1);
|
||||||
|
transition: 0.2s ease background;
|
||||||
|
cursor: ns-resize;
|
||||||
|
touch-action: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell__divider:hover {
|
||||||
|
background: rgba(127, 127, 127, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell__content {
|
||||||
|
height: 100%;
|
||||||
|
font-family: monospace;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell__overlay {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
z-index: 9998;
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.rtl .shell-content {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,173 +1,242 @@
|
|||||||
|
@import "material-icons/iconfont/filled.css";
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-cyrillic-ext.woff2) format("woff2");
|
||||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-cyrillic.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-cyrillic.woff2) format("woff2");
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-greek-ext.woff2) format("woff2");
|
||||||
unicode-range: U+1F00-1FFF;
|
unicode-range: U+1F00-1FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-greek.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-greek.woff2) format("woff2");
|
||||||
unicode-range: U+0370-03FF;
|
unicode-range: U+0370-03FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-vietnamese.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-vietnamese.woff2) format("woff2");
|
||||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin-ext.woff2) format('woff2');
|
src:
|
||||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-latin-ext.woff2) format("woff2");
|
||||||
|
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F,
|
||||||
|
U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
src: local('Roboto'), local('Roboto-Regular'), url(../assets/fonts/roboto/normal-latin.woff2) format('woff2');
|
src:
|
||||||
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;
|
local("Roboto"),
|
||||||
|
local("Roboto-Regular"),
|
||||||
|
url(../assets/fonts/roboto/normal-latin.woff2) format("woff2");
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-cyrillic-ext.woff2) format("woff2");
|
||||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-cyrillic.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-cyrillic.woff2) format("woff2");
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-greek-ext.woff2) format("woff2");
|
||||||
unicode-range: U+1F00-1FFF;
|
unicode-range: U+1F00-1FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-greek.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-greek.woff2) format("woff2");
|
||||||
unicode-range: U+0370-03FF;
|
unicode-range: U+0370-03FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-vietnamese.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-vietnamese.woff2) format("woff2");
|
||||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin-ext.woff2) format('woff2');
|
src:
|
||||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-latin-ext.woff2) format("woff2");
|
||||||
|
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F,
|
||||||
|
U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(../assets/fonts/roboto/medium-latin.woff2) format('woff2');
|
src:
|
||||||
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;
|
local("Roboto Medium"),
|
||||||
|
local("Roboto-Medium"),
|
||||||
|
url(../assets/fonts/roboto/medium-latin.woff2) format("woff2");
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-cyrillic-ext.woff2) format("woff2");
|
||||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-cyrillic.woff2) format("woff2");
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek-ext.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-greek-ext.woff2) format("woff2");
|
||||||
unicode-range: U+1F00-1FFF;
|
unicode-range: U+1F00-1FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-greek.woff2) format("woff2");
|
||||||
unicode-range: U+0370-03FF;
|
unicode-range: U+0370-03FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-vietnamese.woff2) format('woff2');
|
src:
|
||||||
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-vietnamese.woff2) format("woff2");
|
||||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin-ext.woff2) format('woff2');
|
src:
|
||||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-latin-ext.woff2) format("woff2");
|
||||||
|
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F,
|
||||||
|
U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Roboto';
|
font-family: "Roboto";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin.woff2) format('woff2');
|
src:
|
||||||
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;
|
local("Roboto Bold"),
|
||||||
|
local("Roboto-Bold"),
|
||||||
|
url(../assets/fonts/roboto/bold-latin.woff2) format("woff2");
|
||||||
|
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-icons/iconfont/filled.css';
|
|
||||||
|
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -120,6 +120,10 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shell__divider {
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
header .search-button,
|
header .search-button,
|
||||||
header .menu-button {
|
header .menu-button {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@import "~normalize.css/normalize.css";
|
@import "normalize.css/normalize.css";
|
||||||
@import "~noty/lib/noty.css";
|
@import "noty/lib/noty.css";
|
||||||
@import "~noty/lib/themes/mint.css";
|
@import "noty/lib/themes/mint.css";
|
||||||
@import "./_variables.css";
|
@import "./_variables.css";
|
||||||
@import "./_buttons.css";
|
@import "./_buttons.css";
|
||||||
@import "./_inputs.css";
|
@import "./_inputs.css";
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
@import "./upload-files.css";
|
@import "./upload-files.css";
|
||||||
@import "./dashboard.css";
|
@import "./dashboard.css";
|
||||||
@import "./login.css";
|
@import "./login.css";
|
||||||
|
@import "./mobile.css";
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
color: var(--blue);
|
color: var(--blue);
|
||||||
@@ -27,9 +28,9 @@ main .spinner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main .spinner > div {
|
main .spinner > div {
|
||||||
width: .8em;
|
width: 0.8em;
|
||||||
height: .8em;
|
height: 0.8em;
|
||||||
margin: 0 .1em;
|
margin: 0 0.1em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
background-color: rgba(0, 0, 0, 0.3);
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
@@ -71,7 +72,7 @@ main .spinner .bounce2 {
|
|||||||
transition: 0.2s ease all;
|
transition: 0.2s ease all;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #546E7A;
|
color: #546e7a;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -88,12 +89,12 @@ main .spinner .bounce2 {
|
|||||||
|
|
||||||
.action i {
|
.action i {
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
transition: .1s ease-in-out all;
|
transition: 0.1s ease-in-out all;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action:hover {
|
.action:hover {
|
||||||
background-color: rgba(0, 0, 0, .1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action ul {
|
.action ul {
|
||||||
@@ -109,8 +110,8 @@ main .spinner .bounce2 {
|
|||||||
|
|
||||||
.action ul li {
|
.action ul li {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: .7em;
|
padding: 0.7em;
|
||||||
transition: .1s ease background-color;
|
transition: 0.1s ease background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action ul li:hover {
|
.action ul li:hover {
|
||||||
@@ -139,7 +140,7 @@ main .spinner .bounce2 {
|
|||||||
background: var(--blue);
|
background: var(--blue);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
font-size: .75em;
|
font-size: 0.75em;
|
||||||
width: 1.8em;
|
width: 1.8em;
|
||||||
height: 1.8em;
|
height: 1.8em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -148,7 +149,6 @@ main .spinner .bounce2 {
|
|||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* PREVIEWER */
|
/* PREVIEWER */
|
||||||
|
|
||||||
#previewer {
|
#previewer {
|
||||||
@@ -179,7 +179,7 @@ main .spinner .bounce2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#previewer header .action:hover {
|
#previewer header .action:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.3)
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer header .action span {
|
#previewer header .action span {
|
||||||
@@ -220,14 +220,14 @@ main .spinner .bounce2 {
|
|||||||
}
|
}
|
||||||
#previewer .preview .info .title i {
|
#previewer .preview .info .title i {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: .1em;
|
margin-bottom: 0.1em;
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
}
|
}
|
||||||
#previewer .preview .info .button {
|
#previewer .preview .info .button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
#previewer .preview .info .button:hover {
|
#previewer .preview .info .button:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.2)
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
#previewer .preview .info .button i {
|
#previewer .preview .info .button i {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -241,15 +241,15 @@ main .spinner .bounce2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#previewer h2.message {
|
#previewer h2.message {
|
||||||
color: rgba(255, 255, 255, 0.5)
|
color: rgba(255, 255, 255, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer>button {
|
#previewer > button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: calc(50% + 1.85em);
|
top: calc(50% + 1.85em);
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
background-color: rgba(80, 80, 80, .5);
|
background-color: rgba(80, 80, 80, 0.5);
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -259,20 +259,20 @@ main .spinner .bounce2 {
|
|||||||
transition: 0.2s ease all;
|
transition: 0.2s ease all;
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer>button.hidden {
|
#previewer > button.hidden {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer>button>i {
|
#previewer > button > i {
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer>button:first-of-type {
|
#previewer > button:first-of-type {
|
||||||
left: 0.5em;
|
left: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#previewer>button:last-of-type {
|
#previewer > button:last-of-type {
|
||||||
right: 0.5em;
|
right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +321,7 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#editor-container .breadcrumbs span {
|
#editor-container .breadcrumbs span {
|
||||||
font-size: .75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#editor-container .breadcrumbs i {
|
#editor-container .breadcrumbs i {
|
||||||
@@ -339,7 +339,7 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
|
|
||||||
.noty_buttons button {
|
.noty_buttons button {
|
||||||
background: rgba(0, 0, 0, 0.05);
|
background: rgba(0, 0, 0, 0.05);
|
||||||
border: 1px solid rgba(0,0,0,0.1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
box-shadow: 0 0 0 0;
|
box-shadow: 0 0 0 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
@@ -356,7 +356,7 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
|
|
||||||
.credits > span {
|
.credits > span {
|
||||||
display: block;
|
display: block;
|
||||||
margin: .3em 0;
|
margin: 0.3em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.credits a,
|
.credits a,
|
||||||
@@ -365,7 +365,6 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * *
|
||||||
* ANIMATIONS *
|
* ANIMATIONS *
|
||||||
* * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * */
|
||||||
@@ -393,20 +392,20 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
.rules > div {
|
.rules > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: .5rem 0;
|
margin: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules input[type="checkbox"] {
|
.rules input[type="checkbox"] {
|
||||||
margin-right: .2rem;
|
margin-right: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules input[type="text"] {
|
.rules input[type="text"] {
|
||||||
border: 1px solid#ddd;
|
border: 1px solid#ddd;
|
||||||
padding: .2rem;
|
padding: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules label {
|
.rules label {
|
||||||
margin-right: .5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rules button {
|
.rules button {
|
||||||
@@ -414,12 +413,10 @@ body.rtl .breadcrumbs .chevron {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rules button.delete {
|
.rules button.delete {
|
||||||
padding: .2rem .5rem;
|
padding: 0.2rem 0.5rem;
|
||||||
margin-left: .5rem;
|
margin-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import './mobile.css';
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * *
|
||||||
* RTL overrides *
|
* RTL overrides *
|
||||||
* * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * */
|
||||||
|
|||||||
@@ -36,7 +36,8 @@
|
|||||||
"toggleSidebar": "Seitenleiste anzeigen",
|
"toggleSidebar": "Seitenleiste anzeigen",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"openFile": "Datei öffnen"
|
"openFile": "Datei öffnen",
|
||||||
|
"continue": "Fortfahren"
|
||||||
},
|
},
|
||||||
"download": {
|
"download": {
|
||||||
"downloadFile": "Download Datei",
|
"downloadFile": "Download Datei",
|
||||||
@@ -147,7 +148,7 @@
|
|||||||
"rename": "Umbenennen",
|
"rename": "Umbenennen",
|
||||||
"renameMessage": "Fügen Sie einen Namen ein für",
|
"renameMessage": "Fügen Sie einen Namen ein für",
|
||||||
"replace": "Ersetzen",
|
"replace": "Ersetzen",
|
||||||
"replaceMessage": "Eine der Datei mit dem gleichen Namen, wie die Sie hochladen wollen, existiert bereits. Soll die vorhandene Datei ersetzt werden ?\n",
|
"replaceMessage": "Eine der Datei mit dem gleichen Namen, wie die Sie hochladen wollen, existiert bereits. Soll die vorhandene Datei übersprungen oder ersetzt werden?\n",
|
||||||
"schedule": "Plan",
|
"schedule": "Plan",
|
||||||
"scheduleMessage": "Wählen Sie ein Datum und eine Zeit für die Veröffentlichung dieses Beitrags.",
|
"scheduleMessage": "Wählen Sie ein Datum und eine Zeit für die Veröffentlichung dieses Beitrags.",
|
||||||
"show": "Anzeigen",
|
"show": "Anzeigen",
|
||||||
@@ -184,10 +185,14 @@
|
|||||||
"commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen, lesen Sie bitte die {2}.",
|
"commandRunnerHelp": "Hier könne Sie Befehle eintragen, welche bei den benannten Aktionen ausgeführt werden. Sie müssen pro Zeile jeweils einen Befehl eingeben. Die Umgebungsvariable {0} und {1} sind verfügbar, wobei {0} relative zu {1} ist. Für mehr Informationen über diese Funktion und die verfügbaren Umgebungsvariablen, lesen Sie bitte die {2}.",
|
||||||
"commandsUpdated": "Befehle aktualisiert!",
|
"commandsUpdated": "Befehle aktualisiert!",
|
||||||
"createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer",
|
"createUserDir": "Automatisches Erstellen des Home-Verzeichnisses beim Anlegen neuer Benutzer",
|
||||||
|
"tusUploads": "Gestückelter Upload",
|
||||||
|
"tusUploadsHelp": "File Browser unterstützt das Hochladen von gestückelten Dateien und ermöglicht so einen effizienten, zuverlässigen, fortsetzbaren und gestückelten Datei-Upload auch in unzuverlässigen Netzwerken.",
|
||||||
|
"tusUploadsChunkSize": "Gibt die maximale Größe pro Anfrage an (direkte Uploads werden für kleinere Uploads verwendet). Bitte geben Sie eine Byte-Angabe oder eine Zeichenfolge wie 10 MB, 1 GB usw. an",
|
||||||
|
"tusUploadsRetryCount": "Anzahl der Wiederholungsversuche, wenn das Hochladen eines Stückes fehlschlägt.",
|
||||||
"customStylesheet": "Individuelles Stylesheet",
|
"customStylesheet": "Individuelles Stylesheet",
|
||||||
"defaultUserDescription": "Das sind die Standardeinstellung für Benutzer",
|
"defaultUserDescription": "Das sind die Standardeinstellung für Benutzer",
|
||||||
"disableExternalLinks": "Externe Links deaktivieren (außer Dokumentation)",
|
"disableExternalLinks": "Externe Links deaktivieren (außer Dokumentation)",
|
||||||
"disableUsedDiskPercentage": "Disable used disk percentage graph",
|
"disableUsedDiskPercentage": "Diagramm zur Festplattennutzung deaktivieren",
|
||||||
"documentation": "Dokumentation",
|
"documentation": "Dokumentation",
|
||||||
"examples": "Beispiele",
|
"examples": "Beispiele",
|
||||||
"executeOnShell": "In Shell ausführen",
|
"executeOnShell": "In Shell ausführen",
|
||||||
|
|||||||
282
frontend/src/i18n/el.json
Normal file
282
frontend/src/i18n/el.json
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
{
|
||||||
|
"buttons": {
|
||||||
|
"cancel": "Ακύρωση",
|
||||||
|
"close": "Κλείσιμο",
|
||||||
|
"copy": "Αντιγραφή",
|
||||||
|
"copyFile": "Αντιγραφή αρχείου",
|
||||||
|
"copyToClipboard": "Αντιγραφή στο πρόχειρο",
|
||||||
|
"copyDownloadLinkToClipboard": "Αντιγραφή συνδέσμου λήψης στο πρόχειρο",
|
||||||
|
"create": "Δημιουργία",
|
||||||
|
"delete": "Διαγραφή",
|
||||||
|
"download": "Λήψη",
|
||||||
|
"file": "Αρχείο",
|
||||||
|
"folder": "Φάκελος",
|
||||||
|
"hideDotfiles": "Απόκρυψη κρυφών αρχείων",
|
||||||
|
"info": "Πληροφορίες",
|
||||||
|
"more": "Περισσότερα",
|
||||||
|
"move": "Μετακίνηση",
|
||||||
|
"moveFile": "Μετακίνηση αρχείου",
|
||||||
|
"new": "Νέο",
|
||||||
|
"next": "Επόμενο",
|
||||||
|
"ok": "Εντάξει",
|
||||||
|
"permalink": "Λήψη μόνιμου συνδέσμου",
|
||||||
|
"previous": "Προηγούμενο",
|
||||||
|
"publish": "Δημοσίευση",
|
||||||
|
"rename": "Μετονομασία",
|
||||||
|
"replace": "Αντικατάσταση",
|
||||||
|
"reportIssue": "Αναφορά προβλήματος",
|
||||||
|
"save": "Αποθήκευση",
|
||||||
|
"schedule": "Προγραμματισμός",
|
||||||
|
"search": "Αναζήτηση",
|
||||||
|
"select": "Επιλογή",
|
||||||
|
"selectMultiple": "Επιλογή πολλαπλών",
|
||||||
|
"share": "Κοινοποίηση",
|
||||||
|
"submit": "Υποβολή",
|
||||||
|
"switchView": "Εναλλαγή προβολής",
|
||||||
|
"toggleSidebar": "(Απ-)ενεργοποίησης της πλευρικής μπάρας",
|
||||||
|
"update": "Ενημέρωση",
|
||||||
|
"upload": "Μεταφόρτωση",
|
||||||
|
"openFile": "Άνοιγμα αρχείου",
|
||||||
|
"continue": "Συνέχεια"
|
||||||
|
},
|
||||||
|
"download": {
|
||||||
|
"downloadFile": "Λήψη αρχείου",
|
||||||
|
"downloadFolder": "Λήψη φακέλου",
|
||||||
|
"downloadSelected": "Λήψη επιλεγμένων"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"abortUpload": "Είστε σίγουροι ότι θέλετε να διακόψετε τη μεταφόρτωση;"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"forbidden": "Δεν έχετε άδεια πρόσβασης σε αυτό.",
|
||||||
|
"internal": "Προέκυψε εσωτερικό σφάλμα.",
|
||||||
|
"notFound": "Αυτή η τοποθεσία δεν μπορεί να βρεθεί.",
|
||||||
|
"connection": "Ο διακομιστής δεν είναι διαθέσιμος."
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"body": "Περιεχόμενο",
|
||||||
|
"clear": "Καθαρισμός",
|
||||||
|
"closePreview": "Κλείσιμο προεπισκόπησης",
|
||||||
|
"files": "Αρχεία",
|
||||||
|
"folders": "Φάκελοι",
|
||||||
|
"home": "Αρχική",
|
||||||
|
"lastModified": "Τελευταία τροποποίηση",
|
||||||
|
"loading": "Φορτώνει…",
|
||||||
|
"lonely": "Δεν υπάρχει τίποτα εδώ (ακόμη)…",
|
||||||
|
"metadata": "Μεταδεδομένα",
|
||||||
|
"multipleSelectionEnabled": "Ενεργοποιημένη επιλογή πολλαπλών",
|
||||||
|
"name": "Όνομα",
|
||||||
|
"size": "Μέγεθος",
|
||||||
|
"sortByLastModified": "Ταξινόμηση κατά πρόσφατη τροποποίηση",
|
||||||
|
"sortByName": "Ταξινόμηση κατά όνομα",
|
||||||
|
"sortBySize": "Ταξινόμηση κατά μέγεθος",
|
||||||
|
"noPreview": "Η προεπισκόπηση δεν είναι διαθέσιμη για αυτό το αρχείο."
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"click": "επιλέξτε αρχείο ή φάκελο",
|
||||||
|
"ctrl": {
|
||||||
|
"click": "επιλογή πολλαπλών αρχείων ή φακέλων",
|
||||||
|
"f": "ανοίγει την αναζήτηση",
|
||||||
|
"s": "αποθηκεύει ένα αρχείο ή εκκινεί λήψη του φακέλου στον οποίο βρίσκεστε"
|
||||||
|
},
|
||||||
|
"del": "διαγραφή επιλεγμένων στοιχείων",
|
||||||
|
"doubleClick": "ανοίγει ένα αρχείο ή φάκελο",
|
||||||
|
"esc": "καθαρίζει την επιλογή ή/και κλείνει το παράθυρο",
|
||||||
|
"f1": "αυτή η πληροφορία",
|
||||||
|
"f2": "μετονομασία αρχείου",
|
||||||
|
"help": "Βοήθεια"
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"he": "עברית",
|
||||||
|
"hu": "Magyar",
|
||||||
|
"ar": "العربية",
|
||||||
|
"de": "Deutsch",
|
||||||
|
"en": "English",
|
||||||
|
"es": "Español",
|
||||||
|
"el": "Ελληνικά",
|
||||||
|
"fr": "Français",
|
||||||
|
"is": "Icelandic",
|
||||||
|
"it": "Italiano",
|
||||||
|
"ja": "日本語",
|
||||||
|
"ko": "한국어",
|
||||||
|
"nlBE": "Dutch (Belgium)",
|
||||||
|
"pl": "Polski",
|
||||||
|
"pt": "Português",
|
||||||
|
"ptBR": "Português (Brasil)",
|
||||||
|
"ro": "Romanian",
|
||||||
|
"ru": "Русский",
|
||||||
|
"sk": "Slovenčina",
|
||||||
|
"svSE": "Swedish (Sweden)",
|
||||||
|
"tr": "Türkçe",
|
||||||
|
"ua": "Українська",
|
||||||
|
"zhCN": "中文 (简体)",
|
||||||
|
"zhTW": "中文 (繁體)"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"createAnAccount": "Δημιουργία λογαριασμού",
|
||||||
|
"loginInstead": "Έχετε ήδη λογαριασμό",
|
||||||
|
"password": "Κωδικός πρόσβασης",
|
||||||
|
"passwordConfirm": "Επιβεβαίωση κωδικού πρόσβασης",
|
||||||
|
"passwordsDontMatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν",
|
||||||
|
"signup": "Εγγραφή",
|
||||||
|
"submit": "Είσοδος",
|
||||||
|
"username": "Όνομα χρήστη",
|
||||||
|
"usernameTaken": "Το όνομα χρήστη χρησιμοποιείται ήδη",
|
||||||
|
"wrongCredentials": "Λάθος όνομα ή/και κωδικός πρόσβασης"
|
||||||
|
},
|
||||||
|
"permanent": "Μόνιμο",
|
||||||
|
"prompts": {
|
||||||
|
"copy": "Αντιγραφή",
|
||||||
|
"copyMessage": "Επιλέξτε τοποθεσία για αντιγραφή των αρχείων σας:",
|
||||||
|
"deleteMessageMultiple": "Είστε σίγουροι ότι θέλετε να διαγράψετε {count} αρχεία;",
|
||||||
|
"deleteMessageSingle": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το αρχείο/φάκελο;",
|
||||||
|
"deleteMessageShare": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτή την κοινοποίηση ({path});",
|
||||||
|
"deleteTitle": "Διαγραφή αρχείων",
|
||||||
|
"displayName": "Εμφάνιση ονόματος:",
|
||||||
|
"download": "Λήψη αρχείων",
|
||||||
|
"downloadMessage": "Επιλέξτε τη μορφή που θέλετε να λάβετε.",
|
||||||
|
"error": "Προέκυψε κάποιο σφάλμα",
|
||||||
|
"fileInfo": "Πληροφορίες αρχείου",
|
||||||
|
"filesSelected": "Επιλέχθηκαν {count} αρχεία.",
|
||||||
|
"lastModified": "Τελευταία τροποποίηση",
|
||||||
|
"move": "Μετακίνηση",
|
||||||
|
"moveMessage": "Επιλέξτε νέα τοποθεσία για τα αρχεία / τους φακέλους σας:",
|
||||||
|
"newArchetype": "Δημιουργία νέας ανάρτησης με βάση έναν αρχέτυπο. Το αρχείο σας θα δημιουργηθεί στο φάκελο περιεχομένου.",
|
||||||
|
"newDir": "Νέος φάκελος",
|
||||||
|
"newDirMessage": "Γράψτε το όνομα του νέου φακέλου.",
|
||||||
|
"newFile": "Νέο αρχείο",
|
||||||
|
"newFileMessage": "Γράψτε το όνομα του νέου αρχείου.",
|
||||||
|
"numberDirs": "Αριθμός φακέλων",
|
||||||
|
"numberFiles": "Αριθμός αρχείων",
|
||||||
|
"rename": "Μετονομασία",
|
||||||
|
"renameMessage": "Εισαγάγετε ένα νέο όνομα για το",
|
||||||
|
"replace": "Αντικατάσταση",
|
||||||
|
"replaceMessage": "Ένα από τα αρχεία που προσπαθείτε να μεταφορτώσετε δημιουργεί σύγκρουση με υπάρχον αρχείο λόγω του ονόματός του. Θέλετε να συνεχίσετε τη μεταφόρτωση ή να αντικαταστήσετε το υπάρχον;\n",
|
||||||
|
"schedule": "Προγραμματισμός",
|
||||||
|
"scheduleMessage": "Επιλέξτε μια ημερομηνία και ώρα για τον προγραμματισμό της δημοσίευσης αυτής της ανάρτησης.",
|
||||||
|
"show": "Εμφάνιση",
|
||||||
|
"size": "Μέγεθος",
|
||||||
|
"upload": "Μεταφόρτωση",
|
||||||
|
"uploadFiles": "Μεταφόρτωση {files} αρχείων…",
|
||||||
|
"uploadMessage": "Επιλέξτε μια επιλογή για τη μεταφόρτωση.",
|
||||||
|
"optionalPassword": "Προαιρετικός κωδικός πρόσβασης"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"images": "Εικόνες",
|
||||||
|
"music": "Μουσική",
|
||||||
|
"pdf": "PDF",
|
||||||
|
"pressToSearch": "Πατήστε Enter για αναζήτηση…",
|
||||||
|
"search": "Αναζήτηση…",
|
||||||
|
"typeToSearch": "Πληκτρολογήστε για αναζήτηση…",
|
||||||
|
"types": "Τύποι",
|
||||||
|
"video": "Βίντεο"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"admin": "Διαχειριστής",
|
||||||
|
"administrator": "Διαχειριστής",
|
||||||
|
"allowCommands": "Εκτέλεση εντολών",
|
||||||
|
"allowEdit": "Επεξεργασία, μετονομασία και διαγραφή αρχείων ή φακέλων",
|
||||||
|
"allowNew": "Δημιουργία νέων αρχείων και φακέλων",
|
||||||
|
"allowPublish": "Δημοσίευση νέων αναρτήσεων και σελίδων",
|
||||||
|
"allowSignup": "Να επιτρέπεται η εγγραφή νέων χρηστών",
|
||||||
|
"avoidChanges": "(αφήστε το κενό για αποφυγή αλλαγών)",
|
||||||
|
"branding": "Εξατομίκευση",
|
||||||
|
"brandingDirectoryPath": "Διαδρομή φακέλου εξατομίκευσης",
|
||||||
|
"brandingHelp": "Μπορείτε να προσαρμόσετε την εμφάνισης της εφαρμογής File Browser αλλάζοντας το όνομά της, αντικαθιστώντας το λογότυπό της, προσθέτοντας προσαρμοσμένα στυλ και ακόμα και απενεργοποιώντας εξωτερικούς συνδέσμους προς το GitHub.\nΓια περισσότερες πληροφορίες σχετικά με αυτές τις προσαρμογές, ελέγξτε το {0}.",
|
||||||
|
"changePassword": "Αλλαγή κωδικού πρόσβασης",
|
||||||
|
"commandRunner": "Εκτέλεση εντολών",
|
||||||
|
"commandRunnerHelp": "Εδώ μπορείτε να ορίσετε εντολές που εκτελούνται στα ονομασμένα γεγονότα και δραστηριότητες. Πρέπει να γράψετε μία εντολή ανά γραμμή. Οι μεταβλητές περιβάλλοντος {0} και {1} θα είναι διαθέσιμες, και θα είναι {0} σχετικές με το {1}. Για περισσότερες πληροφορίες σχετικά με αυτή τη λειτουργία και τις διαθέσιμες μεταβλητές περιβάλλοντος, παρακαλώ διαβάστε το {2}.",
|
||||||
|
"commandsUpdated": "Οι εντολές ενημερώθηκαν!",
|
||||||
|
"createUserDir": "Αυτόματη δημιουργία φακέλου χρήστη κατά την προσθήκη νέου χρήστη",
|
||||||
|
"tusUploads": "Τμηματικές μεταφορές αρχείων",
|
||||||
|
"tusUploadsHelp": "Η εφαρμογή File Browser υποστηρίζει τμηματικές μεταφορτώσεις αρχείων, επιτρέποντας την αποδοτική, αξιόπιστη και συνεχιζόμενη μεταφόρτωση αρχείων ακόμα και σε ασταθείς συνδέσεις δικτύου.",
|
||||||
|
"tusUploadsChunkSize": "Υποδεικνύει το μέγιστο μέγεθος ενός αιτήματος μεταφόρτωσης (για μικρότερες μεταφορές αρχείων θα χρησιμοποιηθούν απευθείας και όχι τμηματικές μεταφορτώσεις). Μπορείτε να εισάγετε έναν ακέραιο αριθμό που υποδηλώνει το μέγεθος σε bytes, ή κείμενο με αριθμό και μονάδα μέτρησης μεγέθους δεδομένων, όπως 10MB, 1GB κλπ.",
|
||||||
|
"tusUploadsRetryCount": "Αριθμός επαναληπτικών δοκιμών που θα πραγματοποιηθούν αν αποτύχει η μεταφόρτωση ενός τμήματος.",
|
||||||
|
"userHomeBasePath": "Βασική διαδρομή αρχείων για τους φακέλους των χρηστών",
|
||||||
|
"userScopeGenerationPlaceholder": "Η εμβέλεια εφαρμογής θα δημιουργηθεί αυτόματα",
|
||||||
|
"createUserHomeDirectory": "Δημιουργία φακέλου χρήστη",
|
||||||
|
"customStylesheet": "Προσαρμοσμένο στυλ εμφάνισης (stylesheet)",
|
||||||
|
"defaultUserDescription": "Αυτές είναι οι προεπιλεγμένες ρυθμίσεις για νέους χρήστες.",
|
||||||
|
"disableExternalLinks": "Απενεργοποίηση εξωτερικών συνδέσμων (εκτός από συνδέσμους προς τις οδηγίες χρήσης)",
|
||||||
|
"disableUsedDiskPercentage": "Απενεργοποίηση γραφήματος ποσοστού χρήσης χώρου αποθήκευσης",
|
||||||
|
"documentation": "οδηγίες χρήσης",
|
||||||
|
"examples": "Παραδείγματα",
|
||||||
|
"executeOnShell": "Εκτέλεση στο κέλυφος",
|
||||||
|
"executeOnShellDescription": "Από προεπιλογή, η εφαρμογή File Browser εκτελεί τις εντολές καλώντας τα προγράμματα των εντολών απευθείας. Αν θέλετε να τις εκτελέσετε σε ένα κέλυφος (όπως το Bash ή το PowerShell), μπορείτε να το καθορίσετε εδώ με τις απαιτούμενες παραμέτρους. Εάν οριστεί, η εντολή που εκτελείτε θα προστίθεται ως παράμετρος. Αυτό ισχύει τόσο για τις εντολές χρήστη όσο και για τους αγκίστρους συμβάντων (event hooks).",
|
||||||
|
"globalRules": "Πρόκειται για ένα γενικό σύνολο κανόνων που επιτρέπουν και απαγορεύουν διάφορες λειτουργίες και ισχύουν για κάθε χρήστη. Μπορείτε να καθορίσετε συγκεκριμένους κανόνες στις ρυθμίσεις κάθε χρήστη για να παρακάμψετε τους γενικούς κανόνες.",
|
||||||
|
"globalSettings": "Γενικές ρυθμίσεις",
|
||||||
|
"hideDotfiles": "Απόκρυψη κρυφών αρχείων (dotfiles)",
|
||||||
|
"insertPath": "Εισάγετε διαδρομή",
|
||||||
|
"insertRegex": "Εισάγετε έκφραση regex",
|
||||||
|
"instanceName": "Όνομα περιβάλλοντος",
|
||||||
|
"language": "Γλώσσα",
|
||||||
|
"lockPassword": "Αποτρέψτε τον χρήστη από την αλλαγή του κωδικού πρόσβασης",
|
||||||
|
"newPassword": "Νέος κωδικός πρόσβασης",
|
||||||
|
"newPasswordConfirm": "Επιβεβαιώστε τον νέο κωδικό πρόσβασης",
|
||||||
|
"newUser": "Νέος χρήστης",
|
||||||
|
"password": "Κωδικός πρόσβασης",
|
||||||
|
"passwordUpdated": "Ο κωδικός πρόσβασης ενημερώθηκε!",
|
||||||
|
"path": "Διαδρομή",
|
||||||
|
"perm": {
|
||||||
|
"create": "Δημιουργία αρχείων και φακέλων",
|
||||||
|
"delete": "Διαγραφή αρχείων και φακέλων",
|
||||||
|
"download": "Λήψη",
|
||||||
|
"execute": "Εκτέλεση εντολών",
|
||||||
|
"modify": "Επεξεργασία αρχείων",
|
||||||
|
"rename": "Μετονομασία ή μετακίνηση αρχείων και φακέλων",
|
||||||
|
"share": "Κοινοποίηση αρχείων"
|
||||||
|
},
|
||||||
|
"permissions": "Δικαιώματα",
|
||||||
|
"permissionsHelp": "Μπορείτε να ορίσετε τον χρήστη ως διαχειριστή ή να επιλέξετε τα δικαιώματα μεμονωμένα. Αν επιλέξετε \"Διαχειριστής\", όλες οι υπόλοιπες επιλογές θα είναι αυτόματα επιλεγμένες. Η διαχείριση χρηστών παραμένει προνόμιο ενός χρήστη με τον ρόλο του διαχειριστή.\n",
|
||||||
|
"profileSettings": "Ρυθμίσεις προφίλ",
|
||||||
|
"ruleExample1": "αποκλείει την πρόσβαση σε οποιοδήποτε κρυφό αρχείο (όπως .git, .gitignore) σε κάθε φάκελο.\n",
|
||||||
|
"ruleExample2": "αποκλείει την πρόσβαση στο αρχείο με το όνομα Caddyfile στον ριζικό φάκελο της εμβέλειας του κανόνα.",
|
||||||
|
"rules": "Κανόνες",
|
||||||
|
"rulesHelp": "Εδώ μπορείτε να ορίσετε ένα σύνολο κανόνων που επιτρέπουν και απαγορεύουν διάφορες λειτουργίες για τον συγκεκριμένο χρήστη. Τα αποκλεισμένα αρχεία δεν θα εμφανίζονται στα περιεχόμενα των αντίστοιχων φακέλων και δεν θα είναι προσβάσιμα από τον χρήστη. Υποστηρίζονται εκφράσεις regex και διαδρομές σχετικές με την εμβέλεια αρχείων των χρηστών.\n",
|
||||||
|
"scope": "Εμβέλεια",
|
||||||
|
"setDateFormat": "Ορισμός ακριβούς μορφής ημερομηνίας",
|
||||||
|
"settingsUpdated": "Οι ρυθμίσεις ενημερώθηκαν!",
|
||||||
|
"shareDuration": "Διάρκεια κοινοποίησης",
|
||||||
|
"shareManagement": "Διαχείριση κοινοποίησης",
|
||||||
|
"shareDeleted": "Η κοινοποίηση διαγράφηκε!",
|
||||||
|
"singleClick": "Χρήση μονού κλικ για να ανοίξετε αρχεία και φακέλους",
|
||||||
|
"themes": {
|
||||||
|
"dark": "Σκοτεινό",
|
||||||
|
"light": "Φωτεινό",
|
||||||
|
"title": "Μοτίβο"
|
||||||
|
},
|
||||||
|
"user": "Χρήστης",
|
||||||
|
"userCommands": "Εντολές χρήστη",
|
||||||
|
"userCommandsHelp": "Μια λίστα με τις διαθέσιμες εντολές για αυτόν το χρήστη, χωρισμένες μεταξύ τους με κενά. Παράδειγμα:\n",
|
||||||
|
"userCreated": "Ο χρήστης δημιουργήθηκε!",
|
||||||
|
"userDefaults": "Προεπιλεγμένες ρυθμίσεις χρήστη",
|
||||||
|
"userDeleted": "Ο χρήστης διαγράφηκε!",
|
||||||
|
"userManagement": "Διαχείριση χρηστών",
|
||||||
|
"userUpdated": "Ο χρήστης ενημερώθηκε!",
|
||||||
|
"username": "Όνομα χρήστη",
|
||||||
|
"users": "Χρήστες"
|
||||||
|
},
|
||||||
|
"sidebar": {
|
||||||
|
"help": "Βοήθεια",
|
||||||
|
"hugoNew": "Νέο Hugo",
|
||||||
|
"login": "Σύνδεση",
|
||||||
|
"logout": "Αποσύνδεση",
|
||||||
|
"myFiles": "Τα αρχεία μου",
|
||||||
|
"newFile": "Νέο αρχείο",
|
||||||
|
"newFolder": "Νέος φάκελος",
|
||||||
|
"preview": "Προεπισκόπηση",
|
||||||
|
"settings": "Ρυθμίσεις",
|
||||||
|
"signup": "Εγγραφή",
|
||||||
|
"siteSettings": "Ρυθμίσεις ιστότοπου"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"linkCopied": "Ο σύνδεσμος αντιγράφηκε!"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"days": "Ημέρες",
|
||||||
|
"hours": "Ώρες",
|
||||||
|
"minutes": "Λεπτά",
|
||||||
|
"seconds": "Δευτερόλεπτα",
|
||||||
|
"unit": "Μονάδα χρόνου"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,9 @@
|
|||||||
"downloadFolder": "Download Folder",
|
"downloadFolder": "Download Folder",
|
||||||
"downloadSelected": "Download Selected"
|
"downloadSelected": "Download Selected"
|
||||||
},
|
},
|
||||||
|
"upload": {
|
||||||
|
"abortUpload": "Are you sure you want to abort?"
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"forbidden": "You don't have permissions to access this.",
|
"forbidden": "You don't have permissions to access this.",
|
||||||
"internal": "Something really went wrong.",
|
"internal": "Something really went wrong.",
|
||||||
@@ -89,6 +92,7 @@
|
|||||||
"hu": "Magyar",
|
"hu": "Magyar",
|
||||||
"ar": "العربية",
|
"ar": "العربية",
|
||||||
"de": "Deutsch",
|
"de": "Deutsch",
|
||||||
|
"el": "Ελληνικά",
|
||||||
"en": "English",
|
"en": "English",
|
||||||
"es": "Español",
|
"es": "Español",
|
||||||
"fr": "Français",
|
"fr": "Français",
|
||||||
@@ -194,14 +198,14 @@
|
|||||||
"userScopeGenerationPlaceholder": "The scope will be auto generated",
|
"userScopeGenerationPlaceholder": "The scope will be auto generated",
|
||||||
"createUserHomeDirectory": "Create user home directory",
|
"createUserHomeDirectory": "Create user home directory",
|
||||||
"customStylesheet": "Custom Stylesheet",
|
"customStylesheet": "Custom Stylesheet",
|
||||||
"defaultUserDescription": "This are the default settings for new users.",
|
"defaultUserDescription": "These are the default settings for new users.",
|
||||||
"disableExternalLinks": "Disable external links (except documentation)",
|
"disableExternalLinks": "Disable external links (except documentation)",
|
||||||
"disableUsedDiskPercentage": "Disable used disk percentage graph",
|
"disableUsedDiskPercentage": "Disable used disk percentage graph",
|
||||||
"documentation": "documentation",
|
"documentation": "documentation",
|
||||||
"examples": "Examples",
|
"examples": "Examples",
|
||||||
"executeOnShell": "Execute on shell",
|
"executeOnShell": "Execute on shell",
|
||||||
"executeOnShellDescription": "By default, File Browser executes the commands by calling their binaries directly. If you want to run them on a shell instead (such as Bash or PowerShell), you can define it here with the required arguments and flags. If set, the command you execute will be appended as an argument. This apply to both user commands and event hooks.",
|
"executeOnShellDescription": "By default, File Browser executes the commands by calling their binaries directly. If you want to run them on a shell instead (such as Bash or PowerShell), you can define it here with the required arguments and flags. If set, the command you execute will be appended as an argument. This apply to both user commands and event hooks.",
|
||||||
"globalRules": "This is a global set of allow and disallow rules. They apply to every user. You can define specific rules on each user's settings to override this ones.",
|
"globalRules": "This is a global set of allow and disallow rules. They apply to every user. You can define specific rules on each user's settings to override these ones.",
|
||||||
"globalSettings": "Global Settings",
|
"globalSettings": "Global Settings",
|
||||||
"hideDotfiles": "Hide dotfiles",
|
"hideDotfiles": "Hide dotfiles",
|
||||||
"insertPath": "Insert the path",
|
"insertPath": "Insert the path",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import he from "./he.json";
|
|||||||
import hu from "./hu.json";
|
import hu from "./hu.json";
|
||||||
import ar from "./ar.json";
|
import ar from "./ar.json";
|
||||||
import de from "./de.json";
|
import de from "./de.json";
|
||||||
|
import el from "./el.json";
|
||||||
import en from "./en.json";
|
import en from "./en.json";
|
||||||
import es from "./es.json";
|
import es from "./es.json";
|
||||||
import fr from "./fr.json";
|
import fr from "./fr.json";
|
||||||
@@ -38,6 +39,9 @@ export function detectLocale() {
|
|||||||
case /^ar.*/i.test(locale):
|
case /^ar.*/i.test(locale):
|
||||||
locale = "ar";
|
locale = "ar";
|
||||||
break;
|
break;
|
||||||
|
case /^el.*/i.test(locale):
|
||||||
|
locale = "el";
|
||||||
|
break;
|
||||||
case /^es.*/i.test(locale):
|
case /^es.*/i.test(locale):
|
||||||
locale = "es";
|
locale = "es";
|
||||||
break;
|
break;
|
||||||
@@ -114,6 +118,7 @@ const i18n = new VueI18n({
|
|||||||
hu: removeEmpty(hu),
|
hu: removeEmpty(hu),
|
||||||
ar: removeEmpty(ar),
|
ar: removeEmpty(ar),
|
||||||
de: removeEmpty(de),
|
de: removeEmpty(de),
|
||||||
|
el: removeEmpty(el),
|
||||||
en: en,
|
en: en,
|
||||||
es: removeEmpty(es),
|
es: removeEmpty(es),
|
||||||
fr: removeEmpty(fr),
|
fr: removeEmpty(fr),
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import i18n from "@/i18n";
|
|||||||
import Vue from "@/utils/vue";
|
import Vue from "@/utils/vue";
|
||||||
import { recaptcha, loginPage } from "@/utils/constants";
|
import { recaptcha, loginPage } from "@/utils/constants";
|
||||||
import { login, validateLogin } from "@/utils/auth";
|
import { login, validateLogin } from "@/utils/auth";
|
||||||
import App from "@/App";
|
import App from "@/App.vue";
|
||||||
|
|
||||||
cssVars();
|
cssVars();
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Router from "vue-router";
|
import Router from "vue-router";
|
||||||
import Login from "@/views/Login";
|
import Login from "@/views/Login.vue";
|
||||||
import Layout from "@/views/Layout";
|
import Layout from "@/views/Layout.vue";
|
||||||
import Files from "@/views/Files";
|
import Files from "@/views/Files.vue";
|
||||||
import Share from "@/views/Share";
|
import Share from "@/views/Share.vue";
|
||||||
import Users from "@/views/settings/Users";
|
import Users from "@/views/settings/Users.vue";
|
||||||
import User from "@/views/settings/User";
|
import User from "@/views/settings/User.vue";
|
||||||
import Settings from "@/views/Settings";
|
import Settings from "@/views/Settings.vue";
|
||||||
import GlobalSettings from "@/views/settings/Global";
|
import GlobalSettings from "@/views/settings/Global.vue";
|
||||||
import ProfileSettings from "@/views/settings/Profile";
|
import ProfileSettings from "@/views/settings/Profile.vue";
|
||||||
import Shares from "@/views/settings/Shares";
|
import Shares from "@/views/settings/Shares.vue";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { baseURL, name } from "@/utils/constants";
|
import { baseURL, name } from "@/utils/constants";
|
||||||
import i18n, { rtlLanguages } from "@/i18n";
|
import i18n, { rtlLanguages } from "@/i18n";
|
||||||
@@ -33,7 +33,7 @@ const titles = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const router = new Router({
|
const router = new Router({
|
||||||
base: baseURL,
|
base: import.meta.env.PROD ? baseURL : "",
|
||||||
mode: "history",
|
mode: "history",
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const getters = {
|
|||||||
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
||||||
selectedCount: (state) => state.selected.length,
|
selectedCount: (state) => state.selected.length,
|
||||||
progress: (state) => {
|
progress: (state) => {
|
||||||
if (state.upload.progress.length == 0) {
|
if (state.upload.progress.length === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,9 +14,7 @@ const getters = {
|
|||||||
return Math.ceil((sum / totalSize) * 100);
|
return Math.ceil((sum / totalSize) * 100);
|
||||||
},
|
},
|
||||||
filesInUploadCount: (state) => {
|
filesInUploadCount: (state) => {
|
||||||
let total =
|
return Object.keys(state.upload.uploads).length + state.upload.queue.length;
|
||||||
Object.keys(state.upload.uploads).length + state.upload.queue.length;
|
|
||||||
return total;
|
|
||||||
},
|
},
|
||||||
filesInUpload: (state) => {
|
filesInUpload: (state) => {
|
||||||
let files = [];
|
let files = [];
|
||||||
@@ -43,6 +41,16 @@ const getters = {
|
|||||||
|
|
||||||
return files.sort((a, b) => a.progress - b.progress);
|
return files.sort((a, b) => a.progress - b.progress);
|
||||||
},
|
},
|
||||||
|
currentPrompt: (state) => {
|
||||||
|
return state.prompts.length > 0
|
||||||
|
? state.prompts[state.prompts.length - 1]
|
||||||
|
: null;
|
||||||
|
},
|
||||||
|
currentPromptName: (_, getters) => {
|
||||||
|
return getters.currentPrompt?.prompt;
|
||||||
|
},
|
||||||
|
uploadSpeed: (state) => state.upload.speedMbyte,
|
||||||
|
eta: (state) => state.upload.eta,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getters;
|
export default getters;
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ const state = {
|
|||||||
reload: false,
|
reload: false,
|
||||||
selected: [],
|
selected: [],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
show: null,
|
prompts: [],
|
||||||
showShell: false,
|
showShell: false,
|
||||||
showConfirm: null,
|
|
||||||
showAction: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ const state = {
|
|||||||
progress: [],
|
progress: [],
|
||||||
queue: [],
|
queue: [],
|
||||||
uploads: {},
|
uploads: {},
|
||||||
|
speedMbyte: 0,
|
||||||
|
eta: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
|
|||||||
@@ -3,30 +3,35 @@ import moment from "moment";
|
|||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
closeHovers: (state) => {
|
closeHovers: (state) => {
|
||||||
state.show = null;
|
state.prompts.pop();
|
||||||
state.showConfirm = null;
|
|
||||||
state.showAction = null;
|
|
||||||
},
|
},
|
||||||
toggleShell: (state) => {
|
toggleShell: (state) => {
|
||||||
|
state.show = null;
|
||||||
state.showShell = !state.showShell;
|
state.showShell = !state.showShell;
|
||||||
},
|
},
|
||||||
showHover: (state, value) => {
|
showHover: (state, value) => {
|
||||||
if (typeof value !== "object") {
|
if (typeof value !== "object") {
|
||||||
state.show = value;
|
state.prompts.push({
|
||||||
|
prompt: value,
|
||||||
|
confirm: null,
|
||||||
|
action: null,
|
||||||
|
props: null,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.show = value.prompt;
|
state.prompts.push({
|
||||||
state.showConfirm = value.confirm;
|
prompt: value.prompt, // Should not be null
|
||||||
if (value.action !== undefined) {
|
confirm: value?.confirm,
|
||||||
state.showAction = value.action;
|
action: value?.action,
|
||||||
}
|
props: value?.props,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
showError: (state) => {
|
showError: (state) => {
|
||||||
state.show = "error";
|
state.prompts.push("error");
|
||||||
},
|
},
|
||||||
showSuccess: (state) => {
|
showSuccess: (state) => {
|
||||||
state.show = "success";
|
state.prompts.push("success");
|
||||||
},
|
},
|
||||||
setLoading: (state, value) => {
|
setLoading: (state, value) => {
|
||||||
state.loading = value;
|
state.loading = value;
|
||||||
@@ -74,8 +79,15 @@ const mutations = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateRequest: (state, value) => {
|
updateRequest: (state, value) => {
|
||||||
|
const selectedItems = state.selected.map((i) => state.req.items[i]);
|
||||||
state.oldReq = state.req;
|
state.oldReq = state.req;
|
||||||
state.req = value;
|
state.req = value;
|
||||||
|
state.selected = [];
|
||||||
|
|
||||||
|
if (!state.req?.items) return;
|
||||||
|
state.selected = state.req.items
|
||||||
|
.filter((item) => selectedItems.some((rItem) => rItem.url === item.url))
|
||||||
|
.map((item) => item.index);
|
||||||
},
|
},
|
||||||
updateClipboard: (state, value) => {
|
updateClipboard: (state, value) => {
|
||||||
state.clipboard.key = value.key;
|
state.clipboard.key = value.key;
|
||||||
@@ -86,6 +98,21 @@ const mutations = {
|
|||||||
state.clipboard.key = "";
|
state.clipboard.key = "";
|
||||||
state.clipboard.items = [];
|
state.clipboard.items = [];
|
||||||
},
|
},
|
||||||
|
setUploadSpeed: (state, value) => {
|
||||||
|
state.upload.speedMbyte = value;
|
||||||
|
},
|
||||||
|
setETA(state, value) {
|
||||||
|
state.upload.eta = value;
|
||||||
|
},
|
||||||
|
resetUpload(state) {
|
||||||
|
state.upload.uploads = {};
|
||||||
|
state.upload.queue = [];
|
||||||
|
state.upload.progress = [];
|
||||||
|
state.upload.sizes = [];
|
||||||
|
state.upload.id = 0;
|
||||||
|
state.upload.speedMbyte = 0;
|
||||||
|
state.upload.eta = 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mutations;
|
export default mutations;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export async function validateLogin() {
|
|||||||
await renew(localStorage.getItem("jwt"));
|
await renew(localStorage.getItem("jwt"));
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
console.warn('Invalid JWT token in storage') // eslint-disable-line
|
console.warn("Invalid JWT token in storage"); // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
frontend/src/utils/index.js
Normal file
6
frontend/src/utils/index.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { partial } from "filesize";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats filesize as KiB/MiB/...
|
||||||
|
*/
|
||||||
|
export const filesize = partial({ base: 2 });
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
|
|
||||||
const errors = {
|
const errors = {
|
||||||
0: {
|
0: {
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
import Preview from "@/views/files/Preview";
|
import Preview from "@/views/files/Preview.vue";
|
||||||
import Listing from "@/views/files/Listing";
|
import Listing from "@/views/files/Listing.vue";
|
||||||
|
|
||||||
function clean(path) {
|
function clean(path) {
|
||||||
return path.endsWith("/") ? path.slice(0, -1) : path;
|
return path.endsWith("/") ? path.slice(0, -1) : path;
|
||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
Errors,
|
Errors,
|
||||||
Preview,
|
Preview,
|
||||||
Listing,
|
Listing,
|
||||||
Editor: () => import("@/views/files/Editor"),
|
Editor: () => import("@/views/files/Editor.vue"),
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
@@ -50,7 +50,7 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["req", "reload", "loading", "show"]),
|
...mapState(["req", "reload", "loading"]),
|
||||||
currentView() {
|
currentView() {
|
||||||
if (this.req.type == undefined) {
|
if (this.req.type == undefined) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from "vuex";
|
import { mapState, mapGetters } from "vuex";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar.vue";
|
||||||
import Prompts from "@/components/prompts/Prompts";
|
import Prompts from "@/components/prompts/Prompts.vue";
|
||||||
import Shell from "@/components/Shell";
|
import Shell from "@/components/Shell.vue";
|
||||||
import UploadFiles from "../components/prompts/UploadFiles";
|
import UploadFiles from "../components/prompts/UploadFiles.vue";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -30,7 +30,7 @@ export default {
|
|||||||
UploadFiles,
|
UploadFiles,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(["isLogged", "progress"]),
|
...mapGetters(["isLogged", "progress", "currentPrompt"]),
|
||||||
...mapState(["user"]),
|
...mapState(["user"]),
|
||||||
isExecEnabled: () => enableExec,
|
isExecEnabled: () => enableExec,
|
||||||
},
|
},
|
||||||
@@ -38,7 +38,7 @@ export default {
|
|||||||
$route: function () {
|
$route: function () {
|
||||||
this.$store.commit("resetSelected");
|
this.$store.commit("resetSelected");
|
||||||
this.$store.commit("multiple", false);
|
this.$store.commit("multiple", false);
|
||||||
if (this.$store.state.show !== "success")
|
if (this.currentPrompt?.prompt !== "success")
|
||||||
this.$store.commit("closeHovers");
|
this.$store.commit("closeHovers");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "settings",
|
name: "settings",
|
||||||
|
|||||||
@@ -182,15 +182,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations, mapGetters } from "vuex";
|
import { mapState, mapMutations, mapGetters } from "vuex";
|
||||||
import { pub as api } from "@/api";
|
import { pub as api } from "@/api";
|
||||||
import filesize from "filesize";
|
import { filesize } from "@/utils";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
import QrcodeVue from "qrcode.vue";
|
import QrcodeVue from "qrcode.vue";
|
||||||
import Item from "@/components/files/ListingItem";
|
import Item from "@/components/files/ListingItem.vue";
|
||||||
import Clipboard from "clipboard";
|
import Clipboard from "clipboard";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ import { theme } from "@/utils/constants";
|
|||||||
import buttons from "@/utils/buttons";
|
import buttons from "@/utils/buttons";
|
||||||
import url from "@/utils/url";
|
import url from "@/utils/url";
|
||||||
|
|
||||||
|
import { version as ace_version } from "ace-builds";
|
||||||
import ace from "ace-builds/src-min-noconflict/ace.js";
|
import ace from "ace-builds/src-min-noconflict/ace.js";
|
||||||
import modelist from "ace-builds/src-min-noconflict/ext-modelist.js";
|
import modelist from "ace-builds/src-min-noconflict/ext-modelist.js";
|
||||||
import "ace-builds/webpack-resolver";
|
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "editor",
|
name: "editor",
|
||||||
@@ -86,6 +86,11 @@ export default {
|
|||||||
mounted: function () {
|
mounted: function () {
|
||||||
const fileContent = this.req.content || "";
|
const fileContent = this.req.content || "";
|
||||||
|
|
||||||
|
ace.config.set(
|
||||||
|
"basePath",
|
||||||
|
`https://cdn.jsdelivr.net/npm/ace-builds@${ace_version}/src-min-noconflict/`
|
||||||
|
);
|
||||||
|
|
||||||
this.editor = ace.edit("editor", {
|
this.editor = ace.edit("editor", {
|
||||||
value: fileContent,
|
value: fileContent,
|
||||||
showPrintMargin: false,
|
showPrintMargin: false,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar showMenu showLogo>
|
<header-bar showMenu showLogo>
|
||||||
<search /> <title />
|
<search />
|
||||||
|
<title />
|
||||||
<action
|
<action
|
||||||
class="search-button"
|
class="search-button"
|
||||||
icon="search"
|
icon="search"
|
||||||
@@ -274,10 +275,10 @@ import * as upload from "@/utils/upload";
|
|||||||
import css from "@/utils/css";
|
import css from "@/utils/css";
|
||||||
import throttle from "lodash.throttle";
|
import throttle from "lodash.throttle";
|
||||||
|
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
import Search from "@/components/Search";
|
import Search from "@/components/Search.vue";
|
||||||
import Item from "@/components/files/ListingItem";
|
import Item from "@/components/files/ListingItem.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "listing",
|
name: "listing",
|
||||||
@@ -297,16 +298,8 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(["req", "selected", "user", "multiple", "selected", "loading"]),
|
||||||
"req",
|
...mapGetters(["selectedCount", "currentPrompt"]),
|
||||||
"selected",
|
|
||||||
"user",
|
|
||||||
"show",
|
|
||||||
"multiple",
|
|
||||||
"selected",
|
|
||||||
"loading",
|
|
||||||
]),
|
|
||||||
...mapGetters(["selectedCount"]),
|
|
||||||
nameSorted() {
|
nameSorted() {
|
||||||
return this.req.sorting.by === "name";
|
return this.req.sorting.by === "name";
|
||||||
},
|
},
|
||||||
@@ -443,7 +436,7 @@ export default {
|
|||||||
},
|
},
|
||||||
keyEvent(event) {
|
keyEvent(event) {
|
||||||
// No prompts are shown
|
// No prompts are shown
|
||||||
if (this.show !== null) {
|
if (this.currentPrompt !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,14 +143,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapGetters, mapState } from "vuex";
|
||||||
import { files as api } from "@/api";
|
import { files as api } from "@/api";
|
||||||
import { resizePreview } from "@/utils/constants";
|
import { resizePreview } from "@/utils/constants";
|
||||||
import url from "@/utils/url";
|
import url from "@/utils/url";
|
||||||
import throttle from "lodash.throttle";
|
import throttle from "lodash.throttle";
|
||||||
import HeaderBar from "@/components/header/HeaderBar";
|
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||||
import Action from "@/components/header/Action";
|
import Action from "@/components/header/Action.vue";
|
||||||
import ExtendedImage from "@/components/files/ExtendedImage";
|
import ExtendedImage from "@/components/files/ExtendedImage.vue";
|
||||||
|
|
||||||
const mediaTypes = ["image", "video", "audio", "blob"];
|
const mediaTypes = ["image", "video", "audio", "blob"];
|
||||||
|
|
||||||
@@ -177,7 +177,8 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["req", "user", "oldReq", "jwt", "loading", "show"]),
|
...mapState(["req", "user", "oldReq", "jwt", "loading"]),
|
||||||
|
...mapGetters(["currentPrompt"]),
|
||||||
hasPrevious() {
|
hasPrevious() {
|
||||||
return this.previousLink !== "";
|
return this.previousLink !== "";
|
||||||
},
|
},
|
||||||
@@ -195,7 +196,7 @@ export default {
|
|||||||
return api.getDownloadURL(this.req, true);
|
return api.getDownloadURL(this.req, true);
|
||||||
},
|
},
|
||||||
showMore() {
|
showMore() {
|
||||||
return this.$store.state.show === "more";
|
return this.currentPrompt?.prompt === "more";
|
||||||
},
|
},
|
||||||
isResizeEnabled() {
|
isResizeEnabled() {
|
||||||
return resizePreview;
|
return resizePreview;
|
||||||
@@ -247,7 +248,7 @@ export default {
|
|||||||
this.$router.replace({ path: this.nextLink });
|
this.$router.replace({ path: this.nextLink });
|
||||||
},
|
},
|
||||||
key(event) {
|
key(event) {
|
||||||
if (this.show !== null) {
|
if (this.currentPrompt !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,10 +223,10 @@
|
|||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
import { settings as api } from "@/api";
|
import { settings as api } from "@/api";
|
||||||
import { enableExec } from "@/utils/constants";
|
import { enableExec } from "@/utils/constants";
|
||||||
import UserForm from "@/components/settings/UserForm";
|
import UserForm from "@/components/settings/UserForm.vue";
|
||||||
import Rules from "@/components/settings/Rules";
|
import Rules from "@/components/settings/Rules.vue";
|
||||||
import Themes from "@/components/settings/Themes";
|
import Themes from "@/components/settings/Themes.vue";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "settings",
|
name: "settings",
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
import { users as api } from "@/api";
|
import { users as api } from "@/api";
|
||||||
import Languages from "@/components/settings/Languages";
|
import Languages from "@/components/settings/Languages.vue";
|
||||||
import i18n, { rtlLanguages } from "@/i18n";
|
import i18n, { rtlLanguages } from "@/i18n";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ import { share as api, users } from "@/api";
|
|||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import Clipboard from "clipboard";
|
import Clipboard from "clipboard";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "shares",
|
name: "shares",
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="$store.state.show === 'deleteUser'" class="card floating">
|
<div v-if="this.currentPromptName === 'deleteUser'" class="card floating">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>Are you sure you want to delete this user?</p>
|
<p>Are you sure you want to delete this user?</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,10 +61,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations, mapGetters } from "vuex";
|
||||||
import { users as api, settings } from "@/api";
|
import { users as api, settings } from "@/api";
|
||||||
import UserForm from "@/components/settings/UserForm";
|
import UserForm from "@/components/settings/UserForm.vue";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
import deepClone from "lodash.clonedeep";
|
import deepClone from "lodash.clonedeep";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -89,6 +89,7 @@ export default {
|
|||||||
return this.$route.path === "/settings/users/new";
|
return this.$route.path === "/settings/users/new";
|
||||||
},
|
},
|
||||||
...mapState(["loading"]),
|
...mapState(["loading"]),
|
||||||
|
...mapGetters(["currentPrompt", "currentPromptName"]),
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route: "fetchData",
|
$route: "fetchData",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState, mapMutations } from "vuex";
|
import { mapState, mapMutations } from "vuex";
|
||||||
import { users as api } from "@/api";
|
import { users as api } from "@/api";
|
||||||
import Errors from "@/views/Errors";
|
import Errors from "@/views/Errors.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "users",
|
name: "users",
|
||||||
|
|||||||
70
frontend/vite.config.js
Normal file
70
frontend/vite.config.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { fileURLToPath, URL } from "node:url";
|
||||||
|
import path from "node:path";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
import legacy from "@vitejs/plugin-legacy";
|
||||||
|
import vue2 from "@vitejs/plugin-vue2";
|
||||||
|
import { compression } from "vite-plugin-compression2";
|
||||||
|
import pluginRewriteAll from "vite-plugin-rewrite-all";
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
vue2(),
|
||||||
|
legacy({
|
||||||
|
targets: ["ie >= 11"],
|
||||||
|
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
|
||||||
|
}),
|
||||||
|
compression({ include: /\.js$/i, deleteOriginalAssets: true }),
|
||||||
|
pluginRewriteAll(), // fixes 404 error with paths containing dot in dev server
|
||||||
|
];
|
||||||
|
|
||||||
|
const resolve = {
|
||||||
|
alias: {
|
||||||
|
vue: "vue/dist/vue.esm.js",
|
||||||
|
"@/": `${path.resolve(__dirname, "src")}/`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(({ command }) => {
|
||||||
|
if (command === "serve") {
|
||||||
|
return {
|
||||||
|
plugins,
|
||||||
|
resolve,
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
"/api/command": {
|
||||||
|
target: "ws://127.0.0.1:8080",
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
"/api": "http://127.0.0.1:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// command === 'build'
|
||||||
|
return {
|
||||||
|
plugins,
|
||||||
|
resolve,
|
||||||
|
base: "",
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
index: fileURLToPath(
|
||||||
|
new URL(`./public/index.html`, import.meta.url)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
renderBuiltUrl(filename, { hostType }) {
|
||||||
|
if (hostType === "js") {
|
||||||
|
return { runtime: `window.__prependStaticUrl("${filename}")` };
|
||||||
|
} else if (hostType === "html") {
|
||||||
|
return `[{[ .StaticURL ]}]/${filename}`;
|
||||||
|
} else {
|
||||||
|
return { relative: true };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
const CompressionPlugin = require("compression-webpack-plugin");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
runtimeCompiler: true,
|
|
||||||
publicPath: "[{[ .StaticURL ]}]",
|
|
||||||
parallel: 2,
|
|
||||||
configureWebpack: {
|
|
||||||
plugins: [
|
|
||||||
new CompressionPlugin({
|
|
||||||
include: /\.js$/,
|
|
||||||
deleteOriginalAssets: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
10
go.mod
10
go.mod
@@ -23,9 +23,9 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||||
go.etcd.io/bbolt v1.3.7
|
go.etcd.io/bbolt v1.3.7
|
||||||
golang.org/x/crypto v0.10.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/image v0.5.0
|
golang.org/x/image v0.10.0
|
||||||
golang.org/x/text v0.10.0
|
golang.org/x/text v0.13.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
@@ -59,8 +59,8 @@ require (
|
|||||||
github.com/ulikunitz/xz v0.5.9 // indirect
|
github.com/ulikunitz/xz v0.5.9 // indirect
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/net v0.11.0 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/sys v0.9.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
27
go.sum
27
go.sum
@@ -292,8 +292,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -307,8 +307,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
|||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -331,6 +331,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -365,8 +366,9 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -387,6 +389,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -428,10 +431,12 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -440,8 +445,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -493,6 +499,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
41
http/auth.go
41
http/auth.go
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TokenExpirationTime = time.Hour * 2
|
DefaultTokenExpirationTime = time.Hour * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type userInfo struct {
|
type userInfo struct {
|
||||||
@@ -101,19 +101,21 @@ func withAdmin(fn handleFunc) handleFunc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
func loginHandler(tokenExpireTime time.Duration) handleFunc {
|
||||||
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
if err != nil {
|
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
||||||
return http.StatusInternalServerError, err
|
if err != nil {
|
||||||
}
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
user, err := auther.Auth(r, d.store.Users, d.settings, d.server)
|
user, err := auther.Auth(r, d.store.Users, d.settings, d.server)
|
||||||
if err == os.ErrPermission {
|
if err == os.ErrPermission {
|
||||||
return http.StatusForbidden, nil
|
return http.StatusForbidden, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
} else {
|
} else {
|
||||||
return printToken(w, r, d, user)
|
return printToken(w, r, d, user, tokenExpireTime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,11 +174,14 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int,
|
|||||||
return http.StatusOK, nil
|
return http.StatusOK, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
func renewHandler(tokenExpireTime time.Duration) handleFunc {
|
||||||
return printToken(w, r, d, d.user)
|
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
})
|
w.Header().Set("X-Renew-Token", "false")
|
||||||
|
return printToken(w, r, d, d.user, tokenExpireTime)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
|
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) {
|
||||||
claims := &authToken{
|
claims := &authToken{
|
||||||
User: userInfo{
|
User: userInfo{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
@@ -191,7 +196,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use
|
|||||||
},
|
},
|
||||||
RegisteredClaims: jwt.RegisteredClaims{
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)),
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(tokenExpirationTime)),
|
||||||
Issuer: "File Browser",
|
Issuer: "File Browser",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ func (d *data) Check(path string) bool {
|
|||||||
|
|
||||||
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
|
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
for k, v := range globalHeaders {
|
||||||
|
w.Header().Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
settings, err := store.Settings.Get()
|
settings, err := store.Settings.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
9
http/headers.go
Normal file
9
http/headers.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build !dev
|
||||||
|
// +build !dev
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
// global headers to append to every response
|
||||||
|
var globalHeaders = map[string]string{
|
||||||
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||||
|
}
|
||||||
15
http/headers_dev.go
Normal file
15
http/headers_dev.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build dev
|
||||||
|
// +build dev
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
// global headers to append to every response
|
||||||
|
// cross-origin headers are necessary to be able to
|
||||||
|
// access them from a different URL during development
|
||||||
|
var globalHeaders = map[string]string{
|
||||||
|
"Cache-Control": "no-cache, no-store, must-revalidate",
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Headers": "*",
|
||||||
|
"Access-Control-Allow-Methods": "*",
|
||||||
|
"Access-Control-Allow-Credentials": "true",
|
||||||
|
}
|
||||||
@@ -48,9 +48,10 @@ func NewHandler(
|
|||||||
|
|
||||||
api := r.PathPrefix("/api").Subrouter()
|
api := r.PathPrefix("/api").Subrouter()
|
||||||
|
|
||||||
api.Handle("/login", monkey(loginHandler, ""))
|
tokenExpirationTime := server.GetTokenExpirationTime(DefaultTokenExpirationTime)
|
||||||
|
api.Handle("/login", monkey(loginHandler(tokenExpirationTime), ""))
|
||||||
api.Handle("/signup", monkey(signupHandler, ""))
|
api.Handle("/signup", monkey(signupHandler, ""))
|
||||||
api.Handle("/renew", monkey(renewHandler, ""))
|
api.Handle("/renew", monkey(renewHandler(tokenExpirationTime), ""))
|
||||||
|
|
||||||
users := api.PathPrefix("/users").Subrouter()
|
users := api.PathPrefix("/users").Subrouter()
|
||||||
users.Handle("", monkey(usersGetHandler, "")).Methods("GET")
|
users.Handle("", monkey(usersGetHandler, "")).Methods("GET")
|
||||||
@@ -66,7 +67,7 @@ func NewHandler(
|
|||||||
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH")
|
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH")
|
||||||
|
|
||||||
api.PathPrefix("/tus").Handler(monkey(tusPostHandler(), "/api/tus")).Methods("POST")
|
api.PathPrefix("/tus").Handler(monkey(tusPostHandler(), "/api/tus")).Methods("POST")
|
||||||
api.PathPrefix("/tus").Handler(monkey(tusHeadHandler(), "/api/tus")).Methods("HEAD")
|
api.PathPrefix("/tus").Handler(monkey(tusHeadHandler(), "/api/tus")).Methods("HEAD", "GET")
|
||||||
api.PathPrefix("/tus").Handler(monkey(tusPatchHandler(), "/api/tus")).Methods("PATCH")
|
api.PathPrefix("/tus").Handler(monkey(tusPatchHandler(), "/api/tus")).Methods("PATCH")
|
||||||
|
|
||||||
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
|
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("x-xss-protection", "1; mode=block")
|
w.Header().Set("x-xss-protection", "1; mode=block")
|
||||||
return handleWithStaticData(w, r, d, assetsFs, "index.html", "text/html; charset=utf-8")
|
return handleWithStaticData(w, r, d, assetsFs, "public/index.html", "text/html; charset=utf-8")
|
||||||
}, "", store, server)
|
}, "", store, server)
|
||||||
|
|
||||||
static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
@@ -113,6 +113,10 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
|||||||
return http.StatusNotFound, nil
|
return http.StatusNotFound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(r.URL.Path, "/") {
|
||||||
|
return http.StatusNotFound, nil
|
||||||
|
}
|
||||||
|
|
||||||
const maxAge = 86400 // 1 day
|
const maxAge = 86400 // 1 day
|
||||||
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package settings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/rules"
|
"github.com/filebrowser/filebrowser/v2/rules"
|
||||||
)
|
)
|
||||||
@@ -47,6 +49,7 @@ type Server struct {
|
|||||||
EnableExec bool `json:"enableExec"`
|
EnableExec bool `json:"enableExec"`
|
||||||
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
||||||
AuthHook string `json:"authHook"`
|
AuthHook string `json:"authHook"`
|
||||||
|
TokenExpirationTime string `json:"tokenExpirationTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean cleans any variables that might need cleaning.
|
// Clean cleans any variables that might need cleaning.
|
||||||
@@ -54,6 +57,19 @@ func (s *Server) Clean() {
|
|||||||
s.BaseURL = strings.TrimSuffix(s.BaseURL, "/")
|
s.BaseURL = strings.TrimSuffix(s.BaseURL, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetTokenExpirationTime(fallback time.Duration) time.Duration {
|
||||||
|
if s.TokenExpirationTime == "" {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
if duration, err := time.ParseDuration(s.TokenExpirationTime); err == nil {
|
||||||
|
return duration
|
||||||
|
} else {
|
||||||
|
log.Printf("[WARN] Failed to parse tokenExpirationTime: %v", err)
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GenerateKey generates a key of 512 bits.
|
// GenerateKey generates a key of 512 bits.
|
||||||
func GenerateKey() ([]byte, error) {
|
func GenerateKey() ([]byte, error) {
|
||||||
b := make([]byte, 64) //nolint:gomnd
|
b := make([]byte, 64) //nolint:gomnd
|
||||||
|
|||||||
Reference in New Issue
Block a user