Team based role auth for routes
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
Functions,
|
||||
ID,
|
||||
AppwriteException,
|
||||
Teams,
|
||||
} from 'appwrite';
|
||||
import { useAuthStore } from 'src/stores/auth';
|
||||
import { Dialog, Notify } from 'quasar';
|
||||
@@ -67,6 +68,7 @@ const AppwriteIds = process.env.DEV
|
||||
const account = new Account(client);
|
||||
const databases = new Databases(client);
|
||||
const functions = new Functions(client);
|
||||
const teams = new Teams(client);
|
||||
|
||||
let appRouter: Router;
|
||||
|
||||
@@ -136,6 +138,7 @@ async function login(email: string, password: string) {
|
||||
export {
|
||||
client,
|
||||
account,
|
||||
teams,
|
||||
databases,
|
||||
functions,
|
||||
ID,
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
<q-card>
|
||||
<q-toolbar>
|
||||
<q-toolbar-title>Select a Boat and Time</q-toolbar-title>
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
<q-btn
|
||||
icon="close"
|
||||
flat
|
||||
round
|
||||
dense
|
||||
v-close-popup />
|
||||
</q-toolbar>
|
||||
<q-separator />
|
||||
<CalendarHeaderComponent v-model="selectedDate" />
|
||||
@@ -21,8 +26,7 @@
|
||||
:short-interval-label="true"
|
||||
v-model="selectedDate"
|
||||
:column-count="boats.length"
|
||||
v-touch-swipe.left.right="handleSwipe"
|
||||
>
|
||||
v-touch-swipe.left.right="handleSwipe">
|
||||
<template #head-day="{ scope }">
|
||||
<div style="text-align: center; font-weight: 800">
|
||||
{{ getBoatDisplayName(scope) }}
|
||||
@@ -30,7 +34,9 @@
|
||||
</template>
|
||||
|
||||
<template #day-body="{ scope }">
|
||||
<div v-for="block in getBoatBlocks(scope)" :key="block.$id">
|
||||
<div
|
||||
v-for="block in getBoatBlocks(scope)"
|
||||
:key="block.$id">
|
||||
<div
|
||||
class="timeblock"
|
||||
:class="selectedBlock?.$id === block.$id ? 'selected' : ''"
|
||||
@@ -43,9 +49,9 @@
|
||||
"
|
||||
:id="block.id"
|
||||
@click="selectBlock($event, scope, block)"
|
||||
v-close-popup
|
||||
>
|
||||
{{ boats[scope.columnIndex].name }}<br />
|
||||
v-close-popup>
|
||||
{{ boats[scope.columnIndex].name }}
|
||||
<br />
|
||||
{{
|
||||
selectedBlock?.$id === block.$id ? 'Selected' : 'Available'
|
||||
}}
|
||||
@@ -53,26 +59,25 @@
|
||||
</div>
|
||||
<div
|
||||
v-for="reservation in getBoatReservations(scope)"
|
||||
:key="reservation.$id"
|
||||
>
|
||||
:key="reservation.$id">
|
||||
<div
|
||||
class="reservation"
|
||||
class="reservation column"
|
||||
:style="
|
||||
reservationStyles(
|
||||
reservation,
|
||||
scope.timeStartPos,
|
||||
scope.timeDurationHeight
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ getUserName(reservation.user) || 'loading...' }}<br />
|
||||
">
|
||||
{{ getUserName(reservation.user) || 'loading...' }}
|
||||
<br />
|
||||
<q-chip icon="key">{{ reservation.reason }}</q-chip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</QCalendarDay>
|
||||
</div></q-card
|
||||
>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -166,7 +166,6 @@ const onReset = () => {
|
||||
bookingForm.value = { ...newForm };
|
||||
};
|
||||
const onSubmit = () => {
|
||||
console.log('SUBMIT!');
|
||||
const booking = bookingForm.value;
|
||||
if (
|
||||
!(booking.boat && booking.startDate && booking.endDate && auth.currentUser)
|
||||
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
import routes from './routes';
|
||||
import { useAuthStore } from 'src/stores/auth';
|
||||
|
||||
const publicRoutes = routes
|
||||
.filter((route) => route.meta?.publicRoute)
|
||||
.map((r) => r.path);
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
@@ -35,15 +39,33 @@ export default route(function (/* { store, ssrContext } */) {
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE),
|
||||
});
|
||||
|
||||
Router.beforeEach((to) => {
|
||||
const publicPages = routes
|
||||
.filter((route) => route.meta?.publicRoute)
|
||||
.map((r) => r.path);
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
Router.beforeEach(async (to, from, next) => {
|
||||
const authStore = useAuthStore();
|
||||
const currentUser = authStore.currentUser;
|
||||
const authRequired = !publicRoutes.includes(to.path);
|
||||
const requiredRoles = to.meta?.requiredRoles as string[];
|
||||
|
||||
if (authRequired && !useAuthStore().currentUser) {
|
||||
return '/login';
|
||||
if (authRequired && !currentUser) {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
if (requiredRoles) {
|
||||
if (!currentUser) {
|
||||
return next('/login');
|
||||
}
|
||||
|
||||
try {
|
||||
const hasRole = await authStore.hasRequiredRole(requiredRoles);
|
||||
if (!hasRole) {
|
||||
return next(from);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user teams:', error);
|
||||
return next('/error'); // Redirect to an error page or handle it as needed
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
return Router;
|
||||
|
||||
@@ -44,7 +44,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: 'manage',
|
||||
component: () => import('src/pages/schedule/ManageCalendar.vue'),
|
||||
name: 'manage-schedule',
|
||||
meta: { requiresScheduleAdmin: true },
|
||||
meta: { requiredRoles: ['Schedule Admins'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -102,7 +102,7 @@ const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/admin',
|
||||
component: () => import('layouts/AdminLayout.vue'),
|
||||
meta: { requiresAdmin: true },
|
||||
meta: { requiredRoles: ['admin'] },
|
||||
children: [
|
||||
{
|
||||
path: '/user',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ID, account, functions } from 'boot/appwrite';
|
||||
import { ID, account, functions, teams } from 'boot/appwrite';
|
||||
import { ExecutionMethod, OAuthProvider, type Models } from 'appwrite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
@@ -15,6 +15,13 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const hasRequiredRole = async (requiredRoles: string[]): Promise<boolean> => {
|
||||
const userTeams = await teams.list();
|
||||
const userTeamNames = userTeams.teams.map((team) => team.name);
|
||||
console.log(requiredRoles.some((role) => userTeamNames.includes(role)));
|
||||
return requiredRoles.some((role) => userTeamNames.includes(role));
|
||||
};
|
||||
|
||||
async function register(email: string, password: string) {
|
||||
await account.create(ID.unique(), email, password);
|
||||
return await login(email, password);
|
||||
@@ -65,6 +72,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
return {
|
||||
currentUser,
|
||||
getUserNameById,
|
||||
hasRequiredRole,
|
||||
register,
|
||||
login,
|
||||
googleLogin,
|
||||
|
||||
Reference in New Issue
Block a user