commit
dee465ab86
|
@ -50,6 +50,7 @@ type FileOptions struct {
|
|||
ReadHeader bool
|
||||
Token string
|
||||
Checker rules.Checker
|
||||
Content bool
|
||||
}
|
||||
|
||||
// NewFileInfo creates a File object from a path and a given user. This File
|
||||
|
@ -85,7 +86,7 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
|||
return file, nil
|
||||
}
|
||||
|
||||
err = file.detectType(opts.Modify, true, true)
|
||||
err = file.detectType(opts.Modify, opts.Content, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"ace-builds": "^1.4.7",
|
||||
"clipboard": "^2.0.4",
|
||||
"core-js": "^3.9.1",
|
||||
"css-vars-ponyfill": "^2.4.3",
|
||||
"js-base64": "^2.5.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
|
@ -25,7 +26,8 @@
|
|||
"vue-lazyload": "^1.3.3",
|
||||
"vue-router": "^3.1.3",
|
||||
"vuex": "^3.1.2",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
"vuex-router-sync": "^5.0.0",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.2",
|
||||
|
@ -33,6 +35,7 @@
|
|||
"@vue/cli-service": "^4.1.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"compression-webpack-plugin": "^6.0.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
|
@ -1383,6 +1386,46 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/move-file": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
|
||||
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mkdirp": "^1.0.4",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/move-file/node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@npmcli/move-file/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@soda/friendly-errors-webpack-plugin": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.0.tgz",
|
||||
|
@ -4092,6 +4135,165 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-6.0.3.tgz",
|
||||
"integrity": "sha512-xzSWiZWwBs+HHGhlYxw0oFaYL/0VYErEqDHCAJhJ3Mza5fmF5JJ4iaB6Ap2JT68C0UhhmoI4Mh37LVz/THv2Fw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cacache": "^15.0.5",
|
||||
"find-cache-dir": "^3.3.1",
|
||||
"schema-utils": "^3.0.0",
|
||||
"serialize-javascript": "^5.0.1",
|
||||
"webpack-sources": "^1.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/cacache": {
|
||||
"version": "15.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz",
|
||||
"integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@npmcli/move-file": "^1.0.1",
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"infer-owner": "^1.0.4",
|
||||
"lru-cache": "^6.0.0",
|
||||
"minipass": "^3.1.1",
|
||||
"minipass-collect": "^1.0.2",
|
||||
"minipass-flush": "^1.0.5",
|
||||
"minipass-pipeline": "^1.2.2",
|
||||
"mkdirp": "^1.0.3",
|
||||
"p-map": "^4.0.0",
|
||||
"promise-inflight": "^1.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ssri": "^8.0.1",
|
||||
"tar": "^6.0.2",
|
||||
"unique-filename": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/serialize-javascript": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
|
||||
"integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
"integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/compression-webpack-plugin/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/compression/node_modules/bytes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||
|
@ -4663,6 +4865,11 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-vars-ponyfill": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/css-vars-ponyfill/-/css-vars-ponyfill-2.4.3.tgz",
|
||||
"integrity": "sha512-PBfIwjSu27s8kebu8taEYFM8ehVr8o2Qw4H4nSlJzHAJgcduAqxz4oPmYTJuzgauOKaWII9SHWStQ965fxsdZA=="
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
||||
|
@ -9045,6 +9252,25 @@
|
|||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mississippi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
|
||||
|
@ -12746,6 +12972,50 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
||||
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
|
||||
|
@ -14709,6 +14979,11 @@
|
|||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
|
@ -16234,6 +16509,33 @@
|
|||
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
|
||||
"dev": true
|
||||
},
|
||||
"@npmcli/move-file": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
|
||||
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mkdirp": "^1.0.4",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@soda/friendly-errors-webpack-plugin": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.0.tgz",
|
||||
|
@ -18503,6 +18805,120 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"compression-webpack-plugin": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-6.0.3.tgz",
|
||||
"integrity": "sha512-xzSWiZWwBs+HHGhlYxw0oFaYL/0VYErEqDHCAJhJ3Mza5fmF5JJ4iaB6Ap2JT68C0UhhmoI4Mh37LVz/THv2Fw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cacache": "^15.0.5",
|
||||
"find-cache-dir": "^3.3.1",
|
||||
"schema-utils": "^3.0.0",
|
||||
"serialize-javascript": "^5.0.1",
|
||||
"webpack-sources": "^1.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"cacache": {
|
||||
"version": "15.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.6.tgz",
|
||||
"integrity": "sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@npmcli/move-file": "^1.0.1",
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"infer-owner": "^1.0.4",
|
||||
"lru-cache": "^6.0.0",
|
||||
"minipass": "^3.1.1",
|
||||
"minipass-collect": "^1.0.2",
|
||||
"minipass-flush": "^1.0.5",
|
||||
"minipass-pipeline": "^1.2.2",
|
||||
"mkdirp": "^1.0.3",
|
||||
"p-map": "^4.0.0",
|
||||
"promise-inflight": "^1.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ssri": "^8.0.1",
|
||||
"tar": "^6.0.2",
|
||||
"unique-filename": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aggregate-error": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"serialize-javascript": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
|
||||
"integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"ssri": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||
"integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minipass": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
@ -18948,6 +19364,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"css-vars-ponyfill": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/css-vars-ponyfill/-/css-vars-ponyfill-2.4.3.tgz",
|
||||
"integrity": "sha512-PBfIwjSu27s8kebu8taEYFM8ehVr8o2Qw4H4nSlJzHAJgcduAqxz4oPmYTJuzgauOKaWII9SHWStQ965fxsdZA=="
|
||||
},
|
||||
"css-what": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
|
||||
|
@ -22407,6 +22828,24 @@
|
|||
"minipass": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mississippi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
|
||||
|
@ -25512,6 +25951,40 @@
|
|||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
||||
"dev": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
||||
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
|
||||
|
@ -27088,6 +27561,11 @@
|
|||
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
|
||||
"dev": true
|
||||
},
|
||||
"whatwg-fetch": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
|
||||
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"ace-builds": "^1.4.7",
|
||||
"clipboard": "^2.0.4",
|
||||
"core-js": "^3.9.1",
|
||||
"css-vars-ponyfill": "^2.4.3",
|
||||
"js-base64": "^2.5.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
|
@ -27,7 +28,8 @@
|
|||
"vue-lazyload": "^1.3.3",
|
||||
"vue-router": "^3.1.3",
|
||||
"vuex": "^3.1.2",
|
||||
"vuex-router-sync": "^5.0.0"
|
||||
"vuex-router-sync": "^5.0.0",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.2",
|
||||
|
@ -35,6 +37,7 @@
|
|||
"@vue/cli-service": "^4.1.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"compression-webpack-plugin": "^6.0.3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
|
@ -64,6 +67,6 @@
|
|||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
"not ie < 11"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<!-- Inject Some Variables and generate the manifest json -->
|
||||
<script>
|
||||
window.FileBrowser = JSON.parse(`[{[ .Json ]}]`);
|
||||
window.FileBrowser = JSON.parse('[{[ .Json ]}]');
|
||||
|
||||
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
||||
var dynamicManifest = {
|
||||
|
@ -77,7 +77,7 @@
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
#loading .spinner {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
|
@ -87,7 +87,7 @@
|
|||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
#loading .spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
|
@ -97,12 +97,12 @@
|
|||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner .bounce1 {
|
||||
#loading .spinner .bounce1 {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.spinner .bounce2 {
|
||||
#loading .spinner .bounce2 {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ body {
|
|||
#loading {
|
||||
background: var(--background);
|
||||
}
|
||||
#loading .spinner div, #previewer .loading .spinner div {
|
||||
#loading .spinner div, main .spinner div {
|
||||
background: var(--icon);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
mounted() {
|
||||
|
|
|
@ -8,13 +8,18 @@ export async function fetchURL(url, opts) {
|
|||
|
||||
let { headers, ...rest } = opts;
|
||||
|
||||
const res = await fetch(`${baseURL}${url}`, {
|
||||
headers: {
|
||||
"X-Auth": store.state.jwt,
|
||||
...headers,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`${baseURL}${url}`, {
|
||||
headers: {
|
||||
"X-Auth": store.state.jwt,
|
||||
...headers,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
} catch (error) {
|
||||
return { status: 0 };
|
||||
}
|
||||
|
||||
if (res.headers.get("X-Renew-Token") === "true") {
|
||||
await renew(store.state.jwt);
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
@dragover="dragOver"
|
||||
@drop="drop"
|
||||
@click="itemClick"
|
||||
@dblclick="dblclick"
|
||||
@touchstart="touchstart"
|
||||
:data-dir="isDir"
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected"
|
||||
|
@ -96,7 +94,7 @@ export default {
|
|||
// reload the image when the file is replaced
|
||||
const key = Date.parse(this.modified);
|
||||
|
||||
return `${baseURL}/api/preview/thumb/${path}?auth=${this.jwt}&inline=true&k=${key}`;
|
||||
return `${baseURL}/api/preview/thumb/${path}?k=${key}&inline=true`;
|
||||
},
|
||||
isThumbsEnabled() {
|
||||
return enableThumbs;
|
||||
|
@ -153,13 +151,13 @@ export default {
|
|||
for (let i of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[i].url,
|
||||
to: this.url + this.req.items[i].name,
|
||||
to: this.url + encodeURIComponent(this.req.items[i].name),
|
||||
name: this.req.items[i].name,
|
||||
});
|
||||
}
|
||||
|
||||
let base = el.querySelector(".name").innerHTML + "/";
|
||||
let path = this.$route.path + base;
|
||||
// Get url from ListingItem instance
|
||||
let path = el.__vue__.url;
|
||||
let baseItems = (await api.fetch(path)).items;
|
||||
|
||||
let action = (overwrite, rename) => {
|
||||
|
@ -200,6 +198,16 @@ export default {
|
|||
},
|
||||
click: function (event) {
|
||||
if (!this.singleClick && this.selectedCount !== 0) event.preventDefault();
|
||||
|
||||
setTimeout(() => {
|
||||
this.touches = 0;
|
||||
}, 300);
|
||||
|
||||
this.touches++;
|
||||
if (this.touches > 1) {
|
||||
this.open();
|
||||
}
|
||||
|
||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||
this.removeSelected(this.index);
|
||||
return;
|
||||
|
@ -235,19 +243,6 @@ export default {
|
|||
this.resetSelected();
|
||||
this.addSelected(this.index);
|
||||
},
|
||||
dblclick: function () {
|
||||
if (!this.singleClick) this.open();
|
||||
},
|
||||
touchstart() {
|
||||
setTimeout(() => {
|
||||
this.touches = 0;
|
||||
}, 300);
|
||||
|
||||
this.touches++;
|
||||
if (this.touches > 1) {
|
||||
this.open();
|
||||
}
|
||||
},
|
||||
open: function () {
|
||||
this.$router.push({ path: this.url });
|
||||
},
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<strong>{{ $t("prompts.size") }}:</strong>
|
||||
<span id="content_length"></span> {{ humanSize }}
|
||||
</p>
|
||||
<p v-if="selected.length < 2">
|
||||
<p v-if="selected.length < 2" :title="modTime">
|
||||
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||
</p>
|
||||
|
||||
|
@ -110,6 +110,9 @@ export default {
|
|||
|
||||
return moment(this.req.items[this.selected[0]].modified).fromNow();
|
||||
},
|
||||
modTime: function () {
|
||||
return new Date(Date.parse(this.req.modified)).toLocaleString();
|
||||
},
|
||||
name: function () {
|
||||
return this.selectedCount === 0
|
||||
? this.req.name
|
||||
|
|
|
@ -43,6 +43,15 @@
|
|||
word-break: break-all;
|
||||
}
|
||||
|
||||
.share__box__element .button {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.share__box__element .button i {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.share__box__items {
|
||||
text-align: left;
|
||||
flex: 10 0 25em;
|
||||
|
|
|
@ -17,6 +17,48 @@
|
|||
color: var(--blue);
|
||||
}
|
||||
|
||||
main .spinner {
|
||||
display: block;
|
||||
text-align: center;
|
||||
line-height: 0;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
main .spinner > div {
|
||||
width: .8em;
|
||||
height: .8em;
|
||||
margin: 0 .1em;
|
||||
font-size: 1em;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
main .spinner .bounce1 {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
main .spinner .bounce2 {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.delayed {
|
||||
animation: delayed linear 100ms;
|
||||
}
|
||||
|
||||
@keyframes delayed {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* ACTION *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
@ -204,6 +246,20 @@
|
|||
right: 0.5em;
|
||||
}
|
||||
|
||||
#previewer .spinner {
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: calc(50% + 1.85em);
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#previewer .spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* EDITOR */
|
||||
|
||||
#editor-container {
|
||||
|
@ -217,11 +273,6 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
#previewer .loading {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#editor-container #editor {
|
||||
height: calc(100vh - 8.4em);
|
||||
}
|
||||
|
@ -283,7 +334,6 @@
|
|||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
-webkit-transform: rotate(-360deg);
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
"switchView": "Switch view",
|
||||
"toggleSidebar": "Toggle sidebar",
|
||||
"update": "Update",
|
||||
"upload": "Upload"
|
||||
"upload": "Upload",
|
||||
"openFile": "Open file"
|
||||
},
|
||||
"download": {
|
||||
"downloadFile": "Download File",
|
||||
|
@ -43,7 +44,8 @@
|
|||
"errors": {
|
||||
"forbidden": "You don't have permissions to access this.",
|
||||
"internal": "Something really went wrong.",
|
||||
"notFound": "This location can't be reached."
|
||||
"notFound": "This location can't be reached.",
|
||||
"connection": "The server can't be reached."
|
||||
},
|
||||
"files": {
|
||||
"body": "Body",
|
||||
|
@ -214,6 +216,7 @@
|
|||
"settingsUpdated": "Settings updated!",
|
||||
"shareDuration": "Share Duration",
|
||||
"shareManagement": "Share Management",
|
||||
"shareDeleted": "Share deleted!",
|
||||
"singleClick": "Use single clicks to open files and directories",
|
||||
"themes": {
|
||||
"dark": "Dark",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import "whatwg-fetch";
|
||||
import cssVars from "css-vars-ponyfill";
|
||||
import { sync } from "vuex-router-sync";
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
|
@ -7,6 +9,8 @@ import { recaptcha, loginPage } from "@/utils/constants";
|
|||
import { login, validateLogin } from "@/utils/auth";
|
||||
import App from "@/App";
|
||||
|
||||
cssVars();
|
||||
|
||||
sync(store, router);
|
||||
|
||||
async function start() {
|
||||
|
|
|
@ -12,6 +12,8 @@ export function parseToken(token) {
|
|||
|
||||
const data = JSON.parse(Base64.decode(parts[1]));
|
||||
|
||||
document.cookie = `auth=${token}; path=/`;
|
||||
|
||||
localStorage.setItem("jwt", token);
|
||||
store.commit("setJWT", token);
|
||||
store.commit("setUser", data.user);
|
||||
|
@ -81,6 +83,8 @@ export async function signup(username, password) {
|
|||
}
|
||||
|
||||
export function logout() {
|
||||
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
|
||||
|
||||
store.commit("setJWT", "");
|
||||
store.commit("setUser", null);
|
||||
localStorage.setItem("jwt", null);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
<header-bar v-if="showHeader" showMenu showLogo />
|
||||
|
||||
<h2 class="message">
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
<span>{{ message }}</span>
|
||||
<i class="material-icons">{{ info.icon }}</i>
|
||||
<span>{{ $t(info.message) }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -13,6 +13,10 @@
|
|||
import HeaderBar from "@/components/header/HeaderBar";
|
||||
|
||||
const errors = {
|
||||
0: {
|
||||
icon: "cloud_off",
|
||||
message: "errors.connection",
|
||||
},
|
||||
403: {
|
||||
icon: "error",
|
||||
message: "errors.forbidden",
|
||||
|
@ -33,11 +37,17 @@ export default {
|
|||
HeaderBar,
|
||||
},
|
||||
props: ["errorCode", "showHeader"],
|
||||
data: function () {
|
||||
return {
|
||||
icon: errors[this.errorCode].icon,
|
||||
message: this.$t(errors[this.errorCode].message),
|
||||
};
|
||||
computed: {
|
||||
code() {
|
||||
return this.errorCode === "0" ||
|
||||
this.errorCode === "404" ||
|
||||
this.errorCode === "403"
|
||||
? parseInt(this.errorCode)
|
||||
: 500;
|
||||
},
|
||||
info() {
|
||||
return errors[this.code];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -4,10 +4,15 @@
|
|||
|
||||
<breadcrumbs base="/files" />
|
||||
|
||||
<errors v-if="error" :errorCode="errorCode" />
|
||||
<errors v-if="error" :errorCode="error.message" />
|
||||
<component v-else-if="currentView" :is="currentView"></component>
|
||||
<div v-else>
|
||||
<h2 class="message">
|
||||
<h2 class="message delayed">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
<span>{{ $t("files.loading") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -62,11 +67,6 @@ export default {
|
|||
return "preview";
|
||||
}
|
||||
},
|
||||
errorCode() {
|
||||
return this.error.message === "404" || this.error.message === "403"
|
||||
? parseInt(this.error.message)
|
||||
: 500;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchData();
|
||||
|
@ -116,7 +116,7 @@ export default {
|
|||
}
|
||||
|
||||
this.$store.commit("updateRequest", res);
|
||||
document.title = res.name;
|
||||
document.title = `${res.name} - ${this.$route.name}`;
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
|
|
|
@ -34,6 +34,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading">
|
||||
<h2 class="message delayed">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
<span>{{ $t("files.loading") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -49,7 +60,7 @@ export default {
|
|||
HeaderBar,
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
...mapState(["user", "loading"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -19,7 +19,50 @@
|
|||
|
||||
<breadcrumbs :base="'/share/' + hash" />
|
||||
|
||||
<div v-if="!loading">
|
||||
<div v-if="loading">
|
||||
<h2 class="message delayed">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
<span>{{ $t("files.loading") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div v-else-if="error">
|
||||
<div v-if="error.message === '401'">
|
||||
<div class="card floating" id="password">
|
||||
<div v-if="attemptedPasswordLogin" class="share__wrong__password">
|
||||
{{ $t("login.wrongCredentials") }}
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("login.password") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<input
|
||||
v-focus
|
||||
type="password"
|
||||
:placeholder="$t('login.password')"
|
||||
v-model="password"
|
||||
@keyup.enter="fetchData"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat"
|
||||
@click="fetchData"
|
||||
:aria-label="$t('buttons.submit')"
|
||||
:title="$t('buttons.submit')"
|
||||
>
|
||||
{{ $t("buttons.submit") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<errors v-else :errorCode="error.message" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="share">
|
||||
<div class="share__box share__box__info">
|
||||
<div class="share__box__header">
|
||||
|
@ -35,16 +78,30 @@
|
|||
<div class="share__box__element">
|
||||
<strong>{{ $t("prompts.displayName") }}</strong> {{ req.name }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<div class="share__box__element" :title="modTime">
|
||||
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||
</div>
|
||||
<div class="share__box__element">
|
||||
<strong>{{ $t("prompts.size") }}:</strong> {{ humanSize }}
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<a target="_blank" :href="link" class="button button--flat">{{
|
||||
$t("buttons.download")
|
||||
}}</a>
|
||||
<a target="_blank" :href="link" class="button button--flat">
|
||||
<div>
|
||||
<i class="material-icons">file_download</i
|
||||
>{{ $t("buttons.download") }}
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="link + '?inline=true'"
|
||||
class="button button--flat"
|
||||
v-if="!req.isDir"
|
||||
>
|
||||
<div>
|
||||
<i class="material-icons">open_in_new</i
|
||||
>{{ $t("buttons.openFile") }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<qrcode-vue :value="fullLink" size="200" level="M"></qrcode-vue>
|
||||
|
@ -71,7 +128,11 @@
|
|||
readOnly
|
||||
>
|
||||
</item>
|
||||
<div v-if="req.items.length > showLimit" class="item">
|
||||
<div
|
||||
v-if="req.items.length > showLimit"
|
||||
class="item"
|
||||
@click="showLimit += 100"
|
||||
>
|
||||
<div>
|
||||
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
||||
</div>
|
||||
|
@ -106,39 +167,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="error">
|
||||
<div v-if="error.message === '401'">
|
||||
<div class="card floating" id="password">
|
||||
<div v-if="attemptedPasswordLogin" class="share__wrong__password">
|
||||
{{ $t("login.wrongCredentials") }}
|
||||
</div>
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("login.password") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<input
|
||||
v-focus
|
||||
type="password"
|
||||
:placeholder="$t('login.password')"
|
||||
v-model="password"
|
||||
@keyup.enter="fetchData"
|
||||
/>
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat"
|
||||
@click="fetchData"
|
||||
:aria-label="$t('buttons.submit')"
|
||||
:title="$t('buttons.submit')"
|
||||
>
|
||||
{{ $t("buttons.submit") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<errors v-else :errorCode="errorCode" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -168,14 +196,18 @@ export default {
|
|||
},
|
||||
data: () => ({
|
||||
error: null,
|
||||
showLimit: 500,
|
||||
showLimit: 100,
|
||||
password: "",
|
||||
attemptedPasswordLogin: false,
|
||||
hash: null,
|
||||
token: null,
|
||||
}),
|
||||
watch: {
|
||||
$route: "fetchData",
|
||||
$route: function () {
|
||||
this.showLimit = 100;
|
||||
|
||||
this.fetchData();
|
||||
},
|
||||
},
|
||||
created: async function () {
|
||||
const hash = this.$route.params.pathMatch.split("/")[0];
|
||||
|
@ -220,10 +252,8 @@ export default {
|
|||
humanTime: function () {
|
||||
return moment(this.req.modified).fromNow();
|
||||
},
|
||||
errorCode() {
|
||||
return this.error.message === "404" || this.error.message === "403"
|
||||
? parseInt(this.error.message)
|
||||
: 500;
|
||||
modTime: function () {
|
||||
return new Date(Date.parse(this.req.modified)).toLocaleString();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
@ -256,9 +286,11 @@ export default {
|
|||
this.token = file.token || "";
|
||||
|
||||
this.updateRequest(file);
|
||||
this.setLoading(false);
|
||||
document.title = `${file.name} - ${this.$route.name}`;
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
keyEvent(event) {
|
||||
|
|
|
@ -114,8 +114,13 @@
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="$store.state.loading">
|
||||
<h2 class="message">
|
||||
<div v-if="loading">
|
||||
<h2 class="message delayed">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
<span>{{ $t("files.loading") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -254,6 +259,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||
import { users, files as api } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
|
@ -279,10 +285,19 @@ export default {
|
|||
showLimit: 50,
|
||||
dragCounter: 0,
|
||||
width: window.innerWidth,
|
||||
itemWeight: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "selected", "user", "show", "multiple", "selected"]),
|
||||
...mapState([
|
||||
"req",
|
||||
"selected",
|
||||
"user",
|
||||
"show",
|
||||
"multiple",
|
||||
"selected",
|
||||
"loading",
|
||||
]),
|
||||
...mapGetters(["selectedCount"]),
|
||||
nameSorted() {
|
||||
return this.req.sorting.by === "name";
|
||||
|
@ -362,8 +377,14 @@ export default {
|
|||
// Reset the show value
|
||||
this.showLimit = 50;
|
||||
|
||||
// Fill and fit the window with listing items
|
||||
this.fillWindow(true);
|
||||
// Ensures that the listing is displayed
|
||||
Vue.nextTick(() => {
|
||||
// How much every listing item affects the window height
|
||||
this.setItemWeight();
|
||||
|
||||
// Fill and fit the window with listing items
|
||||
this.fillWindow(true);
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted: function () {
|
||||
|
@ -393,7 +414,7 @@ export default {
|
|||
window.removeEventListener("scroll", this.scrollEvent);
|
||||
window.removeEventListener("resize", this.windowsResize);
|
||||
|
||||
if (!this.user.perm.create) return;
|
||||
if (this.user && !this.user.perm.create) return;
|
||||
document.removeEventListener("dragover", this.preventDefault);
|
||||
document.removeEventListener("dragenter", this.dragEnter);
|
||||
document.removeEventListener("dragleave", this.dragLeave);
|
||||
|
@ -484,7 +505,7 @@ export default {
|
|||
for (let i of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[i].url,
|
||||
name: encodeURIComponent(this.req.items[i].name),
|
||||
name: this.req.items[i].name,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -509,7 +530,7 @@ export default {
|
|||
const from = item.from.endsWith("/")
|
||||
? item.from.slice(0, -1)
|
||||
: item.from;
|
||||
const to = this.$route.path + item.name;
|
||||
const to = this.$route.path + encodeURIComponent(item.name);
|
||||
items.push({ from, to, name: item.name });
|
||||
}
|
||||
|
||||
|
@ -631,22 +652,20 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
let base = "";
|
||||
let files = await upload.scanFiles(dt);
|
||||
let items = this.req.items;
|
||||
let path = this.$route.path.endsWith("/")
|
||||
? this.$route.path
|
||||
: this.$route.path + "/";
|
||||
|
||||
if (
|
||||
el !== null &&
|
||||
el.classList.contains("item") &&
|
||||
el.dataset.dir === "true"
|
||||
) {
|
||||
base = el.querySelector(".name").innerHTML + "/";
|
||||
}
|
||||
// Get url from ListingItem instance
|
||||
path = el.__vue__.url;
|
||||
|
||||
let files = await upload.scanFiles(dt);
|
||||
let path = this.$route.path.endsWith("/")
|
||||
? this.$route.path + base
|
||||
: this.$route.path + "/" + base;
|
||||
let items = this.req.items;
|
||||
|
||||
if (base !== "") {
|
||||
try {
|
||||
items = (await api.fetch(path)).items;
|
||||
} catch (error) {
|
||||
|
@ -793,26 +812,28 @@ export default {
|
|||
viewMode: this.user.viewMode === "mosaic" ? "list" : "mosaic",
|
||||
};
|
||||
|
||||
try {
|
||||
await users.update(data, ["viewMode"]);
|
||||
users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
|
||||
// Await ensures correct value for setItemWeight()
|
||||
await this.$store.commit("updateUser", data);
|
||||
// Await ensures correct value for setItemWeight()
|
||||
await this.$store.commit("updateUser", data);
|
||||
|
||||
this.setItemWeight();
|
||||
this.fillWindow();
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
}
|
||||
this.setItemWeight();
|
||||
this.fillWindow();
|
||||
},
|
||||
upload: function () {
|
||||
if (typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined") {
|
||||
if (
|
||||
typeof window.DataTransferItem !== "undefined" &&
|
||||
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||
) {
|
||||
this.$store.commit("showHover", "upload");
|
||||
} else {
|
||||
document.getElementById("upload-input").click();
|
||||
}
|
||||
},
|
||||
setItemWeight() {
|
||||
// Listing element is not displayed
|
||||
if (this.$refs.listing == null) return;
|
||||
|
||||
let itemQuantity = this.req.numDirs + this.req.numFiles;
|
||||
if (itemQuantity > this.showLimit) itemQuantity = this.showLimit;
|
||||
|
||||
|
|
|
@ -46,15 +46,14 @@
|
|||
</template>
|
||||
</header-bar>
|
||||
|
||||
<div class="loading" v-if="loading">
|
||||
<div class="loading delayed" v-if="loading">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="!loading">
|
||||
<template v-else>
|
||||
<div class="preview">
|
||||
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
|
||||
<audio
|
||||
|
@ -176,11 +175,9 @@ export default {
|
|||
if (this.req.type === "image" && !this.fullSize) {
|
||||
return `${baseURL}/api/preview/big${url.encodePath(
|
||||
this.req.path
|
||||
)}?auth=${this.jwt}&k=${key}`;
|
||||
)}?k=${key}`;
|
||||
}
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${
|
||||
this.jwt
|
||||
}&k=${key}`;
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?k=${key}`;
|
||||
},
|
||||
raw() {
|
||||
return `${this.previewUrl}&inline=true`;
|
||||
|
@ -258,7 +255,7 @@ export default {
|
|||
|
||||
if (this.req.subtitles) {
|
||||
this.subtitles = this.req.subtitles.map(
|
||||
(sub) => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`
|
||||
(sub) => `${baseURL}/api/raw${sub}?inline=true`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="row" v-if="settings !== null">
|
||||
<errors v-if="error" :errorCode="error.message" />
|
||||
<div class="row" v-else-if="!loading">
|
||||
<div class="column">
|
||||
<form class="card" @submit.prevent="save">
|
||||
<div class="card-title">
|
||||
|
@ -170,12 +171,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { settings as api } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import UserForm from "@/components/settings/UserForm";
|
||||
import Rules from "@/components/settings/Rules";
|
||||
import Themes from "@/components/settings/Themes";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import Errors from "@/views/Errors";
|
||||
|
||||
export default {
|
||||
name: "settings",
|
||||
|
@ -183,19 +185,23 @@ export default {
|
|||
Themes,
|
||||
UserForm,
|
||||
Rules,
|
||||
Errors,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
error: null,
|
||||
originalSettings: null,
|
||||
settings: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
...mapState(["user", "loading"]),
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
this.setLoading(true);
|
||||
|
||||
const original = await api.get();
|
||||
let settings = { ...original, commands: [] };
|
||||
|
||||
|
@ -211,10 +217,13 @@ export default {
|
|||
this.originalSettings = original;
|
||||
this.settings = settings;
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
capitalize(name, where = "_") {
|
||||
if (where === "caps") where = /(?=[A-Z])/;
|
||||
let splitted = name.split(where);
|
||||
|
|
|
@ -103,12 +103,13 @@ export default {
|
|||
},
|
||||
},
|
||||
created() {
|
||||
this.setLoading(false);
|
||||
this.locale = this.user.locale;
|
||||
this.hideDotfiles = this.user.hideDotfiles;
|
||||
this.singleClick = this.user.singleClick;
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["updateUser"]),
|
||||
...mapMutations(["updateUser", "setLoading"]),
|
||||
async updatePassword(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<errors v-if="error" :errorCode="error.message" />
|
||||
<div class="row" v-else-if="!loading">
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-title">
|
||||
<h2>{{ $t("settings.shareManagement") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content full">
|
||||
<div class="card-content full" v-if="links.length > 0">
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ $t("settings.path") }}</th>
|
||||
|
@ -52,6 +53,10 @@
|
|||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<h2 class="message" v-else>
|
||||
<i class="material-icons">sentiment_dissatisfied</i>
|
||||
<span>{{ $t("files.lonely") }}</span>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,21 +64,28 @@
|
|||
|
||||
<script>
|
||||
import { share as api, users } from "@/api";
|
||||
import moment from "moment";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import moment from "moment";
|
||||
import Clipboard from "clipboard";
|
||||
import { mapState } from "vuex";
|
||||
import Errors from "@/views/Errors";
|
||||
|
||||
export default {
|
||||
name: "shares",
|
||||
computed: mapState(["user"]),
|
||||
components: {
|
||||
Errors,
|
||||
},
|
||||
computed: mapState(["user", "loading"]),
|
||||
data: function () {
|
||||
return {
|
||||
error: null,
|
||||
links: [],
|
||||
clip: null,
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.setLoading(true);
|
||||
|
||||
try {
|
||||
let links = await api.list();
|
||||
if (this.user.perm.admin) {
|
||||
|
@ -87,7 +99,9 @@ export default {
|
|||
}
|
||||
this.links = links;
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -100,6 +114,7 @@ export default {
|
|||
this.clip.destroy();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
deleteLink: async function (event, link) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -108,8 +123,13 @@ export default {
|
|||
confirm: () => {
|
||||
this.$store.commit("closeHovers");
|
||||
|
||||
api.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
try {
|
||||
api.remove(link.hash);
|
||||
this.links = this.links.filter((item) => item.hash !== link.hash);
|
||||
this.$showSuccess(this.$t("settings.shareDeleted"));
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<errors v-if="error" :errorCode="error.message" />
|
||||
<div class="row" v-else-if="!loading">
|
||||
<div class="column">
|
||||
<form v-if="loaded" @submit="save" class="card">
|
||||
<form @submit="save" class="card">
|
||||
<div class="card-title">
|
||||
<h2 v-if="user.id === 0">{{ $t("settings.newUser") }}</h2>
|
||||
<h2 v-else>{{ $t("settings.user") }} {{ user.username }}</h2>
|
||||
|
@ -55,21 +56,23 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations } from "vuex";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { users as api, settings } from "@/api";
|
||||
import UserForm from "@/components/settings/UserForm";
|
||||
import Errors from "@/views/Errors";
|
||||
import deepClone from "lodash.clonedeep";
|
||||
|
||||
export default {
|
||||
name: "user",
|
||||
components: {
|
||||
UserForm,
|
||||
Errors,
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
error: null,
|
||||
originalUser: null,
|
||||
user: {},
|
||||
loaded: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
@ -79,6 +82,7 @@ export default {
|
|||
isNew() {
|
||||
return this.$route.path === "/settings/users/new";
|
||||
},
|
||||
...mapState(["loading"]),
|
||||
},
|
||||
watch: {
|
||||
$route: "fetchData",
|
||||
|
@ -88,8 +92,10 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["closeHovers", "showHover", "setUser"]),
|
||||
...mapMutations(["closeHovers", "showHover", "setUser", "setLoading"]),
|
||||
async fetchData() {
|
||||
this.setLoading(true);
|
||||
|
||||
try {
|
||||
if (this.isNew) {
|
||||
let { defaults } = await settings.get();
|
||||
|
@ -105,10 +111,10 @@ export default {
|
|||
const id = this.$route.params.pathMatch;
|
||||
this.user = { ...(await api.get(id)) };
|
||||
}
|
||||
|
||||
this.loaded = true;
|
||||
} catch (e) {
|
||||
this.$router.push({ path: "/settings/users/new" });
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
deletePrompt() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<errors v-if="error" :errorCode="error.message" />
|
||||
<div class="row" v-else-if="!loading">
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-title">
|
||||
|
@ -41,21 +42,37 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { users as api } from "@/api";
|
||||
import Errors from "@/views/Errors";
|
||||
|
||||
export default {
|
||||
name: "users",
|
||||
components: {
|
||||
Errors,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
error: null,
|
||||
users: [],
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.setLoading(true);
|
||||
|
||||
try {
|
||||
this.users = await api.getAll();
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["loading"]),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
const CompressionPlugin = require("compression-webpack-plugin");
|
||||
|
||||
module.exports = {
|
||||
runtimeCompiler: true,
|
||||
publicPath: "[{[ .StaticURL ]}]",
|
||||
parallel: 2,
|
||||
configureWebpack: {
|
||||
plugins: [
|
||||
new CompressionPlugin({
|
||||
include: /\.js$/,
|
||||
deleteOriginalAssets: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
11
http/auth.go
11
http/auth.go
|
@ -48,11 +48,16 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
|
|||
}
|
||||
|
||||
auth := r.URL.Query().Get("auth")
|
||||
if auth == "" {
|
||||
return "", request.ErrNoTokenInRequest
|
||||
if auth != "" && strings.Count(auth, ".") == 2 {
|
||||
return auth, nil
|
||||
}
|
||||
|
||||
return auth, nil
|
||||
cookie, _ := r.Cookie("auth")
|
||||
if cookie != nil && strings.Count(cookie.Value, ".") == 2 {
|
||||
return cookie.Value, nil
|
||||
}
|
||||
|
||||
return "", request.ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
func withUser(fn handleFunc) handleFunc {
|
||||
|
|
|
@ -49,6 +49,8 @@ func (d *data) Check(path string) bool {
|
|||
|
||||
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
|
||||
settings, err := store.Settings.Get()
|
||||
if err != nil {
|
||||
log.Fatalf("ERROR: couldn't get settings: %v\n", err)
|
||||
|
|
|
@ -57,7 +57,7 @@ func NewHandler(
|
|||
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler(fileCache), "/api/resources")).Methods("PATCH")
|
||||
|
||||
api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET")
|
||||
api.PathPrefix("/share").Handler(monkey(shareGetsHandler, "/api/share")).Methods("GET")
|
||||
|
|
|
@ -71,14 +71,20 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
|
|||
func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgService, fileCache FileCache,
|
||||
file *files.FileInfo, previewSize PreviewSize, enableThumbnails, resizePreview bool) (int, error) {
|
||||
format, err := imgSvc.FormatFromExtension(file.Extension)
|
||||
|
||||
// Unsupported extensions directly return the raw data
|
||||
if err == img.ErrUnsupportedFormat || format == img.FormatGif {
|
||||
return rawFileHandler(w, r, file)
|
||||
}
|
||||
if err != nil {
|
||||
// Unsupported extensions directly return the raw data
|
||||
if err == img.ErrUnsupportedFormat {
|
||||
return rawFileHandler(w, r, file)
|
||||
}
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
isFresh := checkEtag(w, r, file.ModTime.Unix(), file.Size)
|
||||
if isFresh {
|
||||
return http.StatusNotModified, nil
|
||||
}
|
||||
|
||||
cacheKey := previewCacheKey(file.Path, file.ModTime.Unix(), previewSize)
|
||||
cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey)
|
||||
if err != nil {
|
||||
|
@ -89,10 +95,22 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
fd, err := file.Fs.Open(file.Path)
|
||||
resizedImage, err := createPreview(imgSvc, fileCache, file, previewSize, enableThumbnails, resizePreview)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
_, _ = w.Write(resizedImage.Bytes())
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func createPreview(imgSvc ImgService, fileCache FileCache,
|
||||
file *files.FileInfo, previewSize PreviewSize, enableThumbnails, resizePreview bool) (*bytes.Buffer, error) {
|
||||
fd, err := file.Fs.Open(file.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
var (
|
||||
|
@ -102,7 +120,7 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
|||
)
|
||||
|
||||
switch {
|
||||
case previewSize == PreviewSizeBig && resizePreview && format != img.FormatGif:
|
||||
case previewSize == PreviewSizeBig && resizePreview:
|
||||
width = 1080
|
||||
height = 1080
|
||||
options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium))
|
||||
|
@ -111,26 +129,22 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
|||
height = 128
|
||||
options = append(options, img.WithMode(img.ResizeModeFill), img.WithQuality(img.QualityLow), img.WithFormat(img.FormatJpeg))
|
||||
default:
|
||||
if _, err := rawFileHandler(w, r, file); err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
return 0, nil
|
||||
return nil, img.ErrUnsupportedFormat
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
if err := imgSvc.Resize(context.Background(), fd, width, height, buf, options...); err != nil {
|
||||
return 0, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
cacheKey := previewCacheKey(file.Path, file.ModTime.Unix(), previewSize)
|
||||
if err := fileCache.Store(context.Background(), cacheKey, buf.Bytes()); err != nil {
|
||||
fmt.Printf("failed to cache resized image: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
_, _ = w.Write(buf.Bytes())
|
||||
|
||||
return 0, nil
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func previewCacheKey(fPath string, fTime int64, previewSize PreviewSize) string {
|
||||
|
|
|
@ -38,7 +38,7 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
|||
Fs: d.user.Fs,
|
||||
Path: link.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
Token: link.Token,
|
||||
|
|
|
@ -204,6 +204,11 @@ func rawDirHandler(w http.ResponseWriter, r *http.Request, d *data, file *files.
|
|||
}
|
||||
|
||||
func rawFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo) (int, error) {
|
||||
isFresh := checkEtag(w, r, file.ModTime.Unix(), file.Size)
|
||||
if isFresh {
|
||||
return http.StatusNotModified, nil
|
||||
}
|
||||
|
||||
fd, err := file.Fs.Open(file.Path)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
|
145
http/resource.go
145
http/resource.go
|
@ -27,6 +27,7 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
|||
Expand: true,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
Content: true,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
|
@ -63,7 +64,7 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
|
|||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
})
|
||||
|
@ -109,7 +110,7 @@ func resourcePostHandler(fileCache FileCache) handleFunc {
|
|||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
})
|
||||
|
@ -180,64 +181,50 @@ var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
|||
return errToStatus(err), err
|
||||
})
|
||||
|
||||
var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
src := r.URL.Path
|
||||
dst := r.URL.Query().Get("destination")
|
||||
action := r.URL.Query().Get("action")
|
||||
dst, err := url.QueryUnescape(dst)
|
||||
if !d.Check(src) || !d.Check(dst) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
if err != nil {
|
||||
func resourcePatchHandler(fileCache FileCache) handleFunc {
|
||||
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
src := r.URL.Path
|
||||
dst := r.URL.Query().Get("destination")
|
||||
action := r.URL.Query().Get("action")
|
||||
dst, err := url.QueryUnescape(dst)
|
||||
if !d.Check(src) || !d.Check(dst) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
if dst == "/" || src == "/" {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
err = checkParent(src, dst)
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
override := r.URL.Query().Get("override") == "true"
|
||||
rename := r.URL.Query().Get("rename") == "true"
|
||||
if !override && !rename {
|
||||
if _, err = d.user.Fs.Stat(dst); err == nil {
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
}
|
||||
if rename {
|
||||
dst = addVersionSuffix(dst, d.user.Fs)
|
||||
}
|
||||
|
||||
// Permission for overwriting the file
|
||||
if override && !d.user.Perm.Modify {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
err = d.RunHook(func() error {
|
||||
return patchAction(r.Context(), action, src, dst, d, fileCache)
|
||||
}, action, src, dst, d.user)
|
||||
|
||||
return errToStatus(err), err
|
||||
}
|
||||
if dst == "/" || src == "/" {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
if err = checkParent(src, dst); err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
override := r.URL.Query().Get("override") == "true"
|
||||
rename := r.URL.Query().Get("rename") == "true"
|
||||
if !override && !rename {
|
||||
if _, err = d.user.Fs.Stat(dst); err == nil {
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
}
|
||||
if rename {
|
||||
dst = addVersionSuffix(dst, d.user.Fs)
|
||||
}
|
||||
|
||||
// Permission for overwriting the file
|
||||
if override && !d.user.Perm.Modify {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
err = d.RunHook(func() error {
|
||||
switch action {
|
||||
// TODO: use enum
|
||||
case "copy":
|
||||
if !d.user.Perm.Create {
|
||||
return errors.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return fileutils.Copy(d.user.Fs, src, dst)
|
||||
case "rename":
|
||||
if !d.user.Perm.Rename {
|
||||
return errors.ErrPermissionDenied
|
||||
}
|
||||
src = path.Clean("/" + src)
|
||||
dst = path.Clean("/" + dst)
|
||||
|
||||
return fileutils.MoveFile(d.user.Fs, src, dst)
|
||||
default:
|
||||
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
|
||||
}
|
||||
}, action, src, dst, d.user)
|
||||
|
||||
return errToStatus(err), err
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func checkParent(src, dst string) error {
|
||||
rel, err := filepath.Rel(src, dst)
|
||||
|
@ -308,3 +295,43 @@ func delThumbs(ctx context.Context, fileCache FileCache, file *files.FileInfo) e
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func patchAction(ctx context.Context, action, src, dst string, d *data, fileCache FileCache) error {
|
||||
switch action {
|
||||
// TODO: use enum
|
||||
case "copy":
|
||||
if !d.user.Perm.Create {
|
||||
return errors.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return fileutils.Copy(d.user.Fs, src, dst)
|
||||
case "rename":
|
||||
if !d.user.Perm.Rename {
|
||||
return errors.ErrPermissionDenied
|
||||
}
|
||||
src = path.Clean("/" + src)
|
||||
dst = path.Clean("/" + dst)
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: src,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: false,
|
||||
ReadHeader: false,
|
||||
Checker: d,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete thumbnails
|
||||
err = delThumbs(ctx, fileCache, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fileutils.MoveFile(d.user.Fs, src, dst)
|
||||
default:
|
||||
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package http
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
|
@ -71,7 +72,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
|||
}
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
@ -109,6 +110,9 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
|||
return http.StatusNotFound, nil
|
||||
}
|
||||
|
||||
const maxAge = 86400 // 1 day
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%v", maxAge))
|
||||
|
||||
if d.settings.Branding.Files != "" {
|
||||
if strings.HasPrefix(r.URL.Path, "img/") {
|
||||
fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
|
||||
|
@ -127,7 +131,19 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
return handleWithStaticData(w, r, d, assetsFs, r.URL.Path, "application/javascript; charset=utf-8")
|
||||
fileContents, err := fs.ReadFile(assetsFs, r.URL.Path+".gz")
|
||||
if err != nil {
|
||||
return http.StatusNotFound, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Encoding", "gzip")
|
||||
w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
|
||||
|
||||
if _, err := w.Write(fileContents); err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}, "/static/", store, server)
|
||||
|
||||
return index, static
|
||||
|
|
|
@ -3,6 +3,7 @@ package http
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -66,3 +67,11 @@ func stripPrefix(prefix string, h http.Handler) http.Handler {
|
|||
h.ServeHTTP(w, r2)
|
||||
})
|
||||
}
|
||||
|
||||
func checkEtag(w http.ResponseWriter, r *http.Request, fTime, fSize int64) bool {
|
||||
etag := fmt.Sprintf("%x%x", fTime, fSize)
|
||||
w.Header().Set("Cache-Control", "private")
|
||||
w.Header().Set("Etag", etag)
|
||||
|
||||
return r.Header.Get("If-None-Match") == etag
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue