Added admin page.
This commit is contained in:
parent
bf6066e949
commit
75680d1041
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewlalis.gymboard_api.domains.auth.controller;
|
||||
|
||||
import nl.andrewlalis.gymboard_api.domains.auth.dto.*;
|
||||
import nl.andrewlalis.gymboard_api.domains.auth.model.Role;
|
||||
import nl.andrewlalis.gymboard_api.domains.auth.model.User;
|
||||
import nl.andrewlalis.gymboard_api.domains.auth.model.UserPreferences;
|
||||
import nl.andrewlalis.gymboard_api.domains.auth.service.UserAccessService;
|
||||
|
@ -11,6 +12,9 @@ import org.springframework.http.ResponseEntity;
|
|||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class UserController {
|
||||
private final UserService userService;
|
||||
|
@ -156,4 +160,9 @@ public class UserController {
|
|||
userService.reportUser(userId, payload);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@GetMapping(path = "/auth/me/roles")
|
||||
public List<String> getMyRoles(@AuthenticationPrincipal User myUser) {
|
||||
return myUser.getRoles().stream().map(Role::getShortName).toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { api } from 'src/api/main/index';
|
||||
import { AuthStoreType } from 'stores/auth-store';
|
||||
import {AuthStoreType, useAuthStore} from 'stores/auth-store';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
import { WeightUnit } from 'src/api/main/submission';
|
||||
import {Page, PaginationOptions, toQueryParams} from "src/api/main/models";
|
||||
|
@ -81,8 +81,14 @@ class AuthModule {
|
|||
authStore.token = await this.getNewToken(credentials);
|
||||
authStore.user = await this.getMyUser(authStore);
|
||||
// Load the user's attached data right away too.
|
||||
authStore.user.personalDetails = await this.getMyPersonalDetails(authStore);
|
||||
authStore.user.preferences = await this.getMyPreferences(authStore);
|
||||
const [personalDetails, preferences, roles] = await Promise.all([
|
||||
this.getMyPersonalDetails(authStore),
|
||||
this.getMyPreferences(authStore),
|
||||
this.getMyRoles(authStore)
|
||||
]);
|
||||
authStore.user.personalDetails = personalDetails;
|
||||
authStore.user.preferences = preferences;
|
||||
authStore.roles = roles;
|
||||
|
||||
clearTimeout(this.tokenRefreshTimer);
|
||||
this.tokenRefreshTimer = setTimeout(
|
||||
|
@ -288,6 +294,11 @@ class AuthModule {
|
|||
authStore.axiosConfig
|
||||
);
|
||||
}
|
||||
|
||||
public async getMyRoles(authStore: AuthStoreType): Promise<string[]> {
|
||||
const response = await api.get('/auth/me/roles', authStore.axiosConfig);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthModule;
|
||||
|
|
|
@ -37,6 +37,11 @@
|
|||
<q-item-label>Users</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable to="/admin" v-if="authStore.isAdmin">
|
||||
<q-item-section>
|
||||
<q-item-label>Admin Panel</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
|
||||
|
@ -49,7 +54,9 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import AccountMenuItem from 'components/AccountMenuItem.vue';
|
||||
import {useAuthStore} from 'stores/auth-store';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const leftDrawerOpen = ref(false);
|
||||
|
||||
function toggleLeftDrawer() {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
The main page for the admin panel that's only accessible to users with the
|
||||
'admin' role.
|
||||
-->
|
||||
<template>
|
||||
<q-page>
|
||||
<StandardCenteredPage>
|
||||
<h3 class="q-my-md text-center">Admin Panel</h3>
|
||||
<PageMenu
|
||||
base-route="/admin"
|
||||
:items="[
|
||||
{label: 'Dashboard', to: ''}
|
||||
]"
|
||||
/>
|
||||
<router-view/>
|
||||
</StandardCenteredPage>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||
import PageMenu from 'components/PageMenu.vue';
|
||||
import {onMounted} from 'vue';
|
||||
|
||||
onMounted(async () => {
|
||||
console.log('admin');
|
||||
});
|
||||
|
||||
</script>
|
|
@ -16,15 +16,14 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, Ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import {onMounted, ref, Ref} from 'vue';
|
||||
import {useRouter} from 'vue-router';
|
||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||
import { getGymFromRoute, getGymRoute } from 'src/router/gym-routing';
|
||||
import { Gym } from 'src/api/main/gyms';
|
||||
import {getGymFromRoute, getGymRoute} from 'src/router/gym-routing';
|
||||
import {Gym} from 'src/api/main/gyms';
|
||||
import PageMenu from 'components/PageMenu.vue';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const t = useI18n().t;
|
||||
|
||||
|
|
|
@ -82,9 +82,11 @@ const isOwnUser = ref(false);
|
|||
// will end up on the same route component, which means the router won't
|
||||
// re-render.
|
||||
watch(route, async (updatedRoute) => {
|
||||
const userId = updatedRoute.params.userId[0];
|
||||
if (!profile.value || (profile.value.id !== userId)) {
|
||||
await loadUser(userId);
|
||||
if (updatedRoute.params.userId && updatedRoute.params.userId.length > 0) {
|
||||
const userId = updatedRoute.params.userId[0];
|
||||
if (!profile.value || (profile.value.id !== userId)) {
|
||||
await loadUser(userId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ import SubmissionPage from 'pages/SubmissionPage.vue';
|
|||
import UserPage from 'pages/user/UserPage.vue';
|
||||
import UserSettingsPage from 'pages/auth/UserSettingsPage.vue';
|
||||
import UserSearchPage from 'pages/UserSearchPage.vue';
|
||||
import AdminPage from "pages/admin/AdminPage.vue";
|
||||
import {useAuthStore} from "stores/auth-store";
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
// Auth-related pages, which live outside the main layout.
|
||||
|
@ -43,6 +45,12 @@ const routes: RouteRecordRaw[] = [
|
|||
],
|
||||
},
|
||||
{ path: 'submissions/:submissionId', component: SubmissionPage },
|
||||
{ // The admin page, and all child pages within it, are only accessible for users with the 'admin' role.
|
||||
path: 'admin', component: AdminPage, beforeEnter: () => {
|
||||
const s = useAuthStore();
|
||||
if (!s.isAdmin) return '/'; // Redirect non-admins to the main page.
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -7,16 +7,28 @@
|
|||
|
||||
import { defineStore } from 'pinia';
|
||||
import { User } from 'src/api/main/auth';
|
||||
import {AxiosRequestConfig} from "axios";
|
||||
import {AxiosRequestConfig} from 'axios';
|
||||
|
||||
interface AuthState {
|
||||
/**
|
||||
* The currently authenticated user.
|
||||
*/
|
||||
user: User | null;
|
||||
|
||||
/**
|
||||
* The token that was used to authenticate the current user.
|
||||
*/
|
||||
token: string | null;
|
||||
|
||||
/**
|
||||
* The list of roles that the currently authenticated user has.
|
||||
*/
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore('authStore', {
|
||||
state: (): AuthState => {
|
||||
return { user: null, token: null };
|
||||
return { user: null, token: null, roles: [] };
|
||||
},
|
||||
getters: {
|
||||
loggedIn: (state) => state.user !== null && state.token !== null,
|
||||
|
@ -29,6 +41,7 @@ export const useAuthStore = defineStore('authStore', {
|
|||
return {};
|
||||
}
|
||||
},
|
||||
isAdmin: state => state.roles.indexOf('admin') !== -1,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue