Sharing a single front-end among multiple back-ends

Overview

When building a B2B service using Kuroco, you may want to use a common design with different content from customer to customer. In such cases, you can use a shared front-end and switch between back-ends for each customer.

As part of the package, Kuroco's demo site is a simple membership site with a common front-end.

Site URL: https://dev-nuxt-auth.g.kuroco-front.app/
GitHub repository: https://github.com/diverta/front_nuxt_auth

This tutorial explains how to build a B2B application (your main site) and copy it to create new client-specific back-ends (sub-sites).

Before you start

To begin this tutorial, you should have an existing project built with Kuroco and Nuxt.js. If you have not done so, refer to the tutorials below for step-by-step guides.

Main site setup

For this tutorial, we are using the following site keys:

  • Main site (master): sample-b2b-service-en
  • Sub-site: sample-b2b-service-001

Adding contents

First, create a new content structure.

In the left sidebar menu, select [Content structure].

Image from Gyazo

In the upper right corner, click [Add].

Image from Gyazo

Create a new content structure called "Subscribers-only" with two additional fields.

Image from Gyazo Image from Gyazo

Then, go to the Content editor by selecting [Contents] -> [Subscribers-only] in the sidebar menu.

Image from Gyazo

On the content list screen, click [Add]

Image from Gyazo

Create your post and click [Add] at the bottom of the screen to save it.

Image from Gyazo

Note down the content definition ID (topic group ID) and content ID (topic ID) for later use.

Content structure:

Image from Gyazo

Content:

Image from Gyazo

Endpoint setup

Next, in the sidebar menu, select [API] -> [Default].

Image from Gyazo

In the upper right corner of the endpoint list screen, click [Add].

Image from Gyazo

Enter the title, version, and description in the dialog and click [Add].

Image from Gyazo

You will be redirected to the newly created endpoint list screen.

Image from Gyazo

Click [Security].

Image from Gyazo

In the security dialog, select [Cookie] and click [save].

Image from Gyazo

Next, click [Operation CORS] on the endpoint list screen.

Image from Gyazo

Under "CORS_ALLOW_ORIGINS", click [Add Origin] twice and enter the following values:

  • http://localhost:3000
  • Your front-end domain (in this example, https://sample-b2b-service-en.g.kuroco-front.app)

Under "CORS_ALLOW_ORIGINS", click [Add Method] and enter the following:

  • GET
  • POST
  • OPTIONS

When you are done, click [Save].

Image from Gyazo Image from Gyazo

Now, you are ready to create an endpoint for retrieving content from "Subscribers-only".

On the endpoint list screen, click [Add new endpoint].

Image from Gyazo

Input the following and click [Add] to save the settings:

FieldSub-fieldValue
Path-/rcms-api/3/service_top
Enabled/DisabledEnabled
ModelCategoryContent
ModelTopics
Operationdetails
Basic settingstopics_group_idEnter your content structure ID (7 in this example).

Image from Gyazo Image from Gyazo Image from Gyazo

Front-end setup

Adding the "Subscribers-only" page

After the back-end configuration, we will set up the front-end display for the content we created above.

In your /pages directory, make a new folder named /owners-page and add the following index.vue:

/pages/owners-page/index.vue
<template>
  <div>
    <h1>{{ response.details.ext_1}}</h1>
    <p>{{ response.details.ext_2}}</p>

  <button type="button" @click="logout">
  Logout
  </button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  middleware: 'auth',
  async asyncData({$axios}) {
    try {
      const response = await $axios.$get('/rcms-api/3/service_top/3')
      return { response }
    } catch (e) {
      console.log(e.message)
    }
  },
  methods: {
    ...mapActions(['logout'])
  }
}
</script>

Change the URL hitting the endpoint at login

The next step is to make the URL hitting the endpoint modifiable from the front-end.
To do this, modify /pages/login/index.vue and /store/index.js as shown below.

/pages/login/index.vue
<template>
    <form @submit.prevent="login">
      <p v-if="loginStatus !== null" :style="{ color: resultMessageColor }">
        {{ resultMessage }}
      </p>

        <input v-model="sitekey" name="sitekey" type="sitekey" placeholder="sitekey">
        <input v-model="email" name="email" type="email" placeholder="email">
        <input
            v-model="password"
            name="password"
            type="password"
            placeholder="password"
        >
        <button type="submit">
            Login
        </button>

        <div>
            <nuxt-link to="/news/">
                To News list
            </nuxt-link>
        </div>
        <div>
            <nuxt-link to="/owners-page/">
                To Subscribers page
            </nuxt-link>
        </div>
    </form>
</template>

<script>
export default {
    data () {
        return {
            sitekey: '',
            email: '',
            password: '',
            loginStatus: null,
            resultMessage: null
        }
    },
    computed: {
        resultMessageColor () {
            switch (this.loginStatus) {
            case 'success':
                return 'green'
            case 'failure':
                return 'red'
            default:
                return ''
            }
        }
    },
    methods: {
        async login () {
            try {
                const payload = {
                    sitekey: this.sitekey,
                    loginInfo: {
                        email: this.email,
                        password: this.password
                    }
                }
                await this.$store.dispatch('login', payload)
                this.loginStatus = 'success'
                this.resultMessage = 'Login successful'
            } catch (e) {
                this.loginStatus = 'failure'
                this.resultMessage = 'Login failed'
            }
        }
    }
}
</script>
/store/index.js
export const state = () => ({
    profile: null
})

export const getters = {
    authenticated (state) {
        return state.profile !== null
    },
    hostname () {
        try {
            const sitekey = localStorage.getItem('sitekey');
            if (sitekey === '' || sitekey === 'undefined' || sitekey === 'null') {
                throw new Error('unknown sitekey');
            }
            return `https://${sitekey}.g.kuroco.app`;
        } catch (e) {
            return false;
        }
    }
}

export const mutations = {
    setProfile (state, { profile }) {
        state.profile = profile
    },
}

export const actions = {
    async login ({ commit, getters }, payload) {
        localStorage.setItem('sitekey', payload.sitekey);
        this.$axios.defaults.baseURL = getters.hostname;

        await this.$axios.$post('/rcms-api/1/login', payload.loginInfo)
        const profileRes = await this.$axios.$get('/rcms-api/1/profile', { withCredentials: true })
        commit('setProfile', { profile: profileRes.data })        
    },

    async logout ({ commit }) {
        try {
            await this.$axios.$post('/rcms-api/1/logout')
        } catch {
             /** No Process */
             /** In cases of error, ignore this as user is assumed to be logged out. */
            }
            commit('setProfile', { profile: null })

            this.$router.push('/login')
        },

    async restoreLoginState ({ commit, dispatch, getters }) {
        if (!getters.hostname) {
            await dispatch('logout')
            throw new Error('need to login')
        }
        try {
            this.$axios.defaults.baseURL = getters.hostname;
            const profileRes = await this.$axios.$get('/rcms-api/1/profile', { withCredentials: true })
            commit('setProfile', { profile: profileRes.data });
        } catch {
            await dispatch('logout')
            throw new Error('need to login')
        }
    }
}

Make sure to substitute /rcms-api/1/login, /rcms-api/1/profile, and /rcms-api/1/logout in the above code with the corresponding endpoints on your own admin panel.

Create a back-end for users

Adding a sub-site

Next, create a sub-site for subscribers from the admin panel.

In the left sidebar menu, select [Environment] -> [Site list].

Image from Gyazo

On the site list screen, click [Add].

Image from Gyazo

Input the following information:

FieldValue
Source site namesample-b2b-service-en(sample-b2b-service-en)
Site nameDiverta Inc. website
Site keysample-b2b-service-001-en
URLEnable "Front-end sharing" and select [https://sample-b2b-service-en.g.kuroco-front.app].
E-mailEnter the e-mail address for receiving the sign-up notification.
Initial passwordEnter your desired password.
Company nameDiverta Inc.
NameDiverta Taro

When you are done, click [Add] at the bottom of the screen to save the changes.

Image from Gyazo Image from Gyazo

Sub-site content modification

When the sub-site has been successfully created, you will receive en e-mail notification. The sub-site will also appear on the site list screen. Click [Admin panel] to access it.

Image from Gyazo

You will be redirected to the login page.

Image from Gyazo

After logging in, go to [Content] -> [Subscribers-only] in the sidebar menu.

Image from Gyazo

On the content list screen, click [Subscribers main page]

Image from Gyazo

Make the desired changes to the post and click [Update] to save them.

Image from Gyazo

CORS verification

By enabling front-end sharing when creating the sub-site, your front-end URL has been automatically be added to your account settings and the API CORS settings.

To verify this in the CORS settings, select [API] -> [B2B service] in the sidebar menu.

Image from Gyazo

On the endpoint list screen, click [Operation CORS].

Image from Gyazo

We have confirmed that the front-end URL selected in front-end sharing has been added to CORS_ALLOW_ORIGINS.

Make sure the shared front-end has been added under "CORS_ALLOW_ORIGINS".

Image from Gyazo

Verify the front-end display

Serve the project in your terminal and go to http://localhost:3000/owners-page in your browser. You will be redirected to the login page. Log in with the main site key and click the [To Subscribers page] link.

Image from Gyazo

Click [Logout] and refresh the screen. Then, log in with the sub-site key and access the Subscribers page.

Image from Gyazo

This concludes the function to switch back-ends for each customer while sharing the same front-end.
By implementing functions such as e-mailing, content posting, and commenting on the customer-specific pages created with this function, you can build B2B services for your customers using Kuroco.

In this tutorial, the content was modified in the admin panel. Alternatively, you can also enable users to add and update content themselves using the API. You can even add the site itself via API.

Note: Usage fees for sub-sites are billed together with the main site.

If you have any other questions, please use our contact form or Slack workspace.