From 055515269bf89cb323efaf8098d6b0a14daa628d Mon Sep 17 00:00:00 2001 From: Alexander Andreev Date: Sat, 3 Jan 2026 14:50:36 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F,=20=D0=B0=D0=B2=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BE=D0=BA.=20=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D1=80=D1=82=D0=BE=D0=B2=D1=8B=D0=B9=20=D1=8D=D0=BA?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=20=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B8=D0=B2=D1=8B?= =?UTF-8?q?=D1=87=D0=B5=D0=BA=20=D0=BF=D0=BE=D0=BA=D0=B0=20=D0=B1=D0=B5?= =?UTF-8?q?=D0=B7=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D0=B0,=20=D0=BD=D0=BE=20=D1=80=D0=BE=D1=83=D1=82=D1=8B?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/composables/useAuth.ts | 76 +++-------- app/pages/habits.vue | 263 ++++++++++++++++++++++++++++++++----- app/pages/index.vue | 17 +-- app/pages/login.vue | 7 +- 4 files changed, 265 insertions(+), 98 deletions(-) diff --git a/app/composables/useAuth.ts b/app/composables/useAuth.ts index 3a3c66a..fefc069 100644 --- a/app/composables/useAuth.ts +++ b/app/composables/useAuth.ts @@ -1,101 +1,65 @@ // /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. + // 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); - const loading = useState('auth_loading', () => false); const initialized = useState('auth_initialized', () => false); + const loading = ref(false); // This is a local, non-shared loading ref for fetchMe's internal use - // --- 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; - } + if (initialized.value) return; loading.value = true; initialized.value = true; try { - const fetchedUser = await api('/auth/me', { method: 'GET' }); - user.value = fetchedUser; + user.value = await api('/auth/me', { method: 'GET' }); } catch (error) { - user.value = null; // Silently handle 401s or other errors + user.value = null; // Silently set user to null } 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; - } + // 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(); }; - /** - * 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 { + // Always clear state and redirect, regardless of API call success. user.value = null; - initialized.value = false; // Allow re-fetch on next app load/login - loading.value = false; + initialized.value = false; await navigateTo('/login'); } }; + // Expose the state and methods. return { user, - loading, isAuthenticated, + initialized, fetchMe, login, logout, }; -} \ No newline at end of file +} diff --git a/app/pages/habits.vue b/app/pages/habits.vue index 7323f83..8114e42 100644 --- a/app/pages/habits.vue +++ b/app/pages/habits.vue @@ -1,45 +1,163 @@ diff --git a/app/pages/index.vue b/app/pages/index.vue index 02a629e..9b7839c 100644 --- a/app/pages/index.vue +++ b/app/pages/index.vue @@ -1,6 +1,9 @@