84 lines
2.3 KiB
TypeScript
84 lines
2.3 KiB
TypeScript
// /composables/useAuth.ts
|
|
|
|
interface User {
|
|
id: string;
|
|
email: string;
|
|
nickname: string;
|
|
avatar: string | null;
|
|
coins: number;
|
|
exp: number;
|
|
soundOn: boolean;
|
|
confettiOn: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export function useAuth() {
|
|
// All Nuxt composables that require instance access MUST be called inside the setup function.
|
|
// useState is how we create shared, SSR-safe state in Nuxt.
|
|
const user = useState<User | null>('user', () => null);
|
|
const initialized = useState('auth_initialized', () => false);
|
|
const loading = ref(false); // This is a local, non-shared loading ref for fetchMe's internal use
|
|
|
|
const api = useApi();
|
|
const isAuthenticated = computed(() => !!user.value);
|
|
|
|
const fetchMe = async () => {
|
|
// This function can be called multiple times, but the logic inside
|
|
// will only run once thanks to the initialized flag.
|
|
if (initialized.value) return;
|
|
|
|
loading.value = true;
|
|
try {
|
|
// The backend returns the user object nested under a 'user' key.
|
|
const response = await api<{ user: User }>('/auth/me', { method: 'GET' });
|
|
user.value = response.user; // Correctly assign the nested user object
|
|
} catch (error) {
|
|
user.value = null; // Silently set user to null on 401
|
|
} finally {
|
|
loading.value = false;
|
|
initialized.value = true; // Mark as initialized after the first attempt
|
|
}
|
|
};
|
|
|
|
const login = async (email, password) => {
|
|
// The calling component is responsible for its own loading state.
|
|
// This function just performs the action.
|
|
await api('/auth/login', {
|
|
method: 'POST',
|
|
body: { email, password },
|
|
});
|
|
// After a successful login, allow a re-fetch of the user state.
|
|
initialized.value = false;
|
|
await fetchMe();
|
|
};
|
|
|
|
const logout = async () => {
|
|
try {
|
|
await api('/auth/logout', { method: 'POST' });
|
|
} finally {
|
|
// Always clear state and redirect, regardless of API call success.
|
|
user.value = null;
|
|
initialized.value = false;
|
|
await navigateTo('/login');
|
|
}
|
|
};
|
|
|
|
const updateUser = (partialUser: Partial<User>) => {
|
|
if (user.value) {
|
|
user.value = { ...user.value, ...partialUser };
|
|
}
|
|
};
|
|
|
|
// Expose the state and methods.
|
|
return {
|
|
user,
|
|
isAuthenticated,
|
|
initialized,
|
|
fetchMe,
|
|
login,
|
|
logout,
|
|
updateUser,
|
|
};
|
|
}
|