preface

In my last blog, I have encapsulated the Gitlab Api through Egg. This article will introduce the project design around the DevOps process (somewhat behind the scenes), requiring readers to have some back-end knowledge.

This series is both a continuous delivery project tutorial and can be used as a Node development tutorial for a complete DevOps project from develop-test-build-deploy

It consists of the following two series: front and rear modules

The backend module

  1. DevOps – Gitlab Api usage (completed, click to jump)
  2. DevOps – Building a DevOps base (30% complete)
  3. DevOps – Gitlab CI pipeline construction
  4. Devops-jenkins pipelined construction
  5. Devops-docker use
  6. DevOps – Release task flow design
  7. DevOps – Code review points
  8. Devops-node monitors service quality

The front-end module

  1. DevOps – H5 base scaffolding
  2. Devops-react project development

The above series may be adjusted later based on the actual development progress of the DevOps project

Enterprise design

Briefly analyze the structure of the r&d process of this project, and then do the following steps (the script has been written, it depends on how to play).

Project requirements analysis (purpose and results of system development)

  1. From project development – test – build – deploy a complete process, simplify delivery costs
  2. The concept of energy efficiency (R&D time – test time – total delivery time -bug rate and repair time) is added into the R&D process as a reference standard for project efficiency improvement (there are too many influencing factors, just for reference).
  3. Reasonable pick and test points, reduce invalid pick and test, reduce the test burden, improve the closed-loop quality of the process
  4. Provide online monitoring, analyze the usage rate and error rate of each version, improve the quality of project development
  5. Fast rollback of a specified version ensures fast service recovery in case of a new version crash

This project is developed from zero. Before formal development, it is necessary to sort out the requirements in order to avoid serious design defects, which may cause difficulties in later development or expansion (the road can be taken slowly, but not deviated).

Process design


As shown in the figure above, further refinement of the previous release process can be divided into the following four categories:

  1. Single project release process (a requirement requires only one project to complete)
  2. Fast rollback function when the production environment fails
  3. Integrated project release process (a requirement may have multiple projects involved in development and release)
  4. Bug fix release process (no requirement, release process for quick fixes of known but not urgent bugs online)

The design of task flow is actually very complex. In order to speed up the delivery of the first version, the task flow is fixed into the above four categories to reduce the amount of development, and a process will be added or modified later

Database design

The use of sequelize

Sequelize provides the sequelize-CLI tool to implement Migrations, and sequelize-CLI can also be introduced in egg projects (see sequelize operations for details).

If you have set up the environment as described in the previous blog, you can use NPM install –save-dev sequelize-cli to install sequelize-cli, and then use the following configuration to generate the required tables.

use strict';

const path = require('
path');



module.exports = {

  config: path.join(__dirname, '
database/config.json'),

  '
migrations-path': path.join(__dirname, 'database/migrations'),

  '
seeders-path': path.join(__dirname, 'database/seeders'),

  '
models-path': path.join(__dirname, 'app/model'),

};

Copy the code

The above is the.Sequelizerc configuration, please put it in the project root directory

npx sequelize init:config

npx sequelize init:migrations

Copy the code

Json file and database/migrations directory. Modify the contents of database/config.json to the database configuration used in the project:

{

  "development": {  // Local database, other environment database, according to the example of their own change

    "username""root".

    "password""123456".

    "database""devops_dev".

    "host""127.0.0.1".

    "dialect""mysql"

  },

}

Copy the code

Create a database table by sequelize migration:generate –name=init-users

module.exports = { // In order to reduce the workload, we use gitLab's permissions directly, so we only need to drop the following fields in the library

  up: async (queryInterface, Sequelize) => {

    const { INTEGER, DATE, STRING } = Sequelize;

    await queryInterface.createTable('users', {

      id: { type: INTEGER, primaryKeytrue,},

      name: STRING(30),

      username: STRING(30),

      email: STRING(100),

      avatar_url: STRING(200),

      web_url: STRING(200),

      created_at: DATE,

      updated_at: DATE,

    });

  },

  downasync queryInterface => {

    await queryInterface.dropTable('users');

  },

};



Copy the code

Finally, execute Migrate to change the database

Update database

npx sequelize db:migrate

If you have a problem and need to roll back, you can pass`db:migrate:undo`Roll back a change

# npx sequelize db:migrate:undo

# Pass`db:migrate:undo:all`Rollback to the initial state

# npx sequelize db:migrate:undo:all

Copy the code

Basic Design table


Data commonly used by Gitlab Project and Branch are stored locally, and then fields are added according to project requirements. The approximate table structure is shown in the figure above

Combined with the above project flow design, explain the table structure relationship

  1. Project can manage multiple branches branch, you can query the status of all branches under the current project (whether it is being tested, whether it exists in the process)
  2. Create a process (equivalent to a requirement) that associates multiple Branch developments
  3. After the process is created, all steps must be completed until completion (development – test – pre-release – production)
  4. When a branch is associated with a process, it is locked and cannot be added to other processes (requirements lock isolation to ensure no interference with the development process)
  5. During the process of test, multiple test can be performed for different branches (complex requirements can be tested in batches to achieve the desired goal)
  6. After all branch states in the process have been tested, the process state moves to the next phase, otherwise it stays in the test phase

The test record table is not put up, for the time being, the above functions are developed and then modified in combination with the branch management

Enterprise development

Add interface global return parameter

import { Controller } from "egg";



export default class BaseController extends Controller {

  get user() {

    return this.ctx.user;

  }



  success(data) {

    this.ctx.body = {

      code0.

      data,

    };

  }



  error({ code, data, message }) {

    // Return different error codes based on services for the front-end service judgment processing

    this.ctx.body = {

      code,

      data,

      message,

    };

  }

}

Copy the code

Define the global return parameter base class. The business Controller inherits the base class. The front end can make business judgment according to the returned code value

JWT permission validation

The last article introduced the method of obtaining access_token from Gitlab to operate the Open API, but we still need to store the user information locally for our later use

For project authorization verification, simple JWT is adopted, user data and access_token are saved, and improvement will be made after the goal of the first stage is completed

Specific egg-JWT usage can be referred to (egg-JWT usage), here directly attached to the business side of the code for reference:

const excludeUrl = ["/user/getUserToken"]; // Request whitelist to filter request paths that do not require verification



export default() = > {

  const jwtAuth = async (ctx, next) => {

    if (excludeUrl.includes(ctx.request.url)) {

      return await next();

    }

    const token = ctx.request.header.authorization;

    if (token) {

      try {

        / / decoding token

        const deCode = ctx.app.jwt.verify(

          token.replace("Bearer ".""), // When JWT middleware validates, Bearer needs to be removed

          ctx.app.config.jwt.secret

        );

        ctx.user = deCode;

        await next();

      } catch (error) {

        ctx.status = 401;

        ctx.body = {

          code401.

          message: error.message,

        };

      }

      return;

    }

    ctx.status = 401;

    ctx.body = {

      code401.

      message"Verification failed".

    };

    return;

  };

  return jwtAuth;

};

Copy the code

The above is the global interception of JWT permission middleware. After verifying the permission, user data is stored in CTX for subsequent business invocation. For specific use of middleware, see Egg middleware

// Controller

import { Post, Prefix } from "egg-shell-decorators";

import BaseController from "./base";



@Prefix("user")

export default class UserController extends BaseController {

  @Post("/getUserToken")

  public async getUserToken({

    request: {

      body: { params },

    },

{})

    const { ctx, app } = this;

    const { username, password } = params;



    // Gitlab retrieves access_token

    const userToken = await ctx.service.user.getUserToken({

      username,

      password,

    });



    // Gitlab obtains user information

    const userInfo = await ctx.service.user.getUserInfo({

      accessToken: userToken.access_token,

    });



    // User data is stored locally

    ctx.service.user.saveUser({

      userInfo,

    });



    // Register user information and token with JWT

    const token = app.jwt.sign(

      {

        userToken,

        userInfo,

      },

      app.config.jwt.secret

    );

    

    ctx.set({ authorization: token }); / / set the headers

    this.success(userInfo);

  }

}



// Service

import { Service } from "egg";



export default class User extends Service {

  // Get the access_token using the Gitlab API

  public async getUserToken({ username, password }) {

    const { data: token } = await this.ctx.helper.utils.http.post(

      "/oauth/token".

      {

        grant_type"password".

        username,

        password,

      }

    );

    if (token && token.access_token) {

      return token;

    }

    return false;

  }



  // Use the GitLab API to get gitLab user information

  public async getUserInfo({ accessToken }) {

    const userInfo = await this.ctx.helper.api.gitlab.user.getUserInfo({

      accessToken,

    });

    return userInfo;

  }



  // The user information falls into the database

  public async saveUser({ userInfo }) {

    const { ctx } = this;

    const {

      id,

      name,

      username,

      email,

      avatar_url: avatarUrl,

      web_url: webUrl,

    } = userInfo;



    // Check whether the user has logged out of the database

    const exist = await ctx.model.User.findOne({

      where: {

        id,

      },

      rawtrue.

    });

    if (exist) return;



    // Create user information

    ctx.model.User.create({

      id,

      name,

      username,

      email,

      avatarUrl,

      webUrl,

    });

  }

}

Copy the code

The above is an example of JWT on the server side, which can parse out the desired information during global middleware interception for subsequent use. The example on the client side is described separately in the React project.

The above is the example and introduction of database table building and user and authority operation. The next chapter of this series will be launched after the development of basic task flow, which is expected to take about 2 weeks

The end of the

This project is developed from zero, the subsequent series of blogs will be launched according to the actual development progress, after the completion of the project, part of the source code will be open for your reference.

If you have any questions or opinions about the content of the article, please add the wechat Cookieboty communication.

Also follow the public account Cookieboty1024, welcome to join the front end soldier growth camp

Manual doghead town building