diff options
Diffstat (limited to 'src/site')
| -rw-r--r-- | src/site/components/navbar/Navbar.vue | 10 | ||||
| -rw-r--r-- | src/site/layouts/default.vue | 66 | ||||
| -rw-r--r-- | src/site/middleware/admin.js | 4 | ||||
| -rw-r--r-- | src/site/middleware/auth.js | 2 | ||||
| -rw-r--r-- | src/site/pages/dashboard/index.vue | 124 | ||||
| -rw-r--r-- | src/site/pages/index.vue | 10 | ||||
| -rw-r--r-- | src/site/pages/login.vue | 39 | ||||
| -rw-r--r-- | src/site/pages/register.vue | 10 | ||||
| -rw-r--r-- | src/site/plugins/axios.js | 9 | ||||
| -rw-r--r-- | src/site/store/alert.js | 26 | ||||
| -rw-r--r-- | src/site/store/auth.js | 61 | ||||
| -rw-r--r-- | src/site/store/config.js | 19 | ||||
| -rw-r--r-- | src/site/store/images.js | 56 | ||||
| -rw-r--r-- | src/site/store/index.js | 62 |
14 files changed, 317 insertions, 181 deletions
diff --git a/src/site/components/navbar/Navbar.vue b/src/site/components/navbar/Navbar.vue index 47f90cb..aa4a672 100644 --- a/src/site/components/navbar/Navbar.vue +++ b/src/site/components/navbar/Navbar.vue @@ -65,6 +65,8 @@ </template> <script> +import { mapState, mapGetters } from 'vuex'; + export default { props: { isWhite: { @@ -76,12 +78,8 @@ export default { return { hamburger: false }; }, computed: { - loggedIn() { - return this.$store.state.loggedIn; - }, - config() { - return this.$store.state.config; - } + ...mapGetters({ loggedIn: 'auth/isLoggedIn' }), + ...mapState(['config']) }, methods: { logOut() { diff --git a/src/site/layouts/default.vue b/src/site/layouts/default.vue index 61a257e..08d733f 100644 --- a/src/site/layouts/default.vue +++ b/src/site/layouts/default.vue @@ -12,22 +12,18 @@ <script> import Navbar from '~/components/navbar/Navbar.vue'; import Footer from '~/components/footer/Footer'; +import { mapState } from 'vuex'; + export default { components: { - Navbar, + Navbar, Footer }, - computed: { - config() { - return this.$store.state.config; - }, - alert() { - return this.$store.state.alert; - } - }, + computed: mapState(['config', 'alert']), watch: { - alert() { - if (!this.alert) return; + 'alert.text'() { + console.log('aaaaaaaa'); + if (!this.alert.text) return; this.$buefy.toast.open({ duration: 3500, @@ -36,34 +32,40 @@ export default { type: this.alert.error ? 'is-danger' : 'is-success' }); - setTimeout(() => { - this.$store.dispatch('alert', null); - }, 3500); + this.$store.dispatch('alert/clear', null); } }, mounted() { - console.log(`%c lolisafe %c v${this.config.version} %c`, 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', 'background:#ff015b; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', 'background:transparent'); + console.log( + `%c lolisafe %c v${this.config.version} %c`, + 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', + 'background:#ff015b; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', + 'background:transparent' + ); } }; </script> + <style lang="scss"> - html { overflow: hidden !important; } - .is-fullheight { - min-height: 100vh !important; - height: max-content; - } - .nuxt-app > .section { - min-height: auto !important; - height: auto !important; - } - @import "~/assets/styles/style.scss"; - @import "~/assets/styles/icons.min.css"; +html { + overflow: hidden !important; +} +.is-fullheight { + min-height: 100vh !important; + height: max-content; +} +.nuxt-app > .section { + min-height: auto !important; + height: auto !important; +} +@import '~/assets/styles/style.scss'; +@import '~/assets/styles/icons.min.css'; </style> <style lang="scss" scoped> - .default-body { - align-items: baseline !important; - } - .scroll-area { - height: 100vh; - } +.default-body { + align-items: baseline !important; +} +.scroll-area { + height: 100vh; +} </style> diff --git a/src/site/middleware/admin.js b/src/site/middleware/admin.js index fcac9c6..5c09220 100644 --- a/src/site/middleware/admin.js +++ b/src/site/middleware/admin.js @@ -1,5 +1,5 @@ export default function({ store, redirect }) { // If the user is not authenticated - if (!store.state.user) return redirect('/login'); - if (!store.state.user.isAdmin) return redirect('/dashboard'); + if (!store.state.auth.user) return redirect('/login'); + if (!store.state.auth.user.isAdmin) return redirect('/dashboard'); } diff --git a/src/site/middleware/auth.js b/src/site/middleware/auth.js index 58a372e..c3f339c 100644 --- a/src/site/middleware/auth.js +++ b/src/site/middleware/auth.js @@ -1,6 +1,6 @@ export default function({ store, redirect }) { // If the user is not authenticated - if (!store.state.loggedIn) { + if (!store.state.auth.loggedIn) { return redirect('/login'); } } diff --git a/src/site/pages/dashboard/index.vue b/src/site/pages/dashboard/index.vue index 0eb9532..6c1b99b 100644 --- a/src/site/pages/dashboard/index.vue +++ b/src/site/pages/dashboard/index.vue @@ -6,27 +6,55 @@ <Sidebar /> </div> <div class="column"> - <h2 class="subtitle">Your uploaded files</h2> + <nav class="level"> + <div class="level-left"> + <div class="level-item"> + <h2 class="subtitle">Your uploaded files</h2> + </div> + </div> + <div class="level-right"> + <div class="level-item"> + <b-field> + <b-input + placeholder="Search" + type="search"/> + <p class="control"> + <button + outlined + class="button is-primary"> + Search + </button> + </p> + </b-field> + </div> + </div> + </nav> <hr> - <Grid v-if="count" - :files="files" - :enableSearch="false" - class="grid" /> + <b-loading :active="images.isLoading" /> - <b-pagination - v-if="count > perPage" - :total="count" - :per-page="perPage" - :current.sync="current" - class="pagination" - icon-prev="icon-interface-arrow-left" - icon-next="icon-interface-arrow-right" - icon-pack="icon" - aria-next-label="Next page" - aria-previous-label="Previous page" - aria-page-label="Page" - aria-current-label="Current page" /> + <Grid v-if="totalFiles" + :files="images.files" + :enableSearch="false" + class="grid"> + <template v-slot:pagination> + <b-pagination + v-if="shouldPaginate" + :total="totalFiles" + :per-page="limit" + :current.sync="current" + range-before="2" + range-after="2" + class="pagination-slot" + icon-prev="icon-interface-arrow-left" + icon-next="icon-interface-arrow-right" + icon-pack="icon" + aria-next-label="Next page" + aria-previous-label="Previous page" + aria-page-label="Page" + aria-current-label="Current page" /> + </template> + </Grid> </div> </div> </div> @@ -34,6 +62,8 @@ </template> <script> +import { mapState, mapGetters, mapActions } from 'vuex'; + import Sidebar from '~/components/sidebar/Sidebar.vue'; import Grid from '~/components/grid/Grid.vue'; @@ -42,44 +72,34 @@ export default { Sidebar, Grid }, - middleware: 'auth', + middleware: ['auth', ({ store }) => { + store.dispatch('images/fetch'); + }], data() { return { - files: [], - count: 0, - current: 1, - perPage: 30 + current: 1 }; }, + computed: { + ...mapGetters({ + totalFiles: 'images/getTotalFiles', + shouldPaginate: 'images/shouldPaginate', + limit: 'images/getLimit' + }), + ...mapState(['images']) + }, metaInfo() { return { title: 'Uploads' }; }, watch: { - current: 'getFiles' - }, - async asyncData({ $axios, route }) { - const perPage = 30; - const current = 1; // current page - - try { - const response = await $axios.$get(`files`, { params: { page: current, limit: perPage }}); - return { - files: response.files || [], - count: response.count || 0, - current, - perPage - }; - } catch (error) { - console.error(error); - return { files: [] }; - } + current: 'fetchPaginate' }, methods: { - async getFiles() { - // TODO: Cache a few pages once fetched - const response = await this.$axios.$get(`files`, { params: { page: this.current, limit: this.perPage }}); - this.files = response.files; - this.count = response.count; + ...mapActions({ + fetch: 'images/fetch' + }), + fetchPaginate() { + this.fetch(this.current) } } }; @@ -89,4 +109,14 @@ export default { div.grid { margin-bottom: 1rem; } -</style>
\ No newline at end of file + + .pagination-slot { + padding: 1rem 0; + } +</style> + +<style lang="scss"> + .pagination-slot > .pagination-previous, .pagination-slot > .pagination-next { + display: none !important; + } +</style> diff --git a/src/site/pages/index.vue b/src/site/pages/index.vue index 707ae67..bb35be3 100644 --- a/src/site/pages/index.vue +++ b/src/site/pages/index.vue @@ -28,6 +28,8 @@ </div> </template> <script> +import { mapState, mapGetters } from 'vuex'; + import Logo from '~/components/logo/Logo.vue'; import Uploader from '~/components/uploader/Uploader.vue'; import Links from '~/components/home/links/Links.vue'; @@ -43,12 +45,8 @@ export default { return { albums: [] }; }, computed: { - loggedIn() { - return this.$store.state.loggedIn; - }, - config() { - return this.$store.state.config; - } + ...mapGetters({ loggedIn: 'auth/isLoggedIn' }), + ...mapState(['config']) } }; </script> diff --git a/src/site/pages/login.vue b/src/site/pages/login.vue index 514cbc5..3c43755 100644 --- a/src/site/pages/login.vue +++ b/src/site/pages/login.vue @@ -63,6 +63,8 @@ </template> <script> +import { mapState } from 'vuex'; + export default { name: 'Login', data() { @@ -74,40 +76,31 @@ export default { isLoading: false }; }, - computed: { - config() { - return this.$store.state.config; - } - }, + computed: mapState(['config', 'auth']), metaInfo() { return { title: 'Login' }; }, + created() { + if (this.auth.loggedIn) { + this.redirect(); + } + }, methods: { async login() { - if (this.isLoading) return; - if (!this.username || !this.password) { - this.$store.dispatch('alert', { + if (this.auth.isLoading) return; + + const { username, password } = this; + if (!username || !password) { + this.$store.dispatch('alert/set', { text: 'Please fill both fields before attempting to log in.', error: true }); return; } - this.isLoading = true; - - try { - const data = await this.$axios.$post(`auth/login`, { - username: this.username, - password: this.password - }); - this.$axios.setToken(data.token, 'Bearer'); - document.cookie = `token=${encodeURIComponent(data.token)}`; - this.$store.dispatch('login', { token: data.token, user: data.user }); + await this.$store.dispatch('auth/login', { username, password }); + if (this.auth.loggedIn) { this.redirect(); - } catch (error) { - // - } finally { - this.isLoading = false; } }, /* @@ -126,7 +119,7 @@ export default { }); },*/ redirect() { - this.$store.commit('loggedIn', true); + console.log('redirect'); if (typeof this.$route.query.redirect !== 'undefined') { this.$router.push(this.$route.query.redirect); return; diff --git a/src/site/pages/register.vue b/src/site/pages/register.vue index c102abd..92eb35a 100644 --- a/src/site/pages/register.vue +++ b/src/site/pages/register.vue @@ -42,6 +42,8 @@ </template> <script> +import { mapState } from 'vuex'; + export default { name: 'Register', data() { @@ -52,11 +54,7 @@ export default { isLoading: false }; }, - computed: { - config() { - return this.$store.state.config; - } - }, + computed: mapState(['config', 'auth']), metaInfo() { return { title: 'Register' }; }, @@ -72,7 +70,7 @@ export default { } if (this.password !== this.rePassword) { this.$store.dispatch('alert', { - text: 'Passwords don\'t match', + text: "Passwords don't match", error: true }); return; diff --git a/src/site/plugins/axios.js b/src/site/plugins/axios.js index 843a258..cff149c 100644 --- a/src/site/plugins/axios.js +++ b/src/site/plugins/axios.js @@ -1,21 +1,22 @@ export default function({ $axios, store }) { $axios.setHeader('accept', 'application/vnd.lolisafe.json'); + $axios.onRequest(config => { - if (store.state.token) { - config.headers.common['Authorization'] = `bearer ${store.state.token}`; + if (store.state.auth.token) { + config.headers.common['Authorization'] = `bearer ${store.state.auth.token}`; } }); $axios.onError(error => { if (process.env.development) console.error('[AXIOS Error]', error); if (process.browser) { - store.dispatch('alert', { + store.dispatch('alert/set', { text: error.response.data.message, error: true }); if (error.response.data.message.indexOf('Token expired') !== -1) { - store.dispatch('logout'); + store.dispatch('auth/logout'); } } }); diff --git a/src/site/store/alert.js b/src/site/store/alert.js new file mode 100644 index 0000000..78c0eaf --- /dev/null +++ b/src/site/store/alert.js @@ -0,0 +1,26 @@ +/* eslint-disable no-shadow */ +const getDefaultState = () => ({ + text: null, + error: false +}); + +export const state = getDefaultState; + +export const actions = { + set({ commit }, data) { + commit('set', data); + }, + clear({ commit }) { + commit('clear'); + } +}; + +export const mutations = { + set(state, { text, error }) { + state.text = text; + state.error = error; + }, + clear(state) { + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/auth.js b/src/site/store/auth.js new file mode 100644 index 0000000..a62a6ec --- /dev/null +++ b/src/site/store/auth.js @@ -0,0 +1,61 @@ +/* eslint-disable no-shadow */ +// only used so I could keep the convention of naming the first param as "state" in mutations +const getDefaultState = () => ({ + loggedIn: false, + isLoading: false, + user: null, + token: null +}); + +export const state = getDefaultState; + +export const getters = { + isLoggedIn: state => state.loggedIn +}; + +export const actions = { + async verify({ commit, dispatch }) { + try { + const response = await this.$axios.$get('verify'); + commit('loginSuccess', response); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async login({ commit, dispatch }, { username, password }) { + commit('loginRequest'); + + try { + const data = await this.$axios.$post(`auth/login`, { username, password }); + this.$axios.setToken(data.token, 'Bearer'); + + commit('setToken', data.token); + commit('loginSuccess', { token: data.token, user: data.user }); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + logout({ commit }) { + commit('logout'); + } +}; + +export const mutations = { + setToken(state, token) { + state.token = token; + }, + loginRequest(state) { + state.isLoading = true; + }, + loginSuccess(state, { user }) { + this.$cookies.set('token', state.token); + state.user = user; + state.loggedIn = true; + state.isLoading = false; + }, + logout(state) { + this.$cookies.remove('token'); + // reset state to default + Object.assign(state, getDefaultState()); + } +}; diff --git a/src/site/store/config.js b/src/site/store/config.js new file mode 100644 index 0000000..6202f84 --- /dev/null +++ b/src/site/store/config.js @@ -0,0 +1,19 @@ +/* eslint-disable no-shadow */ +export const state = () => ({ + development: true, + version: '4.0.0', + URL: 'http://localhost:8080', + baseURL: 'http://localhost:8080/api', + serviceName: '', + maxFileSize: 100, + chunkSize: 90, + maxLinksPerAlbum: 5, + publicMode: false, + userAccounts: false +}); + +export const mutations = { + set(state, config) { + Object.assign(state, config); + } +}; diff --git a/src/site/store/images.js b/src/site/store/images.js new file mode 100644 index 0000000..e87dac1 --- /dev/null +++ b/src/site/store/images.js @@ -0,0 +1,56 @@ +/* eslint-disable no-shadow */ +export const state = () => ({ + files: [], + isLoading: false, + pagination: { + page: 1, + limit: 30, + totalFiles: 0 + } +}); + +export const getters = { + getTotalFiles: state => state.pagination.totalFiles, + getFetchedCount: state => state.files.length, + shouldPaginate: ({ pagination }) => pagination.totalFiles > pagination.limit, + getLimit: ({ pagination }) => pagination.limit +}; + +export const actions = { + async fetch({ commit, dispatch, state }, page) { + commit('setIsLoading'); + + page = page || 1; + + try { + const response = await this.$axios.$get(`files`, { params: { limit: state.pagination.limit, page } }); + + commit('updateFiles', { files: response.files }); + commit('updatePaginationMeta', { totalFiles: response.count, page }); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + }, + async fetchById({ commit, dispatch }) { + try { + const response = await this.$axios.$get('verify'); + commit('loginSuccess', response); + } catch (e) { + dispatch('alert/set', { text: e.message, error: true }, { root: true }); + } + } +}; + +export const mutations = { + setIsLoading(state) { + state.isLoading = true; + }, + updateFiles(state, { files }) { + state.files = files || []; + state.isLoading = false; + }, + updatePaginationMeta(state, { page, totalFiles }) { + state.pagination.page = page || 1; + state.pagination.totalFiles = totalFiles || 0; + } +}; diff --git a/src/site/store/index.js b/src/site/store/index.js index 1fc2272..8f910ae 100644 --- a/src/site/store/index.js +++ b/src/site/store/index.js @@ -1,66 +1,20 @@ import config from '../../../dist/config.json'; -export const state = () => ({ - loggedIn: false, - user: null, - token: null, - config: null, - alert: null -}); - -/* eslint-disable no-shadow */ -export const mutations = { - loggedIn(state, payload) { - state.loggedIn = payload; - }, - user(state, payload) { - state.user = payload; - }, - token(state, payload) { - state.token = payload; - }, - config(state, payload) { - state.config = payload; - }, - alert(state, payload) { - state.alert = payload; - } -}; export const actions = { - async nuxtClientInit({ commit, dispatch }, { app, req }) { - commit('config', config); + async nuxtClientInit({ commit, dispatch }) { + commit('config/set', config); const cookies = this.$cookies.getAll(); - if (!cookies.token) return dispatch('logout'); + if (!cookies.token) return dispatch('auth/logout'); - commit('token', cookies.token); - try { - const response = await this.$axios.$get('verify'); - dispatch('login', { - token: cookies.token, - user: response.user - }); - } catch (error) { - // dispatch('logout'); - } - }, - login({ commit }, { token, user }) { - this.$cookies.set('token', token); - commit('token', token); - commit('user', user); - commit('loggedIn', true); - }, - logout({ commit }) { - this.$cookies.remove('token'); - commit('token', null); - commit('user', null); - commit('loggedIn', false); - }, - alert({ commit }, payload) { + commit('auth/setToken', cookies.token); + await dispatch('auth/verify'); + } + /* alert({ commit }, payload) { if (!payload) return commit('alert', null); commit('alert', { text: payload.text, error: payload.error }); - } + } */ }; |