diff --git a/middleware/auth.ts b/middleware/auth.ts index e8b09c3..2a0db26 100644 --- a/middleware/auth.ts +++ b/middleware/auth.ts @@ -1,8 +1,7 @@ import { useAuthStore } from '@/stores/auth' export default defineNuxtRouteMiddleware((to, from) => { - //TODO: remove logs - console.log('🔍 Middleware ejecutado') - console.log('📦 to.meta:', to.meta) + // console.log('🔍 Middleware ejecutado') + // console.log('📦 to.meta:', to.meta) const AUTH_ROLES = { ANON: 0, SHOP_USER: 1, @@ -11,12 +10,12 @@ export default defineNuxtRouteMiddleware((to, from) => { } const authStore = useAuthStore() const userRole = authStore.role - console.log('👤 Rol actual:', userRole) + //console.log('👤 Rol actual:', userRole) const authority = to.meta?.auth?.authority as keyof typeof AUTH_ROLES const requiredLevel = AUTH_ROLES[authority] //const required = to.meta.auth?.authority - console.log('⚠️ Autoridad requerida:', authority, requiredLevel) + //console.log('⚠️ Autoridad requerida:', authority, requiredLevel) // Check if user is connected first @@ -25,18 +24,18 @@ export default defineNuxtRouteMiddleware((to, from) => { // Get authorizations for matched routes (with children routes too) const userLevel = AUTH_ROLES[userRole as keyof typeof AUTH_ROLES] - console.log('🧮 userLevel:', userLevel, 'requiredLevel:', requiredLevel) + //console.log('🧮 userLevel:', userLevel, 'requiredLevel:', requiredLevel) - console.log('[Auth Middleware]', { - to: to.path, - meta: to.meta, - userRole: authStore.role, - requiredLevel, - userLevel, - }) + // console.log('[Auth Middleware]', { + // to: to.path, + // meta: to.meta, + // userRole: authStore.role, + // requiredLevel, + // userLevel, + // }) if (userLevel < requiredLevel) { - console.log('🚫 Bloqueando acceso - redirigiendo a /login') + // console.log('🚫 Bloqueando acceso - redirigiendo a /login') return navigateTo('/login') } diff --git a/nuxt.config.ts b/nuxt.config.ts index ce2711d..3a2d425 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -5,6 +5,7 @@ import { $fetch } from 'ofetch' export default defineNuxtConfig({ compatibilityDate: '2025-07-15', components: true, + ssr: true, devtools: { enabled: true }, modules: [ '@nuxt/eslint', @@ -37,7 +38,11 @@ export default defineNuxtConfig({ }, }, }, - + + plugins: [ + '~/plugins/google-analytics.client.ts', + ], + sitemap: { exclude: ['/admin', '/admin/**', '/editar', '/editar/**'], urls: async () => { diff --git a/package-lock.json b/package-lock.json index 426efc1..dbc3f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,13 @@ "@pinia/nuxt": "^0.11.2", "dompurify": "^3.2.6", "eslint": "^9.32.0", + "js-cookie": "^3.0.5", "nuxt": "^3.17.7", "pinia": "^3.0.3", - "pinia-plugin-persistedstate": "^4.4.1", + "pinia-plugin-persistedstate": "^4.5.0", "vue": "^3.5.18", "vue-advanced-cropper": "^2.8.9", + "vue-gtag": "^3.5.2", "vue-router": "^4.5.1", "vue3-carousel": "^0.16.0" }, @@ -9283,6 +9285,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11069,9 +11080,9 @@ } }, "node_modules/pinia-plugin-persistedstate": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.4.1.tgz", - "integrity": "sha512-lmuMPpXla2zJKjxEq34e1E9P9jxkWEhcVwwioCCE0izG45kkTOvQfCzvwhW3i38cvnaWC7T1eRdkd15Re59ldw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.5.0.tgz", + "integrity": "sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==", "license": "MIT", "dependencies": { "deep-pick-omit": "^1.2.1", @@ -14106,6 +14117,21 @@ "eslint": "^8.57.0 || ^9.0.0" } }, + "node_modules/vue-gtag": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/vue-gtag/-/vue-gtag-3.5.2.tgz", + "integrity": "sha512-efTY4yrkNraFSu6CZqhFZLX5LggqCr44d6kcPnPPtzYhvu5ywrTFUnuvM2Vm238QC+YT43HkVEXV/L1OYnHvNg==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + } + }, "node_modules/vue-router": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", diff --git a/package.json b/package.json index 77deeaa..c1b8ac4 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,13 @@ "@pinia/nuxt": "^0.11.2", "dompurify": "^3.2.6", "eslint": "^9.32.0", + "js-cookie": "^3.0.5", "nuxt": "^3.17.7", "pinia": "^3.0.3", - "pinia-plugin-persistedstate": "^4.4.1", + "pinia-plugin-persistedstate": "^4.5.0", "vue": "^3.5.18", "vue-advanced-cropper": "^2.8.9", + "vue-gtag": "^3.5.2", "vue-router": "^4.5.1", "vue3-carousel": "^0.16.0" }, diff --git a/plugins/api.ts b/plugins/api.ts deleted file mode 100644 index 9eb70f9..0000000 --- a/plugins/api.ts +++ /dev/null @@ -1,48 +0,0 @@ -// plugins/api.ts -import { useAuthStore } from '~/stores/auth' - -export default defineNuxtPlugin((nuxtApp) => { - const auth = useAuthStore() - - // Función personalizada para hacer requests - const apiFetch = async (url: string, options: any = {}) => { - try { - const res = await $fetch(url, { - baseURL: useRuntimeConfig().public.apiBase, - credentials: 'include', // para enviar cookies si es necesario - headers: { - ...(options.headers || {}), - ...(auth.access ? { Authorization: `Bearer ${auth.access}` } : {}), - }, - ...options, - }) - return res - } catch (error: any) { - // Si no es 401, relanzamos el error - if (error?.status !== 401) throw error - - // Si es el endpoint de refresh, cerramos sesión - if (url.includes('refresh')) { - auth.logout() - return navigateTo('/login') - } - - // Usuario inactivo - if (error?.data?.code === 'user_inactive') { - auth.logout() - return navigateTo('/login') - } - - // Intentar refresh - try { - await auth.refresh() - return await apiFetch(url, options) // reintentar la petición original - } catch { - return navigateTo({ name: 'index', query: { redirected: 'true' } }) - } - } - } - - // Inyectar como $api - nuxtApp.provide('api', apiFetch) -}) diff --git a/plugins/google-analytics.client.ts b/plugins/google-analytics.client.ts new file mode 100644 index 0000000..2da6843 --- /dev/null +++ b/plugins/google-analytics.client.ts @@ -0,0 +1,19 @@ +// TODO: Review if its OK. https://matteo-gabriele.gitbook.io/vue-gtag/migration-v2-to-v3 +import { configure } from 'vue-gtag' +import { useRouter } from 'vue-router' + +export default defineNuxtPlugin(() => { + const config = useRuntimeConfig() + const router = useRouter() + + if (config.public.googleAnalyticsId) { + configure({ + tagId: config.public.googleAnalyticsId, + appName: 'latienda.coop', + pageTracker: { + router, + useScreenview: true, + }, + }) + } +}) \ No newline at end of file diff --git a/stores/auth.ts b/stores/auth.ts index 26b7428..e3988b3 100644 --- a/stores/auth.ts +++ b/stores/auth.ts @@ -11,6 +11,7 @@ export const useAuthStore = defineStore('auth', { role: 'ANON' as string, cookiesAreAccepted: false, }), + persist: true, // Enable persistence. Cookies will be stored 'auth' getters: { isAuthenticated: (state) => !!state.access, isUser: (state) => state.role === 'SHOP_USER',