working better

Former-commit-id: 682c7d56814a3c9a35fcf55b540e470b5c66d890 [formerly a603a591938ec8edbe3f703772f86ba978ff92de] [formerly 6d1a11fdeb5d3a00c202e125a0873b263a3787cf [formerly 12c466d2aa]]
Former-commit-id: 4f43bbf0b4f91a9528cdf881f4abbdfc098b82cd [formerly 7fc1e010ac54107ff03762ad729ed54beccca02c]
Former-commit-id: 4d464034e98aefbdce39d142a30bf34aa3fd2d2e
pull/726/head
Henrique Dias 2017-07-03 15:19:17 +01:00
parent bae3c341f6
commit 067051ea09
20 changed files with 292 additions and 250 deletions

View File

@ -69,8 +69,7 @@
<script>
import {mapState} from 'vuex'
import Item from './ListingItem'
import webdav from '@/utils/webdav'
import page from '@/utils/page'
import api from '@/utils/api'
export default {
name: 'listing',
@ -138,12 +137,12 @@ export default {
let promises = []
for (let file of files) {
promises.push(webdav.put(window.location.pathname + base + file.name, file))
promises.push(api.put(this.$route.path + base + file.name, file))
}
Promise.all(promises)
.then(() => {
page.reload()
// page.reload()
// buttons.setDone('upload')
})
.catch(e => {

View File

@ -28,8 +28,7 @@
import { mapMutations, mapGetters, mapState } from 'vuex'
import filesize from 'filesize'
import moment from 'moment'
import webdav from '@/utils/webdav.js'
import page from '@/utils/page.js'
import api from '@/utils/api'
export default {
name: 'item',
@ -87,11 +86,13 @@ export default {
let url = this.req.items[i].url
let name = this.req.items[i].name
promises.push(webdav.move(url, this.url + encodeURIComponent(name)))
promises.push(api.move(url, this.url + encodeURIComponent(name)))
}
Promise.all(promises)
.then(() => page.reload())
.then(() => {
// page.reload()
})
.catch(error => console.log(error))
},
click: function (event) {

View File

@ -6,15 +6,15 @@
<search></search>
</div>
<div>
<rename-button v-show="showRenameButton()"></rename-button>
<move-button v-show="showMoveButton()"></move-button>
<delete-button v-show="showDeleteButton()"></delete-button>
<switch-button v-show="req.kind !== 'editor'"></switch-button>
<rename-button v-show="!loading && showRenameButton()"></rename-button>
<move-button v-show="!loading && showMoveButton()"></move-button>
<delete-button v-show="!loading && showDeleteButton()"></delete-button>
<switch-button v-show="!loading && req.kind !== 'editor'"></switch-button>
<download-button></download-button>
<upload-button v-show="showUpload()"></upload-button>
<upload-button v-show="!loading && showUpload()"></upload-button>
<info-button></info-button>
<button v-show="req.kind === 'listing'" @click="$store.commit('multiple', true)" aria-label="Select multiple" class="action">
<button v-show="isListing" @click="$store.commit('multiple', true)" aria-label="Select multiple" class="action">
<i class="material-icons">check_circle</i>
<span>Select</span>
</button>
@ -22,10 +22,10 @@
</header>
<nav>
<a class="action" :href="baseURL + '/'">
<router-link class="action" to="/files/">
<i class="material-icons">folder</i>
<span>My Files</span>
</a>
</router-link>
<div v-if="user.allowNew">
<button @click="$store.commit('showNewDir', true)" aria-label="New directory" title="New directory" class="action">
@ -45,16 +45,16 @@
</button>
</div>
<button @click="logout" class="action" id="logout" tabindex="0" role="button" aria-label="Log out">
<button @click="logout" class="action" id="logout" aria-label="Log out">
<i class="material-icons" title="Logout">exit_to_app</i>
<span>Logout</span>
</button>
</nav>
<main>
<editor v-if="req.kind === 'editor'"></editor>
<listing v-if="req.kind === 'listing'"></listing>
<preview v-if="req.kind === 'preview'"></preview>
<editor v-if="isEditor"></editor>
<listing v-if="isListing"></listing>
<preview v-if="isPreview"></preview>
</main>
<download-prompt v-if="showDownload" :class="{ active: showDownload }"></download-prompt>
@ -65,7 +65,7 @@
<info-prompt v-if="showInfo" :class="{ active: showInfo }"></info-prompt>
<move-prompt v-if="showMove" :class="{ active: showMove }"></move-prompt>
<help v-show="showHelp" :class="{ active: showHelp }"></help>
<div v-show="$store.getters.showOverlay" @click="resetPrompts" class="overlay" :class="{ active: $store.getters.showOverlay }"></div>
<div v-show="showOverlay" @click="resetPrompts" class="overlay" :class="{ active: showOverlay }"></div>
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
</div>
@ -129,7 +129,10 @@ export default {
NewDirPrompt
},
computed: {
...mapGetters(['selectedCount']),
...mapGetters([
'selectedCount',
'showOverlay'
]),
...mapState([
'req',
'user',
@ -143,37 +146,28 @@ export default {
'showNewFile',
'showNewDir',
'showDownload'
])
]),
isListing () {
return this.req.kind === 'listing' && !this.loading
},
isPreview () {
return this.req.kind === 'preview' && !this.loading
},
isEditor () {
return this.req.kind === 'editor' && !this.loading
}
},
data: function () {
return {
plugins: []
plugins: [],
loading: true
}
},
beforeRouteEnter (to, from, next) {
api.fetch(to.params[0])
.then(() => {
next()
})
.catch(error => {
// TODO: 404, 403 and 500!
console.log(error)
window.alert('Something went wrong. Please reload.')
})
created () {
this.fetchData()
},
beforeRouteUpdate (to, from, next) {
this.$store.commit('resetSelected')
this.$store.commit('multiple', false)
api.fetch(to.params[0])
.then(() => {
next()
})
.catch(error => {
// TODO: 404, 403 and 500!
console.log(error)
window.alert('Something went wrong. Please reload.')
})
watch: {
'$route': 'fetchData'
},
mounted () {
updateColumnSizes()
@ -183,38 +177,6 @@ export default {
this.plugins = window.plugins
}
document.title = this.req.name
window.history.replaceState({
url: window.location.pathname,
name: document.title
}, document.title, window.location.pathname)
/* window.addEventListener('popstate', (event) => {
event.preventDefault()
event.stopPropagation()
this.$store.commit('multiple', false)
this.$store.commit('resetSelected')
this.$store.commit('resetPrompts')
let request = new window.XMLHttpRequest()
request.open('GET', event.state.url, true)
request.setRequestHeader('Accept', 'application/json')
request.onload = () => {
if (request.status === 200) {
let req = JSON.parse(request.responseText)
this.$store.commit('updateRequest', req)
document.title = event.state.name
} else {
console.log(request.responseText)
}
}
request.onerror = (error) => { console.log(error) }
request.send()
}) */
window.addEventListener('keydown', (event) => {
// Esc!
if (event.keyCode === 27) {
@ -270,6 +232,33 @@ export default {
})
},
methods: {
fetchData () {
this.loading = true
// Reset selected items and multiple selection.
this.$store.commit('resetSelected')
this.$store.commit('multiple', false)
let url = this.$route.path
if (url === '') url = '/'
if (url[0] !== '/') url = '/' + url
console.log('Going to ' + url)
api.fetch(url)
.then((trueURL) => {
if (!url.endsWith('/') && trueURL.endsWith('/')) {
window.history.replaceState(window.history.state, document.title, window.location.pathname + '/')
}
this.loading = false
})
.catch(error => {
// TODO: 404, 403 and 500!
console.log(error)
this.loading = false
})
},
showUpload: function () {
if (this.req.kind === 'editor') return false
return this.user.allowNew

View File

@ -16,11 +16,11 @@
<audio v-else-if="req.type == 'audio'" :src="raw()" controls></audio>
<video v-else-if="req.type == 'video'" :src="raw()" controls>
Sorry, your browser doesn't support embedded videos,
but don't worry, you can <a href="?download=true">download it</a>
but don't worry, you can <a :href="download()">download it</a>
and watch it with your favorite video player!
</video>
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw()"></object>
<a v-else-if="req.type == 'blob'" href="?download=true">
<a v-else-if="req.type == 'blob'" :href="download()">
<h2 class="message">Download <i class="material-icons">file_download</i></h2>
</a>
<pre v-else >{{ req.content }}</pre>
@ -30,7 +30,7 @@
<script>
import { mapState } from 'vuex'
import page from '../utils/page'
import url from '@/utils/url'
import InfoButton from './buttons/InfoButton'
import DeleteButton from './buttons/DeleteButton'
import RenameButton from './buttons/RenameButton'
@ -46,12 +46,19 @@ export default {
},
computed: mapState(['req']),
methods: {
download: function () {
let url = `${this.$store.state.baseURL}/api/download/`
url += this.req.url.slice(6)
url += `?token=${this.$store.state.jwt}`
return url
},
raw: function () {
return this.req.url + '?raw=true'
return `${this.download()}&inline=true`
},
back: function (event) {
let url = page.removeLastDir(window.location.pathname) + '/'
page.open(url)
let uri = url.removeLastDir(this.$route.path) + '/'
this.$router.push({ path: uri })
},
allowEdit: function (event) {
return this.$store.state.user.allowEdit

View File

@ -26,7 +26,7 @@
<script>
import { mapState } from 'vuex'
import page from '../utils/page'
import url from '@/utils/url'
export default {
name: 'search',
@ -92,7 +92,7 @@ export default {
let uri = window.location.host + window.location.pathname
if (this.$store.state.req.kind !== 'listing') {
uri = page.removeLastDir(uri) + '/'
uri = url.removeLastDir(uri) + '/'
}
uri = `${(this.$store.state.ssl ? 'wss:' : 'ws:')}//${uri}`
@ -110,7 +110,7 @@ export default {
conn.onclose = (event) => {
this.ongoing = false
this.scrollable.scrollTop = this.scrollable.scrollHeight
page.reload()
// page.reload()
}
return

View File

@ -18,7 +18,10 @@ export default {
methods: {
download: function (event) {
if (this.req.kind !== 'listing') {
window.open(`${window.location.pathname}?download=true`)
let url = this.$route.params[0]
url = this.$store.state.baseURL + '/api/download/' + url
url += '?token=' + this.$store.state.jwt
window.open(url)
return
}

View File

@ -12,8 +12,8 @@
<script>
import {mapGetters, mapMutations, mapState} from 'vuex'
import webdav from '@/utils/webdav'
import page from '@/utils/page'
import api from '@/utils/api'
import url from '@/utils/url'
export default {
name: 'delete-prompt',
@ -28,10 +28,10 @@ export default {
// buttons.setLoading('delete')
if (this.req.kind !== 'listing') {
webdav.trash(window.location.pathname)
api.delete(this.$route.path)
.then(() => {
// buttons.setDone('delete')
page.open(page.removeLastDir(window.location.pathname) + '/')
this.$router.push({path: url.removeLastDir(this.$route.path) + '/'})
})
.catch(error => {
// buttons.setDone('delete', false)
@ -49,17 +49,17 @@ export default {
let promises = []
for (let index of this.selected) {
promises.push(webdav.trash(this.req.items[index].url))
promises.push(api.delete(this.req.items[index].url))
}
Promise.all(promises)
.then(() => {
page.reload()
// page.reload()
// buttons.setDone('delete')
})
.catch(error => {
console.log(error)
page.reload()
// page.reload()
// buttons.setDone('delete', false)
})
}

View File

@ -21,7 +21,10 @@ export default {
},
methods: {
download: function (format) {
let uri = `${window.location.pathname}?download=${format}`
let uri = this.$route.params[0]
uri = this.$store.state.baseURL + '/api/download/' + uri
uri += `?token=${this.$store.state.jwt}`
uri += `&format=${format}`
if (this.selectedCount > 0) {
let files = ''

View File

@ -30,6 +30,7 @@
import {mapState, mapGetters} from 'vuex'
import filesize from 'filesize'
import moment from 'moment'
import api from '@/utils/api'
export default {
name: 'info-prompt',
@ -80,28 +81,21 @@ export default {
checksum: function (event, hash) {
event.preventDefault()
let request = new window.XMLHttpRequest()
let link
if (this.selectedCount) {
link = this.req.items[this.selected[0]].url
} else {
link = window.location.pathname
link = this.$route.path
}
request.open('GET', `${link}?checksum=${hash}`, true)
request.onload = () => {
if (request.status >= 300) {
console.log(request.statusText)
return
}
event.target.innerHTML = request.responseText
}
request.onerror = (e) => console.log(e)
request.send()
api.checksum(link, hash)
.then((hash) => {
event.target.innerHTML = hash
})
.catch(error => {
console.log(error)
})
}
}
}

View File

@ -18,8 +18,8 @@
<script>
import { mapState } from 'vuex'
import page from '@/utils/page'
import webdav from '@/utils/webdav'
import url from '@/utils/url'
import api from '@/utils/api'
export default {
name: 'move-prompt',
@ -31,10 +31,10 @@ export default {
},
computed: mapState(['req', 'selected', 'baseURL']),
mounted: function () {
if (window.location.pathname !== this.baseURL + '/') {
if (this.$route.path !== '/files/') {
this.items.push({
name: '..',
url: page.removeLastDir(window.location.pathname) + '/'
url: url.removeLastDir(this.$route.path) + '/'
})
}
@ -70,7 +70,7 @@ export default {
let to = dest + '/' + encodeURIComponent(this.req.items[item].name)
to = to.replace('//', '/')
promises.push(webdav.move(from, to))
promises.push(api.move(from, to))
}
this.$store.commit('showMove', false)
@ -78,7 +78,7 @@ export default {
Promise.all(promises)
.then(() => {
// buttons.setDone('move')
page.open(dest)
this.$router.push({page: dest})
})
.catch(e => {
// buttons.setDone('move', false)
@ -86,16 +86,16 @@ export default {
})
},
next: function (event) {
let url = event.currentTarget.dataset.url
this.json(url)
let uri = event.currentTarget.dataset.url
this.json(uri)
.then((data) => {
this.current = url
this.current = uri
this.items = []
if (url !== this.baseURL + '/') {
if (uri !== this.baseURL + '/') {
this.items.push({
name: '..',
url: page.removeLastDir(url) + '/'
url: url.removeLastDir(uri) + '/'
})
}
@ -105,7 +105,7 @@ export default {
this.items.push({
name: item.name,
url: item.url
url: item.uri
})
}
})

View File

@ -11,8 +11,8 @@
</template>
<script>
import page from '@/utils/page'
import webdav from '@/utils/webdav'
import url from '@/utils/url'
import api from '@/utils/api'
export default {
name: 'new-dir-prompt',
@ -26,23 +26,23 @@ export default {
event.preventDefault()
if (this.new === '') return
let url = window.location.pathname
let uri = window.location.pathname
if (this.$store.state.req.kind !== 'listing') {
url = page.removeLastDir(url) + '/'
uri = url.removeLastDir(uri) + '/'
}
url += this.name + '/'
url = url.replace('//', '/')
uri += this.name + '/'
uri = uri.replace('//', '/')
// buttons.setLoading('newDir')
webdav.create(url)
api.put(uri)
.then(() => {
// buttons.setDone('newDir')
page.open(url)
this.$router.push({ path: uri })
})
.catch(e => {
.catch(error => {
// buttons.setDone('newDir', false)
console.log(e)
console.log(error)
})
this.$store.commit('showNewDir', false)

View File

@ -11,8 +11,8 @@
</template>
<script>
import page from '@/utils/page'
import webdav from '@/utils/webdav'
import url from '@/utils/url'
import api from '@/utils/api'
export default {
name: 'new-file-prompt',
@ -26,23 +26,23 @@ export default {
event.preventDefault()
if (this.new === '') return
let url = window.location.pathname
let uri = window.location.pathname
if (this.$store.state.req.kind !== 'listing') {
url = page.removeLastDir(url) + '/'
uri = url.removeLastDir(uri) + '/'
}
url += this.name
url = url.replace('//', '/')
uri += this.name
uri = uri.replace('//', '/')
// buttons.setLoading('newFile')
webdav.create(url)
api.put(uri)
.then(() => {
// buttons.setDone('newFile')
page.open(url)
this.$router.push({ path: uri })
})
.catch(e => {
.catch(error => {
// buttons.setDone('newFile', false)
console.log(e)
console.log(error)
})
this.$store.commit('showNewFile', false)

View File

@ -12,8 +12,8 @@
<script>
import { mapState } from 'vuex'
import page from '@/utils/page'
import webdav from '@/utils/webdav'
import url from '@/utils/url'
import api from '@/utils/api'
export default {
name: 'rename-prompt',
@ -50,19 +50,21 @@ export default {
}
this.name = encodeURIComponent(this.name)
newLink = page.removeLastDir(oldLink) + '/' + this.name
newLink = url.removeLastDir(oldLink) + '/' + this.name
// buttons.setLoading('rename')
webdav.move(oldLink, newLink)
api.move(oldLink, newLink)
.then(() => {
if (this.req.kind !== 'listing') {
page.open(newLink)
this.$router.push({ path: newLink })
return
}
// TODO: keep selected after reload?
page.reload()
// page.reload()
// buttons.setDone('rename')
console.log('reload')
this.$router.go({ path: this.$route.path })
}).catch(error => {
// buttons.setDone('rename', false)
console.log(error)

View File

@ -25,6 +25,12 @@ const router = new Router({
})
}
},
{
path: '/',
redirect: {
path: '/files/'
}
},
{
path: '/*',
component: Main,
@ -33,7 +39,7 @@ const router = new Router({
},
children: [
{
path: '/files*',
path: '/files/*',
name: 'Files'
},
{

View File

@ -1,26 +1,126 @@
import store from '../store/store'
function removePrefix (url) {
if (url.startsWith('/files')) {
return url.slice(6)
}
return url
}
function fetch (url) {
url = removePrefix(url)
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
let req = JSON.parse(request.responseText)
store.commit('updateRequest', req)
document.title = req.name
resolve()
} else {
reject()
switch (request.status) {
case 200:
let req = JSON.parse(request.responseText)
store.commit('updateRequest', req)
document.title = req.name
resolve(req.url)
break
default:
reject(request.status)
break
}
}
request.onerror = () => reject()
request.onerror = (error) => reject(error)
request.send()
})
}
function rm (url) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('DELETE', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText)
} else {
reject(request.responseText)
}
}
request.onerror = (error) => reject(error)
request.send()
})
}
function put (url) {
url = removePrefix(url)
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('PUT', `${store.state.baseURL}/api/resource${url}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText)
} else {
reject(request.responseText)
}
}
request.onerror = (error) => reject(error)
request.send()
})
}
function move (oldLink, newLink) {
oldLink = removePrefix(oldLink)
newLink = removePrefix(newLink)
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('POST', `${store.state.baseURL}/api/resource${oldLink}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.setRequestHeader('Destination', newLink)
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText)
} else {
reject(request.responseText)
}
}
request.onerror = (error) => reject(error)
request.send()
})
}
function checksum (url, algo) {
url = removePrefix(url)
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('GET', `${store.state.baseURL}/api/checksum${url}?algo=${algo}`, true)
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
request.onload = () => {
if (request.status === 200) {
resolve(request.responseText)
} else {
reject(request.responseText)
}
}
request.onerror = (error) => reject(error)
request.send()
})
}
export default {
fetch
delete: rm,
fetch,
checksum,
move,
put
}

View File

@ -1,87 +0,0 @@
import store from '../store/store'
function convertURL (url) {
return window.location.origin + url.replace(store.state.baseURL + '/', store.state.webDavURL + '/')
}
function move (oldLink, newLink) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
oldLink = convertURL(oldLink)
newLink = newLink.replace(store.state.baseURL + '/', store.state.webDavURL + '/')
newLink = window.location.origin + newLink.substring(store.state.baseURL.length)
request.open('MOVE', oldLink, true)
request.setRequestHeader('Destination', newLink)
request.onload = () => {
if (request.status === 201 || request.status === 204) {
resolve()
} else {
reject(request.statusText)
}
}
request.onerror = () => reject(request.statusText)
request.send()
})
}
function put (link, body, headers = {}) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('PUT', convertURL(link), true)
for (let key in headers) {
request.setRequestHeader(key, headers[key])
}
request.onload = () => {
if (request.status === 201) {
resolve()
} else {
reject(request.statusText)
}
}
request.onerror = () => reject(request.statusText)
request.send(body)
})
}
function trash (link) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open('DELETE', convertURL(link), true)
request.onload = () => {
if (request.status === 204) {
resolve()
} else {
reject(request.statusText)
}
}
request.onerror = () => reject(request.statusText)
request.send()
})
}
function create (link) {
return new Promise((resolve, reject) => {
let request = new window.XMLHttpRequest()
request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), convertURL(link), true)
request.onload = () => {
if (request.status === 201) {
resolve()
} else {
reject(request.statusText)
}
}
request.onerror = () => reject(request.statusText)
request.send()
})
}
export default {
create: create,
trash: trash,
put: put,
move: move
}

19
auth.go
View File

@ -86,16 +86,31 @@ func renewAuthHandler(c *requestContext, w http.ResponseWriter, r *http.Request)
return 0, nil
}
type extractor []string
func (e extractor) ExtractToken(r *http.Request) (string, error) {
token, _ := request.AuthorizationHeaderExtractor.ExtractToken(r)
if token != "" {
return token, nil
}
token, _ = request.ArgumentExtractor{"token"}.ExtractToken(r)
if token != "" {
return token, nil
}
return "", request.ErrNoTokenInRequest
}
// validateAuth is used to validate the authentication and returns the
// User if it is valid.
func validateAuth(c *requestContext, r *http.Request) (bool, *User) {
keyFunc := func(token *jwt.Token) (interface{}, error) {
return c.fm.key, nil
}
var claims claims
token, err := request.ParseFromRequestWithClaims(r,
request.AuthorizationHeaderExtractor,
extractor{},
&claims,
keyFunc,
)

View File

@ -18,7 +18,12 @@ func downloadHandler(c *requestContext, w http.ResponseWriter, r *http.Request)
query := r.URL.Query().Get("format")
if !c.fi.IsDir {
w.Header().Set("Content-Disposition", "attachment; filename="+c.fi.Name)
if r.URL.Query().Get("inline") == "true" {
w.Header().Set("Content-Disposition", "inline")
} else {
w.Header().Set("Content-Disposition", "attachment; filename="+c.fi.Name)
}
http.ServeFile(w, r, c.fi.Path)
return 0, nil
}

View File

@ -99,6 +99,11 @@ func getInfo(url *url.URL, c *FileManager, u *User) (*file, error) {
i.IsDir = info.IsDir()
i.Size = info.Size()
i.Extension = filepath.Ext(i.Name)
if i.IsDir && !strings.HasSuffix(i.URL, "/") {
i.URL += "/"
}
return i, nil
}