import { defineStore } from 'pinia'; import { ref, computed } from 'vue'; import { router } from '@KTXC/router'; import { authenticationService } from '@KTXC/services/authenticationService'; import { userService } from '@KTXC/services/user/userService'; import type { AuthenticatedUser } from '@KTXC/types/authenticationTypes'; import type { UserProfileInterface } from '@KTXC/types/user/userProfileTypes'; import type { UserSettingsInterface } from '@KTXC/types/user/userSettingsTypes'; import { UserProfile } from '@KTXC/models/userProfile'; import { UserSettings } from '@KTXC/models/userSettings'; const STORAGE_KEY = 'userStore.auth'; // Flush pending updates before page unload if (typeof window !== 'undefined') { window.addEventListener('beforeunload', () => { userService.flushAll(); }); } export const useUserStore = defineStore('userStore', () => { // ========================================================================= // State // ========================================================================= const auth = ref( localStorage.getItem(STORAGE_KEY) ? (JSON.parse(localStorage.getItem(STORAGE_KEY)!) as AuthenticatedUser) : null ); const profile = ref(new UserProfile()); const settings = ref(new UserSettings()); const returnUrl = ref(null); // ========================================================================= // Authentication Getters // ========================================================================= const isAuthenticated = computed(() => auth.value !== null); const identifier = computed(() => auth.value?.identifier ?? null); const identity = computed(() => auth.value?.identity ?? null); const label = computed(() => auth.value?.label ?? null); const roles = computed(() => auth.value?.roles ?? []); const permissions = computed(() => auth.value?.permissions ?? []); // ========================================================================= // Profile Getters // ========================================================================= const profileFields = computed(() => profile.value.fields); const editableProfileFields = computed(() => profile.value.editableFields); const managedProfileFields = computed(() => profile.value.managedFields); // ========================================================================= // Authentication Actions // ========================================================================= function setAuth(authUser: AuthenticatedUser): void { auth.value = authUser; localStorage.setItem(STORAGE_KEY, JSON.stringify(authUser)); } function clearAuth(): void { auth.value = null; localStorage.removeItem(STORAGE_KEY); } async function logout(): Promise { try { // Flush any pending profile/settings updates before logout await userService.flushAll(); await authenticationService.logout(); } catch (error) { console.warn('Logout request failed, clearing local state:', error); } finally { clearAuth(); clearProfile(); clearSettings(); router.push('/login'); } } async function refreshToken(): Promise { try { await authenticationService.refresh(); return true; } catch (error) { await logout(); return false; } } // ========================================================================= // Profile Actions // ========================================================================= function initProfile(profileData: UserProfileInterface): void { profile.value = new UserProfile(profileData); } function clearProfile(): void { profile.value = new UserProfile(); } function getProfileField(key: string): any { return profile.value.get(key); } function setProfileField(key: string, value: any): boolean { const success = profile.value.set(key, value); if (success) { // Debounced update to backend userService.updateProfile({ [key]: value }).catch(error => { console.error('Failed to update profile:', error); }); } return success; } function isProfileFieldEditable(key: string): boolean { return profile.value.isEditable(key); } // ========================================================================= // Settings Actions // ========================================================================= function initSettings(settingsData: UserSettingsInterface): void { settings.value = new UserSettings(settingsData); } function clearSettings(): void { settings.value = new UserSettings(); } function getSetting(key: string): any { return settings.value.get(key); } function setSetting(key: string, value: any): void { settings.value.set(key, value); // Debounced update to backend userService.updateSettings({ [key]: value }).catch(error => { console.error('Failed to update setting:', error); }); } // ========================================================================= // Permission Checking // ========================================================================= /** * Check if user has a specific permission * Supports wildcards: user_manager.users.* matches all user actions */ function hasPermission(permission: string): boolean { const userPermissions = permissions.value; // Exact match if (userPermissions.includes(permission)) { return true; } // Wildcard match for (const userPerm of userPermissions) { if (userPerm.endsWith('.*')) { const prefix = userPerm.slice(0, -2); if (permission.startsWith(prefix + '.')) { return true; } } } // Full wildcard if (userPermissions.includes('*')) { return true; } return false; } /** * Check if user has ANY of the permissions (OR logic) */ function hasAnyPermission(perms: string[]): boolean { return perms.some(p => hasPermission(p)); } /** * Check if user has ALL permissions (AND logic) */ function hasAllPermissions(perms: string[]): boolean { return perms.every(p => hasPermission(p)); } /** * Check if user has a specific role */ function hasRole(role: string): boolean { return roles.value.includes(role); } // ========================================================================= // Initialize from /init endpoint // ========================================================================= function init(userData: { auth?: AuthenticatedUser; profile?: UserProfileInterface; settings?: UserSettingsInterface }): void { if (userData.auth) { setAuth(userData.auth); } if (userData.profile) { initProfile(userData.profile); } if (userData.settings) { initSettings(userData.settings); } } return { // State auth, profile, settings, returnUrl, // Auth getters isAuthenticated, identifier, identity, label, roles, permissions, // Profile getters profileFields, editableProfileFields, managedProfileFields, // Auth actions setAuth, clearAuth, logout, refreshToken, // Profile actions initProfile, clearProfile, getProfileField, setProfileField, isProfileFieldEditable, // Settings actions initSettings, clearSettings, getSetting, setSetting, // Permission actions hasPermission, hasAnyPermission, hasAllPermissions, hasRole, // Init init, }; });