- Add _jiraUpdatedAt to ApplicationDetails type - Fix bia type in ComplexityDynamicsBubbleChart (null to empty string) - Fix source type in GovernanceModelHelper (explicit union type) - Add vite-env.d.ts for import.meta.env types - Add node.d.ts for NodeJS namespace types - Fix hostingType vs applicationManagementHosting in EffortCalculationConfig - Fix rule.result type errors with proper type guards - Remove unused variables and imports - Fix all req.query and req.params type errors
154 lines
3.8 KiB
TypeScript
154 lines
3.8 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
|
|
export interface User {
|
|
accountId: string;
|
|
displayName: string;
|
|
emailAddress?: string;
|
|
avatarUrl?: string;
|
|
}
|
|
|
|
interface AuthConfig {
|
|
// The configured authentication method
|
|
authMethod: 'pat' | 'oauth' | 'none';
|
|
// Legacy fields (for backward compatibility)
|
|
oauthEnabled: boolean;
|
|
serviceAccountEnabled: boolean;
|
|
jiraHost: string;
|
|
}
|
|
|
|
interface AuthState {
|
|
// State
|
|
user: User | null;
|
|
isAuthenticated: boolean;
|
|
authMethod: 'oauth' | 'service-account' | null;
|
|
isLoading: boolean;
|
|
error: string | null;
|
|
config: AuthConfig | null;
|
|
|
|
// Actions
|
|
setUser: (user: User | null, method: 'oauth' | 'service-account' | null) => void;
|
|
setLoading: (loading: boolean) => void;
|
|
setError: (error: string | null) => void;
|
|
setConfig: (config: AuthConfig) => void;
|
|
logout: () => Promise<void>;
|
|
checkAuth: () => Promise<void>;
|
|
fetchConfig: () => Promise<void>;
|
|
}
|
|
|
|
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3001';
|
|
|
|
export const useAuthStore = create<AuthState>()(
|
|
persist(
|
|
(set) => ({
|
|
// Initial state
|
|
user: null,
|
|
isAuthenticated: false,
|
|
authMethod: null,
|
|
isLoading: true,
|
|
error: null,
|
|
config: null,
|
|
|
|
// Actions
|
|
setUser: (user, method) => set({
|
|
user,
|
|
isAuthenticated: !!user,
|
|
authMethod: method,
|
|
error: null,
|
|
}),
|
|
|
|
setLoading: (loading) => set({ isLoading: loading }),
|
|
|
|
setError: (error) => set({ error, isLoading: false }),
|
|
|
|
setConfig: (config) => set({ config }),
|
|
|
|
logout: async () => {
|
|
try {
|
|
await fetch(`${API_BASE}/api/auth/logout`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
});
|
|
} catch (error) {
|
|
console.error('Logout error:', error);
|
|
}
|
|
set({
|
|
user: null,
|
|
isAuthenticated: false,
|
|
authMethod: null,
|
|
error: null,
|
|
});
|
|
},
|
|
|
|
checkAuth: async () => {
|
|
set({ isLoading: true });
|
|
try {
|
|
const response = await fetch(`${API_BASE}/api/auth/me`, {
|
|
credentials: 'include',
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Auth check failed');
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.authenticated) {
|
|
set({
|
|
user: data.user,
|
|
isAuthenticated: true,
|
|
authMethod: data.authMethod,
|
|
isLoading: false,
|
|
error: null,
|
|
});
|
|
} else {
|
|
set({
|
|
user: null,
|
|
isAuthenticated: false,
|
|
authMethod: null,
|
|
isLoading: false,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Auth check error:', error);
|
|
set({
|
|
user: null,
|
|
isAuthenticated: false,
|
|
authMethod: null,
|
|
isLoading: false,
|
|
error: error instanceof Error ? error.message : 'Authentication check failed',
|
|
});
|
|
}
|
|
},
|
|
|
|
fetchConfig: async () => {
|
|
try {
|
|
const response = await fetch(`${API_BASE}/api/auth/config`, {
|
|
credentials: 'include',
|
|
});
|
|
|
|
if (response.ok) {
|
|
const config = await response.json();
|
|
set({ config });
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch auth config:', error);
|
|
}
|
|
},
|
|
}),
|
|
{
|
|
name: 'auth-storage',
|
|
partialize: (state) => ({
|
|
// Only persist non-sensitive data
|
|
authMethod: state.authMethod,
|
|
}),
|
|
}
|
|
)
|
|
);
|
|
|
|
// Helper to get login URL
|
|
export function getLoginUrl(): string {
|
|
return `${API_BASE}/api/auth/login`;
|
|
}
|
|
|