I'm currently having trouble finding a bug in router code, it worked before and I don't know when or how I broke it. I already checked in older versions but it seems to not have changed. The problem is that even when I'm deleting all code out of the beforeEach Hook in my router and just using a console.log() statement, nothing gets printed in the console and my auth-guard is therefore not working. I tried to change the order according to this: https://forum.vuejs.org/t/router-beforeeach-if-manually-input-adress-in-browser-it-does-not-work/12461/3 but nothing changed.
I am having the following code:
router/index.js
router.beforeEach = (to, from, next) => {
console.log('he?')
const currentUser = Firebase.auth().currentUser
const isGold = store.getters['user/isGold']
const requiresAuth = to.matched.some(route => route.meta.requiresAuth)
const requiresGold = to.matched.some(route => route.meta.requiresGold)
console.log(requiresGold, isGold, 'halloooooooo?')
if (currentUser && to.name === 'Login') {
next('/dashboard')
}
if (requiresGold && !isGold) {
console.log('trigger')
}
if (requiresAuth && !currentUser) {
next('/login')
} else {
next(false)
}
}
main.js
Firebase.auth().onAuthStateChanged(user => {
if (user) {
const getToken = () => {
return user.getIdToken(true).then(token => {
store.dispatch('user/setToken', token)
})
}
getToken().then(() => {
store.dispatch('user/setUser')
setInterval(getToken, 3540 * 1000)
})
}
new Vue({
el: '#app',
store,
router,
template: '<App/>',
components: { App }
})
})
Thanks in advance for any help!
You do not assign to router.beforeEach. router.beforeEach is a method, and you call it with a function. (docs) This is how you should use the router navigation guard:
router.beforeEach((to, from, next) => {
console.log('he?')
const currentUser = Firebase.auth().currentUser
const isGold = store.getters['user/isGold']
const requiresAuth = to.matched.some(route => route.meta.requiresAuth)
const requiresGold = to.matched.some(route => route.meta.requiresGold)
console.log(requiresGold, isGold, 'halloooooooo?')
if (currentUser && to.name === 'Login') {
next('/dashboard')
}
if (requiresGold && !isGold) {
console.log('trigger')
}
if (requiresAuth && !currentUser) {
next('/login')
} else {
next(false)
}
});
Related
I have the needings to use firebase auth with vue router.
I have this simple guard, but I've noticed that sometimes the users will see for a while the pages also if they are not logged.
router.beforeEach( async (to, from) => {
onAuthStateChanged( getAuth(app), (user) => {
console.log(user, to.meta.requireAuth)
if( to.meta.requireAuth && !user ) {
return {
name: 'Signin'
}
}
})
})
I also have this kind of control inside my components, but I'm looking for something global to use to prevent unregistered users to see the app.
Any suggestion?
You can wrap the onAuthStateChanged in a Promise and make your before each an async function.
// in some global file
export async function getCurrentUser(): Promise<User | null> {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged((user) => {
unsubscribe();
resolve(user);
}, reject);
});
}
// your router file
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.publicAccess)) {
next();
} else {
const currentUser = await getCurrentUser();
if (currentUser) {
next();
} else {
next({ name: "Login" });
}
}
});
// Your route object
{
name: "Login",
path: "/login",
component: () => import("#/views/authentication/Login.vue"),
}
I'm using my own SSR boilerplate for Vue.
https://github.com/Djancyp/luna-vue-ssr-boilerplate
All working fine pages are SSR rendering.
The issue that I'm currently having is no SSR components are failing as soon as I import them to components.
server trow err:
ReferenceError: document is not defined
I'm aware of the issue is document does not exist on nodejs server.
My question is How can I stop rendering no SSR components on the server ?
-I've tried No-SSR but no joy.
my server-enty.ts
import createApp from './app'
const config = require('config')
const isDev = process.env.NODE_ENV !== 'production'
export default context => {
return new Promise((resolve, reject) => {
console.log('what the f server')
const s = isDev && Date.now()
const { app, router, store } = createApp(config)
const { url } = context
const { fullPath } = router.resolve(url).route
if (fullPath !== url) {
return reject({ url: fullPath })
}
router.push(url)
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
console.log(matchedComponents)
if (!matchedComponents.length) {
console.log('what the **** mate error')
return reject({ code: 404 })
}
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
}, reject)
})
}
Vue-SSR executes asyncData and serverPrefetch on the server-side. Remove these from your component, move the code to created/ 'mounted' and it should stop rendering on the server-side.
My assumption is that you are executing document. within asyncData or serverPrefetch.
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
}, reject)
the issue was that I was rejecting if the async template not exist.
Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({
store,
route: router.currentRoute
}))).then(() => {
isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
const meta = (app as any).$meta()
context.meta = meta
context.state = store.state
resolve(app)
}).catch(err => {
console.log(err)
return reject
})
})
this resolved the issue but needs to improve the code a little bit.
Thanks for helps guys.
I have loaded all permissions when the sidebar is loading after login and getters are updated. I can access all permissions from the sidebar component.
Now I want to access all permissions in my middleware. Is it possible? What to do?
Please give a suggestion.
Here is my permission store:
const state = {
permissions: [],
user: [],
}
const getters = {
getPermissions: state => state.permissions,
getUserInfo: state => state.user,
}
const actions = {
userPermission({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const per = res.data.data.permissions;
commit("setPermissions", per);
// console.log(res.data.data.permissions);
})
.catch(err => {
console.log(err);
});
}
},
userInfo({commit}, data) {
if (data != null) {
axios.get("/api/auth/user", {params: { token: data.token}})
.then(res => {
const info = res.data.data.user;
commit("setUserInfo", info);
// console.log(res.data.data.user);
})
.catch(err => {
console.log(err);
});
}
},
}
const mutations = {
setPermissions(state, data) {
state.permissions = data;
},
setUserInfo(state, data) {
state.user = data;
}
}
export default {
state,
getters,
actions,
mutations
}
Here is the middleware function:
import store from '../store';
export default (to, from, next) => {
if (isAuthenticated()) {
if (!hasPermissionsNeeded(to)) {
next('admin/permission-denied');
} else {
next();
}
next();
} else {
next('/admin/session/login');
}
};
function isAuthenticated() {
if (localStorage.getItem("userInfo") != null && localStorage.getItem("userInfo").length > 0) {
return true;
} else {
localStorage.removeItem("userInfo");
return false;
}
};
function hasPermissionsNeeded(to) {
var permissions = store.getters.getPermissions;
if(permissions.includes(to.meta.permissions) || to.meta.permissions == '*') {
return true;
} else {
return false;
}
};
Here is the router logic:
path: "/admin/country",
component: () => import("./views/admin/country/country"),
beforeEnter: authenticate,
meta : {
permissions: 'browse country'
}
I can't see where you're dispatching the userPermission action to load the permissions, but I assume you're only dispatching it somewhere that only gets called after the middleware has run. So it looks like the permissions might not have been loaded by the time you're running the middleware. You might want to dispatch the permission in the middleware, wait for it to finish and only then check the permissions. For example:
export default (to, from, next) => {
store.dispatch('userPermission').then(() => {
if (isAuthenticated()) {
...
})
I am writing a config for ssr
Added mixin, to replace the title and meta Announced it globally in the app.js file.
import headMixin from './util/title'
Vue.mixin(headMixin);
When you first load the page, or when you go to the root of the site, it works.
If you go to another page, it does not work (
I wanted to add it to the config file entry-client.js in the function router.onReady()
import Vue from 'vue'
import 'es6-promise/auto'
import {createApp} from './app'
import ProgressBar from './components/ProgressBar.vue'
const bar = Vue.prototype.$bar = new Vue(ProgressBar).$mount();
document.body.appendChild(bar.$el);
Vue.mixin({
beforeRouteUpdate(to, from, next) {
const {asyncData} = this.$options;
if (asyncData) {
asyncData({
store: this.$store,
route: to
}).then(next).catch(next)
} else {
next()
}
}
});
const {app, router, store} = createApp();
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
router.onReady(() => {
router.beforeResolve((to, from, next) => {
const matched = router.getMatchedComponents(to);
const prevMatched = router.getMatchedComponents(from);
let diffed = false;
const activated = matched.filter((c, i) => {
return diffed || (diffed = (prevMatched[i] !== c))
});
const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _);
if (!asyncDataHooks.length) {
return next()
}
// TODO Обсудить наличие статусбара
bar.start();
Promise.all(asyncDataHooks.map(hook => hook({ store, route: to })))
.then(() => {
bar.finish();
next()
})
.catch(next)
});
app.$mount('#app')
});
But I can not understand how to do it correctly (
Himself mixin here
const cleanMetas = () => {
return new Promise((resolve, reject) => {
const items = document.head.querySelectorAll('meta');
for (const i in items) {
if (typeof items[i] === 'object'
&& ['viewport'].findIndex(val => val === items[i].name) !== 0
&& items[i].name !== '')
document.head.removeChild(items[i])
}
resolve()
})
};
const createMeta = (vm, name, ...attr) => {
const meta = document.createElement('meta');
meta.setAttribute(name[0], name[1]);
for (const i in attr) {
const at = attr[i];
for (const k in at) {
meta.setAttribute(at[k][0], getString(vm, at[k][1]))
}
}
document.head.appendChild(meta);
};
const getString = (vm, content) => {
return typeof content === 'function'
? content.call(vm)
: content
};
export const getMeta = (vm, meta, env) => {
if (typeof meta !== 'object')
return;
if (env) {
return Object.keys(meta)
.map(value => {
return Object.keys(meta[value])
.map(key => `${key}="${getString(vm, meta[value][key])}"`)
.join(" ");
})
.map(value => ` <meta ${value} >`)
.join("\n");
} else {
return meta
}
};
const serverHeadMixin = {
created() {
const {head} = this.$options;
if (head) {
const {title} = head;
if (title)
this.$ssrContext.title = getString(this, title);
const {meta} = head;
if (meta)
this.$ssrContext.meta = `\n${getMeta(this, meta, true)}`
}
}
};
const clientHeadMixin = {
mounted() {
const vm = this;
const {head} = this.$options;
if (head) {
const {title} = head;
if (title) {
document.title = getString(this, title)
}
}
if (head) {
cleanMetas().then(() => {
const {meta} = head;
if (meta)
for (const nm in meta) {
const name = Object.entries(meta[nm])[0];
const attr = Object.entries(meta[nm]).splice(1, Object.entries(meta[nm]).length);
createMeta(vm, name, attr)
}
})
}
}
};
export default process.env.VUE_ENV === 'server'
? serverHeadMixin
: clientHeadMixin
Link to repository with full config
I have used this link as a reference to make a request before entering a route:
https://router.vuejs.org/en/advanced/data-fetching.html
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
function getCities () {
return Vue.http({
method: 'GET',
url: process.env.base_url + 'cities'
})
}
export default {
data () {
return {
cities: []
}
},
beforeRouteEnter (to, from, next) {
getCities((err, cities) => {
if (err) {
next(false)
} else {
next(vm => {
vm.cities = cities.data
})
}
})
},
watch: {
$route () {
this.cities = []
getCities((err, cities) => {
if (err) {
this.error = err.toString()
} else {
this.cities = cities.data
}
})
}
}
However it doesn't seem to be working for me. I have tested this code and the request is successfully being made. However the result is not being returned. Currently, the request itself is being returned from the function, but I cannot show it in the beforeRouteEnter callback where it supposedly should assign it to vm.cities neither in the watch $route section.
Any help/opinion is appreciated.
The Vue.http method returns a promise, so the code should read:
beforeRouteEnter (to, from, next) {
getCities().then(response => {
next(vm => vm.cities = response.body)
}
}