
$ cnpm install -g serverless
$ cnpm update -g serverless
$ serverless -v
0配置,便捷开发,极速部署您的第一个云函数tencent-scf\serverless.yml
# serverless.yml
component: scf # (必填) 引用 component 的名称,当前用到的是 tencent-scf 组件
name: scfdemo # (必填) 该组件创建的实例名称
org: test # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid
app: scfApp # (可选) 该 SCF 应用名称
stage: dev # (可选) 用于区分环境信息,默认值是 dev
inputs:
  name: scfFunctionName
  src: ./src
  runtime: Nodejs10.15 # 云函数的运行时环境。除 Nodejs10.15 外,可选值为:Python2.7、Python3.6、Nodejs6.10、Nodejs8.9、PHP5、PHP7、Golang1、Java8。
  region: ap-beijing
  handler: index.main_handler
  events:
    - apigw:
        name: serverless_api
        parameters:
          protocols:
            - http
            - https
          serviceName:
          description: The service of Serverless Framework
          environment: release
          endpoints:
            - path: /index
              method: GET
tencent-scf\src\index.js
'use strict';
exports.main_handler = async (event, context, callback) => {
    console.log("Hello World")
    console.log(event)
    console.log(event["non-exist"])
    console.log(context)
    return "Hello World";
};
tencent-scf.env

TENCENT_APP_ID=
TENCENT_SECRET_ID=
TENCENT_SECRET_KEY=
sls --debug
# serverless.yml
restApi:
    component: "@serverless/tencent-apigateway"
    inputs:
      region: ap-beijing
      protocol: http
      serviceName: serverless
      environment: release
      endpoints:
        - path: /users
          method: GET
          function:
            functionName: scfFunctionName
sls --debug
http://service-mpkd1e88-1258145019.gz.apigw.tencentcs.com/users
staticwebsite\serverless.yml
component: website # (必填) 引用 component 的名称,当前用到的是 tencent-website 组件
name: websitedemo # (必填) 该 website 组件创建的实例名称
org: test # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid
app: websiteApp # (可选) 该 website 应用名称
stage: dev # (可选) 用于区分环境信息,默认值是 dev
inputs:
  code:
    src: ./code
    index: index.html
    error: index.html  
  region: ap-beijing
  bucketName: my-bucket
staticwebsite\code\index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    static website
</body>
</html>
mkdir tencent-express
cd tencent-express
npm init -y
cnpm i express -S
tencent-express\serverless.yml
org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
stage: dev # (optional) serverless dashboard stage. default is dev.
component: express # (required) name of the component. In that case, it's express.
name: expressDemo # (required) name of your express component instance.
inputs:
  src: ./
  region: ap-beijing
  runtime: Nodejs10.15
  apigatewayConf:
    protocols:
      - http
      - https
    environment: release
tencent-express\sls.js
const express = require('express')
const path = require('path')
const app = express()
app.get(`/*`, (req, res) => {
    res.send('i am express')
})
module.exports = app
tencent-express-layer\serverless.yml
org: orgDemo # (optional) serverless dashboard org. default is the first org you created during signup.
app: appDemo # (optional) serverless dashboard app. default is the same as the name property.
stage: dev # (optional) serverless dashboard stage. default is dev.
component: express # (required) name of the component. In that case, it's express.
name: expressLayerDemo # (required) name of your express component instance.
inputs:
  src:
    src: ./src # (optional) path to the source folder. default is a hello world app.
    exclude:
      - .env
  region: ap-beijing
  layers:
    - name: nodeLayer
      version: 1
  runtime: Nodejs10.15
  apigatewayConf:
    protocols:
      - http
      - https
    environment: release
tencent-express-layer\src\sls.js
const express = require('express')
const path = require('path')
const app = express()
app.get(`/*`, (req, res) => {
    res.sendFile(path.join(__dirname, 'index.html'))
})
app.use(function (err, req, res) {
    console.error(err)
    res.status(500).send('Internal Serverless Error')
})
module.exports = app

vue-fullstack\serverless.yml
name: tencent-fullstack-vue-application
dashboard:
  component: '@serverless/tencent-website'
  inputs:
    code:
      src: dist
      root: dashboard
      hook: npm run build
    env:
      apiUrl: ${api.url}
api:
  component: '@serverless/tencent-express'
  inputs:
    code: ./api
    functionName: tencent-fullstack-vue-api
    apigatewayConf:
      protocols:
        - https
vue-fullstack\api\package.json
{
  "name": "tencent-fullstack-vue-api",
  "version": "0.0.0",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1"
  },
  "license": "ISC"
}
vue-fullstack\api\app.js
'use strict'
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/', (req, res) => {
    res.send(JSON.stringify({ message: `珠峰架构` }))
});
module.exports = app;
vue create dashboard
vue-fullstack\dashboard\src\App.vue
<template>
  <div id="app">{{message}}</div>
</template>
<script>
import "../env";
export default {
  name: "App",
  data() {
    return {
      message: "message"
    };
  },
  mounted() {
    fetch(window.env.apiUrl)
      .then(res => res.json())
      .then(result => {
        this.message = result.message;
      });
  }
};
</script>
用户-角色-权限的授权模型

| 字段 | 字段名 | 类型 | 默认 | 
|---|---|---|---|
| id | ID | int(11) | |
| userName | 用户名 | varchar(255) | |
| password | 密码 | varchar(255) | 
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userName` varchar(255),
  `password` varchar(255),
  PRIMARY KEY (`id`) 
)
INSERT INTO `user` VALUES (1, 'isadmin', '123456');
INSERT INTO `user` VALUES (2, 'isuser', '123456');
| 字段 | 字段名 | 类型 | 默认 | 
|---|---|---|---|
| id | ID | int(11) | |
| name | 名称 | varchar(255) | |
| desc | 描述 | varchar(255) | 
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) ,
  `desc` varchar(255) ,
  PRIMARY KEY (`id`) 
) E
INSERT INTO `role` VALUES (1, 'admin', '管理员');
INSERT INTO `role` VALUES (2, 'user', '普通用户');
| 字段 | 字段名 | 类型 | 默认 | 
|---|---|---|---|
| id | ID | int(11) | |
| name | 名称 | varchar(255) | |
| parent_id | 父ID | int(11) | |
| icon | 图标 | varchar(255) | |
| key | 路径 | varchar(255) | |
| type | 类型 | varchar(32) | 
CREATE TABLE `permission`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) ,
  `parent_id` int(11) NULL DEFAULT NULL,
  `icon` varchar(255) ,
  `key` varchar(255) ,
  `type` varchar(255) ,
  PRIMARY KEY (`id`) 
);
INSERT INTO `permission` VALUES (1, '授权平台', 0, 'desktop', '/api', 'menu');
INSERT INTO `permission` VALUES (2, '角色管理', 1, 'team', '/api/role', 'menu');
INSERT INTO `permission` VALUES (3, '用户管理', 1, 'user', '/api/user', 'menu');
INSERT INTO `permission` VALUES (4, '权限管理', 1, 'idcard', '/api/resource', 'menu');
INSERT INTO `permission` VALUES (5, '添加用户', 3, 'team', '/api/user/add', 'button');
INSERT INTO `permission` VALUES (6, '删除用户', 3, 'team', '/api/user/delete', 'button');
| 字段 | 字段名 | 类型 | 
|---|---|---|
| role_id | 角色ID | int(11) | 
| user_id | 用户ID | int(11) | 
DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user`  (
  `role_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`, `role_id`) 
) 
INSERT INTO `role_user` VALUES (1, 1);
INSERT INTO `role_user` VALUES (2, 2);
| 字段 | 字段名 | 类型 | 
|---|---|---|
| role_id | 角色ID | int(11) | 
| permission_id | 资源ID | int(11) | 
CREATE TABLE `role_permission`  (
  `role_id` int(11) NOT NULL,
  `permission_id` int(255) NOT NULL,
  PRIMARY KEY (`role_id`, `permission_id`) 
) 
INSERT INTO `role_permission` VALUES (1, 1);
INSERT INTO `role_permission` VALUES (1, 2);
INSERT INTO `role_permission` VALUES (1, 3);
INSERT INTO `role_permission` VALUES (1, 4);
INSERT INTO `role_permission` VALUES (1, 5);
INSERT INTO `role_permission` VALUES (1, 6);
INSERT INTO `role_permission` VALUES (2, 1);
INSERT INTO `role_permission` VALUES (2, 4);
$ mkdir egg-cms && cd egg-cms
$ cnpm init egg --type=simple
$ cnpm i
$ cd egg-cms
$ npm install egg-sequelize mysql2 egg-jwt egg-redis --save
'use strict';
module.exports = {
  sequelize: {
    enable: true,
    package: "egg-sequelize"
  },
  jwt: {
    enable: true,
    package: "egg-jwt"
  },
  redis: {
    enable: true,
    package: "egg-redis"
  }
};
/* eslint valid-jsdoc: "off" */
'use strict';
/**
 * @param {Egg.EggAppInfo} appInfo app info
 */
module.exports = appInfo => {
  /**
   * built-in config
   * @type {Egg.EggAppConfig}
   **/
  const config = exports = {};
  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1588409777990_9883';
  // add your middleware config here
  config.middleware = [];
  // add your user config here
  const userConfig = {
+    security: {
+      csrf: false
+    },
+    sequelize: {
+      dialect: "mysql",
+      host: "localhost",
+      port: "3306",
+      database: "egg-cms",
+      username: "root",
+      password: "5f8b8a5d650637f8"
+    },
+    redis: {
+      client: {
+        port: 6379,          // Redis port
+        host: '127.0.0.1',   // Redis host
+        password: 'auth',
+        db: 0,
+      },
+    }
  };
  return {
    ...config,
    ...userConfig,
  };
};
app\model\user.js
module.exports = app => {
    const { STRING, INTEGER, DATE } = app.Sequelize;
    const User = app.model.define("user", {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        userName: STRING(30),
        password: STRING(30),
        created_at: DATE,
        updated_at: DATE
    });
    return User;
};
app\controller\home.js
'use strict';
const Controller = require('egg').Controller;
class HomeController extends Controller {
  async index() {
    const { ctx } = this;
    ctx.body = 'hi, egg';
  }
  async currentUser() {
    const { ctx } = this;
    const { user } = ctx.state;
    this.ctx.body = {
      name: user.userName,
      avatar: 'http://img.zhufengpeixun.cn/tuizi.jpg',
      userid: user.id
    }
  }
  async login() {
    const { ctx, app } = this;
    let { userName, password } = ctx.request.body;
    const users = await ctx.model.User.findAll({
      where: { userName, password },
      limit: 1
    });
    if (users.length > 0) {
      let user = users[0];
      ctx.status = 200;
      const token = app.jwt.sign(
        {
          id: user.id,
          userName: user.userName
        },
        app.config.jwt.secret,
        {
          expiresIn: "1h"
        }
      );
      await app.redis.set(`token_${user.id}`, token);
      ctx.body = {
        status: 'ok',
        type: 'account',
        currentAuthority: 'admin',
        token
      }
    } else {
      ctx.body = {
        status: 'error'
      }
    }
  }
}
module.exports = HomeController;
app\router.js
module.exports = app => {
  const { router, controller, jwt } = app;
  router.get('/', controller.home.index);
+  router.post('/api/login/account', controller.home.login);
+  router.get('/api/currentUser', jwt, controller.home.currentUser);
};
app.js
class AppBootHook {
    constructor(app) {
        this.app = app;
    }
    async willReady() {
        await this.app.model.sync({ logging: console.log, force: true });
        await this.app.model.query(
            "INSERT INTO users (user_name, password) VALUES ('admin', '123456')"
        );
    }
}
module.exports = AppBootHook;
umi -v
cnpm create umi
config\config.ts
export default {
  dev: {
+    '/server/api/': {
+      target: 'http://127.0.0.1:7001',
+      changeOrigin: true,
+      pathRewrite: { '^/server': '' },
+    },
    /* '/api/': {
      target: 'https://preview.pro.ant.design',
      changeOrigin: true,
      pathRewrite: { '^': '' },
    }, */
  },
  test: {
    '/api/': {
      target: 'https://preview.pro.ant.design',
      changeOrigin: true,
      pathRewrite: { '^': '' },
    },
  },
  pre: {
    '/api/': {
      target: 'your pre url',
      changeOrigin: true,
      pathRewrite: { '^': '' },
    },
  },
};
src\services\login.ts
export async function fakeAccountLogin(params: LoginParamsType) {
+  return request('/server/api/login/account', {
    method: 'POST',
    data: params,
  });
}
src\services\user.ts
export async function queryCurrent(): Promise<any> {
+  return request('/server/api/currentUser');
}
src\models\login.ts
effects: {
    *login({ payload }, { call, put }) {
      const response = yield call(fakeAccountLogin, payload);
      yield put({
        type: 'changeLoginStatus',
        payload: response,
      });
      // Login successfully
      if (response.status === 'ok') {
+        if (response.token) {
+          localStorage.setItem('token', response.token);
+        }
        const urlParams = new URL(window.location.href);
src\utils\request.ts
const request = extend({
  errorHandler, // 默认错误处理
  credentials: 'include', // 默认请求是否带上cookie
});
+request.interceptors.request.use((url: any, options: any) => {
+  if (localStorage.getItem('token')) {
+    options.headers.Authorization = 'Bearer ' + localStorage.getItem('token')
+  }
+  return { url, options };
+});
export default request;