├── temp └── logs │ └── job │ └── 15.pid ├── _config.yml ├── tools ├── int.go ├── int64.go ├── float64.go ├── app │ ├── msg │ │ └── message.go │ ├── model.go │ └── return.go ├── config │ ├── log.go │ ├── jwt.go │ ├── application.go │ ├── database.go │ └── config.go ├── env.go ├── url.go ├── command │ ├── task_test.go │ ├── command_test.go │ ├── task.go │ └── command.go ├── ip.go ├── captcha │ └── captcha.go ├── gois │ └── is.go ├── string.go ├── logger.go ├── user.go ├── gofile │ └── file.go ├── utils.go ├── ws │ └── webSocket.go └── gostring │ └── string.go ├── database ├── sqlite3.go ├── drill.go └── mysql.go ├── global └── orm │ └── db.go ├── .gitignore ├── handler ├── ping.go └── nofound.go ├── README.md ├── apis ├── tpl │ └── tpl.go ├── system │ ├── captcha.go │ ├── index.go │ ├── rolemenu.go │ ├── info.go │ ├── config.go │ ├── dict │ │ ├── dictType.go │ │ └── dictData.go │ ├── menu.go │ └── post.go ├── deploy │ ├── websocketUpgrader.go │ ├── server.go │ └── deploytask.go ├── common │ ├── inspace.go │ ├── queryForm.go │ ├── hook.go │ └── common.go ├── tools │ ├── dbcolumns.go │ ├── dbtables.go │ └── gen.go ├── monitor │ └── server.go ├── article │ └── article.go ├── process │ └── classify.go └── log │ └── operlog.go ├── pkg ├── task │ ├── send.go │ ├── server.go │ └── worker │ │ ├── worker.go │ │ └── tasks.go ├── file │ ├── file.go │ └── getdir.go ├── convert │ └── convert.go ├── cronjob │ └── testjob.go ├── cache │ └── freecache.go ├── hash │ ├── string.go │ ├── byte.go │ ├── string_test.go │ ├── byte_test.go │ ├── file_test.go │ └── file.go ├── ldap │ ├── login.go │ ├── connection.go │ ├── ldapFieldsMap.go │ ├── updatePwd.go │ └── search.go ├── service │ ├── task.go │ ├── getState.go │ └── getVariableValue.go ├── pagination │ ├── params.go │ └── pagination.go ├── jsonTime │ └── JSONTime.go ├── casbin │ └── mycasbin.go └── notify │ ├── email │ └── email.go │ └── send.go ├── static └── go.txt ├── Makefile ├── config ├── rbac_model.conf ├── settings.yml └── settings-prod.yml ├── middleware ├── init.go ├── requestid.go ├── auth.go ├── permission.go ├── customerror.go ├── header.go └── logger.go ├── models ├── process │ ├── classify.go │ ├── tplData.go │ ├── task.go │ ├── tpl.go │ ├── history.go │ ├── circulationHistory.go │ ├── process.go │ └── workOrder.go ├── base │ └── base.go ├── casbinrule.go ├── login.go ├── gorm │ └── gorm.go ├── server_group.go ├── roledept.go ├── initdb.go ├── deploytask.go ├── server.go ├── datascope.go ├── tools │ ├── dbtables.go │ ├── dbcolumns.go │ └── syscolumns.go ├── loginlog.go └── article.go ├── router ├── process │ ├── classify.go │ ├── task.go │ ├── tpl.go │ ├── process.go │ └── workOrder.go ├── init_router.go ├── article_router.go ├── article_router.go-copy ├── deploy_router.go ├── router.go └── releasecicd_router.go ├── sonar-project.properties ├── cmd ├── cobra.go ├── migrate │ └── server.go └── api │ └── server.go ├── template ├── js.go.template ├── router.go.template └── api.go.template ├── test ├── gen_test.go ├── model.go.template └── api.go.template ├── render └── render.go ├── go.mod ├── main.go └── module └── deploy ├── build.go ├── apply.go └── cmdexe.go /temp/logs/job/15.pid: -------------------------------------------------------------------------------- 1 | 䆈 -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /tools/int.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "strconv" 4 | 5 | func IntToString(e int) string { 6 | return strconv.Itoa(e) 7 | } -------------------------------------------------------------------------------- /database/sqlite3.go: -------------------------------------------------------------------------------- 1 | // +build sqlite3 2 | 3 | package database 4 | 5 | import ( 6 | _ "github.com/jinzhu/gorm/dialects/sqlite" 7 | ) 8 | -------------------------------------------------------------------------------- /tools/int64.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "strconv" 4 | 5 | func Int64ToString(e int64) string { 6 | return strconv.FormatInt(e, 10) 7 | } -------------------------------------------------------------------------------- /global/orm/db.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | ) 6 | 7 | var Eloquent *gorm.DB 8 | var MysqlConn string 9 | 10 | -------------------------------------------------------------------------------- /tools/float64.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "strconv" 4 | 5 | func Float64ToString(e float64) string { 6 | return strconv.FormatFloat(e, 'E', -1, 64) 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | static/uploadfile 3 | *.exe 4 | main 5 | temp/ 6 | !temp 7 | vendor 8 | config/settings.dev.yml 9 | pashash 10 | devnotes.txt 11 | *.log -------------------------------------------------------------------------------- /handler/ping.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | func Ping(c *gin.Context) { 8 | c.JSON(200, gin.H{ 9 | "message": "ok", 10 | }) 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### vAdmin[后端] -- CICD过程指标度量、自动化收集、可视化呈现 2 | 3 | #### 配置 4 | ... 5 | 6 | #### 编译 7 | `go build main.go` 8 | 9 | #### 迁移 10 | `main migrate -c config/settings.yml` 11 | 12 | #### 启动 13 | `main server -c config/settings.yml` 14 | -------------------------------------------------------------------------------- /tools/app/msg/message.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | var ( 4 | CreatedSuccess = "创建成功!" 5 | UpdatedSuccess = "更新成功!" 6 | DeletedSuccess = "删除成功!" 7 | DeletedFail = "删除失败!" 8 | GetSuccess = "查询成功!" 9 | NotFound = "未找到相关内容或者数据为空!" 10 | ) 11 | -------------------------------------------------------------------------------- /apis/tpl/tpl.go: -------------------------------------------------------------------------------- 1 | package tpl 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | /* 10 | @Author : Rongxin Linghu 11 | */ 12 | 13 | func Tpl(c *gin.Context) { 14 | c.HTML(http.StatusOK, "index.html", gin.H{}) 15 | } 16 | -------------------------------------------------------------------------------- /tools/config/log.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/spf13/viper" 4 | 5 | type Log struct { 6 | Dir string 7 | } 8 | 9 | func InitLog(cfg *viper.Viper) *Log { 10 | return &Log{ 11 | Dir: cfg.GetString("dir"), 12 | } 13 | } 14 | 15 | var LogConfig = new(Log) 16 | -------------------------------------------------------------------------------- /tools/env.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | type ( 4 | Mode string 5 | ) 6 | 7 | const ( 8 | ModeDev Mode = "dev" //开发模式 9 | ModeTest Mode = "test" //测试模式 10 | ModeProd Mode = "prod" //生产模式 11 | Mysql = "mysql" //mysql数据库标识 12 | Sqlite = "sqlite" //sqlite 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/task/send.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | */ 6 | 7 | import ( 8 | "context" 9 | "vAdmin/pkg/task/worker" 10 | ) 11 | 12 | func Send(classify string, scriptPath string, params string) { 13 | worker.SendTask(context.Background(), classify, scriptPath, params) 14 | } 15 | -------------------------------------------------------------------------------- /static/go.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ,---. 5 | ,----._,. ' ,'\ 6 | / / ' / / / | 7 | | : |. ; ,. : 8 | | | .\ .' | |: : 9 | . ; '; |' | .; : 10 | ' . . || : | 11 | `---`-'| | \ \ / 12 | .'__/\_: | `----' 13 | | : : 14 | \ \ / 15 | `--`-' 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT:=vAdmin 2 | 3 | .PHONY: build 4 | build: 5 | CGO_ENABLED=0 go build -o vAdmin main.go 6 | build-sqlite: 7 | go build -tags sqlite3 -o vAdmin main.go 8 | #.PHONY: test 9 | #test: 10 | # go test -v ./... -cover 11 | 12 | #.PHONY: docker 13 | #docker: 14 | # docker build . -t vAdmin:latest 15 | -------------------------------------------------------------------------------- /config/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [policy_effect] 8 | e = some(where (p.eft == allow)) 9 | 10 | [matchers] 11 | m = r.sub == p.sub && (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) && (r.act == p.act || p.act == "*") -------------------------------------------------------------------------------- /tools/config/jwt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | ) 6 | 7 | type Jwt struct { 8 | Secret string 9 | Timeout int64 10 | } 11 | 12 | func InitJwt(cfg *viper.Viper) *Jwt { 13 | return &Jwt{ 14 | Secret: cfg.GetString("secret"), 15 | Timeout: cfg.GetInt64("timeout"), 16 | } 17 | } 18 | 19 | var JwtConfig = new(Jwt) 20 | -------------------------------------------------------------------------------- /pkg/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import( 4 | "io/ioutil" 5 | ) 6 | 7 | // 获取文件夹内各文件的文件名 8 | func GetFolderSubFileName(path string) (fileNames []string,err error) { 9 | dirList, err := ioutil.ReadDir(path) 10 | if err != nil { 11 | return 12 | } 13 | for _, v := range dirList { 14 | fileNames=append(fileNames,v.Name()) 15 | } 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /pkg/convert/convert.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import( 4 | "encoding/binary" 5 | ) 6 | 7 | // int64 转 byte 8 | func Int64ToBytes(i int64) []byte { 9 | var buf = make([]byte, 8) 10 | binary.BigEndian.PutUint64(buf, uint64(i)) 11 | return buf 12 | } 13 | 14 | // byte 转 int64 15 | func BytesToInt64(buf []byte) int64 { 16 | return int64(binary.BigEndian.Uint64(buf)) 17 | } -------------------------------------------------------------------------------- /handler/nofound.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | jwt "vAdmin/pkg/jwtauth" 6 | "log" 7 | "net/http" 8 | ) 9 | 10 | func NoFound(c *gin.Context) { 11 | claims := jwt.ExtractClaims(c) 12 | log.Printf("NoRoute claims: %#v\n", claims) 13 | c.JSON(http.StatusOK, gin.H{ 14 | "code": "404", 15 | "message": "not found", 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/cronjob/testjob.go: -------------------------------------------------------------------------------- 1 | package cronjob 2 | 3 | import ( 4 | "github.com/robfig/cron/v3" 5 | "log" 6 | ) 7 | 8 | func TestJob(c *cron.Cron) { 9 | id, err := c.AddFunc("1 * * * *", func() { 10 | 11 | log.Println("Every hour on the one hour") 12 | }) 13 | if err != nil { 14 | log.Println(err) 15 | log.Println("start error") 16 | } else { 17 | log.Println("Start Success; ID: %v", id) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cache/freecache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/coocood/freecache" 5 | ) 6 | 7 | var cache = freecache.NewCache(100 * 1024 * 1024) 8 | 9 | func Set(key, value []byte, expireSeconds int) error { 10 | return cache.Set(key, value, expireSeconds) 11 | } 12 | 13 | func Get(key []byte) ([]byte, error) { 14 | return cache.Get(key) 15 | } 16 | 17 | func Del(key []byte) bool { 18 | return cache.Del(key) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/task/server.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | */ 6 | 7 | import ( 8 | "vAdmin/pkg/logger" 9 | "vAdmin/pkg/task/worker" 10 | ) 11 | 12 | func Start() { 13 | // 1. 启动服务,连接redis 14 | worker.StartServer() 15 | 16 | // 2. 启动异步调度 17 | taskWorker := worker.NewAsyncTaskWorker(10) 18 | err := taskWorker.Launch() 19 | if err != nil { 20 | logger.Errorf("启动machinery失败,%v", err.Error()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /middleware/init.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | ) 7 | 8 | 9 | func InitMiddleware(r *gin.Engine) { 10 | // 日志处理 11 | r.Use(LoggerToFile()) 12 | // 自定义错误处理 13 | r.Use(CustomError) 14 | // NoCache is a middleware function that appends headers 15 | r.Use(NoCache) 16 | // 跨域处理 17 | r.Use(Options) 18 | // Secure is a middleware function that appends security 19 | r.Use(Secure) 20 | // Set X-Request-Id header 21 | r.Use(RequestId()) 22 | } -------------------------------------------------------------------------------- /models/process/classify.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "vAdmin/models/base" 5 | ) 6 | 7 | /* 8 | @Author : Rongxin Linghu 9 | */ 10 | 11 | // 流程分类 12 | type Classify struct { 13 | base.Model 14 | Name string `gorm:"column:name; type: varchar(128)" json:"name" form:"name"` // 分类名称 15 | Creator int `gorm:"column:creator; type: int(11)" json:"creator" form:"creator"` // 创建者 16 | } 17 | 18 | func (Classify) TableName() string { 19 | return "p_process_classify" 20 | } 21 | -------------------------------------------------------------------------------- /tools/url.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "strings" 6 | ) 7 | 8 | //获取URL中批量id并解析 9 | func IdsStrToIdsIntGroup(key string, c *gin.Context) []int { 10 | return idsStrToIdsIntGroup(c.Param(key)) 11 | } 12 | 13 | 14 | func idsStrToIdsIntGroup(keys string) []int { 15 | IDS := make([]int, 0) 16 | ids := strings.Split(keys, ",") 17 | for i := 0; i < len(ids); i++ { 18 | ID, _ := StringToInt(ids[i]) 19 | IDS = append(IDS, ID) 20 | } 21 | return IDS 22 | } -------------------------------------------------------------------------------- /apis/system/captcha.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/tools" 6 | _ "vAdmin/tools" 7 | "vAdmin/tools/app" 8 | "vAdmin/tools/captcha" 9 | ) 10 | 11 | func GenerateCaptchaHandler(c *gin.Context) { 12 | id, b64s, err := captcha.DriverDigitFunc() 13 | tools.HasError(err, "验证码获取失败", 500) 14 | //id, b64s, _ := captcha.DriverDigitFunc() 15 | app.Custum(c, gin.H{ 16 | "code": 200, 17 | "data": b64s, 18 | "id": id, 19 | "msg": "success", 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/hash/string.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | /* 对字符串取hash值 */ 4 | 5 | // Md5String 获取字符串md5值 6 | func Md5String(s string) string { 7 | return Md5Byte([]byte(s)) 8 | } 9 | 10 | // Sha1String 获取字符串sha1值 11 | func Sha1String(s string) string { 12 | return Sha1Byte([]byte(s)) 13 | } 14 | 15 | // Sha256String 获取字符串sha256值 16 | func Sha256String(s string) string { 17 | return Sha256Byte([]byte(s)) 18 | } 19 | 20 | // Sha512String 获取字符串sha512值 21 | func Sha512String(s string) string { 22 | return Sha512Byte([]byte(s)) 23 | } 24 | -------------------------------------------------------------------------------- /models/base/base.go: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : Rongxin Linghu 3 | */ 4 | 5 | package base 6 | 7 | import ( 8 | "vAdmin/pkg/jsonTime" 9 | ) 10 | 11 | type Model struct { 12 | Id int `gorm:"primary_key;AUTO_INCREMENT;column:id" json:"id" form:"id"` 13 | CreatedAt jsonTime.JSONTime `gorm:"column:create_time" json:"create_time" form:"create_time"` 14 | UpdatedAt jsonTime.JSONTime `gorm:"column:update_time" json:"update_time" form:"update_time"` 15 | DeletedAt *jsonTime.JSONTime `gorm:"column:delete_time" sql:"index" json:"-"` 16 | } 17 | -------------------------------------------------------------------------------- /apis/deploy/websocketUpgrader.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "github.com/gorilla/websocket" 5 | //"github.com/gin-gonic/gin" 6 | "net/http" 7 | ) 8 | 9 | var ( 10 | upGrader websocket.Upgrader 11 | wsUpdateInterval int64 12 | ) 13 | 14 | func InitWs() { 15 | 16 | /* 17 | router := gin.Default() 18 | router.GET("/api/v1/deployws", DeployStart) 19 | router.Run(":3000") 20 | */ 21 | upGrader = websocket.Upgrader{ 22 | // 允许跨域 23 | CheckOrigin: func(r *http.Request) bool { 24 | return true 25 | }, 26 | } 27 | wsUpdateInterval = 10 28 | } 29 | -------------------------------------------------------------------------------- /models/casbinrule.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | //casbin_rule 4 | type CasbinRule struct { 5 | PType string `json:"p_type" gorm:"type:varchar(100);"` 6 | V0 string `json:"v0" gorm:"type:varchar(100);"` 7 | V1 string `json:"v1" gorm:"type:varchar(100);"` 8 | V2 string `json:"v2" gorm:"type:varchar(100);"` 9 | V3 string `json:"v3" gorm:"type:varchar(100);"` 10 | V4 string `json:"v4" gorm:"type:varchar(100);"` 11 | V5 string `json:"v5" gorm:"type:varchar(100);"` 12 | } 13 | 14 | func (CasbinRule) TableName() string { 15 | return "casbin_rule" 16 | } 17 | -------------------------------------------------------------------------------- /tools/command/task_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package command 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestTaskRun(t *testing.T) { 12 | cmds := []string{ 13 | "echo 'syncd'", 14 | "whoami", 15 | "date", 16 | } 17 | task := TaskNew(cmds, 10) 18 | task.Run() 19 | if err := task.GetError(); err != nil { 20 | t.Errorf("cmd task running error: %s", err.Error()) 21 | } 22 | } -------------------------------------------------------------------------------- /pkg/ldap/login.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-ldap/ldap/v3" 7 | ) 8 | 9 | /* 10 | @Author : Rongxin Linghu 11 | */ 12 | 13 | func LdapLogin(username string, password string) (userInfo *ldap.Entry, err error) { 14 | err = ldapConnection() 15 | if err != nil { 16 | return 17 | } 18 | defer conn.Close() 19 | 20 | userInfo, err = searchRequest(username) 21 | if err != nil { 22 | return 23 | } 24 | 25 | err = conn.Bind(userInfo.DN, password) 26 | if err != nil { 27 | return nil, fmt.Errorf("用户或密码不正确。") 28 | } 29 | 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /pkg/service/task.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "vAdmin/pkg/task" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | /* 12 | @Author : Rongxin Linghu 13 | */ 14 | 15 | func ExecTask(taskList []string, params string) { 16 | for _, taskName := range taskList { 17 | filePath := fmt.Sprintf("%v/%v", viper.GetString("script.path"), taskName) 18 | if strings.HasSuffix(filePath, ".py") { 19 | task.Send("python", filePath, params) 20 | } else if strings.HasSuffix(filePath, ".sh") { 21 | task.Send("shell", filePath, params) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /router/process/classify.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | */ 6 | 7 | import ( 8 | "vAdmin/apis/process" 9 | "vAdmin/middleware" 10 | jwt "vAdmin/pkg/jwtauth" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func RegisterClassifyRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 16 | classify := v1.Group("/classify").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 17 | { 18 | classify.GET("", process.ClassifyList) 19 | classify.POST("", process.CreateClassify) 20 | classify.PUT("", process.UpdateClassify) 21 | classify.DELETE("", process.DeleteClassify) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.host.url=http://xxx.xx.xx 2 | sonar.sourceEncoding=UTF-8 3 | sonar.login=admin 4 | sonar.password=xxxxxxxxxx 5 | sonar.projectKey=vAdmin 6 | sonar.projectName=vAdmin 7 | sonar.projectVersion=1.0 8 | sonar.golint.reportPath=report.xml 9 | sonar.coverage.reportPath=coverage.xml 10 | sonar.coverage.dtdVerification=false 11 | sonar.test.reportPath=test.xml 12 | sonar.sources=./ 13 | sonar.sources.inclusions=**/**.go 14 | sonar.sources.exclusions=**/**_test.go,**/vendor/*.com/**,**/vendor/*.org/**,**/vendor/** 15 | sonar.tests=./ 16 | sonar.test.inclusions=**/**_test.go 17 | sonar.test.exclusions=**/vendor/*.com/**,**/vendor/*.org/**,**/vendor/** -------------------------------------------------------------------------------- /middleware/requestid.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/satori/go.uuid" 6 | ) 7 | 8 | func RequestId() gin.HandlerFunc { 9 | return func(c *gin.Context) { 10 | // Check for incoming header, use it if exists 11 | requestId := c.Request.Header.Get("X-Request-Id") 12 | 13 | // Create request id with UUID4 14 | if requestId == "" { 15 | u4 := uuid.NewV4() 16 | requestId = u4.String() 17 | } 18 | 19 | // Expose it for use in the application 20 | c.Set("X-Request-Id", requestId) 21 | 22 | // Set X-Request-Id header 23 | c.Writer.Header().Set("X-Request-Id", requestId) 24 | c.Next() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /router/init_router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/middleware" 6 | _ "vAdmin/pkg/jwtauth" 7 | "vAdmin/tools" 8 | ) 9 | 10 | func InitRouter() *gin.Engine { 11 | 12 | r := gin.New() 13 | middleware.InitMiddleware(r) 14 | // the jwt middleware 15 | authMiddleware, err := middleware.AuthInit() 16 | tools.HasError(err, "JWT Init Error", 500) 17 | 18 | // 注册系统路由 19 | InitSysRouter(r, authMiddleware) 20 | 21 | // 注册业务路由 22 | // TODO: 这里可存放业务路由,里边并无实际路由是有演示代码 23 | InitAppsRouter(r, authMiddleware) 24 | 25 | //InitarticleCheckRoleRouter(r, authMiddleware) 26 | //InitreleasecicdCheckRoleRouter(r, authMiddleware) 27 | return r 28 | } 29 | -------------------------------------------------------------------------------- /router/process/task.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "vAdmin/apis/process" 5 | "vAdmin/middleware" 6 | jwt "vAdmin/pkg/jwtauth" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | /* 12 | @Author : Rongxin Linghu 13 | */ 14 | 15 | func RegisterTaskRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 16 | taskRouter := v1.Group("/task").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 17 | { 18 | taskRouter.GET("", process.TaskList) 19 | taskRouter.GET("/details", process.TaskDetails) 20 | taskRouter.POST("", process.CreateTask) 21 | taskRouter.PUT("", process.UpdateTask) 22 | taskRouter.DELETE("", process.DeleteTask) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pkg/pagination/params.go: -------------------------------------------------------------------------------- 1 | package pagination 2 | 3 | import ( 4 | "vAdmin/pkg/logger" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | /* 10 | @Author : Rongxin Linghu 11 | */ 12 | 13 | func RequestParams(c *gin.Context) map[string]interface{} { 14 | params := make(map[string]interface{}, 10) 15 | 16 | if c.Request.Form == nil { 17 | if err := c.Request.ParseMultipartForm(32 << 20); err != nil { 18 | logger.Error(err) 19 | } 20 | } 21 | 22 | if len(c.Request.Form) > 0 { 23 | for key, value := range c.Request.Form { 24 | if key == "page" || key == "per_page" || key == "sort" { 25 | continue 26 | } 27 | params[key] = value[0] 28 | } 29 | } 30 | 31 | return params 32 | } 33 | -------------------------------------------------------------------------------- /models/process/tplData.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "encoding/json" 5 | "vAdmin/models/base" 6 | ) 7 | 8 | /* 9 | @Author : Rongxin Linghu 10 | */ 11 | 12 | // 工单绑定模版数据 13 | type TplData struct { 14 | base.Model 15 | WorkOrder int `gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"` // 工单ID 16 | FormStructure json.RawMessage `gorm:"column:form_structure; type: json" json:"form_structure" form:"form_structure"` // 表单结构 17 | FormData json.RawMessage `gorm:"column:form_data; type: json" json:"form_data" form:"form_data"` // 表单数据 18 | } 19 | 20 | func (TplData) TableName() string { 21 | return "p_work_order_tpl_data" 22 | } 23 | -------------------------------------------------------------------------------- /apis/common/inspace.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "github.com/gin-gonic/gin" 9 | "github.com/dreamans/syncd/render" 10 | "github.com/dreamans/syncd/module/project" 11 | ) 12 | 13 | func InSpaceCheck(c *gin.Context, spaceId int) bool { 14 | member := &project.Member{ 15 | UserId: c.GetInt("user_id"), 16 | SpaceId: spaceId, 17 | } 18 | if in := member.MemberInSpace(); !in { 19 | render.CustomerError(c, render.CODE_ERR_NO_PRIV, "user is not in the project space") 20 | return false 21 | } 22 | return true 23 | } -------------------------------------------------------------------------------- /tools/ip.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | func GetLocation(ip string) string { 11 | if ip == "127.0.0.1" || ip == "localhost" { 12 | return "内部IP" 13 | } 14 | resp, err := http.Get("https://restapi.amap.com/v3/ip?ip=" + ip + "&key=3fabc36c20379fbb9300c79b19d5d05e") 15 | if err != nil { 16 | panic(err) 17 | 18 | } 19 | defer resp.Body.Close() 20 | s, err := ioutil.ReadAll(resp.Body) 21 | fmt.Printf(string(s)) 22 | 23 | m := make(map[string]string) 24 | 25 | err = json.Unmarshal(s, &m) 26 | if err != nil { 27 | fmt.Println("Umarshal failed:", err) 28 | } 29 | if m["province"] == "" { 30 | return "未知位置" 31 | } 32 | return m["province"] + "-" + m["city"] 33 | } -------------------------------------------------------------------------------- /router/process/tpl.go: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : Rongxin Linghu 3 | */ 4 | 5 | package process 6 | 7 | import ( 8 | "vAdmin/apis/process" 9 | "vAdmin/middleware" 10 | 11 | //"vAdmin/apis/process" 12 | //"vAdmin/middleware" 13 | jwt "vAdmin/pkg/jwtauth" 14 | 15 | "github.com/gin-gonic/gin" 16 | ) 17 | 18 | func RegisterTplRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 19 | tplRouter := v1.Group("/tpl").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 20 | { 21 | tplRouter.GET("", process.TemplateList) 22 | tplRouter.POST("", process.CreateTemplate) 23 | tplRouter.PUT("", process.UpdateTemplate) 24 | tplRouter.DELETE("", process.DeleteTemplate) 25 | tplRouter.GET("/details", process.TemplateDetails) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /router/article_router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | //InitarticleCheckRoleRouter(r, authMiddleware) 4 | import ( 5 | "github.com/gin-gonic/gin" 6 | "vAdmin/middleware" 7 | "vAdmin/pkg/jwtauth" 8 | "vAdmin/apis/article" 9 | ) 10 | 11 | //业务 需要认证的路由 12 | func InitarticleCheckRoleRouter(v1 *gin.Engine, authMiddleware *jwtauth.GinJWTMiddleware) { 13 | 14 | v1auth := v1.Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 15 | { 16 | v1auth.GET("/api/v1/articleList", article.GetArticleList) 17 | v1auth.GET("/api/v1/article/:articleId", article.GetArticle) 18 | v1auth.POST("/api/v1/article", article.InsertArticle) 19 | v1auth.PUT("/api/v1/article", article.UpdateArticle) 20 | v1auth.DELETE("/api/v1/article/:articleId", article.DeleteArticle) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /router/process/process.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | */ 6 | 7 | import ( 8 | "vAdmin/apis/process" 9 | "vAdmin/middleware" 10 | jwt "vAdmin/pkg/jwtauth" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func RegisterProcessRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 16 | processRouter := v1.Group("/process").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 17 | { 18 | processRouter.GET("/classify", process.ClassifyProcessList) 19 | processRouter.GET("", process.ProcessList) 20 | processRouter.POST("", process.CreateProcess) 21 | processRouter.PUT("", process.UpdateProcess) 22 | processRouter.DELETE("", process.DeleteProcess) 23 | processRouter.GET("/details", process.ProcessDetails) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "vAdmin/handler" 5 | jwt "vAdmin/pkg/jwtauth" 6 | "vAdmin/tools/config" 7 | "time" 8 | ) 9 | 10 | func AuthInit() (*jwt.GinJWTMiddleware, error) { 11 | return jwt.New(&jwt.GinJWTMiddleware{ 12 | Realm: "test zone", 13 | Key: []byte(config.ApplicationConfig.JwtSecret), 14 | Timeout: time.Hour, 15 | MaxRefresh: time.Hour, 16 | PayloadFunc: handler.PayloadFunc, 17 | IdentityHandler: handler.IdentityHandler, 18 | Authenticator: handler.Authenticator, 19 | Authorizator: handler.Authorizator, 20 | Unauthorized: handler.Unauthorized, 21 | TokenLookup: "header: Authorization, query: token, cookie: jwt", 22 | TokenHeadName: "Bearer", 23 | TimeFunc: time.Now, 24 | }) 25 | 26 | } 27 | -------------------------------------------------------------------------------- /router/article_router.go-copy: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | //InitarticleCheckRoleRouter(r, authMiddleware) 4 | import ( 5 | "github.com/gin-gonic/gin" 6 | //_ "vAdmin/pkg/jwtauth" 7 | "vAdmin/middleware" 8 | "vAdmin/pkg/jwtauth" 9 | "vAdmin/apis/article" 10 | ) 11 | 12 | //业务 需要认证的路由 13 | func InitarticleCheckRoleRouter(v1 *gin.Engine, authMiddleware *jwtauth.GinJWTMiddleware) { 14 | 15 | v1auth := v1.Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 16 | { 17 | v1auth.GET("/api/v1/articleList", article.GetArticleList) 18 | v1auth.GET("/api/v1/article/:articleId", article.GetArticle) 19 | v1auth.POST("/api/v1/article", article.InsertArticle) 20 | v1auth.PUT("/api/v1/article", article.UpdateArticle) 21 | v1auth.DELETE("/api/v1/article/:articleId", article.DeleteArticle) 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /models/process/task.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "vAdmin/models/base" 5 | ) 6 | 7 | /* 8 | @Author : Rongxin Linghu 9 | */ 10 | 11 | // 任务 12 | type TaskInfo struct { 13 | base.Model 14 | Name string `gorm:"column:name; type: varchar(256)" json:"name" form:"name"` // 任务名称 15 | TaskType string `gorm:"column:task_type; type: varchar(45)" json:"task_type" form:"task_type"` // 任务类型 16 | Content string `gorm:"column:content; type: longtext" json:"content" form:"content"` // 任务内容 17 | Creator int `gorm:"column:creator; type: int(11)" json:"creator" form:"creator"` // 创建者 18 | Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注 19 | } 20 | 21 | func (TaskInfo) TableName() string { 22 | return "p_task_info" 23 | } 24 | -------------------------------------------------------------------------------- /pkg/file/getdir.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | func GetCurrentDirectory() string { 12 | dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) 13 | return strings.Replace(dir, "\\", "/", -1) 14 | } 15 | 16 | func GetRootDir() string { 17 | file, err := filepath.Abs(filepath.Dir(os.Args[0])) 18 | if err != nil { 19 | file = fmt.Sprintf(".%s", string(os.PathSeparator)) 20 | } else { 21 | file = fmt.Sprintf("%s%s", file, string(os.PathSeparator)) 22 | } 23 | return file 24 | } 25 | 26 | func GetExecFilePath() string { 27 | file, err := exec.LookPath(os.Args[0]) 28 | if err != nil { 29 | file = fmt.Sprintf(".%s", string(os.PathSeparator)) 30 | } else { 31 | file, _ = filepath.Abs(file) 32 | } 33 | return file 34 | } 35 | -------------------------------------------------------------------------------- /pkg/hash/byte.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/sha1" 6 | "crypto/sha256" 7 | "crypto/sha512" 8 | "encoding/hex" 9 | ) 10 | 11 | /* 对字节数组取hash值 */ 12 | 13 | // Md5Byte 获取字节数组md5值 14 | func Md5Byte(s []byte) string { 15 | h := md5.New() 16 | h.Write(s) 17 | return hex.EncodeToString(h.Sum(nil)) 18 | } 19 | 20 | // Sha1Byte 获取节数组sha1值 21 | func Sha1Byte(s []byte) string { 22 | h := sha1.New() 23 | h.Write(s) 24 | return hex.EncodeToString(h.Sum(nil)) 25 | } 26 | 27 | // Sha256Byte 获取节数组sha256值 28 | func Sha256Byte(s []byte) string { 29 | h := sha256.New() 30 | h.Write(s) 31 | return hex.EncodeToString(h.Sum(nil)) 32 | } 33 | 34 | // Sha512Byte 获取节数组sha512值 35 | func Sha512Byte(s []byte) string { 36 | h := sha512.New() 37 | h.Write(s) 38 | return hex.EncodeToString(h.Sum(nil)) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/jsonTime/JSONTime.go: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : Rongxin Linghu 3 | */ 4 | 5 | package jsonTime 6 | 7 | import ( 8 | "database/sql/driver" 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | // 重写MarshalJSON实现models json返回的时间格式 14 | type JSONTime struct { 15 | time.Time 16 | } 17 | 18 | func (t JSONTime) MarshalJSON() ([]byte, error) { 19 | formatted := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05")) 20 | return []byte(formatted), nil 21 | } 22 | 23 | func (t JSONTime) Value() (driver.Value, error) { 24 | var zeroTime time.Time 25 | if t.Time.UnixNano() == zeroTime.UnixNano() { 26 | return nil, nil 27 | } 28 | return t.Time, nil 29 | } 30 | 31 | func (t *JSONTime) Scan(v interface{}) error { 32 | value, ok := v.(time.Time) 33 | if ok { 34 | *t = JSONTime{Time: value} 35 | return nil 36 | } 37 | return fmt.Errorf("无法转换 %v 的时间格式", v) 38 | } 39 | -------------------------------------------------------------------------------- /tools/config/application.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/spf13/viper" 4 | 5 | type Application struct { 6 | ReadTimeout int 7 | WriterTimeout int 8 | Host string 9 | Port string 10 | Name string 11 | JwtSecret string 12 | Mode string 13 | DemoMsg string 14 | } 15 | 16 | func InitApplication(cfg *viper.Viper) *Application { 17 | return &Application{ 18 | ReadTimeout: cfg.GetInt("readTimeout"), 19 | WriterTimeout: cfg.GetInt("writerTimeout"), 20 | Host: cfg.GetString("host"), 21 | Port: cfg.GetString("port"), 22 | Name: cfg.GetString("name"), 23 | JwtSecret: cfg.GetString("jwtSecret"), 24 | Mode: cfg.GetString("mode"), 25 | DemoMsg: cfg.GetString("demoMsg"), 26 | } 27 | } 28 | 29 | var ApplicationConfig = new(Application) 30 | -------------------------------------------------------------------------------- /models/login.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | "vAdmin/tools" 6 | ) 7 | 8 | type Login struct { 9 | Username string `form:"UserName" json:"username" binding:"required"` 10 | Password string `form:"Password" json:"password" binding:"required"` 11 | Code string `form:"Code" json:"code" binding:""` 12 | UUID string `form:"UUID" json:"uuid" binding:"required"` 13 | } 14 | 15 | func (u *Login) GetUser() (user SysUser, role SysRole, e error) { 16 | 17 | e = orm.Eloquent.Table("sys_user").Where("username = ? ", u.Username).Find(&user).Error 18 | if e != nil { 19 | return 20 | } 21 | _, e = tools.CompareHashAndPassword(user.Password, u.Password) 22 | if e != nil { 23 | return 24 | } 25 | e = orm.Eloquent.Table("sys_role").Where("role_id = ? ", user.RoleId).First(&role).Error 26 | if e != nil { 27 | return 28 | } 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /models/process/tpl.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "encoding/json" 5 | "vAdmin/models/base" 6 | ) 7 | 8 | /* 9 | @Author : Rongxin Linghu 10 | */ 11 | 12 | // 模板 13 | type TplInfo struct { 14 | base.Model 15 | Name string `gorm:"column:name; type: varchar(128)" json:"name" form:"name" binding:"required"` // 模板名称 16 | FormStructure json.RawMessage `gorm:"column:form_structure; type: json" json:"form_structure" form:"form_structure" binding:"required"` // 表单结构 17 | Creator int `gorm:"column:creator; type: int(11)" json:"creator" form:"creator"` // 创建者 18 | Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注 19 | } 20 | 21 | func (TplInfo) TableName() string { 22 | return "p_tpl_info" 23 | } 24 | -------------------------------------------------------------------------------- /router/deploy_router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/apis/deploy" 6 | "vAdmin/middleware" 7 | jwt "vAdmin/pkg/jwtauth" 8 | ) 9 | 10 | //业务 需要认证的路由 11 | 12 | func registerDeployAppRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 13 | 14 | r := v1.Group("/deploy").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 15 | { 16 | r.GET("/:id", deploy.GetDeployApp) 17 | r.POST("", deploy.InsertDeployApp) 18 | r.PUT("", deploy.UpdateDeployApp) 19 | } 20 | 21 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 22 | { 23 | l.GET("/applyList", deploy.GetDeployAppList) 24 | l.GET("/rollList", deploy.GetRollbackAppList) 25 | l.GET("/deploystatus", deploy.DeployStatus) 26 | l.GET("/deploystart", deploy.DeployStart) 27 | //l.GET("/killtask", deploy.KillTask) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /tools/app/model.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | type Response struct { 4 | // 代码 5 | Code int `json:"code" example:"200"` 6 | // 数据集 7 | Data interface{} `json:"data"` 8 | // 消息 9 | Msg string `json:"msg"` 10 | } 11 | 12 | type Page struct { 13 | List interface{} `json:"list"` 14 | Count int `json:"count"` 15 | PageIndex int `json:"pageIndex"` 16 | PageSize int `json:"pageSize"` 17 | } 18 | 19 | type PageResponse struct { 20 | // 代码 21 | Code int `json:"code" example:"200"` 22 | // 数据集 23 | Data Page `json:"data"` 24 | // 消息 25 | Msg string `json:"msg"` 26 | } 27 | 28 | 29 | func (res *Response) ReturnOK() *Response { 30 | res.Code = 200 31 | return res 32 | } 33 | 34 | func (res *Response) ReturnError(code int) *Response { 35 | res.Code = code 36 | return res 37 | } 38 | 39 | 40 | func (res *PageResponse) ReturnOK() *PageResponse { 41 | res.Code = 200 42 | return res 43 | } 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /middleware/permission.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | mycasbin "vAdmin/pkg/casbin" 6 | "vAdmin/pkg/jwtauth" 7 | _ "vAdmin/pkg/jwtauth" 8 | "vAdmin/tools" 9 | "log" 10 | "net/http" 11 | ) 12 | 13 | //权限检查中间件 14 | func AuthCheckRole() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | data, _ := c.Get("JWT_PAYLOAD") 17 | v := data.(jwtauth.MapClaims) 18 | e, err := mycasbin.Casbin() 19 | tools.HasError(err, "", 500) 20 | //检查权限 21 | res, err := e.Enforce(v["rolekey"], c.Request.URL.Path, c.Request.Method) 22 | log.Println("----------------", v["rolekey"], c.Request.URL.Path, c.Request.Method) 23 | 24 | tools.HasError(err, "", 500) 25 | 26 | if res { 27 | c.Next() 28 | } else { 29 | c.JSON(http.StatusOK, gin.H{ 30 | "code": 403, 31 | "msg": "对不起,您没有该接口访问权限,请联系管理员", 32 | }) 33 | c.Abort() 34 | return 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /apis/system/index.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | const INDEX = ` 8 | 9 | 10 | 11 | 12 | vAdmin欢迎您 13 | 20 | 21 | 29 | 30 | 31 | 32 | 33 | 34 | ` 35 | 36 | 37 | func HelloWorld(c *gin.Context){ 38 | c.Header("Content-Type", "text/html; charset=utf-8") 39 | c.String(200, INDEX) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/ldap/connection.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "crypto/tls" 5 | "errors" 6 | "vAdmin/pkg/logger" 7 | "fmt" 8 | "time" 9 | 10 | "github.com/spf13/viper" 11 | 12 | "github.com/go-ldap/ldap/v3" 13 | ) 14 | 15 | /* 16 | @Author : Rongxin Linghu 17 | */ 18 | 19 | var conn *ldap.Conn 20 | 21 | // ldap连接 22 | func ldapConnection() (err error) { 23 | var ldapConn = fmt.Sprintf("%v:%v", viper.GetString("settings.ldap.host"), viper.GetString("settings.ldap.port")) 24 | 25 | if viper.GetBool("settings.ldap.tls") { 26 | tlsConf := &tls.Config{ 27 | InsecureSkipVerify: true, 28 | } 29 | conn, err = ldap.DialTLS("tcp", ldapConn, tlsConf) 30 | } else { 31 | conn, err = ldap.Dial("tcp", ldapConn) 32 | } 33 | if err != nil { 34 | err = errors.New(fmt.Sprintf("无法连接到ldap服务器,%v", err)) 35 | logger.Error(err) 36 | return 37 | } 38 | 39 | //设置超时时间 40 | conn.SetTimeout(5 * time.Second) 41 | 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /models/process/history.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "vAdmin/models/base" 5 | ) 6 | 7 | /* 8 | @Author : Rongxin Linghu 9 | */ 10 | 11 | // 任务 12 | type History struct { 13 | base.Model 14 | Task int `gorm:"column:task; type: int(11)" json:"task" form:"task"` // 任务ID 15 | Name string `gorm:"column:name; type: varchar(256)" json:"name" form:"name"` // 任务名称 16 | TaskType int `gorm:"column:task_type; type: int(11)" json:"task_type" form:"task_type"` // 任务类型, python, shell 17 | ExecutionTime string `gorm:"column:execution_time; type: varchar(128)" json:"execution_time" form:"execution_time"` // 执行时间 18 | Result string `gorm:"column:result; type: longtext" json:"result" form:"result"` // 任务返回 19 | } 20 | 21 | func (History) TableName() string { 22 | return "p_task_history" 23 | } 24 | -------------------------------------------------------------------------------- /pkg/casbin/mycasbin.go: -------------------------------------------------------------------------------- 1 | package mycasbin 2 | 3 | import ( 4 | "fmt" 5 | "github.com/casbin/casbin/v2" 6 | gormadapter "github.com/casbin/gorm-adapter/v2" 7 | "github.com/go-kit/kit/endpoint" 8 | _ "github.com/go-sql-driver/mysql" 9 | "vAdmin/database" 10 | "vAdmin/tools/config" 11 | ) 12 | 13 | var Em endpoint.Middleware 14 | 15 | func Casbin() (*casbin.Enforcer, error) { 16 | conn := database.GetMysqlConnect() 17 | if config.MysqlConfig.Dbtype == "sqlite3" { 18 | conn = config.MysqlConfig.Host 19 | } 20 | Apter, err := gormadapter.NewAdapter(config.MysqlConfig.Dbtype, conn, true) 21 | if err != nil { 22 | return nil, err 23 | } 24 | e, err := casbin.NewEnforcer("config/rbac_model.conf", Apter) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if err := e.LoadPolicy(); err == nil { 29 | return e, err 30 | } else { 31 | fmt.Print("casbin rbac_model or policy init error, message: %v", err) 32 | return nil, err 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /models/gorm/gorm.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "vAdmin/models" 6 | "vAdmin/models/process" 7 | "vAdmin/models/tools" 8 | ) 9 | 10 | func AutoMigrate(db *gorm.DB) error { 11 | db.SingularTable(true) 12 | return db.AutoMigrate( 13 | 14 | // 系统管理 15 | new(models.CasbinRule), 16 | new(tools.SysTables), 17 | new(tools.SysColumns), 18 | new(models.Dept), 19 | new(models.Menu), 20 | new(models.LoginLog), 21 | new(models.SysOperLog), 22 | new(models.RoleMenu), 23 | new(models.SysRoleDept), 24 | new(models.SysUser), 25 | new(models.SysRole), 26 | new(models.Post), 27 | new(models.DictData), 28 | new(models.SysConfig), 29 | new(models.DictType), 30 | 31 | // 流程中心 32 | new(process.Classify), 33 | new(process.TplInfo), 34 | new(process.TplData), 35 | new(process.WorkOrderInfo), 36 | new(process.TaskInfo), 37 | new(process.Info), 38 | new(process.History), 39 | new(process.CirculationHistory), 40 | ).Error 41 | } 42 | -------------------------------------------------------------------------------- /pkg/hash/string_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // 测试字符串hash 8 | 9 | func Test_Md5String(t *testing.T) { 10 | val := Md5String("111111") 11 | if val != "96e79218965eb72c92a549dd5a330112" { 12 | t.Errorf("string md5值计算错误:%s", val) 13 | } 14 | } 15 | 16 | func Test_Sha1String(t *testing.T) { 17 | val := Sha1String("111111") 18 | if val != "3d4f2bf07dc1be38b20cd6e46949a1071f9d0e3d" { 19 | t.Errorf("string sha1值计算错误:%s", val) 20 | } 21 | } 22 | 23 | func Test_Sha256String(t *testing.T) { 24 | val := Sha256String("111111") 25 | if val != "bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a" { 26 | t.Errorf("string sha256值计算错误:%s", val) 27 | } 28 | } 29 | 30 | func Test_Sha512String(t *testing.T) { 31 | val := Sha512String("111111") 32 | if val != "b0412597dcea813655574dc54a5b74967cf85317f0332a2591be7953a016f8de56200eb37d5ba593b1e4aa27cea5ca27100f94dccd5b04bae5cadd4454dba67d" { 33 | t.Errorf("string sha512值计算错误:%s", val) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/hash/byte_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // 测试字节数组hash 8 | 9 | func Test_Md5Byte(t *testing.T) { 10 | val := Md5Byte([]byte("111111")) 11 | if val != "96e79218965eb72c92a549dd5a330112" { 12 | t.Errorf("byte md5值计算错误:%s", val) 13 | } 14 | } 15 | 16 | func Test_Sha1Byte(t *testing.T) { 17 | val := Sha1Byte([]byte("111111")) 18 | if val != "3d4f2bf07dc1be38b20cd6e46949a1071f9d0e3d" { 19 | t.Errorf("byte sha1值计算错误:%s", val) 20 | } 21 | } 22 | 23 | func Test_Sha256Byte(t *testing.T) { 24 | val := Sha256Byte([]byte("111111")) 25 | if val != "bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a" { 26 | t.Errorf("byte sha256值计算错误:%s", val) 27 | } 28 | } 29 | 30 | func Test_Sha512Byte(t *testing.T) { 31 | val := Sha512Byte([]byte("111111")) 32 | if val != "b0412597dcea813655574dc54a5b74967cf85317f0332a2591be7953a016f8de56200eb37d5ba593b1e4aa27cea5ca27100f94dccd5b04bae5cadd4454dba67d" { 33 | t.Errorf("byte sha512值计算错误:%s", val) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tools/config/database.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/spf13/viper" 4 | 5 | type Database struct { 6 | Dbtype string 7 | Host string 8 | Port int 9 | Name string 10 | Username string 11 | Password string 12 | MaxIdleConns int 13 | MaxOpenConns int 14 | } 15 | 16 | func InitMysql(cfg *viper.Viper) *Database { 17 | return &Database{ 18 | Port: cfg.GetInt("port"), 19 | Dbtype: cfg.GetString("dbType"), 20 | Host: cfg.GetString("host"), 21 | Name: cfg.GetString("name"), 22 | Username: cfg.GetString("username"), 23 | Password: cfg.GetString("password"), 24 | MaxIdleConns: cfg.GetInt("MaxIdleConns"), 25 | MaxOpenConns: cfg.GetInt("MaxOpenConns"), 26 | } 27 | } 28 | 29 | func InitDrill(cfg *viper.Viper) *Database { 30 | return &Database{ 31 | Port: cfg.GetInt("port"), 32 | Dbtype: cfg.GetString("dbType"), 33 | Host: cfg.GetString("host"), 34 | } 35 | } 36 | 37 | var MysqlConfig = new(Database) 38 | 39 | var DrillConfig = new(Database) 40 | 41 | -------------------------------------------------------------------------------- /cmd/cobra.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "errors" 5 | "github.com/spf13/cobra" 6 | "vAdmin/cmd/api" 7 | "vAdmin/cmd/migrate" 8 | "log" 9 | "os" 10 | ) 11 | 12 | var rootCmd = &cobra.Command{ 13 | Use: "main", 14 | Short: "-v", 15 | SilenceUsage: true, 16 | DisableAutoGenTag: true, 17 | Long: `main`, 18 | Args: func(cmd *cobra.Command, args []string) error { 19 | if len(args) < 1 { 20 | return errors.New("requires at least one arg") 21 | } 22 | return nil 23 | }, 24 | PersistentPreRunE: func(*cobra.Command, []string) error { return nil }, 25 | Run: func(cmd *cobra.Command, args []string) { 26 | usageStr := `rnotes web 1.0.0 欢迎使用,可以是用 -h 查看命令` 27 | log.Printf("%s\n", usageStr) 28 | }, 29 | } 30 | 31 | func init() { 32 | rootCmd.AddCommand(api.StartCmd) 33 | rootCmd.AddCommand(migrate.StartCmd) 34 | } 35 | 36 | //Execute : apply commands 37 | func Execute() { 38 | if err := rootCmd.Execute(); err != nil { 39 | os.Exit(-1) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /template/js.go.template: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 查询{{.ClassName}}列表 4 | export function list{{.ClassName}}(query) { 5 | return request({ 6 | url: '/api/v1/{{.ModuleName}}List', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | // 查询{{.ClassName}}详细 13 | export function get{{.ClassName}} ({{.PkJsonField}}) { 14 | return request({ 15 | url: '/api/v1/{{.ModuleName}}/' + {{.PkJsonField}}, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | 21 | // 新增{{.ClassName}} 22 | export function add{{.ClassName}}(data) { 23 | return request({ 24 | url: '/api/v1/{{.ModuleName}}', 25 | method: 'post', 26 | data: data 27 | }) 28 | } 29 | 30 | // 修改{{.ClassName}} 31 | export function update{{.ClassName}}(data) { 32 | return request({ 33 | url: '/api/v1/{{.ModuleName}}', 34 | method: 'put', 35 | data: data 36 | }) 37 | } 38 | 39 | // 删除{{.ClassName}} 40 | export function del{{.ClassName}}({{.PkJsonField}}) { 41 | return request({ 42 | url: '/api/v1/{{.ModuleName}}/' + {{.PkJsonField}}, 43 | method: 'delete' 44 | }) 45 | } 46 | 47 | -------------------------------------------------------------------------------- /tools/app/return.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | ) 7 | 8 | // 失败数据处理 9 | func Error(c *gin.Context, code int, err error, msg string) { 10 | var res Response 11 | res.Msg = err.Error() 12 | if msg != "" { 13 | res.Msg = msg 14 | } 15 | c.JSON(http.StatusOK, res.ReturnError(code)) 16 | } 17 | 18 | // 通常成功数据处理 19 | func OK(c *gin.Context, data interface{}, msg string) { 20 | var res Response 21 | res.Data = data 22 | if msg != "" { 23 | res.Msg = msg 24 | } 25 | c.JSON(http.StatusOK, res.ReturnOK()) 26 | } 27 | 28 | // 分页数据处理 29 | func PageOK(c *gin.Context, result interface{},count int,pageIndex int,pageSize int, msg string) { 30 | var res PageResponse 31 | res.Data.List = result 32 | res.Data.Count = count 33 | res.Data.PageIndex = pageIndex 34 | res.Data.PageSize = pageSize 35 | if msg != "" { 36 | res.Msg = msg 37 | } 38 | c.JSON(http.StatusOK, res.ReturnOK()) 39 | } 40 | 41 | // 兼容函数 42 | func Custum(c *gin.Context, data gin.H) { 43 | c.JSON(http.StatusOK,data) 44 | } 45 | -------------------------------------------------------------------------------- /router/process/workOrder.go: -------------------------------------------------------------------------------- 1 | /* 2 | @Author : Rongxin Linghu 3 | */ 4 | 5 | package process 6 | 7 | import ( 8 | "vAdmin/apis/process" 9 | "vAdmin/middleware" 10 | jwt "vAdmin/pkg/jwtauth" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func RegisterWorkOrderRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 16 | workOrderRouter := v1.Group("/work-order").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 17 | { 18 | workOrderRouter.GET("/process-structure", process.ProcessStructure) 19 | workOrderRouter.POST("/create", process.CreateWorkOrder) 20 | workOrderRouter.GET("/list", process.WorkOrderList) 21 | workOrderRouter.POST("/handle", process.ProcessWorkOrder) 22 | workOrderRouter.GET("/unity", process.UnityWorkOrder) 23 | workOrderRouter.POST("/inversion", process.InversionWorkOrder) 24 | workOrderRouter.GET("/urge", process.UrgeWorkOrder) 25 | workOrderRouter.PUT("/active-order/:id", process.ActiveOrder) 26 | workOrderRouter.DELETE("/delete/:id", process.DeleteWorkOrder) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/gen_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "vAdmin/models/tools" 5 | "os" 6 | "testing" 7 | "text/template" 8 | ) 9 | 10 | func TestGoModelTemplate(t *testing.T) { 11 | t1, err := template.ParseFiles("model.go.template") 12 | if err != nil { 13 | t.Error(err) 14 | } 15 | table := tools.SysTables{} 16 | table.TableName = "sys_tables" 17 | tab, err := table.Get() 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | file, err := os.Create("models/"+table.PackageName+".go") 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | defer file.Close() 26 | 27 | _ = t1.Execute(file, tab) 28 | t.Log("") 29 | } 30 | 31 | func TestGoApiTemplate(t *testing.T) { 32 | t1, err := template.ParseFiles("api.go.template") 33 | if err != nil { 34 | t.Error(err) 35 | } 36 | table := tools.SysTables{} 37 | table.TableName = "sys_tables" 38 | tab, _ := table.Get() 39 | file, err := os.Create("apis/"+table.PackageName+".go") 40 | if err != nil { 41 | t.Error(err) 42 | } 43 | defer file.Close() 44 | 45 | _ = t1.Execute(file, tab) 46 | t.Log("") 47 | } 48 | -------------------------------------------------------------------------------- /apis/tools/dbcolumns.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/models/tools" 6 | tools2 "vAdmin/tools" 7 | "vAdmin/tools/app" 8 | "net/http" 9 | ) 10 | 11 | func GetDBColumnList(c *gin.Context) { 12 | var data tools.DBColumns 13 | var err error 14 | var pageSize = 10 15 | var pageIndex = 1 16 | 17 | if size := c.Request.FormValue("pageSize"); size != "" { 18 | pageSize = tools2.StrToInt(err, size) 19 | } 20 | 21 | if index := c.Request.FormValue("pageIndex"); index != "" { 22 | pageIndex = tools2.StrToInt(err, index) 23 | } 24 | 25 | data.TableName = c.Request.FormValue("tableName") 26 | tools2.Assert(data.TableName=="","table name cannot be empty!",500) 27 | result, count, err := data.GetPage(pageSize, pageIndex) 28 | tools2.HasError(err, "", -1) 29 | 30 | var mp = make(map[string]interface{}, 3) 31 | mp["list"] = result 32 | mp["count"] = count 33 | mp["pageIndex"] = pageIndex 34 | mp["pageSize"] = pageSize 35 | 36 | var res app.Response 37 | res.Data = mp 38 | 39 | c.JSON(http.StatusOK, res.ReturnOK()) 40 | } 41 | -------------------------------------------------------------------------------- /middleware/customerror.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "net/http" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func CustomError(c *gin.Context) { 13 | defer func() { 14 | if err := recover(); err != nil { 15 | 16 | if c.IsAborted() { 17 | c.Status(200) 18 | } 19 | switch errStr := err.(type) { 20 | case string: 21 | p := strings.Split(errStr, "#") 22 | if len(p) == 3 && p[0] == "CustomError" { 23 | statusCode, e := strconv.Atoi(p[1]) 24 | if e != nil { 25 | break 26 | } 27 | c.Status(statusCode) 28 | fmt.Println( 29 | time.Now().Format("\n 2006-01-02 15:04:05.9999"), 30 | "[ERROR]", 31 | c.Request.Method, 32 | c.Request.URL, 33 | statusCode, 34 | c.Request.RequestURI, 35 | c.ClientIP(), 36 | p[2], 37 | ) 38 | c.JSON(http.StatusOK, gin.H{ 39 | "code": statusCode, 40 | "msg": p[2], 41 | }) 42 | } 43 | default: 44 | panic(err) 45 | } 46 | } 47 | }() 48 | c.Next() 49 | } 50 | -------------------------------------------------------------------------------- /models/server_group.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type ServerGroup struct { 4 | ID int `gorm:"primary_key"` 5 | Name string `gorm:"type:varchar(100);not null;default:''"` 6 | CreateBy string `json:"create_by" gorm:"type:varchar(128);"` // 创建人 7 | UpdateBy string `json:"update_by" gorm:"type:varchar(128);"` // 更新者 8 | } 9 | 10 | func (m *ServerGroup) TableName() string { 11 | return "dep_server_group" 12 | } 13 | 14 | func (m *ServerGroup) Create() bool { 15 | //m.Ctime = int(time.Now().Unix()) 16 | return Create(m) 17 | } 18 | 19 | func (m *ServerGroup) Update() bool { 20 | return UpdateByPk(m) 21 | } 22 | 23 | func (m *ServerGroup) List(query QueryParam) ([]ServerGroup, bool) { 24 | var data []ServerGroup 25 | ok := GetMulti(&data, query) 26 | return data, ok 27 | } 28 | 29 | func (m *ServerGroup) Count(query QueryParam) (int, bool) { 30 | var count int 31 | ok := Count(m, &count, query) 32 | return count, ok 33 | } 34 | 35 | func (m *ServerGroup) Delete() bool { 36 | return DeleteByPk(m) 37 | } 38 | 39 | func (m *ServerGroup) Get(id int) bool { 40 | return GetByPk(m, id) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/ldap/ldapFieldsMap.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "vAdmin/models/system" 5 | 6 | "github.com/go-ldap/ldap/v3" 7 | ) 8 | 9 | /* 10 | @Author : Rongxin Linghu 11 | */ 12 | 13 | func LdapFieldsMap(ldapUserInfo *ldap.Entry) (userInfo system.SysUser, err error) { 14 | var ( 15 | ldapFields []map[string]string 16 | ) 17 | 18 | ldapFields, err = getLdapFields() 19 | if err != nil { 20 | return 21 | } 22 | 23 | for _, v := range ldapFields { 24 | switch v["local_field_name"] { 25 | case "nick_name": 26 | userInfo.NickName = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 27 | case "phone": 28 | userInfo.Phone = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 29 | case "avatar": 30 | userInfo.Avatar = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 31 | case "sex": 32 | userInfo.Sex = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 33 | case "email": 34 | userInfo.Email = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 35 | case "remark": 36 | userInfo.Remark = ldapUserInfo.GetAttributeValue(v["ldap_field_name"]) 37 | } 38 | } 39 | 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /pkg/ldap/updatePwd.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "vAdmin/pkg/logger" 5 | "fmt" 6 | 7 | "github.com/go-ldap/ldap/v3" 8 | "golang.org/x/text/encoding/unicode" 9 | 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | /* 14 | @Author : Rongxin Linghu 15 | */ 16 | 17 | func LdapUpdatePwd(username string, oldPassword string, newPassword string) (err error) { 18 | err = ldapConnection() 19 | if err != nil { 20 | return 21 | } 22 | defer conn.Close() 23 | 24 | var userDn = fmt.Sprintf("cn=%v,%v", username, viper.GetString("settings.ldap.baseDn")) 25 | 26 | err = conn.Bind(userDn, oldPassword) 27 | if err != nil { 28 | logger.Error("用户或密码错误。", err) 29 | return 30 | } 31 | 32 | sql2 := ldap.NewModifyRequest(userDn, nil) 33 | 34 | utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 35 | pwdEncoded, _ := utf16.NewEncoder().String(newPassword) 36 | 37 | sql2.Replace("unicodePwd", []string{pwdEncoded}) 38 | sql2.Replace("userAccountControl", []string{"512"}) 39 | 40 | if err = conn.Modify(sql2); err != nil { 41 | logger.Error("密码修改失败,%v", err.Error()) 42 | return 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /template/router.go.template: -------------------------------------------------------------------------------- 1 | 2 | // 需认证的路由代码 3 | func register{{.ClassName}}Router(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 4 | 5 | r := v1.Group("/{{.ModuleName}}").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 6 | { 7 | r.GET("/:{{.PkJsonField}}", {{.ModuleName}}.Get{{.ClassName}}) 8 | r.POST("", {{.ModuleName}}.Insert{{.ClassName}}) 9 | r.PUT("", {{.ModuleName}}.Update{{.ClassName}}) 10 | r.DELETE("/:{{.PkJsonField}}", {{.ModuleName}}.Delete{{.ClassName}}) 11 | } 12 | 13 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 14 | { 15 | l.GET("/{{.ModuleName}}List",{{.ModuleName}}.Get{{.ClassName}}List) 16 | } 17 | 18 | } 19 | 20 | // 无需认证的路由代码 21 | func register{{.ClassName}}Router(v1 *gin.RouterGroup) { 22 | 23 | v1.GET("/{{.ModuleName}}List",{{.ModuleName}}.Get{{.ClassName}}List) 24 | 25 | r := v1.Group("/{{.ModuleName}}") 26 | { 27 | r.GET("/:{{.PkJsonField}}", {{.ModuleName}}.Get{{.ClassName}}) 28 | r.POST("", {{.ModuleName}}.Insert{{.ClassName}}) 29 | r.PUT("", {{.ModuleName}}.Update{{.ClassName}}) 30 | r.DELETE("/:{{.PkJsonField}}", {{.ModuleName}}.Delete{{.ClassName}}) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /apis/tools/dbtables.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/models/tools" 6 | tools2 "vAdmin/tools" 7 | "vAdmin/tools/app" 8 | config2 "vAdmin/tools/config" 9 | "net/http" 10 | ) 11 | 12 | func GetDBTableList(c *gin.Context) { 13 | var res app.Response 14 | var data tools.DBTables 15 | var err error 16 | var pageSize = 10 17 | var pageIndex = 1 18 | if config2.MysqlConfig.Dbtype=="sqlite3"{ 19 | res.Msg="对不起,sqlite3 暂不支持代码生成!" 20 | c.JSON(http.StatusOK, res.ReturnError(500)) 21 | return 22 | } 23 | 24 | if size := c.Request.FormValue("pageSize"); size != "" { 25 | pageSize = tools2.StrToInt(err, size) 26 | } 27 | 28 | if index := c.Request.FormValue("pageIndex"); index != "" { 29 | pageIndex = tools2.StrToInt(err, index) 30 | } 31 | 32 | data.TableName = c.Request.FormValue("tableName") 33 | result, count, err := data.GetPage(pageSize, pageIndex) 34 | tools2.HasError(err, "", -1) 35 | 36 | var mp = make(map[string]interface{}, 3) 37 | mp["list"] = result 38 | mp["count"] = count 39 | mp["pageIndex"] = pageIndex 40 | mp["pageSize"] = pageSize 41 | 42 | 43 | res.Data = mp 44 | 45 | c.JSON(http.StatusOK, res.ReturnOK()) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/hash/file_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // 测试文件hash 8 | 9 | func Test_Md5File(t *testing.T) { 10 | val, err := Md5File("./test.txt") 11 | if err != nil { 12 | t.Error(err) 13 | } 14 | if val != "098f6bcd4621d373cade4e832627b4f6" { 15 | t.Errorf("file md5值计算错误:%s", val) 16 | } 17 | } 18 | 19 | func Test_Sha1File(t *testing.T) { 20 | val, err := Sha1File("./test.txt") 21 | if err != nil { 22 | t.Error(err) 23 | } 24 | if val != "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3" { 25 | t.Errorf("file sha1值计算错误:%s", val) 26 | } 27 | } 28 | 29 | func Test_Sha256File(t *testing.T) { 30 | val, err := Sha256File("./test.txt") 31 | if err != nil { 32 | t.Error(err) 33 | } 34 | if val != "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08" { 35 | t.Errorf("file sha256值计算错误:%s", val) 36 | } 37 | } 38 | 39 | func Test_Sha512File(t *testing.T) { 40 | val, err := Sha512File("./test.txt") 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | if val != "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff" { 45 | t.Errorf("file sha512值计算错误:%s", val) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /models/roledept.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | orm "vAdmin/database" 6 | ) 7 | 8 | //sys_role_dept 9 | type SysRoleDept struct { 10 | RoleId int `gorm:"type:int(11)"` 11 | DeptId int `gorm:"type:int(11)"` 12 | } 13 | 14 | func (SysRoleDept) TableName() string { 15 | return "sys_role_dept" 16 | } 17 | 18 | func (rm *SysRoleDept) Insert(roleId int, deptIds []int) (bool, error) { 19 | //ORM不支持批量插入所以需要拼接 sql 串 20 | sql := "INSERT INTO `sys_role_dept` (`role_id`,`dept_id`) VALUES " 21 | 22 | for i := 0; i < len(deptIds); i++ { 23 | if len(deptIds)-1 == i { 24 | //最后一条数据 以分号结尾 25 | sql += fmt.Sprintf("(%d,%d);", roleId, deptIds[i]) 26 | } else { 27 | sql += fmt.Sprintf("(%d,%d),", roleId, deptIds[i]) 28 | } 29 | } 30 | orm.Eloquent.Exec(sql) 31 | 32 | return true, nil 33 | } 34 | 35 | func (rm *SysRoleDept) DeleteRoleDept(roleId int) (bool, error) { 36 | if err := orm.Eloquent.Table("sys_role_dept").Where("role_id = ?", roleId).Delete(&rm).Error; err != nil { 37 | return false, err 38 | } 39 | var role SysRole 40 | if err := orm.Eloquent.Table("sys_role").Where("role_id = ?", roleId).First(&role).Error; err != nil { 41 | return false, err 42 | } 43 | 44 | return true, nil 45 | 46 | } 47 | -------------------------------------------------------------------------------- /apis/system/rolemenu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "vAdmin/models" 7 | "vAdmin/tools/app" 8 | "net/http" 9 | ) 10 | 11 | func GetRoleMenu(c *gin.Context) { 12 | var Rm models.RoleMenu 13 | err := c.ShouldBind(&Rm) 14 | result, err := Rm.Get() 15 | var res app.Response 16 | if err != nil { 17 | res.Msg = "抱歉未找到相关信息" 18 | c.JSON(http.StatusOK, res.ReturnError(200)) 19 | return 20 | } 21 | res.Data = result 22 | c.JSON(http.StatusOK, res.ReturnOK()) 23 | } 24 | 25 | type RoleMenuPost struct { 26 | RoleId string 27 | RoleMenu []models.RoleMenu 28 | } 29 | 30 | func InsertRoleMenu(c *gin.Context) { 31 | 32 | var res app.Response 33 | res.Msg = "添加成功" 34 | c.JSON(http.StatusOK, res.ReturnOK()) 35 | return 36 | 37 | } 38 | 39 | func DeleteRoleMenu(c *gin.Context) { 40 | var t models.RoleMenu 41 | id := c.Param("id") 42 | menuId := c.Request.FormValue("menu_id") 43 | fmt.Println(menuId) 44 | _, err := t.Delete(id, menuId) 45 | if err != nil { 46 | var res app.Response 47 | res.Msg = "删除失败" 48 | c.JSON(http.StatusOK, res.ReturnError(200)) 49 | return 50 | } 51 | var res app.Response 52 | res.Msg = "删除成功" 53 | c.JSON(http.StatusOK, res.ReturnOK()) 54 | return 55 | } 56 | -------------------------------------------------------------------------------- /config/settings.yml: -------------------------------------------------------------------------------- 1 | script: 2 | path: ./static/scripts 3 | settings: 4 | application: 5 | host: 0.0.0.0 6 | mode: dev 7 | name: vAdmin 8 | port: "8000" 9 | readtimeout: 1 10 | writertimeout: 2 11 | domain: 12 | gethost: 1 13 | url: localhost:9527 14 | drill: 15 | dbtype: drill 16 | host: 192.168.0.18 17 | port: 30700 18 | email: 19 | alias: vAdmin 20 | host: smtp.163.com 21 | pass: xxx 22 | port: 465 23 | user: pygo_k8s@163.com 24 | gorm: 25 | logmode: 0 26 | maxidleconn: 0 27 | maxopenconn: 20000 28 | jwt: 29 | secret: vAdminx 30 | timeout: 7200 31 | log: 32 | compress: 1 33 | consolestdout: 1 34 | dir: temp/logs 35 | filestdout: 0 36 | level: debug 37 | localtime: 1 38 | maxage: 30 39 | maxbackups: 300 40 | maxsize: 10240 41 | path: ./logs/vAdmin.log 42 | mysql: 43 | dbtype: mysql 44 | host: xx.xx.xx.xx 45 | maxidleconns: 0 46 | maxopenconns: 100 47 | name: cicdadmin 48 | password: ********8 49 | port: 3306 50 | username: cicd_user 51 | public: 52 | islocation: 0 53 | redis: 54 | url: redis://xx.xx.xx.xx:6379 55 | ssl: 56 | key: keystring 57 | pem: temp/pem.pem 58 | -------------------------------------------------------------------------------- /models/initdb.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | orm "vAdmin/database" 6 | config2 "vAdmin/tools/config" 7 | "io/ioutil" 8 | "strings" 9 | ) 10 | 11 | func InitDb() error { 12 | filePath := "config/db.sql" 13 | if config2.MysqlConfig.Dbtype == "sqlite3" { 14 | fmt.Println("sqlite3数据库无需初始化!") 15 | return nil 16 | } 17 | sql, err := Ioutil(filePath) 18 | if err != nil { 19 | fmt.Println("数据库基础数据初始化脚本读取失败!原因:", err.Error()) 20 | return err 21 | } 22 | sqlList := strings.Split(sql, ";") 23 | for i := 0; i < len(sqlList) - 1; i++ { 24 | if strings.Contains(sqlList[i], "--") { 25 | fmt.Println(sqlList[i]) 26 | continue 27 | } 28 | sql := strings.Replace(sqlList[i]+";", "\n", "", 0) 29 | if err = orm.Eloquent.Exec(sql).Error; err != nil { 30 | if !strings.Contains(err.Error(), "Query was empty") { 31 | return err 32 | } 33 | } 34 | } 35 | return nil 36 | } 37 | 38 | func Ioutil(name string) (string, error) { 39 | if contents, err := ioutil.ReadFile(name); err == nil { 40 | //因为contents是[]byte类型,直接转换成string类型后会多一行空格,需要使用strings.Replace替换换行符 41 | result := strings.Replace(string(contents), "\n", "", 1) 42 | fmt.Println("Use ioutil.ReadFile to read a file:", result) 43 | return result, nil 44 | } else { 45 | return "", err 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tools/captcha/captcha.go: -------------------------------------------------------------------------------- 1 | package captcha 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | "github.com/mojocn/base64Captcha" 6 | "image/color" 7 | ) 8 | 9 | var store = base64Captcha.DefaultMemStore 10 | 11 | //configJsonBody json request body. 12 | type configJsonBody struct { 13 | Id string 14 | CaptchaType string 15 | VerifyValue string 16 | DriverAudio *base64Captcha.DriverAudio 17 | DriverString *base64Captcha.DriverString 18 | DriverChinese *base64Captcha.DriverChinese 19 | DriverMath *base64Captcha.DriverMath 20 | DriverDigit *base64Captcha.DriverDigit 21 | } 22 | 23 | 24 | func DriverStringFunc() (id, b64s string, err error) { 25 | e :=configJsonBody{} 26 | e.Id = uuid.New().String() 27 | e.DriverString = base64Captcha.NewDriverString(46, 140, 2, 2, 4, "234567890abcdefghjkmnpqrstuvwxyz", &color.RGBA{240, 240, 246, 246}, []string{"wqy-microhei.ttc"}) 28 | driver := e.DriverString.ConvertFonts() 29 | cap := base64Captcha.NewCaptcha(driver, store) 30 | return cap.Generate() 31 | } 32 | 33 | 34 | func DriverDigitFunc() (id, b64s string, err error) { 35 | e := configJsonBody{} 36 | e.Id = uuid.New().String() 37 | e.DriverDigit = base64Captcha.DefaultDriverDigit 38 | driver := e.DriverDigit 39 | cap := base64Captcha.NewCaptcha(driver, store) 40 | return cap.Generate() 41 | } -------------------------------------------------------------------------------- /tools/gois/is.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gois 6 | 7 | import ( 8 | "strings" 9 | "net" 10 | "regexp" 11 | ) 12 | 13 | func IsInteger(val interface{}) bool { 14 | switch val.(type) { 15 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 16 | case string: 17 | str := val.(string) 18 | if str == "" { 19 | return false 20 | } 21 | str = strings.TrimSpace(str) 22 | if str[0] == '-' || str[0] == '+' { 23 | if len(str) == 1 { 24 | return false 25 | } 26 | str = str[1:] 27 | } 28 | for _, v := range str { 29 | if v < '0' || v > '9' { 30 | return false 31 | } 32 | } 33 | } 34 | return true 35 | } 36 | 37 | func IsIp(s string) bool { 38 | ip := net.ParseIP(s) 39 | if ip == nil { 40 | return false 41 | } 42 | return true 43 | } 44 | 45 | func IsEmail(s string) bool { 46 | pattern := `^[0-9A-Za-z][\.\-_0-9A-Za-z]*\@[0-9A-Za-z\-]+(\.[0-9A-Za-z]+)+$` 47 | reg := regexp.MustCompile(pattern) 48 | return reg.MatchString(s) 49 | } 50 | -------------------------------------------------------------------------------- /pkg/service/getState.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | /* 8 | @Author : Rongxin Linghu 9 | @Desc : 获取节点数据 10 | */ 11 | 12 | type ProcessState struct { 13 | Structure map[string][]map[string]interface{} 14 | } 15 | 16 | // 获取节点信息 17 | func (p *ProcessState) GetNode(stateId string) (nodeValue map[string]interface{}, err error) { 18 | for _, node := range p.Structure["nodes"] { 19 | if node["id"] == stateId { 20 | nodeValue = node 21 | } 22 | } 23 | return 24 | } 25 | 26 | // 获取流转信息 27 | func (p *ProcessState) GetEdge(stateId string, classify string) (edgeValue []map[string]interface{}, err error) { 28 | var ( 29 | leftSort int 30 | rightSort int 31 | ) 32 | 33 | for _, edge := range p.Structure["edges"] { 34 | if edge[classify] == stateId { 35 | edgeValue = append(edgeValue, edge) 36 | } 37 | } 38 | 39 | // 排序 40 | if len(edgeValue) > 1 { 41 | for i := 0; i < len(edgeValue)-1; i++ { 42 | for j := i + 1; j < len(edgeValue); j++ { 43 | if t, ok := edgeValue[i]["sort"]; ok { 44 | leftSort, _ = strconv.Atoi(t.(string)) 45 | } 46 | if t, ok := edgeValue[j]["sort"]; ok { 47 | rightSort, _ = strconv.Atoi(t.(string)) 48 | } 49 | if leftSort > rightSort { 50 | edgeValue[j], edgeValue[i] = edgeValue[i], edgeValue[j] 51 | } 52 | } 53 | } 54 | } 55 | 56 | return 57 | } 58 | -------------------------------------------------------------------------------- /apis/system/info.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/models" 6 | "vAdmin/tools" 7 | "vAdmin/tools/app" 8 | ) 9 | 10 | func GetInfo(c *gin.Context) { 11 | 12 | var roles = make([]string, 1) 13 | roles[0] = tools.GetRoleName(c) 14 | 15 | var permissions = make([]string, 1) 16 | permissions[0] = "*:*:*" 17 | 18 | var buttons = make([]string, 1) 19 | buttons[0] = "*:*:*" 20 | 21 | RoleMenu := models.RoleMenu{} 22 | RoleMenu.RoleId = tools.GetRoleId(c) 23 | 24 | var mp = make(map[string]interface{}) 25 | mp["roles"] = roles 26 | if tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员" { 27 | mp["permissions"] = permissions 28 | mp["buttons"] = buttons 29 | } else { 30 | list, _ := RoleMenu.GetPermis() 31 | mp["permissions"] = list 32 | mp["buttons"] = list 33 | } 34 | 35 | sysuser := models.SysUser{} 36 | sysuser.UserId = tools.GetUserId(c) 37 | user, err := sysuser.Get() 38 | tools.HasError(err, "", 500) 39 | 40 | mp["introduction"] = " am a super administrator" 41 | 42 | mp["avatar"] = "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif" 43 | if user.Avatar != "" { 44 | mp["avatar"] = user.Avatar 45 | } 46 | mp["userName"] = user.NickName 47 | mp["userId"] = user.UserId 48 | mp["deptId"] = user.DeptId 49 | mp["name"] = user.NickName 50 | 51 | app.OK(c, mp, "") 52 | } 53 | -------------------------------------------------------------------------------- /models/deploytask.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type DeployTask struct { 4 | ID int `gorm:"primary_key"` 5 | ApplyId int `gorm:"type:int(11);not null;default:0"` 6 | GroupId int `gorm:"type:int(11);not null;default:0"` 7 | Status int `json:"status" gorm:"type:varchar(50);"` 8 | Content string `gorm:"type:text;not null"` 9 | CreateBy string `json:"create_by" gorm:"type:varchar(128);"` // 创建人 10 | UpdateBy string `json:"update_by" gorm:"type:varchar(128);"` // 更新者 11 | } 12 | 13 | func (m *DeployTask) TableName() string { 14 | return "dep_task" 15 | } 16 | 17 | func (m *DeployTask) List(query QueryParam) ([]DeployTask, bool) { 18 | var data []DeployTask 19 | ok := GetMulti(&data, query) 20 | return data, ok 21 | } 22 | 23 | func (m *DeployTask) GetByApplyId(id int) bool { 24 | return GetOne(m, QueryParam{ 25 | Where: []WhereParam{ 26 | WhereParam{ 27 | Field: "apply_id", 28 | Prepare: id, 29 | }, 30 | }, 31 | }) 32 | } 33 | 34 | func (m *DeployTask) UpdateByFields(data map[string]interface{}, query QueryParam) bool { 35 | return Update(m, data, query) 36 | } 37 | 38 | func (m *DeployTask) Create() bool { 39 | //m.Ctime = int(time.Now().Unix()) 40 | return Create(m) 41 | } 42 | 43 | func (m *DeployTask) Delete(query QueryParam) bool { 44 | return Delete(m, query) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/service/getVariableValue.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | system "vAdmin/models" 6 | ) 7 | 8 | /* 9 | @Author : Rongxin Linghu 10 | */ 11 | 12 | func GetVariableValue(stateList []interface{}, creator int) (err error) { 13 | var ( 14 | userInfo system.SysUser 15 | deptInfo system.Dept 16 | ) 17 | 18 | // 变量转为实际的数据 19 | for _, stateItem := range stateList { 20 | if stateItem.(map[string]interface{})["process_method"] == "variable" { 21 | for processorIndex, processor := range stateItem.(map[string]interface{})["processor"].([]interface{}) { 22 | if int(processor.(float64)) == 1 { 23 | // 创建者 24 | stateItem.(map[string]interface{})["processor"].([]interface{})[processorIndex] = creator 25 | } else if int(processor.(float64)) == 2 { 26 | // 1. 查询用户信息 27 | err = orm.Eloquent.Model(&userInfo).Where("user_id = ?", creator).Find(&userInfo).Error 28 | if err != nil { 29 | return 30 | } 31 | // 2. 查询部门信息 32 | err = orm.Eloquent.Model(&deptInfo).Where("dept_id = ?", userInfo.DeptId).Find(&deptInfo).Error 33 | if err != nil { 34 | return 35 | } 36 | 37 | // 3. 替换处理人信息 38 | stateItem.(map[string]interface{})["processor"].([]interface{})[processorIndex] = deptInfo.Leader 39 | } 40 | } 41 | stateItem.(map[string]interface{})["process_method"] = "person" 42 | } 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /cmd/migrate/server.go: -------------------------------------------------------------------------------- 1 | package migrate 2 | 3 | import ( 4 | "fmt" 5 | "vAdmin/database" 6 | orm "vAdmin/database" 7 | "vAdmin/models" 8 | "vAdmin/models/gorm" 9 | "vAdmin/tools" 10 | config2 "vAdmin/tools/config" 11 | "log" 12 | 13 | "github.com/spf13/cobra" 14 | ) 15 | 16 | var ( 17 | config string 18 | mode string 19 | StartCmd = &cobra.Command{ 20 | Use: "init", 21 | Short: "initialize the database", 22 | Run: func(cmd *cobra.Command, args []string) { 23 | run() 24 | }, 25 | } 26 | ) 27 | 28 | func init() { 29 | StartCmd.PersistentFlags().StringVarP(&config, "config", "c", "config/settings.yml", "Start server with provided configuration file") 30 | StartCmd.PersistentFlags().StringVarP(&mode, "mode", "m", "dev", "server mode ; eg:dev,test,prod") 31 | } 32 | 33 | func run() { 34 | usage := `start init` 35 | fmt.Println(usage) 36 | //1. 读取配置 37 | config2.ConfigSetup(config) 38 | //2. 设置日志 39 | tools.InitLogger() 40 | //3. 初始化数据库链接 41 | database.SetupMysql() 42 | //4. 数据库迁移 43 | _ = migrateModel() 44 | log.Println("数据库结构初始化成功!") 45 | //5. 数据初始化完成 46 | if err := models.InitDb(); err != nil { 47 | log.Fatal("数据库基础数据初始化失败!") 48 | } 49 | 50 | usage = `数据库基础数据初始化成功` 51 | fmt.Println(usage) 52 | } 53 | 54 | func migrateModel() error { 55 | if config2.MysqlConfig.Dbtype == "mysql" { 56 | orm.Eloquent = orm.Eloquent.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4") 57 | } 58 | return gorm.AutoMigrate(orm.Eloquent) 59 | } 60 | -------------------------------------------------------------------------------- /config/settings-prod.yml: -------------------------------------------------------------------------------- 1 | script: 2 | path: ./static/scripts 3 | settings: 4 | application: 5 | host: 0.0.0.0 6 | mode: dev 7 | name: vAdmin 8 | port: "8000" 9 | readtimeout: 1 10 | writertimeout: 2 11 | domain: 12 | gethost: 1 13 | url: localhost:9527 14 | drill: 15 | dbtype: drill 16 | host: 192.168.0.18 17 | port: 30700 18 | email: 19 | alias: vAdmin 20 | host: smtp.163.com 21 | pass: your password 22 | port: 465 23 | user: fdevops@163.com 24 | gorm: 25 | logmode: 0 26 | maxidleconn: 0 27 | maxopenconn: 20000 28 | jwt: 29 | secret: vAdminx 30 | timeout: 7200 31 | ldap: 32 | anonymousquery: 0 33 | basedn: dc=vdevops,dc=com 34 | bindpwd: 123456 35 | binduserdn: cn=admin,dc=fdevops,dc=com 36 | host: localhost 37 | port: 389 38 | tls: 0 39 | userfield: uid 40 | log: 41 | compress: 1 42 | consolestdout: 1 43 | dir: temp/logs 44 | filestdout: 0 45 | level: debug 46 | localtime: 1 47 | maxage: 30 48 | maxbackups: 300 49 | maxsize: 10240 50 | path: ./logs/vAdmin.log 51 | mysql: 52 | dbtype: mysql 53 | host: xx.xx.xx.xx 54 | maxidleconns: 20 55 | maxopenconns: 100 56 | name: vadmin 57 | password: ******** 58 | port: 3306 59 | username: root 60 | public: 61 | islocation: 0 62 | redis: 63 | url: redis://vAdmin123456@xx.xx.xx.xx:6379 64 | ssl: 65 | key: keystring 66 | pem: temp/pem.pem 67 | -------------------------------------------------------------------------------- /models/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package models 6 | 7 | type Server struct { 8 | ID int `gorm:"primary_key"` 9 | GroupId int `gorm:"type:int(11);not null;default:0"` 10 | Name string `gorm:"type:varchar(100);not null;default:''"` 11 | Ip string `gorm:"type:varchar(100);not null;default:''"` 12 | SSHPort int `gorm:"type:int(11);not null;default:0"` 13 | CreateBy string `json:"create_by" gorm:"type:varchar(128);"` // 创建人 14 | UpdateBy string `json:"update_by" gorm:"type:varchar(128);"` // 更新者 15 | } 16 | 17 | func (m *Server) TableName() string { 18 | return "dep_server" 19 | } 20 | 21 | func (m *Server) Create() bool { 22 | //m.Ctime = int(time.Now().Unix()) 23 | return Create(m) 24 | } 25 | 26 | func (m *Server) Update() bool { 27 | return UpdateByPk(m) 28 | } 29 | 30 | func (m *Server) List(query QueryParam) ([]Server, bool) { 31 | var data []Server 32 | ok := GetMulti(&data, query) 33 | return data, ok 34 | } 35 | 36 | func (m *Server) Count(query QueryParam) (int, bool) { 37 | var count int 38 | ok := Count(m, &count, query) 39 | return count, ok 40 | } 41 | 42 | func (m *Server) Delete() bool { 43 | return DeleteByPk(m) 44 | } 45 | 46 | func (m *Server) Get(id int) bool { 47 | return GetByPk(m, id) 48 | } 49 | -------------------------------------------------------------------------------- /models/datascope.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | "github.com/jinzhu/gorm" 6 | "vAdmin/tools" 7 | ) 8 | 9 | type DataPermission struct { 10 | DataScope string 11 | UserId int 12 | DeptId int 13 | RoleId int 14 | } 15 | 16 | func (e *DataPermission) GetDataScope(tbname string, table *gorm.DB) (*gorm.DB, error) { 17 | SysUser := new(SysUser) 18 | SysRole := new(SysRole) 19 | SysUser.UserId = e.UserId 20 | user, err := SysUser.Get() 21 | if err != nil { 22 | return nil, errors.New("获取用户数据出错 msg:" + err.Error()) 23 | } 24 | SysRole.RoleId = user.RoleId 25 | role, err := SysRole.Get() 26 | if err != nil { 27 | return nil, errors.New("获取用户数据出错 msg:" + err.Error()) 28 | } 29 | if role.DataScope == "2" { 30 | table = table.Where(tbname+".create_by in (select sys_user.user_id from sys_role_dept left join sys_user on sys_user.dept_id=sys_role_dept.dept_id where sys_role_dept.role_id = ?)", user.RoleId) 31 | } 32 | if role.DataScope == "3" { 33 | table = table.Where(tbname+".create_by in (SELECT user_id from sys_user where dept_id = ? )", user.DeptId) 34 | } 35 | if role.DataScope == "4" { 36 | table = table.Where(tbname+".create_by in (SELECT user_id from sys_user where sys_user.dept_id in(select dept_id from sys_dept where dept_path like ? ))", "%"+tools.IntToString(user.DeptId)+"%") 37 | } 38 | if role.DataScope == "5" || role.DataScope == "" { 39 | table = table.Where(tbname+".create_by = ?", e.UserId) 40 | } 41 | 42 | return table, nil 43 | } 44 | -------------------------------------------------------------------------------- /pkg/task/worker/worker.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "vAdmin/pkg/logger" 5 | 6 | "github.com/spf13/viper" 7 | 8 | "github.com/RichardKnop/machinery/v1" 9 | taskConfig "github.com/RichardKnop/machinery/v1/config" 10 | "github.com/RichardKnop/machinery/v1/tasks" 11 | ) 12 | 13 | var AsyncTaskCenter *machinery.Server 14 | 15 | func StartServer() { 16 | tc, err := NewTaskCenter() 17 | if err != nil { 18 | panic(err) 19 | } 20 | AsyncTaskCenter = tc 21 | } 22 | 23 | func NewTaskCenter() (*machinery.Server, error) { 24 | cnf := &taskConfig.Config{ 25 | Broker: viper.GetString("settings.redis.url"), 26 | DefaultQueue: "ServerTasksQueue", 27 | ResultBackend: "eager", 28 | } 29 | server, err := machinery.NewServer(cnf) 30 | if err != nil { 31 | return nil, err 32 | } 33 | initAsyncTaskMap() 34 | return server, server.RegisterTasks(asyncTaskMap) 35 | } 36 | 37 | func NewAsyncTaskWorker(concurrency int) *machinery.Worker { 38 | consumerTag := "TaskWorker" 39 | worker := AsyncTaskCenter.NewWorker(consumerTag, concurrency) 40 | errorHandler := func(err error) { 41 | logger.Error("执行失败: ", err) 42 | } 43 | preTaskHandler := func(signature *tasks.Signature) { 44 | logger.Info("开始执行: ", signature.Name) 45 | } 46 | postTaskHandler := func(signature *tasks.Signature) { 47 | logger.Info("执行结束: ", signature.Name) 48 | } 49 | worker.SetPostTaskHandler(postTaskHandler) 50 | worker.SetErrorHandler(errorHandler) 51 | worker.SetPreTaskHandler(preTaskHandler) 52 | return worker 53 | } 54 | -------------------------------------------------------------------------------- /pkg/notify/email/email.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | @Desc : 发送邮件 6 | */ 7 | 8 | import ( 9 | "vAdmin/pkg/logger" 10 | "strconv" 11 | 12 | "github.com/spf13/viper" 13 | 14 | "gopkg.in/gomail.v2" 15 | ) 16 | 17 | func server(mailTo []string, subject, body string, args ...string) error { 18 | //定义邮箱服务器连接信息,如果是网易邮箱 pass填密码,qq邮箱填授权码 19 | mailConn := map[string]string{ 20 | "user": viper.GetString("settings.email.user"), 21 | "pass": viper.GetString("settings.email.pass"), 22 | "host": viper.GetString("settings.email.host"), 23 | "port": viper.GetString("settings.email.port"), 24 | } 25 | 26 | port, _ := strconv.Atoi(mailConn["port"]) //转换端口类型为int 27 | 28 | m := gomail.NewMessage() 29 | 30 | m.SetHeader("From", m.FormatAddress(mailConn["user"], viper.GetString("settings.email.alias"))) //这种方式可以添加别名,即“XX官方” 31 | m.SetHeader("To", mailTo...) //发送给多个用户 32 | m.SetHeader("Subject", subject) //设置邮件主题 33 | m.SetBody("text/html", body) //设置邮件正文 34 | 35 | d := gomail.NewDialer(mailConn["host"], port, mailConn["user"], mailConn["pass"]) 36 | err := d.DialAndSend(m) 37 | return err 38 | 39 | } 40 | 41 | func SendMail(mailTo []string, subject, body string) { 42 | err := server(mailTo, subject, body) 43 | if err != nil { 44 | logger.Info(err) 45 | return 46 | } 47 | logger.Info("send successfully") 48 | } 49 | -------------------------------------------------------------------------------- /pkg/hash/file.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/sha1" 6 | "crypto/sha256" 7 | "crypto/sha512" 8 | "encoding/hex" 9 | "io" 10 | "os" 11 | ) 12 | 13 | /* 对文件取hash值 */ 14 | 15 | // Md5File 文件md5值 16 | func Md5File(path string) (string, error) { 17 | f, err := os.Open(path) 18 | if err != nil { 19 | return "", err 20 | } 21 | defer f.Close() 22 | 23 | h := md5.New() 24 | if _, err := io.Copy(h, f); err != nil { 25 | return "", err 26 | } 27 | return hex.EncodeToString(h.Sum(nil)), nil 28 | } 29 | 30 | // Sha1File 文件sha1值 31 | func Sha1File(path string) (string, error) { 32 | f, err := os.Open(path) 33 | if err != nil { 34 | return "", err 35 | } 36 | defer f.Close() 37 | 38 | h := sha1.New() 39 | if _, err := io.Copy(h, f); err != nil { 40 | return "", err 41 | } 42 | return hex.EncodeToString(h.Sum(nil)), nil 43 | } 44 | 45 | // Sha256File 文件sha256值 46 | func Sha256File(path string) (string, error) { 47 | f, err := os.Open(path) 48 | if err != nil { 49 | return "", err 50 | } 51 | defer f.Close() 52 | 53 | h := sha256.New() 54 | if _, err := io.Copy(h, f); err != nil { 55 | return "", err 56 | } 57 | return hex.EncodeToString(h.Sum(nil)), nil 58 | } 59 | 60 | // Sha512File 文件sha512值 61 | func Sha512File(path string) (string, error) { 62 | f, err := os.Open(path) 63 | if err != nil { 64 | return "", err 65 | } 66 | defer f.Close() 67 | 68 | h := sha512.New() 69 | if _, err := io.Copy(h, f); err != nil { 70 | return "", err 71 | } 72 | return hex.EncodeToString(h.Sum(nil)), nil 73 | } 74 | -------------------------------------------------------------------------------- /apis/monitor/server.go: -------------------------------------------------------------------------------- 1 | package monitor 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/shirou/gopsutil/cpu" 6 | "github.com/shirou/gopsutil/disk" 7 | "github.com/shirou/gopsutil/mem" 8 | "vAdmin/tools/app" 9 | "runtime" 10 | ) 11 | 12 | const ( 13 | B = 1 14 | KB = 1024 * B 15 | MB = 1024 * KB 16 | GB = 1024 * MB 17 | ) 18 | 19 | func ServerInfo(c *gin.Context) { 20 | 21 | osDic := make(map[string]interface{}, 0) 22 | osDic["goOs"] = runtime.GOOS 23 | osDic["arch"] = runtime.GOARCH 24 | osDic["mem"] = runtime.MemProfileRate 25 | osDic["compiler"] = runtime.Compiler 26 | osDic["version"] = runtime.Version() 27 | osDic["numGoroutine"] = runtime.NumGoroutine() 28 | 29 | dis, _ := disk.Usage("/") 30 | diskTotalGB := int(dis.Total) / GB 31 | diskFreeGB := int(dis.Free) / GB 32 | diskDic := make(map[string]interface{}, 0) 33 | diskDic["total"] = diskTotalGB 34 | diskDic["free"] = diskFreeGB 35 | 36 | mem, _ := mem.VirtualMemory() 37 | memUsedMB := int(mem.Used) / GB 38 | memTotalMB := int(mem.Total) / GB 39 | memFreeMB := int(mem.Free) / GB 40 | memUsedPercent := int(mem.UsedPercent) 41 | memDic := make(map[string]interface{}, 0) 42 | memDic["total"] = memTotalMB 43 | memDic["used"] = memUsedMB 44 | memDic["free"] = memFreeMB 45 | memDic["usage"] = memUsedPercent 46 | 47 | cpuDic := make(map[string]interface{}, 0) 48 | cpuDic["cpuNum"], _ = cpu.Counts(false) 49 | 50 | 51 | app.Custum(c, gin.H{ 52 | "code": 200, 53 | "os": osDic, 54 | "mem": memDic, 55 | "cpu": cpuDic, 56 | "disk": diskDic, 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /models/process/circulationHistory.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "vAdmin/models/base" 5 | ) 6 | 7 | /* 8 | @Author : Rongxin Linghu 9 | */ 10 | 11 | // 工单流转历史 12 | type CirculationHistory struct { 13 | base.Model 14 | Title string `gorm:"column:title; type: varchar(128)" json:"title" form:"title"` // 工单标题 15 | WorkOrder int `gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"` // 工单ID 16 | State string `gorm:"column:state; type: varchar(128)" json:"state" form:"state"` // 工单状态 17 | Source string `gorm:"column:source; type: varchar(128)" json:"source" form:"source"` // 源节点ID 18 | Target string `gorm:"column:target; type: varchar(128)" json:"target" form:"target"` // 目标节点ID 19 | Circulation string `gorm:"column:circulation; type: varchar(128)" json:"circulation" form:"circulation"` // 流转ID 20 | Processor string `gorm:"column:processor; type: varchar(45)" json:"processor" form:"processor"` // 处理人 21 | ProcessorId int `gorm:"column:processor_id; type: int(11)" json:"processor_id" form:"processor_id"` // 处理人ID 22 | CostDuration string `gorm:"column:cost_duration; type: varchar(128)" json:"cost_duration" form:"cost_duration"` // 处理时长 23 | Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注 24 | } 25 | 26 | func (CirculationHistory) TableName() string { 27 | return "p_work_order_circulation_history" 28 | } 29 | -------------------------------------------------------------------------------- /tools/string.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "io/ioutil" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | func StringToInt64(e string) (int64, error) { 13 | return strconv.ParseInt(e, 10, 64) 14 | } 15 | 16 | func StringToInt(e string) (int, error) { 17 | return strconv.Atoi(e) 18 | } 19 | 20 | 21 | func GetCurrntTimeStr() string { 22 | return time.Now().Format("2006/01/02 15:04:05") 23 | } 24 | 25 | func GetCurrntTime() time.Time { 26 | return time.Now() 27 | } 28 | 29 | 30 | func StructToJsonStr(e interface{}) (string, error) { 31 | if b, err := json.Marshal(e); err == nil { 32 | return string(b), err 33 | } else { 34 | return "", err 35 | } 36 | } 37 | 38 | func GetBodyString(c *gin.Context) (string, error) { 39 | body, err := ioutil.ReadAll(c.Request.Body) 40 | if err != nil { 41 | fmt.Printf("read body err, %v\n", err) 42 | return string(body), nil 43 | } else { 44 | return "", err 45 | } 46 | } 47 | 48 | func JsonStrToMap(e string) (map[string]interface{}, error) { 49 | var dict map[string]interface{} 50 | if err := json.Unmarshal([]byte(e), &dict); err == nil { 51 | return dict, err 52 | } else { 53 | return nil, err 54 | } 55 | } 56 | 57 | func StructToMap(data interface{}) (map[string]interface{}, error) { 58 | dataBytes, err := json.Marshal(data) 59 | if err != nil { 60 | return nil, err 61 | } 62 | mapData := make(map[string]interface{}) 63 | err = json.Unmarshal(dataBytes, &mapData) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return mapData, nil 68 | } 69 | -------------------------------------------------------------------------------- /tools/logger.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | log "github.com/sirupsen/logrus" 6 | "github.com/spf13/viper" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func InitLogger() { 12 | switch Mode(viper.GetString("settings.application.mode")) { 13 | case ModeDev, ModeTest: 14 | log.SetOutput(os.Stdout) 15 | log.SetLevel(log.TraceLevel) 16 | case ModeProd: 17 | file, err := os.OpenFile(viper.GetString("logger.dir")+"/api-"+time.Now().Format("2006-01-02")+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600) 18 | if err != nil { 19 | log.Fatalln("log init failed") 20 | } 21 | 22 | var info os.FileInfo 23 | info, err = file.Stat() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | fileWriter := logFileWriter{file, info.Size()} 28 | log.SetOutput(&fileWriter) 29 | log.SetLevel(log.ErrorLevel) 30 | } 31 | 32 | log.SetReportCaller(true) 33 | } 34 | 35 | type logFileWriter struct { 36 | file *os.File 37 | size int64 38 | } 39 | 40 | func (p *logFileWriter) Write(data []byte) (n int, err error) { 41 | if p == nil { 42 | return 0, errors.New("logFileWriter is nil") 43 | } 44 | if p.file == nil { 45 | return 0, errors.New("file not opened") 46 | } 47 | n, e := p.file.Write(data) 48 | p.size += int64(n) 49 | //每天一个文件 50 | if p.file.Name() != viper.GetString("logger.dir")+"/api-"+time.Now().Format("2006-01-02")+".log" { 51 | p.file.Close() 52 | p.file, _ = os.OpenFile(viper.GetString("logger.dir")+"/api-"+time.Now().Format("2006-01-02")+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600) 53 | p.size = 0 54 | } 55 | return n, e 56 | } 57 | -------------------------------------------------------------------------------- /apis/tools/gen.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "bytes" 5 | "github.com/gin-gonic/gin" 6 | "vAdmin/models/tools" 7 | tools2 "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "net/http" 10 | "text/template" 11 | ) 12 | 13 | func Preview(c *gin.Context) { 14 | table := tools.SysTables{} 15 | id, err := tools2.StringToInt(c.Param("tableId")) 16 | tools2.HasError(err, "", -1) 17 | table.TableId = id 18 | t1, err := template.ParseFiles("template/model.go.template") 19 | tools2.HasError(err, "", -1) 20 | t2, err := template.ParseFiles("template/api.go.template") 21 | tools2.HasError(err, "", -1) 22 | t3, err := template.ParseFiles("template/js.go.template") 23 | tools2.HasError(err, "", -1) 24 | t4, err := template.ParseFiles("template/vue.go.template") 25 | tools2.HasError(err, "", -1) 26 | t5, err := template.ParseFiles("template/router.go.template") 27 | tools2.HasError(err, "", -1) 28 | tab, _ := table.Get() 29 | var b1 bytes.Buffer 30 | err = t1.Execute(&b1, tab) 31 | var b2 bytes.Buffer 32 | err = t2.Execute(&b2, tab) 33 | var b3 bytes.Buffer 34 | err = t3.Execute(&b3, tab) 35 | var b4 bytes.Buffer 36 | err = t4.Execute(&b4, tab) 37 | var b5 bytes.Buffer 38 | err = t5.Execute(&b5, tab) 39 | 40 | mp := make(map[string]interface{}) 41 | mp["template/model.go.template"] = b1.String() 42 | mp["template/api.go.template"] = b2.String() 43 | mp["template/js.go.template"] = b3.String() 44 | mp["template/vue.go.template"] = b4.String() 45 | mp["template/router.go.template"] = b5.String() 46 | var res app.Response 47 | res.Data = mp 48 | 49 | c.JSON(http.StatusOK, res.ReturnOK()) 50 | } 51 | -------------------------------------------------------------------------------- /database/drill.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "bytes" 5 | _ "github.com/pinpt/go-drill"//加载drill 6 | "database/sql" 7 | "vAdmin/tools/config" 8 | "log" 9 | "strconv" 10 | ) 11 | 12 | var Eloq *sql.DB 13 | 14 | type Databasex interface { 15 | Open(dbType string, conn string) (db *sql.DB, err error) 16 | } 17 | 18 | type Drill struct { 19 | } 20 | 21 | func SetupDrill() { 22 | 23 | DbType = config.DrillConfig.Dbtype 24 | Host = config.DrillConfig.Host 25 | Port = config.DrillConfig.Port 26 | 27 | if DbType != "drill" { 28 | log.Println("db type unknow") 29 | } 30 | var err error 31 | 32 | conn := GetDrillConnect() 33 | 34 | log.Println(conn) 35 | 36 | var db Databasex 37 | if DbType == "drill" { 38 | db = new(Drill) 39 | Eloq, err = db.Open(DbType, conn) 40 | 41 | } else { 42 | panic("db type unknow") 43 | } 44 | if err != nil { 45 | log.Fatalf("%s connect error %v", DbType, err) 46 | } else { 47 | log.Printf("%s connect success!", DbType) 48 | } 49 | 50 | 51 | if Eloquent.Error != nil { 52 | log.Fatalf("database error %v", Eloquent.Error) 53 | } 54 | 55 | Eloquent.LogMode(true) 56 | } 57 | 58 | func GetDrillConnect() string { 59 | var conn bytes.Buffer 60 | conn.WriteString("http://") 61 | conn.WriteString(Host) 62 | conn.WriteString(":") 63 | conn.WriteString(strconv.Itoa(Port)) 64 | return conn.String() 65 | } 66 | 67 | func (*Drill) Open(dbType string, conn string) (db *sql.DB, err error) { 68 | // sql.Open("drill", "http://192.168.0.18:30700") 69 | eloq, err := sql.Open(dbType, conn) 70 | eloq.SetMaxIdleConns(5) 71 | return eloq, err 72 | } 73 | 74 | 75 | -------------------------------------------------------------------------------- /models/process/process.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "encoding/json" 5 | "vAdmin/models/base" 6 | ) 7 | 8 | /* 9 | @Author : Rongxin Linghu 10 | */ 11 | 12 | // 流程 13 | type Info struct { 14 | base.Model 15 | Name string `gorm:"column:name; type:varchar(128)" json:"name" form:"name"` // 流程名称 16 | Icon string `gorm:"column:icon; type:varchar(128)" json:"icon" form:"icon"` // 流程标签 17 | Structure json.RawMessage `gorm:"column:structure; type:json" json:"structure" form:"structure"` // 流程结构 18 | Classify int `gorm:"column:classify; type:int(11)" json:"classify" form:"classify"` // 分类ID 19 | Tpls json.RawMessage `gorm:"column:tpls; type:json" json:"tpls" form:"tpls"` // 模版 20 | Task json.RawMessage `gorm:"column:task; type:json" json:"task" form:"task"` // 任务ID, array, 可执行多个任务,可以当成通知任务,每个节点都会去执行 21 | SubmitCount int `gorm:"column:submit_count; type:int(11); default:0" json:"submit_count" form:"submit_count"` // 提交统计 22 | Creator int `gorm:"column:creator; type:int(11)" json:"creator" form:"creator"` // 创建者 23 | Notice json.RawMessage `gorm:"column:notice; type:json" json:"notice" form:"notice"` // 绑定通知 24 | Remarks string `gorm:"column:remarks; type:varchar(1024)" json:"remarks" form:"remarks"` // 流程备注 25 | } 26 | 27 | func (Info) TableName() string { 28 | return "p_process_info" 29 | } 30 | -------------------------------------------------------------------------------- /middleware/header.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // NoCache is a middleware function that appends headers 10 | // to prevent the client from caching the HTTP response. 11 | func NoCache(c *gin.Context) { 12 | c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") 13 | c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") 14 | c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) 15 | c.Next() 16 | } 17 | 18 | // Options is a middleware function that appends headers 19 | // for options requests and aborts then exits the middleware 20 | // chain and ends the request. 21 | func Options(c *gin.Context) { 22 | if c.Request.Method != "OPTIONS" { 23 | c.Next() 24 | } else { 25 | c.Header("Access-Control-Allow-Origin", "*") 26 | c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS") 27 | c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept") 28 | c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS") 29 | c.Header("Content-Type", "application/json") 30 | c.AbortWithStatus(200) 31 | } 32 | } 33 | 34 | // Secure is a middleware function that appends security 35 | // and resource access headers. 36 | func Secure(c *gin.Context) { 37 | c.Header("Access-Control-Allow-Origin", "*") 38 | //c.Header("X-Frame-Options", "DENY") 39 | c.Header("X-Content-Type-Options", "nosniff") 40 | c.Header("X-XSS-Protection", "1; mode=block") 41 | if c.Request.TLS != nil { 42 | c.Header("Strict-Transport-Security", "max-age=31536000") 43 | } 44 | 45 | // Also consider adding Content-Security-Policy headers 46 | // c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com") 47 | } 48 | -------------------------------------------------------------------------------- /apis/common/queryForm.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "errors" 5 | 6 | "vAdmin/pkg/convert" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // GetQueryToStrE 12 | func GetQueryToStrE(c *gin.Context,key string) (string,error) { 13 | str,ok:=c.GetQuery(key) 14 | if !ok { 15 | return "",errors.New("没有这个值传入") 16 | } 17 | return str,nil 18 | } 19 | 20 | // GetQueryToStr 21 | func GetQueryToStr(c *gin.Context,key string,defaultValues ...string) string { 22 | var defaultValue string 23 | if len(defaultValues)>0{ 24 | defaultValue=defaultValues[0] 25 | } 26 | str,err:=GetQueryToStrE(c,key) 27 | if str=="" || err!=nil{ 28 | return defaultValue 29 | } 30 | return str 31 | } 32 | 33 | // QueryToUintE 34 | func GetQueryToUintE(c *gin.Context,key string) (uint,error) { 35 | str,err:=GetQueryToStrE(c,key) 36 | if err !=nil { 37 | return 0,err 38 | } 39 | return convert.ToUintE(str) 40 | } 41 | 42 | // QueryToUint 43 | func GetQueryToUint(c *gin.Context,key string,defaultValues ...uint) uint { 44 | var defaultValue uint 45 | if len(defaultValues)>0{ 46 | defaultValue=defaultValues[0] 47 | } 48 | val,err:=GetQueryToUintE(c,key) 49 | if err!=nil { 50 | return defaultValue 51 | } 52 | return val 53 | } 54 | 55 | // QueryToUintE 56 | func GetQueryToUint64E(c *gin.Context,key string) (uint64,error) { 57 | str,err:=GetQueryToStrE(c,key) 58 | if err !=nil { 59 | return 0,err 60 | } 61 | return convert.ToUint64E(str) 62 | } 63 | 64 | // QueryToUint 65 | func GetQueryToUint64(c *gin.Context,key string,defaultValues ...uint64) uint64 { 66 | var defaultValue uint64 67 | if len(defaultValues)>0{ 68 | defaultValue=defaultValues[0] 69 | } 70 | val,err:=GetQueryToUint64E(c,key) 71 | if err!=nil { 72 | return defaultValue 73 | } 74 | return val 75 | } 76 | -------------------------------------------------------------------------------- /tools/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/viper" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var cfgMysql *viper.Viper 13 | var cfgDrill *viper.Viper 14 | var cfgApplication *viper.Viper 15 | var cfgJwt *viper.Viper 16 | var cfgLog *viper.Viper 17 | 18 | 19 | //载入配置文件 20 | func ConfigSetup(path string) { 21 | viper.SetConfigFile(path) 22 | content, err := ioutil.ReadFile(path) 23 | if err != nil { 24 | log.Fatal(fmt.Sprintf("Read config file fail: %s", err.Error())) 25 | } 26 | 27 | //Replace environment variables 28 | err = viper.ReadConfig(strings.NewReader(os.ExpandEnv(string(content)))) 29 | if err != nil { 30 | log.Fatal(fmt.Sprintf("Parse config file fail: %s", err.Error())) 31 | } 32 | 33 | cfgMysql = viper.Sub("settings.mysql") 34 | if cfgMysql == nil { 35 | panic("config not found settings.mysql") 36 | } 37 | MysqlConfig = InitMysql(cfgMysql) 38 | 39 | cfgDrill = viper.Sub("settings.drill") 40 | if cfgDrill == nil { 41 | panic("config not found settings.drill") 42 | } 43 | DrillConfig = InitDrill(cfgDrill) 44 | 45 | cfgApplication = viper.Sub("settings.application") 46 | if cfgApplication == nil { 47 | panic("config not found settings.application") 48 | } 49 | ApplicationConfig = InitApplication(cfgApplication) 50 | 51 | cfgJwt = viper.Sub("settings.jwt") 52 | if cfgJwt == nil { 53 | panic("config not found settings.jwt") 54 | } 55 | JwtConfig = InitJwt(cfgJwt) 56 | 57 | cfgLog = viper.Sub("settings.log") 58 | if cfgLog == nil { 59 | panic("config not found settings.log") 60 | } 61 | LogConfig = InitLog(cfgLog) 62 | } 63 | 64 | 65 | func SetConfig(configPath string, key string, value interface{}) { 66 | viper.AddConfigPath(configPath) 67 | viper.Set(key, value) 68 | viper.WriteConfig() 69 | } 70 | -------------------------------------------------------------------------------- /models/tools/dbtables.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | orm "vAdmin/database" 6 | config2 "vAdmin/tools/config" 7 | ) 8 | 9 | type DBTables struct { 10 | TableName string `gorm:"column:TABLE_NAME" json:"tableName"` 11 | Engine string `gorm:"column:ENGINE" json:"engine"` 12 | TableRows string `gorm:"column:TABLE_ROWS" json:"tableRows"` 13 | TableCollation string `gorm:"column:TABLE_COLLATION" json:"tableCollation"` 14 | CreateTime string `gorm:"column:CREATE_TIME" json:"createTime"` 15 | UpdateTime string `gorm:"column:UPDATE_TIME" json:"updateTime"` 16 | TableComment string `gorm:"column:TABLE_COMMENT" json:"tableComment"` 17 | } 18 | 19 | func (e *DBTables) GetPage(pageSize int, pageIndex int) ([]DBTables, int, error) { 20 | var doc []DBTables 21 | 22 | table := orm.Eloquent.Select("*").Table("information_schema.tables") 23 | table = table.Where("TABLE_NAME not in (select table_name from "+config2.MysqlConfig.Name+".sys_tables) ") 24 | table = table.Where("table_schema= ? ", config2.MysqlConfig.Name) 25 | 26 | if e.TableName != "" { 27 | table = table.Where("TABLE_NAME = ?", e.TableName) 28 | } 29 | 30 | var count int 31 | 32 | if err := table.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&doc).Error; err != nil { 33 | return nil, 0, err 34 | } 35 | table.Count(&count) 36 | return doc, count, nil 37 | } 38 | 39 | func (e *DBTables) Get() (DBTables, error) { 40 | var doc DBTables 41 | 42 | table := orm.Eloquent.Select("*").Table("information_schema.tables") 43 | table = table.Where("table_schema= ? ", config2.MysqlConfig.Name) 44 | if e.TableName == "" { 45 | return doc, errors.New("table name cannot be empty!") 46 | } 47 | table = table.Where("TABLE_NAME = ?", e.TableName) 48 | 49 | if err := table.First(&doc).Error; err != nil { 50 | return doc, err 51 | } 52 | return doc, nil 53 | } 54 | -------------------------------------------------------------------------------- /tools/user.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | jwt "vAdmin/pkg/jwtauth" 7 | "log" 8 | ) 9 | 10 | func ExtractClaims(c *gin.Context) jwt.MapClaims { 11 | claims, exists := c.Get("JWT_PAYLOAD") 12 | if !exists { 13 | return make(jwt.MapClaims) 14 | } 15 | 16 | return claims.(jwt.MapClaims) 17 | } 18 | 19 | func GetUserId(c *gin.Context) int { 20 | data := ExtractClaims(c) 21 | if data["identity"] != nil { 22 | return int((data["identity"]).(float64)) 23 | } 24 | log.Println("****************************** 路径:" + c.Request.URL.Path + " 请求方法:" + c.Request.Method + " 说明:缺少identity") 25 | return 0 26 | } 27 | 28 | func GetUserIdStr(c *gin.Context) string { 29 | data := ExtractClaims(c) 30 | if data["identity"] != nil { 31 | return Int64ToString(int64((data["identity"]).(float64))) 32 | } 33 | log.Println("****************************** 路径:" + c.Request.URL.Path + " 请求方法:" + c.Request.Method + " 缺少identity") 34 | return "" 35 | } 36 | 37 | func GetUserName(c *gin.Context) string { 38 | data := ExtractClaims(c) 39 | if data["nice"] != nil { 40 | return (data["nice"]).(string) 41 | } 42 | fmt.Println("****************************** 路径:" + c.Request.URL.Path + " 请求方法:" + c.Request.Method + " 缺少nice") 43 | return "" 44 | } 45 | 46 | func GetRoleName(c *gin.Context) string { 47 | data := ExtractClaims(c) 48 | if data["rolekey"] != nil { 49 | return (data["rolekey"]).(string) 50 | } 51 | fmt.Println("****************************** 路径:" + c.Request.URL.Path + " 请求方法:" + c.Request.Method + " 缺少rolekey") 52 | return "" 53 | } 54 | 55 | func GetRoleId(c *gin.Context) int { 56 | data := ExtractClaims(c) 57 | if data["roleid"] != nil { 58 | i := int((data["roleid"]).(float64)) 59 | return i 60 | } 61 | fmt.Println("****************************** 路径:" + c.Request.URL.Path + " 请求方法:" + c.Request.Method + " 缺少roleid") 62 | return 0 63 | } 64 | -------------------------------------------------------------------------------- /models/process/workOrder.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "encoding/json" 5 | "vAdmin/models/base" 6 | ) 7 | 8 | /* 9 | @Author : Rongxin Linghu 10 | */ 11 | 12 | // 工单 13 | type WorkOrderInfo struct { 14 | base.Model 15 | Title string `gorm:"column:title; type:varchar(128)" json:"title" form:"title"` // 工单标题 16 | Priority int `gorm:"column:priority; type:int(11)" json:"priority" form:"priority"` // 工单优先级 1,正常 2,紧急 3,非常紧急 17 | Process int `gorm:"column:process; type:int(11)" json:"process" form:"process"` // 流程ID 18 | Classify int `gorm:"column:classify; type:int(11)" json:"classify" form:"classify"` // 分类ID 19 | IsEnd int `gorm:"column:is_end; type:int(11); default:0" json:"is_end" form:"is_end"` // 是否结束, 0 未结束,1 已结束 20 | IsDenied int `gorm:"column:is_denied; type:int(11); default:0" json:"is_denied" form:"is_denied"` // 是否被拒绝, 0 没有,1 有 21 | State json.RawMessage `gorm:"column:state; type:json" json:"state" form:"state"` // 状态信息 22 | RelatedPerson json.RawMessage `gorm:"column:related_person; type:json" json:"related_person" form:"related_person"` // 工单所有处理人 23 | Creator int `gorm:"column:creator; type:int(11)" json:"creator" form:"creator"` // 创建人 24 | UrgeCount int `gorm:"column:urge_count; type:int(11); default:0" json:"urge_count" form:"urge_count"` // 催办次数 25 | UrgeLastTime int `gorm:"column:urge_last_time; type:int(11); default:0" json:"urge_last_time" form:"urge_last_time"` // 上一次催促时间 26 | } 27 | 28 | func (WorkOrderInfo) TableName() string { 29 | return "p_work_order_info" 30 | } 31 | -------------------------------------------------------------------------------- /apis/deploy/server.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "fmt" 5 | "vAdmin/tools/command" 6 | ) 7 | 8 | const ( 9 | COMMAND_TIMEOUT = 3600 10 | ) 11 | 12 | type Server struct { 13 | ID int 14 | Addr string 15 | User string 16 | Port int 17 | Cmd string 18 | Key string 19 | task *command.Task 20 | result *ServerResult 21 | } 22 | 23 | type ServerResult struct { 24 | ID int 25 | TaskResult []*command.TaskResult 26 | Status int 27 | Error error 28 | } 29 | 30 | func NewServer(srv *Server) { 31 | srv.result = &ServerResult{ 32 | ID: srv.ID, 33 | Status: STATUS_INIT, 34 | } 35 | srv.task = command.NewTask( 36 | srv.deployCmd(), 37 | COMMAND_TIMEOUT, 38 | ) 39 | } 40 | 41 | func (srv *Server) Deploy() { 42 | srv.result.Status = STATUS_ING 43 | srv.task.Run() 44 | if err := srv.task.GetError(); err != nil { 45 | srv.result.Error = err 46 | srv.result.Status = STATUS_FAILED 47 | } else { 48 | srv.result.Status = STATUS_DONE 49 | } 50 | } 51 | 52 | func (srv *Server) Terminate() { 53 | if srv.result.Status == STATUS_ING { 54 | srv.task.Terminate() 55 | } 56 | } 57 | 58 | func (srv *Server) Result() *ServerResult { 59 | srv.result.TaskResult = srv.task.Result() 60 | return srv.result 61 | } 62 | 63 | func (srv *Server) deployCmd() []string { 64 | var ( 65 | useCustomKey, useSshPort string 66 | ) 67 | if srv.Key != "" { 68 | useCustomKey = fmt.Sprintf("-i %s", srv.Key) 69 | } 70 | if srv.Port != 0 { 71 | useSshPort = fmt.Sprintf("-p %d", srv.Port) 72 | } 73 | var cmds []string 74 | 75 | if srv.Cmd != "" { 76 | cmds = append( 77 | cmds, 78 | fmt.Sprintf("/usr/bin/env ssh -o StrictHostKeyChecking=no %s %s %s@%s '%s'", 79 | useCustomKey, 80 | useSshPort, 81 | srv.User, 82 | srv.Addr, 83 | srv.Cmd, 84 | ), 85 | ) 86 | } 87 | return cmds 88 | } -------------------------------------------------------------------------------- /render/render.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | const ( 14 | CODE_OK = 0 15 | CODE_ERR_SYSTEM = 1000 16 | CODE_ERR_APP = 1001 17 | CODE_ERR_PARAM = 1002 18 | CODE_ERR_DATA_REPEAT = 1003 19 | CODE_ERR_LOGIN_FAILED = 1004 20 | CODE_ERR_NO_LOGIN = 1005 21 | CODE_ERR_NO_PRIV = 1006 22 | CODE_ERR_TASK_ERROR = 1007 23 | CODE_ERR_USER_OR_PASS_WRONG = 1008 24 | CODE_ERR_NO_DATA = 1009 25 | ) 26 | 27 | func JSON(c *gin.Context, data interface{}) { 28 | c.JSON(http.StatusOK, gin.H{ 29 | "code": CODE_OK, 30 | "message": "success", 31 | "data": data, 32 | }) 33 | } 34 | 35 | func CustomerError(c *gin.Context, code int, message string) { 36 | c.JSON(http.StatusOK, gin.H{ 37 | "code": code, 38 | "message": message, 39 | }) 40 | } 41 | 42 | func RepeatError(c *gin.Context, message string) { 43 | c.JSON(http.StatusOK, gin.H{ 44 | "code": CODE_ERR_DATA_REPEAT, 45 | "message": message, 46 | }) 47 | } 48 | 49 | func NoDataError(c *gin.Context, message string) { 50 | c.JSON(http.StatusOK, gin.H{ 51 | "code": CODE_ERR_NO_DATA, 52 | "message": message, 53 | }) 54 | } 55 | 56 | func ParamError(c *gin.Context, message string) { 57 | c.JSON(http.StatusOK, gin.H{ 58 | "code": CODE_ERR_PARAM, 59 | "message": message, 60 | }) 61 | } 62 | 63 | func AppError(c *gin.Context, message string) { 64 | c.JSON(http.StatusOK, gin.H{ 65 | "code": CODE_ERR_APP, 66 | "message": message, 67 | }) 68 | } 69 | 70 | func Success(c *gin.Context) { 71 | c.JSON(http.StatusOK, gin.H{ 72 | "code": CODE_OK, 73 | "message": "success", 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /tools/command/command_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package command 6 | 7 | import ( 8 | "testing" 9 | "time" 10 | "strings" 11 | ) 12 | 13 | func TestCmdRun(t *testing.T) { 14 | c := &Command{ 15 | Cmd: "echo 'syncd'", 16 | Timeout: time.Second * 60, 17 | } 18 | var err error 19 | if c, err = NewCmd(c); err != nil { 20 | t.Error(err) 21 | } 22 | if err = c.Run(); err != nil { 23 | t.Error(err) 24 | } 25 | output := c.Stdout() 26 | if output != "syncd" { 27 | t.Errorf("cmd `echo syncd` output '%s' not eq 'syncd'", output) 28 | } 29 | } 30 | 31 | func TestCmdTimeout(t *testing.T) { 32 | c := &Command{ 33 | Cmd: "sleep 2", 34 | Timeout: time.Second * 1, 35 | } 36 | var err error 37 | if c, err = NewCmd(c); err != nil { 38 | t.Error(err) 39 | } 40 | err = c.Run() 41 | if err == nil { 42 | t.Error("cmd should run timeout and output errmsg, but err is nil") 43 | } 44 | if strings.Index(err.Error(), "cmd run timeout") == -1 { 45 | t.Errorf("cmd run timeout output '%s' prefix not eq 'cmd run timeout'", err.Error()) 46 | } 47 | } 48 | 49 | func TestCmdTerminate(t *testing.T) { 50 | tChan := make(chan int) 51 | c := &Command{ 52 | Cmd: "sleep 5", 53 | TerminateChan: tChan, 54 | } 55 | var err error 56 | if c, err = NewCmd(c); err != nil { 57 | t.Error(err) 58 | } 59 | go func() { 60 | err = c.Run() 61 | if err == nil { 62 | t.Error("cmd should be terminated and output errmsg, but err is nil") 63 | } 64 | if strings.Index(err.Error(), "cmd is terminated") == -1 { 65 | t.Errorf("cmd is terminated output '%s' prefix not eq 'cmd is terminated'", err.Error()) 66 | } 67 | }() 68 | 69 | tChan<- 1 70 | } 71 | 72 | -------------------------------------------------------------------------------- /pkg/notify/send.go: -------------------------------------------------------------------------------- 1 | package notify 2 | 3 | import ( 4 | "bytes" 5 | system "vAdmin/models" 6 | "vAdmin/pkg/logger" 7 | "vAdmin/pkg/notify/email" 8 | "text/template" 9 | 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | /* 14 | @Author : Rongxin Linghu 15 | @同时发送多种通知方式 16 | */ 17 | 18 | type BodyData struct { 19 | SendTo interface{} // 接受人 20 | Subject string // 标题 21 | Classify []int // 通知类型 22 | Id int // 工单ID 23 | Title string // 工单标题 24 | Creator string // 工单创建人 25 | Priority int // 工单优先级 26 | PriorityValue string // 工单优先级 27 | CreatedAt string // 工单创建时间 28 | Content string // 通知的内容 29 | Description string // 表格上面的描述信息 30 | ProcessId int // 流程ID 31 | Domain string // 域名地址 32 | } 33 | 34 | func (b *BodyData) ParsingTemplate() (err error) { 35 | // 读取模版数据 36 | var ( 37 | buf bytes.Buffer 38 | ) 39 | 40 | tmpl, err := template.ParseFiles("./static/template/email.html") 41 | if err != nil { 42 | return 43 | } 44 | 45 | b.Domain = viper.GetString("settings.domain.url") 46 | err = tmpl.Execute(&buf, b) 47 | if err != nil { 48 | return 49 | } 50 | 51 | b.Content = buf.String() 52 | 53 | return 54 | } 55 | 56 | func (b *BodyData) SendNotify() (err error) { 57 | var ( 58 | emailList []string 59 | ) 60 | 61 | switch b.Priority { 62 | case 1: 63 | b.PriorityValue = "正常" 64 | case 2: 65 | b.PriorityValue = "紧急" 66 | case 3: 67 | b.PriorityValue = "非常紧急" 68 | } 69 | 70 | for _, c := range b.Classify { 71 | switch c { 72 | case 1: // 邮件 73 | users := b.SendTo.(map[string]interface{})["userList"].([]system.SysUser) 74 | if len(users) > 0 { 75 | for _, user := range users { 76 | emailList = append(emailList, user.Email) 77 | } 78 | err = b.ParsingTemplate() 79 | if err != nil { 80 | logger.Errorf("模版内容解析失败,%v", err.Error()) 81 | return 82 | } 83 | go email.SendMail(emailList, b.Subject, b.Content) 84 | } 85 | } 86 | } 87 | return 88 | } 89 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module vAdmin 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/RichardKnop/machinery v1.10.0 7 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect 8 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 9 | github.com/casbin/casbin/v2 v2.2.1 10 | github.com/casbin/gorm-adapter/v2 v2.0.3 11 | github.com/coocood/freecache v1.1.0 12 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 13 | github.com/dreamans/syncd v0.0.0-20200328071902-9eed461abb73 14 | github.com/francoispqt/gojay v1.2.13 // indirect 15 | github.com/gin-gonic/gin v1.4.0 16 | github.com/go-kit/kit v0.8.0 17 | github.com/go-ldap/ldap/v3 v3.4.1 18 | github.com/go-ole/go-ole v1.2.4 // indirect 19 | github.com/go-openapi/spec v0.19.8 // indirect 20 | github.com/go-openapi/swag v0.19.9 // indirect 21 | github.com/go-sql-driver/mysql v1.5.0 22 | github.com/google/uuid v1.1.2 23 | github.com/gorilla/websocket v1.4.0 24 | github.com/jinzhu/gorm v1.9.10 25 | github.com/jinzhu/now v1.1.2 // indirect 26 | github.com/json-iterator/go v1.1.9 // indirect 27 | github.com/mailru/easyjson v0.7.1 // indirect 28 | github.com/mattn/go-isatty v0.0.10 // indirect 29 | github.com/mojocn/base64Captcha v1.3.1 30 | github.com/mssola/user_agent v0.5.1 31 | github.com/pinpt/go-drill v0.0.0-20190117152140-1a586edddf3d 32 | github.com/pkg/errors v0.9.1 33 | github.com/robfig/cron/v3 v3.0.1 34 | github.com/satori/go.uuid v1.2.0 35 | github.com/shirou/gopsutil v2.20.3+incompatible 36 | github.com/sirupsen/logrus v1.4.2 37 | github.com/spf13/afero v1.2.2 // indirect 38 | github.com/spf13/cobra v1.0.0 39 | github.com/spf13/pflag v1.0.5 // indirect 40 | github.com/spf13/viper v1.6.2 41 | github.com/stretchr/testify v1.6.1 42 | github.com/swaggo/gin-swagger v1.2.0 43 | github.com/swaggo/swag v1.6.7 44 | github.com/ugorji/go v1.1.7 // indirect 45 | go.uber.org/zap v1.10.0 46 | golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 47 | golang.org/x/text v0.3.4 48 | gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df 49 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 50 | ) 51 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | //"vAdmin/apis/tpl" 6 | "vAdmin/apis/releasecicd" 7 | "vAdmin/router/process" 8 | jwt "vAdmin/pkg/jwtauth" 9 | ) 10 | 11 | 12 | 13 | // 路由示例 14 | func InitAppsRouter(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware) *gin.Engine { 15 | 16 | // 无需认证的路由 17 | examplesNoCheckRoleRouter(r) 18 | // 需要认证的路由 19 | ReleasecicdCheckRoleRouter(r, authMiddleware) 20 | 21 | DeployAppCheckRoleRouter(r, authMiddleware) 22 | 23 | ProcessCheckRoleRouter(r, authMiddleware) 24 | 25 | return r 26 | } 27 | 28 | // 无需认证的路由示例 29 | func examplesNoCheckRoleRouter(r *gin.Engine) { 30 | 31 | v1 := r.Group("/api/v1") 32 | //v1.GET("/examples/list", examples.apis) 33 | v1.GET("/releasechart/deploybyday",releasecicd.GetDeployByDay) 34 | v1.GET("/releasechart/deploybyhour",releasecicd.GetDeployByHour) 35 | } 36 | 37 | // 需要认证的路由示例 38 | func ReleasecicdCheckRoleRouter(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware) { 39 | v1:= r.Group("/api/v1") 40 | registerReleasecicdRouter(v1,authMiddleware) 41 | registerReleaseridRouter(v1,authMiddleware) 42 | registerReleasetodoRouter(v1,authMiddleware) 43 | //registerReleaseChartRouter(v0,authMiddleware) 44 | registerReleaseStgRouter(v1,authMiddleware) 45 | //v1auth := v1.Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 46 | //{ 47 | // v1auth.GET("/examples/list", examples.apis) 48 | //} 49 | } 50 | 51 | func ProcessCheckRoleRouter(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware) { 52 | 53 | v1 := r.Group("/api/v1") 54 | 55 | // 兼容前后端不分离的情 56 | // r.GET("/", tpl.Tpl) 57 | 58 | // 流程中心 59 | process.RegisterClassifyRouter(v1, authMiddleware) 60 | process.RegisterProcessRouter(v1, authMiddleware) 61 | process.RegisterTaskRouter(v1, authMiddleware) 62 | process.RegisterTplRouter(v1, authMiddleware) 63 | process.RegisterWorkOrderRouter(v1, authMiddleware) 64 | 65 | } 66 | 67 | // 需要认证的路由示例 68 | func DeployAppCheckRoleRouter(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware) { 69 | v1:= r.Group("/api/v1") 70 | registerDeployAppRouter(v1,authMiddleware) 71 | } -------------------------------------------------------------------------------- /pkg/task/worker/tasks.go: -------------------------------------------------------------------------------- 1 | package worker 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "vAdmin/pkg/logger" 7 | "os/exec" 8 | "syscall" 9 | 10 | "github.com/RichardKnop/machinery/v1/tasks" 11 | ) 12 | 13 | var asyncTaskMap map[string]interface{} 14 | 15 | func executeTaskBase(scriptPath string, params string) (err error) { 16 | command := exec.Command(scriptPath, params) //初始化Cmd 17 | out, err := command.CombinedOutput() 18 | if err != nil { 19 | logger.Errorf("task exec failed,%v", err.Error()) 20 | return 21 | } 22 | logger.Info("Output: ", string(out)) 23 | logger.Info("ProcessState PID: ", command.ProcessState.Pid()) 24 | logger.Info("Exit Code ", command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()) 25 | return 26 | } 27 | 28 | // ExecCommand 异步任务 29 | func ExecCommand(classify string, scriptPath string, params string) (err error) { 30 | if classify == "shell" { 31 | logger.Info("start exec shell - ", scriptPath) 32 | err = executeTaskBase(scriptPath, params) 33 | if err != nil { 34 | return 35 | } 36 | } else if classify == "python" { 37 | logger.Info("start exec python - ", scriptPath) 38 | err = executeTaskBase(scriptPath, params) 39 | if err != nil { 40 | return 41 | } 42 | } else { 43 | err = errors.New("目前仅支持Python与Shell脚本的执行,请知悉。") 44 | return 45 | } 46 | return 47 | } 48 | 49 | func SendTask(ctx context.Context, classify string, scriptPath string, params string) { 50 | args := make([]tasks.Arg, 0) 51 | args = append(args, tasks.Arg{ 52 | Name: "classify", 53 | Type: "string", 54 | Value: classify, 55 | }) 56 | args = append(args, tasks.Arg{ 57 | Name: "scriptPath", 58 | Type: "string", 59 | Value: scriptPath, 60 | }) 61 | args = append(args, tasks.Arg{ 62 | Name: "params", 63 | Type: "string", 64 | Value: params, 65 | }) 66 | task, _ := tasks.NewSignature("ExecCommandTask", args) 67 | task.RetryCount = 5 68 | _, err := AsyncTaskCenter.SendTaskWithContext(ctx, task) 69 | if err != nil { 70 | logger.Error(err.Error()) 71 | } 72 | } 73 | 74 | func initAsyncTaskMap() { 75 | asyncTaskMap = make(map[string]interface{}) 76 | asyncTaskMap["ExecCommandTask"] = ExecCommand 77 | } 78 | -------------------------------------------------------------------------------- /tools/command/task.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package command 6 | 7 | import ( 8 | "errors" 9 | "time" 10 | ) 11 | 12 | type Task struct { 13 | Commands []string 14 | done bool 15 | timeout int 16 | termChan chan int 17 | err error 18 | result []*TaskResult 19 | } 20 | 21 | type TaskResult struct { 22 | Cmd string `json:"cmd"` 23 | Stdout string `json:"stdout"` 24 | Stderr string `json:"stderr"` 25 | Success bool `json:"success"` 26 | } 27 | 28 | func NewTask(cmds []string, timeout int) *Task { 29 | return &Task{ 30 | Commands: cmds, 31 | termChan: make(chan int), 32 | timeout: timeout, 33 | } 34 | } 35 | 36 | func (t *Task) Run() { 37 | for _, cmd := range t.Commands { 38 | result, err := t.next(cmd) 39 | t.result = append(t.result, result) 40 | if err != nil { 41 | t.err = errors.New("task run failed, " + err.Error()) 42 | break 43 | } 44 | } 45 | t.done = true 46 | } 47 | 48 | func (t *Task) GetError() error { 49 | return t.err 50 | } 51 | 52 | func (t *Task) Result() []*TaskResult { 53 | return t.result 54 | } 55 | 56 | func (t *Task) next(cmd string) (*TaskResult, error) { 57 | result := &TaskResult{ 58 | Cmd: cmd, 59 | } 60 | command := &Command{ 61 | Cmd: cmd, 62 | Timeout: time.Second * time.Duration(t.timeout), 63 | TerminateChan: t.termChan, 64 | } 65 | var err error 66 | command, err = NewCmd(command) 67 | if err != nil { 68 | return result, err 69 | } 70 | if err := command.Run(); err != nil { 71 | result.Stderr = command.Stderr() 72 | return result, err 73 | } 74 | result.Stdout = command.Stdout() 75 | println("result.Stdout:%s", result.Stdout) 76 | result.Success = true 77 | return result, nil 78 | } 79 | 80 | func (t *Task) Terminate() { 81 | if !t.done { 82 | t.termChan <- 1 83 | } 84 | } -------------------------------------------------------------------------------- /template/api.go.template: -------------------------------------------------------------------------------- 1 | package apis 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "vAdmin/tools/app/msg" 10 | ) 11 | 12 | func Get{{.ClassName}}List(c *gin.Context) { 13 | var data models.{{.ClassName}} 14 | var err error 15 | var pageSize = 10 16 | var pageIndex = 1 17 | 18 | if size := c.Request.FormValue("pageSize"); size != "" { 19 | pageSize = tools.StrToInt(err, size) 20 | } 21 | if index := c.Request.FormValue("pageIndex"); index != "" { 22 | pageIndex = tools.StrToInt(err, index) 23 | } 24 | 25 | {{ range .Columns -}} 26 | {{$z := .IsQuery}} 27 | {{- if ($z) -}}data.{{.GoField}} = c.Request.FormValue("{{.JsonField}}") 28 | {{ end -}} 29 | {{end}} 30 | 31 | data.DataScope = tools.GetUserIdStr(c) 32 | result, count, err := data.GetPage(pageSize, pageIndex) 33 | tools.HasError(err, "", -1) 34 | 35 | app.PageOK(c, result, count, pageIndex, pageSize, "") 36 | } 37 | 38 | func Get{{.ClassName}}(c *gin.Context) { 39 | var data models.{{.ClassName}} 40 | data.{{.PkGoField}}, _ = tools.StringToInt(c.Param("{{.PkJsonField}}")) 41 | result, err := data.Get() 42 | tools.HasError(err, "抱歉未找到相关信息", -1) 43 | 44 | app.OK(c, result, "") 45 | } 46 | 47 | func Insert{{.ClassName}}(c *gin.Context) { 48 | var data models.{{.ClassName}} 49 | err := c.ShouldBindJSON(&data) 50 | data.CreateBy = tools.GetUserIdStr(c) 51 | tools.HasError(err, "", 500) 52 | result, err := data.Create() 53 | tools.HasError(err, "", -1) 54 | app.OK(c, result, "") 55 | } 56 | 57 | func Update{{.ClassName}}(c *gin.Context) { 58 | var data models.{{.ClassName}} 59 | err := c.BindWith(&data, binding.JSON) 60 | tools.HasError(err, "数据解析失败", -1) 61 | data.UpdateBy = tools.GetUserIdStr(c) 62 | result, err := data.Update(data.{{.PkGoField}}) 63 | tools.HasError(err, "", -1) 64 | 65 | app.OK(c, result, "") 66 | } 67 | 68 | func Delete{{.ClassName}}(c *gin.Context) { 69 | var data models.{{.ClassName}} 70 | data.UpdateBy = tools.GetUserIdStr(c) 71 | 72 | IDS := tools.IdsStrToIdsIntGroup("{{.PkJsonField}}", c) 73 | _, err := data.BatchDelete(IDS) 74 | tools.HasError(err, msg.DeletedFail, 500) 75 | app.OK(c, nil, msg.DeletedSuccess) 76 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "vAdmin/cmd" 5 | ) 6 | 7 | // @title DevOps API 8 | // @version 0.0.1 9 | // @description Rnotes系统 10 | // @SecurityDefinitions.apikey Bearer 11 | // @in header 12 | // @name Authorization 13 | // @BasePath / 14 | 15 | //func main() { 16 | // configName := "settings" 17 | // 18 | // 19 | // config.InitConfig(configName) 20 | // 21 | // gin.SetMode(gin.DebugMode) 22 | // log.Println(config.MysqlConfig.Port) 23 | // 24 | // err := gorm.AutoMigrate(orm.Eloquent) 25 | // if err != nil { 26 | // log.Fatalln("数据库初始化失败 err: %v", err) 27 | // } 28 | // 29 | // if config.ApplicationConfig.IsInit { 30 | // if err := models.InitDb(); err != nil { 31 | // log.Fatal("数据库基础数据初始化失败!") 32 | // } else { 33 | // config.SetApplicationIsInit() 34 | // } 35 | // } 36 | // 37 | // r := router.InitRouter() 38 | // 39 | // defer orm.Eloquent.Close() 40 | // 41 | // srv := &http.Server{ 42 | // Addr: config.ApplicationConfig.Host + ":" + config.ApplicationConfig.Port, 43 | // Handler: r, 44 | // } 45 | // 46 | // go func() { 47 | // // 服务连接 48 | // if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 49 | // log.Fatalf("listen: %s\n", err) 50 | // } 51 | // }() 52 | // log.Println("Server Run ", config.ApplicationConfig.Host+":"+config.ApplicationConfig.Port) 53 | // log.Println("Enter Control + C Shutdown Server") 54 | // // 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间) 55 | // quit := make(chan os.Signal) 56 | // signal.Notify(quit, os.Interrupt) 57 | // <-quit 58 | // log.Println("Shutdown Server ...") 59 | // 60 | // ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 61 | // defer cancel() 62 | // if err := srv.Shutdown(ctx); err != nil { 63 | // log.Fatal("Server Shutdown:", err) 64 | // } 65 | // log.Println("Server exiting") 66 | //} 67 | 68 | func main() { 69 | 70 | /* 71 | log.Println("Starting...") 72 | 73 | c := cron.New() 74 | c.AddFunc("0 0 0 *", func() { 75 | log.Println("Run models.PullRelease...") 76 | models.PullRelease() 77 | }) 78 | 79 | c.Start() 80 | 81 | t1 := time.NewTimer(time.Second * 10) 82 | for { 83 | select { 84 | case <-t1.C: 85 | t1.Reset(time.Second * 10) 86 | } 87 | } 88 | */ 89 | cmd.Execute() 90 | } 91 | -------------------------------------------------------------------------------- /tools/gofile/file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gofile 6 | 7 | import ( 8 | "bufio" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "strings" 13 | ) 14 | 15 | func CreateFile(filePath string, data []byte, perm os.FileMode) error { 16 | return ioutil.WriteFile(filePath, data, perm) 17 | } 18 | 19 | func ReadlineAddHead(filePath string, headstr string) error { 20 | var result string 21 | f, err := os.Open(filePath) 22 | if err != nil { 23 | return err 24 | } 25 | defer f.Close() 26 | rd := bufio.NewReader(f) 27 | for { 28 | line, err := rd.ReadString('\n') 29 | line = strings.Replace(line,"\r","",-1) 30 | line = strings.Replace(line,"\n","",-1) 31 | //fmt.Println(line) 32 | result += headstr + " " + string(line) + "\n" 33 | if err != nil || io.EOF == err { 34 | break 35 | } 36 | } 37 | 38 | fw, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)//os.O_TRUNC清空文件重新写入,否则原文件内容可能残留 39 | w := bufio.NewWriter(fw) 40 | w.WriteString(result) 41 | if err != nil { 42 | return err 43 | } 44 | w.Flush() 45 | return err 46 | } 47 | 48 | func ReadlineAddafter(filePath string, afterstr string) error { 49 | var result string 50 | f, err := os.Open(filePath) 51 | if err != nil { 52 | return err 53 | } 54 | defer f.Close() 55 | rd := bufio.NewReader(f) 56 | for { 57 | line, err := rd.ReadString('\n') 58 | line = strings.Replace(line,"\r","",-1) 59 | line = strings.Replace(line,"\n","",-1) 60 | //fmt.Println(line) 61 | result += string(line) + " " + afterstr + "\n" 62 | if err != nil || io.EOF == err { 63 | break 64 | } 65 | } 66 | 67 | fw, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)//os.O_TRUNC清空文件重新写入,否则原文件内容可能残留 68 | w := bufio.NewWriter(fw) 69 | w.WriteString(result) 70 | if err != nil { 71 | return err 72 | } 73 | w.Flush() 74 | return err 75 | } -------------------------------------------------------------------------------- /router/releasecicd_router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/apis/releasecicd" 6 | "vAdmin/middleware" 7 | jwt "vAdmin/pkg/jwtauth" 8 | ) 9 | 10 | //业务 需要认证的路由 11 | func registerReleasecicdRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 12 | 13 | r := v1.Group("/releasecicd").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 14 | { 15 | r.GET("/:id", releasecicd.GetReleasecicd) 16 | r.POST("", releasecicd.InsertReleasecicd) 17 | r.PUT("", releasecicd.UpdateReleasecicd) 18 | r.DELETE("/:id", releasecicd.DeleteReleasecicd) 19 | } 20 | 21 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 22 | { 23 | l.GET("/releasecicdList",releasecicd.GetReleasecicdList) 24 | l.GET("/pageList",releasecicd.GetPageList) 25 | } 26 | 27 | } 28 | 29 | func registerReleaseridRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 30 | 31 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 32 | { 33 | l.GET("/releaseridList",releasecicd.GetRidList) 34 | } 35 | 36 | } 37 | 38 | func registerReleasetodoRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 39 | 40 | r := v1.Group("/releasetodo").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 41 | { 42 | r.PUT("", releasecicd.UpdateReleasetodo) 43 | } 44 | 45 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 46 | { 47 | l.GET("/releasetodoList",releasecicd.GetReleasetodolist) 48 | } 49 | 50 | } 51 | 52 | func registerReleaseStgRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) { 53 | 54 | l := v1.Group("").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 55 | { 56 | l.GET("/releasestgList",releasecicd.GetReleaseStglist) 57 | } 58 | 59 | } 60 | 61 | /* 62 | func registerReleaseChartRouter(v1 *gin.RouterGroup,authMiddleware *jwt.GinJWTMiddleware) { 63 | c := v1.Group("releasechart").Use(authMiddleware.MiddlewareFunc()).Use(middleware.AuthCheckRole()) 64 | 65 | { 66 | c.GET("/deploybyday",releasecicd.GetDeployByDay) 67 | c.GET("/deploybyhour",releasecicd.GetDeployByHour) 68 | } 69 | } 70 | */ -------------------------------------------------------------------------------- /pkg/ldap/search.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | orm "vAdmin/database" 7 | "vAdmin/models/system" 8 | "vAdmin/pkg/logger" 9 | "fmt" 10 | 11 | "github.com/go-ldap/ldap/v3" 12 | "github.com/spf13/viper" 13 | ) 14 | 15 | /* 16 | @Author : Rongxin Linghu 17 | */ 18 | 19 | func getLdapFields() (ldapFields []map[string]string, err error) { 20 | var ( 21 | settingsValue system.Settings 22 | contentList []map[string]string 23 | ) 24 | 25 | err = orm.Eloquent.Model(&settingsValue).Where("classify = 2").Find(&settingsValue).Error 26 | if err != nil { 27 | return 28 | } 29 | 30 | err = json.Unmarshal(settingsValue.Content, &contentList) 31 | if err != nil { 32 | return 33 | } 34 | 35 | for _, v := range contentList { 36 | if v["ldap_field_name"] != "" { 37 | ldapFields = append(ldapFields, v) 38 | } 39 | } 40 | return 41 | } 42 | 43 | func searchRequest(username string) (userInfo *ldap.Entry, err error) { 44 | var ( 45 | ldapFields []map[string]string 46 | cur *ldap.SearchResult 47 | ldapFieldsFilter = []string{ 48 | "dn", 49 | } 50 | ) 51 | ldapFields, err = getLdapFields() 52 | for _, v := range ldapFields { 53 | ldapFieldsFilter = append(ldapFieldsFilter, v["ldap_field_name"]) 54 | } 55 | // 用来获取查询权限的用户。如果 ldap 禁止了匿名查询,那我们就需要先用这个帐户 bind 以下才能开始查询 56 | if !viper.GetBool("settings.ldap.anonymousQuery") { 57 | err = conn.Bind( 58 | viper.GetString("settings.ldap.bindUserDn"), 59 | viper.GetString("settings.ldap.bindPwd")) 60 | if err != nil { 61 | logger.Error("用户或密码错误。", err) 62 | return 63 | } 64 | } 65 | 66 | sql := ldap.NewSearchRequest( 67 | viper.GetString("settings.ldap.baseDn"), 68 | ldap.ScopeWholeSubtree, 69 | ldap.DerefAlways, 70 | 0, 71 | 0, 72 | false, 73 | fmt.Sprintf("(%v=%v)", viper.GetString("settings.ldap.userField"), username), 74 | ldapFieldsFilter, 75 | nil) 76 | 77 | if cur, err = conn.Search(sql); err != nil { 78 | err = errors.New(fmt.Sprintf("在Ldap搜索用户失败, %v", err)) 79 | logger.Error(err) 80 | return 81 | } 82 | 83 | if len(cur.Entries) == 0 { 84 | err = errors.New("未查询到对应的用户信息。") 85 | logger.Error(err) 86 | return 87 | } 88 | 89 | userInfo = cur.Entries[0] 90 | 91 | return 92 | } 93 | -------------------------------------------------------------------------------- /apis/article/article.go: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "vAdmin/tools/app/msg" 10 | "strconv" 11 | ) 12 | 13 | func GetArticleList(c *gin.Context) { 14 | var data models.Article 15 | var err error 16 | var pageSize = 10 17 | var pageIndex = 1 18 | 19 | if size := c.Request.FormValue("pageSize"); size != "" { 20 | pageSize = tools.StrToInt(err, size) 21 | } 22 | if index := c.Request.FormValue("pageIndex"); index != "" { 23 | pageIndex = tools.StrToInt(err, index) 24 | } 25 | 26 | s, err := strconv.Atoi(c.Request.FormValue("articleId")) 27 | data.ArticleId = s 28 | //data.ArticleId = tools.StrToInt(err, c.Request.FormValue("articleId")) 29 | data.Title = c.Request.FormValue("title") 30 | data.Author = c.Request.FormValue("author") 31 | 32 | 33 | data.DataScope = tools.GetUserIdStr(c) 34 | result, count, err := data.GetPage(pageSize, pageIndex) 35 | tools.HasError(err, "", -1) 36 | 37 | app.PageOK(c, result, count, pageIndex, pageSize, "") 38 | } 39 | 40 | func GetArticle(c *gin.Context) { 41 | var data models.Article 42 | data.ArticleId, _ = tools.StringToInt(c.Param("articleId")) 43 | result, err := data.Get() 44 | tools.HasError(err, "抱歉未找到相关信息", -1) 45 | 46 | app.OK(c, result, "") 47 | } 48 | 49 | func InsertArticle(c *gin.Context) { 50 | var data models.Article 51 | err := c.ShouldBindJSON(&data) 52 | data.CreateBy = tools.GetUserIdStr(c) 53 | tools.HasError(err, "", 500) 54 | result, err := data.Create() 55 | tools.HasError(err, "", -1) 56 | app.OK(c, result, "") 57 | } 58 | 59 | func UpdateArticle(c *gin.Context) { 60 | var data models.Article 61 | err := c.BindWith(&data, binding.JSON) 62 | tools.HasError(err, "数据解析失败", -1) 63 | data.UpdateBy = tools.GetUserIdStr(c) 64 | result, err := data.Update(data.ArticleId) 65 | tools.HasError(err, "", -1) 66 | 67 | app.OK(c, result, "") 68 | } 69 | 70 | func DeleteArticle(c *gin.Context) { 71 | var data models.Article 72 | data.UpdateBy = tools.GetUserIdStr(c) 73 | 74 | IDS := tools.IdsStrToIdsIntGroup("articleId", c) 75 | _, err := data.BatchDelete(IDS) 76 | tools.HasError(err, msg.DeletedFail, 500) 77 | app.OK(c, nil, msg.DeletedSuccess) 78 | } -------------------------------------------------------------------------------- /models/tools/dbcolumns.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | orm "vAdmin/database" 6 | config2 "vAdmin/tools/config" 7 | ) 8 | 9 | type DBColumns struct { 10 | TableSchema string `gorm:"column:TABLE_SCHEMA" json:"tableSchema"` 11 | TableName string `gorm:"column:TABLE_NAME" json:"tableName"` 12 | ColumnName string `gorm:"column:COLUMN_NAME" json:"columnName"` 13 | ColumnDefault string `gorm:"column:COLUMN_DEFAULT" json:"columnDefault"` 14 | IsNullable string `gorm:"column:IS_NULLABLE" json:"isNullable"` 15 | DataType string `gorm:"column:DATA_TYPE" json:"dataType"` 16 | CharacterMaximumLength string `gorm:"column:CHARACTER_MAXIMUM_LENGTH" json:"characterMaximumLength"` 17 | CharacterSetName string `gorm:"column:CHARACTER_SET_NAME" json:"characterSetName"` 18 | ColumnType string `gorm:"column:COLUMN_TYPE" json:"columnType"` 19 | ColumnKey string `gorm:"column:COLUMN_KEY" json:"columnKey"` 20 | Extra string `gorm:"column:EXTRA" json:"extra"` 21 | ColumnComment string `gorm:"column:COLUMN_COMMENT" json:"columnComment"` 22 | } 23 | 24 | func (e *DBColumns) GetPage(pageSize int, pageIndex int) ([]DBColumns, int, error) { 25 | var doc []DBColumns 26 | 27 | table := orm.Eloquent.Select("*").Table("information_schema.`COLUMNS`") 28 | table = table.Where("table_schema= ? ", config2.MysqlConfig.Name) 29 | 30 | if e.TableName != "" { 31 | return nil, 0, errors.New("table name cannot be empty!") 32 | } 33 | 34 | table = table.Where("TABLE_NAME = ?", e.TableName) 35 | 36 | var count int 37 | 38 | if err := table.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&doc).Error; err != nil { 39 | return nil, 0, err 40 | } 41 | table.Count(&count) 42 | return doc, count, nil 43 | } 44 | 45 | func (e *DBColumns) GetList() ([]DBColumns, error) { 46 | var doc []DBColumns 47 | 48 | table := orm.Eloquent.Select("*").Table("information_schema.columns") 49 | table = table.Where("table_schema= ? ", config2.MysqlConfig.Name) 50 | 51 | if e.TableName == "" { 52 | return nil, errors.New("table name cannot be empty!") 53 | } 54 | 55 | table = table.Where("TABLE_NAME = ?", e.TableName) 56 | 57 | if err := table.Find(&doc).Error; err != nil { 58 | return doc, err 59 | } 60 | return doc, nil 61 | } 62 | -------------------------------------------------------------------------------- /apis/common/hook.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package common 6 | 7 | import ( 8 | "fmt" 9 | "io/ioutil" 10 | "vAdmin/module/deploy" 11 | "vAdmin/tools/command" 12 | "vAdmin/tools/gofile" 13 | "vAdmin/tools/gostring" 14 | ) 15 | 16 | func HookDeploy(applyId int) { 17 | apply := &deploy.Apply{ 18 | ID: applyId, 19 | } 20 | if err := apply.Detail(); err != nil { 21 | return 22 | } 23 | if apply.CommitVersion == "" { 24 | return 25 | } 26 | 27 | tmpFile := gostring.JoinStrings("/tmp", "/", gostring.StrRandom(24), ".log") 28 | if err := gofile.CreateFile(tmpFile, []byte(apply.CommitVersion), 0744); err != nil { 29 | fmt.Println("apply.CommitVersion tmp file create failed, err[%s], apply_id[%d]", err.Error(), applyId) 30 | return 31 | } 32 | if err := gofile.ReadlineAddHead(tmpFile,"ls"); err != nil { 33 | fmt.Println("read file change failed, err[%s], apply_id[%d]", err.Error(), applyId) 34 | return 35 | } 36 | 37 | var deployCmd string 38 | data, err := ioutil.ReadFile(tmpFile) 39 | if err != nil { 40 | fmt.Println(err) 41 | } else { 42 | //fmt.Println(string(data)) 43 | deployCmd = string(data) 44 | } 45 | 46 | var deployStatus int 47 | if apply.Status == deploy.APPLY_STATUS_SUCCESS { 48 | deployStatus = 1 49 | } 50 | 51 | s := gostring.JoinStrings( 52 | "#!/bin/bash\n\n", 53 | "#--------- deploy hook scripts env ---------\n", 54 | fmt.Sprintf("env_apply_id=%d\n", apply.ID), 55 | fmt.Sprintf("env_apply_name=%s\n", apply.DemandId), 56 | fmt.Sprintf("env_deploy_status=%d\n", deployStatus), 57 | deployCmd, 58 | ) 59 | hookCommandTaskRun(s, applyId) 60 | } 61 | 62 | func hookCommandTaskRun(s string, applyId int) { 63 | scriptFile := gostring.JoinStrings("/tmp", "/", gostring.StrRandom(24), ".sh") 64 | if err := gofile.CreateFile(scriptFile, []byte(s), 0744); err != nil { 65 | fmt.Println("hook script file create failed, err[%s], apply_id[%d]", err.Error(), applyId) 66 | return 67 | } 68 | cmds := []string{ 69 | fmt.Sprintf("/bin/bash -c %s", scriptFile), 70 | //fmt.Sprintf("rm -f %s", scriptFile), 71 | } 72 | task := command.NewTask(cmds, 86400) 73 | task.Run() 74 | if err := task.GetError(); err != nil { 75 | fmt.Println("hook script run failed, err[%s], output[%s], apply_id[%d]", err.Error(), gostring.JsonEncode(task.Result()), applyId) 76 | } 77 | } -------------------------------------------------------------------------------- /database/mysql.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "bytes" 5 | _ "github.com/go-sql-driver/mysql" //加载mysql 6 | "github.com/jinzhu/gorm" 7 | "vAdmin/tools/config" 8 | "log" 9 | "strconv" 10 | ) 11 | 12 | var Eloquent *gorm.DB 13 | 14 | var ( 15 | DbType string 16 | Host string 17 | Port int 18 | Name string 19 | Username string 20 | Password string 21 | MaxIdleConns int 22 | MaxOpenConns int 23 | 24 | ) 25 | 26 | func SetupMysql() { 27 | 28 | DbType = config.MysqlConfig.Dbtype 29 | Host = config.MysqlConfig.Host 30 | Port = config.MysqlConfig.Port 31 | Name = config.MysqlConfig.Name 32 | Username = config.MysqlConfig.Username 33 | Password = config.MysqlConfig.Password 34 | MaxIdleConns = config.MysqlConfig.MaxIdleConns 35 | MaxOpenConns = config.MysqlConfig.MaxOpenConns 36 | 37 | if DbType != "mysql" { 38 | log.Println("db type unknow") 39 | } 40 | var err error 41 | 42 | conn := GetMysqlConnect() 43 | 44 | log.Println(conn) 45 | 46 | var db Database 47 | if DbType == "mysql" { 48 | db = new(Mysql) 49 | Eloquent, err = db.Open(DbType, conn) 50 | 51 | } else { 52 | panic("db type unknow") 53 | } 54 | if err != nil { 55 | log.Fatalf("%s connect error %v", DbType, err) 56 | } else { 57 | log.Printf("%s connect success!", DbType) 58 | } 59 | 60 | 61 | if Eloquent.Error != nil { 62 | log.Fatalf("database error %v", Eloquent.Error) 63 | } 64 | 65 | Eloquent.LogMode(true) 66 | } 67 | 68 | func GetMysqlConnect() string { 69 | var conn bytes.Buffer 70 | conn.WriteString(Username) 71 | conn.WriteString(":") 72 | conn.WriteString(Password) 73 | conn.WriteString("@tcp(") 74 | conn.WriteString(Host) 75 | conn.WriteString(":") 76 | conn.WriteString(strconv.Itoa(Port)) 77 | conn.WriteString(")") 78 | conn.WriteString("/") 79 | conn.WriteString(Name) 80 | conn.WriteString("?charset=utf8&parseTime=True&loc=Local&timeout=1000ms") 81 | return conn.String() 82 | } 83 | 84 | type Database interface { 85 | Open(dbType string, conn string) (db *gorm.DB, err error) 86 | } 87 | 88 | type Mysql struct { 89 | } 90 | 91 | func (*Mysql) Open(dbType string, conn string) (db *gorm.DB, err error) { 92 | eloquent, err := gorm.Open(dbType, conn) 93 | return eloquent, err 94 | } 95 | 96 | type SqlLite struct { 97 | } 98 | 99 | func (*SqlLite) Open(dbType string, conn string) (db *gorm.DB, err error) { 100 | eloquent, err := gorm.Open(dbType, conn) 101 | return eloquent, err 102 | } 103 | -------------------------------------------------------------------------------- /tools/command/command.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package command 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "os/exec" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | const ( 17 | DEFAULT_RUM_TIMEOUT = 3600 18 | ) 19 | 20 | type Command struct { 21 | Cmd string 22 | Timeout time.Duration 23 | TerminateChan chan int 24 | Setpgid bool 25 | command *exec.Cmd 26 | stdout bytes.Buffer 27 | stderr bytes.Buffer 28 | } 29 | 30 | func NewCmd(c *Command) (*Command, error) { 31 | if c.Timeout == 0 * time.Second { 32 | c.Timeout = DEFAULT_RUM_TIMEOUT * time.Second 33 | } 34 | if c.TerminateChan == nil { 35 | c.TerminateChan = make(chan int) 36 | } 37 | cmd := exec.Command("/bin/bash", "-c", c.Cmd) 38 | /* 39 | if c.Setpgid { 40 | cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 41 | } 42 | */ 43 | cmd.Stderr = &c.stderr 44 | cmd.Stdout = &c.stdout 45 | c.command = cmd 46 | 47 | return c, nil 48 | } 49 | 50 | func (c *Command) Run() error { 51 | if err := c.command.Start(); err != nil { 52 | return err 53 | } 54 | 55 | errChan := make(chan error) 56 | go func(){ 57 | errChan <- c.command.Wait() 58 | defer close(errChan) 59 | }() 60 | 61 | var err error 62 | select { 63 | case err = <-errChan: 64 | case <-time.After(c.Timeout): 65 | err = c.terminate() 66 | if err == nil { 67 | err = errors.New(fmt.Sprintf("cmd run timeout, cmd [%s], time[%v]", c.Cmd, c.Timeout)) 68 | } 69 | case <-c.TerminateChan: 70 | err = c.terminate() 71 | if err == nil { 72 | err = errors.New(fmt.Sprintf("cmd is terminated, cmd [%s]", c.Cmd)) 73 | } 74 | } 75 | return err 76 | } 77 | 78 | func (c *Command) Stderr() string { 79 | return strings.TrimSpace(string(c.stderr.Bytes())) 80 | } 81 | 82 | func (c *Command) Stdout() string { 83 | return strings.TrimSpace(string(c.stdout.Bytes())) 84 | } 85 | 86 | func (c *Command) terminate() error { 87 | /* 88 | if c.Setpgid { 89 | return syscall.Kill(-c.command.Process.Pid, syscall.SIGKILL) 90 | } else { 91 | return syscall.Kill(c.command.Process.Pid, syscall.SIGKILL) 92 | } 93 | */ 94 | return nil 95 | } -------------------------------------------------------------------------------- /tools/utils.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "golang.org/x/crypto/bcrypt" 5 | "log" 6 | "strconv" 7 | ) 8 | 9 | func StrToInt(err error, index string) int { 10 | result, err := strconv.Atoi(index) 11 | if err != nil { 12 | HasError(err, "string to int error"+err.Error(), -1) 13 | } 14 | return result 15 | } 16 | 17 | /* 18 | bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) 19 | Must be the already hashed PW ^ ^ Plain Text Password to compare 20 | */ 21 | func CompareHashAndPassword(e string, p string) (bool, error) { 22 | 23 | byteHash := []byte(e) // e: hashedPassword 24 | plainPwd := []byte(p) 25 | log.Println("vvvvvvvvvvvvvvvvvvvvvvv--->hashedPassword", e) 26 | log.Println("vvvvvvvvvvvvvvvvvvvvvvv--->PlainPassword", p) 27 | err := bcrypt.CompareHashAndPassword(byteHash, plainPwd) 28 | if err != nil { 29 | log.Println(err.Error()) 30 | return false, err 31 | } 32 | return true, nil 33 | } 34 | 35 | func CompareHashAndPassword2(e string, p string) (bool, error) { 36 | 37 | hash, err := bcrypt.GenerateFromPassword([]byte(e), bcrypt.DefaultCost) //加密处理 38 | if err != nil { 39 | log.Println(err) 40 | } 41 | encodePWD := string(hash) // 保存在数据库的密码,虽然每次生成都不同,只需保存一份即可 42 | 43 | byteHash := []byte(encodePWD) // e: hashedPassword 44 | plainPwd := []byte(p) 45 | log.Println("vvvvvvvvvvvvvvvvvvvvvvv--->hashedPassword", encodePWD) 46 | log.Println("vvvvvvvvvvvvvvvvvvvvvvv--->PlainPassword", p) 47 | err = bcrypt.CompareHashAndPassword(byteHash, plainPwd) 48 | if err != nil { 49 | log.Println(err.Error()) 50 | return false, err 51 | } 52 | return true, nil 53 | } 54 | 55 | /* 56 | func CompareHashAndPassword(e string, p []byte) (bool, error) { 57 | 58 | byteHash := []byte(e) 59 | err := bcrypt.CompareHashAndPassword(byteHash, p) 60 | if err != nil { 61 | log.Print(err.Error()) 62 | return false, err 63 | } 64 | return true, nil 65 | } 66 | */ 67 | 68 | // Assert 条件断言 69 | // 当断言条件为 假 时触发 panic 70 | // 对于当前请求不会再执行接下来的代码,并且返回指定格式的错误信息和错误码 71 | func Assert(condition bool, msg string, code ...int) { 72 | if !condition { 73 | statusCode := 200 74 | if len(code) > 0 { 75 | statusCode = code[0] 76 | } 77 | panic("CustomError#" + strconv.Itoa(statusCode) + "#" + msg) 78 | } 79 | } 80 | 81 | // HasError 错误断言 82 | // 当 error 不为 nil 时触发 panic 83 | // 对于当前请求不会再执行接下来的代码,并且返回指定格式的错误信息和错误码 84 | // 若 msg 为空,则默认为 error 中的内容 85 | func HasError(err error, msg string, code ...int) { 86 | if err != nil { 87 | statusCode := 200 88 | if len(code) > 0 { 89 | statusCode = code[0] 90 | } 91 | if msg == "" { 92 | msg = err.Error() 93 | } 94 | log.Println(err) 95 | panic("CustomError#" + strconv.Itoa(statusCode) + "#" + msg) 96 | } 97 | } -------------------------------------------------------------------------------- /apis/system/config.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "vAdmin/tools/app/msg" 10 | "net/http" 11 | ) 12 | 13 | func GetConfigList(c *gin.Context) { 14 | var data models.SysConfig 15 | var err error 16 | var pageSize = 10 17 | var pageIndex = 1 18 | 19 | if size := c.Request.FormValue("pageSize"); size != "" { 20 | pageSize = tools.StrToInt(err, size) 21 | } 22 | 23 | if index := c.Request.FormValue("pageIndex"); index != "" { 24 | pageIndex = tools.StrToInt(err, index) 25 | } 26 | 27 | data.ConfigKey = c.Request.FormValue("configKey") 28 | data.ConfigName = c.Request.FormValue("configName") 29 | data.ConfigType = c.Request.FormValue("configType") 30 | data.DataScope = tools.GetUserIdStr(c) 31 | result, count, err := data.GetPage(pageSize, pageIndex) 32 | tools.HasError(err, "", -1) 33 | 34 | var mp = make(map[string]interface{}, 3) 35 | mp["list"] = result 36 | mp["count"] = count 37 | mp["pageIndex"] = pageIndex 38 | mp["pageSize"] = pageSize 39 | 40 | var res app.Response 41 | res.Data = mp 42 | 43 | c.JSON(http.StatusOK, res.ReturnOK()) 44 | } 45 | 46 | func GetConfig(c *gin.Context) { 47 | var Config models.SysConfig 48 | Config.ConfigId, _ = tools.StringToInt(c.Param("configId")) 49 | result, err := Config.Get() 50 | tools.HasError(err, "抱歉未找到相关信息", -1) 51 | 52 | var res app.Response 53 | res.Data = result 54 | 55 | c.JSON(http.StatusOK, res.ReturnOK()) 56 | } 57 | 58 | 59 | func GetConfigByConfigKey(c *gin.Context) { 60 | var Config models.SysConfig 61 | Config.ConfigKey = c.Param("configKey") 62 | result, err := Config.Get() 63 | tools.HasError(err, "抱歉未找到相关信息", -1) 64 | 65 | app.OK(c, result,result.ConfigValue) 66 | } 67 | 68 | func InsertConfig(c *gin.Context) { 69 | var data models.SysConfig 70 | err := c.BindWith(&data, binding.JSON) 71 | data.CreateBy = tools.GetUserIdStr(c) 72 | tools.HasError(err, "", 500) 73 | result, err := data.Create() 74 | tools.HasError(err, "", -1) 75 | 76 | app.OK(c, result,"") 77 | } 78 | 79 | func UpdateConfig(c *gin.Context) { 80 | var data models.SysConfig 81 | err := c.BindWith(&data, binding.JSON) 82 | tools.HasError(err, "数据解析失败", -1) 83 | data.UpdateBy = tools.GetUserIdStr(c) 84 | result, err := data.Update(data.ConfigId) 85 | tools.HasError(err, "", -1) 86 | app.OK(c, result,"") 87 | } 88 | 89 | func DeleteConfig(c *gin.Context) { 90 | var data models.SysConfig 91 | data.UpdateBy = tools.GetUserIdStr(c) 92 | IDS := tools.IdsStrToIdsIntGroup("configId", c) 93 | result, err := data.BatchDelete(IDS) 94 | tools.HasError(err, "修改失败", 500) 95 | app.OK(c, result, msg.DeletedSuccess) 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /apis/deploy/deploytask.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "sync" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | type deployTask struct { 10 | deploys map[int][]*Deploy 11 | mu sync.Mutex 12 | } 13 | 14 | type CallbackFn func(int, int, int, []*ServerResult) 15 | 16 | type TaskCallbackFn func(int, int) 17 | 18 | var task = &deployTask{ 19 | deploys: map[int][]*Deploy{}, 20 | } 21 | 22 | func NewTask(id int, mode string, deploys []*Deploy, startFn, finishFn CallbackFn, taskFn TaskCallbackFn) error { 23 | if exists := task.exists(id); exists { 24 | return errors.New(fmt.Sprintf("deploy task [id: %d] have exists", id)) 25 | } 26 | task.append(id, deploys) 27 | go func() { 28 | taskStatus := STATUS_DONE 29 | for _, deploy := range deploys { 30 | if startFn != nil { 31 | rest, status := deploy.Result() 32 | startFn(id, deploy.ID, status, rest) 33 | } 34 | switch mode { 35 | case DEPLOY_PARALLEL: 36 | deploy.Parallel() 37 | default: 38 | deploy.Serial() 39 | } 40 | resultList, status := deploy.Result() 41 | if finishFn != nil { 42 | finishFn(id, deploy.ID, status, resultList) 43 | } 44 | if status == STATUS_FAILED { 45 | taskStatus = STATUS_FAILED 46 | } 47 | } 48 | task.remove(id) 49 | if taskFn != nil { 50 | taskFn(id, taskStatus) 51 | } 52 | }() 53 | return nil 54 | } 55 | 56 | func StopTask(id int) { 57 | task.stop(id) 58 | } 59 | 60 | func ExistsTask(id int) bool { 61 | return task.exists(id) 62 | } 63 | 64 | func StatusTask(id int) []*DeployResult { 65 | deploys, exists := task.get(id) 66 | if !exists { 67 | return nil 68 | } 69 | rests := []*DeployResult{} 70 | for _, deploy := range deploys { 71 | rest, s := deploy.Result() 72 | rests = append(rests, &DeployResult{ 73 | ID: deploy.ID, 74 | Status: s, 75 | ServerRest: rest, 76 | }) 77 | } 78 | return rests 79 | } 80 | 81 | func (t *deployTask) exists(id int) bool { 82 | t.mu.Lock() 83 | defer t.mu.Unlock() 84 | _, exists := t.deploys[id] 85 | return exists 86 | } 87 | 88 | func (t *deployTask) append(id int, deploys []*Deploy) { 89 | t.mu.Lock() 90 | defer t.mu.Unlock() 91 | t.deploys[id] = deploys 92 | } 93 | 94 | func (t *deployTask) remove(id int) { 95 | t.mu.Lock() 96 | defer t.mu.Unlock() 97 | delete(t.deploys, id) 98 | } 99 | 100 | func (t *deployTask) get(id int) ([]*Deploy, bool) { 101 | t.mu.Lock() 102 | defer t.mu.Unlock() 103 | deploys, exists := t.deploys[id] 104 | return deploys, exists 105 | } 106 | 107 | func (t *deployTask) stop(id int) { 108 | t.mu.Lock() 109 | defer t.mu.Unlock() 110 | deploys, exists := t.deploys[id] 111 | if exists { 112 | for _, deploy := range deploys { 113 | deploy.Terminate() 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /tools/ws/webSocket.go: -------------------------------------------------------------------------------- 1 | package ws 2 | import ( 3 | "errors" 4 | "fmt" 5 | "github.com/gorilla/websocket" 6 | "vAdmin/pkg/logger" 7 | "sync" 8 | ) 9 | 10 | var once sync.Once 11 | 12 | type Connection struct { 13 | WsConn *websocket.Conn 14 | InChan chan []byte 15 | OutChan chan []byte 16 | CloseReadLoopChan chan struct{} 17 | CloseWriteLoopChan chan struct{} 18 | } 19 | 20 | func InitConnection(wsConn *websocket.Conn) *Connection { 21 | conn := &Connection{ 22 | WsConn: wsConn, 23 | InChan: make(chan []byte, 1000), 24 | OutChan: make(chan []byte, 1000), 25 | CloseReadLoopChan: make(chan struct{}, 1), 26 | CloseWriteLoopChan: make(chan struct{}, 1), 27 | } 28 | 29 | go conn.readLoop() 30 | go conn.writeLoop() 31 | 32 | return conn 33 | } 34 | 35 | // API 36 | func (c *Connection) ReadMessage() (data []byte, err error) { 37 | select { 38 | case data = <-c.InChan: 39 | case <-c.CloseReadLoopChan: 40 | c.Close() 41 | c.StopWriteLoop() 42 | err = errors.New("connection is closed, stop read msg") 43 | } 44 | return 45 | } 46 | 47 | func (c *Connection) WriteMessage(data []byte) (err error) { 48 | select { 49 | case c.OutChan <- data: 50 | case <-c.CloseWriteLoopChan: 51 | c.Close() 52 | c.StopReadLoop() 53 | err = errors.New("connection is closed, stop write msg") 54 | } 55 | return 56 | } 57 | 58 | func (c *Connection) Close() { 59 | // websocket 的 Close() 方法是线程安全的,是可重入的。是一个特例,websocket其他的方法不是线程安全的。 60 | once.Do(func() { 61 | c.WsConn.Close() 62 | logger.Error("websocket connect closed") 63 | }) 64 | } 65 | 66 | func (c *Connection) StopReadLoop() { 67 | c.CloseReadLoopChan <- struct{}{} 68 | } 69 | 70 | func (c *Connection) StopWriteLoop() { 71 | c.CloseWriteLoopChan <- struct{}{} 72 | } 73 | 74 | // 内部实现 75 | func (c *Connection) readLoop() { 76 | for { 77 | _, data, err := c.WsConn.ReadMessage() 78 | if err != nil { 79 | c.Close() 80 | logger.Error(fmt.Sprintf("read message from websocket error, err: %s", err)) 81 | c.StopWriteLoop() 82 | return 83 | } 84 | select { 85 | case c.InChan <- data: 86 | case <-c.CloseReadLoopChan: 87 | logger.Info("end websocket read loop") 88 | return 89 | } 90 | } 91 | } 92 | 93 | func (c *Connection) writeLoop() { 94 | for { 95 | select { 96 | case data := <-c.OutChan: 97 | err := c.WsConn.WriteMessage(websocket.TextMessage, data) 98 | if err != nil { 99 | logger.Error(fmt.Sprintf("write message to websocket error, err: %s", err)) 100 | c.Close() 101 | c.StopReadLoop() 102 | return 103 | } 104 | case <-c.CloseWriteLoopChan: 105 | logger.Info("end websocket write loop") 106 | return 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /middleware/logger.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/sirupsen/logrus" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | //实例化 13 | var logger = logrus.New() 14 | 15 | // 日志记录到文件 16 | func LoggerToFile() gin.HandlerFunc { 17 | 18 | return func(c *gin.Context) { 19 | // 开始时间 20 | startTime := time.Now() 21 | 22 | // 处理请求 23 | c.Next() 24 | 25 | // 结束时间 26 | endTime := time.Now() 27 | 28 | // 执行时间 29 | latencyTime := endTime.Sub(startTime) 30 | 31 | // 请求方式 32 | reqMethod := c.Request.Method 33 | 34 | // 请求路由 35 | reqUri := c.Request.RequestURI 36 | 37 | // 状态码 38 | statusCode := c.Writer.Status() 39 | 40 | // 请求IP 41 | clientIP := c.ClientIP() 42 | 43 | // 日志格式 44 | logger.Infof("%s [%s] %3d %13v %15s %s %s \r\n", 45 | startTime.Format("2006-01-02 15:04:05.9999"), 46 | strings.ToUpper(logger.Level.String()), 47 | statusCode, 48 | latencyTime, 49 | clientIP, 50 | reqMethod, 51 | reqUri, 52 | ) 53 | 54 | if c.Request.Method != "GET" && c.Request.Method != "OPTIONS" { 55 | menu := models.Menu{} 56 | menu.Path = reqUri 57 | menu.Action = reqMethod 58 | menuList, _ := menu.Get() 59 | sysOperLog := models.SysOperLog{} 60 | sysOperLog.OperIp = clientIP 61 | sysOperLog.OperLocation = tools.GetLocation(clientIP) 62 | sysOperLog.Status = tools.IntToString(statusCode) 63 | sysOperLog.OperName = tools.GetUserName(c) 64 | sysOperLog.RequestMethod = c.Request.Method 65 | sysOperLog.OperUrl = reqUri 66 | if reqUri == "/login" { 67 | sysOperLog.BusinessType = "10" 68 | sysOperLog.Title = "用户登录" 69 | sysOperLog.OperName = "-" 70 | } else if strings.Contains(reqUri, "/api/v1/logout") { 71 | sysOperLog.BusinessType = "11" 72 | } else if strings.Contains(reqUri, "/api/v1/getCaptcha") { 73 | sysOperLog.BusinessType = "12" 74 | sysOperLog.Title = "验证码" 75 | } else { 76 | if reqMethod == "POST" { 77 | sysOperLog.BusinessType = "1" 78 | } else if reqMethod == "PUT" { 79 | sysOperLog.BusinessType = "2" 80 | } else if reqMethod == "DELETE" { 81 | sysOperLog.BusinessType = "3" 82 | } 83 | } 84 | sysOperLog.Method = reqMethod 85 | if len(menuList) > 0 { 86 | sysOperLog.Title = menuList[0].Title 87 | } 88 | b, _ := c.Get("body") 89 | sysOperLog.OperParam, _ = tools.StructToJsonStr(b) 90 | sysOperLog.CreateBy = tools.GetUserName(c) 91 | sysOperLog.OperTime = tools.GetCurrntTime() 92 | sysOperLog.LatencyTime = (latencyTime).String() 93 | sysOperLog.UserAgent = c.Request.UserAgent() 94 | if c.Err() == nil { 95 | sysOperLog.Status = "0" 96 | } else { 97 | sysOperLog.Status = "1" 98 | } 99 | _, _ = sysOperLog.Create() 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /pkg/pagination/pagination.go: -------------------------------------------------------------------------------- 1 | package pagination 2 | 3 | /* 4 | @Author : Rongxin Linghu 5 | */ 6 | 7 | import ( 8 | "vAdmin/pkg/logger" 9 | "fmt" 10 | "math" 11 | 12 | "github.com/gin-gonic/gin" 13 | "github.com/jinzhu/gorm" 14 | ) 15 | 16 | type Param struct { 17 | C *gin.Context 18 | DB *gorm.DB 19 | ShowSQL bool 20 | } 21 | 22 | type Paginator struct { 23 | TotalCount int `json:"total_count"` 24 | TotalPage int `json:"total_page"` 25 | Data interface{} `json:"data"` 26 | PerPage int `json:"per_page"` 27 | Page int `json:"page"` 28 | } 29 | 30 | type ListRequest struct { 31 | Page int `json:"page" form:"page"` 32 | PerPage int `json:"per_page" form:"per_page"` 33 | Sort int `json:"sort" form:"sort"` 34 | } 35 | 36 | // Paging 分页 37 | func Paging(p *Param, result interface{}, args ...interface{}) (*Paginator, error) { 38 | var ( 39 | param ListRequest 40 | paginator Paginator 41 | count int 42 | offset int 43 | tableName string 44 | ) 45 | 46 | if err := p.C.Bind(¶m); err != nil { 47 | logger.Errorf("参数绑定失败,错误:%v", err) 48 | return nil, err 49 | } 50 | 51 | db := p.DB 52 | 53 | if p.ShowSQL { 54 | db = db.Debug() 55 | } 56 | 57 | if param.Page < 1 { 58 | param.Page = 1 59 | } 60 | 61 | if param.PerPage == 0 { 62 | param.PerPage = 10 63 | } 64 | 65 | if param.Sort == 0 || param.Sort == -1 { 66 | db = db.Order("id desc") 67 | } 68 | 69 | if len(args) > 1 { 70 | tableName = fmt.Sprintf("`%s`.", args[1].(string)) 71 | } 72 | 73 | if len(args) > 0 { 74 | for paramType, paramsValue := range args[0].(map[string]map[string]interface{}) { 75 | if paramType == "like" { 76 | for key, value := range paramsValue { 77 | db = db.Where(fmt.Sprintf("%v%v like ?", tableName, key), fmt.Sprintf("%%%v%%", value)) 78 | } 79 | } else if paramType == "equal" { 80 | for key, value := range paramsValue { 81 | db = db.Where(fmt.Sprintf("%v%v = ?", tableName, key), value) 82 | } 83 | } 84 | } 85 | } 86 | 87 | done := make(chan bool, 1) 88 | 89 | go countRecords(db, result, done, &count) 90 | 91 | if param.Page == 1 { 92 | offset = 0 93 | } else { 94 | offset = (param.Page - 1) * param.PerPage 95 | } 96 | err := db.Limit(param.PerPage).Offset(offset).Scan(result).Error 97 | if err != nil { 98 | logger.Errorf("数据查询失败,错误:%v", err) 99 | return nil, err 100 | } 101 | <-done 102 | 103 | paginator.TotalCount = count 104 | paginator.Data = result 105 | paginator.Page = param.Page 106 | paginator.PerPage = param.PerPage 107 | paginator.TotalPage = int(math.Ceil(float64(count) / float64(param.PerPage))) 108 | 109 | return &paginator, nil 110 | } 111 | 112 | func countRecords(db *gorm.DB, anyType interface{}, done chan bool, count *int) { 113 | db.Model(anyType).Count(count) 114 | done <- true 115 | } 116 | -------------------------------------------------------------------------------- /apis/system/dict/dictType.go: -------------------------------------------------------------------------------- 1 | package dict 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "net/http" 10 | ) 11 | 12 | func GetDictTypeList(c *gin.Context) { 13 | var data models.DictType 14 | var err error 15 | var pageSize = 10 16 | var pageIndex = 1 17 | 18 | if size := c.Request.FormValue("pageSize"); size != "" { 19 | pageSize = tools.StrToInt(err, size) 20 | } 21 | 22 | if index := c.Request.FormValue("pageIndex"); index != "" { 23 | pageIndex = tools.StrToInt(err, index) 24 | } 25 | 26 | data.DictName = c.Request.FormValue("dictName") 27 | id := c.Request.FormValue("dictId") 28 | data.DictId, _ = tools.StringToInt(id) 29 | data.DictType = c.Request.FormValue("dictType") 30 | data.DataScope = tools.GetUserIdStr(c) 31 | result, count, err := data.GetPage(pageSize, pageIndex) 32 | tools.HasError(err, "", -1) 33 | 34 | var mp = make(map[string]interface{}, 3) 35 | mp["list"] = result 36 | mp["count"] = count 37 | mp["pageIndex"] = pageIndex 38 | mp["pageSize"] = pageSize 39 | 40 | var res app.Response 41 | res.Data = mp 42 | c.JSON(http.StatusOK, res.ReturnOK()) 43 | } 44 | 45 | func GetDictType(c *gin.Context) { 46 | var DictType models.DictType 47 | DictType.DictName = c.Request.FormValue("dictName") 48 | DictType.DictId, _ = tools.StringToInt(c.Param("dictId")) 49 | result, err := DictType.Get() 50 | tools.HasError(err, "抱歉未找到相关信息", -1) 51 | var res app.Response 52 | res.Data = result 53 | c.JSON(http.StatusOK, res.ReturnOK()) 54 | } 55 | 56 | func GetDictTypeOptionSelect(c *gin.Context) { 57 | var DictType models.DictType 58 | DictType.DictName = c.Request.FormValue("dictName") 59 | DictType.DictId, _ = tools.StringToInt(c.Param("dictId")) 60 | result, err := DictType.GetList() 61 | tools.HasError(err, "抱歉未找到相关信息", -1) 62 | var res app.Response 63 | res.Data = result 64 | c.JSON(http.StatusOK, res.ReturnOK()) 65 | } 66 | 67 | func InsertDictType(c *gin.Context) { 68 | var data models.DictType 69 | err := c.BindWith(&data, binding.JSON) 70 | data.CreateBy = tools.GetUserIdStr(c) 71 | tools.HasError(err, "", 500) 72 | result, err := data.Create() 73 | tools.HasError(err, "", -1) 74 | var res app.Response 75 | res.Data = result 76 | c.JSON(http.StatusOK, res.ReturnOK()) 77 | } 78 | 79 | func UpdateDictType(c *gin.Context) { 80 | var data models.DictType 81 | err := c.BindWith(&data, binding.JSON) 82 | data.UpdateBy = tools.GetUserIdStr(c) 83 | tools.HasError(err, "", -1) 84 | result, err := data.Update(data.DictId) 85 | tools.HasError(err, "", -1) 86 | var res app.Response 87 | res.Data = result 88 | c.JSON(http.StatusOK, res.ReturnOK()) 89 | } 90 | 91 | func DeleteDictType(c *gin.Context) { 92 | var data models.DictType 93 | data.UpdateBy = tools.GetUserIdStr(c) 94 | IDS := tools.IdsStrToIdsIntGroup("dictId", c) 95 | result, err := data.BatchDelete(IDS) 96 | tools.HasError(err, "修改失败", 500) 97 | app.OK(c, result, "删除成功") 98 | } 99 | -------------------------------------------------------------------------------- /apis/system/menu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | ) 10 | 11 | func GetMenuList(c *gin.Context) { 12 | var Menu models.Menu 13 | Menu.MenuName = c.Request.FormValue("menuName") 14 | Menu.Visible = c.Request.FormValue("visible") 15 | Menu.Title = c.Request.FormValue("title") 16 | Menu.DataScope = tools.GetUserIdStr(c) 17 | result, err := Menu.SetMenu() 18 | tools.HasError(err, "抱歉未找到相关信息", -1) 19 | 20 | app.OK(c, result, "") 21 | } 22 | 23 | func GetMenu(c *gin.Context) { 24 | var data models.Menu 25 | id, err := tools.StringToInt(c.Param("id")) 26 | data.MenuId = id 27 | result, err := data.GetByMenuId() 28 | tools.HasError(err, "抱歉未找到相关信息", -1) 29 | app.OK(c, result, "") 30 | } 31 | 32 | func GetMenuTreeRoleselect(c *gin.Context) { 33 | var Menu models.Menu 34 | var SysRole models.SysRole 35 | id, err := tools.StringToInt(c.Param("roleId")) 36 | SysRole.RoleId = id 37 | result, err := Menu.SetMenuLable() 38 | tools.HasError(err, "抱歉未找到相关信息", -1) 39 | menuIds := make([]int, 0) 40 | if id != 0 { 41 | menuIds, err = SysRole.GetRoleMeunId() 42 | tools.HasError(err, "抱歉未找到相关信息", -1) 43 | } 44 | app.Custum(c, gin.H{ 45 | "code": 200, 46 | "menus": result, 47 | "checkedKeys": menuIds, 48 | }) 49 | } 50 | 51 | func GetMenuTreeelect(c *gin.Context) { 52 | var data models.Menu 53 | result, err := data.SetMenuLable() 54 | tools.HasError(err, "抱歉未找到相关信息", -1) 55 | app.OK(c, result, "") 56 | } 57 | 58 | func InsertMenu(c *gin.Context) { 59 | var data models.Menu 60 | err := c.BindWith(&data, binding.JSON) 61 | tools.HasError(err, "抱歉未找到相关信息", -1) 62 | data.CreateBy = tools.GetUserIdStr(c) 63 | result, err := data.Create() 64 | tools.HasError(err, "抱歉未找到相关信息", -1) 65 | app.OK(c, result, "") 66 | } 67 | 68 | func UpdateMenu(c *gin.Context) { 69 | var data models.Menu 70 | err2 := c.BindWith(&data, binding.JSON) 71 | data.UpdateBy = tools.GetUserIdStr(c) 72 | tools.HasError(err2, "修改失败", -1) 73 | _, err := data.Update(data.MenuId) 74 | tools.HasError(err, "", 501) 75 | app.OK(c, "", "修改成功") 76 | 77 | } 78 | 79 | 80 | func DeleteMenu(c *gin.Context) { 81 | var data models.Menu 82 | id, err := tools.StringToInt(c.Param("id")) 83 | data.UpdateBy = tools.GetUserIdStr(c) 84 | _, err = data.Delete(id) 85 | tools.HasError(err, "删除失败", 500) 86 | app.OK(c, "", "删除成功") 87 | } 88 | 89 | func GetMenuRole(c *gin.Context) { 90 | var Menu models.Menu 91 | result, err := Menu.SetMenuRole(tools.GetRoleName(c)) 92 | tools.HasError(err, "获取失败", 500) 93 | app.OK(c, result, "") 94 | } 95 | 96 | func GetMenuIDS(c *gin.Context) { 97 | var data models.RoleMenu 98 | data.RoleName = c.GetString("role") 99 | data.UpdateBy = tools.GetUserIdStr(c) 100 | result, err := data.GetIDS() 101 | tools.HasError(err, "获取失败", 500) 102 | app.OK(c, result, "") 103 | } 104 | -------------------------------------------------------------------------------- /apis/system/dict/dictData.go: -------------------------------------------------------------------------------- 1 | package dict 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "net/http" 10 | ) 11 | 12 | func GetDictDataList(c *gin.Context) { 13 | var data models.DictData 14 | var err error 15 | var pageSize = 10 16 | var pageIndex = 1 17 | 18 | if size := c.Request.FormValue("pageSize"); size != "" { 19 | pageSize = tools.StrToInt(err, size) 20 | } 21 | 22 | if index := c.Request.FormValue("pageIndex"); index != "" { 23 | pageIndex = tools.StrToInt(err, index) 24 | } 25 | 26 | data.DictLabel = c.Request.FormValue("dictLabel") 27 | data.Status = c.Request.FormValue("status") 28 | data.DictType = c.Request.FormValue("dictType") 29 | id := c.Request.FormValue("dictCode") 30 | data.DictCode, _ = tools.StringToInt(id) 31 | data.DataScope = tools.GetUserIdStr(c) 32 | result, count, err := data.GetPage(pageSize, pageIndex) 33 | tools.HasError(err, "", -1) 34 | 35 | var mp = make(map[string]interface{}, 3) 36 | mp["list"] = result 37 | mp["count"] = count 38 | mp["pageIndex"] = pageIndex 39 | mp["pageSize"] = pageSize 40 | 41 | var res app.Response 42 | res.Data = mp 43 | 44 | c.JSON(http.StatusOK, res.ReturnOK()) 45 | } 46 | 47 | func GetDictData(c *gin.Context) { 48 | var DictData models.DictData 49 | DictData.DictLabel = c.Request.FormValue("dictLabel") 50 | DictData.DictCode, _ = tools.StringToInt(c.Param("dictCode")) 51 | result, err := DictData.GetByCode() 52 | tools.HasError(err, "抱歉未找到相关信息", -1) 53 | var res app.Response 54 | res.Data = result 55 | c.JSON(http.StatusOK, res.ReturnOK()) 56 | } 57 | 58 | func GetDictDataByDictType(c *gin.Context) { 59 | var DictData models.DictData 60 | DictData.DictType = c.Param("dictType") 61 | result, err := DictData.Get() 62 | tools.HasError(err, "抱歉未找到相关信息", -1) 63 | 64 | var res app.Response 65 | res.Data = result 66 | c.JSON(http.StatusOK, res.ReturnOK()) 67 | } 68 | 69 | func InsertDictData(c *gin.Context) { 70 | var data models.DictData 71 | err := c.BindWith(&data, binding.JSON) 72 | data.CreateBy = tools.GetUserIdStr(c) 73 | tools.HasError(err, "", 500) 74 | result, err := data.Create() 75 | tools.HasError(err, "", -1) 76 | var res app.Response 77 | res.Data = result 78 | c.JSON(http.StatusOK, res.ReturnOK()) 79 | } 80 | 81 | func UpdateDictData(c *gin.Context) { 82 | var data models.DictData 83 | err := c.BindWith(&data, binding.JSON) 84 | data.UpdateBy = tools.GetUserIdStr(c) 85 | tools.HasError(err, "", -1) 86 | result, err := data.Update(data.DictCode) 87 | tools.HasError(err, "", -1) 88 | var res app.Response 89 | res.Data = result 90 | c.JSON(http.StatusOK, res.ReturnOK()) 91 | } 92 | 93 | func DeleteDictData(c *gin.Context) { 94 | var data models.DictData 95 | data.UpdateBy = tools.GetUserIdStr(c) 96 | IDS := tools.IdsStrToIdsIntGroup("dictCode", c) 97 | result, err := data.BatchDelete(IDS) 98 | tools.HasError(err, "修改失败", 500) 99 | app.OK(c,result,"删除成功") 100 | } 101 | -------------------------------------------------------------------------------- /apis/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | const ( 10 | SUCCESS_CODE = 20000 //成功的状态码 11 | FAIL_CODE = 30000 //失败的状态码 12 | MD5_PREFIX = "jkfldfsf" //MD5加密前缀字符串 13 | TOKEN_KEY = "X-Token" //页面token键名 14 | USER_ID_Key = "X-USERID" //页面用户ID键名 15 | USER_UUID_Key = "X-UUID" //页面UUID键名 16 | SUPER_ADMIN_ID uint64 = 956986 // 超级管理员账号ID 17 | ) 18 | 19 | type ResponseModel struct { 20 | Code int `json:"code"` 21 | Message string `json:"message"` 22 | Data interface{} `json:"data"` 23 | } 24 | 25 | type ResponseModelBase struct { 26 | Code int `json:"code"` 27 | Message string `json:"message"` 28 | } 29 | 30 | // 响应成功 31 | func ResSuccess(c *gin.Context, v interface{}) { 32 | ret := ResponseModel{Code: SUCCESS_CODE, Message: "ok", Data: v} 33 | ResJSON(c, http.StatusOK, &ret) 34 | } 35 | 36 | // 响应成功 37 | func ResSuccessMsg(c *gin.Context) { 38 | ret := ResponseModelBase{Code: SUCCESS_CODE, Message: "ok"} 39 | ResJSON(c, http.StatusOK, &ret) 40 | } 41 | 42 | // 响应失败 43 | func ResFail(c *gin.Context, msg string) { 44 | ret := ResponseModelBase{Code: FAIL_CODE, Message: msg} 45 | ResJSON(c, http.StatusOK, &ret) 46 | } 47 | 48 | // 响应失败 49 | func ResFailCode(c *gin.Context, msg string, code int) { 50 | ret := ResponseModelBase{Code: code, Message: msg} 51 | ResJSON(c, http.StatusOK, &ret) 52 | } 53 | 54 | // 响应JSON数据 55 | func ResJSON(c *gin.Context, status int, v interface{}) { 56 | c.JSON(status, v) 57 | c.Abort() 58 | } 59 | 60 | // 响应错误-服务端故障 61 | func ResErrSrv(c *gin.Context, err error) { 62 | ret := ResponseModelBase{Code: FAIL_CODE, Message: "服务端故障"} 63 | ResJSON(c, http.StatusOK, &ret) 64 | } 65 | 66 | // 响应错误-用户端故障 67 | func ResErrCli(c *gin.Context, err error) { 68 | ret := ResponseModelBase{Code: FAIL_CODE, Message: "err"} 69 | ResJSON(c, http.StatusOK, &ret) 70 | } 71 | 72 | type ResponsePageData struct { 73 | Total uint64 `json:"total"` 74 | Items interface{} `json:"items"` 75 | } 76 | 77 | type ResponsePage struct { 78 | Code int `json:"code"` 79 | Message string `json:"message"` 80 | Data ResponsePageData `json:"data"` 81 | } 82 | 83 | // 响应成功-分页数据 84 | func ResSuccessPage(c *gin.Context, total uint64, list interface{}) { 85 | ret := ResponsePage{Code: SUCCESS_CODE, Message: "ok", Data: ResponsePageData{Total: total, Items: list}} 86 | ResJSON(c, http.StatusOK, &ret) 87 | } 88 | 89 | // 获取页码 90 | func GetPageIndex(c *gin.Context) uint64 { 91 | return GetQueryToUint64(c, "page", 1) 92 | } 93 | 94 | // 获取每页记录数 95 | func GetPageLimit(c *gin.Context) uint64 { 96 | limit := GetQueryToUint64(c, "limit", 20) 97 | if limit > 500 { 98 | limit = 20 99 | } 100 | return limit 101 | } 102 | 103 | // 获取排序信息 104 | func GetPageSort(c *gin.Context) string { 105 | return GetQueryToStr(c, "sort") 106 | } 107 | 108 | // 获取搜索关键词信息 109 | func GetPageKey(c *gin.Context) string { 110 | return GetQueryToStr(c, "key") 111 | } 112 | -------------------------------------------------------------------------------- /test/model.go.template: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | "vAdmin/utils" 6 | "time" 7 | ) 8 | 9 | type {{.ClassName}} struct { 10 | 11 | {{ range .Columns -}} 12 | {{$x := .Pk}} 13 | // {{.ColumnComment}} 14 | {{if ($x)}}{{.GoField}} {{.GoType}} `json:"{{.JsonField}}" gorm:"column:{{.ColumnName}};primary_key"`{{else}}{{.GoField}} {{.GoType}} `json:"{{.JsonField}}" gorm:"column:{{.ColumnName}};"`{{end}} 15 | {{ end -}} 16 | } 17 | 18 | // 创建{{.ClassName}} 19 | func (e *{{.ClassName}}) Create() ({{.ClassName}}, error) { 20 | var doc {{.ClassName}} 21 | doc.IsDel = "0" 22 | e.CreateTime = time.Now().String() 23 | result := orm.Eloquent.Table("{{.TableName}}").Create(&e) 24 | if result.Error != nil { 25 | err := result.Error 26 | return doc, err 27 | } 28 | doc = *e 29 | return doc, nil 30 | } 31 | 32 | // 获取{{.ClassName}} 33 | func (e *{{.ClassName}}) Get() ({{.ClassName}}, error) { 34 | var doc {{.ClassName}} 35 | 36 | table := orm.Eloquent.Table("{{.TableName}}") 37 | {{ range .Columns -}} 38 | {{$z := .IsQuery}} 39 | {{- if ($z) -}}if e.{{.GoField}} != "" { 40 | table = table.Where("{{.ColumnName}} = ?", e.{{.GoField}}) 41 | } 42 | {{ end }} 43 | {{- end -}} 44 | 45 | if err := table.First(&doc).Error; err != nil { 46 | return doc, err 47 | } 48 | return doc, nil 49 | } 50 | 51 | // 获取{{.ClassName}}带分页 52 | func (e *{{.ClassName}}) GetPage(pageSize int, pageIndex int) ([]{{.ClassName}}, int32, error) { 53 | var doc []{{.ClassName}} 54 | 55 | table := orm.Eloquent.Select("*").Table("{{.TableName}}") 56 | {{ range .Columns -}} 57 | {{$z := .IsQuery}} 58 | {{- if ($z) -}}if e.{{.GoField}} != "" { 59 | table = table.Where("{{.ColumnName}} = ?", e.{{.GoField}}) 60 | } 61 | {{ end }} 62 | {{- end -}} 63 | 64 | // 数据权限控制 65 | dataPermission := new(DataPermission) 66 | dataPermission.UserId, _ = utils.StringToInt(e.DataScope) 67 | table,err := dataPermission.GetDataScope("{{.TableName}}", table) 68 | if err != nil { 69 | return nil, 0, err 70 | } 71 | var count int32 72 | table = table.Offset((pageIndex - 1) * pageSize).Limit(pageSize) 73 | if err := table.Find(&doc).Error; err != nil { 74 | return nil, 0, err 75 | } 76 | table.Count(&count) 77 | return doc, count, nil 78 | } 79 | 80 | // 更新{{.ClassName}} 81 | func (e *{{.ClassName}}) Update(id int) (update {{.ClassName}}, err error) { 82 | if err = orm.Eloquent.Table("{{.TableName}}").Where("{{.PkColumn}} = ?", id).First(&update).Error; err != nil { 83 | return 84 | } 85 | 86 | //参数1:是要修改的数据 87 | //参数2:是修改的数据 88 | if err = orm.Eloquent.Table("{{.TableName}}").Model(&update).Updates(&e).Error; err != nil { 89 | return 90 | } 91 | return 92 | } 93 | 94 | // 删除{{.ClassName}} 95 | func (e *{{.ClassName}}) Delete(id int) (success bool, err error) { 96 | 97 | if err = orm.Eloquent.Table("{{.TableName}}").Where("{{.PkColumn}} = ?", id).Delete(&{{.ClassName}}{}).Error; err != nil { 98 | success = false 99 | return 100 | } 101 | success = true 102 | return 103 | } 104 | 105 | -------------------------------------------------------------------------------- /cmd/api/server.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/gin-gonic/gin" 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | "vAdmin/apis/deploy" 10 | "vAdmin/database" 11 | "vAdmin/router" 12 | "vAdmin/tools" 13 | config2 "vAdmin/tools/config" 14 | "io/ioutil" 15 | "log" 16 | "net/http" 17 | "os" 18 | "os/signal" 19 | "time" 20 | ) 21 | 22 | 23 | 24 | var ( 25 | config string 26 | port string 27 | mode string 28 | StartCmd = &cobra.Command{ 29 | Use: "server", 30 | Short: "Start API server", 31 | Example: "main server config/settings.yml", 32 | PreRun: func(cmd *cobra.Command, args []string) { 33 | usage() 34 | setup() 35 | }, 36 | RunE: func(cmd *cobra.Command, args []string) error { 37 | return run() 38 | }, 39 | } 40 | ) 41 | 42 | func init() { 43 | StartCmd.PersistentFlags().StringVarP(&config, "config", "c", "config/settings.yml", "Start server with provided configuration file") 44 | StartCmd.PersistentFlags().StringVarP(&port, "port", "p", "8000", "Tcp port server listening on") 45 | StartCmd.PersistentFlags().StringVarP(&mode, "mode", "m", "dev", "server mode ; eg:dev,test,prod") 46 | } 47 | 48 | func usage() { 49 | usageStr := `starting api server` 50 | fmt.Printf("%s\n", usageStr) 51 | } 52 | 53 | func setup() { 54 | 55 | //1. 读取配置 56 | config2.ConfigSetup(config) 57 | //2. 设置日志 58 | tools.InitLogger() 59 | //3. 初始化数据库链接 60 | database.SetupMysql() 61 | //database.SetupDrill() 62 | 63 | 64 | } 65 | 66 | func run() error { 67 | if mode != "" { 68 | config2.SetConfig(config, "settings.application.mode", mode) 69 | } 70 | if viper.GetString("settings.application.mode") == string(tools.ModeProd) { 71 | gin.SetMode(gin.ReleaseMode) 72 | } 73 | 74 | r := router.InitRouter() 75 | 76 | defer database.Eloquent.Close() 77 | //defer database.Eloq.Close() 78 | 79 | if port != "" { 80 | config2.SetConfig(config, "settings.application.port", port) 81 | } 82 | 83 | 84 | srv := &http.Server{ 85 | Addr: config2.ApplicationConfig.Host + ":" + config2.ApplicationConfig.Port, 86 | Handler: r, 87 | } 88 | 89 | go func() { 90 | // 服务连接 91 | if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 92 | log.Fatalf("listen: %s\n", err) 93 | } 94 | }() 95 | content, _ := ioutil.ReadFile("./static/go.txt") 96 | log.Println(string(content)) 97 | log.Println("Server Run http://127.0.0.1:" + config2.ApplicationConfig.Port + "/") 98 | log.Println("Swagger URL http://127.0.0.1:" + config2.ApplicationConfig.Port + "/swagger/index.html") 99 | 100 | log.Println("Enter Control + C Shutdown Server") 101 | // 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间) 102 | 103 | deploy.InitWs() 104 | 105 | quit := make(chan os.Signal) 106 | signal.Notify(quit, os.Interrupt) 107 | <-quit 108 | log.Println("Shutdown Server ...") 109 | 110 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 111 | defer cancel() 112 | if err := srv.Shutdown(ctx); err != nil { 113 | log.Fatal("Server Shutdown:", err) 114 | } 115 | log.Println("Server exiting") 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /tools/gostring/string.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gostring 6 | 7 | import ( 8 | "strings" 9 | "strconv" 10 | "math/rand" 11 | "time" 12 | "crypto/md5" 13 | "encoding/hex" 14 | "encoding/base64" 15 | "encoding/json" 16 | ) 17 | 18 | func JoinStrings(multiString ...string) string { 19 | return strings.Join(multiString, "") 20 | } 21 | 22 | func JoinIntSlice2String(intSlice []int, sep string) string { 23 | return strings.Join(IntSlice2StrSlice(intSlice), sep) 24 | } 25 | 26 | func StrSplit2IntSlice(str, sep string) []int { 27 | return StrSlice2IntSlice(StrFilterSliceEmpty(strings.Split(str, sep))) 28 | } 29 | 30 | func Str2StrSlice(str, sep string) []string { 31 | return StrFilterSliceEmpty(strings.Split(str, sep)) 32 | } 33 | 34 | func StrSlice2IntSlice(strSlice []string) []int { 35 | var intSlice []int 36 | for _, s := range strSlice { 37 | i, _ := strconv.Atoi(s) 38 | intSlice = append(intSlice, i) 39 | } 40 | return intSlice 41 | } 42 | 43 | func StrFilterSliceEmpty(strSlice []string) []string { 44 | var filterSlice []string 45 | for _, s := range strSlice { 46 | ss := strings.TrimSpace(s) 47 | if ss != "" { 48 | filterSlice = append(filterSlice, ss) 49 | } 50 | } 51 | return filterSlice 52 | } 53 | 54 | func IntSlice2StrSlice(intSlice []int) []string { 55 | var strSlice []string 56 | for _, i := range intSlice { 57 | s := strconv.Itoa(i) 58 | strSlice = append(strSlice, s) 59 | } 60 | return strSlice 61 | } 62 | 63 | func Str2Int(s string) int { 64 | i, _ := strconv.Atoi(s) 65 | return i 66 | } 67 | 68 | func Int2Str(i int) string { 69 | return strconv.Itoa(i) 70 | } 71 | 72 | func StrRandom(l int) string { 73 | str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 74 | bytes := []byte(str) 75 | result := []byte{} 76 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 77 | for i := 0; i < l; i++ { 78 | result = append(result, bytes[r.Intn(len(bytes))]) 79 | } 80 | return string(result) 81 | } 82 | 83 | func StrMd5(s string) string { 84 | md5Ctx := md5.New() 85 | md5Ctx.Write([]byte(s)) 86 | return hex.EncodeToString(md5Ctx.Sum(nil)) 87 | } 88 | 89 | func Base64Encode(b []byte) string { 90 | return base64.StdEncoding.EncodeToString(b) 91 | } 92 | 93 | func Base64Decode(s string) ([]byte, error) { 94 | ds, err := base64.StdEncoding.DecodeString(s) 95 | return ds, err 96 | } 97 | 98 | func Base64UrlEncode(b []byte) string { 99 | return base64.URLEncoding.EncodeToString(b) 100 | } 101 | 102 | func Base64UrlDecode(s string) ([]byte, error) { 103 | ds, err := base64.URLEncoding.DecodeString(s) 104 | return ds, err 105 | } 106 | 107 | func JsonEncode(obj interface{}) []byte { 108 | b, _ := json.Marshal(obj) 109 | return b 110 | } 111 | 112 | func JsonDecode(data []byte, obj interface{}) { 113 | json.Unmarshal(data, obj) 114 | } 115 | -------------------------------------------------------------------------------- /apis/process/classify.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "errors" 5 | //orm "vAdmin/database" 6 | orm "vAdmin/database" 7 | process2 "vAdmin/models/process" 8 | "vAdmin/pkg/pagination" 9 | "vAdmin/tools" 10 | "vAdmin/tools/app" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | /* 16 | @Author : Rongxin Linghu 17 | */ 18 | 19 | // 创建流程分类 20 | func CreateClassify(c *gin.Context) { 21 | var ( 22 | err error 23 | classifyValue process2.Classify 24 | classifyCount int 25 | ) 26 | 27 | err = c.ShouldBind(&classifyValue) 28 | if err != nil { 29 | app.Error(c, -1, err, "") 30 | return 31 | } 32 | 33 | // 判断创建的分类是否存在 34 | err = orm.Eloquent.Table("p_process_classify"). 35 | Where("name = ?", classifyValue.Name). 36 | Where("`delete_time` IS NULL"). 37 | Count(&classifyCount).Error 38 | if err != nil { 39 | app.Error(c, -1, err, "") 40 | return 41 | } 42 | if classifyCount > 0 { 43 | app.Error(c, -1, errors.New("创建的分类名称已经存在"), "") 44 | return 45 | } 46 | 47 | classifyValue.Creator = tools.GetUserId(c) 48 | 49 | err = orm.Eloquent.Table("p_process_classify").Create(&classifyValue).Error 50 | if err != nil { 51 | app.Error(c, -1, err, "") 52 | return 53 | } 54 | 55 | app.OK(c, "", "创建流程分类成功") 56 | } 57 | 58 | // 流程分类列表 59 | func ClassifyList(c *gin.Context) { 60 | type classifyValue struct { 61 | process2.Classify 62 | CreateUser string `json:"create_user"` 63 | CreateName string `json:"create_name"` 64 | } 65 | 66 | var ( 67 | err error 68 | classifyList []*classifyValue 69 | ) 70 | 71 | /* 72 | SearchParams := map[string]map[string]interface{}{ 73 | "like": pagination.RequestParams(c), 74 | } 75 | */ 76 | SearchParams := map[string]map[string]interface{}{} 77 | 78 | db := orm.Eloquent.Model(&process2.Classify{}).Joins("left join sys_user on sys_user.user_id = p_process_classify.creator"). 79 | Select("p_process_classify.*, sys_user.username as create_user, sys_user.nick_name as create_name"). 80 | Where("p_process_classify.`delete_time` IS NULL") 81 | 82 | result, err := pagination.Paging(&pagination.Param{ 83 | C: c, 84 | DB: db, 85 | }, &classifyList, SearchParams, "p_process_classify") 86 | 87 | if err != nil { 88 | app.Error(c, -1, err, "") 89 | return 90 | } 91 | app.OK(c, result, "获取分类列表成功") 92 | } 93 | 94 | // 更新流程分类 95 | func UpdateClassify(c *gin.Context) { 96 | var ( 97 | err error 98 | classifyValue process2.Classify 99 | ) 100 | 101 | err = c.ShouldBind(&classifyValue) 102 | if err != nil { 103 | app.Error(c, -1, err, "") 104 | return 105 | } 106 | 107 | // 更新 108 | err = orm.Eloquent.Model(&classifyValue). 109 | Where("id = ?", classifyValue.Id). 110 | Update("name", classifyValue.Name).Error 111 | if err != nil { 112 | app.Error(c, -1, err, "") 113 | return 114 | } 115 | 116 | app.OK(c, classifyValue, "流程分类更新成功") 117 | } 118 | 119 | // 删除流程分类 120 | func DeleteClassify(c *gin.Context) { 121 | classifyId := c.DefaultQuery("classifyId", "") 122 | if classifyId == "" { 123 | app.Error(c, -1, errors.New("参数传递失败,请确认classifyId是否传递"), "") 124 | return 125 | } 126 | 127 | err := orm.Eloquent.Delete(process2.Classify{}, "id = ?", classifyId).Error 128 | if err != nil { 129 | app.Error(c, -1, err, "") 130 | return 131 | } 132 | 133 | app.OK(c, "", "流程分类删除成功") 134 | } 135 | -------------------------------------------------------------------------------- /apis/system/post.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "vAdmin/models" 6 | "vAdmin/tools" 7 | "vAdmin/tools/app" 8 | ) 9 | 10 | // @Summary 职位列表数据 11 | // @Description 获取JSON 12 | // @Tags 职位 13 | // @Param name query string false "name" 14 | // @Param id query string false "id" 15 | // @Param position query string false "position" 16 | // @Success 200 {object} app.Response "{"code": 200, "data": [...]}" 17 | // @Router /api/v1/post [get] 18 | // @Security Bearer 19 | func GetPostList(c *gin.Context) { 20 | var data models.Post 21 | var err error 22 | var pageSize = 10 23 | var pageIndex = 1 24 | 25 | if size := c.Request.FormValue("pageSize"); size != "" { 26 | pageSize = tools.StrToInt(err, size) 27 | } 28 | 29 | if index := c.Request.FormValue("pageIndex"); index != "" { 30 | pageIndex = tools.StrToInt(err, index) 31 | } 32 | 33 | data.PostName = c.Request.FormValue("postName") 34 | id := c.Request.FormValue("postId") 35 | data.PostId, _ = tools.StringToInt(id) 36 | 37 | data.PostName = c.Request.FormValue("postName") 38 | data.DataScope = tools.GetUserIdStr(c) 39 | result, count, err := data.GetPage(pageSize, pageIndex) 40 | tools.HasError(err, "", -1) 41 | app.PageOK(c, result, count, pageIndex, pageSize, "") 42 | } 43 | 44 | func GetPost(c *gin.Context) { 45 | var Post models.Post 46 | Post.PostId, _ = tools.StringToInt(c.Param("postId")) 47 | result, err := Post.Get() 48 | tools.HasError(err, "抱歉未找到相关信息", -1) 49 | app.OK(c,result,"") 50 | } 51 | 52 | // @Summary 添加职位 53 | // @Description 获取JSON 54 | // @Tags 职位 55 | // @Accept application/json 56 | // @Product application/json 57 | // @Param data body models.Post true "data" 58 | // @Success 200 {string} string "{"code": 200, "message": "添加成功"}" 59 | // @Success 200 {string} string "{"code": -1, "message": "添加失败"}" 60 | // @Router /api/v1/post [post] 61 | // @Security Bearer 62 | func InsertPost(c *gin.Context) { 63 | var data models.Post 64 | err := c.Bind(&data) 65 | data.CreateBy = tools.GetUserIdStr(c) 66 | tools.HasError(err, "", 500) 67 | result, err := data.Create() 68 | tools.HasError(err, "", -1) 69 | app.OK(c,result,"") 70 | } 71 | 72 | // @Summary 修改职位 73 | // @Description 获取JSON 74 | // @Tags 职位 75 | // @Accept application/json 76 | // @Product application/json 77 | // @Param data body models.Dept true "body" 78 | // @Success 200 {string} string "{"code": 200, "message": "添加成功"}" 79 | // @Success 200 {string} string "{"code": -1, "message": "添加失败"}" 80 | // @Router /api/v1/post/ [put] 81 | // @Security Bearer 82 | func UpdatePost(c *gin.Context) { 83 | var data models.Post 84 | 85 | err := c.Bind(&data) 86 | data.UpdateBy = tools.GetUserIdStr(c) 87 | tools.HasError(err, "", -1) 88 | result, err := data.Update(data.PostId) 89 | tools.HasError(err, "", -1) 90 | app.OK(c,result,"修改成功") 91 | } 92 | 93 | // @Summary 删除职位 94 | // @Description 删除数据 95 | // @Tags 职位 96 | // @Param id path int true "id" 97 | // @Success 200 {string} string "{"code": 200, "message": "删除成功"}" 98 | // @Success 200 {string} string "{"code": -1, "message": "删除失败"}" 99 | // @Router /api/v1/post/{postId} [delete] 100 | func DeletePost(c *gin.Context) { 101 | var data models.Post 102 | data.UpdateBy = tools.GetUserIdStr(c) 103 | IDS := tools.IdsStrToIdsIntGroup("postId", c) 104 | result, err := data.BatchDelete(IDS) 105 | tools.HasError(err, "删除失败", 500) 106 | app.OK(c,result,"删除成功") 107 | } 108 | -------------------------------------------------------------------------------- /module/deploy/build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package deploy 6 | 7 | import ( 8 | "errors" 9 | "time" 10 | 11 | "github.com/dreamans/syncd/model" 12 | ) 13 | 14 | type Build struct{ 15 | ID int `json:"id"` 16 | ApplyId int `json:"apply_id"` 17 | StartTime int `json:"start_time"` 18 | FinishTime int `json:"finish_time"` 19 | Status int `json:"status"` 20 | Tar string `json:"tar"` 21 | Output string `json:"Output"` 22 | Errmsg string `json:"errmsg"` 23 | Ctime int `json:"ctime"` 24 | } 25 | 26 | const ( 27 | BUILD_STATUS_NONE = 0 28 | BUILD_STATUS_START = 1 29 | BUILD_STATUS_SUCCESS = 2 30 | BUILD_STATUS_FAILED = 3 31 | ) 32 | 33 | func (b *Build) Create() error { 34 | build := &model.DeployBuild{ 35 | ApplyId: b.ApplyId, 36 | Status: b.Status, 37 | StartTime: int(time.Now().Unix()), 38 | } 39 | if ok := build.Create(); !ok { 40 | return errors.New("create deploy build failed") 41 | } 42 | return nil 43 | } 44 | 45 | func (b *Build) CreateFull() error { 46 | build := &model.DeployBuild{ 47 | ApplyId: b.ApplyId, 48 | StartTime: b.StartTime, 49 | FinishTime: b.FinishTime, 50 | Status: b.Status, 51 | Tar: b.Tar, 52 | Output: b.Output, 53 | Errmsg: b.Errmsg, 54 | } 55 | if ok := build.Create(); !ok { 56 | return errors.New("create deploy build failed") 57 | } 58 | return nil 59 | } 60 | 61 | func (b *Build) Detail() error { 62 | build := &model.DeployBuild{} 63 | if ok := build.GetByApplyId(b.ApplyId); !ok { 64 | return errors.New("get deploy build detail failed") 65 | } 66 | if build.ID == 0 { 67 | build.Status = BUILD_STATUS_NONE 68 | return nil 69 | } 70 | b.ID = build.ID 71 | b.Status = build.Status 72 | b.StartTime = build.StartTime 73 | b.FinishTime = build.FinishTime 74 | b.Tar = build.Tar 75 | b.Output = build.Output 76 | b.Errmsg = build.Errmsg 77 | b.Ctime = build.Ctime 78 | 79 | return nil 80 | } 81 | 82 | func (b *Build) Exists() (bool, error) { 83 | if err := b.Detail(); err != nil { 84 | return false, err 85 | } 86 | if b.ID == 0 { 87 | return false, nil 88 | } 89 | return true, nil 90 | } 91 | 92 | func (b *Build) Finish() error { 93 | build := &model.DeployBuild{} 94 | updateData := map[string]interface{}{ 95 | "status": b.Status, 96 | "tar": b.Tar, 97 | "output": b.Output, 98 | "finish_time": int(time.Now().Unix()), 99 | "errmsg": b.Errmsg, 100 | } 101 | if ok := build.UpdateByFields(updateData, model.QueryParam{ 102 | Where:[]model.WhereParam{ 103 | model.WhereParam{ 104 | Field: "apply_id", 105 | Prepare: b.ApplyId, 106 | }, 107 | }, 108 | }); !ok { 109 | return errors.New("update deploy build failed") 110 | } 111 | return nil 112 | } 113 | 114 | func (b *Build) Delete() error { 115 | build := &model.DeployBuild{ 116 | ID: b.ID, 117 | } 118 | if ok := build.Delete(); !ok { 119 | return errors.New("remove deploy build failed") 120 | } 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /models/loginlog.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | "time" 6 | ) 7 | 8 | type LoginLog struct { 9 | InfoId int `json:"infoId" gorm:"primary_key;AUTO_INCREMENT"` //主键 10 | Username string `json:"username" gorm:"type:varchar(128);"` //用户名 11 | Status string `json:"status" gorm:"type:int(1);"` //状态 12 | Ipaddr string `json:"ipaddr" gorm:"type:varchar(255);"` //ip地址 13 | LoginLocation string `json:"loginLocation" gorm:"type:varchar(255);"` //归属地 14 | Browser string `json:"browser" gorm:"type:varchar(255);"` //浏览器 15 | Os string `json:"os" gorm:"type:varchar(255);"` //系统 16 | Platform string `json:"platform" gorm:"type:varchar(255);"` // 固件 17 | LoginTime time.Time `json:"loginTime" gorm:"type:timestamp;"` //登录时间 18 | CreateBy string `json:"createBy" gorm:"type:varchar(128);"` //创建人 19 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);"` //更新者 20 | DataScope string `json:"dataScope" gorm:"-"` //数据 21 | Params string `json:"params" gorm:"-"` // 22 | Remark string `json:"remark" gorm:"type:varchar(255);"` //备注 23 | Msg string `json:"msg" gorm:"type:varchar(255);"` 24 | BaseModel 25 | } 26 | 27 | func (LoginLog) TableName() string { 28 | return "sys_loginlog" 29 | } 30 | 31 | func (e *LoginLog) Get() (LoginLog, error) { 32 | var doc LoginLog 33 | 34 | table := orm.Eloquent.Table(e.TableName()) 35 | if e.Ipaddr != "" { 36 | table = table.Where("ipaddr = ?", e.Ipaddr) 37 | } 38 | if e.InfoId != 0 { 39 | table = table.Where("info_id = ?", e.InfoId) 40 | } 41 | 42 | if err := table.First(&doc).Error; err != nil { 43 | return doc, err 44 | } 45 | return doc, nil 46 | } 47 | 48 | func (e *LoginLog) GetPage(pageSize int, pageIndex int) ([]LoginLog, int, error) { 49 | var doc []LoginLog 50 | 51 | table := orm.Eloquent.Select("*").Table(e.TableName()) 52 | if e.Ipaddr != "" { 53 | table = table.Where("ipaddr = ?", e.Ipaddr) 54 | } 55 | if e.Status != "" { 56 | table = table.Where("status = ?", e.Status) 57 | } 58 | if e.Username != "" { 59 | table = table.Where("userName = ?", e.Username) 60 | } 61 | 62 | var count int 63 | 64 | if err := table.Order("info_id desc").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&doc).Error; err != nil { 65 | return nil, 0, err 66 | } 67 | table.Where("`deleted_at` IS NULL").Count(&count) 68 | return doc, count, nil 69 | } 70 | 71 | func (e *LoginLog) Create() (LoginLog, error) { 72 | var doc LoginLog 73 | e.CreateBy = "0" 74 | e.UpdateBy = "0" 75 | result := orm.Eloquent.Table(e.TableName()).Create(&e) 76 | if result.Error != nil { 77 | err := result.Error 78 | return doc, err 79 | } 80 | doc = *e 81 | return doc, nil 82 | } 83 | 84 | func (e *LoginLog) Update(id int) (update LoginLog, err error) { 85 | 86 | if err = orm.Eloquent.Table(e.TableName()).First(&update, id).Error; err != nil { 87 | return 88 | } 89 | 90 | //参数1:是要修改的数据 91 | //参数2:是修改的数据 92 | if err = orm.Eloquent.Table(e.TableName()).Model(&update).Updates(&e).Error; err != nil { 93 | return 94 | } 95 | return 96 | } 97 | 98 | func (e *LoginLog) BatchDelete(id []int) (Result bool, err error) { 99 | if err = orm.Eloquent.Table(e.TableName()).Where("info_id in (?)", id).Delete(&LoginLog{}).Error; err != nil { 100 | return 101 | } 102 | Result = true 103 | return 104 | } 105 | -------------------------------------------------------------------------------- /apis/log/operlog.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/tools" 8 | "vAdmin/tools/app" 9 | "net/http" 10 | ) 11 | 12 | // @Summary 登录日志列表 13 | // @Description 获取JSON 14 | // @Tags 登录日志 15 | // @Param status query string false "status" 16 | // @Param dictCode query string false "dictCode" 17 | // @Param dictType query string false "dictType" 18 | // @Param pageSize query int false "页条数" 19 | // @Param pageIndex query int false "页码" 20 | // @Success 200 {object} app.Response "{"code": 200, "data": [...]}" 21 | // @Router /api/v1/operloglist [get] 22 | // @Security Bearer 23 | func GetOperLogList(c *gin.Context) { 24 | var data models.SysOperLog 25 | var err error 26 | var pageSize = 10 27 | var pageIndex = 1 28 | 29 | size := c.Request.FormValue("pageSize") 30 | if size != "" { 31 | pageSize = tools.StrToInt(err, size) 32 | } 33 | 34 | index := c.Request.FormValue("pageIndex") 35 | if index != "" { 36 | pageIndex = tools.StrToInt(err, index) 37 | } 38 | 39 | data.OperName = c.Request.FormValue("operName") 40 | data.Status = c.Request.FormValue("status") 41 | data.OperIp = c.Request.FormValue("operIp") 42 | result, count, err := data.GetPage(pageSize, pageIndex) 43 | tools.HasError(err, "", -1) 44 | 45 | var mp = make(map[string]interface{}, 3) 46 | mp["list"] = result 47 | mp["count"] = count 48 | mp["pageIndex"] = pageIndex 49 | mp["pageSize"] = pageSize 50 | 51 | var res app.Response 52 | res.Data = mp 53 | 54 | c.JSON(http.StatusOK, res.ReturnOK()) 55 | } 56 | 57 | // @Summary 通过编码获取登录日志 58 | // @Description 获取JSON 59 | // @Tags 登录日志 60 | // @Param infoId path int true "infoId" 61 | // @Success 200 {object} app.Response "{"code": 200, "data": [...]}" 62 | // @Router /api/v1/operlog/{infoId} [get] 63 | // @Security Bearer 64 | func GetOperLog(c *gin.Context) { 65 | var OperLog models.SysOperLog 66 | OperLog.OperId, _ = tools.StringToInt(c.Param("operId")) 67 | result, err := OperLog.Get() 68 | tools.HasError(err, "抱歉未找到相关信息", -1) 69 | var res app.Response 70 | res.Data = result 71 | c.JSON(http.StatusOK, res.ReturnOK()) 72 | } 73 | 74 | // @Summary 添加操作日志 75 | // @Description 获取JSON 76 | // @Tags 操作日志 77 | // @Accept application/json 78 | // @Product application/json 79 | // @Param data body models.SysOperLog true "data" 80 | // @Success 200 {string} string "{"code": 200, "message": "添加成功"}" 81 | // @Success 200 {string} string "{"code": -1, "message": "添加失败"}" 82 | // @Router /api/v1/operlog [post] 83 | // @Security Bearer 84 | func InsertOperLog(c *gin.Context) { 85 | var data models.SysOperLog 86 | err := c.BindWith(&data, binding.JSON) 87 | tools.HasError(err, "", 500) 88 | result, err := data.Create() 89 | tools.HasError(err, "", -1) 90 | var res app.Response 91 | res.Data = result 92 | c.JSON(http.StatusOK, res.ReturnOK()) 93 | } 94 | 95 | // @Summary 批量删除操作日志 96 | // @Description 删除数据 97 | // @Tags 操作日志 98 | // @Param operId path string true "以逗号(,)分割的operId" 99 | // @Success 200 {string} string "{"code": 200, "message": "删除成功"}" 100 | // @Success 200 {string} string "{"code": -1, "message": "删除失败"}" 101 | // @Router /api/v1/operlog/{operId} [delete] 102 | func DeleteOperLog(c *gin.Context) { 103 | var data models.SysOperLog 104 | data.UpdateBy = tools.GetUserIdStr(c) 105 | IDS := tools.IdsStrToIdsIntGroup("operId", c) 106 | _, err := data.BatchDelete(IDS) 107 | tools.HasError(err, "删除失败", 500) 108 | var res app.Response 109 | res.Msg = "删除成功" 110 | c.JSON(http.StatusOK, res.ReturnOK()) 111 | } 112 | -------------------------------------------------------------------------------- /models/article.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | "vAdmin/tools" 6 | _ "time" 7 | ) 8 | 9 | type Article struct { 10 | 11 | ArticleId int `json:"articleId" gorm:"type:int(11);primary_key"` // 编码 12 | Title string `json:"title" gorm:"type:varchar(128);"` // 标题 13 | Author string `json:"author" gorm:"type:varchar(128);"` // 作者 14 | Content string `json:"content" gorm:"type:varchar(255);"` // 内容 15 | CreateBy string `json:"createBy" gorm:"type:varchar(128);"` // 创建人 16 | UpdateBy string `json:"updateBy" gorm:"type:varchar(128);"` // 更新人 17 | DataScope string `json:"dataScope" gorm:"-"` 18 | Params string `json:"params" gorm:"-"` 19 | BaseModel 20 | } 21 | 22 | func (Article) TableName() string { 23 | return "article" 24 | } 25 | 26 | // 创建Article 27 | func (e *Article) Create() (Article, error) { 28 | var doc Article 29 | result := orm.Eloquent.Table(e.TableName()).Create(&e) 30 | if result.Error != nil { 31 | err := result.Error 32 | return doc, err 33 | } 34 | doc = *e 35 | return doc, nil 36 | } 37 | 38 | 39 | // 获取Article 40 | func (e *Article) Get() (Article, error) { 41 | var doc Article 42 | table := orm.Eloquent.Table(e.TableName()) 43 | 44 | 45 | if e.ArticleId != 0 { 46 | table = table.Where("article_id = ?", e.ArticleId) 47 | } 48 | 49 | if e.Title != "" { 50 | table = table.Where("title = ?", e.Title) 51 | } 52 | 53 | 54 | if e.Author != "" { 55 | table = table.Where("author = ?", e.Author) 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | if err := table.First(&doc).Error; err != nil { 66 | return doc, err 67 | } 68 | return doc, nil 69 | } 70 | 71 | // 获取Article带分页 72 | func (e *Article) GetPage(pageSize int, pageIndex int) ([]Article, int, error) { 73 | var doc []Article 74 | 75 | table := orm.Eloquent.Select("*").Table(e.TableName()) 76 | 77 | if e.ArticleId != 0 { 78 | table = table.Where("article_id = ?", e.ArticleId) 79 | } 80 | 81 | if e.Title != "" { 82 | table = table.Where("title = ?", e.Title) 83 | } 84 | 85 | if e.Author != "" { 86 | table = table.Where("author = ?", e.Author) 87 | } 88 | 89 | 90 | // 数据权限控制(如果不需要数据权限请将此处去掉) 91 | dataPermission := new(DataPermission) 92 | dataPermission.UserId, _ = tools.StringToInt(e.DataScope) 93 | table,err := dataPermission.GetDataScope(e.TableName(), table) 94 | if err != nil { 95 | return nil, 0, err 96 | } 97 | var count int 98 | 99 | if err := table.Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&doc).Error; err != nil { 100 | return nil, 0, err 101 | } 102 | table.Where("`deleted_at` IS NULL").Count(&count) 103 | return doc, count, nil 104 | } 105 | 106 | // 更新Article 107 | func (e *Article) Update(id int) (update Article, err error) { 108 | if err = orm.Eloquent.Table(e.TableName()).Where("article_id = ?", id).First(&update).Error; err != nil { 109 | return 110 | } 111 | 112 | //参数1:是要修改的数据 113 | //参数2:是修改的数据 114 | if err = orm.Eloquent.Table(e.TableName()).Model(&update).Updates(&e).Error; err != nil { 115 | return 116 | } 117 | return 118 | } 119 | 120 | // 删除Article 121 | func (e *Article) Delete(id int) (success bool, err error) { 122 | if err = orm.Eloquent.Table(e.TableName()).Where("article_id = ?", id).Delete(&Article{}).Error; err != nil { 123 | success = false 124 | return 125 | } 126 | success = true 127 | return 128 | } 129 | 130 | //批量删除 131 | func (e *Article) BatchDelete(id []int) (Result bool, err error) { 132 | if err = orm.Eloquent.Table(e.TableName()).Where("article_id in (?)", id).Delete(&Article{}).Error; err != nil { 133 | return 134 | } 135 | Result = true 136 | return 137 | } -------------------------------------------------------------------------------- /models/tools/syscolumns.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | orm "vAdmin/database" 5 | "vAdmin/models" 6 | ) 7 | 8 | type SysColumns struct { 9 | ColumnId int `gorm:"primary_key;auto_increment" json:"columnId"` 10 | TableId int `gorm:"type:int(11);" json:"tableId"` 11 | ColumnName string `gorm:"type:varchar(128);" json:"columnName"` 12 | ColumnComment string `gorm:"column:column_comment;type:varchar(128);" json:"columnComment"` 13 | ColumnType string `gorm:"column:column_type;type:varchar(128);" json:"columnType"` 14 | GoType string `gorm:"column:go_type;type:varchar(128);" json:"goType"` 15 | GoField string `gorm:"column:go_field;type:varchar(128);" json:"goField"` 16 | JsonField string `gorm:"column:json_field;type:varchar(128);" json:"jsonField"` 17 | IsPk string `gorm:"column:is_pk;type:char(4);" json:"isPk"` 18 | IsIncrement string `gorm:"column:is_increment;type:char(4);" json:"isIncrement"` 19 | IsRequired string `gorm:"column:is_required;type:char(4);" json:"isRequired"` 20 | IsInsert string `gorm:"column:is_insert;type:char(4);" json:"isInsert"` 21 | IsEdit string `gorm:"column:is_edit;type:char(4);" json:"isEdit"` 22 | IsList string `gorm:"column:is_list;type:char(4);" json:"isList"` 23 | IsQuery string `gorm:"column:is_query;type:char(4);" json:"isQuery"` 24 | QueryType string `gorm:"column:query_type;type:varchar(128);" json:"queryType"` 25 | HtmlType string `gorm:"column:html_type;type:varchar(128);" json:"htmlType"` 26 | DictType string `gorm:"column:dict_type;type:varchar(128);" json:"dictType"` 27 | Sort int `gorm:"column:sort;type:int(4);" json:"sort"` 28 | List string `gorm:"column:list;type:char(1);" json:"list"` 29 | Pk bool `gorm:"column:pk;type:char(1);" json:"pk"` 30 | Required bool `gorm:"column:required;type:char(1);" json:"required"` 31 | SuperColumn bool `gorm:"column:super_column;type:char(1);" json:"superColumn"` 32 | UsableColumn bool `gorm:"column:usable_column;type:char(1);" json:"usableColumn"` 33 | Increment bool `gorm:"column:increment;type:char(1);" json:"increment"` 34 | Insert bool `gorm:"column:insert;type:char(1);" json:"insert"` 35 | Edit bool `gorm:"column:edit;type:char(1);" json:"edit"` 36 | Query bool `gorm:"column:query;type:char(1);" json:"query"` 37 | Remark string `gorm:"column:remark;type:varchar(255);" json:"remark"` 38 | CreateBy string `gorm:"column:create_by;type:varchar(128);" json:"createBy"` 39 | UpdateBy string `gorm:"column:update_By;type:varchar(128);" json:"updateBy"` 40 | 41 | models.BaseModel 42 | } 43 | 44 | func (SysColumns) TableName() string { 45 | return "sys_columns" 46 | } 47 | 48 | func (e *SysColumns) GetList() ([]SysColumns, error) { 49 | var doc []SysColumns 50 | 51 | table := orm.Eloquent.Select("*").Table("sys_columns") 52 | 53 | table = table.Where("table_id = ?", e.TableId) 54 | 55 | if err := table.Find(&doc).Error; err != nil { 56 | return nil, err 57 | } 58 | return doc, nil 59 | } 60 | 61 | func (e *SysColumns) Create() (SysColumns, error) { 62 | var doc SysColumns 63 | result := orm.Eloquent.Table("sys_columns").Create(&e) 64 | if result.Error != nil { 65 | err := result.Error 66 | return doc, err 67 | } 68 | doc = *e 69 | return doc, nil 70 | } 71 | 72 | func (e *SysColumns) Update() (update SysColumns, err error) { 73 | if err = orm.Eloquent.Table("sys_columns").First(&update, e.ColumnId).Error; err != nil { 74 | return 75 | } 76 | 77 | //参数1:是要修改的数据 78 | //参数2:是修改的数据 79 | if err = orm.Eloquent.Table("sys_columns").Model(&update).Updates(&e).Error; err != nil { 80 | return 81 | } 82 | 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /module/deploy/apply.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 syncd Author. All Rights Reserved. 2 | // Use of this source code is governed by a MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package deploy 6 | 7 | import ( 8 | "errors" 9 | "time" 10 | "vAdmin/models" 11 | ) 12 | 13 | type Apply struct { 14 | ID int `json:"id"` 15 | DemandId string `json:"demand_id"` 16 | OnlineCluster []int `json:"online_cluster"` 17 | CommitVersion string `json:"commit_version"` 18 | Description string `json:"description"` 19 | AuditStatus string `json:"audit_status"` 20 | AuditRefusalReasion string `json:"audit_refusal_reasion"` 21 | Status string `json:"status"` 22 | DeployUser string `json:"deploy_user"` 23 | DeployMode string `json:"deploy_mode"` 24 | CreateBy string `json:"create_by"` 25 | UpdateBy string `json:"update_by"` 26 | CreateByname string `json:"create_byname"` 27 | CreatePhone string `json:"create_phone"` 28 | //RollbackStatus string `json:"rollback_status"` 29 | } 30 | 31 | const ( 32 | AUDIT_STATUS_PENDING = "unaudit" 33 | AUDIT_STATUS_OK = "audit_pass" 34 | AUDIT_STATUS_REFUSE = "audit_denied" 35 | ) 36 | 37 | const ( 38 | APPLY_STATUS_NONE = "not_online" 39 | APPLY_STATUS_ING = "onlineing" 40 | APPLY_STATUS_SUCCESS = "online_success" 41 | APPLY_STATUS_FAILED = "online_failed" 42 | APPLY_STATUS_DROP = "deprecated" 43 | APPLY_STATUS_ROLLBACK = "rollback" 44 | ) 45 | 46 | func (a *Apply) Detail() error { 47 | apply := &models.DeployApp{} 48 | if ok := apply.GetPram(a.ID); !ok { 49 | return errors.New("get deploy apply detail failed") 50 | } 51 | if apply.ID == 0 { 52 | return errors.New("deploy apply detail not exists") 53 | } 54 | a.DemandId = apply.DemandId 55 | a.Description = apply.Description 56 | a.CommitVersion = apply.CommitVersion 57 | a.AuditStatus = apply.AuditStatus 58 | a.Status = apply.Status 59 | a.DeployMode = apply.DeployMode 60 | a.CreateBy = apply.CreateBy 61 | a.CreateByname = apply.CreateByname 62 | a.CreatePhone = apply.CreatePhone 63 | return nil 64 | } 65 | 66 | func (a *Apply) UpdateStatus() error { 67 | apply := &models.DeployApp{} 68 | updateData := map[string]interface{}{ 69 | "status": a.Status, 70 | } 71 | if ok := apply.UpdateByFields(updateData, models.QueryParam{ 72 | Where: []models.WhereParam{ 73 | models.WhereParam{ 74 | Field: "id", 75 | Prepare: a.ID, 76 | }, 77 | }, 78 | }); !ok { 79 | return errors.New("update deploy apply status failed") 80 | } 81 | 82 | return nil 83 | } 84 | 85 | 86 | func (a *Apply) CheckHaveDeploying() (bool, error) { 87 | apply := &models.DeployApp{} 88 | count, ok := apply.Count(models.QueryParam{ 89 | Where: []models.WhereParam{ 90 | models.WhereParam{ 91 | Field: "id", 92 | Tag: "!=", 93 | Prepare: a.ID, 94 | }, 95 | models.WhereParam{ 96 | Field: "status", 97 | Prepare: APPLY_STATUS_ING, 98 | }, 99 | models.WhereParam{ 100 | Field: "created_at", 101 | Tag: ">=", 102 | Prepare: int(time.Now().Unix()) - 86400, 103 | }, 104 | }, 105 | }) 106 | if !ok { 107 | return false, errors.New("get apply count failed") 108 | } 109 | 110 | return count == 0, nil 111 | } -------------------------------------------------------------------------------- /test/api.go.template: -------------------------------------------------------------------------------- 1 | package apis 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/gin-gonic/gin/binding" 6 | "vAdmin/models" 7 | "vAdmin/pkg" 8 | "vAdmin/utils" 9 | "net/http" 10 | ) 11 | 12 | // @Summary 配置列表数据 13 | // @Description 获取JSON 14 | // @Tags 配置 15 | // @Param configKey query string false "configKey" 16 | // @Param configName query string false "configName" 17 | // @Param configType query string false "configType" 18 | // @Param pageSize query int false "页条数" 19 | // @Param pageIndex query int false "页码" 20 | // @Success 200 {object} models.Response "{"code": 200, "data": [...]}" 21 | // @Router /api/v1/configList [get] 22 | // @Security Bearer 23 | func Get{{.ClassName}}List(c *gin.Context) { 24 | var data models.{{.ClassName}} 25 | var err error 26 | var pageSize = 10 27 | var pageIndex = 1 28 | 29 | if size := c.Request.FormValue("pageSize"); size != "" { 30 | pageSize = pkg.StrToInt(err, size) 31 | } 32 | 33 | if index := c.Request.FormValue("pageIndex"); index != "" { 34 | pageIndex = pkg.StrToInt(err, index) 35 | } 36 | 37 | {{ range .Columns -}} 38 | {{$z := .IsQuery}} 39 | {{- if ($z) -}} 40 | data.{{.GoField}} = c.Request.FormValue("{{.JsonField}}") 41 | {{ end }} 42 | {{- end -}} 43 | 44 | data.DataScope = utils.GetUserIdStr(c) 45 | result, count, err := data.GetPage(pageSize, pageIndex) 46 | pkg.HasError(err, "", -1) 47 | 48 | var mp = make(map[string]interface{}, 3) 49 | mp["list"] = result 50 | mp["count"] = count 51 | mp["pageIndex"] = pageIndex 52 | mp["pageIndex"] = pageSize 53 | 54 | var res models.Response 55 | res.Data = mp 56 | 57 | c.JSON(http.StatusOK, res.ReturnOK()) 58 | } 59 | 60 | // @Summary 获取配置 61 | // @Description 获取JSON 62 | // @Tags 配置 63 | // @Param configId path int true "配置编码" 64 | // @Success 200 {object} models.Response "{"code": 200, "data": [...]}" 65 | // @Router /api/v1/config/{configId} [get] 66 | // @Security Bearer 67 | func Get{{.ClassName}}(c *gin.Context) { 68 | var data models.{{.ClassName}} 69 | data.{{.PkGoField}}, _ = utils.StringToInt(c.Param("{{.PkJsonField}}")) 70 | result, err := data.Get() 71 | pkg.HasError(err, "抱歉未找到相关信息", -1) 72 | 73 | var res models.Response 74 | res.Data = result 75 | 76 | c.JSON(http.StatusOK, res.ReturnOK()) 77 | } 78 | 79 | // @Summary 添加配置 80 | // @Description 获取JSON 81 | // @Tags 配置 82 | // @Accept application/json 83 | // @Product application/json 84 | // @Param data body models.{{.ClassName}} true "data" 85 | // @Success 200 {string} string "{"code": 200, "message": "添加成功"}" 86 | // @Success 200 {string} string "{"code": -1, "message": "添加失败"}" 87 | // @Router /api/v1/dict/data [post] 88 | // @Security Bearer 89 | func Insert{{.ClassName}}(c *gin.Context) { 90 | var data models.{{.ClassName}} 91 | err := c.BindWith(&data, binding.JSON) 92 | data.CreateBy = utils.GetUserIdStr(c) 93 | pkg.HasError(err, "", 500) 94 | result, err := data.Create() 95 | pkg.HasError(err, "", -1) 96 | 97 | var res models.Response 98 | res.Data = result 99 | c.JSON(http.StatusOK, res.ReturnOK()) 100 | 101 | } 102 | 103 | func Update{{.ClassName}}(c *gin.Context) { 104 | var data models.{{.ClassName}} 105 | err := c.BindWith(&data, binding.JSON) 106 | pkg.HasError(err, "数据解析失败", -1) 107 | data.UpdateBy = utils.GetUserIdStr(c) 108 | result, err := data.Update(data.{{.PkGoField}}) 109 | pkg.HasError(err, "", -1) 110 | 111 | var res models.Response 112 | res.Data = result 113 | c.JSON(http.StatusOK, res.ReturnOK()) 114 | } 115 | 116 | func Delete{{.ClassName}}(c *gin.Context) { 117 | var data models.{{.ClassName}} 118 | id, err := utils.StringToInt(c.Param("{{.PkJsonField}}")) 119 | data.UpdateBy = utils.GetUserIdStr(c) 120 | _, err = data.Delete(id) 121 | pkg.HasError(err, "修改失败", 500) 122 | 123 | var res models.Response 124 | res.Msg = "删除成功" 125 | c.JSON(http.StatusOK, res.ReturnOK()) 126 | } -------------------------------------------------------------------------------- /module/deploy/cmdexe.go: -------------------------------------------------------------------------------- 1 | package deploy 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "strings" 12 | ) 13 | 14 | func AsyncLog(reader io.ReadCloser, ResultChan chan string, ExitChan chan int)error { 15 | cache := "" //缓存不足一行的日志信息 16 | buf := make([]byte, 1024) 17 | for { 18 | num, err := reader.Read(buf) 19 | if err != nil && err!=io.EOF{ 20 | return err 21 | } 22 | if err == io.EOF { 23 | ExitChan <- 1 // 记录协程管斌 24 | } 25 | if num > 0 { 26 | b := buf[:num] 27 | s := strings.Split(string(b), "\n") 28 | line := strings.Join(s[:len(s)-1], "\n") //取出整行的日志 29 | fmt.Printf("%s%s\n", cache, line) 30 | ResultChan <- line 31 | cache = s[len(s)-1] 32 | } 33 | } 34 | return nil 35 | } 36 | 37 | func Execute(CMD string,ResultExecChan chan string, ExitExecChan chan int) error { 38 | var ( 39 | ResultChan = make(chan string, 1000) 40 | ExitChan = make(chan int) 41 | ) 42 | fmt.Println("CMD:",CMD) 43 | //cmd := exec.Command("sh", "-c", CMD) 44 | cmd := exec.Command("cmd", "/C", "dir D: /s/b") 45 | // 命令的错误输出和标准输出都连接到同一个管道 46 | // stdout, err := cmd.StdoutPipe() 47 | // cmd.Stderr = cmd.Stdout 48 | 49 | stdout, _ := cmd.StdoutPipe() 50 | cmd.Stderr = cmd.Stdout 51 | //stderr, _ := cmd.StderrPipe() 52 | 53 | if err := cmd.Start(); err != nil { 54 | log.Printf("Error starting command: %s......", err.Error()) 55 | return err 56 | } 57 | 58 | go AsyncLog(stdout,ResultChan,ExitChan) 59 | //go AsyncLog(stderr,ResultChan,ExitChan) 60 | 61 | go func() { 62 | for i := 0; i < 1; i++ { // 从exitchan管道中获取到1次goroute完成完毕记录才放行 63 | a := <-ExitChan // 没有记录的话会堵塞,会等下次的记录被插入才会放行 64 | if a == 1 { 65 | fmt.Println("AsyncLog goroute结束") 66 | } 67 | } 68 | close(ResultChan) // 1次循环结束代表1个收集结果的goroute全部完成完毕了,此时才能关闭resultchan,为的是让下面for循环取完resultchan管道中值的时可以正常退出 69 | }() 70 | 71 | for v := range ResultChan { 72 | //fmt.Println(v) 73 | ResultExecChan <- v 74 | } 75 | 76 | ExitExecChan <- 1 // 记录协程管斌 77 | 78 | if err := cmd.Wait(); err != nil { 79 | log.Printf("Error waiting for command execution: %s......", err.Error()) 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | func ExecuteToLog(CMD string, Filename string, ExitExecChan chan int) error { 86 | // open the out file for writing 87 | outfile, err := os.Create(Filename) 88 | if err != nil { 89 | panic(err) 90 | } 91 | defer outfile.Close() 92 | 93 | fmt.Println("CMD:",CMD) 94 | sysType := runtime.GOOS 95 | switch sysType { 96 | case "linux": 97 | cmd := exec.Command("sh", "-c", CMD) 98 | stdout, _ := cmd.StdoutPipe() 99 | cmd.Stderr = cmd.Stdout 100 | //stderr, _ := cmd.StderrPipe() 101 | 102 | if err := cmd.Start(); err != nil { 103 | log.Printf("Error starting command: %s......", err.Error()) 104 | return err 105 | } 106 | writer := bufio.NewWriter(outfile) 107 | go io.Copy(writer, stdout) 108 | 109 | if err := cmd.Wait(); err != nil { 110 | log.Printf("Error waiting for command execution: %s......", err.Error()) 111 | return err 112 | } 113 | case "windows": 114 | //cmd := exec.Command("cmd", "/C", "del", "D:\\a.txt") 115 | cmd := exec.Command("cmd", "/C", "dir D: /s/b") 116 | stdout, _ := cmd.StdoutPipe() 117 | cmd.Stderr = cmd.Stdout 118 | //stderr, _ := cmd.StderrPipe() 119 | 120 | if err := cmd.Start(); err != nil { 121 | log.Printf("Error starting command: %s......", err.Error()) 122 | return err 123 | } 124 | writer := bufio.NewWriter(outfile) 125 | go io.Copy(writer, stdout) 126 | 127 | if err := cmd.Wait(); err != nil { 128 | log.Printf("Error waiting for command execution: %s......", err.Error()) 129 | return err 130 | } 131 | } 132 | // 命令的错误输出和标准输出都连接到同一个管道 133 | // stdout, err := cmd.StdoutPipe() 134 | // cmd.Stderr = cmd.Stdout 135 | 136 | ExitExecChan <- 1 // 记录协程管斌 137 | return nil 138 | } --------------------------------------------------------------------------------