NuxtJS/VueJS - Wildcard subdomain configuration - vue.js

I am trying to build a system where users will be able to register and use their names as subdomain on my main domain such as john.example.com. I have already configured the DNS which accepts wildcard subdomain on Vercel but am kinda stuck on how to configure it on Nuxt or Vue.
What I see so far doing on Vue as seen here is as follow:
const router = new VueRouter({
routes: [
{
path: '*',
name: user,
component: User
}]
})
router.beforeEach((to, from, next) => {
var subdir = window.location.host.split('.')[0]
var domain = 'localhost'
var pageToLoad = 'user'
if (subdir !== domain && to.name !=== pageToLoad) {
next({name: pageToLoad, params:{}})
} else {
next()
}
})
But I need to do same thing on Nuxtjs but I have no idea. Any help to accomplish this will be greatly appreciated.

Related

Dynamically add a route in a Nuxt3 middleware

I have a Nuxt3 project where I'd like to add new routes based on an API call to a database. For example, let's say a user navigates to /my-product-1. A route middleware will look into the database and if it finds an entry, it will return that a product page should be rendered (instead of a category page, for example).
This is what I came up with:
export default defineNuxtPlugin(() => {
const router = useRouter()
addRouteMiddleware('routing', async (to) => {
if (to.path == '/my-awesome-product') {
router.addRoute({
component: () => import('/pages/product.vue'),
name: to.path,
path: to.path
})
console.log(router.hasRoute(to.path)) // returns TRUE
}
}, { global: true })
})
To keep it simple, I excluded the API call from this example. The solution above works, but not on initial load of the route. The route is indeed added to the Vue Router (even on the first visit), however, when I go directly to that route, it shows a 404 and only if I don't reload the page on the client does it show the correct page when navigated to it for the second time.
I guess it has something to do with the router not being updated... I found the following example in a GitHub issue, however, I can't get it to work in Nuxt3 as (as far as I'm aware) it doesn't provide the next() method.
When I tried adding router.replace(to.path) below the router.addRoute line, I ended up in an infinite redirect loop.
// from https://github.com/vuejs/vue-router/issues/3660
// You need to trigger a redirect to resolve again so it includes the newly added
route:
let hasAdded = false;
router.beforeEach((to, from, next) => {
if (!hasAdded && to.path === "/route3") {
router.addRoute(
{
path: "/route3",
name: "route3",
component: () => import("#/views/Route3.vue")
}
);
hasAdded = true;
next('/route3');
return;
}
next();
});
How could I fix this issue, please?
Edit:
Based on a suggestion, I tried using navigateTo() as a replacement for the next() method from Vue Router. This, however, also doesn't work on the first navigation to the route.
let dynamicPages: { path: string, type: string }[] = []
export default defineNuxtRouteMiddleware((to, _from) => {
const router = useRouter()
router.addRoute({
path: to.path,
name: to.path,
component: () => import ('/pages/[[dynamic]]/product.vue')
})
if (!dynamicPages.some(route => route.path === to.path)) {
dynamicPages.push({
path: to.path,
type: 'product'
})
return navigateTo(to.fullPath)
}
})
I also came up with this code (which works like I wanted), however, I don't know whether it is the best solution.
export default defineNuxtPlugin(() => {
const router = useRouter()
let routes = []
router.beforeEach(async (to, _from, next) => {
const pageType = await getPageType(to.path) // api call
if (isDynamicPage(pageType)) {
router.addRoute({
path: to.path,
name: to.path,
component: () => import(`/pages/[[dynamic]]/product.vue`),
})
if (!routes.some(route => route.path === to.path)) {
routes.push({
path: to.path,
type: pageType,
})
next(to.fullPath)
return
}
}
next()
})
})
I suggest you use dynamic routing within /page directory structure - https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes
The [slug] concept is designed exactly for your usecase. You don't need to know all possible routes in advance. You just provide a placeholder and Nuxt will take care of resolving during runtime.
If you insist on resolving method called before each route change, the Nuxt's replacement for next() method you're looking for is navigateTo
https://nuxt.com/docs/api/utils/navigate-to
And I advise you to use route middleware and put your logic into /middleware/routeGuard.global.ts. It will be auto-executed upon every route resolving event. The file will contain:
export default defineNuxtRouteMiddleware((to, from) => {
// your route-resolving logic you wanna perform
if ( /* navigation should happen */ {
return navigateTo( /* your dynamic route */ )
}
// otherwise do nothing - code will flow and given to.path route will be resolved
})
EDIT: However, this would still need content inside /pages directory or some routes created via Vue Router. Because otherwise navigateTo will fail, as there would be no route to go.
Here is an example of one possible approach:
https://stackblitz.com/edit/github-8wz4sj
Based on pageType returned from API Nuxt route guard can dynamically re-route the original URL to a specific slug page.

Redirect to a route without the assigned base path in vue

I have a vue router implemented and the configuration looks something like this:
const router = new Router({
mode: "history",
base: '/base/path,
routes,
});
Then my routes looks something like:
const routes = [
{
path: "/products",
name: "products",
component: () => import("#/test/items/Index.vue"),
beforeEnter(routeTo, routeFrom, next) {
if (//SOME CONDITION THAT SHOULD MATCH){
next('url/without/base'); // ROUTE THAT SHOULD BE WITHOUT BASE
}
next()
},
},
]
in the above case scenario whenever the IF condition is fullfiled the router is redirected to localhost:8080/base/path/url/without/base.
Is there a way or option so that the specific route is loaded without the base path so it would look something like: localhost:8080/url/without/base
I don't think there's a direct option for this but it sounds like something you will need to use window.location rather than next() to redirect to since that route will fall outside of your router's routes definition.
You could probably accomplish this by stripping the base url from router.START_LOCATION (Vue Router v3.5.0+), something like this:
beforeEnter(routeTo, routeFrom, next) {
if (//SOME CONDITION THAT SHOULD MATCH) {
const url = `${router.START_LOCATION.replace(router.base, '')}/url/without/base`;
window.location.replace(url);
return;
}
next();
}

Navigation guard for dynamic route

I have navigation guards to prevent visitors from viewing protected pages without being logged in. One of the pages I want them to see without login is a dynamic route e.g. example.com/dynamic_part. Below is my vuejs code:
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem("local_token");
let whitelist = [
"/",
"/register",
"/login",
"/dynamic_part/",
];
below works but it doesn't allow for the dynamic route "/dynamic_part/"
if (whitelist.includes(to.path)) {
below works for the dynamic route but breaks other route guards i.e. can't move to Products after logging in. I get this error: Error: Redirected when going from "/login" to "/Products" via a navigation guard.
whitelist.some(item => console.log(to.path.includes(item), item))
if (whitelist.some(item => to.path.includes(item))) {
The rest of the navigation guard:
if (token) {
next({
name: "Products",
});
} else {
next();
}
} else {
if (token) {
next();
} else {
next({
name: "Login",
});
}
}
});
What am I doing wrong and how can get all urls to work?
The problem here is all routes will match to.path.includes("/").
You need to separate the routes you want to match fully, with the ones you match with contains (you might want startsWith()?).
const whitelist = [
"/",
"/register",
"/login",
];
const dynamicWhitelist = [
"/dynamic_part/",
];
if (whitelist.includes(to.path) || dynamicWhitelist.some(item => to.path.includes(item))) {
/// etc
}
The more 'Vue-router-like' way of doing this is defining a meta object in your routes and testing against those.
//routes:
const routes = [
{
path: '/login',
component: Login,
meta: { allowAnon: true }
}
...
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem("local_token");
if(to.meta.allowAnon) {
//etc
See the docs here for more details.

Vue router, redirect when the url isnt a match to the correct url

Probaly a simple solution but I cant seem to find it. I have a couple of routes and a redirect. The redirect should prevent to use of a wrong language prefix(slug or how its called) and redirect it to the correct set value. I dont want to use a long list of values that I cant use but use a wildcard for it
{
path: `/:locale(de|nl|es|it|...and many more)?`,
redirect: `/en`
},
// the idea
{
path: `/:locale(!en)?`,//not working
redirect: `/en`
},
{
path: `/:locale/:slug`,
beforeEnter: (to, from, next) => {
var slug = to.path.split("/");
//to get last value on route
let lang = slug[slug.length - 1];
if(lang !== "en"){
next({ path: '/en'})
}
},

Vue router allways redirecting to error page

i am trying to setup a redirect when the user is not logged in. But when i do it like in my example the URL changes but i get This page could not be found from nuxt. The code is inside an login.js inside the plugins folder. Then i included this in the nuxt config like this.
plugins: [
'~/plugins/login.js'
],
And here is the actual code for handling redirecting
export default ({ app, store }) => {
app.router.beforeEach((to, from, next) => {
const loggedIn = store.state.account.loggedInAccount
if (!loggedIn) {
if (to.path !== '/redirect') {
next({ path: '/redirect' })
} else {
next()
}
} else {
next()
}
})
}
It looks like the routes are not mounted yet.
You should try to use middleware. It is the conventional way to implement the beforeEach function as mentioned by the official docs. You can read about it from here. If have access to the route object, store object and redirect function inside the middleware, so use redirect to direct to the other routes after validation.