habits.andr33v.ru/app/composables/useAuth.ts

101 lines
2.4 KiB
TypeScript

// /composables/useAuth.ts
// Define User interface
interface User {
id: string;
email: string;
nickname: string;
}
/**
* Composable for authentication management.
* All state is managed by Nuxt's useState to ensure it's shared and SSR-safe.
*/
export function useAuth() {
// --- State ---
// All state is defined here, inside the composable function.
// Nuxt's useState ensures this state is a singleton across the app.
const user = useState<User | null>('user', () => null);
const loading = useState('auth_loading', () => false);
const initialized = useState('auth_initialized', () => false);
// --- Composables ---
const api = useApi();
// --- Computed Properties ---
const isAuthenticated = computed(() => !!user.value);
// --- Methods ---
/**
* Fetches the current user from the backend.
* Runs only once, guarded by the 'initialized' state.
*/
const fetchMe = async () => {
if (initialized.value) {
return;
}
loading.value = true;
initialized.value = true;
try {
const fetchedUser = await api<User>('/auth/me', { method: 'GET' });
user.value = fetchedUser;
} catch (error) {
user.value = null; // Silently handle 401s or other errors
} finally {
loading.value = false;
}
};
/**
* Logs the user in and fetches their data on success.
*/
const login = async (email, password) => {
loading.value = true;
try {
await api('/auth/login', {
method: 'POST',
body: { email, password },
});
// We must re-fetch the user after logging in.
// Resetting 'initialized' allows fetchMe to run again.
initialized.value = false;
await fetchMe();
await navigateTo('/');
} catch (error) {
console.error('Login failed:', error);
throw error; // Re-throw to allow the UI to handle it
} finally {
loading.value = false;
}
};
/**
* Logs the user out.
*/
const logout = async () => {
loading.value = true;
try {
await api('/auth/logout', { method: 'POST' });
} catch (error) {
console.error('Logout API call failed, proceeding with client-side logout:', error);
} finally {
user.value = null;
initialized.value = false; // Allow re-fetch on next app load/login
loading.value = false;
await navigateTo('/login');
}
};
return {
user,
loading,
isAuthenticated,
fetchMe,
login,
logout,
};
}