Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subdomain routing #22

Open
Aperrix opened this issue Nov 3, 2018 · 13 comments
Open

Subdomain routing #22

Aperrix opened this issue Nov 3, 2018 · 13 comments

Comments

@Aperrix
Copy link

Aperrix commented Nov 3, 2018

What problem does this feature solve?

By default, nuxt creates the URL from the pages folder, for example :

/pages/blog/index.vue = mywebsite.com/blog
/pages/blog/posts/index.vue = mywebsite.com/blog/posts

It's super convenient, our URLs match our file architecture, no problems.

However, there is no option to change the behavior of the router to allow the folder to point to a subdomain of the same name, for example :

/pages/blog/index.vue = blog.mywebsite.com
/pages/blog/posts/index.vue = blog.mywebsite.com/posts

I know it's impossible to change the client-side URL without reloading the page and so vue-router can not do it, however nuxt-i18n allows you to use a subdomain to change language and after my research, using NuxtServerInit it would be possible to create an alternative, something that I started to do but I have not managed to go all the way. 😔

My try :
https://gitlab.com/ankaworld/ankaworld.net/merge_requests/1/diffs

My research :
https://cmty.app/nuxt/nuxt.js/issues/c2076
nuxt/nuxt#2378
vuejs/vue-router#1611

This is my first issue on Nuxt community, it will surely lack explanations, details or other so do not hesitate to propose changes but do not be aggressive 😄

This feature request is available on Nuxt community (#c28)
@ghost ghost added the cmty:feature-request label Nov 3, 2018
@mostafa6765
Copy link

mostafa6765 commented Jul 25, 2019

@Aperrix can you share your code for subdomain nuxt router.

@ricardogobbosouza ricardogobbosouza self-assigned this Jul 25, 2019
@mozafarfahimi
Copy link

The problem is your going to solve the problem only with nuxt but what you actually need is reverse proxy.
you can use nginx for instead.

@verstoff
Copy link

Here is solution using @nuxt/router-module

All pages from folder 'pages/root-domain' will be available on 'example.com'
All pages from folder 'pages/sub-domain' will be available on 'foo-bar.example.com'
All other pages will be available on any domain

// router.js

import Router from 'vue-router'

export function createRouter(ssrContext, createDefaultRouter, routerOptions) {
  const options = routerOptions || createDefaultRouter(ssrContext).options

  let routesDirectory = null

  if (process.server && ssrContext && ssrContext.nuxt && ssrContext.req) {
    const req = ssrContext.req

    const domainLevel = (req.headers.host.match(/\./g) || []).length + 1

    // Get routes directory by hostname

    routesDirectory = domainLevel > 2 ? 'sub-domain' : 'root-domain'
    // Save to the object that will be sent to the client as inline-script
    ssrContext.nuxt.routesDirectory = routesDirectory
  }
  if (process.client) {
    // Get what we saved on SSR
    if (window.__NUXT__ && window.__NUXT__.routesDirectory) {
      routesDirectory = window.__NUXT__.routesDirectory
    }
  }

  function isUnderDirectory(route, directory) {
    const path = route.path
    return path === '/' + directory || path.startsWith('/' + directory + '/')
  }

  let newRoutes = options.routes
  if (routesDirectory) {
    newRoutes = options.routes
      .filter((route) => {
        // remove routes from other directories
        const toRemove =
          routesDirectory === 'sub-domain'
            ? 'root-domain'
            : 'sub-domain'
        return !isUnderDirectory(route, toRemove)
      })
      .map((route) => {
        // remove directory from path and name
        if (isUnderDirectory(route, routesDirectory)) {
          return {
            ...route,
            path: route.path.substr(routesDirectory.length + 1) || '/',
            name: route.name.substr(routesDirectory.length + 1) || 'index'
          }
        }
        return route
      })
  }

  return new Router({
    ...options,
    routes: newRoutes
  })
}

@tomasSlouka
Copy link

@verstoff is there a way how to make your code work in a more general way.
Let's say I have more domains/subdomains that I want to map on more directories.

page/admin.domain.com > will show only on the subdomain admin.domain.com
page/example.com > will show only on the domain example.com
page/anotherpage.com > will show only on the domain anotherpage.com
page/subdomain.example.com > will show only on the subdomain.example.com

Therefore I can have multiple domains and subdomains pointing on one server with one NuxtJS SSR app.

THX

@verstoff
Copy link

@tomasSlouka

There is two steps:

  1. Determine which directory is needed for the requested domain:
    routesDirectory = req.headers.host suitable for your purposes
  2. Exclude all routes that matches your domain directories (except current one - routesDirectory) from routes list.
    For your purposes change filter method to something like this:
const domains = [
   'admin.domain.com', 'example.com', 'anotherpage.com', 'subdomain.example.com'
]

...

newRoutes = options.routes.filter((route) => {
        if (isUnderDirectory(route, routesDirectory)) {
            return true
        }
        // remove routes from other domain-directories
        return !domains.find(domain => isUnderDirectory(route, domain))
})

Or you can define some prefix for domain-directories and filter routes by it.

@madhusudanbabar
Copy link

madhusudanbabar commented Sep 8, 2020

Hello @Aperrix, I've created a module to handle multiple subdomains with nuxtJS, inspired by the answer of @verstoff but not limiting to just one subdomain, you can add any number of domains, please have a look k-domains thanks @verstoff for the initial idea.

@bodgerbarnett
Copy link

This is almost exactly what I need for my app. I'm trying to replicate the method that heroku uses on their site.

I am aiming for 2 domains - example.com and dashboard.example.com. The idea is that if an unauthenticated user goes to dashboard.example.com, they are forced to log in first - at example.com/login - then they're redirected to the main dashboard page.

Using @verstoff's solution above, I managed to get the subdomains working (although I needed to change the domainLevel check to 1, from 2) but if I put the auth middleware on the dashboard page (to force a user login), I can't get it to go to example.com/login.

  • If I put the login.vue page in the root of the pages directory (pages/login.vue), the login page is dashboard.example.com/login. It looks like Heroku does this - then that page redirects to the real login page (which is at id.heroku.com) but how would I go about that, without just hardcoding the url?
  • If I put the login.vue page in the root-domain (pages/root-domain/login.vue), the login page is not found.

Any ideas?

@verstoff
Copy link

verstoff commented Feb 9, 2021

@bodgerbarnett

In order not to hardcode, you can get domain of the authorization page using string methods or regular expressions, knowing the required domain level and req.headers.host. Then save the result somewhere, for example, in ssrContext.nuxt.loginURL (like in my answer), which is available both on the server and on the client (via a global variable). In the middleware, get this variable and redirect to the resulting domain instead of current.

Make sure the redirection happens through 30x code on the server and through location on the client (with a page reload and not just a Vue Router route change).

@bodgerbarnett
Copy link

Thanks @verstoff - I used the runtime config to do that in the end, so thanks for that!

@cosbgn
Copy link

cosbgn commented Aug 17, 2021

Hello, I've followed this guide and all seem to work perfectly on localhost. However when I deploy using nuxt-generate it seems that it shows always the root-domain and never the sub-domain this is probably because they get generated without subdomain and then this never runs again. Is there a way to have something like this which works with nuxt generate? Any workaround I can try?

@amirakbulut
Copy link

Here is solution using @nuxt/router-module

All pages from folder 'pages/root-domain' will be available on 'example.com'
All pages from folder 'pages/sub-domain' will be available on 'foo-bar.example.com'
All other pages will be available on any domain

// router.js

import Router from 'vue-router'

export function createRouter(ssrContext, createDefaultRouter, routerOptions) {
  const options = routerOptions || createDefaultRouter(ssrContext).options

  let routesDirectory = null

  if (process.server && ssrContext && ssrContext.nuxt && ssrContext.req) {
    const req = ssrContext.req

    const domainLevel = (req.headers.host.match(/\./g) || []).length + 1

    // Get routes directory by hostname

    routesDirectory = domainLevel > 2 ? 'sub-domain' : 'root-domain'
    // Save to the object that will be sent to the client as inline-script
    ssrContext.nuxt.routesDirectory = routesDirectory
  }
  if (process.client) {
    // Get what we saved on SSR
    if (window.__NUXT__ && window.__NUXT__.routesDirectory) {
      routesDirectory = window.__NUXT__.routesDirectory
    }
  }

  function isUnderDirectory(route, directory) {
    const path = route.path
    return path === '/' + directory || path.startsWith('/' + directory + '/')
  }

  let newRoutes = options.routes
  if (routesDirectory) {
    newRoutes = options.routes
      .filter((route) => {
        // remove routes from other directories
        const toRemove =
          routesDirectory === 'sub-domain'
            ? 'root-domain'
            : 'sub-domain'
        return !isUnderDirectory(route, toRemove)
      })
      .map((route) => {
        // remove directory from path and name
        if (isUnderDirectory(route, routesDirectory)) {
          return {
            ...route,
            path: route.path.substr(routesDirectory.length + 1) || '/',
            name: route.name.substr(routesDirectory.length + 1) || 'index'
          }
        }
        return route
      })
  }

  return new Router({
    ...options,
    routes: newRoutes
  })
}

I added this in my Nuxt directory and it doesn't do anything for me.

Is there anything else needed for this to work?

@binumathew
Copy link

createDefaultRouter

I am getting createDefaultRouter is not a function error, any idea why? is this code need to be updated?

@naczu
Copy link

naczu commented Jan 30, 2022

Here is solution using @nuxt/router-module

All pages from folder 'pages/root-domain' will be available on 'example.com' All pages from folder 'pages/sub-domain' will be available on 'foo-bar.example.com' All other pages will be available on any domain

// router.js

import Router from 'vue-router'

export function createRouter(ssrContext, createDefaultRouter, routerOptions) {
  const options = routerOptions || createDefaultRouter(ssrContext).options

  let routesDirectory = null

  if (process.server && ssrContext && ssrContext.nuxt && ssrContext.req) {
    const req = ssrContext.req

    const domainLevel = (req.headers.host.match(/\./g) || []).length + 1

    // Get routes directory by hostname

    routesDirectory = domainLevel > 2 ? 'sub-domain' : 'root-domain'
    // Save to the object that will be sent to the client as inline-script
    ssrContext.nuxt.routesDirectory = routesDirectory
  }
  if (process.client) {
    // Get what we saved on SSR
    if (window.__NUXT__ && window.__NUXT__.routesDirectory) {
      routesDirectory = window.__NUXT__.routesDirectory
    }
  }

  function isUnderDirectory(route, directory) {
    const path = route.path
    return path === '/' + directory || path.startsWith('/' + directory + '/')
  }

  let newRoutes = options.routes
  if (routesDirectory) {
    newRoutes = options.routes
      .filter((route) => {
        // remove routes from other directories
        const toRemove =
          routesDirectory === 'sub-domain'
            ? 'root-domain'
            : 'sub-domain'
        return !isUnderDirectory(route, toRemove)
      })
      .map((route) => {
        // remove directory from path and name
        if (isUnderDirectory(route, routesDirectory)) {
          return {
            ...route,
            path: route.path.substr(routesDirectory.length + 1) || '/',
            name: route.name.substr(routesDirectory.length + 1) || 'index'
          }
        }
        return route
      })
  }

  return new Router({
    ...options,
    routes: newRoutes
  })
}

this is not working when using nuxt-i18n module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests