Приветствую разработчиков. Мы закончили основные аспекты нашей серверной архитектуры, теперь пора приступить к созданию внешнего интерфейса нашего приложения. Мы будем использовать Nuxt.js для создания наших форм входа и регистрации, чтобы позволить пользователям входить в систему и создавать учетные записи.

Оглавление

Первое, что нам нужно сделать, это убедиться, что в нашем API включен CORS. Для этого откройте backend/config/custom.js и в свойстве cors добавьте следующее:

allRoutes: true, // Apply CORS to every route in the api by default.
allowOrigins: 'http://localhost:3000', // Allow requests from nuxt
allowCredentials: true, // Allow cookie sharing within CORS requests

Следующее, что я сделал, - это создал inexistantUser настраиваемый ответ, чтобы, если пользователь не найден, мы могли отловить ошибку и вывести ошибки в наши формы. Для этого создайте файл backend/api/reposnes/inexistantUser.js и добавьте следующее:

module.exports = function inexistentUser(message) {

  const res = this.res;

  // Set result message
  let result = {
    status: 401,
    // Set custom message if it is passed into the response
    message: message ? message : 
    'The details provided does not match any user registered.'
  };

  // Send JSON as that's what we want!
  return res.status(result.status).json(result);

};

Теперь в рамках нашего действия проверки и входа в систему мы можем использовать настраиваемый ответ, чтобы выбросить inexistantUser в качестве выхода:

exits: {
  ... other exits
  inexistentUser: {
    description: 
    'The details provided does not match any user registered.',
    responseType: 'inexistentUser',
  },
},
fn: async function (inputs, exits) {
  .... other code

  // if the user cannot be found throw a bad combo
  if(!user)
    throw 'inexistentUser';
  
  ...... rest of code
}

Теперь мы можем запустить sails lift, чтобы запустить наш сервер, при включенном сервере нам нужно будет настроить хранилище приложений. Хранилище - это контейнер, в котором будет храниться состояние нашего приложения. Nuxt.js реализует Vuex в своем ядре, что позволяет нам добавлять наши модули satte в папку store для чтения Nuxt и автоматической загрузки.

Во-первых, нам нужно установить базовый URL-адрес для Axios, чтобы открыть frontend/nuxt.config.js и добавить baseURL: ‘http://localhost:1337' к параметру axios.

Чтобы иметь возможность использовать модули в нашем магазине, нам нужно добавить файл frontend/store/index.js. Вместо создания экземпляра хранилища требуется экспортировать состояние как функцию, а мутации и действия как объекты.

// Store state
export const state = () => ({});
// Store Actions
export const actions = {};
// Store Mutations
export const mutations = {};

Затем мы создадим для этого модуль учетной записи, создадим файл frontend/store/account.js и добавим следующее:

// Account state
export const state = () => ({
  user: null,
});

// Account actions
export const actions = {
  USER_LOGIN({commit}, data) {
    // Send request to log in the user
    return this.$axios.put('api/v1/user/login', data)
      .then(res => {
        // Mutate the user state with logged in user
        commit('SET_USER', res.data);
        return res;
      });
  },

  USER_REGISTER({commit}, data) {
    // Send a request to register the user
    return this.$axios.post('api/v1/user/create', data)
      .then(res => {
        // Mutate the user state with newly registered  user
        commit('SET_USER', res.data);
        return res;
      });
  }
};

// Account mutations
export const mutations = {
  SET_USER(state, user) {
    // Mutate the user state
    state.user = user.id ? user : null;
  }
};

Теперь, когда мы настроили наш магазин, мы можем создать нашу страницу входа в систему. С помощью Nuxt. js, мы можем автоматически сгенерировать конфигурацию vue-router из файлового дерева файлов Vue в каталоге pages, например из следующего дерева файлов:

Автоматически сгенерирует следующую конфигурацию.

Итак, чтобы сгенерировать маршрут входа в систему, нам нужно создать файл frontend/pages/login.vue и rontend/pages/account.vue. В login.vue нам нужно создать шаблон для форм, а также параметры данных для входной привязки и методы для отправки форм. Я также добавил обработку ошибок, чтобы мы могли отображать ошибки в форме. Для account.vue page добавьте все, что хотите. В login.vue добавьте следующее:

<template>
  <section class="container">
    <form id="login" @submit.prevent="submit('login')">
      <div v-if="errors.login">
        <strong>Danger!</strong> {{ errors.login }}
      </div>

      <label for="login__email">Email Address</label>

      <input id="login__email"
             v-model="login.emailAddress"
             type="email"
             required>

      <label for="login__password">Password</label>

      <input id="login__password"
             v-model="login.password"
             type="password"
             required>

      <button type="submit">Submit</button>
    </form>

    <form id="register" @submit.prevent="submit('register')">
      <div v-if="errors.register">
        <strong>Danger!</strong> {{ errors.register }}
      </div>

        <label for="register__email">Email Address</label>

        <input id="register__email"
          v-model="register.emailAddress"
          type="email"
          required>

        <label for="register__full-name">Full Name</label>

        <input id="register__full-name"
          v-model="register.fullName"
          type="text"
          class="form-control"
          required>

        <label for="register__password">Password</label>

        <input id="register__password"
          v-model="register.password"
          type="password"
          class="form-control"
          required>

        <button type="submit" >Submit</button>
    </form>
  </section>
</template>

<script>

  export default {
    name: "Login",
    data() {
      return {
        // Set login form data
        login: {
          emailAddress: '',
          password: '',
        },
        // Set register form data
        register: {
          emailAddress: '',
          fullName: '',
          password: '',
        },
        // Set form errors
        errors: {
          login: '',
          register: ''
        }
      }
    },
    methods: {
      submit(form) {
        const _this = this;
        _this.$store
          .dispatch('account/USER_' + form.toUpperCase()
          , this[form])
          .then(res => {
            if (res.status === 200)
              _this.$router.push('/account');
          })
          .catch(error =>
            _this.errors[form] = error.response.data.message
          );
      }
    }
  }
</script>

Теперь будут созданы представления для формы входа. Если вы сейчас перейдете к http: // localhost: 3000 / login, мы сможем использовать форму для входа и создания пользователя. Затем пользователь будет перенаправлен на созданный вами шаблон учетной записи, хотя, если вы обновите страницу, пользователь не будет установлен в вашем магазине.

Нам необходимо использовать файлы cookie сеанса, которые установлены в API, для этого мы должны использовать действие API user.check для отправки пользовательских данных с сервера на сторону клиента, для этого нам нужно использовать nuxtServerInit в нашем магазине. Сначала нам нужно добавить GET_USER действие к нашему хранилищу учетных записей, в frontend/store/account.js добавьте следующее:

GET_USER({commit}) {
  // Send request to check user against cookie
  return this.$axios.get('api/v1/user/check')
    .then(res => {
      // Mutate the user state with logged in user
      commit('SET_USER', res.data);
      return res;
    })
    .catch(error => {
      // Mutate the user state to null
      commit('SET_USER');
      return error;
    })
},

Затем нам нужно добавить действие nuxtServerInit, открыть файл frontend/store/index.js и добавить замену actions следующим:

// Store Actions
export const actions = {
  nuxtServerInit({dispatch}, {req}) {
    return new Promise((resolve, reject) => {
      // Reset Axios default headers
      this.$axios.defaults.headers.common = {};
      // Match Axios default headers with request headers
      Object.keys(req.headers).map((key) => {
        this.$axios.defaults.headers.common[key] = req.headers[key]
      });
      // dispatch GET_USER
      dispatch('account/GET_USER')
        .then(res => {
          resolve(true)
        })
        .catch(error => {
          console.log('Provided token is invalid:', error);
          resolve(false)
        });
    });
  },
};

Это будет сбрасывать глухие заголовки Axios с заголовками в запросе, чтобы мы могли использовать серверную сторону файлов cookie на стороне клиента. Без этого файлы cookie не будут отправляться на сервер, поэтому он всегда будет возвращать, что пользователь не найден.

Наша единственная проблема сейчас заключается в том, что гостевой пользователь по-прежнему сможет перейти на страницу учетной записи, даже если он не вошел в систему. Чтобы предотвратить это, нам нужно создать isAuthenticated Getter для нашей учетной записи как а также Промежуточное ПО, чтобы проверить, есть ли в хранилище приложений, аутентифицирован ли пользователь. Для этого в frontend/store/account.js добавьте следующее:

// Account getters
export const getters = {
  isAuthenticated(state) {
    return !!state.user
  },
  loggedUser(state) {
    return state.user
  }
};

И вам нужно будет создать файл backend/middleware/authenticated.js и добавить следующее:

export default function ({store, redirect, route}) {
  // Get isAuthenticated getter from account store
  const isAuthenticated = store.getters['account/isAuthenticated'];
  // Test route to see if is login page or admin page.
  const isAdminUrl = 
        /^\/admin|\/account(\/|$)/.test(route.fullPath);
  const isLoginUrl = /^\/login(\/|$)/.test(route.fullPath);

  // If user is authenticated and is login url
  if (isAuthenticated && isLoginUrl)
    // Redirect to account page
    return redirect('/account');

  // If user is not authenticated and is an admin url
  else if (!isAuthenticated && isAdminUrl)
    // Redirect to login page
    return redirect('/login');

  // Resolve promise
  return Promise.resolve()
}

Затем нам необходимо добавить конфигурацию маршрутизатора в frontend/nuxt.config.js, это выглядит следующим образом:

router: {
  middleware: 'authenticate'
},

Теперь, если бы мы открыли страницу входа в систему как зарегистрированный пользователь, она перенаправила бы на страницу учетной записи, а если бы гость пытался получить доступ к URL-адресу под учетной записью или администратором, он бы перенаправил на страницу входа.

Заключение

Теперь у нас есть возможности для входа пользователя на сайт, проверка того, вошли ли они в систему на стороне сервера и функциональность, которая будет аутентифицировать маршруты и посмотреть, вошел ли пользователь в систему. Затем мы можем начать создавать представления, которые будут возможность создавать обновления и удалять наши модели!

Я бы рекомендовал вытащить серию GitHub Repository, как я комментировал в коде. Вы также можете следить за моими успехами в Instagram и Twitter.