Веб-приложение на Node и Vue, часть 1: структура проекта, API, аутентификация / Хабр

Babel config / run commands file

Path: /.babelrc

The babel config file defines the presets used by babel to transpile the ES6 code. The babel transpiler is run by webpack via the babel-loader module configured in the webpack.config.js file below.

{
  "presets": [
      "env",
      "stage-0"
  ]
}

Deploying the vue.js app to aws

This video shows how to setup a production ready web server from scratch on AWS, then deploy the example Vue.js Vuex app and configure it to run with a real Node.js MongoDB backend api. The tutorial used in the video is available at Vue.js Node.js on AWS – How to Deploy a MEVN Stack App to Amazon EC2.

Deploying the vue.js app to microsoft azure

For instructions on how to deploy the Vue.js app to Azure with a real backend api built with ASP.NET Core and SQL Server see Vue.js ASP.NET Core SQL on Azure – How to Deploy a Full Stack App to Microsoft Azure.

Npm package.json

Path: /package.json

The package.json file contains project configuration information including package dependencies which get installed when you run npm install. Full documentation is available on the npm docs website.

Pdo. подключаемся к mysql и пишем вспомогательные функции

Может, обратили внимание на строчку use PDO; в самом начале helpers.php? Она разрешит нам использовать в пространстве имен Helpers возможности pdo. Первой функцией будет подключение к базе.

Vue vuex helpers folder

Path: /src/_helpers

The helpers folder contains all the bits and pieces that don’t fit into other folders but don’t justify having a folder of their own.

Vue app component

Path: /src/app/App.vue

The app component is the root component for the vue tutorial application, it contains the outer html, routes and global alert notification for the tutorial app.

Vue app entrypoint

Path: /src/index.js

The root index.js file bootstraps the vue vuex tutorial application by rendering the App component into the #app div element defined in the main index html file above.

The tutorial app uses a fake / mock backend that stores data in browser local storage, to switch to a real backend api simply remove the fake backend code below the comment // setup fake backend.

The VeeValidate library is used for form validation on the login and registration pages.

import Vue from 'vue';
import VeeValidate from 'vee-validate';

import { store } from './_store';
import { router } from './_helpers';
import App from './app/App';

Vue.use(VeeValidate);

// setup fake backend
import { configureFakeBackend } from './_helpers';
configureFakeBackend();

new Vue({
    el: '#app',
    router,
    store,
    render: h => h(App)
});

Vue app feature folder

Path: /src/app

The app folder is for vue components and other code that is used only by the app component in the tutorial application.

Vue auth header

Path: /src/_helpers/auth-header.js

Vue helpers index

Path: /src/_helpers/index.js

The helpers index file groups all helper exports together so they can be imported in other parts of the app using only the folder path, and enables importing multiple helpers in a single statement (e.g. import { helper1, helper2, … } from ‘../_helpers’).

export * from './fake-backend';
export * from './router';
export * from './auth-header';

Vue home feature folder

Path: /src/home

The home folder is for vue components and other code that is used only by the home page component in the tutorial application.

Vue home page component

Path: /src/home/HomePage.vue

Vue login feature folder

Path: /src/login

The login folder is for vue components and other code that is used only by the login page component in the tutorial application.

Vue login page component

Path: /src/login/LoginPage.vue

Vue main index html

Path: /src/index.html

The main index html file contains the outer html for the whole tutorial application. When the app is started with npm start, Webpack bundles up all of the vue vuex code into a single javascript file and injects it into the body of the page.

Vue register feature folder

Path: /src/register

The register folder is for vue components and other code that is used only by the register page component in the tutorial application.

Vue register page component

Path: /src/register/RegisterPage.vue

Vue router

Path: /src/_helpers/router.js

Vue services folder

Path: /src/_services

Vue services index

Path: /src/_services/index.js

The services index file groups all service exports together so they can be imported in other parts of the app using only the folder path, and enables importing multiple services in a single statement (e.g. import { service1, service2, … } from ‘../_services’).

Vue webpack config

Path: /webpack.config.js

Webpack is used to compile and bundle all the project files so they’re ready to be loaded into a browser, it does this with the help of loaders and plugins that are configured in the webpack.config.js file. For more info about webpack check out the webpack docs.

The webpack config file also defines a global config object for the application using the externals property, you can use this to define different config variables for your development and production environments.

Vue.js vuex tutorial project structure

All source code for the Vue.js Vuex tutorial app is located in the /src folder. Inside the src folder there is a folder per feature (app, home, login, register) and a few folders for non-feature code that can be shared across different parts of the app (_helpers, _services, _store).

I prefixed non-feature folders with an underscore “_” to group them together and make it easy to distinguish between features and non-features, it also keeps the project folder structure shallow so it’s quick to see everything at a glance from the top level and to navigate around the project.

Click any of the below links to jump down to a description of each file along with it’s code:

Vuex account module

Path: /src/_store/account.module.js

Vuex alert module

Path: /src/_store/alert.module.js

The vuex alert module is in charge of the alert section of the centralised state store, it contains actions and mutations for setting a success or error alert message, and for clearing the alert.

In this module each alert action just commits a single mutation so it would be possible to commit the mutations directly from your vue components and get rid of the actions. However I prefer to dispatch actions from everywhere for consistency rather than dispatching actions for some things and committing mutations for others.

const state = {
    type: null,
    message: null
};

const actions = {
    success({ commit }, message) {
        commit('success', message);
    },
    error({ commit }, message) {
        commit('error', message);
    },
    clear({ commit }) {
        commit('clear');
    }
};

const mutations = {
    success(state, message) {
        state.type = 'alert-success';
        state.message = message;
    },
    error(state, message) {
        state.type = 'alert-danger';
        state.message = message;
    },
    clear(state) {
        state.type = null;
        state.message = null;
    }
};

export const alert = {
    namespaced: true,
    state,
    actions,
    mutations
};

Vuex store

Path: /src/_store/index.js

This is the main vuex store file that configures the store with all of the above vuex modules.

Vuex store folder

Path: /src/_store

Испытания

Проверим то, что мы создали, предварительно запустив сервер приложения и сервер базы данных. А именно, если перейти по адресу

Маршруты api, файл budgetmanagerapi/app/routes/auth.js

Займёмся созданием маршрутов API. Для этого перейдём в папку

services/BudgetManagerAPI/app

и создадим в ней директорию

routes

, в которой создадим файл

auth.js

со следующим содержимым:

Настройка package.json


Перейдём в корневую директорию проекта, откроем

package.json

и добавим в него, сразу перед блоком

dependencies

, следующее:

Настройка сервера, файл services/index.js


После того, как мы справились с некоторыми из вспомогательных подсистем, займёмся настройкой сервера. Перейдите в папку

services

и откройте уже имеющийся в ней файл

index.js

. Добавьте в него следующее:

Общие настройки проекта settings.py

config = Config(".env")
DEBUG = config("DEBUG", cast=bool, default=False)
DATABASE_URL = config("DATABASE_URL", cast=str)
SECRET_KEY = config("SECRET_KEY", cast=Secret)
ALLOWED_HOSTS = config("ALLOWED_HOSTS", cast=CommaSeparatedStrings)
JWT_PREFIX = config("JWT_PREFIX", cast=str)
JWT_ALGORITHM = config("JWT_ALGORITHM", cast=str)

Для удобства использования вынесем переменные из файла .env в отдельный файл настроек.

Промежуточный слой для работы с jwt middleware.py


Поскольку Starlette еще достаточно молодой фреймворк, удобной «батарейки» JWT к нему еще не написано. Исправим этот недочет.

Работа с брендами. роутер brands

С брендами мы работаем абсолютно так же, как и с категориями. Те же операции, те же параметры, та же структура таблицы. Только название таблицы другое (brands) и поле названия бренда (brand). Я даже код копипастить не буду, возьмите в исходниках.

Работа с категориями. роутер categories

Сначала создадим папку routers, а в ней файл categories.php. Помните, в index.php мы запускали функцию route? Еще упоминали, что она все разрулит. Давайте посмотрим, каким образом

Работа с товарами. роутер products

Код в products.php очень похож на предыдущие, но с тремя отличиями:

1. Добавляется запрос GET /products/{id} – вытаскиваем данные по одному товару

2. Запрос GET /products/ поддерживает пагинацию и фильтрацию по id категории. Это достигается передачей необязательных get-параметров categoryId, offset и limit

3. При добавлении и редактировании товаров в теле запроса передается больше параметров. Не один title, а такой набор: title, categoryId, brandId, price и rating.
Никакой разницы нет, просто приходится делать больше проверок и sql-запросы будут длиннее.


Давайте я выложу всю портянку, а потом рассмотрим эти 3 пункта подробнее.

Ссылки

Полный код на Github:

Бекенд на Starlette

Фронтенд на Vue.js

Пример работы

Спасибо за внимание Удачных интеграций.

Структура проекта и установка зависимостей


Для начала создадим структуру папок для проекта, которая, в самом начале работы, должна выглядеть следующим образом:

Веб-приложение на Node и Vue, часть 1: структура проекта, API, аутентификация / Хабр
Структура папок API

В ходе продвижения по материалу мы значительно расширим эту структуру.

Теперь нужно установить несколько зависимостей. Для этого перейдём в корневую папку проекта (здесь это focus-budget-manager) и, предварительно сформировав package.json командой npm init, выполним следующую команду:

npm i --save express body-parser mongoose consign cors bcrypt jsonwebtoken morgan passport passport-jwt module-alias

Рассмотрим некоторые из этих зависимостей и их роль в проекте:

Тестирование приложения с использованием postman


Для начала подключимся к конечной точке

setup

для создания учётной записи администратора. В интерфейсе Postman это будет выглядеть так:

Файл budgetmanagerapi/app/api/auth.js

Теперь начинаем создавать некоторые из методов API. Перейдём в папку

BudgetManagerAPI/app

, создадим в ней директорию

api

, а в ней — файл

auth.js

. Запишем в него следующее:

const mongoose = require('mongoose'),
      jwt = require('jsonwebtoken'),
      config = require('@config');


Обратите внимание на то, что, благодаря использованию модуля

module_alias

мы сделали код чище. Иначе пришлось бы писать примерно следующее:

config = require('./../../config);

Теперь, после подключения пакетов, сделаем в том же файле следующее:

Файл budgetmanagerapi/app/setup/index.js

После настройки псевдонимов перейдём в папку

BudgetManagerAPI/app

и создадим новую папку

setup

, а в ней — файл

index.js

. Добавим в него следующее:

Файл budgetmanagerapi/config/app.js


В директории

BudgetManagerAPI/config

создадим файл

app.js

. Для начала подключим зависимости:

const express = require('express'),
      app = express(),
      bodyParser = require('body-parser'),
      mongoose = require('mongoose'),
      morgan = require('morgan'),
      consign = require('consign'),
      cors = require('cors'),
      passport = require('passport'),
      passportConfig = require('./passport')(passport),
      jwt = require('jsonwebtoken'),
      config = require('./index.js'),
      database = require('./database')(mongoose, config);

В строке

passportConfig = require('./passport')(passport)

мы импортируем конфигурационный файл для

passport

, передавая

passport

в качестве аргумента, так как в

passport.js

имеется такая команда:

Благодаря такому подходу мы можем работать с passport внутри файла passport.js без необходимости подключать его.

Далее, в файле app.js, начинаем работу с пакетами и устанавливаем секретный ключ:

app.use(express.static('.'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use(cors());
app.use(passport.initialize());

app.set('budgetsecret', config.secret);

Как вариант, вместо использования пакета

cors

, можно сделать следующее:

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

Файл budgetmanagerapi/config/database.js


В папке

BudgetManagerAPI/config

создадим файл

database.js

, который ответственен за работу с базой данных. Добавим в этот файл следующее:

module.exports = (mongoose, config) => {
  const database = mongoose.connection;
  mongoose.Promise = Promise;
  mongoose.connect(config.database, {
    useMongoClient: true,
    promiseLibrary: global.Promise
  });
  database.on('error', error => console.log(`Connection to BudgetManager database failed: ${error}`));
  database.on('connected', () => console.log('Connected to BudgetManager database'));
  database.on('disconnected', () => console.log('Disconnected from BudgetManager database'));
  process.on('SIGINT', () => {
    database.close(() => {
      console.log('BudgetManager terminated, connection closed');
      process.exit(0);
    })
  });
};

Тут мы сначала переключили

mongoose

на использование стандартного объекта

Promise

. Если этого не сделать, можно столкнуться с предупреждениями, выводимыми в консоль. Затем мы создали стандартное подключение

mongoose

Файл budgetmanagerapi/config/index.js


Создадим в папке

BudgetManagerAPI/config

файл

index.js

и внесём в него следующий код:

module.exports = {
  secret: 'budgetsecret',
  session: { session: false },
  database: 'mongodb://127.0.0.1:27017/budgetmanager'
}


Этот файл содержит параметры подключения к базе данных и секретный ключ, который мы используем для создания JWT-токенов.

Здесь предполагается работа с локальным сервером MongoDB. При этом в строке 127.0.0.1:27017 можно использовать localhost. Если хотите, можете работать с облачной базой данных MongoDB, созданной, например, средствами MLabs.

Файл budgetmanagerapi/config/passport.js

После того, как модель

Файл с переменными .env


Объявим здесь переменные, которые нам в дальнейшем понадобятся для работы:

Running the tutorial example with a real backend api

The boilerplate code uses a fake / mock backend by default that uses browser local storage for managing application data, to switch to a real backend api you just have to remove a couple of lines of code from the main vue entry file /src/index.js below the comment // setup fake backend.

You can build your own backend api or start with one of the below options:

Итоги

На этом мы завершаем первую часть данной серии. Здесь вы узнали о том, как, с чистого листа, создать Node.js-приложение и настроить простую JWT-аутентификацию. В следующей части приступим к разработке пользовательского интерфейса приложения с использованием Vue.js.

Уважаемые читатели! Как по-вашему, подойдёт ли способ аутентификации пользователей, предложенный автором, для использования в продакшне?

Vue fake / mock backend

Path: /src/_helpers/fake-backend.js

The fake backend is used for running the tutorial example without a server api (backend-less). It monkey patches the fetch() function to intercept certain api requests and mimic the behaviour of a real api. Any requests that aren’t intercepted get passed through to the real fetch() function.

I created it so I could focus the tutorial on the Vue Vuex code and not worry about the backend, and also to make it work on StackBlitz.

Похожее:  Личный кабинет Большой Пенсионный Фонд: вход, регистрация, возможности, официальный сайт

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *