Added systems to handle banned users

This commit is contained in:
2025-12-18 09:17:35 -05:00
parent 3b261bc18e
commit 52e06c8f00
2 changed files with 92 additions and 57 deletions

26
ui/src/pages/Banned.vue Normal file
View File

@@ -0,0 +1,26 @@
<template>
<div class="w-full max-w-2xl flex flex-col gap-8 justify-center mx-auto">
<h1 class="text-3xl sm:text-4xl font-bold text-left">
Access Restricted
</h1>
<div class="space-y-4 text-muted-foreground text-left leading-relaxed">
<p>
Your access to the <strong>17th Ranger Battalion</strong> has been
<strong>revoked</strong> due to a violation of our community standards
or policies.
</p>
<p>
If you believe this action was taken in error or would like to better
understand the reason for this decision, you may
<strong>reach out to our administrative staff on Discord</strong>
to request clarification or discuss a potential appeal.
</p>
<p>
Regards,<br />
<span class="text-foreground font-medium">
The 17th Ranger Battalion Administration Team
</span>
</p>
</div>
</div>
</template>

View File

@@ -2,82 +2,91 @@ import { useUserStore } from '@/stores/user'
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [ routes: [
// PUBLIC // PUBLIC
{ path: '/join', component: () => import('@/pages/Join.vue') }, { path: '/join', component: () => import('@/pages/Join.vue') },
{ path: '/applications', component: () => import('@/pages/MyApplications.vue'), meta: { requiresAuth: true } }, { path: '/applications', component: () => import('@/pages/MyApplications.vue'), meta: { requiresAuth: true } },
{ path: '/applications/:id', component: () => import('@/pages/MyApplications.vue'), meta: { requiresAuth: true } }, { path: '/applications/:id', component: () => import('@/pages/MyApplications.vue'), meta: { requiresAuth: true } },
// AUTH REQUIRED // AUTH REQUIRED
{ path: '/', component: () => import('@/pages/Homepage.vue') }, { path: '/', component: () => import('@/pages/Homepage.vue') },
// MEMBER ROUTES // MEMBER ROUTES
{ path: '/members', component: () => import('@/pages/memberList.vue'), meta: { requiresAuth: true, memberOnly: true } }, { path: '/members', component: () => import('@/pages/memberList.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/loa', component: () => import('@/pages/SubmitLOA.vue'), meta: { requiresAuth: true, memberOnly: true } }, { path: '/loa', component: () => import('@/pages/SubmitLOA.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/transfer', component: () => import('@/pages/Transfer.vue'), meta: { requiresAuth: true, memberOnly: true } }, { path: '/transfer', component: () => import('@/pages/Transfer.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/profile', component: () => import('@/pages/MyProfile.vue'), meta: { requiresAuth: true } }, { path: '/profile', component: () => import('@/pages/MyProfile.vue'), meta: { requiresAuth: true } },
{ path: '/calendar', component: () => import('@/pages/Calendar.vue') },
{ path: '/calendar/event/:id', component: () => import('@/pages/Calendar.vue') },
// disabled in favor of linking
// { path: '/documents', component: () => import('@/pages/Documentation.vue'), meta: { requiresAuth: true, memberOnly: true }, },
{ path: '/trainingReport', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } }, { path: '/calendar', component: () => import('@/pages/Calendar.vue') },
{ path: '/trainingReport/new', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } }, { path: '/calendar/event/:id', component: () => import('@/pages/Calendar.vue') },
{ path: '/trainingReport/:id', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },
// ADMIN / STAFF ROUTES // disabled in favor of linking
{ // { path: '/documents', component: () => import('@/pages/Documentation.vue'), meta: { requiresAuth: true, memberOnly: true }, },
path: '/administration',
meta: { requiresAuth: true, memberOnly: true, roles: ['17th Administrator', '17th HQ', '17th Command'] },
children: [
{ path: 'applications', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'applications/:id', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'rankChange', component: () => import('@/pages/RankChange.vue') },
{ path: 'applications/:id', component: () => import('@/pages/Application.vue') },
{ path: 'transfer', component: () => import('@/pages/ManageTransfers.vue') },
{ path: 'loa', component: () => import('@/pages/ManageLOA.vue') },
{ path: 'roles', component: () => import('@/pages/ManageRoles.vue') }
]
},
// UNAUTHORIZED PAGE { path: '/trainingReport', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },
{ path: '/unauthorized', component: () => import('@/pages/Unauthorized.vue') } { path: '/trainingReport/new', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },
] { path: '/trainingReport/:id', component: () => import('@/pages/TrainingReport.vue'), meta: { requiresAuth: true, memberOnly: true } },
// ADMIN / STAFF ROUTES
{
path: '/administration',
meta: { requiresAuth: true, memberOnly: true, roles: ['17th Administrator', '17th HQ', '17th Command'] },
children: [
{ path: 'applications', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'applications/:id', component: () => import('@/pages/ManageApplications.vue') },
{ path: 'rankChange', component: () => import('@/pages/RankChange.vue') },
{ path: 'applications/:id', component: () => import('@/pages/Application.vue') },
{ path: 'transfer', component: () => import('@/pages/ManageTransfers.vue') },
{ path: 'loa', component: () => import('@/pages/ManageLOA.vue') },
{ path: 'roles', component: () => import('@/pages/ManageRoles.vue') }
]
},
// UNAUTHORIZED PAGE
{ path: '/unauthorized', component: () => import('@/pages/Unauthorized.vue') },
{ path: '/restricted', component: () => import('@/pages/Banned.vue'), meta: { requiresAuth: true } },
]
}) })
const addr = import.meta.env.VITE_APIHOST; const addr = import.meta.env.VITE_APIHOST;
router.beforeEach(async (to) => { router.beforeEach(async (to) => {
const user = useUserStore() const user = useUserStore()
// Make sure user state is loaded before checking // Make sure user state is loaded before checking
if (!user.loaded) { if (!user.loaded) {
await user.loadUser(); await user.loadUser();
} }
// Not logged in // Not logged in
if (to.meta.requiresAuth && !user.isLoggedIn) { if (to.meta.requiresAuth && !user.isLoggedIn) {
// Redirect back to original page after login // Redirect back to original page after login
const redirectUrl = encodeURIComponent(window.location.origin + to.fullPath) const redirectUrl = encodeURIComponent(window.location.origin + to.fullPath)
window.location.href = `${addr}/login?redirect=${redirectUrl}` window.location.href = `${addr}/login?redirect=${redirectUrl}`
return false // Prevent Vue Router from continuing return false // Prevent Vue Router from continuing
} }
// banned state
if (user.state === 'banned' && to.path !== '/restricted') {
return '/restricted';
}
// Must be a member // Must be a member
if (to.meta.memberOnly && user.state !== 'member') { if (to.meta.memberOnly && user.state !== 'member') {
return '/unauthorized' return '/unauthorized'
} }
// Must have specific role // Must have specific role
if (to.meta.roles && !user.hasRole('Dev') && !user.hasAnyRole(to.meta.roles)) { if (to.meta.roles && !user.hasRole('Dev') && !user.hasAnyRole(to.meta.roles)) {
return '/unauthorized' return '/unauthorized'
} }
}) })
export default router; export default router;