diff --git a/_assets/src/App.vue b/_assets/src/App.vue index 006ae4bb..43e89b0c 100644 --- a/_assets/src/App.vue +++ b/_assets/src/App.vue @@ -1,295 +1,17 @@ diff --git a/_assets/src/components/Files.vue b/_assets/src/components/Files.vue new file mode 100644 index 00000000..508e8b1d --- /dev/null +++ b/_assets/src/components/Files.vue @@ -0,0 +1,9 @@ + + + diff --git a/_assets/src/components/Login.vue b/_assets/src/components/Login.vue new file mode 100644 index 00000000..10d89b3e --- /dev/null +++ b/_assets/src/components/Login.vue @@ -0,0 +1,117 @@ + + + + + + diff --git a/_assets/src/components/Main.vue b/_assets/src/components/Main.vue new file mode 100644 index 00000000..a0567512 --- /dev/null +++ b/_assets/src/components/Main.vue @@ -0,0 +1,290 @@ + + + diff --git a/_assets/src/css/base.css b/_assets/src/css/base.css index 9c87b58b..395cc1d6 100644 --- a/_assets/src/css/base.css +++ b/_assets/src/css/base.css @@ -40,6 +40,11 @@ pre { word-wrap: break-word; } +input, button { + outline: 0 !important; +} + +input[type="submit"], button { border: 0; padding: .5em 1em; @@ -53,6 +58,7 @@ button { transition: .1s ease all; } +input[type="submit"]:hover, button:hover { background-color: #1E88E5; } diff --git a/_assets/src/main.js b/_assets/src/main.js index 3177fa2a..eac66423 100644 --- a/_assets/src/main.js +++ b/_assets/src/main.js @@ -1,18 +1,15 @@ import Vue from 'vue' import App from './App' import store from './store/store' +import router from './router' Vue.config.productionTip = false -if (window.info === undefined || window.info === null) { - window.alert('Something is wrong, please refresh!') - window.location.reload() -} - /* eslint-disable no-new */ new Vue({ el: '#app', store, + router, template: '', components: { App } }) diff --git a/_assets/src/router/index.js b/_assets/src/router/index.js new file mode 100644 index 00000000..c17eb508 --- /dev/null +++ b/_assets/src/router/index.js @@ -0,0 +1,72 @@ +import Vue from 'vue' +import Router from 'vue-router' +import Login from '@/components/Login' +import Files from '@/components/Files' +import Main from '@/components/Main' +import auth from '@/utils/auth.js' + +Vue.use(Router) + +const router = new Router({ + base: document.querySelector('meta[name="base"]').getAttribute('content'), + mode: 'history', + routes: [ + { + path: '/login', + name: 'Login', + component: Login, + beforeEnter: function (to, from, next) { + auth.loggedIn() + .then(() => { + next({ path: '/files' }) + }) + .catch(() => { + next() + }) + } + }, + { + path: '/*', + component: Main, + meta: { + requiresAuth: true + }, + children: [ + { + path: '/files/*', + name: 'Files', + component: Files + }, + { + path: '/*', + redirect: { + name: 'Files' + } + } + ] + } + ] +}) + +router.beforeEach((to, from, next) => { + if (to.matched.some(record => record.meta.requiresAuth)) { + // this route requires auth, check if logged in + // if not, redirect to login page. + auth.loggedIn() + .then(() => { + next() + }) + .catch(e => { + next({ + path: '/login', + query: { redirect: to.fullPath } + }) + }) + + return + } + + next() +}) + +export default router diff --git a/_assets/src/store/mutations.js b/_assets/src/store/mutations.js index e9c0edab..7fea1859 100644 --- a/_assets/src/store/mutations.js +++ b/_assets/src/store/mutations.js @@ -17,6 +17,7 @@ const mutations = { state.showNewDir = false state.showDownload = false }, + setUser: (state, value) => (state.user = value), multiple: (state, value) => (state.multiple = value), addSelected: (state, value) => (state.selected.push(value)), removeSelected: (state, value) => { diff --git a/_assets/src/store/store.js b/_assets/src/store/store.js index ddf74ac1..330b9d5b 100644 --- a/_assets/src/store/store.js +++ b/_assets/src/store/store.js @@ -6,10 +6,9 @@ import getters from './getters' Vue.use(Vuex) const state = { - user: window.info.user, - req: window.info.req, - webDavURL: window.info.webdavURL, - baseURL: window.info.baseURL, + user: {}, + req: {}, + baseURL: document.querySelector('meta[name="base"]').getAttribute('content'), ssl: (window.location.protocol === 'https:'), selected: [], multiple: false, diff --git a/_assets/src/utils/auth.js b/_assets/src/utils/auth.js new file mode 100644 index 00000000..df2a2eaf --- /dev/null +++ b/_assets/src/utils/auth.js @@ -0,0 +1,59 @@ +import cookie from './cookie' +import store from '@/store/store' +import router from '@/router' + +function parseToken (token) { + document.cookie = `auth=${token}; max-age=86400; path=${store.state.baseURL}` + let res = token.split('.') + let user = JSON.parse(window.atob(res[1])) + store.commit('setUser', user) +} + +function loggedIn () { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open('GET', `${store.state.baseURL}/api/auth/renew`, true) + request.setRequestHeader('Authorization', `Bearer ${cookie('auth')}`) + + request.onload = () => { + if (request.status === 200) { + parseToken(request.responseText) + resolve() + } else { + reject() + } + } + request.onerror = () => reject() + request.send() + }) +} + +function login (user, password) { + let data = {username: user, password: password} + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open('POST', `${store.state.baseURL}/api/auth/get`, true) + + request.onload = () => { + if (request.status === 200) { + parseToken(request.responseText) + resolve() + } else { + reject(request.responseText) + } + } + request.onerror = () => reject() + request.send(JSON.stringify(data)) + }) +} + +function logout () { + document.cookie = `auth='nothing'; max-age=0; path=${store.state.baseURL}` + router.push({path: 'login'}) +} + +export default { + loggedIn: loggedIn, + login: login, + logout: logout +} diff --git a/http.go b/http.go index fa209b94..aac225ff 100644 --- a/http.go +++ b/http.go @@ -56,23 +56,17 @@ func serveHTTP(c *requestContext, w http.ResponseWriter, r *http.Request) (int, return serveAPI(c, w, r) } - // Checks if this request is made to the base path /files. If so, - // shows the index.html page. - if matchURL(r.URL.Path, "/files") { - w.Header().Set("x-frame-options", "SAMEORIGIN") - w.Header().Set("x-content-type", "nosniff") - w.Header().Set("x-xss-protection", "1; mode=block") + // Any other request should show the index.html file. + w.Header().Set("x-frame-options", "SAMEORIGIN") + w.Header().Set("x-content-type", "nosniff") + w.Header().Set("x-xss-protection", "1; mode=block") - return renderFile( - w, - c.fm.assets.MustString("index.html"), - "text/html", - c.fm.RootURL(), - ) - } - - http.Redirect(w, r, c.fm.RootURL()+"/files"+r.URL.Path, http.StatusTemporaryRedirect) - return 0, nil + return renderFile( + w, + c.fm.assets.MustString("index.html"), + "text/html", + c.fm.RootURL(), + ) } // staticHandler handles the static assets path.