refactor: refactor most of the admin pages to use the store instead of internal states
This commit is contained in:
parent
da703de1d0
commit
fd3f6de51a
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue