diff --git a/app/components/forms/auth/login-form.vue b/app/components/forms/auth/login-form.vue new file mode 100644 index 0000000..5632403 --- /dev/null +++ b/app/components/forms/auth/login-form.vue @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/app/components/profile-menu.vue b/app/components/profile-menu.vue index 8a0687c..93f2171 100644 --- a/app/components/profile-menu.vue +++ b/app/components/profile-menu.vue @@ -2,11 +2,13 @@ const dropdownOpen = ref(false) const profileMenu = ref(null) -function toggleDropdown() { +const {logout} = useAuth() + +const toggleDropdown = () => { dropdownOpen.value = !dropdownOpen.value } -function onClickOutside(e: MouseEvent) { +const onClickOutside = (e: MouseEvent) => { if (profileMenu.value && !profileMenu.value.contains(e.target as Node)) { dropdownOpen.value = false } @@ -26,7 +28,7 @@ onUnmounted(() => document.removeEventListener('click', onClickOutside))
  • Account
  • -
  • Log Out
  • +
  • Log Out
  • diff --git a/app/composables/$api.ts b/app/composables/$api.ts new file mode 100644 index 0000000..22261e2 --- /dev/null +++ b/app/composables/$api.ts @@ -0,0 +1,17 @@ +export const $api = ( + path: string, + options: Parameters>[1] = {} +) => { + const config = useRuntimeConfig() + const xsrfToken = useCookie('XSRF-TOKEN') + + return $fetch(path, { + baseURL: config.public.apiBase, + credentials: 'include', + headers: { + Accept: 'application/json', + ...(xsrfToken.value ? {'X-XSRF-TOKEN': xsrfToken.value} : {}), + }, + ...options, + }) +} diff --git a/app/composables/useApiData.ts b/app/composables/useApiData.ts new file mode 100644 index 0000000..2cb0c25 --- /dev/null +++ b/app/composables/useApiData.ts @@ -0,0 +1,21 @@ +export const useApiData = ( + url: string | Ref, + options: Parameters>[1] = {} +) => { + const config = useRuntimeConfig() + const xsrfToken = useCookie('XSRF-TOKEN') + const requestHeaders = useRequestHeaders(['cookie']) + + return useFetch(url, { + baseURL: config.public.apiBase, + credentials: 'include', + watch: false, + server: false, + headers: computed(() => ({ + Accept: 'application/json', + ...requestHeaders, + ...(xsrfToken.value ? {'X-XSRF-TOKEN': xsrfToken.value} : {}), + })), + ...options, + }) +} diff --git a/app/composables/useAuth.ts b/app/composables/useAuth.ts new file mode 100644 index 0000000..d6774cd --- /dev/null +++ b/app/composables/useAuth.ts @@ -0,0 +1,20 @@ +export const useAuth = () => { + const config = useRuntimeConfig() + const router = useRouter() + + const login = async (email: string, password: string) => { + await $fetch('/sanctum/csrf-cookie', { + baseURL: config.public.apiBase, + credentials: 'include', + }) + await $api('/api/login', {method: 'POST', body: {email, password}}) + await router.push('/') + } + + const logout = async () => { + await $api('/api/logout', {method: 'POST'}) + await router.push('/') + } + + return {login, logout} +} diff --git a/app/pages/auth/login.vue b/app/pages/auth/login.vue new file mode 100644 index 0000000..576b08c --- /dev/null +++ b/app/pages/auth/login.vue @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/nuxt.config.ts b/nuxt.config.ts index 6d589bd..fe5d619 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,6 +1,11 @@ // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ compatibilityDate: '2025-07-15', + runtimeConfig: { + public: { + apiBase: '', // overridden by NUXT_PUBLIC_API_BASE + }, + }, devtools: {enabled: true}, css: ['~/assets/css/reset.css'], modules: [