Vue and Pinia wont read an updated value from a ref() variable - vue.js

I'm using Pinia and vue for an auth system that prevents anyone from accesing the private pages, however I would like to change the status of a page from public to private after an initial setup.
{
path: '/settings',
name: 'settings',
component: SettingsView
},
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/setup',
name: 'setup',
component: SetupView,
beforeEnter: (to) => {
const auth = useAuthStore()
console.log(`in router, auth.configInit = ${auth.configInit}`)
if (auth.configInit && to.name !== 'login') {
console.log('entered as true')
return { name: 'login' }
}
}
},
{
path: '/:pathMatch(.*)*',
name: 'page404',
component: Page404View
}]
})
router.beforeEach(async (to) => {
const publicPages = ['/login', '/setup']
const authRequired = !publicPages.includes(to.path)
const auth = useAuthStore()
if (authRequired && !auth.checkSession()) {
auth.returnUrl = to.fullPath
return '/setup'
}
however, whatever I try to do, auth.configInit never changes from false to true. this is the Pinia state:
state: () => ({
token: JSON.parse(localStorage.getItem('userToken')),
sessionTime: JSON.parse(localStorage.getItem('userSession')),
returnUrl: null,
configInit: useStore().store.value.is_config_init
})
and the part that is refering to useStore() is the next one:
const store = ref({
tienda_id: "foo",
is_config_init: false
})
and the proper get under the same class
const get_store_settings = async() => {
const url = `http://${host}/api/store`
await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
})
.then((res) => res.json())
.then((datos) => {
store.value = datos
})
.catch((error) => {
console.log(error)
})
I have very litle experience and I'm the only front end in my workplace, please I need help

Related

Vue 3, SocketIO - won't join the room after login and router push

I am experiencing the following issue - once the user is logged in, and onMounted event is finished, SocketIO client side should join the room. But this doesn't happen for some reason. I have to manually refresh browser in order to join the room. What am I doing wrong here?
I have the following code for the SocketIO on the client side:
import { io } from "socket.io-client";
const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
console.log(token);
const ioSocket = io('https://dolphin-app-e4ozn.ondigitalocean.app', {
withCredentials: true,
transportOptions: {
polling: {
extraHeaders: {
'authorization': `${token}`,
},
},
},
});
export const socket = ioSocket
The vue 3 router logic:
import { createRouter, createWebHistory } from 'vue-router'
import Landing from '#/views/Landing.vue'
import Login from '#/views/login/Login.vue'
import ResetPassword from '#/views/login/ResetPassword.vue'
import ForgotPassword from '#/views/login/ForgotPassword.vue'
const routes = [
{
path: '/login',
name: 'login',
component: Login,
meta: {
isGuest: true,
title: 'Servant | Login'
}
},
{
path: '/resetPassword',
name: 'resetPassword',
component: ResetPassword,
meta: {
isGuest: true,
title: 'Servant | Reset Password'
}
},
{
path: '/forgotPassword',
name: 'forgotPassword',
component: ForgotPassword,
meta: {
isGuest: true,
title: 'Servant | Forgot Password'
}
},
{
path: '/',
name: 'landing',
component: Landing,
meta: {
requiresAuth: true,
title: 'Servant',
role: 'waiter'
}
},
{
path: '/:pathMatch(.*)*',
component: Landing
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title || "Servant"
let token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
if(to.meta.requiresAuth && !token)
{
next({name: 'login'})
}
if (token && to.meta.isGuest )
{
next({ name: 'landing' })
}
next();
});
export default router
Login component logic:
function login() {
loading.value = true
formClass = ''
if (user.remember)
{
window.localStorage.setItem('remember', user.remember)
}
mainStore
.login(user)
.then((response) => {
loading.value = false
router.push({
name: 'landing',
})
})
.catch((err) => {
loading.value = false
errorMsg.value = err.response.data.messages.error
formClass = 'was-validated'
})
}
Once the component is mounter I have following logic:
onMounted(() => {
socket.emit("join", import.meta.env.VITE_SOCKET_ROOM, (message) => {
console.log(message);
});
})
On the SocketIO server side I have following logic:
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
if(header !== 'null')
{
jwtVerify(header, secret).then((res) => {
if (res === true) {
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next();
}
return next(new Error("authentication error"));
});
}
});

how solve Avoided redundant navigation to current location: "/login". in vue?

Hi everybody i'm trying to make login page and redirect to home page ('/')
When i'm logging i haven't errors in console i can see the error using vue devtools
ERROR VUE DEV TOOL
End of navigation
/login
02:27:19.124
guard:afterEach
failure:Avoided redundant navigation to current location: "/login".
status:❌
from:/login
fullPath:"/login"
path:"/login"
query:Object (empty)
hash:""
name:"login"
params:Object (empty)
matched:Array[1]
meta:Object (empty)
redirectedFrom:undefined
href:"/login"
to:/login
fullPath:"/login"
hash:""
query:Object (empty)
name:"login"
path:"/login"
params:Object (empty)
matched:Array[1]
meta:Object (empty)
redirectedFrom:Object
href:"/login"
this is my login's method
methods:{
async submitForm(user){
const userForm=new FormData();
userForm.append("username", this.username);
userForm.append("password", this.password);
await this.$store.dispatch("auth/login", userForm).then(
()=>{
const user = localStorage.getItem('user')
console.log(user) //to check if i logged, in console get undefined but if try localStorage.getItem('user') i got the user.
this.$router.push('/')
}),
(error)=>{
console.log(error)
}
}
}
ROUTES
const router = createRouter({
history: createWebHistory(),
scrollBehavior() {
return { top: 0 }
},
routes,
})
{
path: '/',
name: 'dashboard',
component: () => import('#/views/Dashboard.vue'),
children: [
{
path: '',
name: 'home',
component: () => import('#/views/dashboard/Home.vue'),
},
....
router.beforeEach((to, from, next) => {
const publicPages = ['/login'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
// trying to access a restricted page + not logged in
// redirect to login page
if (authRequired && !loggedIn) {
next('/login');
} else {
next();
}
});
auth.service
class AuthService {
login(user) {
let dator={
access_token: '',
user:{}
}
console.log('AUTHSERVICE-->\n'+user)
return axios
.post(API_URL + 'login/access-token', user)
.then(response => {
console.log(response.data.access_token)
if (response.data.access_token) {
dator.access_token=response.data.access_token
localStorage.setItem('token', JSON.stringify(dator.access_token))
axios.get(API_URL + 'users/me/', { headers: authHeader() })
.then(response =>{
localStorage.setItem('user', JSON.stringify(response.data))
dator.user=response.data
})
}
return dator;
});
}
auth.module VUEX
import AuthService from '../services/auth.service';
const token = JSON.parse(localStorage.getItem('token'));
const user = JSON.parse(localStorage.getItem('user'));
const initialState = token && user
? { status: { loggedIn: true }, token,user }
: { status: { loggedIn: false }, token:null, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, userForm) {
console.log(userForm)
return AuthService.login(userForm).then(
datologin => {
console.log('datologin',datologin)
commit('loginSuccess', datologin);
return Promise.resolve(datologin);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
if after login i force the '/' in the browser the page work. So i don't know where is my bad.
Sorry for noob error. The problem was in second request with axios so i refactor this part and now work
async asyncLogin(user){
let uservuex={
access_token: '',
user:{}
}
try{
const token = await axios.post(API_URL + 'login/access-token', user)
uservuex.access_token = await token.data.access_token
localStorage.setItem('token', JSON.stringify(uservuex.access_token))
const me = await axios.get(API_URL + 'users/me/', { headers: authHeader() })
uservuex.user= await me.data
localStorage.setItem('user', JSON.stringify(uservuex.user))
}catch(error){
console.log(error)
}
return uservuex
}

Vue Router Warning and Router.push is not Functioning

I am trying to route to another page after getting response from adonis project. Calling to post method is working. However router.push('/') is not functioning. Only login page reloaded every time when I submitted the b-from.
async login({ commit, state }) {
console.log("Login")
try {
const response = await HTTP()
.post('/admin/login', {
email: state.loginEmail,
password: state.loginPassword
})
.then(response => {
console.log("Ok")
console.log(response.data)
if (response.data == 'UserNotFoundException') {
alert('User not found')
router.push('/')
}
if (response.data == 'PasswordMisMatchException') {
alert('password not ms')
router.push('/')
}
if (response.data.token) {
console.log(response)
//commit('setToken', response.data.token)
router.push('/')
} else {
router.push('/')
}
})
console.log(response)
//return router.push('/')
} catch (error) {
console.log(error)
}
},
Routes:
routes: [
{
name: "FullLogin",
path: "/login",
component: () => import("#/views/authentication/FullLogin"),
},
{
path: "/",
redirect: "/dashboard/docs-dashboard",
component: () => import("#/layouts/full-layout/FullLayout"),
children: [
{
name: "Dashboard",
path: "/dashboard/docs-dashboard",
component: () => import("#/views/dashboards/docsDashboard"),
},
]
}
]
router.beforeEach((to, from, next) => {
next()
})
I can't figure out why router.push('/') is not routing.
According to docs:
Note: Inside of a Vue instance, you have access to the router instance as $router. You can therefore call this.$router.push.

Vue Router catch 401 on route changes and destroy local storage JWT

Having a dashboard app created with vuejs and vue-router most of my routes are requiring authentication and I want to catch globally if token is expired and route back in that case to login component.
Right now router looks as it follows
import Vue from 'vue'
import VueRouter from 'vue-router'
import AttendersView from '#/views/AttendersView.vue'
import LoginView from '#/views/LoginView.vue'
import store from '../store'
import log from '#/middleware/log'
Vue.use(VueRouter)
const routes = [
{
path: '*',
meta: {
name: '',
requiresAuth: true
},
redirect: {
path: '/attenders'
}
},
{
path: '/login',
component: LoginView,
meta: {
guest: true
},
children: [
{
path: '',
component: () => import('#/components/LoginForm.vue')
}
]
},
{
path: '/',
meta: {
name: 'Dashboard View',
requiresAuth: true
},
component: () => import('#/views/DashboardView.vue'),
children: [
{
path: '/attenders',
name: 'Anmeldungen',
component: AttendersView,
meta: {
requiresAuth: true,
middleware: log
}
},
{
path: '/organizations',
name: 'Verbände',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/OrganizationsView.vue')
},
{
path: '/workgroups',
name: 'Arbeitsgruppen',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/WorkgroupsView.vue')
},
{
path: '/status',
name: 'Status',
meta: {
requiresAuth: true,
middleware: log
},
component: () => import(/* webpackChunkName: "about" */ '../views/StatusView.vue')
}
]
}
]
const router = new VueRouter({
mode: 'history',
base: 'dashboard',
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
if (to.hash) {
return { selector: to.hash }
}
return { x: 0, y: 0 }
}
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.authorized) {
next()
return
}
next('/login')
} else {
next()
}
})
export default router
and here is my custom request.js
import axios from 'axios'
class HttpClient {
constructor (token) {
if (localStorage.getItem('token')) {
token = localStorage.getItem('token')
}
const service = axios.create({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
this.service = service
}
redirectTo = (document, path) => {
document.location = path
}
get (path) {
return this.service.get(path)
}
patch (path, payload, callback) {
return this.service
.request({
method: 'PATCH',
url: path,
responseType: 'json',
data: payload
})
}
post (path, payload) {
return this.service.request({
method: 'POST',
url: path,
responseType: 'json',
data: payload
})
}
delete (path, payload) {
return this.service.request({
method: 'DELETE',
url: path,
responseType: 'json',
data: payload
})
}
download (path) {
return this.service.request({
method: 'POST',
url: path,
responseType: 'blob'
})
}
}
const HttpRequests = new HttpClient()
export default HttpRequests
and right now I'm doing like this in component to catch 401
methods: {
fetchInitialData: function () {
this.isLoading = true
HttpClient.get(API_ATTENDERS_ENDPOINT).then(resp => {
this.attenders = resp.data.attenders
this.organizations = resp.data.organizations
this.workgroups = resp.data.workgroups
this.isLoading = false
}).catch(error => {
if (error.response.status === 401) {
this.$store.dispatch('logout')
}
})
}
but I need something generic.
What is the best approach and where should I place an axios interceptor ?
What you can do is use navigation guards for this task: https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Before the user enters any of the routes you want Authentication attached to then you can have a before enter, and then check their privileges.
Pass then on with a next() if they succeed or throw them to the login.

loggedIn state reverts back to false after Logging in and doesn't allow me to guard route

I'm trying to guard my routes with state: { loggedIn: false }, when I login from my Login.vue component the goal is to trigger an action this.$store.dispatch('setLogin') that mutates the state of loggedIn to true. There is then navigation guard that is suppose to prevent me form seeing my Login.vue and Regester.vue components. The problem is that it seems like the state changes to true, but not the base state: allowing me to keep hitting the /auth/login and /auth/register routes.
Routes
const routes = [
{
path: '/auth',
name: 'auth',
component: Auth,
children: [
{ name: 'login', path: 'login', component: Login },
{ name: 'register', path: 'register', component: Register },
],
meta: {
requiresVisitor: true,
}
},
{
path: '/logout',
name: 'logout',
component: Logout
}
]
Login Component
login() {
this.$http.get('/sanctum/csrf-cookie').then(response => {
this.$http.post('/login', {
email: this.username,
password: this.password,
}).then(response2 => {
this.$store.dispatch('setLogin')
this.$store.dispatch('getUser')
alert(this.$store.state.loggedIn)
this.$router.push({ name: 'Home' })
}).catch(error => {
console.log(error.response.data);
const key = Object.keys(error.response.data.errors)[0]
this.errorMessage = error.response.data.errors[key][0]
})
});
}
Vuex
export default new Vuex.Store({
state: {
loggedIn: false,
user: JSON.parse(localStorage.getItem('user')) || null,
},
mutations: {
setLogin: (state) => {
state.loggedIn = true
},
SET_USER_DATA (state, userData) {
localStorage.setItem('user', JSON.stringify(userData))
state.user = userData;
},
removeUser(state) {
localStorage.removeItem('user');
state.user = null;
}
},
actions: {
getUser(context) {
if (context.state.loggedIn) {
alert('hit');
return new Promise((resolve, reject) => {
axios.get('api/user')
.then(response => {
context.commit('SET_USER_DATA', response.data.data)
resolve(response)
})
.catch(error => {
reject(error)
})
})
}
},
setLogin(context){
context.commit('setLogin')
}
},
modules: {
}
})
It's strange because alert(this.$store.state.loggedIn) renders true, but when I go back the auth link there's a mounted state alert that comes back false.
Here's my navigation guards as well:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.loggedIn) {
next({
name: 'login',
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.state.loggedIn) {
next({
name: 'Home',
})
return
} else {
next()
}
} else {
next()
}
})
You need to store the loggedIn user in local storage:
setLogin: (state) => {
state.loggedIn = localStorage.setItem('loggedIn', 'true')
state.loggedIn = true
},
Then your state should look like:
state: {
loggedIn: localStorage.getItem('loggedIn') || null,
},