Serverless + Egg.js 後臺管理系統實戰

做爲一名前端開發者,在選擇 Nodejs 後端服務框架時,第一時間會想到 Egg.js,不得不說 Egg.js 是一個很是優秀的企業級框架,它的高擴展性和豐富的插件,極大的提升了開發效率。開發者只須要關注業務就好,好比要使用 redis,引入 egg-redis 插件,而後簡單配置就能夠了。正由於如此,第一次接觸它,我便喜歡上了它,以後也用它開發過很多應用。css

有了如此優秀的框架,那麼如何將一個 Egg.js 的服務遷移到 Serverless 架構上呢?html

背景

我在文章 基於 Serverless Component 的全棧解決方案 中講述了,如何將一個基於 Vue.js 的前端應用和基於 Express 的後端服務,快速部署到騰訊雲上。雖然受到很多開發者的喜好,可是不少開發者私信問我,這仍是一個 Demo 性質的項目而已,有沒有更加實用性的解決方案。並且他們實際開發中,不少使用的正是 Egg.js 框架,能不能提供一個 Egg.js 的解決方案?前端

本文將手把手教你結合 Egg.jsServerless 實現一個後臺管理系統。vue

讀完此文你將學到:mysql

  1. Egg.js 基本使用
  2. 如何使用 Sequelize ORM 模塊進行 Mysql 操做
  3. 如何使用 Redis
  4. 如何使用 JWT 進行用戶登陸驗證
  5. Serverless Framework 的基本使用
  6. 如何將本地開發好的 Egg.js 應用部署到騰訊云云函數上
  7. 如何基於雲端對象存儲快速部署靜態網站

Egg.js 入門

初始化 Egg.js 項目:ios

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

啓動項目:git

$ npm run dev

而後瀏覽器訪問 http://localhost:7001,就能夠看到親切的 hi, egg 了。github

關於 Egg.js 的框架更多知識,建議閱讀 官方文檔web

準備

對 Egg.js 有了簡單瞭解,接下來咱們來初始化咱們的後臺管理系統,新建一個項目目錄 admin-system:redis

$ mkdir admin-system

將上面建立的 Egg.js 項目複製到 admin-system 目錄下,重命名爲 backend。而後將前端模板項目複製到 frontend 文件夾中:

$ git clone https://github.com/PanJiaChen/vue-admin-template.git frontend
說明: vue-admin-template 是基於 Vue2.0 的管理系統模板,是一個很是優秀的項目,建議對 Vue.js 感興趣的開發者能夠去學習下,固然若是你對 Vue.js 還不是太瞭解,這裏有個基礎入門學習教程 Vuejs 從入門到精通系列文章

以後你的項目目錄結構以下:

.
├── README.md
├── backend     // 建立的 Egg.js 項目
└── frontend    // 克隆的 Vue.js 前端項目模板

啓動前端項目熟悉下界面:

$ cd frontend
$ npm install
$ npm run dev

而後訪問 http://localhost:9528 就能夠看到登陸界面了。

開發後端服務

對於一個後臺管理系統服務,咱們這裏只實現登陸鑑權和文章管理功能,剩下的其餘功能大同小異,讀者能夠以後自由補充擴展。

1. 添加 Sequelize 插件

在正式開發以前,咱們須要引入數據庫插件,這裏本人偏向於使用 Sequelize ORM 工具進行數據庫操做,正好 Egg.js 提供了 egg-sequelize 插件,因而直接拿來用,須要先安裝:

$ cd frontend
# 由於須要經過 sequelize 連接 mysql 因此這也同時安裝 mysql2 模塊
$ npm install egg-sequelize mysql2 --save

而後在 backend/config/plugin.js 中引入該插件:

module.exports = {
  // ....
  sequelize: {
    enable: true,
    package: "egg-sequelize"
  }
  // ....
};

backend/config/config.default.js 中配置數據庫鏈接參數:

// ...
const userConfig = {
  // ...
  sequelize: {
    dialect: "mysql",

    // 這裏也能夠經過 .env 文件注入環境變量,而後經過 process.env 獲取
    host: "xxx",
    port: "xxx",
    database: "xxx",
    username: "xxx",
    password: "xxx"
  }
  // ...
};
// ...

2. 添加 JWT 插件

系統將使用 JWT token 方式進行登陸鑑權,安裝配置參考官方文檔,egg-jwt

3. 添加 Redis 插件

系統將使用 redis 來存儲和管理用戶 token,安裝配置參考官方文檔,egg-redis

4. 角色 API

定義用戶模型,建立 backend/app/model/role.js 文件以下:

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
  });

  // 這裏定義與 users 表的關係,一個角色能夠含有多個用戶,外鍵相關
  Role.associate = () => {
    app.model.Role.hasMany(app.model.User, { as: "users" });
  };

  return Role;
};

實現 Role 相關服務,建立 backend/app/service/role.js 文件以下:

const { Service } = require("egg");

class RoleService extends Service {
  // 獲取角色列表
  async list(options) {
    const {
      ctx: { model }
    } = this;
    return model.Role.findAndCountAll({
      ...options,
      order: [
        ["created_at", "desc"],
        ["id", "desc"]
      ]
    });
  }

  // 經過 id 獲取角色
  async find(id) {
    const {
      ctx: { model }
    } = this;
    const role = await model.Role.findByPk(id);
    if (!role) {
      this.ctx.throw(404, "role not found");
    }
    return role;
  }

  // 建立角色
  async create(role) {
    const {
      ctx: { model }
    } = this;
    return model.Role.create(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);
  }

  // 刪除角色
  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;

一個完整的 RESTful API 就該包括以上五個方法,而後實現 RoleController, 建立 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;

以後在 backend/app/route.js 路由配置文件中定義 role 的 RESTful API:

router.resources("roles", "/roles", controller.role);

經過 router.resources 方法,咱們將 roles 這個資源的增刪改查接口映射到了 app/controller/roles.js 文件。詳細說明參考 官方文檔

5. 用戶 API

同 Role 同樣定義咱們的用戶 API,這裏就不復制粘貼了,能夠參考項目實例源碼 admin-system

6. 同步數據庫表格

上面只是定義好了 RoleUser 兩個 Schema,那麼如何同步到數據庫呢?這裏先借助 Egg.js 啓動的 hooks 來實現,Egg.js 框架提供了統一的入口文件(app.js)進行啓動過程自定義,這個文件返回一個 Boot 類,咱們能夠經過定義 Boot 類中的生命週期方法來執行啓動應用過程當中的初始化工做。

咱們在 backend 目錄中建立 app.js 文件,以下:

"use strict";

class AppBootHook {
  constructor(app) {
    this.app = app;
  }

  async willReady() {
    // 這裏只能在開發模式下同步數據庫表格
    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:25', '2020-02-04 09:54:25'),(2, 'editor', '2020-02-04 09:54:30', '2020-02-04 09:54:30');"
        );
        await this.app.model.query(
          "INSERT INTO users (id, name, password, age, avatar, introduction, created_at, updated_at, role_id) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 20, 'https://yugasun.com/static/avatar.jpg', 'Fullstack Engineer', '2020-02-04 09:55:23', '2020-02-04 09:55:23', 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 a awesome framework', '2020-02-04 09:57:24', '2020-02-04 09:57:24', 1),(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:23', '2020-02-04 10: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;

經過 willReady 生命週期函數,咱們能夠執行 this.app.model.sync() 函數來同步數據表,固然這裏同時初始化了角色和用戶數據記錄,用來作爲演示用。

注意:這的數據庫同步只是本地調試用,若是想要騰訊雲的 Mysql 數據庫,建議開啓遠程鏈接,經過 sequelize db:migrate 實現,而不是每次啓動 Egg 應用時同步,示例代碼已經完成此功能, 參考 Egg Sequelize 文檔
這裏本人爲了省事,直接開啓騰訊雲 Mysql 公網鏈接,而後修改 config.default.js 中的 sequelize 配置,運行 npm run dev 進行開發模式同步。

到這裏,咱們的用戶和角色的 API 都已經定義好了,啓動服務 npm run dev,訪問 https://127.0.0.1:7001/users 能夠獲取全部用戶列表了。

7. 用戶登陸/註銷 API

這裏登陸邏輯比較簡單,客戶端發送 用戶名密碼/login 路由,後端經過 login 函數接受,而後從數據庫中查詢該用戶名,同時比對密碼是否正確。若是正確則調用 app.jwt.sign() 函數生成 token,並將 token 存入到 redis 中,同時返回該 token,以後客戶端須要鑑權的請求都會攜帶 token,進行鑑權驗證。思路很簡單,咱們就開始實現了。

流程圖以下:

<center>
<img src="https://static.yugasun.com/serverless/login-process.jpg" width="300" alt="Login Process"/>
</center>

首先,在 backend/app/controller/home.js 中新增登陸處理 login 方法:

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"
        };
      }
    }
  }
}
註釋:這裏有個密碼存儲邏輯,用戶在註冊時,密碼都是經過 helper 函數 encryptPwd() 進行加密的(這裏用到最簡單的 md5 加密方式,實際開發中建議使用更加高級加密方式),因此在校驗密碼正確性時,也須要先加密一次。至於如何在 Egg.js 框架中新增 helper 函數,只須要在 backend/app/extend 文件夾中新增 helper.js 文件,而後 modole.exports 一個包含該函數的對象就行,參考 Egg 框架擴展文檔

而後,在 backend/app/controller/home.js 中新增 userInfo 方法,獲取用戶信息:

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

egg-jwt 插件,在鑑權經過的路由對應 controller 函數中,會將 app.jwt.sign(user, secrete) 加密的用戶信息,添加到 ctx.state.user 中,因此 userInfo 函數只須要將它返回就行。

以後,在 backend/app/controller/home.js 中新增 logout 方法:

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

userInfologout 函數很是簡單,重點是路由中間件如何處理。

接下來,咱們來定義登陸相關路由,修改 backend/app/router.js 文件,新增 /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);
};

Egg.js 框架定義路由時,router.post() 函數能夠接受中間件函數,用來處理一些路由相關的特殊邏輯。

好比 /user-info,路由添加了 app.jwt 做爲 JWT 鑑權中間件函數,至於爲何這麼用,egg-jwt 插件有明確說明。

這裏稍微複雜的是 /logout 路由,由於咱們在註銷登陸時,須要將用戶的 tokenredis 中移除,因此這裏藉助了 koa-jwt2isRevokded 參數,來進行 token 刪除。

後端服務部署

到這裏,後端服務的登陸和註銷邏輯基本完成了。那麼如何部署到雲函數呢?能夠直接使用 tencent-egg 組件,它是專門爲 Egg.js 框架打造的 Serverless Component,使用它能夠快速將咱們的 Egg.js 項目部署到騰訊云云函數上。

1. 準備

咱們先建立一個 backend/sls.js 入口文件:

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

而後修改 backend/config/config.default.js 文件:

const config = (exports = {
  env: "prod", // 推薦雲函數的 egg 運行環境變量修改成 prod
  rundir: "/tmp",
  logger: {
    dir: "/tmp"
  }
});
註釋:這裏之全部須要修改運行和日誌目錄,是由於雲函數運行時,只有 /tmp 纔有寫權限。

全局安裝 serverless 命令:

$ npm install serverless -g

2. 配置 Serverless

在項目根目錄下建立 serverless.yml 文件,同時新增 backend 配置:

backend:
  component: "@serverless/tencent-egg"
  inputs:
    code: ./backend
    functionName: admin-system
    # 這裏必須指定一個具備操做 mysql 和 redis 的角色,具體角色建立,可訪問 https://console.cloud.tencent.com/cam/role
    role: QCS_SCFFull
    functionConf:
      timeout: 120
      # 這裏的私有網絡必須和 mysql、redis 實例一致
      vpcConfig:
        vpcId: vpc-xxx
        subnetId: subnet-xxx
    apigatewayConf:
      protocols:
        - https

此時你的項目目錄結構以下:

.
├── README.md         // 項目說明文件
├── serverless.yml    // serverless yml 配合文件
├── backend           // 建立的 Egg.js 項目
└── frontend          // 克隆的 Vue.js 前端項目模板

3. 執行部署

執行部署命令:

$ serverless --debug

以後控制檯須要進行掃碼登陸驗證騰訊雲帳號,掃碼登陸就好。等部署成功會發揮以下信息:

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

這裏輸出的 url 就是部署成功的 API 網關接口,能夠直接訪問測試。

註釋:雲函數部署時,會自動在騰訊雲的 API 網關建立一個服務,同時建立一個 API,經過該 API 就能夠觸發雲函數執行了。

4. 帳號配置(可選)

當前默認支持 Serverless cli 掃描二維碼登陸,若是但願配置持久的環境變量/祕鑰信息,也能夠在項目根目錄建立 .env 文件

.env 文件中配置騰訊雲的 SecretId 和 SecretKey 信息並保存,密鑰能夠在 API 密鑰管理 中獲取或者建立.

# .env
TENCENT_SECRET_ID=123
TENCENT_SECRET_KEY=123

5. 文章 API

跟用戶 API 相似,只須要複製粘貼上面用戶相關模塊,修更名稱爲 posts, 並修改數據模型就行,這裏就不粘貼代碼了。

前端開發

本實例直接使用的 vue-admin-template 的前端模板。

咱們須要作以下幾部分修改:

  1. 刪除接口模擬:更換爲真實的後端服務接口
  2. 修改接口函數:包括用戶相關的 frontend/src/api/user.js 和文章相關接口 frontend/src/api/post.js
  3. 修改接口工具函數:主要是修改 frontend/src/utils/request.js 文件,包括 axios請求的 baseURL 和請求的 header。
  4. UI 界面修改:主要是新增文章管理頁面,包括列表頁和新增頁。

1. 刪除接口模擬

首先刪除 frontend/mock 文件夾。而後修改前端入口文件 frontend/src/main.js

// 1. 引入接口變量文件,這個會依賴 @serverless/tencent-website 組件自動生成
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. 下面這段就是 mock server 引入,刪除就好
// 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. 修改接口函數

修改 frontend/src/api/user.js 文件,包括登陸、註銷、獲取用戶信息和獲取用戶列表函數以下:

import request from "@/utils/request";

// 登陸
export function login(data) {
  return request({
    url: "/login",
    method: "post",
    data
  });
}

// 獲取用戶信息
export function getInfo(token) {
  return request({
    url: "/user-info",
    method: "get"
  });
}

// 註銷登陸
export function logout() {
  return request({
    url: "/logout",
    method: "post"
  });
}

// 獲取用戶列表
export function getList() {
  return request({
    url: "/users",
    method: "get"
  });
}

新增 frontend/src/api/post.js 文件以下:

import request from "@/utils/request";

// 獲取文章列表
export function getList(params) {
  return request({
    url: "/posts",
    method: "get",
    params
  });
}

// 建立文章
export function create(data) {
  return request({
    url: "/posts",
    method: "post",
    data
  });
}

// 刪除文章
export function destroy(id) {
  return request({
    url: `/posts/${id}`,
    method: "delete"
  });
}

3. 修改接口工具函數

由於 @serverless/tencent-website 組件能夠定義 env 參數,執行成功後它會在指定 root 目錄自動生成 env.js,而後在 frontend/src/main.js 中引入使用。
它會掛載 env 中定義的接口變量到 window 對象上。好比這生成的 env.js 文件以下:

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

根據此文件咱們來修改 frontend/src/utils/request.js 文件:

import axios from "axios";
import { MessageBox, Message } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";

// 建立 axios 實例
const service = axios.create({
  // 1. 這裏設置爲 `env.js` 中的變量 `window.env.apiUrl`
  baseURL: window.env.apiUrl || "/", // url = base url + request url
  timeout: 5000 // request timeout
});

// request 注入
service.interceptors.request.use(
  config => {
    // 2. 添加鑑權token
    if (store.getters.token) {
      config.headers["Authorization"] = `Bearer ${getToken()}`;
    }
    return config;
  },
  error => {
    console.log(error); // for debug
    return Promise.reject(error);
  }
);

// 請求 response 注入
service.interceptors.response.use(
  response => {
    const res = response.data;

    // 只有請求code爲0,纔是正常返回,不然須要提示接口錯誤
    if (res.code !== 0) {
      Message({
        message: res.message || "Error",
        type: "error",
        duration: 5 * 1000
      });

      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 log in 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 界面修改

關於 UI 界面修改,這裏就不作說明了,由於涉及到 Vue.js 的基礎使用,若是還不會使用 Vue.js,建議先複製示例代碼就好。若是對 Vue.js 感興趣,能夠到 Vue.js 官網 學習。也能夠閱讀本人的 Vuejs 從入門到精通系列文章,喜歡的話,能夠送上您寶貴的 Star (*^▽^*)

這裏只須要複製 Demo 源碼frontend/routerfrontend/views 兩個文件夾就好。

前端部署

由於前端編譯後都是靜態文件,咱們須要將靜態文件上傳到騰訊雲的 COS(對象存儲) 服務,而後開啓 COS 的靜態網站功能就能夠了,這些都不須要你手動操做,使用 @serverless/tencent-website 組件就能夠輕鬆搞定。

1. 修改 Serverless 配置文件

修改項目根目錄下 serverless.yml 文件,新增前端相關配置:

name: admin-system

# 前端配置
frontend:
  component: "@serverless/tencent-website"
  inputs:
    code:
      src: dist
      root: frontend
      envPath: src # 相對於 root 指定目錄,這裏實際就是 frontend/src
      hook: npm run build
    env:
      # 依賴後端部署成功後生成的 url
      apiUrl: ${backend.url}
    protocol: https
    # TODO: CDN 配置,請修改!!!
    hosts:
      - host: sls-admin.yugasun.com # CDN 加速域名
        https:
          certId: abcdedg # 爲加速域名在騰訊雲平臺申請的免費證書 ID
          http2: off
          httpsType: 4
          forceSwitch: -2

# 後端配置
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:
        - https

2. 執行部署

執行部署命令:

$ serverless --debug

輸出以下成功結果:

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/
註釋:這裏 frontend 中多輸出了 host,是咱們的 CDN 加速域名,能夠經過配置 @serverless/tencent-website 組件的 inputs.hosts 來實現。有關 CDN 相關配置說明能夠閱讀 基於 Serverless Component 的全棧解決方案 - 續集。固然,若是你不想配置 CDN,直接刪除,而後訪問 COS 生成的靜態網站 url。

部署成功後,咱們就能夠訪問 https://sls-admin.yugasun.com 登陸體驗了。

源碼

本篇涉及到全部源碼都維護在開源項目 tencent-serverless-demoadmin-system

總結

本文基於騰訊雲的無服務器框架 Serverless Framework 實現,涉及到內容較多,推薦在閱讀時,邊看邊開發,跟着文章節奏一步一步實現。

若是遇到問題,能夠參考本文源碼。若是你成功實現了,能夠到官網進一步熟悉 Egg.js 框架,以便從此能夠實現更加複雜的應用。雖然本文使用的是 Vue.js 前端框架,可是你也能夠將 frontend 更換爲任何你喜歡的前端框架項目,開發時只須要將接口請求前綴使用 @serverless/tencent-website 組件生成的 env.js 文件就行。

傳送門:

歡迎訪問:Serverless 中文網,您能夠在 最佳實踐 裏體驗更多關於 Serverless 應用的開發!

推薦閱讀: 《Serverless 架構:從原理、設計到項目實戰》
相關文章
相關標籤/搜索