94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import { ref, computed, watch } from 'vue'
|
|
import { defineStore } from 'pinia'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { myData } from '@shared/types/member'
|
|
|
|
|
|
const POLL_INTERVAL = 10_000
|
|
|
|
export const useUserStore = defineStore('user', () => {
|
|
const user = ref<myData>(null)
|
|
const roles = computed(() => new Set(user.value?.roles?.map(r => r.name) ?? []));
|
|
const loaded = ref(false);
|
|
const state = computed<string | undefined>(() => user.value?.state || undefined);
|
|
const isLoggedIn = computed(() => user.value !== null)
|
|
const displayName = computed(() => user.value?.member.displayName || user.value?.member.member_name)
|
|
|
|
async function loadUser() {
|
|
//@ts-ignore
|
|
const res = await fetch(`${import.meta.env.VITE_APIHOST}/members/me`, {
|
|
credentials: 'include',
|
|
});
|
|
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
user.value = data;
|
|
}
|
|
|
|
loaded.value = true;
|
|
}
|
|
|
|
|
|
function hasRole(role: string): boolean {
|
|
return roles.value.has(role)
|
|
}
|
|
|
|
|
|
function hasAnyRole(requiredRoles: string[]): boolean {
|
|
return requiredRoles.some(r => roles.value.has(r))
|
|
}
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
watch(user, (newUser) => {
|
|
if (!newUser) return
|
|
|
|
const currentRoute = route.meta
|
|
|
|
// Member-only route
|
|
if (currentRoute.memberOnly && state.value !== 'member') {
|
|
router.replace('/unauthorized')
|
|
return
|
|
}
|
|
|
|
// Role-based route
|
|
if (currentRoute.roles && !hasRole('Dev') && !hasAnyRole(currentRoute.roles as string[])) {
|
|
return '/unauthorized'
|
|
}
|
|
},
|
|
{ deep: true } // deep watch ensures nested changes trigger
|
|
)
|
|
|
|
//polling system
|
|
let pollTimeout: number | null = null
|
|
let polling = false;
|
|
let lastVersion: string | null = null
|
|
|
|
async function poll() {
|
|
// Only poll if tab is visible
|
|
if (document.hidden) {
|
|
polling = false;
|
|
return
|
|
}
|
|
|
|
await loadUser();
|
|
|
|
scheduleNext()
|
|
}
|
|
|
|
function scheduleNext() {
|
|
polling = true;
|
|
pollTimeout = window.setTimeout(poll, POLL_INTERVAL)
|
|
}
|
|
|
|
poll() //start polling
|
|
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (!document.hidden && polling === false) {
|
|
poll()
|
|
}
|
|
})
|
|
|
|
return { user, displayName, isLoggedIn, roles, loadUser, loaded, hasAnyRole, hasRole, state }
|
|
})
|