diff options
| author | Zephyrrus <[email protected]> | 2020-07-09 02:24:40 +0300 |
|---|---|---|
| committer | Zephyrrus <[email protected]> | 2020-07-09 02:24:40 +0300 |
| commit | fd3f6de51a082dcd72c2ef557747e031ef7b9c4a (patch) | |
| tree | 78e655286f6984b171604f3bc15e41eb52b01cef /src | |
| parent | fix: register handler as a plugin (diff) | |
| download | host.fuwn.me-fd3f6de51a082dcd72c2ef557747e031ef7b9c4a.tar.xz host.fuwn.me-fd3f6de51a082dcd72c2ef557747e031ef7b9c4a.zip | |
refactor: refactor most of the admin pages to use the store instead of internal states
Diffstat (limited to 'src')
| -rw-r--r-- | src/site/pages/dashboard/account.vue | 22 | ||||
| -rw-r--r-- | src/site/pages/dashboard/admin/user/_id.vue | 66 | ||||
| -rw-r--r-- | src/site/pages/dashboard/admin/users.vue | 66 | ||||
| -rw-r--r-- | src/site/pages/dashboard/albums/_id.vue | 6 | ||||
| -rw-r--r-- | src/site/pages/dashboard/index.vue | 6 | ||||
| -rw-r--r-- | src/site/pages/dashboard/tags/index.vue | 6 | ||||
| -rw-r--r-- | src/site/pages/login.vue | 47 | ||||
| -rw-r--r-- | src/site/pages/register.vue | 57 | ||||
| -rw-r--r-- | src/site/store/admin.js | 93 | ||||
| -rw-r--r-- | src/site/store/alert.js | 18 | ||||
| -rw-r--r-- | src/site/store/auth.js | 10 | ||||
| -rw-r--r-- | src/site/store/images.js | 2 |
12 files changed, 263 insertions, 136 deletions
diff --git a/src/site/pages/dashboard/account.vue b/src/site/pages/dashboard/account.vue index fb8b273..31ec8af 100644 --- a/src/site/pages/dashboard/account.vue +++ b/src/site/pages/dashboard/account.vue @@ -52,11 +52,11 @@ </b-field> <div class="mb2 mt2 text-center"> - <button - class="button is-primary" + <b-button + type="is-lolisafe" @click="changePassword"> Change password - </button> + </b-button> </div> <b-field @@ -69,19 +69,21 @@ expanded disabled /> <p class="control"> - <button class="button is-primary"> + <b-button + type="is-lolisafe" + @click="copyKey"> Copy - </button> + </b-button> </p> </b-field> </b-field> <div class="mb2 mt2 text-center"> - <button - class="button is-primary" + <b-button + type="is-lolisafe" @click="promptNewAPIKey"> Request new API key - </button> + </b-button> </div> </div> </div> @@ -154,6 +156,10 @@ export default { onConfirm: () => this.requestNewAPIKey(), }); }, + copyKey() { + this.$clipboard(this.apiKey); + this.$notifier.success('API key copied to clipboard'); + }, async requestNewAPIKey() { const response = await this.$store.dispatch('auth/requestAPIKey'); this.$buefy.toast.open(response.message); diff --git a/src/site/pages/dashboard/admin/user/_id.vue b/src/site/pages/dashboard/admin/user/_id.vue index 1755b89..7814468 100644 --- a/src/site/pages/dashboard/admin/user/_id.vue +++ b/src/site/pages/dashboard/admin/user/_id.vue @@ -41,20 +41,27 @@ <b-field label="Files" horizontal> - <span>{{ files.length }}</span> + <span>{{ user.files.length }}</span> </b-field> <div class="mb2 mt2 text-center"> - <button - class="button is-danger" + <b-button + v-if="user.enabled" + type="is-danger" @click="promptDisableUser"> Disable user - </button> + </b-button> + <b-button + v-if="!user.enabled" + type="is-success" + @click="promptEnableUser"> + Enable user + </b-button> </div> <Grid - v-if="files.length" - :files="files" /> + v-if="user.files.length" + :files="user.files" /> </div> </div> </div> @@ -62,6 +69,7 @@ </template> <script> +import { mapState } from 'vuex'; import Sidebar from '~/components/sidebar/Sidebar.vue'; import Grid from '~/components/grid/Grid.vue'; @@ -70,42 +78,42 @@ export default { Sidebar, Grid, }, - middleware: ['auth', 'admin'], + middleware: ['auth', 'admin', ({ route, store }) => { + try { + store.dispatch('admin/fetchUser', route.params.id); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + }], data() { return { options: {}, - files: null, - user: null, }; }, - async asyncData({ $axios, route }) { - try { - const response = await $axios.$get(`/admin/users/${route.params.id}`); - return { - files: response.files ? response.files : null, - user: response.user ? response.user : null, - }; - } catch (error) { - console.error(error); - return { - files: null, - user: null, - }; - } - }, + computed: mapState({ + user: (state) => state.admin.user, + }), methods: { promptDisableUser() { this.$buefy.dialog.confirm({ type: 'is-danger', - message: 'Are you sure you want to disable the account of the user that uploaded this file?', + message: 'Are you sure you want to disable the account of this user?', onConfirm: () => this.disableUser(), }); }, - async disableUser() { - const response = await this.$axios.$post('admin/users/disable', { - id: this.user.id, + promptEnableUser() { + this.$buefy.dialog.confirm({ + type: 'is-danger', + message: 'Are you sure you want to enable the account of this user?', + onConfirm: () => this.enableUser(), }); - this.$buefy.toast.open(response.message); + }, + disableUser() { + this.$handler.executeAction('admin/disableUser', this.user.id); + }, + enableUser() { + this.$handler.executeAction('admin/enableUser', this.user.id); }, }, }; diff --git a/src/site/pages/dashboard/admin/users.vue b/src/site/pages/dashboard/admin/users.vue index 269946c..bed4c2b 100644 --- a/src/site/pages/dashboard/admin/users.vue +++ b/src/site/pages/dashboard/admin/users.vue @@ -13,7 +13,7 @@ <div class="view-container"> <b-table - :data="users || []" + :data="users" :mobile-cards="true"> <template slot-scope="props"> <b-table-column @@ -37,7 +37,7 @@ label="Enabled" centered> <b-switch - v-model="props.row.enabled" + :value="props.row.enabled" @input="changeEnabledStatus(props.row)" /> </b-table-column> @@ -46,18 +46,18 @@ label="Admin" centered> <b-switch - v-model="props.row.isAdmin" + :value="props.row.isAdmin" @input="changeIsAdmin(props.row)" /> </b-table-column> <b-table-column field="purge" centered> - <button - class="button is-primary" + <b-button + type="is-danger" @click="promptPurgeFiles(props.row)"> Purge files - </button> + </b-button> </b-table-column> </template> <template slot="empty"> @@ -82,45 +82,42 @@ </template> <script> +import { mapState } from 'vuex'; import Sidebar from '~/components/sidebar/Sidebar.vue'; export default { components: { Sidebar, }, - middleware: ['auth', 'admin'], - data() { - return { - users: [], - }; - }, - computed: { - config() { - return this.$store.state.config; - }, - }, + middleware: ['auth', 'admin', ({ route, store }) => { + try { + store.dispatch('admin/fetchUsers', route.params.id); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + }], + computed: mapState({ + users: (state) => state.admin.users, + config: (state) => state.config, + }), metaInfo() { return { title: 'Uploads' }; }, - mounted() { - this.getUsers(); - }, methods: { - async getUsers() { - const response = await this.$axios.$get('admin/users'); - this.users = response.users; - }, async changeEnabledStatus(row) { - const response = await this.$axios.$post(`admin/users/${row.enabled ? 'enable' : 'disable'}`, { - id: row.id, - }); - this.$buefy.toast.open(response.message); + if (row.enabled) { + this.$handler.executeAction('admin/disableUser', row.id); + } else { + this.$handler.executeAction('admin/enableUser', row.id); + } }, async changeIsAdmin(row) { - const response = await this.$axios.$post(`admin/users/${row.isAdmin ? 'promote' : 'demote'}`, { - id: row.id, - }); - this.$buefy.toast.open(response.message); + if (row.isAdmin) { + this.$handler.executeAction('admin/demoteUser', row.id); + } else { + this.$handler.executeAction('admin/promoteUser', row.id); + } }, promptPurgeFiles(row) { this.$buefy.dialog.confirm({ @@ -129,10 +126,7 @@ export default { }); }, async purgeFiles(row) { - const response = await this.$axios.$post('admin/users/purge', { - id: row.id, - }); - this.$buefy.toast.open(response.message); + this.$handler.executeAction('admin/purgeUserFiles', row.id); }, }, }; diff --git a/src/site/pages/dashboard/albums/_id.vue b/src/site/pages/dashboard/albums/_id.vue index b25762e..a909e75 100644 --- a/src/site/pages/dashboard/albums/_id.vue +++ b/src/site/pages/dashboard/albums/_id.vue @@ -30,11 +30,9 @@ placeholder="Search" type="search" /> <p class="control"> - <button - outlined - class="button is-primary"> + <b-button type="is-lolisafe"> Search - </button> + </b-button> </p> </b-field> </div> diff --git a/src/site/pages/dashboard/index.vue b/src/site/pages/dashboard/index.vue index 2f06c7e..08c5166 100644 --- a/src/site/pages/dashboard/index.vue +++ b/src/site/pages/dashboard/index.vue @@ -21,11 +21,9 @@ placeholder="Search" type="search" /> <p class="control"> - <button - outlined - class="button is-primary"> + <b-button type="is-lolisafe"> Search - </button> + </b-button> </p> </b-field> </div> diff --git a/src/site/pages/dashboard/tags/index.vue b/src/site/pages/dashboard/tags/index.vue index 10193a8..a9c5756 100644 --- a/src/site/pages/dashboard/tags/index.vue +++ b/src/site/pages/dashboard/tags/index.vue @@ -144,11 +144,11 @@ type="text" @keyup.enter.native="createTag" /> <p class="control"> - <button - class="button is-primary" + <b-button + type="is-lolisafe" @click="createTag"> Create tags - </button> + </b-button> </p> </b-field> </div> diff --git a/src/site/pages/login.vue b/src/site/pages/login.vue index 9e5658d..569e9d9 100644 --- a/src/site/pages/login.vue +++ b/src/site/pages/login.vue @@ -25,20 +25,32 @@ @keyup.enter.native="login" /> </b-field> - <p class="control has-addons is-pulled-right"> - <router-link - v-if="config.userAccounts" - to="/register" - class="is-text"> - Don't have an account? - </router-link> - <span v-else>Registration is closed at the moment</span> - <button - class="button is-primary big ml1" - @click="login"> - login - </button> - </p> + <p class="control has-addons is-pulled-right" /> + + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <router-link + v-if="config.userAccounts" + to="/register" + class="is-text"> + Don't have an account? + </router-link> + <span v-else>Registration is closed at the moment</span> + </div> + </div> + + <div class="level-right"> + <p class="level-item"> + <b-button + size="is-medium" + type="is-lolisafe" + @click="login"> + Login + </b-button> + </p> + </div> + </div> </div> </div> </div> @@ -99,10 +111,7 @@ export default { const { username, password } = this; if (!username || !password) { - this.$store.dispatch('alert/set', { - text: 'Please fill both fields before attempting to log in.', - error: true, - }); + this.$notifier.error('Please fill both fields before attempting to log in.'); return; } @@ -113,7 +122,7 @@ export default { this.redirect(); } } catch (e) { - this.$store.dispatch('alert/set', { text: e.message, error: true }, { root: true }); + this.$notifier.error(e.message); } finally { this.isLoading = false; } diff --git a/src/site/pages/register.vue b/src/site/pages/register.vue index 1216dc1..7cf4573 100644 --- a/src/site/pages/register.vue +++ b/src/site/pages/register.vue @@ -31,19 +31,30 @@ @keyup.enter.native="register" /> </b-field> - <p class="control has-addons is-pulled-right"> - <router-link - to="/login" - class="is-text"> - Already have an account? - </router-link> - <button - class="button is-primary big ml1" - :disabled="isLoading" - @click="register"> - Register - </button> - </p> + <div class="level"> + <!-- Left side --> + <div class="level-left"> + <div class="level-item"> + <router-link + to="/login" + class="is-text"> + Already have an account? + </router-link> + </div> + </div> + <!-- Right side --> + <div class="level-right"> + <p class="level-item"> + <b-button + size="is-medium" + type="is-lolisafe" + :disabled="isLoading" + @click="register"> + Register + </b-button> + </p> + </div> + </div> </div> </div> </div> @@ -70,32 +81,28 @@ export default { methods: { async register() { if (this.isLoading) return; + if (!this.username || !this.password || !this.rePassword) { - this.$store.dispatch('alert', { - text: 'Please fill all fields before attempting to register.', - error: true, - }); + this.$notifier.error('Please fill all fields before attempting to register.'); return; } if (this.password !== this.rePassword) { - this.$store.dispatch('alert', { - text: "Passwords don't match", - error: true, - }); + this.$notifier.error('Passwords don\'t match'); return; } this.isLoading = true; try { - const response = await this.$axios.$post('auth/register', { + const response = await this.$store.dispatch('auth/register', { username: this.username, password: this.password, }); - this.$store.dispatch('alert', { text: response.message }); - return this.$router.push('/login'); + this.$notifier.success(response.message); + this.$router.push('/login'); + return; } catch (error) { - // + this.$notifier.error(error.message); } finally { this.isLoading = false; } diff --git a/src/site/store/admin.js b/src/site/store/admin.js index e69de29..2586a18 100644 --- a/src/site/store/admin.js +++ b/src/site/store/admin.js @@ -0,0 +1,93 @@ +export const state = () => ({ + users: [], + user: { + id: null, + username: null, + enabled: false, + createdAt: null, + editedAt: null, + apiKeyEditedAt: null, + isAdmin: null, + files: [], + }, + file: {}, + settings: {}, +}); + +export const actions = { + async fetchUsers({ commit }) { + const response = await this.$axios.$get('admin/users'); + commit('setUsers', response); + + return response; + }, + async fetchUser({ commit }, id) { + const response = await this.$axios.$get(`/admin/users/${id}`); + commit('setUserInfo', response); + + return response; + }, + async enableUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/enable', { id }); + + commit('changeUserState', { userId: id, enabled: true }); + + return response; + }, + async disableUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/disable', { id }); + + commit('changeUserState', { userId: id, enabled: false }); + + return response; + }, + async promoteUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/promote', { id }); + + commit('changeUserState', { userId: id, isAdmin: true }); + + return response; + }, + async demoteUser({ commit }, id) { + const response = await this.$axios.$post('admin/users/demote', { id }); + + commit('changeUserState', { userId: id, isAdmin: false }); + + return response; + }, + async purgeUserFiles(_, id) { + const response = await this.$axios.$post('admin/users/purge', { id }); + + return response; + }, +}; + +export const mutations = { + setUsers(state, { users }) { + state.users = users; + }, + setUserInfo(state, { user, files }) { + state.user = { ...state.user, ...user }; + state.user.files = files || []; + }, + changeUserState(state, { userId, enabled, isAdmin }) { + const foundIndex = state.users.findIndex(({ id }) => id === userId); + if (foundIndex > -1) { + if (enabled !== undefined) { + state.users[foundIndex].enabled = enabled; + } + if (isAdmin !== undefined) { + state.users[foundIndex].isAdmin = isAdmin; + } + } + + if (state.user.id === userId) { + if (enabled !== undefined) { + state.user.enabled = enabled; + } + if (isAdmin !== undefined) { + state.user.isAdmin = isAdmin; + } + } + }, +}; diff --git a/src/site/store/alert.js b/src/site/store/alert.js index ff38e09..580dcc8 100644 --- a/src/site/store/alert.js +++ b/src/site/store/alert.js @@ -1,12 +1,19 @@ +import AlertTypes from '~/constants/alertTypes'; + const getDefaultState = () => ({ - text: null, - error: false, + message: null, + type: null, + snackbar: false, }); export const state = getDefaultState; export const actions = { set({ commit }, data) { + // Only exists for backwards compatibility, remove one day + if (data.error === true) data.type = AlertTypes.ERROR; + if (data.text !== undefined) data.message = data.text; + commit('set', data); }, clear({ commit }) { @@ -15,9 +22,10 @@ export const actions = { }; export const mutations = { - set(state, { text, error }) { - state.text = text; - state.error = error; + set(state, { message, type, snackbar }) { + state.message = message; + state.type = type; + state.snackbar = snackbar || false; }, clear(state) { Object.assign(state, getDefaultState()); diff --git a/src/site/store/auth.js b/src/site/store/auth.js index fcc051b..465de7d 100644 --- a/src/site/store/auth.js +++ b/src/site/store/auth.js @@ -30,6 +30,12 @@ export const actions = { commit('setToken', data.token); commit('loginSuccess', { token: data.token, user: data.user }); }, + async register(_, { username, password }) { + return this.$axios.$post('auth/register', { + username, + password, + }); + }, async fetchCurrentUser({ commit, dispatch }) { try { const data = await this.$axios.$get('users/me'); @@ -83,13 +89,13 @@ export const mutations = { state.isLoading = true; }, loginSuccess(state, { user }) { - this.$cookies.set('token', state.token); + this.$cookies.set('token', state.token, { path: '/' }); state.user = user; state.loggedIn = true; state.isLoading = false; }, logout(state) { - this.$cookies.remove('token'); + this.$cookies.remove('token', { path: '/' }); // reset state to default Object.assign(state, getDefaultState()); }, diff --git a/src/site/store/images.js b/src/site/store/images.js index 3019d85..a7581e0 100644 --- a/src/site/store/images.js +++ b/src/site/store/images.js @@ -10,7 +10,7 @@ export const getDefaultState = () => ({ }, name: null, downloadEnabled: false, - filesAlbums: {}, + filesAlbums: {}, // map of file ids with a list of album objects the file is in }); export const state = getDefaultState; |