mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: 目录及代码优化调整
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -9,7 +9,7 @@
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}/../main.go",
|
||||
"program": "${fileDirname}/main.go",
|
||||
"env": {},
|
||||
"args": []
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package model
|
||||
package biz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"reflect"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,12 @@ func IsTrue(exp bool, msg string, params ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
func IsTrueBy(exp bool, err BizError) {
|
||||
if !exp {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func NotEmpty(str string, msg string, params ...interface{}) {
|
||||
if str == "" {
|
||||
panic(NewBizErr(fmt.Sprintf(msg, params...)))
|
||||
41
base/biz/bizerror.go
Normal file
41
base/biz/bizerror.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package biz
|
||||
|
||||
// 业务错误
|
||||
type BizError struct {
|
||||
code int16
|
||||
err string
|
||||
}
|
||||
|
||||
const (
|
||||
SuccessCode = 200
|
||||
SuccessMsg = "success"
|
||||
|
||||
BizErrorCode = 400
|
||||
BizErrorMsg = "error"
|
||||
|
||||
ServerErrorCode = 500
|
||||
ServerErrorMsg = "server error"
|
||||
|
||||
TokenErrorCode = 501
|
||||
TokenErrorMsg = "token error"
|
||||
)
|
||||
|
||||
// 错误消息
|
||||
func (e *BizError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// 错误码
|
||||
func (e *BizError) Code() int16 {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBizErr(msg string) *BizError {
|
||||
return &BizError{code: BizErrorCode, err: msg}
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,可设置指定错误code
|
||||
func NewBizErrCode(code int16, msg string) *BizError {
|
||||
return &BizError{code: code, err: msg}
|
||||
}
|
||||
@@ -2,12 +2,11 @@ package base
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/mlog"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/token"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/beego/beego/v2/core/validation"
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
@@ -25,7 +24,7 @@ type operationFunc func(loginAccount *ctx.LoginAccount)
|
||||
// 将请求体的json赋值给指定的结构体
|
||||
func (c *Controller) UnmarshalBody(data interface{}) {
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, data)
|
||||
model.BizErrIsNil(err, "request body解析错误")
|
||||
biz.BizErrIsNil(err, "request body解析错误")
|
||||
}
|
||||
|
||||
// 校验表单数据
|
||||
@@ -37,7 +36,7 @@ func (c *Controller) validForm(form interface{}) {
|
||||
}
|
||||
if !b {
|
||||
e := valid.Errors[0]
|
||||
panic(model.NewBizErr(e.Field + " " + e.Message))
|
||||
panic(biz.NewBizErr(e.Field + " " + e.Message))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,72 +47,61 @@ func (c *Controller) UnmarshalBodyAndValid(data interface{}) {
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
// @param checkToken 是否校验token
|
||||
// @param reqCtx 请求上下文
|
||||
// @param getData 获取数据的回调函数
|
||||
func (c *Controller) ReturnData(checkToken bool, getData getDataFunc) {
|
||||
func (c *Controller) ReturnData(reqCtx *ctx.ReqCtx, getData getDataFunc) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctx.ApplyAfterHandler(reqCtx, err.(error))
|
||||
c.parseErr(err)
|
||||
} else {
|
||||
// 应用所有请求后置处理器
|
||||
ctx.ApplyAfterHandler(reqCtx, nil)
|
||||
}
|
||||
}()
|
||||
var loginAccount *ctx.LoginAccount
|
||||
if checkToken {
|
||||
loginAccount = c.CheckToken()
|
||||
reqCtx.Req = c.Ctx.Request
|
||||
// 调用请求前所有处理器
|
||||
err := ctx.ApplyBeforeHandler(reqCtx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Success(getData(loginAccount))
|
||||
}
|
||||
|
||||
// 返回数据
|
||||
// @param checkToken 是否校验token
|
||||
// @param getData 获取数据的回调函数
|
||||
func (c *Controller) ReturnDataWithPermisison(permission ctx.Permission, getData getDataFunc) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.parseErr(err)
|
||||
}
|
||||
}()
|
||||
var logMsg string
|
||||
var loginAccount *ctx.LoginAccount
|
||||
if permission.CheckToken {
|
||||
loginAccount = c.CheckToken()
|
||||
logMsg = fmt.Sprintf("[uid=%d, uname=%s]\n", loginAccount.Id, loginAccount.Username)
|
||||
}
|
||||
c.Success(getData(loginAccount))
|
||||
logs.Info(logMsg)
|
||||
resp := getData(reqCtx.LoginAccount)
|
||||
c.Success(resp)
|
||||
reqCtx.RespObj = resp
|
||||
}
|
||||
|
||||
// 无返回数据的操作,如新增修改等无需返回数据的操作
|
||||
// @param checkToken 是否校验token
|
||||
func (c *Controller) Operation(checkToken bool, operation operationFunc) {
|
||||
// @param reqCtx 请求上下文
|
||||
func (c *Controller) Operation(reqCtx *ctx.ReqCtx, operation operationFunc) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctx.ApplyAfterHandler(reqCtx, err.(error))
|
||||
c.parseErr(err)
|
||||
} else {
|
||||
ctx.ApplyAfterHandler(reqCtx, nil)
|
||||
}
|
||||
}()
|
||||
var loginAccount *ctx.LoginAccount
|
||||
if checkToken {
|
||||
loginAccount = c.CheckToken()
|
||||
reqCtx.Req = c.Ctx.Request
|
||||
// 调用请求前所有处理器
|
||||
err := ctx.ApplyBeforeHandler(reqCtx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
operation(loginAccount)
|
||||
c.SuccessNoData()
|
||||
}
|
||||
|
||||
// 校验token,并返回登录者账号信息
|
||||
func (c *Controller) CheckToken() *ctx.LoginAccount {
|
||||
tokenStr := c.Ctx.Input.Header("Authorization")
|
||||
loginAccount, err := token.ParseToken(tokenStr)
|
||||
if err != nil || loginAccount == nil {
|
||||
panic(model.NewBizErrCode(model.TokenErrorCode, model.TokenErrorMsg))
|
||||
}
|
||||
return loginAccount
|
||||
operation(reqCtx.LoginAccount)
|
||||
c.SuccessNoData()
|
||||
|
||||
// 不记录返回结果
|
||||
reqCtx.RespObj = 0
|
||||
}
|
||||
|
||||
// 获取分页参数
|
||||
func (c *Controller) GetPageParam() *model.PageParam {
|
||||
pn, err := c.GetInt("pageNum", 1)
|
||||
model.BizErrIsNil(err, "pageNum参数错误")
|
||||
biz.BizErrIsNil(err, "pageNum参数错误")
|
||||
ps, serr := c.GetInt("pageSize", 10)
|
||||
model.BizErrIsNil(serr, "pageSize参数错误")
|
||||
biz.BizErrIsNil(serr, "pageSize参数错误")
|
||||
return &model.PageParam{PageNum: pn, PageSize: ps}
|
||||
}
|
||||
|
||||
@@ -134,7 +122,7 @@ func (c *Controller) SuccessNoData() {
|
||||
}
|
||||
|
||||
// 返回业务错误
|
||||
func (c *Controller) BizError(bizError model.BizError) {
|
||||
func (c *Controller) BizError(bizError *biz.BizError) {
|
||||
c.Result(model.Error(bizError.Code(), bizError.Error()))
|
||||
}
|
||||
|
||||
@@ -146,20 +134,20 @@ func (c *Controller) ServerError() {
|
||||
// 解析error,并对不同error返回不同result
|
||||
func (c *Controller) parseErr(err interface{}) {
|
||||
switch t := err.(type) {
|
||||
case model.BizError:
|
||||
case *biz.BizError:
|
||||
c.BizError(t)
|
||||
break
|
||||
case error:
|
||||
c.ServerError()
|
||||
logs.Error(t)
|
||||
mlog.Log.Error(t)
|
||||
panic(err)
|
||||
//break
|
||||
case string:
|
||||
c.ServerError()
|
||||
logs.Error(t)
|
||||
mlog.Log.Error(t)
|
||||
panic(err)
|
||||
//break
|
||||
default:
|
||||
logs.Error(t)
|
||||
mlog.Log.Error(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package ctx
|
||||
|
||||
type AppContext struct {
|
||||
}
|
||||
|
||||
type LoginAccount struct {
|
||||
Id uint64
|
||||
Username string
|
||||
|
||||
62
base/ctx/req_ctx.go
Normal file
62
base/ctx/req_ctx.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ReqCtx struct {
|
||||
Req *http.Request
|
||||
NeedToken bool // 是否需要token
|
||||
LoginAccount *LoginAccount // 登录账号信息
|
||||
|
||||
// 日志相关信息
|
||||
NeedLog bool // 是否需要记录日志
|
||||
LogResp bool // 是否记录返回结果
|
||||
Description string // 请求描述
|
||||
ReqParam interface{} // 请求参数
|
||||
RespObj interface{} // 响应结果
|
||||
}
|
||||
|
||||
// 请求前置处理器
|
||||
type BeforeHandler interface {
|
||||
Handler(rc *ReqCtx) error
|
||||
}
|
||||
|
||||
// 请求后置处理器
|
||||
type AfterHandler interface {
|
||||
Handler(rc *ReqCtx, err error)
|
||||
}
|
||||
|
||||
var (
|
||||
BeforeHandlers []BeforeHandler
|
||||
AfterHandlers []AfterHandler
|
||||
)
|
||||
|
||||
// 应用所有请求前置处理器
|
||||
func ApplyBeforeHandler(rc *ReqCtx) error {
|
||||
for _, e := range BeforeHandlers {
|
||||
err := e.Handler(rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 应用所有后置处理器
|
||||
func ApplyAfterHandler(rc *ReqCtx, err error) {
|
||||
for _, e := range AfterHandlers {
|
||||
e.Handler(rc, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 新建请求上下文,默认为需要记录日志
|
||||
// @param needToken 是否需要token才可访问
|
||||
// @param description 请求描述
|
||||
func NewReqCtx(needToken bool, description string) *ReqCtx {
|
||||
return &ReqCtx{NeedToken: needToken, Description: description, NeedLog: true}
|
||||
}
|
||||
|
||||
func NewNoLogReqCtx(needToken bool) *ReqCtx {
|
||||
return &ReqCtx{NeedToken: needToken, NeedLog: false}
|
||||
}
|
||||
86
base/ctx/req_log_handler.go
Normal file
86
base/ctx/req_log_handler.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/mlog"
|
||||
"mayfly-go/base/utils"
|
||||
"reflect"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// customFormatter := new(log.TextFormatter)
|
||||
// customFormatter.TimestampFormat = "2006-01-02 15:04:05.000"
|
||||
// customFormatter.FullTimestamp = true
|
||||
log.SetFormatter(new(mlog.LogFormatter))
|
||||
log.SetReportCaller(true)
|
||||
|
||||
AfterHandlers = append(AfterHandlers, new(LogHandler))
|
||||
}
|
||||
|
||||
type LogHandler struct{}
|
||||
|
||||
func (l *LogHandler) Handler(rc *ReqCtx, err error) {
|
||||
if !rc.NeedLog {
|
||||
return
|
||||
}
|
||||
|
||||
lfs := log.Fields{}
|
||||
if la := rc.LoginAccount; la != nil {
|
||||
lfs["uid"] = la.Id
|
||||
lfs["uname"] = la.Username
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// lfs["errMsg"] = err.Error()
|
||||
|
||||
// switch t := err.(type) {
|
||||
// case *biz.BizError:
|
||||
// lfs["errCode"] = t.Code()
|
||||
// break
|
||||
// default:
|
||||
// }
|
||||
log.WithFields(lfs).Error(getErrMsg(rc, err))
|
||||
return
|
||||
}
|
||||
|
||||
// rb, _ := json.Marshal(rc.ReqParam)
|
||||
// lfs["req"] = string(rb)
|
||||
// // 返回结果不为空,则记录返回结果
|
||||
// if rc.LogResp && !utils.IsBlank(reflect.ValueOf(rc.RespObj)) {
|
||||
// respB, _ := json.Marshal(rc.RespObj)
|
||||
// lfs["resp"] = string(respB)
|
||||
// }
|
||||
log.WithFields(lfs).Info(getLogMsg(rc))
|
||||
}
|
||||
|
||||
func getLogMsg(rc *ReqCtx) string {
|
||||
msg := rc.Description
|
||||
rb, _ := json.Marshal(rc.ReqParam)
|
||||
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
|
||||
// 返回结果不为空,则记录返回结果
|
||||
if rc.LogResp && !utils.IsBlank(reflect.ValueOf(rc.RespObj)) {
|
||||
respB, _ := json.Marshal(rc.RespObj)
|
||||
msg = msg + fmt.Sprintf("\n<-- %s", string(respB))
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func getErrMsg(rc *ReqCtx, err error) string {
|
||||
msg := rc.Description
|
||||
rb, _ := json.Marshal(rc.ReqParam)
|
||||
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
|
||||
|
||||
var errMsg string
|
||||
switch t := err.(type) {
|
||||
case *biz.BizError:
|
||||
errMsg = fmt.Sprintf("\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())
|
||||
break
|
||||
default:
|
||||
errMsg = fmt.Sprintf("\n<-e errMsg: %s", t.Error())
|
||||
}
|
||||
return (msg + errMsg)
|
||||
}
|
||||
29
base/ctx/req_permission_handler.go
Normal file
29
base/ctx/req_permission_handler.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"mayfly-go/base/biz"
|
||||
)
|
||||
|
||||
func init() {
|
||||
BeforeHandlers = append(BeforeHandlers, new(PermissionHandler))
|
||||
}
|
||||
|
||||
var permissionError = biz.NewBizErrCode(501, "token error")
|
||||
|
||||
type PermissionHandler struct{}
|
||||
|
||||
func (p *PermissionHandler) Handler(rc *ReqCtx) error {
|
||||
if !rc.NeedToken {
|
||||
return nil
|
||||
}
|
||||
tokenStr := rc.Req.Header.Get("Authorization")
|
||||
if tokenStr == "" {
|
||||
return permissionError
|
||||
}
|
||||
loginAccount, err := ParseToken(tokenStr)
|
||||
if err != nil || loginAccount == nil {
|
||||
return permissionError
|
||||
}
|
||||
rc.LoginAccount = loginAccount
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package token
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/model"
|
||||
|
||||
"mayfly-go/base/biz"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
@@ -26,12 +26,12 @@ func CreateToken(userId uint64, username string) string {
|
||||
|
||||
// 使用自定义字符串加密 and get the complete encoded token as a string
|
||||
tokenString, err := token.SignedString([]byte(JwtKey))
|
||||
model.BizErrIsNil(err, "token创建失败")
|
||||
biz.BizErrIsNil(err, "token创建失败")
|
||||
return tokenString
|
||||
}
|
||||
|
||||
// 解析token,并返回登录者账号信息
|
||||
func ParseToken(tokenStr string) (*ctx.LoginAccount, error) {
|
||||
func ParseToken(tokenStr string) (*LoginAccount, error) {
|
||||
if tokenStr == "" {
|
||||
return nil, errors.New("token error")
|
||||
}
|
||||
@@ -43,5 +43,5 @@ func ParseToken(tokenStr string) (*ctx.LoginAccount, error) {
|
||||
return nil, err
|
||||
}
|
||||
i := token.Claims.(jwt.MapClaims)
|
||||
return &ctx.LoginAccount{Id: uint64(i["id"].(float64)), Username: i["username"].(string)}, nil
|
||||
return &LoginAccount{Id: uint64(i["id"].(float64)), Username: i["username"].(string)}, nil
|
||||
}
|
||||
37
base/mlog/mlog.go
Normal file
37
base/mlog/mlog.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var Log = logrus.New()
|
||||
|
||||
func init() {
|
||||
// customFormatter := new(logrus.TextFormatter)
|
||||
// customFormatter.TimestampFormat = "2006-01-02 15:04:05.000"
|
||||
// customFormatter.FullTimestamp = true
|
||||
Log.SetFormatter(new(LogFormatter))
|
||||
Log.SetReportCaller(true)
|
||||
}
|
||||
|
||||
type LogFormatter struct{}
|
||||
|
||||
func (l *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
timestamp := time.Now().Local().Format("2006-01-02 15:04:05.000")
|
||||
|
||||
level := entry.Level
|
||||
logMsg := fmt.Sprintf("%s [%s]", timestamp, strings.ToUpper(level.String()))
|
||||
// 如果存在调用信息,且为error级别以上记录文件及行号
|
||||
if caller := entry.Caller; caller != nil && level <= logrus.ErrorLevel {
|
||||
logMsg = logMsg + fmt.Sprintf(" [%s:%d]", caller.File, caller.Line)
|
||||
}
|
||||
for k, v := range entry.Data {
|
||||
logMsg = logMsg + fmt.Sprintf(" [%s=%v]", k, v)
|
||||
}
|
||||
logMsg = logMsg + fmt.Sprintf(" : %s\n", entry.Message)
|
||||
return []byte(logMsg), nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package model
|
||||
|
||||
// 业务错误
|
||||
type BizError struct {
|
||||
code int16
|
||||
err string
|
||||
}
|
||||
|
||||
// 错误消息
|
||||
func (e *BizError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// 错误码
|
||||
func (e *BizError) Code() int16 {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBizErr(msg string) BizError {
|
||||
return BizError{code: BizErrorCode, err: msg}
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,可设置指定错误code
|
||||
func NewBizErrCode(code int16, msg string) BizError {
|
||||
return BizError{code: code, err: msg}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/utils"
|
||||
"reflect"
|
||||
@@ -125,9 +126,9 @@ func GetPage(seter orm.QuerySeter, pageParam *PageParam, models interface{}, toM
|
||||
return PageResult{Total: 0, List: nil}
|
||||
}
|
||||
_, qerr := seter.Limit(pageParam.PageSize, pageParam.PageNum-1).All(models, getFieldNames(toModels)...)
|
||||
BizErrIsNil(qerr, "查询错误")
|
||||
biz.BizErrIsNil(qerr, "查询错误")
|
||||
err := utils.Copy(toModels, models)
|
||||
BizErrIsNil(err, "实体转换错误")
|
||||
biz.BizErrIsNil(err, "实体转换错误")
|
||||
return PageResult{Total: count, List: toModels}
|
||||
}
|
||||
|
||||
@@ -176,7 +177,7 @@ func GetListBySql(sql string, params ...interface{}) *[]orm.Params {
|
||||
func GetList(seter orm.QuerySeter, model interface{}, toModel interface{}) {
|
||||
_, _ = seter.All(model, getFieldNames(toModel)...)
|
||||
err := utils.Copy(toModel, model)
|
||||
BizErrIsNil(err, "实体转换错误")
|
||||
biz.BizErrIsNil(err, "实体转换错误")
|
||||
}
|
||||
|
||||
// 根据toModel结构体字段查询单条记录,并将值赋值给toModel
|
||||
@@ -186,7 +187,7 @@ func GetOne(seter orm.QuerySeter, model interface{}, toModel interface{}) error
|
||||
return err
|
||||
}
|
||||
cerr := utils.Copy(toModel, model)
|
||||
BizErrIsNil(cerr, "实体转换错误")
|
||||
biz.BizErrIsNil(cerr, "实体转换错误")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/controllers/vo"
|
||||
"mayfly-go/models"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type AccountDetailVO struct {
|
||||
Id int64
|
||||
Username string
|
||||
}
|
||||
|
||||
func init() {
|
||||
orm.RegisterDriver("mysql", orm.DRMySQL)
|
||||
|
||||
orm.RegisterDataBase("default", "mysql", "root:111049@tcp(localhost:3306)/mayfly-go?charset=utf8")
|
||||
orm.Debug = true
|
||||
}
|
||||
|
||||
func TestGetList(t *testing.T) {
|
||||
query := QuerySetter(new(models.Account)).OrderBy("-Id")
|
||||
list := new([]AccountDetailVO)
|
||||
GetList(query, new([]models.Account), list)
|
||||
fmt.Println(list)
|
||||
}
|
||||
|
||||
func TestGetOne(t *testing.T) {
|
||||
model := new(models.Account)
|
||||
query := QuerySetter(model).Filter("Id", 2)
|
||||
adv := new(AccountDetailVO)
|
||||
GetOne(query, model, adv)
|
||||
fmt.Println(adv)
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
//o := getOrm()
|
||||
//
|
||||
////v := new([]Account)
|
||||
//var maps []orm.Params
|
||||
//_, err := o.Raw("SELECT a.Id, a.Username, r.Id AS 'Role.Id', r.Name AS 'Role.Name' FROM " +
|
||||
// "t_account a JOIN t_role r ON a.id = r.account_id").Values(&maps)
|
||||
//fmt.Println(err)
|
||||
//////res := new([]Account)
|
||||
////model := &Account{}
|
||||
////o.QueryTable("t_account").Filter("id", 1).RelatedSel().One(model)
|
||||
////o.LoadRelated(model, "Role")
|
||||
res := new([]vo.AccountVO)
|
||||
sql := "SELECT a.Id, a.Username, r.Id AS 'Role.Id', r.Name AS 'Role.Name' FROM t_account a JOIN t_role r ON a.id = r.account_id"
|
||||
//limitSql := sql + " LIMIT 1, 3"
|
||||
//selectIndex := strings.Index(sql, "SELECT ") + 7
|
||||
//fromIndex := strings.Index(sql, " FROM")
|
||||
//selectCol := sql[selectIndex:fromIndex]
|
||||
//countSql := strings.Replace(sql, selectCol, "COUNT(*)", 1)
|
||||
//fmt.Println(limitSql)
|
||||
//fmt.Println(selectCol)
|
||||
//fmt.Println(countSql)
|
||||
page := GetPageBySql(sql, res, &PageParam{PageNum: 1, PageSize: 1})
|
||||
fmt.Println(page)
|
||||
//return res
|
||||
}
|
||||
|
||||
func TestCase2Camel(t *testing.T) {
|
||||
fmt.Println(utils.Case2Camel("create_time"))
|
||||
fmt.Println(strings.Title("username"))
|
||||
}
|
||||
64
base/rediscli/rediscli.go
Normal file
64
base/rediscli/rediscli.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package rediscli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
var cli *redis.Client
|
||||
|
||||
func SetCli(client *redis.Client) {
|
||||
cli = client
|
||||
}
|
||||
|
||||
func GetCli() *redis.Client {
|
||||
return cli
|
||||
}
|
||||
|
||||
// get key value
|
||||
func Get(key string) string {
|
||||
val, err := cli.Get(key).Result()
|
||||
switch {
|
||||
case err == redis.Nil:
|
||||
fmt.Println("key does not exist")
|
||||
case err != nil:
|
||||
fmt.Println("Get failed", err)
|
||||
case val == "":
|
||||
fmt.Println("value is empty")
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// set key value
|
||||
func Set(key string, val string, expiration time.Duration) {
|
||||
cli.Set(key, val, expiration)
|
||||
}
|
||||
|
||||
func HSet(key string, field string, val interface{}) {
|
||||
cli.HSet(key, field, val)
|
||||
}
|
||||
|
||||
// hget
|
||||
func HGet(key string, field string) string {
|
||||
val, _ := cli.HGet(key, field).Result()
|
||||
return val
|
||||
}
|
||||
|
||||
// hget
|
||||
func HExist(key string, field string) bool {
|
||||
val, _ := cli.HExists(key, field).Result()
|
||||
return val
|
||||
}
|
||||
|
||||
// hgetall
|
||||
func HGetAll(key string) map[string]string {
|
||||
vals, _ := cli.HGetAll(key).Result()
|
||||
return vals
|
||||
}
|
||||
|
||||
// hdel
|
||||
func HDel(key string, fields ...string) int {
|
||||
return int(cli.HDel(key, fields...).Val())
|
||||
}
|
||||
22
base/utils/yml/yml.go
Normal file
22
base/utils/yml/yml.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package yml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 从指定路径加载yaml文件
|
||||
func LoadYml(path string, out interface{}) error {
|
||||
yamlFileBytes, readErr := ioutil.ReadFile(path)
|
||||
if readErr != nil {
|
||||
return readErr
|
||||
}
|
||||
// yaml解析
|
||||
err := yaml.Unmarshal(yamlFileBytes, out)
|
||||
if err != nil {
|
||||
return errors.New("无法解析 [" + path + "]")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3,7 +3,7 @@ httpport = 8888
|
||||
copyrequestbody = true
|
||||
autorender = false
|
||||
EnableErrorsRender = false
|
||||
runmode = "dev"
|
||||
runmode = "prod"
|
||||
; mysqluser = "root"
|
||||
; mysqlpass = "111049"
|
||||
; mysqlurls = "127.0.0.1"
|
||||
@@ -16,6 +16,6 @@ AdminHttpPort = 8088
|
||||
[dev]
|
||||
httpport = 8888
|
||||
[prod]
|
||||
httpport = 8080
|
||||
httpport = 8888
|
||||
[test]
|
||||
httpport = 8888
|
||||
@@ -2,11 +2,11 @@ package controllers
|
||||
|
||||
import (
|
||||
"mayfly-go/base"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/token"
|
||||
"mayfly-go/controllers/form"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/devops/controllers/form"
|
||||
"mayfly-go/devops/models"
|
||||
)
|
||||
|
||||
type AccountController struct {
|
||||
@@ -20,14 +20,14 @@ type AccountController struct {
|
||||
|
||||
// @router /accounts/login [post]
|
||||
func (c *AccountController) Login() {
|
||||
c.ReturnData(false, func(la *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewReqCtx(false, "用户登录"), func(la *ctx.LoginAccount) interface{} {
|
||||
loginForm := &form.LoginForm{}
|
||||
c.UnmarshalBodyAndValid(loginForm)
|
||||
|
||||
a := &models.Account{Username: loginForm.Username, Password: loginForm.Password}
|
||||
model.BizErrIsNil(model.GetBy(a, "Username", "Password"), "用户名或密码错误")
|
||||
biz.BizErrIsNil(model.GetBy(a, "Username", "Password"), "用户名或密码错误")
|
||||
return map[string]interface{}{
|
||||
"token": token.CreateToken(a.Id, a.Username),
|
||||
"token": ctx.CreateToken(a.Id, a.Username),
|
||||
"username": a.Username,
|
||||
}
|
||||
})
|
||||
@@ -35,7 +35,7 @@ func (c *AccountController) Login() {
|
||||
|
||||
// @router /accounts [get]
|
||||
func (c *AccountController) Accounts() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewReqCtx(true, "获取账号列表"), func(account *ctx.LoginAccount) interface{} {
|
||||
//s := c.GetString("username")
|
||||
//query := models.QuerySetter(new(models.Account)).OrderBy("-Id").RelatedSel()
|
||||
//return models.GetPage(query, c.GetPageParam(), new([]models.Account), new([]vo.AccountVO))
|
||||
@@ -3,12 +3,13 @@ package controllers
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/base"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/controllers/form"
|
||||
"mayfly-go/controllers/vo"
|
||||
"mayfly-go/db"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/devops/controllers/form"
|
||||
"mayfly-go/devops/controllers/vo"
|
||||
"mayfly-go/devops/db"
|
||||
"mayfly-go/devops/models"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ type DbController struct {
|
||||
|
||||
// @router /api/dbs [get]
|
||||
func (c *DbController) Dbs() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
m := new([]models.Db)
|
||||
querySetter := model.QuerySetter(new(models.Db))
|
||||
return model.GetPage(querySetter, c.GetPageParam(), m, new([]vo.SelectDataDbVO))
|
||||
@@ -27,12 +28,14 @@ func (c *DbController) Dbs() {
|
||||
|
||||
// @router /api/db/:dbId/select [get]
|
||||
func (c *DbController) SelectData() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
rc := ctx.NewReqCtx(true, "执行数据库查询语句")
|
||||
c.ReturnData(rc, func(account *ctx.LoginAccount) interface{} {
|
||||
selectSql := c.GetString("selectSql")
|
||||
model.NotEmpty(selectSql, "selectSql不能为空")
|
||||
rc.ReqParam = selectSql
|
||||
biz.NotEmpty(selectSql, "selectSql不能为空")
|
||||
res, err := db.GetDbInstance(c.GetDbId()).SelectData(selectSql)
|
||||
if err != nil {
|
||||
panic(model.NewBizErr(fmt.Sprintf("查询失败: %s", err.Error())))
|
||||
panic(biz.NewBizErr(fmt.Sprintf("查询失败: %s", err.Error())))
|
||||
}
|
||||
return res
|
||||
})
|
||||
@@ -40,12 +43,12 @@ func (c *DbController) SelectData() {
|
||||
|
||||
// @router /api/db/:dbId/exec-sql [post]
|
||||
func (c *DbController) ExecSql() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewReqCtx(true, "sql执行"), func(account *ctx.LoginAccount) interface{} {
|
||||
selectSql := c.GetString("sql")
|
||||
model.NotEmpty(selectSql, "sql不能为空")
|
||||
biz.NotEmpty(selectSql, "sql不能为空")
|
||||
num, err := db.GetDbInstance(c.GetDbId()).Exec(selectSql)
|
||||
if err != nil {
|
||||
panic(model.NewBizErr(fmt.Sprintf("执行失败: %s", err.Error())))
|
||||
panic(biz.NewBizErr(fmt.Sprintf("执行失败: %s", err.Error())))
|
||||
}
|
||||
return num
|
||||
})
|
||||
@@ -53,16 +56,16 @@ func (c *DbController) ExecSql() {
|
||||
|
||||
// @router /api/db/:dbId/t-metadata [get]
|
||||
func (c *DbController) TableMA() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
return db.GetDbInstance(c.GetDbId()).GetTableMetedatas()
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/db/:dbId/c-metadata [get]
|
||||
func (c *DbController) ColumnMA() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
tn := c.GetString("tableName")
|
||||
model.NotEmpty(tn, "tableName不能为空")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
return db.GetDbInstance(c.GetDbId()).GetColumnMetadatas(tn)
|
||||
})
|
||||
}
|
||||
@@ -70,7 +73,7 @@ func (c *DbController) ColumnMA() {
|
||||
// @router /api/db/:dbId/hint-tables [get]
|
||||
// 数据表及字段前端提示接口
|
||||
func (c *DbController) HintTables() {
|
||||
c.ReturnData(false, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
dbi := db.GetDbInstance(c.GetDbId())
|
||||
tables := dbi.GetTableMetedatas()
|
||||
res := make(map[string][]string)
|
||||
@@ -94,14 +97,16 @@ func (c *DbController) HintTables() {
|
||||
|
||||
// @router /api/db/:dbId/sql [post]
|
||||
func (c *DbController) SaveSql() {
|
||||
c.Operation(true, func(account *ctx.LoginAccount) {
|
||||
rc := ctx.NewReqCtx(true, "保存sql内容")
|
||||
c.Operation(rc, func(account *ctx.LoginAccount) {
|
||||
dbSqlForm := &form.DbSqlSaveForm{}
|
||||
c.UnmarshalBodyAndValid(dbSqlForm)
|
||||
rc.ReqParam = dbSqlForm
|
||||
|
||||
dbId := c.GetDbId()
|
||||
// 判断dbId是否存在
|
||||
err := model.GetById(new(models.Db), dbId)
|
||||
model.BizErrIsNil(err, "该数据库信息不存在")
|
||||
biz.BizErrIsNil(err, "该数据库信息不存在")
|
||||
|
||||
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
||||
dbSql := &models.DbSql{Type: dbSqlForm.Type, DbId: dbId}
|
||||
@@ -121,7 +126,7 @@ func (c *DbController) SaveSql() {
|
||||
|
||||
// @router /api/db/:dbId/sql [get]
|
||||
func (c *DbController) GetSql() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
// 获取用于是否有该dbsql的保存记录,有则更改,否则新增
|
||||
dbSql := &models.DbSql{Type: 1, DbId: c.GetDbId()}
|
||||
dbSql.CreatorId = account.Id
|
||||
@@ -135,6 +140,6 @@ func (c *DbController) GetSql() {
|
||||
|
||||
func (c *DbController) GetDbId() uint64 {
|
||||
dbId, _ := strconv.Atoi(c.Ctx.Input.Param(":dbId"))
|
||||
model.IsTrue(dbId > 0, "dbId错误")
|
||||
biz.IsTrue(dbId > 0, "dbId错误")
|
||||
return uint64(dbId)
|
||||
}
|
||||
@@ -2,10 +2,10 @@ package controllers
|
||||
|
||||
import (
|
||||
"mayfly-go/base"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/machine"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/devops/machine"
|
||||
"mayfly-go/devops/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -25,44 +25,47 @@ var upgrader = websocket.Upgrader{
|
||||
}
|
||||
|
||||
func (c *MachineController) Machines() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
return models.GetMachineList(c.GetPageParam())
|
||||
})
|
||||
}
|
||||
|
||||
func (c *MachineController) Run() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
rc := ctx.NewReqCtx(true, "执行机器命令")
|
||||
c.ReturnData(rc, func(account *ctx.LoginAccount) interface{} {
|
||||
cmd := c.GetString("cmd")
|
||||
model.NotEmpty(cmd, "cmd不能为空")
|
||||
biz.NotEmpty(cmd, "cmd不能为空")
|
||||
|
||||
rc.ReqParam = cmd
|
||||
|
||||
res, err := c.getCli().Run(cmd)
|
||||
model.BizErrIsNil(err, "执行命令失败")
|
||||
biz.BizErrIsNil(err, "执行命令失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
// 系统基本信息
|
||||
func (c *MachineController) SysInfo() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
res, err := c.getCli().GetSystemInfo()
|
||||
model.BizErrIsNil(err, "获取系统基本信息失败")
|
||||
biz.BizErrIsNil(err, "获取系统基本信息失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
// top命令信息
|
||||
func (c *MachineController) Top() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
return c.getCli().GetTop()
|
||||
})
|
||||
}
|
||||
|
||||
func (c *MachineController) GetProcessByName() {
|
||||
c.ReturnData(true, func(account *ctx.LoginAccount) interface{} {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
name := c.GetString("name")
|
||||
model.NotEmpty(name, "name不能为空")
|
||||
biz.NotEmpty(name, "name不能为空")
|
||||
res, err := c.getCli().GetProcessByName(name)
|
||||
model.BizErrIsNil(err, "获取失败")
|
||||
biz.BizErrIsNil(err, "获取失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
@@ -70,7 +73,7 @@ func (c *MachineController) GetProcessByName() {
|
||||
func (c *MachineController) WsSSH() {
|
||||
wsConn, err := upgrader.Upgrade(c.Ctx.ResponseWriter, c.Ctx.Request, nil)
|
||||
if err != nil {
|
||||
panic(model.NewBizErr("获取requst responsewirte错误"))
|
||||
panic(biz.NewBizErr("获取requst responsewirte错误"))
|
||||
}
|
||||
|
||||
cols, _ := c.GetInt("cols", 80)
|
||||
@@ -78,7 +81,7 @@ func (c *MachineController) WsSSH() {
|
||||
|
||||
sws, err := machine.NewLogicSshWsSession(cols, rows, c.getCli(), wsConn)
|
||||
if sws == nil {
|
||||
panic(model.NewBizErr("连接失败"))
|
||||
panic(biz.NewBizErr("连接失败"))
|
||||
}
|
||||
//if wshandleError(wsConn, err) {
|
||||
// return
|
||||
@@ -94,12 +97,12 @@ func (c *MachineController) WsSSH() {
|
||||
|
||||
func (c *MachineController) GetMachineId() uint64 {
|
||||
machineId, _ := strconv.Atoi(c.Ctx.Input.Param(":machineId"))
|
||||
model.IsTrue(machineId > 0, "machineId错误")
|
||||
biz.IsTrue(machineId > 0, "machineId错误")
|
||||
return uint64(machineId)
|
||||
}
|
||||
|
||||
func (c *MachineController) getCli() *machine.Cli {
|
||||
cli, err := machine.GetCli(c.GetMachineId())
|
||||
model.BizErrIsNil(err, "获取客户端错误")
|
||||
biz.BizErrIsNil(err, "获取客户端错误")
|
||||
return cli
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/devops/models"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -104,12 +104,12 @@ func GetDbInstance(id uint64) *DbInstance {
|
||||
}
|
||||
}
|
||||
d := models.GetDbById(uint64(id))
|
||||
model.NotNil(d, "数据库信息不存在")
|
||||
biz.NotNil(d, "数据库信息不存在")
|
||||
DB, err := sql.Open(d.Type, getDsn(d))
|
||||
model.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
|
||||
biz.ErrIsNil(err, fmt.Sprintf("Open %s failed, err:%v\n", d.Type, err))
|
||||
perr := DB.Ping()
|
||||
if perr != nil {
|
||||
panic(model.NewBizErr(fmt.Sprintf("数据库连接失败: %s", perr.Error())))
|
||||
panic(biz.NewBizErr(fmt.Sprintf("数据库连接失败: %s", perr.Error())))
|
||||
}
|
||||
|
||||
// 最大连接周期,超过时间的连接就close
|
||||
1
devops/lastupdate.tmp
Executable file
1
devops/lastupdate.tmp
Executable file
@@ -0,0 +1 @@
|
||||
{"/Users/hml/Desktop/project/go/mayfly-go/devops/controllers":1616491109228810023}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/devops/models"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -115,12 +115,12 @@ func (c *Cli) Close() {
|
||||
func (c *Cli) GetSftpCli() *sftp.Client {
|
||||
if c.client == nil {
|
||||
if err := c.connect(); err != nil {
|
||||
panic(model.NewBizErr("连接ssh失败:" + err.Error()))
|
||||
panic(biz.NewBizErr("连接ssh失败:" + err.Error()))
|
||||
}
|
||||
}
|
||||
client, serr := sftp.NewClient(c.client, sftp.MaxPacket(1<<15))
|
||||
if serr != nil {
|
||||
panic(model.NewBizErr("获取sftp client失败:" + serr.Error()))
|
||||
panic(biz.NewBizErr("获取sftp client失败:" + serr.Error()))
|
||||
}
|
||||
return client
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package machine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/models"
|
||||
"mayfly-go/devops/models"
|
||||
"time"
|
||||
|
||||
"github.com/siddontang/go/log"
|
||||
@@ -51,7 +51,7 @@ func getShellContent(name string) string {
|
||||
return cacheShell
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(BasePath + name + ".sh")
|
||||
model.ErrIsNil(err, "获取shell文件失败")
|
||||
biz.ErrIsNil(err, "获取shell文件失败")
|
||||
shellStr := string(bytes)
|
||||
shellCache[name] = shellStr
|
||||
return shellStr
|
||||
@@ -1,7 +1,7 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -93,7 +93,7 @@ func (c *Cli) GetTop() *Top {
|
||||
|
||||
top := &Top{Time: time, Up: up, NowUsers: users, OneMinLoadavg: float32(oneMinLa), FiveMinLoadavg: float32(fiveMinLa), FifteenMinLoadavg: float32(fifMinLa)}
|
||||
err := utils.Map2Struct(resMap, top)
|
||||
model.BizErrIsNil(err, "解析top出错")
|
||||
biz.BizErrIsNil(err, "解析top出错")
|
||||
return top
|
||||
}
|
||||
|
||||
212
devops/machine/ws_shell_session.go
Normal file
212
devops/machine/ws_shell_session.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mayfly-go/base/mlog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type safeBuffer struct {
|
||||
buffer bytes.Buffer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (w *safeBuffer) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Write(p)
|
||||
}
|
||||
func (w *safeBuffer) Bytes() []byte {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.buffer.Bytes()
|
||||
}
|
||||
func (w *safeBuffer) Reset() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
w.buffer.Reset()
|
||||
}
|
||||
|
||||
const (
|
||||
wsMsgCmd = "cmd"
|
||||
wsMsgResize = "resize"
|
||||
)
|
||||
|
||||
type WsMsg struct {
|
||||
Type string `json:"type"`
|
||||
Msg string `json:"msg"`
|
||||
Cols int `json:"cols"`
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
type LogicSshWsSession struct {
|
||||
stdinPipe io.WriteCloser
|
||||
comboOutput *safeBuffer //ssh 终端混合输出
|
||||
logBuff *safeBuffer //保存session的日志
|
||||
inputFilterBuff *safeBuffer //用来过滤输入的命令和ssh_filter配置对比的
|
||||
session *ssh.Session
|
||||
wsConn *websocket.Conn
|
||||
}
|
||||
|
||||
func NewLogicSshWsSession(cols, rows int, cli *Cli, wsConn *websocket.Conn) (*LogicSshWsSession, error) {
|
||||
sshSession, err := cli.GetSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stdinP, err := sshSession.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comboWriter := new(safeBuffer)
|
||||
logBuf := new(safeBuffer)
|
||||
inputBuf := new(safeBuffer)
|
||||
//ssh.stdout and stderr will write output into comboWriter
|
||||
sshSession.Stdout = comboWriter
|
||||
sshSession.Stderr = comboWriter
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1, // disable echo
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
// Request pseudo terminal
|
||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Start remote shell
|
||||
if err := sshSession.Shell(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//sshSession.Run("top")
|
||||
return &LogicSshWsSession{
|
||||
stdinPipe: stdinP,
|
||||
comboOutput: comboWriter,
|
||||
logBuff: logBuf,
|
||||
inputFilterBuff: inputBuf,
|
||||
session: sshSession,
|
||||
wsConn: wsConn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//Close 关闭
|
||||
func (sws *LogicSshWsSession) Close() {
|
||||
if sws.session != nil {
|
||||
sws.session.Close()
|
||||
}
|
||||
if sws.logBuff != nil {
|
||||
sws.logBuff = nil
|
||||
}
|
||||
if sws.comboOutput != nil {
|
||||
sws.comboOutput = nil
|
||||
}
|
||||
}
|
||||
func (sws *LogicSshWsSession) Start(quitChan chan bool) {
|
||||
go sws.receiveWsMsg(quitChan)
|
||||
go sws.sendComboOutput(quitChan)
|
||||
}
|
||||
|
||||
//receiveWsMsg receive websocket msg do some handling then write into ssh.session.stdin
|
||||
func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) {
|
||||
wsConn := sws.wsConn
|
||||
//tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
for {
|
||||
select {
|
||||
case <-exitCh:
|
||||
return
|
||||
default:
|
||||
//read websocket msg
|
||||
_, wsData, err := wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
mlog.Log.Error("reading webSocket message failed: ", err)
|
||||
return
|
||||
}
|
||||
//unmashal bytes into struct
|
||||
msgObj := WsMsg{}
|
||||
if err := json.Unmarshal(wsData, &msgObj); err != nil {
|
||||
mlog.Log.Error("unmarshal websocket message failed:", err)
|
||||
}
|
||||
switch msgObj.Type {
|
||||
case wsMsgResize:
|
||||
//handle xterm.js size change
|
||||
if msgObj.Cols > 0 && msgObj.Rows > 0 {
|
||||
if err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil {
|
||||
mlog.Log.Error("ssh pty change windows size failed")
|
||||
}
|
||||
}
|
||||
case wsMsgCmd:
|
||||
//handle xterm.js stdin
|
||||
//decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd)
|
||||
//if err != nil {
|
||||
// logs.Error("websock cmd string base64 decoding failed")
|
||||
// //panic(base.NewBizErr("websock cmd string base64 decoding failed"))
|
||||
//}
|
||||
sws.sendWebsocketInputCommandToSshSessionStdinPipe([]byte(msgObj.Msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sendWebsocketInputCommandToSshSessionStdinPipe
|
||||
func (sws *LogicSshWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) {
|
||||
if _, err := sws.stdinPipe.Write(cmdBytes); err != nil {
|
||||
mlog.Log.Error("ws cmd bytes write to ssh.stdin pipe failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
|
||||
wsConn := sws.wsConn
|
||||
//todo 优化成一个方法
|
||||
//tells other go routine quit
|
||||
defer setQuit(exitCh)
|
||||
|
||||
//every 120ms write combine output bytes into websocket response
|
||||
tick := time.NewTicker(time.Millisecond * time.Duration(60))
|
||||
//for range time.Tick(120 * time.Millisecond){}
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
if sws.comboOutput == nil {
|
||||
return
|
||||
}
|
||||
bs := sws.comboOutput.Bytes()
|
||||
if len(bs) > 0 {
|
||||
err := wsConn.WriteMessage(websocket.TextMessage, bs)
|
||||
if err != nil {
|
||||
mlog.Log.Error("ssh sending combo output to webSocket failed")
|
||||
}
|
||||
_, err = sws.logBuff.Write(bs)
|
||||
if err != nil {
|
||||
mlog.Log.Error("combo output to log buffer failed")
|
||||
}
|
||||
sws.comboOutput.buffer.Reset()
|
||||
}
|
||||
|
||||
case <-exitCh:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) Wait(quitChan chan bool) {
|
||||
if err := sws.session.Wait(); err != nil {
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
|
||||
func (sws *LogicSshWsSession) LogString() string {
|
||||
return sws.logBuff.buffer.String()
|
||||
}
|
||||
|
||||
func setQuit(ch chan bool) {
|
||||
ch <- true
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "mayfly-go/routers"
|
||||
scheduler "mayfly-go/scheudler"
|
||||
_ "mayfly-go/devops/routers"
|
||||
scheduler "mayfly-go/devops/scheudler"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -20,7 +20,7 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
orm.Debug = true
|
||||
// orm.Debug = true
|
||||
// 跨域配置
|
||||
web.InsertFilter("/**", web.BeforeRouter, cors.Allow(&cors.Options{
|
||||
AllowAllOrigins: true,
|
||||
@@ -39,5 +39,5 @@ func TransparentStatic(ctx *context.Context) {
|
||||
if strings.Index(ctx.Request.URL.Path, "api/") >= 0 {
|
||||
return
|
||||
}
|
||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, "static/"+ctx.Request.URL.Path)
|
||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, "mock-server/static/"+ctx.Request.URL.Path)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/controllers/vo"
|
||||
"mayfly-go/devops/controllers/vo"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
@@ -2,7 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/controllers/vo"
|
||||
"mayfly-go/devops/controllers/vo"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
func init() {
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:AccountController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:AccountController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:AccountController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:AccountController"],
|
||||
beego.ControllerComments{
|
||||
Method: "Accounts",
|
||||
Router: "/accounts",
|
||||
@@ -16,7 +16,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:AccountController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:AccountController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:AccountController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:AccountController"],
|
||||
beego.ControllerComments{
|
||||
Method: "Login",
|
||||
Router: "/accounts/login",
|
||||
@@ -25,7 +25,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "ColumnMA",
|
||||
Router: "/api/db/:dbId/c-metadata",
|
||||
@@ -34,7 +34,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "ExecSql",
|
||||
Router: "/api/db/:dbId/exec-sql",
|
||||
@@ -43,7 +43,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "HintTables",
|
||||
Router: "/api/db/:dbId/hint-tables",
|
||||
@@ -52,7 +52,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "SelectData",
|
||||
Router: "/api/db/:dbId/select",
|
||||
@@ -61,7 +61,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "SaveSql",
|
||||
Router: "/api/db/:dbId/sql",
|
||||
@@ -70,7 +70,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "GetSql",
|
||||
Router: "/api/db/:dbId/sql",
|
||||
@@ -79,7 +79,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "TableMA",
|
||||
Router: "/api/db/:dbId/t-metadata",
|
||||
@@ -88,7 +88,7 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/controllers:DbController"],
|
||||
beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"] = append(beego.GlobalControllerRouter["mayfly-go/devops/controllers:DbController"],
|
||||
beego.ControllerComments{
|
||||
Method: "Dbs",
|
||||
Router: "/api/dbs",
|
||||
@@ -1,7 +1,7 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"mayfly-go/controllers"
|
||||
"mayfly-go/devops/controllers"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
@@ -1,12 +1,11 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"mayfly-go/base/mlog"
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/machine"
|
||||
"mayfly-go/models"
|
||||
|
||||
"github.com/siddontang/go/log"
|
||||
"mayfly-go/devops/machine"
|
||||
"mayfly-go/devops/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -20,14 +19,14 @@ func SaveMachineMonitor() {
|
||||
go func() {
|
||||
cli, err := machine.GetCli(uint64(utils.GetInt4Map(m, "id")))
|
||||
if err != nil {
|
||||
log.Error("获取客户端失败:", err.Error())
|
||||
mlog.Log.Error("获取客户端失败:", err.Error())
|
||||
return
|
||||
}
|
||||
mm := cli.GetMonitorInfo()
|
||||
if mm != nil {
|
||||
_, err := model.Insert(mm)
|
||||
if err != nil {
|
||||
log.Error("保存机器监控信息失败: ", err.Error())
|
||||
mlog.Log.Error("保存机器监控信息失败: ", err.Error())
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -1,7 +1,7 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"mayfly-go/base/model"
|
||||
"mayfly-go/base/biz"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
@@ -23,7 +23,7 @@ func GetCron() *cron.Cron {
|
||||
func AddFun(spec string, cmd func()) cron.EntryID {
|
||||
id, err := c.AddFunc(spec, cmd)
|
||||
if err != nil {
|
||||
panic(model.NewBizErr("添加任务失败:" + err.Error()))
|
||||
panic(biz.NewBizErr("添加任务失败:" + err.Error()))
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
5
go.mod
5
go.mod
@@ -6,13 +6,16 @@ require (
|
||||
github.com/beego/beego/v2 v2.0.1
|
||||
// jwt
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/go-redis/redis v6.14.2+incompatible
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/pkg/sftp v1.12.0
|
||||
// 定时任务
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
// ssh
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
// github.com/go-redis/redis/v8 v8.6.0
|
||||
)
|
||||
|
||||
35
go.sum
35
go.sum
@@ -7,8 +7,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
|
||||
github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
|
||||
github.com/beego/beego/v2 v2.0.1 h1:07a7Z0Ok5vbqyqh+q53sDPl9LdhKh0ZDy3gbyGrhFnE=
|
||||
github.com/beego/beego/v2 v2.0.1/go.mod h1:8zyHi1FnWO1mZLwTn62aKRIZF/aIKvkCBB2JYs+eqQI=
|
||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
||||
@@ -43,19 +41,19 @@ github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3C
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
|
||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godror/godror v0.9.0 h1:IQ+HRUl00B1V03jR4AduWKkKlP9RsTq9E4iEG3gcCZY=
|
||||
github.com/godror/godror v0.9.0/go.mod h1:06LLZykQEXjuUYiVKBa7Ls+AvbUZ8sbtolIALCeL/jw=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -82,22 +80,20 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@@ -114,8 +110,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -125,12 +119,13 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -163,20 +158,15 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE=
|
||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
|
||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -204,8 +194,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -252,8 +240,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
@@ -265,7 +251,6 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -302,8 +287,10 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
ENV = 'production'
|
||||
|
||||
# base api
|
||||
VUE_APP_BASE_API = 'http://localhost:8888/api'
|
||||
VUE_APP_BASE_API = 'http://128.64.98.54:8888/api'
|
||||
@@ -13,6 +13,7 @@
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^4.8.0",
|
||||
"element-ui": "^2.13.2",
|
||||
"jsonlint": "^1.6.3",
|
||||
"sql-formatter": "^2.3.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
|
||||
72
mayfly-go-front/src/common/Utils.ts
Normal file
72
mayfly-go-front/src/common/Utils.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
*/
|
||||
class Utils {
|
||||
/**
|
||||
* 属性拷贝,将一个对象的属性拷贝给另一个对象
|
||||
* @param {Object} source 源对象
|
||||
* @param {Object} target 目标对象
|
||||
*/
|
||||
static copyProperties(source: any, target: any) {
|
||||
for (const k in target) {
|
||||
const value = source[k];
|
||||
if (value) {
|
||||
target[k] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置对象属性为null
|
||||
* @param {Object} target 对象
|
||||
*/
|
||||
static resetProperties(target: any) {
|
||||
for (const k in target) {
|
||||
const value = target[k];
|
||||
if (value != null) {
|
||||
target[k] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Utils
|
||||
|
||||
/**
|
||||
* @description 绑定事件 on(element, event, handler)
|
||||
*/
|
||||
export const on = (function () {
|
||||
if (document.addEventListener != null) {
|
||||
return function (element: any, event: any, handler: any) {
|
||||
if (element && event && handler) {
|
||||
element.addEventListener(event, handler, false);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (element: any, event: any, handler: any) {
|
||||
if (element && event && handler) {
|
||||
element.attachEvent('on' + event, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* @description 解绑事件 off(element, event, handler)
|
||||
*/
|
||||
export const off = (function () {
|
||||
if (document.removeEventListener != null) {
|
||||
return function (element: any, event: any, handler: any) {
|
||||
if (element && event) {
|
||||
element.removeEventListener(event, handler, false);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (element: any, event: any, handler: any) {
|
||||
if (element && event) {
|
||||
element.detachEvent('on' + event, handler);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
@@ -4,7 +4,7 @@ import { ResultEnum } from './enums'
|
||||
import Api from './Api';
|
||||
import { AuthUtils } from './AuthUtils'
|
||||
import config from './config';
|
||||
import ElementUI from 'element-ui';
|
||||
import { Message } from 'element-ui';
|
||||
|
||||
export interface Result {
|
||||
/**
|
||||
@@ -29,7 +29,7 @@ const baseUrl = config.baseApiUrl
|
||||
*/
|
||||
function notifyErrorMsg(msg: string) {
|
||||
// 危险通知
|
||||
ElementUI.Message.error(msg);
|
||||
Message.error(msg);
|
||||
}
|
||||
|
||||
// create an axios instance
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
|
||||
import { Message } from 'element-ui'
|
||||
@Component({
|
||||
name: 'DynamicForm'
|
||||
})
|
||||
@@ -99,7 +100,7 @@ export default class DynamicForm extends Vue {
|
||||
this.submitDisabled = true
|
||||
operation.request(this.form).then(
|
||||
(res: any) => {
|
||||
this.$message.success('保存成功')
|
||||
Message.success('保存成功')
|
||||
this.$emit('submitSuccess', subform)
|
||||
this.submitDisabled = false
|
||||
// this.cancel()
|
||||
@@ -109,7 +110,7 @@ export default class DynamicForm extends Vue {
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.$message.error('表单未设置对应的提交权限')
|
||||
Message.error('表单未设置对应的提交权限')
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<span class="big">Mayfly-Go</span>
|
||||
<span class="big">Mock-Server.go</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<span class="header-btn">
|
||||
@@ -176,6 +176,22 @@ export default class App extends Vue {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 1,
|
||||
name: 'Mock数据',
|
||||
icon: 'el-icon-menu',
|
||||
children: [
|
||||
{
|
||||
id: 31,
|
||||
type: 1,
|
||||
name: '数据列表',
|
||||
url: '/mock-data',
|
||||
icon: 'el-icon-menu',
|
||||
code: 'mock-data',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 1,
|
||||
|
||||
@@ -48,7 +48,16 @@ const routes: Array<RouteConfig> = [
|
||||
// },
|
||||
// component: () => import('@/views/db/SelectData.vue')
|
||||
// }]
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: 'mock-data',
|
||||
name: 'mock-data',
|
||||
meta: {
|
||||
title: '数据列表',
|
||||
keepAlive: false
|
||||
},
|
||||
component: () => import('@/views/mock-server')
|
||||
},]
|
||||
},
|
||||
]
|
||||
|
||||
@@ -65,11 +74,13 @@ router.beforeEach((to: any, from: any, next: any) => {
|
||||
next()
|
||||
return
|
||||
}
|
||||
if (!AuthUtils.getToken() && toPath != '/login') {
|
||||
next({ path: '/login' });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
// if (!AuthUtils.getToken() && toPath != '/login') {
|
||||
// next({ path: '/login' });
|
||||
// } else {
|
||||
// next();
|
||||
// }
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export default router
|
||||
|
||||
@@ -149,6 +149,7 @@ import 'codemirror/addon/hint/sql-hint.js'
|
||||
|
||||
import sqlFormatter from 'sql-formatter'
|
||||
import { notEmpty } from '@/common/assert'
|
||||
import { Message } from 'element-ui'
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -239,7 +240,7 @@ export default class SelectData extends Vue {
|
||||
notEmpty(this.sql, 'sql内容不能为空')
|
||||
notEmpty(this.dbId, '请先选择数据库')
|
||||
await dbApi.saveSql.request({ id: this.dbId, sql: this.sql, type: 1 })
|
||||
this.$message.success('保存成功')
|
||||
Message.success('保存成功')
|
||||
}
|
||||
|
||||
// 更改数据库事件
|
||||
|
||||
@@ -146,16 +146,17 @@
|
||||
:visible.sync="terminalDialog.visible"
|
||||
width="70%"
|
||||
:close-on-click-modal="false"
|
||||
:modal="false"
|
||||
@close="closeTermnial"
|
||||
>
|
||||
<ssh-terminal ref="terminal" :socketURI="terminalDialog.socketUri" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- <FileManage
|
||||
:title="dialog.title"
|
||||
:visible.sync="dialog.visible"
|
||||
:machineId.sync="dialog.machineId"
|
||||
/>-->
|
||||
<service-manage
|
||||
:title="serviceDialog.title"
|
||||
:visible.sync="serviceDialog.visible"
|
||||
:machineId.sync="serviceDialog.machineId"
|
||||
/>
|
||||
|
||||
<dynamic-form-dialog
|
||||
:visible.sync="formDialog.visible"
|
||||
@@ -173,6 +174,7 @@ import { DynamicFormDialog } from '@/components/dynamic-form'
|
||||
import Monitor from './Monitor.vue'
|
||||
import { machineApi } from './api'
|
||||
import SshTerminal from './SshTerminal.vue'
|
||||
import ServiceManage from './ServiceManage.vue';
|
||||
|
||||
@Component({
|
||||
name: 'MachineList',
|
||||
@@ -180,6 +182,7 @@ import SshTerminal from './SshTerminal.vue'
|
||||
DynamicFormDialog,
|
||||
Monitor,
|
||||
SshTerminal,
|
||||
ServiceManage
|
||||
},
|
||||
})
|
||||
export default class MachineList extends Vue {
|
||||
@@ -191,6 +194,11 @@ export default class MachineList extends Vue {
|
||||
visible: false,
|
||||
info: '',
|
||||
}
|
||||
serviceDialog = {
|
||||
visible: false,
|
||||
machineId: 0,
|
||||
title: ''
|
||||
}
|
||||
monitorDialog = {
|
||||
visible: false,
|
||||
machineId: 0,
|
||||
@@ -369,10 +377,10 @@ export default class MachineList extends Vue {
|
||||
this.search()
|
||||
}
|
||||
|
||||
fileManage(row: any) {
|
||||
this.dialog.machineId = row.id
|
||||
this.dialog.visible = true
|
||||
this.dialog.title = `${row.name} => ${row.ip}`
|
||||
serviceManager(row: any) {
|
||||
this.serviceDialog.machineId = row.id
|
||||
this.serviceDialog.visible = true
|
||||
this.serviceDialog.title = `${row.name} => ${row.ip}`
|
||||
}
|
||||
|
||||
submitSuccess() {
|
||||
|
||||
@@ -108,8 +108,7 @@ export default class Monitor extends Vue {
|
||||
icon: 'md-chatbubbles',
|
||||
count: 12,
|
||||
color: '#91AFC8',
|
||||
},
|
||||
{ title: '新增页面', icon: 'md-map', count: 14, color: '#91AFC8' },
|
||||
}
|
||||
]
|
||||
taskData = [
|
||||
{ value: 0, name: '运行中', color: '#3AA1FFB' },
|
||||
@@ -280,7 +279,7 @@ export default class Monitor extends Vue {
|
||||
|
||||
startInterval() {
|
||||
if (!this.timer) {
|
||||
this.timer = setInterval(this.getTop, 3000)
|
||||
// this.timer = setInterval(this.getTop, 3000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
345
mayfly-go-front/src/views/machine/ServiceManage.vue
Normal file
345
mayfly-go-front/src/views/machine/ServiceManage.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<div class="file-manage">
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="visible"
|
||||
:show-close="true"
|
||||
:before-close="handleClose"
|
||||
width="800px"
|
||||
>
|
||||
<div style="float: right;">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="add"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
plain
|
||||
>添加</el-button>
|
||||
</div>
|
||||
<el-table :data="fileTable" stripe style="width: 100%">
|
||||
<el-table-column prop="name" label="名称" width>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.name"
|
||||
size="mini"
|
||||
:disabled="scope.row.id != null"
|
||||
clearable
|
||||
></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="类型" width>
|
||||
<template slot-scope="scope">
|
||||
<el-select
|
||||
:disabled="scope.row.id != null"
|
||||
size="mini"
|
||||
v-model="scope.row.type"
|
||||
style="width: 100px"
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in enums.FileTypeEnum"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="path" label="路径" width>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.path"
|
||||
:disabled="scope.row.id != null"
|
||||
size="mini"
|
||||
clearable
|
||||
></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="scope.row.id == null"
|
||||
@click="addFiles(scope.row)"
|
||||
type="success"
|
||||
:ref="scope.row"
|
||||
icon="el-icon-success"
|
||||
size="mini"
|
||||
plain
|
||||
>确定</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.id != null"
|
||||
@click="getConf(scope.row)"
|
||||
type="primary"
|
||||
:ref="scope.row"
|
||||
icon="el-icon-tickets"
|
||||
size="mini"
|
||||
plain
|
||||
>查看</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:ref="scope.row"
|
||||
@click="deleteRow(scope.$index, scope.row)"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
plain
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
:title="fileContent.dialogTitle"
|
||||
:visible.sync="fileContent.contentVisible"
|
||||
width="850px"
|
||||
>
|
||||
<el-form :model="form">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="fileContent.content"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 18, maxRows:25}"
|
||||
autocomplete="off"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="fileContent.contentVisible = false" size="mini">取 消</el-button>
|
||||
<el-button
|
||||
v-permission="permission.updateFileContent.code"
|
||||
type="primary"
|
||||
@click="updateContent"
|
||||
size="mini"
|
||||
>确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
|
||||
import { machineApi } from './api'
|
||||
|
||||
@Component({
|
||||
name: 'ServiceManage',
|
||||
})
|
||||
export default class ServiceManage extends Vue {
|
||||
@Prop()
|
||||
visible: boolean
|
||||
@Prop()
|
||||
machineId: [number]
|
||||
@Prop()
|
||||
title: string
|
||||
|
||||
addFile = machineApi.addConf
|
||||
delFile = machineApi.delConf
|
||||
updateFileContent = machineApi.updateFileContent
|
||||
uploadFile = machineApi.uploadFile
|
||||
files = machineApi.files
|
||||
activeName = 'conf-file'
|
||||
token = sessionStorage.getItem('token')
|
||||
form = {
|
||||
id: null,
|
||||
type: null,
|
||||
name: '',
|
||||
remark: '',
|
||||
}
|
||||
fileTable: any[] = []
|
||||
btnLoading = false
|
||||
fileContent = {
|
||||
fileId: 0,
|
||||
content: '',
|
||||
contentVisible: false,
|
||||
dialogTitle: '',
|
||||
path: '',
|
||||
}
|
||||
tree = {
|
||||
title: '',
|
||||
visible: false,
|
||||
folder: { id: 0 },
|
||||
node: {
|
||||
childNodes: [],
|
||||
},
|
||||
resolve: {},
|
||||
}
|
||||
props = {
|
||||
label: 'name',
|
||||
children: 'zones',
|
||||
isLeaf: 'leaf',
|
||||
}
|
||||
|
||||
@Watch('machineId', { deep: true })
|
||||
onDataChange() {
|
||||
if (this.machineId) {
|
||||
this.getFiles()
|
||||
}
|
||||
}
|
||||
|
||||
async getFiles() {
|
||||
const res = await this.files.request({ id: this.machineId })
|
||||
this.fileTable = res
|
||||
}
|
||||
|
||||
/**
|
||||
* tab切换触发事件
|
||||
* @param {Object} tab
|
||||
* @param {Object} event
|
||||
*/
|
||||
// handleClick(tab, event) {
|
||||
// // if (tab.name == 'file-manage') {
|
||||
// // this.fileManage.node.childNodes = [];
|
||||
// // this.loadNode(this.fileManage.node, this.fileManage.resolve);
|
||||
// // }
|
||||
// }
|
||||
|
||||
add() {
|
||||
// 往数组头部添加元素
|
||||
this.fileTable = [{}].concat(this.fileTable)
|
||||
}
|
||||
|
||||
async addFiles(row: any) {
|
||||
row.machineId = this.machineId
|
||||
await this.addFile.request(row)
|
||||
this.$message.success('添加成功')
|
||||
this.getFiles()
|
||||
}
|
||||
|
||||
deleteRow(idx: any, row: any) {
|
||||
if (row.id) {
|
||||
this.$confirm(`此操作将删除 [${row.name}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
// 删除配置文件
|
||||
this.delFile
|
||||
.request({
|
||||
machineId: this.machineId,
|
||||
id: row.id,
|
||||
})
|
||||
.then((res) => {
|
||||
this.fileTable.splice(idx, 1)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.fileTable.splice(idx, 1)
|
||||
}
|
||||
}
|
||||
|
||||
getConf(row: any) {
|
||||
if (row.type == 1) {
|
||||
this.tree.folder = row
|
||||
this.tree.title = row.name
|
||||
const treeNode = (this.tree.node.childNodes = [])
|
||||
this.loadNode(this.tree.node, this.tree.resolve)
|
||||
this.tree.visible = true
|
||||
return
|
||||
}
|
||||
this.getFileContent(row.id, row.path)
|
||||
}
|
||||
|
||||
async getFileContent(fileId: number, path: string) {
|
||||
const res = await machineApi.fileContent.request({
|
||||
fileId,
|
||||
path,
|
||||
})
|
||||
this.fileContent.content = res
|
||||
this.fileContent.fileId = fileId
|
||||
this.fileContent.dialogTitle = path
|
||||
this.fileContent.path = path
|
||||
this.fileContent.contentVisible = true
|
||||
}
|
||||
|
||||
async updateContent() {
|
||||
await this.updateFileContent.request({
|
||||
content: this.fileContent.content,
|
||||
id: this.fileContent.fileId,
|
||||
path: this.fileContent.path,
|
||||
})
|
||||
this.$message.success('修改成功')
|
||||
this.fileContent.contentVisible = false
|
||||
this.fileContent.content = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭取消按钮触发的事件
|
||||
*/
|
||||
handleClose() {
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('update:machineId', null)
|
||||
this.$emit('cancel')
|
||||
this.activeName = 'conf-file'
|
||||
this.fileTable = []
|
||||
this.tree.folder = { id: 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载文件树节点
|
||||
* @param {Object} node
|
||||
* @param {Object} resolve
|
||||
*/
|
||||
async loadNode(node: any, resolve: any) {
|
||||
if (typeof resolve !== 'function') {
|
||||
return
|
||||
}
|
||||
|
||||
const folder: any = this.tree.folder
|
||||
if (node.level === 0) {
|
||||
this.tree.node = node
|
||||
this.tree.resolve = resolve
|
||||
|
||||
// let folder: any = this.tree.folder
|
||||
const path = folder ? folder.path : '/'
|
||||
return resolve([
|
||||
{
|
||||
name: path,
|
||||
type: 'd',
|
||||
path: path,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
let path
|
||||
const data = node.data
|
||||
// 只有在第一级节点时,name==path,即上述level==0时设置的
|
||||
if (!data || data.name == data.path) {
|
||||
path = folder.path
|
||||
} else {
|
||||
path = data.path
|
||||
}
|
||||
|
||||
const res = await machineApi.lsFile.request({
|
||||
fileId: folder.id,
|
||||
path,
|
||||
})
|
||||
for (const file of res) {
|
||||
const type = file.type
|
||||
if (type != 'd') {
|
||||
file.leaf = true
|
||||
}
|
||||
}
|
||||
return resolve(res)
|
||||
}
|
||||
|
||||
deleteFile(node: any, data: any) {
|
||||
const file = data.path
|
||||
this.$confirm(`此操作将删除 [${file}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
machineApi.rmFile
|
||||
.request({ fileId: this.tree.folder.id, path: file })
|
||||
.then((res) => {
|
||||
this.$message.success('删除成功')
|
||||
const fileTree: any = this.$refs.fileTree
|
||||
fileTree.remove(node)
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
</style>
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
cmd: String
|
||||
},
|
||||
watch: {
|
||||
socketURI(val) {
|
||||
@@ -38,11 +39,11 @@ export default {
|
||||
// cursorStyle: 'underline', //光标样式
|
||||
disableStdin: false,
|
||||
theme: {
|
||||
foreground: "#7e9192", //字体
|
||||
background: "#002833", //背景色
|
||||
cursor: "help", //设置光标
|
||||
lineHeight: 16
|
||||
}
|
||||
foreground: '#7e9192', //字体
|
||||
background: '#002833', //背景色
|
||||
cursor: 'help', //设置光标
|
||||
lineHeight: 16,
|
||||
},
|
||||
})
|
||||
const fitAddon = new FitAddon()
|
||||
term.loadAddon(fitAddon)
|
||||
@@ -66,11 +67,7 @@ export default {
|
||||
// * /
|
||||
// 支持输入与粘贴方法
|
||||
term.onData((key) => {
|
||||
const cmd = {
|
||||
type: 'cmd',
|
||||
msg: key,
|
||||
}
|
||||
this.send(cmd)
|
||||
this.sendCmd(key)
|
||||
})
|
||||
// 为解决窗体resize方法才会向后端发送列数和行数,所以页面加载时也要触发此方法
|
||||
this.send({
|
||||
@@ -78,6 +75,10 @@ export default {
|
||||
Cols: parseInt(term.cols),
|
||||
Rows: parseInt(term.rows),
|
||||
})
|
||||
// 如果有初始要执行的命令,则发送执行命令
|
||||
if (this.cmd) {
|
||||
this.sendCmd(this.cmd + " \r")
|
||||
}
|
||||
},
|
||||
initSocket() {
|
||||
this.socket = new WebSocket(this.socketURI)
|
||||
@@ -108,7 +109,7 @@ export default {
|
||||
// this.reconnect()
|
||||
},
|
||||
getMessage: function (msg) {
|
||||
// console.log(msg)
|
||||
// console.log(msg)
|
||||
this.term.write(msg['data'])
|
||||
//msg是返回的数据
|
||||
// msg = JSON.parse(msg.data);
|
||||
@@ -125,9 +126,16 @@ export default {
|
||||
// this.reset();
|
||||
},
|
||||
send: function (msg) {
|
||||
// console.log(msg)
|
||||
// console.log(msg)
|
||||
this.socket.send(JSON.stringify(msg))
|
||||
},
|
||||
|
||||
sendCmd(key) {
|
||||
this.send({
|
||||
type: 'cmd',
|
||||
msg: key,
|
||||
})
|
||||
},
|
||||
closeAll() {
|
||||
this.close()
|
||||
this.term.dispose()
|
||||
|
||||
236
mayfly-go-front/src/views/mock-server/MockDataEdit.vue
Normal file
236
mayfly-go-front/src/views/mock-server/MockDataEdit.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div class="mock-data-dialog">
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible="visible"
|
||||
:show-close="false"
|
||||
width="800px"
|
||||
>
|
||||
<el-form :model="form" ref="mockDataForm" label-width="70px" size="small">
|
||||
<el-form-item prop="method" label="方法名">
|
||||
<el-input
|
||||
:disabled="type == 1"
|
||||
v-model.trim="form.method"
|
||||
placeholder="请输入方法名"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="description" label="描述">
|
||||
<el-input
|
||||
v-model.trim="form.description"
|
||||
placeholder="请输入方法描述"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="description" label="生效用户">
|
||||
<el-select
|
||||
v-model="form.effectiveUser"
|
||||
multiple
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
style="width:100%"
|
||||
placeholder="请选择或创建生效用户,空为所有用户都生效"
|
||||
>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="data" label="数据" id="jsonedit">
|
||||
<codemirror
|
||||
style="height: 400px"
|
||||
ref="cmEditor"
|
||||
v-model="form.data"
|
||||
:options="cmOptions"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="text-align: center" class="dialog-footer">
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="btnLoading"
|
||||
@click="btnOk"
|
||||
size="mini"
|
||||
:disabled="submitDisabled"
|
||||
>确 定</el-button
|
||||
>
|
||||
<el-button @click="cancel()" :disabled="submitDisabled" size="mini"
|
||||
>取 消</el-button
|
||||
>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
|
||||
import { mockApi } from './api'
|
||||
import { notEmpty } from '@/common/assert'
|
||||
import Utils from '../../common/Utils'
|
||||
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/panda-syntax.css'
|
||||
// import base style
|
||||
require('codemirror/addon/selection/active-line')
|
||||
import 'codemirror/mode/javascript/javascript.js'
|
||||
import 'codemirror/addon/selection/active-line.js'
|
||||
// 匹配括号
|
||||
import 'codemirror/addon/edit/matchbrackets.js'
|
||||
|
||||
// json校验
|
||||
require('script-loader!jsonlint')
|
||||
import 'codemirror/addon/lint/lint'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import 'codemirror/addon/lint/json-lint'
|
||||
|
||||
@Component({
|
||||
name: 'MockDataEdit',
|
||||
components: {
|
||||
codemirror,
|
||||
},
|
||||
})
|
||||
export default class MockDataEdit extends Vue {
|
||||
@Prop()
|
||||
visible: boolean
|
||||
@Prop()
|
||||
data: [object, boolean]
|
||||
@Prop()
|
||||
title: string
|
||||
@Prop()
|
||||
type: number
|
||||
|
||||
cmOptions = {
|
||||
tabSize: 2,
|
||||
mode: 'application/json',
|
||||
theme: 'panda-syntax',
|
||||
// mode: {
|
||||
// // 模式, 可查看 codemirror/mode 中的所有模式
|
||||
// name: 'javascript',
|
||||
// json: true,
|
||||
// },
|
||||
line: true,
|
||||
// 开启校验
|
||||
lint: true,
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
indentWithTabs: true,
|
||||
smartIndent: true,
|
||||
matchBrackets: true,
|
||||
autofocus: true,
|
||||
styleSelectedText: true,
|
||||
styleActiveLine: true, // 高亮选中行
|
||||
foldGutter: true, // 块槽
|
||||
hintOptions: {
|
||||
// 当匹配只有一项的时候是否自动补全
|
||||
completeSingle: false,
|
||||
},
|
||||
}
|
||||
|
||||
submitDisabled = false
|
||||
|
||||
form = {
|
||||
method: '',
|
||||
description: '',
|
||||
data: '',
|
||||
effectiveUser: null
|
||||
}
|
||||
|
||||
btnLoading = false
|
||||
|
||||
// rules = {
|
||||
// method: [
|
||||
// {
|
||||
// required: true,
|
||||
// message: '请输入方法名',
|
||||
// trigger: ['change', 'blur'],
|
||||
// },
|
||||
// ],
|
||||
// description: [
|
||||
// {
|
||||
// required: true,
|
||||
// message: '请输入方法描述',
|
||||
// trigger: ['change', 'blur'],
|
||||
// },
|
||||
// ],
|
||||
// }
|
||||
|
||||
@Watch('data', { deep: true })
|
||||
onDataChange() {
|
||||
if (this.data) {
|
||||
Utils.copyProperties(this.data, this.form)
|
||||
} else {
|
||||
Utils.resetProperties(this.form)
|
||||
this.form.data = ''
|
||||
}
|
||||
}
|
||||
|
||||
// mounted() {
|
||||
// Utils.copyProperties(this.data, this.form)
|
||||
// this.codemirror.setValue(
|
||||
// JSON.stringify(JSON.parse(this.data['data']), null, 2)
|
||||
// )
|
||||
// }
|
||||
|
||||
get codemirror() {
|
||||
return this.$refs.cmEditor['codemirror']
|
||||
}
|
||||
|
||||
btnOk() {
|
||||
const mockDataForm: any = this.$refs['mockDataForm']
|
||||
mockDataForm.validate((valid: any) => {
|
||||
if (valid) {
|
||||
notEmpty(this.form.method, '方法名不能为空')
|
||||
notEmpty(this.form.description, '描述不能为空')
|
||||
notEmpty(this.form.data, '数据不能为空')
|
||||
let res
|
||||
if (this.type == 1) {
|
||||
res = mockApi.update.request(this.form)
|
||||
} else {
|
||||
res = mockApi.create.request(this.form)
|
||||
}
|
||||
res.then(
|
||||
(res: any) => {
|
||||
this.$message.success('保存成功')
|
||||
this.$emit('submitSuccess')
|
||||
this.submitDisabled = false
|
||||
this.cancel()
|
||||
},
|
||||
(e: any) => {
|
||||
this.submitDisabled = false
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
cancel() {
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('cancel')
|
||||
// this.codemirror.setValue('')
|
||||
// setTimeout(() => {
|
||||
// this.resetForm()
|
||||
// }, 200)
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
const mockDataForm: any = this.$refs['mockDataForm']
|
||||
if (mockDataForm) {
|
||||
mockDataForm.clearValidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
// .m-dialog {
|
||||
// .el-cascader {
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
#jsonedit {
|
||||
.CodeMirror {
|
||||
overflow-y: scroll !important;
|
||||
height: 400px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
244
mayfly-go-front/src/views/mock-server/MockDataList.vue
Normal file
244
mayfly-go-front/src/views/mock-server/MockDataList.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="toolbar">
|
||||
<div class="fl">
|
||||
<el-button
|
||||
@click="search"
|
||||
type="primary"
|
||||
icon="el-icon-refresh"
|
||||
size="mini"
|
||||
plain
|
||||
>刷新</el-button
|
||||
>
|
||||
<el-button
|
||||
@click="editData(null)"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
plain
|
||||
>添加</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
size="mini"
|
||||
:disabled="currentData == null"
|
||||
@click="editData(currentData)"
|
||||
plain
|
||||
>查看&编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
:disabled="currentData == null"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
@click="deleteData()"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
<el-input
|
||||
placeholder="方法名过滤"
|
||||
size="mini"
|
||||
style="width: 140px"
|
||||
@clear="search"
|
||||
plain
|
||||
v-model="queryParam"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-button
|
||||
@click="filterData"
|
||||
type="success"
|
||||
icon="el-icon-search"
|
||||
size="mini"
|
||||
></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="data"
|
||||
@current-change="choose"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="选择" width="55px">
|
||||
<template slot-scope="scope">
|
||||
<el-radio v-model="currentMethod" :label="scope.row.method">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="method"
|
||||
label="方法名"
|
||||
:min-width="50"
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
label="描述"
|
||||
:min-width="50"
|
||||
></el-table-column>
|
||||
<el-table-column prop="description" label="状态" :width="75">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip
|
||||
:content="scope.row.enable == 1 ? '启用' : '禁用'"
|
||||
placement="top"
|
||||
>
|
||||
<el-switch
|
||||
v-model="scope.row.enable"
|
||||
:active-value="1"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
@change="changeStatus(scope.row)"
|
||||
></el-switch>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="effectiveUser"
|
||||
label="生效用户"
|
||||
:min-width="70"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ showEffectiveUser(scope.row.effectiveUser) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="data"
|
||||
label="数据"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
></el-table-column>
|
||||
</el-table>
|
||||
|
||||
<mock-data-edit
|
||||
:visible.sync="editDialog.visible"
|
||||
:data.sync="editDialog.data"
|
||||
:title="editDialog.title"
|
||||
:type="editDialog.type"
|
||||
@cancel="closeEditDialog"
|
||||
@submitSuccess="submitSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator'
|
||||
import { mockApi } from './api'
|
||||
import MockDataEdit from './MockDataEdit.vue'
|
||||
|
||||
@Component({
|
||||
name: 'MockDataList',
|
||||
components: {
|
||||
MockDataEdit,
|
||||
},
|
||||
})
|
||||
export default class MockDataList extends Vue {
|
||||
data: Array<any> = []
|
||||
|
||||
currentData: any = null
|
||||
currentMethod = null
|
||||
queryParam = null
|
||||
|
||||
editDialog = {
|
||||
data: {},
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 0,
|
||||
}
|
||||
|
||||
choose(item: any) {
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
this.currentData = item
|
||||
this.currentMethod = item.method
|
||||
}
|
||||
|
||||
editData(data: any) {
|
||||
this.editDialog.data = data
|
||||
if (data) {
|
||||
this.editDialog.title = '修改mock数据'
|
||||
this.editDialog.type = 1
|
||||
} else {
|
||||
this.editDialog.title = '新增mock数据'
|
||||
this.editDialog.type = 0
|
||||
// this.delChoose()
|
||||
}
|
||||
this.editDialog.visible = true
|
||||
}
|
||||
|
||||
closeEditDialog() {
|
||||
// this.currentData.opentime = Date.now()
|
||||
// this.editDialog.data = this.currentData
|
||||
}
|
||||
|
||||
delChoose() {
|
||||
this.currentMethod = null
|
||||
this.currentData = null
|
||||
}
|
||||
|
||||
changeStatus(row: any) {
|
||||
const enable = row.enable
|
||||
row.enable = enable ? 1 : 0
|
||||
mockApi.update
|
||||
.request(row)
|
||||
.then((res) => {
|
||||
this.$message.success('操作成功')
|
||||
})
|
||||
.catch((e) => {
|
||||
row.enable = enable
|
||||
this.$message.success('操作失败')
|
||||
})
|
||||
}
|
||||
|
||||
showEffectiveUser(users: string[]) {
|
||||
if (!users || users.length == 0) {
|
||||
return '全部用户'
|
||||
}
|
||||
return users.join(', ')
|
||||
}
|
||||
|
||||
deleteData() {
|
||||
mockApi.delete.request({ method: this.currentMethod }).then((res) => {
|
||||
this.$message.success('删除成功')
|
||||
this.search()
|
||||
})
|
||||
}
|
||||
|
||||
submitSuccess() {
|
||||
this.delChoose()
|
||||
this.search()
|
||||
}
|
||||
|
||||
mounted() {
|
||||
this.search()
|
||||
}
|
||||
|
||||
filterData() {
|
||||
this.data = this.data.filter(
|
||||
(item) => item.method.indexOf(this.queryParam) != -1
|
||||
)
|
||||
}
|
||||
|
||||
async search() {
|
||||
if (this.data.length != 0) {
|
||||
this.data = []
|
||||
}
|
||||
const res = await mockApi.list.request(null)
|
||||
const values: string[] = Object.values(res)
|
||||
for (const value of values) {
|
||||
this.data.push(JSON.parse(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-dialog__body {
|
||||
padding: 2px 2px;
|
||||
}
|
||||
</style>
|
||||
9
mayfly-go-front/src/views/mock-server/api.ts
Normal file
9
mayfly-go-front/src/views/mock-server/api.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const mockApi = {
|
||||
// 获取权限列表
|
||||
list: Api.create("/mock-datas", 'get'),
|
||||
create: Api.create("/mock-datas", 'post'),
|
||||
update: Api.create("/mock-datas", 'put'),
|
||||
delete: Api.create("/mock-datas/{method}", 'delete'),
|
||||
}
|
||||
1
mayfly-go-front/src/views/mock-server/index.ts
Normal file
1
mayfly-go-front/src/views/mock-server/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './MockDataList.vue';
|
||||
21
mock-server/conf/app.conf
Normal file
21
mock-server/conf/app.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
appname = mayfly-mock
|
||||
httpport = 8888
|
||||
copyrequestbody = true
|
||||
autorender = false
|
||||
EnableErrorsRender = false
|
||||
runmode = "prod"
|
||||
; mysqluser = "root"
|
||||
; mysqlpass = "111049"
|
||||
; mysqlurls = "127.0.0.1"
|
||||
; mysqldb = "mayfly-job"
|
||||
# EnableAdmin = false
|
||||
# AdminHttpAddr = 0.0.0.0 #默认监听地址是localhost
|
||||
# AdminHttpPort = 8088
|
||||
|
||||
|
||||
[dev]
|
||||
httpport = 8888
|
||||
[prod]
|
||||
httpport = 8888
|
||||
[test]
|
||||
httpport = 8888
|
||||
3
mock-server/config.yml
Normal file
3
mock-server/config.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
23
mock-server/controllers/form/form.go
Normal file
23
mock-server/controllers/form/form.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package form
|
||||
|
||||
type MockData struct {
|
||||
Method string `valid:"Required" json:"method"`
|
||||
Enable uint `json:"enable"`
|
||||
Description string `valid:"Required" json:"description"`
|
||||
Data string `valid:"Required" json:"data"`
|
||||
EffectiveUser []string `json:"effectiveUser"`
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"-"`
|
||||
Port int `json:"port"` // 端口号
|
||||
}
|
||||
|
||||
type MachineService struct {
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Service string `json:"service"` // 服务命令
|
||||
}
|
||||
127
mock-server/controllers/machine.go
Normal file
127
mock-server/controllers/machine.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"mayfly-go/base"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/rediscli"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/mock-server/machine"
|
||||
"mayfly-go/mock-server/models"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type MachineController struct {
|
||||
base.Controller
|
||||
}
|
||||
|
||||
const machineKey = "ccbscf:machines"
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024 * 1024 * 10,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func (c *MachineController) Machines() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
return rediscli.HGetAll(machineKey)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建机器信息
|
||||
func (c *MachineController) CreateMachine() {
|
||||
c.Operation(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) {
|
||||
machine := &models.Machine{}
|
||||
c.UnmarshalBodyAndValid(machine)
|
||||
machine.CreateMachine()
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/mock-datas/:method [delete]
|
||||
func (c *MockController) DeleteMachine() {
|
||||
c.Operation(ctx.NewReqCtx(false, "删除mock数据"), func(account *ctx.LoginAccount) {
|
||||
models.DeleteMachine(c.Ctx.Input.Param(":ip"))
|
||||
})
|
||||
}
|
||||
|
||||
func (c *MachineController) Run() {
|
||||
rc := ctx.NewReqCtx(true, "执行机器命令")
|
||||
c.ReturnData(rc, func(account *ctx.LoginAccount) interface{} {
|
||||
cmd := c.GetString("cmd")
|
||||
biz.NotEmpty(cmd, "cmd不能为空")
|
||||
|
||||
rc.ReqParam = cmd
|
||||
|
||||
res, err := c.getCli().Run(cmd)
|
||||
biz.BizErrIsNil(err, "执行命令失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
// 系统基本信息
|
||||
func (c *MachineController) SysInfo() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
res, err := c.getCli().GetSystemInfo()
|
||||
biz.BizErrIsNil(err, "获取系统基本信息失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
// top命令信息
|
||||
func (c *MachineController) Top() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
return c.getCli().GetTop()
|
||||
})
|
||||
}
|
||||
|
||||
func (c *MachineController) GetProcessByName() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(true), func(account *ctx.LoginAccount) interface{} {
|
||||
name := c.GetString("name")
|
||||
biz.NotEmpty(name, "name不能为空")
|
||||
res, err := c.getCli().GetProcessByName(name)
|
||||
biz.BizErrIsNil(err, "获取失败")
|
||||
return res
|
||||
})
|
||||
}
|
||||
|
||||
func (c *MachineController) WsSSH() {
|
||||
wsConn, err := upgrader.Upgrade(c.Ctx.ResponseWriter, c.Ctx.Request, nil)
|
||||
if err != nil {
|
||||
panic(biz.NewBizErr("获取requst responsewirte错误"))
|
||||
}
|
||||
|
||||
cols, _ := c.GetInt("cols", 80)
|
||||
rows, _ := c.GetInt("rows", 40)
|
||||
|
||||
sws, err := machine.NewLogicSshWsSession(cols, rows, c.getCli(), wsConn)
|
||||
if sws == nil {
|
||||
panic(biz.NewBizErr("连接失败"))
|
||||
}
|
||||
//if wshandleError(wsConn, err) {
|
||||
// return
|
||||
//}
|
||||
defer sws.Close()
|
||||
|
||||
quitChan := make(chan bool, 3)
|
||||
sws.Start(quitChan)
|
||||
go sws.Wait(quitChan)
|
||||
|
||||
<-quitChan
|
||||
}
|
||||
|
||||
func (c *MachineController) GetMachineIp() string {
|
||||
machineIp := c.Ctx.Input.Param(":ip")
|
||||
biz.IsTrue(utils.StrLen(machineIp) > 0, "ip错误")
|
||||
return machineIp
|
||||
}
|
||||
|
||||
func (c *MachineController) getCli() *machine.Cli {
|
||||
cli, err := machine.GetCli(c.GetMachineIp())
|
||||
biz.BizErrIsNil(err, "获取客户端错误")
|
||||
return cli
|
||||
}
|
||||
78
mock-server/controllers/mock.go
Normal file
78
mock-server/controllers/mock.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"mayfly-go/base"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/rediscli"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/mock-server/controllers/form"
|
||||
)
|
||||
|
||||
const key = "ccbscf:mock:data"
|
||||
|
||||
type MockController struct {
|
||||
base.Controller
|
||||
}
|
||||
|
||||
// @router /api/mock-datas/:method [get]
|
||||
func (c *MockController) GetMockData() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(false), func(account *ctx.LoginAccount) interface{} {
|
||||
val := rediscli.HGet(key, c.Ctx.Input.Param(":method"))
|
||||
mockData := &form.MockData{}
|
||||
json.Unmarshal([]byte(val), mockData)
|
||||
biz.IsTrue(mockData.Enable == 1, "无该mock数据")
|
||||
|
||||
eu := mockData.EffectiveUser
|
||||
// 如果设置的生效用户为空,则表示所有用户都生效
|
||||
if len(eu) == 0 {
|
||||
return mockData.Data
|
||||
}
|
||||
|
||||
// 该mock数据需要指定的生效用户才可访问
|
||||
username := c.GetString("username")
|
||||
biz.IsTrue(utils.StrLen(username) != 0, "该用户无法访问该mock数据")
|
||||
for _, e := range eu {
|
||||
if username == e {
|
||||
return mockData.Data
|
||||
}
|
||||
}
|
||||
panic(biz.NewBizErr("该用户无法访问该mock数据"))
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/mock-datas [put]
|
||||
func (c *MockController) UpdateMockData() {
|
||||
c.Operation(ctx.NewReqCtx(true, "修改mock数据"), func(account *ctx.LoginAccount) {
|
||||
mockData := &form.MockData{}
|
||||
c.UnmarshalBodyAndValid(mockData)
|
||||
val, _ := json.Marshal(mockData)
|
||||
rediscli.HSet(key, mockData.Method, val)
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/mock-datas [post]
|
||||
func (c *MockController) CreateMockData() {
|
||||
c.Operation(ctx.NewReqCtx(true, "保存mock数据"), func(account *ctx.LoginAccount) {
|
||||
mockData := &form.MockData{}
|
||||
c.UnmarshalBodyAndValid(mockData)
|
||||
biz.IsTrue(!rediscli.HExist(key, mockData.Method), "该方法已存在")
|
||||
val, _ := json.Marshal(mockData)
|
||||
rediscli.HSet(key, mockData.Method, val)
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/mock-datas [get]
|
||||
func (c *MockController) GetAllData() {
|
||||
c.ReturnData(ctx.NewNoLogReqCtx(false), func(account *ctx.LoginAccount) interface{} {
|
||||
return rediscli.HGetAll(key)
|
||||
})
|
||||
}
|
||||
|
||||
// @router /api/mock-datas/:method [delete]
|
||||
func (c *MockController) DeleteMockData() {
|
||||
c.Operation(ctx.NewReqCtx(false, "删除mock数据"), func(account *ctx.LoginAccount) {
|
||||
rediscli.HDel(key, c.Ctx.Input.Param(":method"))
|
||||
})
|
||||
}
|
||||
1
mock-server/lastupdate.tmp
Executable file
1
mock-server/lastupdate.tmp
Executable file
@@ -0,0 +1 @@
|
||||
{"/Users/hml/Desktop/project/go/mayfly-go/mock-server/controllers":1615026881770981752}
|
||||
192
mock-server/machine/machine.go
Normal file
192
mock-server/machine/machine.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/utils"
|
||||
"mayfly-go/mock-server/models"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// 客户端信息
|
||||
type Cli struct {
|
||||
machine *models.Machine
|
||||
// ssh客户端
|
||||
client *ssh.Client
|
||||
}
|
||||
|
||||
// 客户端缓存
|
||||
var clientCache sync.Map
|
||||
var mutex sync.Mutex
|
||||
|
||||
// 从缓存中获取客户端信息,不存在则查库,并新建
|
||||
func GetCli(machineIp string) (*Cli, error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
load, ok := clientCache.Load(machineIp)
|
||||
if ok {
|
||||
return load.(*Cli), nil
|
||||
}
|
||||
|
||||
cli, err := newClient(models.GetMachineByIp(machineIp))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientCache.LoadOrStore(machineIp, cli)
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
//根据机器信息创建客户端对象
|
||||
func newClient(machine *models.Machine) (*Cli, error) {
|
||||
if machine == nil {
|
||||
return nil, errors.New("机器不存在")
|
||||
}
|
||||
|
||||
cli := new(Cli)
|
||||
cli.machine = machine
|
||||
err := cli.connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
//连接
|
||||
func (c *Cli) connect() error {
|
||||
// 如果已经有client则直接返回
|
||||
if c.client != nil {
|
||||
return nil
|
||||
}
|
||||
m := c.machine
|
||||
config := ssh.ClientConfig{
|
||||
User: m.Username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||||
sshClient, err := ssh.Dial("tcp", addr, &config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.client = sshClient
|
||||
return nil
|
||||
}
|
||||
|
||||
// 测试连接
|
||||
func TestConn(m *models.Machine) (*ssh.Client, error) {
|
||||
config := ssh.ClientConfig{
|
||||
User: m.Username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(m.Password)},
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", m.Ip, m.Port)
|
||||
sshClient, err := ssh.Dial("tcp", addr, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sshClient, nil
|
||||
}
|
||||
|
||||
// 关闭client和并从缓存中移除
|
||||
func (c *Cli) Close() {
|
||||
if c.client != nil {
|
||||
c.client.Close()
|
||||
}
|
||||
if utils.StrLen(c.machine.Ip) > 0 {
|
||||
clientCache.Delete(c.machine.Ip)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取sftp client
|
||||
func (c *Cli) GetSftpCli() *sftp.Client {
|
||||
if c.client == nil {
|
||||
if err := c.connect(); err != nil {
|
||||
panic(biz.NewBizErr("连接ssh失败:" + err.Error()))
|
||||
}
|
||||
}
|
||||
client, serr := sftp.NewClient(c.client, sftp.MaxPacket(1<<15))
|
||||
if serr != nil {
|
||||
panic(biz.NewBizErr("获取sftp client失败:" + serr.Error()))
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// 获取session
|
||||
func (c *Cli) GetSession() (*ssh.Session, error) {
|
||||
if c.client == nil {
|
||||
if err := c.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.client.NewSession()
|
||||
}
|
||||
|
||||
//执行shell
|
||||
//@param shell shell脚本命令
|
||||
func (c *Cli) Run(shell string) (*string, error) {
|
||||
session, err := c.GetSession()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer session.Close()
|
||||
buf, rerr := session.CombinedOutput(shell)
|
||||
if rerr != nil {
|
||||
return nil, rerr
|
||||
}
|
||||
res := string(buf)
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
//执行带交互的命令
|
||||
func (c *Cli) RunTerminal(shell string, stdout, stderr io.Writer) error {
|
||||
session, err := c.GetSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//defer session.Close()
|
||||
|
||||
fd := int(os.Stdin.Fd())
|
||||
oldState, err := terminal.MakeRaw(fd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer terminal.Restore(fd, oldState)
|
||||
|
||||
session.Stdout = stdout
|
||||
session.Stderr = stderr
|
||||
session.Stdin = os.Stdin
|
||||
|
||||
termWidth, termHeight, err := terminal.GetSize(fd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Set up terminal modes
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 1, // enable echoing
|
||||
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
|
||||
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
|
||||
}
|
||||
|
||||
// Request pseudo terminal
|
||||
if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return session.Run(shell)
|
||||
}
|
||||
142
mock-server/machine/machine_test.go
Normal file
142
mock-server/machine/machine_test.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/base/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSSH(t *testing.T) {
|
||||
//ssh.ListenAndServe("148.70.36.197")
|
||||
//cli := New("148.70.36.197", "root", "gea&630_..91mn#", 22)
|
||||
////output, err := cli.Run("free -h")
|
||||
////fmt.Printf("%v\n%v", output, err)
|
||||
//err := cli.RunTerminal("tail -f /usr/local/java/logs/eatlife-info.log", os.Stdout, os.Stdin)
|
||||
//fmt.Println(err)
|
||||
|
||||
res := "top - 17:14:07 up 5 days, 6:30, 2 users, load average: 0.03, 0.04, 0.05\nTasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie\n%Cpu(s): 6.2 us, 0.0 sy, 0.0 ni, 93.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st\nKiB Mem : 1882012 total, 73892 free, 770360 used, 1037760 buff/cache\nKiB Swap: 0 total, 0 free, 0 used. 933492 avail Mem"
|
||||
split := strings.Split(res, "\n")
|
||||
//var firstLine string
|
||||
//for i := 0; i < len(split); i++ {
|
||||
// if i == 0 {
|
||||
// val := strings.Split(split[i], "top -")[1]
|
||||
// vals := strings.Split(val, ",")
|
||||
//
|
||||
// }
|
||||
//}
|
||||
firstLine := strings.Split(strings.Split(split[0], "top -")[1], ",")
|
||||
// 17:14:07 up 5 days
|
||||
up := strings.Trim(strings.Split(firstLine[0], "up")[1], " ") + firstLine[1]
|
||||
// 2 users
|
||||
users := strings.Split(strings.Trim(firstLine[2], " "), " ")[0]
|
||||
// load average: 0.03
|
||||
oneMinLa := strings.Trim(strings.Split(strings.Trim(firstLine[3], " "), ":")[1], " ")
|
||||
fiveMinLa := strings.Trim(firstLine[4], " ")
|
||||
fietMinLa := strings.Trim(firstLine[5], " ")
|
||||
fmt.Println(firstLine, up, users, oneMinLa, fiveMinLa, fietMinLa)
|
||||
tasks := Parse(strings.Split(split[1], "Tasks:")[1])
|
||||
cpu := Parse(strings.Split(split[2], "%Cpu(s):")[1])
|
||||
mem := Parse(strings.Split(split[3], "KiB Mem :")[1])
|
||||
fmt.Println(tasks, cpu, mem)
|
||||
}
|
||||
|
||||
func Parse(val string) map[string]string {
|
||||
res := make(map[string]string)
|
||||
vals := strings.Split(val, ",")
|
||||
for i := 0; i < len(vals); i++ {
|
||||
trimData := strings.Trim(vals[i], " ")
|
||||
keyValue := strings.Split(trimData, " ")
|
||||
res[keyValue[1]] = keyValue[0]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func TestTemplateRev(t *testing.T) {
|
||||
temp := "hello my name is {name} hahahaha lihaiba {age} years old {public}"
|
||||
str := "hello my name is hmlhmlhm 慌慌信息 hahahaha lihaiba 15 years old private protected"
|
||||
|
||||
//temp1 := " top - {up}, {users} users, load average: {loadavg}"
|
||||
//str1 := " top - 17:14:07 up 5 days, 6:30, 2 users, load average: 0.03, 0.04, 0.05"
|
||||
|
||||
//taskTemp := "Tasks: {total} total, {running} running, {sleeping} sleeping, {stopped} stopped, {zombie} zombie"
|
||||
//taskVal := "Tasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie"
|
||||
|
||||
//nameRunne := []rune(str)
|
||||
//index := strings.Index(temp, "{")
|
||||
//ei := strings.Index(temp, "}") + 1
|
||||
//next := temp[ei:]
|
||||
//key := temp[index+1 : ei-1]
|
||||
//value := SubString(str, index, UnicodeIndex(str, next))
|
||||
res := make(map[string]interface{})
|
||||
utils.ReverStrTemplate(temp, str, res)
|
||||
fmt.Println(res)
|
||||
}
|
||||
|
||||
//func ReverStrTemplate(temp, str string, res map[string]string) {
|
||||
// index := UnicodeIndex(temp, "{")
|
||||
// ei := UnicodeIndex(temp, "}") + 1
|
||||
// next := temp[ei:]
|
||||
// nextContain := UnicodeIndex(next, "{")
|
||||
// nextIndexValue := next
|
||||
// if nextContain != -1 {
|
||||
// nextIndexValue = SubString(next, 0, nextContain)
|
||||
// }
|
||||
// key := temp[index+1 : ei-1]
|
||||
// // 如果后面没有内容了,则取字符串的长度即可
|
||||
// var valueLastIndex int
|
||||
// if nextIndexValue == "" {
|
||||
// valueLastIndex = StrLen(str)
|
||||
// } else {
|
||||
// valueLastIndex = UnicodeIndex(str, nextIndexValue)
|
||||
// }
|
||||
// value := SubString(str, index, valueLastIndex)
|
||||
// res[key] = value
|
||||
//
|
||||
// if nextContain != -1 {
|
||||
// ReverStrTemplate(next, SubString(str, UnicodeIndex(str, value)+StrLen(value), StrLen(str)), res)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func StrLen(str string) int {
|
||||
// return len([]rune(str))
|
||||
//}
|
||||
//
|
||||
//func SubString(str string, begin, end int) (substr string) {
|
||||
// // 将字符串的转换成[]rune
|
||||
// rs := []rune(str)
|
||||
// lth := len(rs)
|
||||
//
|
||||
// // 简单的越界判断
|
||||
// if begin < 0 {
|
||||
// begin = 0
|
||||
// }
|
||||
// if begin >= lth {
|
||||
// begin = lth
|
||||
// }
|
||||
// if end > lth {
|
||||
// end = lth
|
||||
// }
|
||||
//
|
||||
// // 返回子串
|
||||
// return string(rs[begin:end])
|
||||
//}
|
||||
//
|
||||
//func UnicodeIndex(str, substr string) int {
|
||||
// // 子串在字符串的字节位置
|
||||
// result := strings.Index(str, substr)
|
||||
// if result >= 0 {
|
||||
// // 获得子串之前的字符串并转换成[]byte
|
||||
// prefix := []byte(str)[0:result]
|
||||
// // 将子串之前的字符串转换成[]rune
|
||||
// rs := []rune(string(prefix))
|
||||
// // 获得子串之前的字符串的长度,便是子串在字符串的字符位置
|
||||
// result = len(rs)
|
||||
// }
|
||||
//
|
||||
// return result
|
||||
//}
|
||||
|
||||
func TestRunShellFile(t *testing.T) {
|
||||
|
||||
}
|
||||
34
mock-server/machine/shell.go
Normal file
34
mock-server/machine/shell.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"mayfly-go/base/biz"
|
||||
)
|
||||
|
||||
const BasePath = "./machine/shell/"
|
||||
|
||||
const MonitorTemp = "cpuRate:{cpuRate}%,memRate:{memRate}%,sysLoad:{sysLoad}\n"
|
||||
|
||||
// shell文件内容缓存,避免每次读取文件
|
||||
var shellCache = make(map[string]string)
|
||||
|
||||
func (c *Cli) GetProcessByName(name string) (*string, error) {
|
||||
return c.Run(getShellContent("sys_info"))
|
||||
}
|
||||
|
||||
func (c *Cli) GetSystemInfo() (*string, error) {
|
||||
return c.Run(getShellContent("system_info"))
|
||||
}
|
||||
|
||||
// 获取shell内容
|
||||
func getShellContent(name string) string {
|
||||
cacheShell := shellCache[name]
|
||||
if cacheShell != "" {
|
||||
return cacheShell
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(BasePath + name + ".sh")
|
||||
biz.ErrIsNil(err, "获取shell文件失败")
|
||||
shellStr := string(bytes)
|
||||
shellCache[name] = shellStr
|
||||
return shellStr
|
||||
}
|
||||
23
mock-server/machine/shell/get_process_by_name.sh
Normal file
23
mock-server/machine/shell/get_process_by_name.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#! /bin/bash
|
||||
# Function: 根据输入的程序的名字过滤出所对应的PID,并显示出详细信息,如果有几个PID,则全部显示
|
||||
NAME=%s
|
||||
N=`ps -aux | grep $NAME | grep -v grep | wc -l` ##统计进程总数
|
||||
if [ $N -le 0 ];then
|
||||
echo "该进程名没有运行!"
|
||||
fi
|
||||
i=1
|
||||
while [ $N -gt 0 ]
|
||||
do
|
||||
echo "进程PID: `ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $2}'`"
|
||||
echo "进程命令:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $11}'`"
|
||||
echo "进程所属用户: `ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $1}'`"
|
||||
echo "CPU占用率:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $3}'`%"
|
||||
echo "内存占用率:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $4}'`%"
|
||||
echo "进程开始运行的时刻:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $9}'`"
|
||||
echo "进程运行的时间:` ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $11}'`"
|
||||
echo "进程状态:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $8}'`"
|
||||
echo "进程虚拟内存:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $5}'`"
|
||||
echo "进程共享内存:`ps -aux | grep $NAME | grep -v grep | awk 'NR=='$i'{print $0}'| awk '{print $6}'`"
|
||||
echo "***************************************************************"
|
||||
let N-- i++
|
||||
done
|
||||
13
mock-server/machine/shell/monitor.sh
Normal file
13
mock-server/machine/shell/monitor.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
# 获取监控信息
|
||||
function get_monitor_info() {
|
||||
cpu_rate=$(cat /proc/stat | awk '/cpu/{printf("%.2f%\n"), ($2+$4)*100/($2+$4+$5)}' | awk '{print $0}' | head -1)
|
||||
mem_rate=$(free -m | sed -n '2p' | awk '{print""($3/$2)*100"%"}')
|
||||
sys_load=$(uptime | cut -d: -f5)
|
||||
cat <<EOF | column -t
|
||||
cpuRate:${cpu_rate},memRate:${mem_rate},sysLoad:${sys_load}
|
||||
EOF
|
||||
}
|
||||
|
||||
get_monitor_info
|
||||
192
mock-server/machine/shell/sys_info.sh
Normal file
192
mock-server/machine/shell/sys_info.sh
Normal file
@@ -0,0 +1,192 @@
|
||||
#!/bin/bash
|
||||
# func:sys info check
|
||||
[ $(id -u) -ne 0 ] && echo "请用root用户执行此脚本!" && exit 1
|
||||
sysversion=$(rpm -q centos-release | cut -d- -f3)
|
||||
line="-------------------------------------------------"
|
||||
# 获取系统cpu信息
|
||||
function get_cpu_info() {
|
||||
Physical_CPUs=$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l)
|
||||
Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l)
|
||||
CPU_Kernels=$(grep "cores" /proc/cpuinfo | uniq | awk -F ': ' '{print $2}')
|
||||
CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq)
|
||||
CPU_Arch=$(uname -m)
|
||||
cpu_usage=$(cat /proc/stat | awk '/cpu/{printf("%.2f%\n"), ($2+$4)*100/($2+$4+$5)}' | awk '{print $0}' | head -1)
|
||||
#echo -e '\033[32m CPU信息:\033[0m'
|
||||
echo -e ' CPU信息:'
|
||||
cat <<EOF | column -t
|
||||
物理CPU个数: $Physical_CPUs
|
||||
逻辑CPU个数: $Virt_CPUs
|
||||
每CPU核心数: $CPU_Kernels
|
||||
CPU型号: $CPU_Type
|
||||
CPU架构: $CPU_Arch
|
||||
CPU使用率: $cpu_usage
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取系统内存信息
|
||||
function get_mem_info() {
|
||||
Total=$(free -m | sed -n '2p' | awk '{print $2"M"}')
|
||||
Used=$(free -m | sed -n '2p' | awk '{print $3"M"}')
|
||||
Rate=$(free -m | sed -n '2p' | awk '{print""($3/$2)*100"%"}')
|
||||
echo -e ' 内存信息:'
|
||||
cat <<EOF | column -t
|
||||
内存总容量:$Total
|
||||
内存已使用:$Used
|
||||
内存使用率:$Rate
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取系统网络信息
|
||||
function get_net_info() {
|
||||
pri_ipadd=$(ifconfig | awk 'NR==2{print $2}')
|
||||
#pub_ipadd=$(curl ip.sb 2>&1)
|
||||
pub_ipadd=$(curl -s http://ddns.oray.com/checkip | awk -F ":" '{print $2}' | awk -F "<" '{print $1}' | awk '{print $1}')
|
||||
gateway=$(ip route | grep default | awk '{print $3}')
|
||||
mac_info=$(ip link | egrep -v "lo" | grep link | awk '{print $2}')
|
||||
dns_config=$(egrep 'nameserver' /etc/resolv.conf)
|
||||
route_info=$(route -n)
|
||||
echo -e ' IP信息:'
|
||||
cat <<EOF | column -t
|
||||
系统公网地址: ${pub_ipadd}
|
||||
系统私网地址: ${pri_ipadd}
|
||||
网关地址: ${gateway}
|
||||
MAC地址: ${mac_info}
|
||||
路由信息:
|
||||
${route_info}
|
||||
DNS 信息:
|
||||
${dns_config}
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取系统磁盘信息
|
||||
function get_disk_info() {
|
||||
disk_info=$(fdisk -l | grep "Disk /dev" | cut -d, -f1)
|
||||
disk_use=$(df -hTP | awk '$2!="tmpfs"{print}')
|
||||
disk_inode=$(df -hiP | awk '$1!="tmpfs"{print}')
|
||||
echo -e ' 磁盘信息:'
|
||||
cat <<EOF
|
||||
${disk_info}
|
||||
磁盘使用:
|
||||
${disk_use}
|
||||
inode信息:
|
||||
${disk_inode}
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取系统信息
|
||||
function get_systatus_info() {
|
||||
sys_os=$(uname -o)
|
||||
sys_release=$(cat /etc/redhat-release)
|
||||
sys_kernel=$(uname -r)
|
||||
sys_hostname=$(hostname)
|
||||
sys_selinux=$(getenforce)
|
||||
sys_lang=$(echo $LANG)
|
||||
sys_lastreboot=$(who -b | awk '{print $3,$4}')
|
||||
sys_runtime=$(uptime | awk '{print $3,$4}' | cut -d, -f1)
|
||||
sys_time=$(date)
|
||||
sys_load=$(uptime | cut -d: -f5)
|
||||
echo -e ' 系统信息:'
|
||||
cat <<EOF | column -t
|
||||
系统: ${sys_os}
|
||||
发行版本: ${sys_release}
|
||||
系统内核: ${sys_kernel}
|
||||
主机名: ${sys_hostname}
|
||||
selinux状态: ${sys_selinux}
|
||||
系统语言: ${sys_lang}
|
||||
系统当前时间: ${sys_time}
|
||||
系统最后重启时间: ${sys_lastreboot}
|
||||
系统运行时间: ${sys_runtime}
|
||||
系统负载: ${sys_load}
|
||||
---------------------------------------
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取服务信息
|
||||
function get_service_info() {
|
||||
port_listen=$(netstat -lntup | grep -v "Active Internet")
|
||||
kernel_config=$(sysctl -p 2>/dev/null)
|
||||
if [ ${sysversion} -gt 6 ]; then
|
||||
service_config=$(systemctl list-unit-files --type=service --state=enabled | grep "enabled")
|
||||
run_service=$(systemctl list-units --type=service --state=running | grep ".service")
|
||||
else
|
||||
service_config=$(/sbin/chkconfig | grep -E ":on|:启用" | column -t)
|
||||
run_service=$(/sbin/service --status-all | grep -E "running")
|
||||
fi
|
||||
echo -e ' 服务启动配置:'
|
||||
cat <<EOF
|
||||
${service_config}
|
||||
${line}
|
||||
运行的服务:
|
||||
${run_service}
|
||||
${line}
|
||||
监听端口:
|
||||
${port_listen}
|
||||
${line}
|
||||
内核参考配置:
|
||||
${kernel_config}
|
||||
EOF
|
||||
}
|
||||
|
||||
function get_sys_user() {
|
||||
login_user=$(awk -F: '{if ($NF=="/bin/bash") print $0}' /etc/passwd)
|
||||
ssh_config=$(egrep -v "^#|^$" /etc/ssh/sshd_config)
|
||||
sudo_config=$(egrep -v "^#|^$" /etc/sudoers | grep -v "^Defaults")
|
||||
host_config=$(egrep -v "^#|^$" /etc/hosts)
|
||||
crond_config=$(for cronuser in /var/spool/cron/*; do
|
||||
ls ${cronuser} 2>/dev/null | cut -d/ -f5
|
||||
egrep -v "^$|^#" ${cronuser} 2>/dev/null
|
||||
echo ""
|
||||
done)
|
||||
echo -e ' 系统登录用户:'
|
||||
cat <<EOF
|
||||
${login_user}
|
||||
${line}
|
||||
ssh 配置信息:
|
||||
${ssh_config}
|
||||
${line}
|
||||
sudo 配置用户:
|
||||
${sudo_config}
|
||||
${line}
|
||||
定时任务配置:
|
||||
${crond_config}
|
||||
${line}
|
||||
hosts 信息:
|
||||
${host_config}
|
||||
EOF
|
||||
}
|
||||
|
||||
function process_top_info() {
|
||||
top_title=$(top -b n1 | head -7 | tail -1)
|
||||
cpu_top10=$(top b -n1 | head -17 | tail -10)
|
||||
mem_top10=$(top -b n1 | head -17 | tail -10 | sort -k10 -r)
|
||||
echo -e ' CPU占用top10:'
|
||||
cat <<EOF
|
||||
${top_title}
|
||||
${cpu_top10}
|
||||
EOF
|
||||
echo -e ' 内存占用top10:'
|
||||
cat <<EOF
|
||||
${top_title}
|
||||
${mem_top10}
|
||||
EOF
|
||||
}
|
||||
|
||||
function sys_check() {
|
||||
get_systatus_info
|
||||
echo ${line}
|
||||
get_cpu_info
|
||||
echo ${line}
|
||||
get_mem_info
|
||||
echo ${line}
|
||||
# get_net_info
|
||||
# echo ${line}
|
||||
get_disk_info
|
||||
echo ${line}
|
||||
get_service_info
|
||||
echo ${line}
|
||||
# get_sys_user
|
||||
# echo ${line}
|
||||
process_top_info
|
||||
}
|
||||
|
||||
sys_check
|
||||
41
mock-server/machine/shell/system_info.sh
Normal file
41
mock-server/machine/shell/system_info.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
# 获取系统cpu信息
|
||||
function get_cpu_info() {
|
||||
Physical_CPUs=$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l)
|
||||
Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l)
|
||||
CPU_Kernels=$(grep "cores" /proc/cpuinfo | uniq | awk -F ': ' '{print $2}')
|
||||
CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq)
|
||||
CPU_Arch=$(uname -m)
|
||||
echo -e '\n-------------------------- CPU信息 --------------------------'
|
||||
cat <<EOF | column -t
|
||||
物理CPU个数: $Physical_CPUs
|
||||
逻辑CPU个数: $Virt_CPUs
|
||||
每CPU核心数: $CPU_Kernels
|
||||
CPU型号: $CPU_Type
|
||||
CPU架构: $CPU_Arch
|
||||
EOF
|
||||
}
|
||||
|
||||
# 获取系统信息
|
||||
function get_systatus_info() {
|
||||
sys_os=$(uname -o)
|
||||
sys_release=$(cat /etc/redhat-release)
|
||||
sys_kernel=$(uname -r)
|
||||
sys_hostname=$(hostname)
|
||||
sys_selinux=$(getenforce)
|
||||
sys_lang=$(echo $LANG)
|
||||
sys_lastreboot=$(who -b | awk '{print $3,$4}')
|
||||
echo -e '-------------------------- 系统信息 --------------------------'
|
||||
cat <<EOF | column -t
|
||||
系统: ${sys_os}
|
||||
发行版本: ${sys_release}
|
||||
系统内核: ${sys_kernel}
|
||||
主机名: ${sys_hostname}
|
||||
selinux状态: ${sys_selinux}
|
||||
系统语言: ${sys_lang}
|
||||
系统最后重启时间: ${sys_lastreboot}
|
||||
EOF
|
||||
}
|
||||
|
||||
get_systatus_info
|
||||
#echo -e "\n"
|
||||
get_cpu_info
|
||||
105
mock-server/machine/status.go
Normal file
105
mock-server/machine/status.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SystemVersion struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (c *Cli) GetSystemVersion() *SystemVersion {
|
||||
res, _ := c.Run("cat /etc/redhat-release")
|
||||
return &SystemVersion{
|
||||
Version: *res,
|
||||
}
|
||||
}
|
||||
|
||||
//top - 17:14:07 up 5 days, 6:30, 2 users, load average: 0.03, 0.04, 0.05
|
||||
//Tasks: 101 total, 1 running, 100 sleeping, 0 stopped, 0 zombie
|
||||
//%Cpu(s): 6.2 us, 0.0 sy, 0.0 ni, 93.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
|
||||
//KiB Mem : 1882012 total, 73892 free, 770360 used, 1037760 buff/cache
|
||||
//KiB Swap: 0 total, 0 free, 0 used. 933492 avail Mem
|
||||
type Top struct {
|
||||
Time string `json:"time"`
|
||||
// 从本次开机到现在经过的时间
|
||||
Up string `json:"up"`
|
||||
// 当前有几个用户登录到该机器
|
||||
NowUsers int `json:"nowUsers"`
|
||||
// load average: 0.03, 0.04, 0.05 (系统1分钟、5分钟、15分钟内的平均负载值)
|
||||
OneMinLoadavg float32 `json:"oneMinLoadavg"`
|
||||
FiveMinLoadavg float32 `json:"fiveMinLoadavg"`
|
||||
FifteenMinLoadavg float32 `json:"fifteenMinLoadavg"`
|
||||
// 进程总数
|
||||
TotalTask int `json:"totalTask"`
|
||||
// 正在运行的进程数,对应状态TASK_RUNNING
|
||||
RunningTask int `json:"runningTask"`
|
||||
SleepingTask int `json:"sleepingTask"`
|
||||
StoppedTask int `json:"stoppedTask"`
|
||||
ZombieTask int `json:"zombieTask"`
|
||||
// 进程在用户空间(user)消耗的CPU时间占比,不包含调整过优先级的进程
|
||||
CpuUs float32 `json:"cpuUs"`
|
||||
// 进程在内核空间(system)消耗的CPU时间占比
|
||||
CpuSy float32 `json:"cpuSy"`
|
||||
// 调整过用户态优先级的(niced)进程的CPU时间占比
|
||||
CpuNi float32 `json:"cpuNi"`
|
||||
// 空闲的(idle)CPU时间占比
|
||||
CpuId float32 `json:"cpuId"`
|
||||
// 等待(wait)I/O完成的CPU时间占比
|
||||
CpuWa float32 `json:"cpuWa"`
|
||||
// 处理硬中断(hardware interrupt)的CPU时间占比
|
||||
CpuHi float32 `json:"cpuHi"`
|
||||
// 处理硬中断(hardware interrupt)的CPU时间占比
|
||||
CpuSi float32 `json:"cpuSi"`
|
||||
// 当Linux系统是在虚拟机中运行时,等待CPU资源的时间(steal time)占比
|
||||
CpuSt float32 `json:"cpuSt"`
|
||||
|
||||
TotalMem int `json:"totalMem"`
|
||||
FreeMem int `json:"freeMem"`
|
||||
UsedMem int `json:"usedMem"`
|
||||
CacheMem int `json:"cacheMem"`
|
||||
|
||||
TotalSwap int `json:"totalSwap"`
|
||||
FreeSwap int `json:"freeSwap"`
|
||||
UsedSwap int `json:"usedSwap"`
|
||||
AvailMem int `json:"availMem"`
|
||||
}
|
||||
|
||||
func (c *Cli) GetTop() *Top {
|
||||
res, _ := c.Run("top -b -n 1 | head -5")
|
||||
topTemp := "top - {upAndUsers}, load average: {loadavg}\n" +
|
||||
"Tasks:{totalTask} total,{runningTask} running,{sleepingTask} sleeping,{stoppedTask} stopped,{zombieTask} zombie\n" +
|
||||
"%Cpu(s):{cpuUs} us,{cpuSy} sy,{cpuNi} ni,{cpuId} id,{cpuWa} wa,{cpuHi} hi,{cpuSi} si,{cpuSt} st\n" +
|
||||
"KiB Mem :{totalMem} total,{freeMem} free,{usedMem} used,{cacheMem} buff/cache\n" +
|
||||
"KiB Swap:{totalSwap} total,{freeSwap} free,{usedSwap} used. {availMem} avail Mem \n"
|
||||
resMap := make(map[string]interface{})
|
||||
utils.ReverStrTemplate(topTemp, *res, resMap)
|
||||
|
||||
//17:14:07 up 5 days, 6:30, 2
|
||||
timeUpAndUserStr := resMap["upAndUsers"].(string)
|
||||
timeUpAndUser := strings.Split(timeUpAndUserStr, "up")
|
||||
time := utils.StrTrim(timeUpAndUser[0])
|
||||
upAndUsers := strings.Split(timeUpAndUser[1], ",")
|
||||
up := utils.StrTrim(upAndUsers[0]) + upAndUsers[1]
|
||||
users, _ := strconv.Atoi(utils.StrTrim(strings.Split(utils.StrTrim(upAndUsers[2]), " ")[0]))
|
||||
// 0.03, 0.04, 0.05
|
||||
loadavgs := strings.Split(resMap["loadavg"].(string), ",")
|
||||
oneMinLa, _ := strconv.ParseFloat(loadavgs[0], 32)
|
||||
fiveMinLa, _ := strconv.ParseFloat(utils.StrTrim(loadavgs[1]), 32)
|
||||
fifMinLa, _ := strconv.ParseFloat(utils.StrTrim(loadavgs[2]), 32)
|
||||
|
||||
top := &Top{Time: time, Up: up, NowUsers: users, OneMinLoadavg: float32(oneMinLa), FiveMinLoadavg: float32(fiveMinLa), FifteenMinLoadavg: float32(fifMinLa)}
|
||||
err := utils.Map2Struct(resMap, top)
|
||||
biz.BizErrIsNil(err, "解析top出错")
|
||||
return top
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
// 系统版本
|
||||
SysVersion SystemVersion
|
||||
// top信息
|
||||
Top Top
|
||||
}
|
||||
@@ -199,7 +199,6 @@ func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) {
|
||||
|
||||
func (sws *LogicSshWsSession) Wait(quitChan chan bool) {
|
||||
if err := sws.session.Wait(); err != nil {
|
||||
logs.Error("ssh session wait failed: ", err)
|
||||
setQuit(quitChan)
|
||||
}
|
||||
}
|
||||
95
mock-server/main.go
Normal file
95
mock-server/main.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"mayfly-go/base/rediscli"
|
||||
"mayfly-go/base/utils/yml"
|
||||
_ "mayfly-go/mock-server/routers"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/beego/beego/v2/server/web/context"
|
||||
"github.com/beego/beego/v2/server/web/filter/cors"
|
||||
"github.com/go-redis/redis"
|
||||
// _ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// 启动配置参数
|
||||
type StartConfigParam struct {
|
||||
ConfigFilePath string // 配置文件路径
|
||||
}
|
||||
|
||||
// yaml配置文件映射对象
|
||||
type Config struct {
|
||||
Server struct {
|
||||
Port int `yaml:"port"`
|
||||
}
|
||||
Redis struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Password string `yaml:"password"`
|
||||
Db int `yaml:"db"`
|
||||
}
|
||||
}
|
||||
|
||||
// 启动可执行文件时的参数
|
||||
var startConfigParam *StartConfigParam
|
||||
|
||||
// 配置文件映射对象
|
||||
var ymlConfig Config
|
||||
|
||||
// 获取执行可执行文件时,指定的启动参数
|
||||
func getStartConfig() *StartConfigParam {
|
||||
configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
|
||||
flag.Parse()
|
||||
// 获取配置文件绝对路径
|
||||
path, _ := filepath.Abs(*configFilePath)
|
||||
sc := &StartConfigParam{ConfigFilePath: path}
|
||||
return sc
|
||||
}
|
||||
|
||||
func init() {
|
||||
configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
|
||||
flag.Parse()
|
||||
// 获取启动参数中,配置文件的绝对路径
|
||||
path, _ := filepath.Abs(*configFilePath)
|
||||
startConfigParam = &StartConfigParam{ConfigFilePath: path}
|
||||
// 读取配置文件信息
|
||||
yc := &Config{}
|
||||
if err := yml.LoadYml(startConfigParam.ConfigFilePath, yc); err != nil {
|
||||
panic(fmt.Sprintf("读取配置文件[%s]失败: %s", startConfigParam.ConfigFilePath, err.Error()))
|
||||
}
|
||||
ymlConfig = *yc
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 设置redis客户端
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", ymlConfig.Redis.Host, ymlConfig.Redis.Port),
|
||||
Password: ymlConfig.Redis.Password, // no password set
|
||||
DB: ymlConfig.Redis.Db, // use default DB
|
||||
})
|
||||
rediscli.SetCli(rdb)
|
||||
|
||||
web.InsertFilter("/*", web.BeforeRouter, TransparentStatic)
|
||||
// 跨域配置
|
||||
web.InsertFilter("/**", web.BeforeRouter, cors.Allow(&cors.Options{
|
||||
AllowAllOrigins: true,
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
|
||||
ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
web.Run()
|
||||
}
|
||||
|
||||
// 解决beego无法访问根目录静态文件
|
||||
func TransparentStatic(ctx *context.Context) {
|
||||
if strings.Index(ctx.Request.URL.Path, "api/") >= 0 {
|
||||
return
|
||||
}
|
||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, "static/"+ctx.Request.URL.Path)
|
||||
}
|
||||
BIN
mock-server/mock-server
Executable file
BIN
mock-server/mock-server
Executable file
Binary file not shown.
37
mock-server/models/machine.go
Normal file
37
mock-server/models/machine.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/rediscli"
|
||||
)
|
||||
|
||||
const machineKey = "ccbscf:machines"
|
||||
|
||||
type Machine struct {
|
||||
Name string `json:"name"`
|
||||
Ip string `json:"ip"` // IP地址
|
||||
Username string `json:"username"` // 用户名
|
||||
Password string `json:"-"`
|
||||
Port int `json:"port"` // 端口号
|
||||
}
|
||||
|
||||
func (c *Machine) CreateMachine() {
|
||||
biz.IsTrue(!rediscli.HExist(machineKey, c.Ip), "该机器已存在")
|
||||
val, _ := json.Marshal(c)
|
||||
rediscli.HSet(machineKey, c.Ip, val)
|
||||
}
|
||||
|
||||
func DeleteMachine(ip string) {
|
||||
rediscli.HDel(machineKey, ip)
|
||||
}
|
||||
|
||||
func GetMachineByIp(ip string) *Machine {
|
||||
machine := &Machine{}
|
||||
json.Unmarshal([]byte(rediscli.HGet(machineKey, ip)), machine)
|
||||
return machine
|
||||
}
|
||||
|
||||
func GetMachineList() map[string]string {
|
||||
return rediscli.HGetAll(machineKey)
|
||||
}
|
||||
55
mock-server/routers/commentsRouter_controllers.go
Normal file
55
mock-server/routers/commentsRouter_controllers.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
"github.com/beego/beego/v2/server/web/context/param"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"] = append(beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"],
|
||||
beego.ControllerComments{
|
||||
Method: "UpdateMockData",
|
||||
Router: "/api/mock-datas",
|
||||
AllowHTTPMethods: []string{"put"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"] = append(beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"],
|
||||
beego.ControllerComments{
|
||||
Method: "CreateMockData",
|
||||
Router: "/api/mock-datas",
|
||||
AllowHTTPMethods: []string{"post"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"] = append(beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"],
|
||||
beego.ControllerComments{
|
||||
Method: "GetAllData",
|
||||
Router: "/api/mock-datas",
|
||||
AllowHTTPMethods: []string{"get"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"] = append(beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"],
|
||||
beego.ControllerComments{
|
||||
Method: "GetMockData",
|
||||
Router: "/api/mock-datas/:method",
|
||||
AllowHTTPMethods: []string{"get"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"] = append(beego.GlobalControllerRouter["mayfly-go/mock-server/controllers:MockController"],
|
||||
beego.ControllerComments{
|
||||
Method: "DeleteMockData",
|
||||
Router: "/api/mock-datas/:method",
|
||||
AllowHTTPMethods: []string{"delete"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
}
|
||||
17
mock-server/routers/router.go
Normal file
17
mock-server/routers/router.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"mayfly-go/mock-server/controllers"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
func init() {
|
||||
web.Include(&controllers.MockController{})
|
||||
|
||||
mock := &controllers.MockController{}
|
||||
web.Router("/api/mock-datas", mock, "post:CreateMockData")
|
||||
web.Router("/api/mock-datas/?:method", mock, "get:GetMockData")
|
||||
web.Router("/api/mock-datas", mock, "put:UpdateMockData")
|
||||
web.Router("/api/mock-datas/?:method", mock, "delete:DeleteMockData")
|
||||
}
|
||||
BIN
mock-server/static/favicon.ico
Normal file
BIN
mock-server/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
1
mock-server/static/index.html
Normal file
1
mock-server/static/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>mayfly-go-front</title><link href="/static/css/chunk-09c3f704.f03c28a3.css" rel="prefetch"><link href="/static/css/chunk-589cf4a2.676f6792.css" rel="prefetch"><link href="/static/css/chunk-76193938.2d81c5bb.css" rel="prefetch"><link href="/static/css/chunk-7f8e443f.53f73f21.css" rel="prefetch"><link href="/static/css/chunk-e3082940.1463bb24.css" rel="prefetch"><link href="/static/js/chunk-09c3f704.a1d504c3.js" rel="prefetch"><link href="/static/js/chunk-589cf4a2.b6d10755.js" rel="prefetch"><link href="/static/js/chunk-6e9f0a70.cf14b007.js" rel="prefetch"><link href="/static/js/chunk-76193938.f9c11d74.js" rel="prefetch"><link href="/static/js/chunk-7f8e443f.2c011f90.js" rel="prefetch"><link href="/static/js/chunk-e3082940.ee363d97.js" rel="prefetch"><link href="/static/css/app.b4088619.css" rel="preload" as="style"><link href="/static/css/chunk-vendors.16da611a.css" rel="preload" as="style"><link href="/static/js/app.3ffb27d0.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.bf6005f5.js" rel="preload" as="script"><link href="/static/css/chunk-vendors.16da611a.css" rel="stylesheet"><link href="/static/css/app.b4088619.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but mayfly-go-front doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.bf6005f5.js"></script><script src="/static/js/app.3ffb27d0.js"></script></body></html>
|
||||
1
mock-server/static/static/css/app.b4088619.css
Normal file
1
mock-server/static/static/css/app.b4088619.css
Normal file
@@ -0,0 +1 @@
|
||||
#app{background-color:#222d32}.main{display:flex}.main .el-menu:not(.el-menu--collapse){width:230px}.main .app{width:100%;background-color:#ecf0f5}.main .aside{position:fixed;margin-top:50px;z-index:10;background-color:#222d32;transition:all .3s ease-in-out}.main .aside .menu{overflow-y:auto;height:100vh}.main .app-body{margin-left:230px;transition:margin-left .3s ease-in-out}.main .main-container{margin-top:88px;padding:2px;min-height:calc(100vh - 88px)}.header{width:100%;position:fixed;display:flex;z-index:10}.header,.header .logo{height:50px;background-color:#303643}.header .logo{width:230px;text-align:center;line-height:50px;color:#fff;transition:all .3s ease-in-out}.header .logo .min{display:none}.header .right{position:absolute;right:0}.header .header-btn{overflow:hidden;height:50px;display:inline-block;text-align:center;line-height:50px;cursor:pointer;padding:0 14px;color:#fff}.header .header-btn .el-badge__content{top:14px;right:7px;text-align:center;font-size:9px;padding:0 3px;background-color:#00a65a;color:#fff;border:none;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.header .header-btn:hover{background-color:#222d32}.menu{border-right:none;-moz-user-select:-moz-none;-moz-user-select:none;-o-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.el-menu--vertical{min-width:190px}.setting-category{padding:10px 0;border-bottom:1px solid #eee}#mainContainer iframe{border:none;outline:none;width:100%;height:100%;position:absolute;background-color:#ecf0f5}.el-menu-item,.el-submenu__title{font-weight:500}#nav-bar{margin-top:50px;height:38px;width:100%;z-index:8;background:#fff;box-shadow:0 1px 3px 0 rgba(0,0,0,.12),0 0 3px 0 rgba(0,0,0,.04);position:fixed;top:0}*{padding:0;margin:0;outline:none;box-sizing:border-box}body{font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif}a{color:#3c8dbc;text-decoration:none}::-webkit-scrollbar{width:4px;height:8px;background-color:#f5f5f5}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgba(0,0,0,.3);background-color:#f5f5f5}.el-menu .fa{vertical-align:middle;margin-right:5px;width:24px;text-align:center}.el-menu .fa:not(.is-children){font-size:14px}.gray-mode{filter:grayscale(100%)}.fade-enter-active,.fade-leave-active{transition:opacity .2s ease-in-out}.fade-enter,.fade-leave-to{opacity:0}.none-select{moz-user-select:-moz-none;-moz-user-select:none;-o-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.toolbar{width:100%;padding:8px;background-color:#fff;overflow:hidden;line-height:32px;border:1px solid #e6ebf5}.fl{float:left}
|
||||
@@ -0,0 +1 @@
|
||||
.cm-s-panda-syntax{background:#292a2b;color:#e6e6e6;line-height:1.5;font-family:Operator Mono,Source Code Pro,Menlo,Monaco,Consolas,Courier New,monospace}.cm-s-panda-syntax .CodeMirror-cursor{border-color:#ff2c6d}.cm-s-panda-syntax .CodeMirror-activeline-background{background:rgba(99,123,156,.1)}.cm-s-panda-syntax .CodeMirror-selected{background:#fff}.cm-s-panda-syntax .cm-comment{font-style:italic;color:#676b79}.cm-s-panda-syntax .cm-operator{color:#f3f3f3}.cm-s-panda-syntax .cm-string{color:#19f9d8}.cm-s-panda-syntax .cm-string-2{color:#ffb86c}.cm-s-panda-syntax .cm-tag{color:#ff2c6d}.cm-s-panda-syntax .cm-meta{color:#b084eb}.cm-s-panda-syntax .cm-number{color:#ffb86c}.cm-s-panda-syntax .cm-atom{color:#ff2c6d}.cm-s-panda-syntax .cm-keyword{color:#ff75b5}.cm-s-panda-syntax .cm-variable{color:#ffb86c}.cm-s-panda-syntax .cm-type,.cm-s-panda-syntax .cm-variable-2,.cm-s-panda-syntax .cm-variable-3{color:#ff9ac1}.cm-s-panda-syntax .cm-def{color:#e6e6e6}.cm-s-panda-syntax .cm-property{color:#f3f3f3}.cm-s-panda-syntax .cm-attribute,.cm-s-panda-syntax .cm-unit{color:#ffb86c}.cm-s-panda-syntax .CodeMirror-matchingbracket{border-bottom:1px dotted #19f9d8;padding-bottom:2px;color:#e6e6e6}.cm-s-panda-syntax .CodeMirror-gutters{background:#292a2b;border-right-color:hsla(0,0%,100%,.1)}.cm-s-panda-syntax .CodeMirror-linenumber{color:#e6e6e6;opacity:.6}.CodeMirror-lint-markers{width:16px}.CodeMirror-lint-tooltip{background-color:#ffd;border:1px solid #000;border-radius:4px 4px 4px 4px;color:#000;font-family:monospace;font-size:10pt;overflow:hidden;padding:2px 5px;position:fixed;white-space:pre;white-space:pre-wrap;z-index:100;max-width:600px;opacity:0;transition:opacity .4s;-moz-transition:opacity .4s;-webkit-transition:opacity .4s;-o-transition:opacity .4s;-ms-transition:opacity .4s}.CodeMirror-lint-mark{background-position:0 100%;background-repeat:repeat-x}.CodeMirror-lint-mark-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=")}.CodeMirror-lint-mark-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")}.CodeMirror-lint-marker{background-position:50%;background-repeat:no-repeat;cursor:pointer;display:inline-block;height:16px;width:16px;vertical-align:middle;position:relative}.CodeMirror-lint-message{padding-left:18px;background-position:0 0;background-repeat:no-repeat}.CodeMirror-lint-marker-warning,.CodeMirror-lint-message-warning{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=")}.CodeMirror-lint-marker-error,.CodeMirror-lint-message-error{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=")}.CodeMirror-lint-marker-multiple{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");background-repeat:no-repeat;background-position:100% 100%;width:100%;height:100%}#jsonedit .CodeMirror{overflow-y:scroll!important;height:400px!important}.el-dialog__body{padding:2px 2px}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user