Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to carry out Serverless + Egg.js background Management system

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

Today, I will talk to you about how to carry out the actual combat of the Serverless + Egg.js background management system, which may not be well understood by many people. In order to make you understand better, the editor has summarized the following contents for you. I hope you can get something according to this article.

Background

In my article, I described how to quickly deploy a Vue.js-based front-end application and Express-based back-end service to Tencent Cloud in a full-stack solution based on Serverless Component. Although loved by many developers, many developers asked me privately that this is still a Demo project and whether there is a more practical solution. And in their actual development, many of them use the Egg.js framework, can they provide an Egg.js solution? How to migrate an Egg.js service to the Serverless architecture?

Getting started with Egg.js

Initialize the Egg.js project:

$mkdir egg-example & & cd egg-example$ npm init egg--type=simple$ npm I

Start the project:

$npm run dev

Then the browser visits http://localhost:7001, and you can see the cordial hi, egg.

For more information about the framework of Egg.js, it is recommended to read the official documentation

Prepare for

Now that we have a simple understanding of Egg.js, let's initialize our background management system and create a new project directory admin-system:

$mkdir admin-system

Copy the Egg.js project created above to the admin-system directory and rename it backend. Then copy the front-end template project to the frontend folder:

$git clone https://github.com/PanJiaChen/vue-admin-template.git frontend

Description: vue-admin-template is a Vue2.0-based management system template, is a very excellent project, it is recommended that developers interested in Vue.js can learn, of course, if you do not know much about Vue.js, here is a basic tutorial Vuejs from getting started to mastering a series of articles

After that, your project directory structure is as follows:

.├── README.md ├── backend / / Egg.js project created └── frontend / / cloned Vue.js front-end project template

Start the front-end project and get familiar with the following interface:

$cd frontend$ npm install$ npm run dev

Then visit http://localhost:9528 and you can see the login screen.

Develop back-end services

For a background management system service, we only implement login authentication and article management functions, and the remaining functions are more or less the same. Readers are free to add and expand later.

1. Add a Sequelize plug-in

Before formal development, we need to introduce database plug-ins. Here, I prefer to use Sequelize ORM tools for database operations. It just so happens that Egg.js provides egg-sequelize plug-ins, so we need to install them directly.

$cd frontend# because you need to link to mysql through sequelize, so this also installs the mysql2 module $npm install egg-sequelize mysql2-- save.

Then introduce the plug-in into backend/config/plugin.js:

Module.exports = {/ /.... Sequelize: {enable: true, package: "egg-sequelize"} / /....}

Configure database connection parameters in backend/config/config.default.js:

/ /. Const userConfig = {/ /. Sequelize: {dialect: "mysql", / / you can also inject environment variables through .env file, and then get host through process.env: "xxx", port: "xxx", database: "xxx", username: "xxx", password: "xxx"} / /.}; / /. 2. Add a JWT plug-in

The system will use JWT token for login authentication. For installation and configuration, refer to the official documentation and egg-jwt.

3. Add a Redis plug-in

The system will use redis to store and manage user token, installation and configuration refer to the official documentation, egg-redis

4. Role API

Define the user model and create the backend/app/model/role.js file as follows:

Module.exports = app = > {const {STRING, INTEGER, DATE} = app.Sequelize; const Role = app.model.define ("role", {id: {type: INTEGER, primaryKey: true, autoIncrement: true}, name: STRING (30), created_at: DATE, updated_at: DATE}) / / the relationship with the users table is defined here. A role can contain multiple users. Foreign key related Role.associate = () = > {app.model.Role.hasMany (app.model.User, {as: "users"});}; return Role;}

To implement Role-related services, create the backend/app/service/role.js file as follows:

Const {Service} = require ("egg"); class RoleService extends Service {/ / get role list async list (options) {const {ctx: {model}} = this; return model.Role.findAndCountAll ({... options, order: [["created_at", "desc"], ["id", "desc"]]}) } / / obtain role async find (id) {const {ctx: {model}} = this; const role = await model.Role.findByPk (id) through id; if (! role) {this.ctx.throw (404, "role not found");} return role;} / / create role async create (role) {const {ctx: {model}} = this Return model.Role.create (role);} / / Update role async update ({id, updates}) {const role = await this.ctx.model.Role.findByPk (id); if (! role) {this.ctx.throw (404, "role not found");} return role.update (updates);} / Delete role async destroy (id) {const role = await this.ctx.model.Role.findByPk (id) If (! role) {this.ctx.throw (404, "role not found");} return role.destroy ();}} module.exports = RoleService

A complete RESTful API should include the above five methods, then implement RoleController and create a backend/app/controller/role.js:

Const {Controller} = require ("egg"); class RoleController extends Controller {async index () {const {ctx} = this; const {query, service, helper} = ctx; const options = {limit: helper.parseInt (query.limit), offset: helper.parseInt (query.offset)}; const data = await service.role.list (options) Ctx.body = {code: 0, data: {count: data.count, items: data.rows}};} async show () {const {ctx} = this; const {params, service, helper} = ctx; const id = helper.parseInt (params.id); ctx.body = await service.role.find (id);} async create () {const {ctx} = this Const {service} = ctx; const body = ctx.request.body; const role = await service.role.create (body); ctx.status = 201; ctx.body = role;} async update () {const {ctx} = this; const {params, service, helper} = ctx; const body = ctx.request.body; const id = helper.parseInt (params.id) Ctx.body = await service.role.update ({id, updates: body});} async destroy () {const {ctx} = this; const {params, service, helper} = ctx; const id = helper.parseInt (params.id); await service.role.destroy (id); ctx.status = 200;} module.exports = RoleController

Then define the RESTful API of role in the backend/app/route.js routing profile:

Router.resources ("roles", "/ roles", controller.role)

Through the router.resources method, we map the add, delete, change and query interface of the roles resource to the app/controller/roles.js file. Refer to the official documentation for detailed instructions.

5. User API

Define our user API as Role. Instead of copying and pasting here, you can refer to the project example source code admin-system.

6. Synchronize database tables

The above is only defined Role and User two Schema, so how to synchronize to the database? Here, we first implement it with the help of hooks started by Egg.js. The Egg.js framework provides a unified entry file (app.js) for customizing the startup process. This file returns a Boot class. We can perform the initialization work during the startup process by defining the lifecycle method in the Boot class.

We create the app.js file in the backend directory as follows:

"use strict"; class AppBootHook {constructor (app) {this.app = app;} async willReady () {/ / here you can only synchronize database tables const isDev = process.env.NODE_ENV = = "development"; if (isDev) {try {console.log ("Start syncing database models..."); await this.app.model.sync ({logging: console.log, force: isDev}) Console.log ("Start init database data..."); await this.app.model.query ("INSERT INTO roles (id, name, created_at, updated_at) VALUES (1, 'admin',' 2020-02-04 09) 54 INSERT INTO roles 25', '2020-02-04 09 54 INSERT INTO roles 25'), (2, 'editor',' 2020-02-04 09 54 INSERT INTO roles 30', '2020-02-04 09 54 INSERT INTO roles 30') "); await this.app.model.query (" INSERT INTO users (id, name, password, age, avatar, introduction, created_at, updated_at, role_id) VALUES (1, 'admin',' e10adc3949ba59abbe56e057f20f883egoat, 20, 'https://yugasun.com/static/avatar.jpg',' Fullstack Engineer', '2020-02-04 09V 55v 23mm,' 2020-02-04 09V 55v 23V, 1); ") Await this.app.model.query ("INSERT INTO posts (id, title, content, created_at, updated_at, user_id) VALUES (2, 'Awesome Egg.js',' Egg.js is an awesome framework', '2020-02-04 09) 57 Awesome Serverless', 24,' 2020-02-04 09), (3, 'Awesome Serverless',' Build web, mobile and IoT applications using Tencent Cloud and API Gateway, Tencent Cloud Functions, and more.' '2020-02-04 10-00-00-23-9,' 2020-02-04-10-00-00-23, 1) "); console.log (" Successfully init database data. "); console.log (" Successfully sync database models. ");} catch (e) {console.log (e); throw new Error (" Database migration failed. ");} module.exports = AppBootHook

With the willReady lifecycle function, we can execute the this.app.model.sync () function to synchronize the data table, and of course both the role and user data records are initialized for demonstration purposes.

Note: the database synchronization here is only for local debugging. If you want Tencent Cloud's Mysql database, it is recommended to open a remote connection and implement it through sequelize db:migrate, instead of synchronizing each time you start the Egg application. The sample code has completed this function, refer to the Egg Sequelize documentation. To save trouble, I directly open the Tencent Cloud Mysql public network connection, then modify the sequelize configuration in config.default.js, and run npm run dev to synchronize the development mode.

At this point, the API of our users and roles have been defined, start the service npm run dev, and visit https://127.0.0.1:7001/users to get a list of all users.

7. User logs in / logs out of API

Here, the login logic is relatively simple. The client sends the user name and password to the / login route, and the backend accepts it through the login function, then queries the database for the user name, and compares whether the password is correct. If it is correct, call the app.jwt.sign () function to generate token, store the token in redis, and return the token. After that, the client requests for authentication will carry token for authentication verification. The train of thought is very simple, we begin to realize.

The flow chart is as follows:

Login Process

First, add the login processing login method in backend/app/controller/home.js:

Class HomeController extends Controller {/ /... Async login () {const {ctx, app, config} = this; const {service, helper} = ctx; const {username, password} = ctx.request.body; const user = await service.user.findByName (username); if (! user) {ctx.status = 403; ctx.body = {code: 403, message: "Username or password wrong"} } else {if (user.password = helper.encryptPwd (password)) {ctx.status = 200 Const token = app.jwt.sign ({id: user.id, name: user.name, role: user.role.name, avatar: user.avatar}, config.jwt.secret, {expiresIn: "1h"}) Try {await app.redis.set (`token_$ {user.id} `, token); ctx.body = {code: 0, message: "Get token success", token};} catch (e) {console.error (e) Ctx.body = {code: 500, message: "Server busy, please try again"};}} else {ctx.status = 403; ctx.body = {code: 403, message: "Username or password wrong"};}

Note: there is a password storage logic. When users register, their passwords are encrypted through the helper function encryptPwd () (the simplest md5 encryption method is used here, but a more advanced encryption method is recommended in actual development), so you also need to encrypt it before verifying the correctness of the password. As for how to add the helper function to the Egg.js framework, just add the helper.js file to the backend/app/extend folder, and then modole.exports an object containing the function. Refer to the Egg framework extension documentation.

Then, add the userInfo method to backend/app/controller/home.js to get the user information:

Async userInfo () {const {ctx} = this; const {user} = ctx.state; ctx.status = 200; ctx.body = {code: 0, data: user,};}

The egg-jwt plug-in adds the user information encrypted by app.jwt.sign (user, secrete) to the ctx.state.user in the controller function corresponding to the route passed by authentication, so the userInfo function only needs to return it.

After that, add the logout method to backend/app/controller/home.js:

Async logout () {const {ctx} = this; ctx.status = 200; ctx.body = {code: 0, message: 'Logout success',};}

The userInfo and logout functions are very simple, focusing on how the routing middleware handles it.

Next, we define the routes related to login, modify the backend/app/router.js file, and add three routes: / login, / user-info, / logout:

Const koajwt = require ("koa-jwt2"); module.exports = app = > {const {router, controller, jwt} = app; router.get ("/", controller.home.index); router.post ("/ login", controller.home.login); router.get ("/ user-info", jwt, controller.home.userInfo); const isRevokedAsync = function (req, payload) {return new Promise (resolve = > {try {const userId = payload.id) Const tokenKey = `token_$ {userId} `; const token = app.redis.get (tokenKey); if (token) {app.redis.del (tokenKey);} resolve (false);} catch (e) {resolve (true);}});} Router.post ("/ logout", koajwt ({secret: app.config.jwt.secret, credentialsRequired: false, isRevoked: isRevokedAsync}), controller.home.logout); router.resources ("roles", "/ roles", controller.role); router.resources ("users", "/ users", controller.user); router.resources ("posts", "/ posts", controller.post);}

When the Egg.js framework defines routes, the router.post () function can accept middleware functions to deal with some special logic related to routing.

For example, / user-info, routing adds app.jwt as a JWT authentication middleware function, as to why it is used, the egg-jwt plug-in has a clear explanation.

Slightly more complicated here is the / logout route, because when we log out, we need to remove the user's token from the redis, so here we use the isRevokded parameter of koa-jwt2 to delete the token.

Back-end service deployment

At this point, the login and logout logic for the back-end service is almost complete. So how to deploy to the cloud function? You can directly use the tencent-egg component, which is a Serverless Component specially built for the Egg.js framework, and can be used to quickly deploy our Egg.js project to Tencent Cloud functions.

1. Prepare for

Let's first create a backend/sls.js entry file:

Const {Application} = require ("egg"); const app = new Application (); module.exports = app

Then modify the backend/config/config.default.js file:

Const config = (exports = {env: "prod", / / it is recommended that the egg runtime environment variable of the cloud function be modified to prod rundir: "/ tmp", logger: {dir: "/ tmp"}})

Note: you need to modify the run and log directories here because only / tmp has write permission when the cloud function is running.

Install the serverless command globally:

$npm install serverless-G2. Configure Serverless

Create a serverless.yml file in the root directory of the project, and add a new backend configuration:

Backend: component: "@ serverless/tencent-egg" inputs: code:. / backend functionName: admin-system # A role with manipulating mysql and redis must be specified here, and the specific role creation You can visit https://console.cloud.tencent.com/cam/role role: QCS_SCFFull functionConf: timeout: 120# the VPC here must be the same as mysql and redis instances vpcConfig: vpcId: vpc-xxx subnetId: subnet-xxx apigatewayConf: protocols:-https

At this point, your project directory structure is as follows:

.├── README.md / / Project description file ├── serverless.yml / / serverless yml matching file ├── backend / / Egg.js project └── frontend / / cloned Vue.js front-end project template 3. Perform deployment

Execute the deployment command:

$serverless-debug

After that, the console needs to scan the code to log in to verify the Tencent Cloud account. Just log in with the scan code. The following information will be used when the deployment is successful:

Backend: region: ap-guangzhou functionName: admin-system apiGatewayServiceId: service-f1bhmhk4 url: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/

The url output here is the successfully deployed API gateway interface, which can directly access the test.

Note: when a cloud function is deployed, a service and an API are automatically created in Tencent Cloud's API gateway, through which the cloud function can be triggered to execute.

4. Account configuration (optional)

Currently, Serverless cli scanning QR code login is supported by default. If you want to configure persistent environment variables / key information, you can also create .env files in the project root directory.

Configure Tencent Cloud SecretId and SecretKey information in .env file and save them. Keys can be obtained or created in API key management.

# .envTENCENT _ SECRET_ID=123TENCENT_SECRET_KEY=1235. Article API

Similar to the user API, you only need to copy and paste the user-related module above, change the name to posts, and modify the data model, and don't paste the code here.

Front-end development

The front-end template of vue-admin-template directly used in this example.

We need to make the following changes:

Delete interface simulation: replace it with a real back-end service interface

Modify the interface function: including user-related frontend/src/api/user.js and article-related interface frontend/src/api/post.js.

Modify the interface tool function: mainly modify the frontend/src/utils/request.js file, including the axios request baseURL and the request header.

UI interface modification: mainly add article management page, including list page and new page.

1. Delete interface simulation

First delete the frontend/mock folder. Then modify the front-end entry file frontend/src/main.js:

/ / 1. Introduce an interface variable file, which relies on @ serverless/tencent-website components to automatically generate import ". / env.js"; import Vue from "vue"; import "normalize.css/normalize.css"; import ElementUI from "element-ui"; import "element-ui/lib/theme-chalk/index.css"; import locale from "element-ui/lib/locale/lang/en"; import "@ / styles/index.scss"; import App from ". / App"; import store from ". / store" Import router from ". / router"; import "@ / icons"; import "@ / permission"; / / 2. The following paragraph is the introduction of mock server, delete / / if (process.env.NODE_ENV = 'production') {/ / const {mockXHR} = require ('.. / mock') / / mockXHR () / /} Vue.use (ElementUI, {locale}); Vue.config.productionTip = false;new Vue ({el: "# app", router, store, render: h = > h (App)}); 2. Modify interface function

Modify the frontend/src/api/user.js file, including login, logout, get user information and get user list functions as follows:

Import request from "@ / utils/request"; / / Log in to export function login (data) {return request ({url: "/ login", method: "post", data});} / / get user information export function getInfo (token) {return request ({url: "/ user-info", method: "get"}) } / / Login export function logout () {return request ({url: "/ logout", method: "post"});} / / get the user list export function getList () {return request ({url: "/ users", method: "get"});}

The new frontend/src/api/post.js file is as follows:

Import request from "@ / utils/request"; / / get article list export function getList (params) {return request ({url: "/ posts", method: "get", params});} / / create article export function create (data) {return request ({url: "/ posts", method: "post", data}) } / / Delete export function destroy (id) {return request ({url: `/ posts/$ {id}`, method: "delete"});} 3. Modify interface tool function

Because the @ serverless/tencent-website component can define the env parameter, it automatically generates env.js in the specified root directory after successful execution, and then introduces it into frontend/src/main.js. It mounts the interface variables defined in env to the window object. For example, the generated env.js file is as follows:

Window.env = {}; window.env.apiUrl = "https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/";

According to this file, let's modify the frontend/src/utils/request.js file:

Import axios from "axios"; import {MessageBox, Message} from "element-ui"; import store from "@ / store"; import {getToken} from "@ / utils/auth"; / / create axios instance const service = axios.create ({/ / 1. Here, set to the variable `window.env.apiUrl` baseURL: window.env.apiUrl | | "/", / / url = base url + request url timeout: 5000 / / request timeout} in `env.js`; / / request injection service.interceptors.request.use (config = > {/ / 2. Add authentication token if (store.getters.token) {config.headers ["Authorization"] = `Bearer ${getToken ()} `;} return config;}, error = > {console.log (error); / / for debug return Promise.reject (error);}); / / request response to inject service.interceptors.response.use (response = > {const res = response.data) / / only a request code of 0 is returned normally, otherwise the API error if (res.code! = = 0) {Message ({message: res.message | | "Error", type: "error", duration: 5 * 1000}) is required. If (res.code = 50008 | | res.code = 50012 | | res.code = 50014) {/ / to re-login MessageBox.confirm ("You have been logged out, you can cancel to stay on this page, or login again", "Confirm logout", {confirmButtonText: "Re-Login", cancelButtonText: "Cancel" Type: "warning"}) .then (() = > {store.dispatch ("user/resetToken") .then () = > {location.reload () });} return Promise.reject (new Error (res.message | | "Error"));} else {return res;}}, error = > {console.log ("err" + error); Message ({message: error.message, type: "error", duration: 5 * 1000}); return Promise.reject (error);}); export default service 4. UI interface modification

With regard to the modification of the UI interface, I will not explain it here, because it involves the basic use of Vue.js. If you do not know how to use Vue.js, it is recommended to copy the sample code first. If you are interested in Vue.js, you can go to the Vue.js official website to learn. You can also read my Vuejs series from entry to proficiency. If you like, you can send your precious Star (* ^ ▽ ^ *)

Here you only need to copy the frontend/router and frontend/views folders of the Demo source code.

Front-end deployment

Because the front end is compiled with static files, we need to upload the static files to Tencent Cloud's COS (object Storage) service, and then enable the static website feature of COS. All these do not need to be done manually, and can be easily done using @ serverless/tencent-website component.

1. Modify Serverless configuration file

Modify the serverless.yml file under the root directory of the project, and add the relevant configuration of the frontend:

Name: admin-system# frontend configuration frontend: component: "@ serverless/tencent-website" inputs: code: src: dist root: frontend envPath: src # relative to the directory specified by root, this is actually frontend/src hook: npm run build env: # depending on the url apiUrl: ${backend.url} protocol: https # TODO: CDN configuration generated after successful deployment of the backend, please modify! Hosts:-host: sls-admin.yugasun.com # CDN accelerated domain name https: certId: abcdedg # Free certificate applied for on Tencent Cloud platform for accelerated domain name ID http2: off httpsType: 4 forceSwitch:-configuration backend: component: "@ serverless/tencent-egg" inputs: code:. / backend functionName: admin-system role: QCS _ SCFFull functionConf: timeout: 120 vpcConfig: vpcId: vpc-6n5x55kb subnetId: subnet-4cvr91js apigatewayConf: protocols:-https2. Perform deployment

Execute the deployment command:

$serverless-debug

Output the following successful results:

Frontend: url: https://dtnu69vl-470dpfh-1251556596.cos-website.ap-guangzhou.myqcloud.com env: apiUrl: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/ host:-https://sls-admin.yugasun.com (CNAME: sls-admin.yugasun.com.cdn.dnsv1.com) backend: region: ap-guangzhou functionName: admin-system ApiGatewayServiceId: service-f1bhmhk4 url: https://service-f1bhmhk4-1251556596.gz.apigw.tencentcs.com/release/

Note: host is mostly output in frontend, which is our CDN accelerated domain name, which can be realized by configuring the inputs.hosts of @ serverless/tencent-website component. For configuration instructions on CDN, you can read the Serverless Component-based full stack solution-sequel. Of course, if you don't want to configure CDN, just delete it and visit the static website url generated by COS.

Once the deployment is successful, we can access the https://sls-admin.yugasun.com login experience.

After reading the above, do you have any further understanding of how to carry out the Serverless + Egg.js background management system? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report