101 lines
2.4 KiB
TypeScript
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,
|
|
};
|
|
} |