mirror of https://github.com/ouqiang/gocron
style: 代码格式化
parent
21028ec6f0
commit
f0ff9a88a7
296
cmd/web.go
296
cmd/web.go
|
@ -1,194 +1,192 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/routers"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/macaron.v1"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/routers"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/macaron.v1"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// web服务器默认端口
|
||||
const DefaultPort = 5920
|
||||
|
||||
|
||||
var CmdWeb = cli.Command{
|
||||
Name: "web",
|
||||
Usage: "run web server",
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "0.0.0.0",
|
||||
Usage: "bind host",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port,p",
|
||||
Value: DefaultPort,
|
||||
Usage: "bind port",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "env,e",
|
||||
Value: "prod",
|
||||
Usage: "runtime environment, dev|test|prod",
|
||||
},
|
||||
},
|
||||
Name: "web",
|
||||
Usage: "run web server",
|
||||
Action: runWeb,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "0.0.0.0",
|
||||
Usage: "bind host",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port,p",
|
||||
Value: DefaultPort,
|
||||
Usage: "bind port",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "env,e",
|
||||
Value: "prod",
|
||||
Usage: "runtime environment, dev|test|prod",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) {
|
||||
// 设置运行环境
|
||||
setEnvironment(ctx)
|
||||
// 初始化应用
|
||||
app.InitEnv(ctx.App.Version)
|
||||
// 初始化模块 DB、定时任务等
|
||||
initModule()
|
||||
// 捕捉信号,配置热更新等
|
||||
go catchSignal()
|
||||
m := macaron.Classic()
|
||||
// 设置运行环境
|
||||
setEnvironment(ctx)
|
||||
// 初始化应用
|
||||
app.InitEnv(ctx.App.Version)
|
||||
// 初始化模块 DB、定时任务等
|
||||
initModule()
|
||||
// 捕捉信号,配置热更新等
|
||||
go catchSignal()
|
||||
m := macaron.Classic()
|
||||
|
||||
// 注册路由
|
||||
routers.Register(m)
|
||||
// 注册中间件.
|
||||
routers.RegisterMiddleware(m)
|
||||
host := parseHost(ctx)
|
||||
port := parsePort(ctx)
|
||||
m.Run(host, port)
|
||||
// 注册路由
|
||||
routers.Register(m)
|
||||
// 注册中间件.
|
||||
routers.RegisterMiddleware(m)
|
||||
host := parseHost(ctx)
|
||||
port := parsePort(ctx)
|
||||
m.Run(host, port)
|
||||
}
|
||||
|
||||
func initModule() {
|
||||
if !app.Installed {
|
||||
return
|
||||
}
|
||||
func initModule() {
|
||||
if !app.Installed {
|
||||
return
|
||||
}
|
||||
|
||||
config, err := setting.Read(app.AppConfig)
|
||||
if err != nil {
|
||||
logger.Fatal("读取应用配置失败", err)
|
||||
}
|
||||
app.Setting = config
|
||||
config, err := setting.Read(app.AppConfig)
|
||||
if err != nil {
|
||||
logger.Fatal("读取应用配置失败", err)
|
||||
}
|
||||
app.Setting = config
|
||||
|
||||
// 初始化DB
|
||||
models.Db = models.CreateDb()
|
||||
// 初始化DB
|
||||
models.Db = models.CreateDb()
|
||||
|
||||
// 版本升级
|
||||
upgradeIfNeed()
|
||||
// 版本升级
|
||||
upgradeIfNeed()
|
||||
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
func parsePort(ctx *cli.Context) int {
|
||||
var port int = DefaultPort
|
||||
if ctx.IsSet("port") {
|
||||
port = ctx.Int("port")
|
||||
}
|
||||
if port <= 0 || port >= 65535 {
|
||||
port = DefaultPort
|
||||
}
|
||||
var port int = DefaultPort
|
||||
if ctx.IsSet("port") {
|
||||
port = ctx.Int("port")
|
||||
}
|
||||
if port <= 0 || port >= 65535 {
|
||||
port = DefaultPort
|
||||
}
|
||||
|
||||
return port
|
||||
return port
|
||||
}
|
||||
|
||||
func parseHost(ctx *cli.Context) string {
|
||||
if ctx.IsSet("host") {
|
||||
return ctx.String("host")
|
||||
}
|
||||
func parseHost(ctx *cli.Context) string {
|
||||
if ctx.IsSet("host") {
|
||||
return ctx.String("host")
|
||||
}
|
||||
|
||||
return "0.0.0.0"
|
||||
return "0.0.0.0"
|
||||
}
|
||||
|
||||
func setEnvironment(ctx *cli.Context) {
|
||||
var env string = "prod"
|
||||
if ctx.IsSet("env") {
|
||||
env = ctx.String("env")
|
||||
}
|
||||
func setEnvironment(ctx *cli.Context) {
|
||||
var env string = "prod"
|
||||
if ctx.IsSet("env") {
|
||||
env = ctx.String("env")
|
||||
}
|
||||
|
||||
switch env {
|
||||
case "test":
|
||||
macaron.Env = macaron.TEST
|
||||
case "dev":
|
||||
macaron.Env = macaron.DEV
|
||||
default:
|
||||
macaron.Env = macaron.PROD
|
||||
}
|
||||
switch env {
|
||||
case "test":
|
||||
macaron.Env = macaron.TEST
|
||||
case "dev":
|
||||
macaron.Env = macaron.DEV
|
||||
default:
|
||||
macaron.Env = macaron.PROD
|
||||
}
|
||||
}
|
||||
|
||||
// 捕捉信号
|
||||
func catchSignal() {
|
||||
c := make(chan os.Signal)
|
||||
// todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2
|
||||
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
for {
|
||||
s := <- c
|
||||
logger.Info("收到信号 -- ", s)
|
||||
switch s {
|
||||
case syscall.SIGHUP:
|
||||
logger.Info("收到终端断开信号, 忽略")
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
func catchSignal() {
|
||||
c := make(chan os.Signal)
|
||||
// todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2
|
||||
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
for {
|
||||
s := <-c
|
||||
logger.Info("收到信号 -- ", s)
|
||||
switch s {
|
||||
case syscall.SIGHUP:
|
||||
logger.Info("收到终端断开信号, 忽略")
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 应用退出
|
||||
func shutdown() {
|
||||
defer func() {
|
||||
logger.Info("已退出")
|
||||
os.Exit(0)
|
||||
}()
|
||||
func shutdown() {
|
||||
defer func() {
|
||||
logger.Info("已退出")
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
if !app.Installed {
|
||||
return
|
||||
}
|
||||
logger.Info("应用准备退出")
|
||||
serviceTask := new(service.Task)
|
||||
// 停止所有任务调度
|
||||
logger.Info("停止定时任务调度")
|
||||
serviceTask.StopAll()
|
||||
if !app.Installed {
|
||||
return
|
||||
}
|
||||
logger.Info("应用准备退出")
|
||||
serviceTask := new(service.Task)
|
||||
// 停止所有任务调度
|
||||
logger.Info("停止定时任务调度")
|
||||
serviceTask.StopAll()
|
||||
|
||||
taskNumInRunning := service.TaskNum.Num()
|
||||
logger.Infof("正在运行的任务有%d个", taskNumInRunning)
|
||||
if taskNumInRunning > 0 {
|
||||
logger.Info("等待所有任务执行完成后退出")
|
||||
}
|
||||
for {
|
||||
if taskNumInRunning <= 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
taskNumInRunning = service.TaskNum.Num()
|
||||
}
|
||||
taskNumInRunning := service.TaskNum.Num()
|
||||
logger.Infof("正在运行的任务有%d个", taskNumInRunning)
|
||||
if taskNumInRunning > 0 {
|
||||
logger.Info("等待所有任务执行完成后退出")
|
||||
}
|
||||
for {
|
||||
if taskNumInRunning <= 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
taskNumInRunning = service.TaskNum.Num()
|
||||
}
|
||||
|
||||
// 释放gRPC连接池
|
||||
grpcpool.Pool.ReleaseAll()
|
||||
// 释放gRPC连接池
|
||||
grpcpool.Pool.ReleaseAll()
|
||||
}
|
||||
|
||||
// 判断应用是否需要升级, 当存在版本号文件且版本小于app.VersionId时升级
|
||||
func upgradeIfNeed() {
|
||||
currentVersionId := app.GetCurrentVersionId()
|
||||
// 没有版本号文件
|
||||
if currentVersionId == 0 {
|
||||
return;
|
||||
}
|
||||
if currentVersionId >= app.VersionId {
|
||||
return
|
||||
}
|
||||
func upgradeIfNeed() {
|
||||
currentVersionId := app.GetCurrentVersionId()
|
||||
// 没有版本号文件
|
||||
if currentVersionId == 0 {
|
||||
return
|
||||
}
|
||||
if currentVersionId >= app.VersionId {
|
||||
return
|
||||
}
|
||||
|
||||
migration := new(models.Migration)
|
||||
logger.Infof("版本升级开始, 当前版本号%d", currentVersionId)
|
||||
migration := new(models.Migration)
|
||||
logger.Infof("版本升级开始, 当前版本号%d", currentVersionId)
|
||||
|
||||
migration.Upgrade(currentVersionId)
|
||||
app.UpdateVersionFile()
|
||||
migration.Upgrade(currentVersionId)
|
||||
app.UpdateVersionFile()
|
||||
|
||||
logger.Infof("已升级到最新版本%d", app.VersionId)
|
||||
}
|
||||
logger.Infof("已升级到最新版本%d", app.VersionId)
|
||||
}
|
||||
|
|
102
gocron-node.go
102
gocron-node.go
|
@ -4,67 +4,65 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
"github.com/ouqiang/gocron/modules/rpc/server"
|
||||
"flag"
|
||||
"runtime"
|
||||
"os"
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const AppVersion = "1.2.2"
|
||||
|
||||
func main() {
|
||||
func main() {
|
||||
var serverAddr string
|
||||
var allowRoot bool
|
||||
var version bool
|
||||
var CAFile string
|
||||
var certFile string
|
||||
var keyFile string
|
||||
var enableTLS bool
|
||||
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
||||
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
||||
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
||||
flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls")
|
||||
flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path")
|
||||
flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path")
|
||||
flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path")
|
||||
flag.Parse()
|
||||
var allowRoot bool
|
||||
var version bool
|
||||
var CAFile string
|
||||
var certFile string
|
||||
var keyFile string
|
||||
var enableTLS bool
|
||||
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
||||
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
||||
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
||||
flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls")
|
||||
flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path")
|
||||
flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path")
|
||||
flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path")
|
||||
flag.Parse()
|
||||
|
||||
if version {
|
||||
fmt.Println(AppVersion)
|
||||
return
|
||||
}
|
||||
if version {
|
||||
fmt.Println(AppVersion)
|
||||
return
|
||||
}
|
||||
|
||||
if (enableTLS) {
|
||||
if !utils.FileExist(CAFile) {
|
||||
fmt.Printf("failed to read ca cert file: %s", CAFile)
|
||||
return
|
||||
}
|
||||
if !utils.FileExist(certFile) {
|
||||
fmt.Printf("failed to read server cert file: %s", certFile)
|
||||
return
|
||||
}
|
||||
if !utils.FileExist(keyFile) {
|
||||
fmt.Printf("failed to read server key file: %s", keyFile)
|
||||
return
|
||||
}
|
||||
}
|
||||
if enableTLS {
|
||||
if !utils.FileExist(CAFile) {
|
||||
fmt.Printf("failed to read ca cert file: %s", CAFile)
|
||||
return
|
||||
}
|
||||
if !utils.FileExist(certFile) {
|
||||
fmt.Printf("failed to read server cert file: %s", certFile)
|
||||
return
|
||||
}
|
||||
if !utils.FileExist(keyFile) {
|
||||
fmt.Printf("failed to read server key file: %s", keyFile)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
certificate := auth.Certificate{
|
||||
CAFile: strings.TrimSpace(CAFile),
|
||||
CertFile: strings.TrimSpace(certFile),
|
||||
KeyFile: strings.TrimSpace(keyFile),
|
||||
}
|
||||
|
||||
|
||||
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
||||
fmt.Println("Do not run gocron-node as root user")
|
||||
return
|
||||
}
|
||||
certificate := auth.Certificate{
|
||||
CAFile: strings.TrimSpace(CAFile),
|
||||
CertFile: strings.TrimSpace(certFile),
|
||||
KeyFile: strings.TrimSpace(keyFile),
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
||||
fmt.Println("Do not run gocron-node as root user")
|
||||
return
|
||||
}
|
||||
|
||||
server.Start(serverAddr, enableTLS, certificate)
|
||||
}
|
||||
}
|
||||
|
|
24
gocron.go
24
gocron.go
|
@ -4,22 +4,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
"os"
|
||||
"github.com/urfave/cli"
|
||||
"os"
|
||||
|
||||
"github.com/ouqiang/gocron/cmd"
|
||||
"github.com/ouqiang/gocron/cmd"
|
||||
)
|
||||
|
||||
const AppVersion = "1.2.2"
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "gocron"
|
||||
app.Usage = "gocron service"
|
||||
app.Version = AppVersion
|
||||
app.Commands = []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
app.Run(os.Args)
|
||||
app := cli.NewApp()
|
||||
app.Name = "gocron"
|
||||
app.Usage = "gocron service"
|
||||
app.Version = AppVersion
|
||||
app.Commands = []cli.Command{
|
||||
cmd.CmdWeb,
|
||||
}
|
||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
|
103
models/host.go
103
models/host.go
|
@ -1,95 +1,94 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
)
|
||||
|
||||
// 主机
|
||||
type Host struct {
|
||||
Id int16 `xorm:"smallint pk autoincr"`
|
||||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||
Port int `xorm:"notnull default 22"` // 主机端口
|
||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
||||
BaseModel `xorm:"-"`
|
||||
Selected bool `xorm:"-"`
|
||||
Id int16 `xorm:"smallint pk autoincr"`
|
||||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||
Port int `xorm:"notnull default 22"` // 主机端口
|
||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
||||
BaseModel `xorm:"-"`
|
||||
Selected bool `xorm:"-"`
|
||||
}
|
||||
|
||||
// 新增
|
||||
func (host *Host) Create() (insertId int16, err error) {
|
||||
_, err = Db.Insert(host)
|
||||
if err == nil {
|
||||
insertId = host.Id
|
||||
}
|
||||
_, err = Db.Insert(host)
|
||||
if err == nil {
|
||||
insertId = host.Id
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
||||
}
|
||||
|
||||
|
||||
// 更新
|
||||
func (host *Host) Update(id int, data CommonMap) (int64, error) {
|
||||
return Db.Table(host).ID(id).Update(data)
|
||||
return Db.Table(host).ID(id).Update(data)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (host *Host) Delete(id int) (int64, error) {
|
||||
return Db.Id(id).Delete(new(Host))
|
||||
return Db.Id(id).Delete(new(Host))
|
||||
}
|
||||
|
||||
func (host *Host) Find(id int) error {
|
||||
_, err := Db.Id(id).Get(host)
|
||||
_, err := Db.Id(id).Get(host)
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
func (host *Host) NameExists(name string, id int16) (bool, error) {
|
||||
if id == 0 {
|
||||
count, err := Db.Where("name = ?", name).Count(host);
|
||||
return count > 0, err
|
||||
}
|
||||
func (host *Host) NameExists(name string, id int16) (bool, error) {
|
||||
if id == 0 {
|
||||
count, err := Db.Where("name = ?", name).Count(host)
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host);
|
||||
return count > 0, err
|
||||
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host)
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func (host *Host) List(params CommonMap) ([]Host, error) {
|
||||
host.parsePageAndPageSize(params)
|
||||
list := make([]Host, 0)
|
||||
session := Db.Desc("id")
|
||||
host.parseWhere(session, params)
|
||||
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||
host.parsePageAndPageSize(params)
|
||||
list := make([]Host, 0)
|
||||
session := Db.Desc("id")
|
||||
host.parseWhere(session, params)
|
||||
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (host *Host) AllList() ([]Host, error) {
|
||||
list := make([]Host, 0)
|
||||
err := Db.Cols("name,port").Desc("id").Find(&list)
|
||||
list := make([]Host, 0)
|
||||
err := Db.Cols("name,port").Desc("id").Find(&list)
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (host *Host) Total(params CommonMap) (int64, error) {
|
||||
session := Db.NewSession()
|
||||
host.parseWhere(session, params)
|
||||
return session.Count(host)
|
||||
session := Db.NewSession()
|
||||
host.parseWhere(session, params)
|
||||
return session.Count(host)
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("id = ?", id)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("name = ?", name)
|
||||
}
|
||||
}
|
||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("id = ?", id)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("name = ?", name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 用户登录日志
|
||||
|
||||
type LoginLog struct {
|
||||
Id int `xorm:"pk autoincr notnull "`
|
||||
Username string `xorm:"varchar(32) notnull"`
|
||||
Ip string `xorm:"varchar(15) not null"`
|
||||
Created time.Time `xorm:"datetime notnull created"`
|
||||
BaseModel `xorm:"-"`
|
||||
Id int `xorm:"pk autoincr notnull "`
|
||||
Username string `xorm:"varchar(32) notnull"`
|
||||
Ip string `xorm:"varchar(15) not null"`
|
||||
Created time.Time `xorm:"datetime notnull created"`
|
||||
BaseModel `xorm:"-"`
|
||||
}
|
||||
|
||||
func (log *LoginLog) Create() (insertId int, err error) {
|
||||
_, err = Db.Insert(log)
|
||||
if err == nil {
|
||||
insertId = log.Id
|
||||
}
|
||||
_, err = Db.Insert(log)
|
||||
if err == nil {
|
||||
insertId = log.Id
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) {
|
||||
log.parsePageAndPageSize(params)
|
||||
list := make([]LoginLog, 0)
|
||||
err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list)
|
||||
log.parsePageAndPageSize(params)
|
||||
list := make([]LoginLog, 0)
|
||||
err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list)
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (log *LoginLog) Total() (int64, error) {
|
||||
return Db.Count(log)
|
||||
}
|
||||
return Db.Count(log)
|
||||
}
|
||||
|
|
|
@ -1,159 +1,157 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/go-xorm/xorm"
|
||||
"strconv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
type Migration struct{}
|
||||
|
||||
// 首次安装, 创建数据库表
|
||||
func (migration *Migration) Install(dbName string) error {
|
||||
if !isDatabaseExist(dbName) {
|
||||
return errors.New("数据库不存在")
|
||||
}
|
||||
setting := new(Setting)
|
||||
task := new(Task)
|
||||
tables := []interface{}{
|
||||
&User{}, task, &TaskLog{}, &Host{}, setting,&LoginLog{},&TaskHost{},
|
||||
}
|
||||
for _, table := range tables {
|
||||
exist, err:= Db.IsTableExist(table)
|
||||
if exist {
|
||||
return errors.New("数据表已存在")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = Db.Sync2(table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
setting.InitBasicField()
|
||||
task.CreateTestTask()
|
||||
if !isDatabaseExist(dbName) {
|
||||
return errors.New("数据库不存在")
|
||||
}
|
||||
setting := new(Setting)
|
||||
task := new(Task)
|
||||
tables := []interface{}{
|
||||
&User{}, task, &TaskLog{}, &Host{}, setting, &LoginLog{}, &TaskHost{},
|
||||
}
|
||||
for _, table := range tables {
|
||||
exist, err := Db.IsTableExist(table)
|
||||
if exist {
|
||||
return errors.New("数据表已存在")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = Db.Sync2(table)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
setting.InitBasicField()
|
||||
task.CreateTestTask()
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断数据库是否存在
|
||||
func isDatabaseExist(name string) bool {
|
||||
_, err := Db.Exec("use ?", name)
|
||||
_, err := Db.Exec("use ?", name)
|
||||
|
||||
return err != nil
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// 迭代升级数据库, 新建表、新增字段等
|
||||
func (migration *Migration) Upgrade(oldVersionId int) {
|
||||
// v1.2版本不支持升级
|
||||
if oldVersionId == 120 {
|
||||
return
|
||||
}
|
||||
func (migration *Migration) Upgrade(oldVersionId int) {
|
||||
// v1.2版本不支持升级
|
||||
if oldVersionId == 120 {
|
||||
return
|
||||
}
|
||||
|
||||
versionIds := []int{110, 122}
|
||||
upgradeFuncs := []func(*xorm.Session) error {
|
||||
migration.upgradeFor110,
|
||||
migration.upgradeFor122,
|
||||
}
|
||||
versionIds := []int{110, 122}
|
||||
upgradeFuncs := []func(*xorm.Session) error{
|
||||
migration.upgradeFor110,
|
||||
migration.upgradeFor122,
|
||||
}
|
||||
|
||||
startIndex := -1
|
||||
// 从当前版本的下一版本开始升级
|
||||
for i, value := range versionIds {
|
||||
if value > oldVersionId {
|
||||
startIndex = i
|
||||
break;
|
||||
}
|
||||
}
|
||||
startIndex := -1
|
||||
// 从当前版本的下一版本开始升级
|
||||
for i, value := range versionIds {
|
||||
if value > oldVersionId {
|
||||
startIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if startIndex == -1 {
|
||||
return
|
||||
}
|
||||
if startIndex == -1 {
|
||||
return
|
||||
}
|
||||
|
||||
length := len(versionIds)
|
||||
if startIndex >= length {
|
||||
return
|
||||
}
|
||||
length := len(versionIds)
|
||||
if startIndex >= length {
|
||||
return
|
||||
}
|
||||
|
||||
session := Db.NewSession()
|
||||
err := session.Begin()
|
||||
if err != nil {
|
||||
logger.Fatalf("开启事务失败-%s", err.Error())
|
||||
}
|
||||
for startIndex < length {
|
||||
err = upgradeFuncs[startIndex](session)
|
||||
if err == nil {
|
||||
startIndex++
|
||||
continue
|
||||
}
|
||||
dbErr := session.Rollback()
|
||||
if dbErr != nil {
|
||||
logger.Fatalf("事务回滚失败-%s",dbErr.Error())
|
||||
}
|
||||
logger.Fatal(err)
|
||||
}
|
||||
err = session.Commit()
|
||||
if err != nil {
|
||||
logger.Fatalf("提交事务失败-%s", err.Error())
|
||||
}
|
||||
session := Db.NewSession()
|
||||
err := session.Begin()
|
||||
if err != nil {
|
||||
logger.Fatalf("开启事务失败-%s", err.Error())
|
||||
}
|
||||
for startIndex < length {
|
||||
err = upgradeFuncs[startIndex](session)
|
||||
if err == nil {
|
||||
startIndex++
|
||||
continue
|
||||
}
|
||||
dbErr := session.Rollback()
|
||||
if dbErr != nil {
|
||||
logger.Fatalf("事务回滚失败-%s", dbErr.Error())
|
||||
}
|
||||
logger.Fatal(err)
|
||||
}
|
||||
err = session.Commit()
|
||||
if err != nil {
|
||||
logger.Fatalf("提交事务失败-%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 升级到v1.1版本
|
||||
func (migration *Migration) upgradeFor110(session *xorm.Session) error {
|
||||
logger.Info("开始升级到v1.1")
|
||||
// 创建表task_host
|
||||
err := session.Sync2(new(TaskHost))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("开始升级到v1.1")
|
||||
// 创建表task_host
|
||||
err := session.Sync2(new(TaskHost))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tableName := TablePrefix + "task"
|
||||
// 把task对应的host_id写入task_host表
|
||||
sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName)
|
||||
results, err := session.Query(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tableName := TablePrefix + "task"
|
||||
// 把task对应的host_id写入task_host表
|
||||
sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName)
|
||||
results, err := session.Query(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, value := range results {
|
||||
taskHostModel := &TaskHost{}
|
||||
taskId, err := strconv.Atoi(string(value["id"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostId, err := strconv.Atoi(string(value["host_id"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskHostModel.TaskId = taskId
|
||||
taskHostModel.HostId = int16(hostId)
|
||||
_, err = session.Insert(taskHostModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, value := range results {
|
||||
taskHostModel := &TaskHost{}
|
||||
taskId, err := strconv.Atoi(string(value["id"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostId, err := strconv.Atoi(string(value["host_id"]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
taskHostModel.TaskId = taskId
|
||||
taskHostModel.HostId = int16(hostId)
|
||||
_, err = session.Insert(taskHostModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 删除task表host_id字段
|
||||
_, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName))
|
||||
|
||||
// 删除task表host_id字段
|
||||
_, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName))
|
||||
logger.Info("已升级到v1.1\n")
|
||||
|
||||
logger.Info("已升级到v1.1\n")
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级到1.2.2版本
|
||||
func (migration *Migration) upgradeFor122(session *xorm.Session) error {
|
||||
logger.Info("开始升级到v1.2.2")
|
||||
logger.Info("开始升级到v1.2.2")
|
||||
|
||||
tableName := TablePrefix + "task"
|
||||
// task表增加tag字段
|
||||
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName))
|
||||
tableName := TablePrefix + "task"
|
||||
// task表增加tag字段
|
||||
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName))
|
||||
|
||||
logger.Info("已升级到v1.2.2\n")
|
||||
logger.Info("已升级到v1.2.2\n")
|
||||
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
|
162
models/model.go
162
models/model.go
|
@ -1,16 +1,16 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
"gopkg.in/macaron.v1"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/go-xorm/core"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"gopkg.in/macaron.v1"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Status int8
|
||||
|
@ -20,105 +20,105 @@ var TablePrefix string = ""
|
|||
var Db *xorm.Engine
|
||||
|
||||
const (
|
||||
Disabled Status = 0 // 禁用
|
||||
Failure Status = 0 // 失败
|
||||
Enabled Status = 1 // 启用
|
||||
Running Status = 1 // 运行中
|
||||
Finish Status = 2 // 完成
|
||||
Cancel Status = 3 // 取消
|
||||
Waiting Status = 5 // 等待中
|
||||
Disabled Status = 0 // 禁用
|
||||
Failure Status = 0 // 失败
|
||||
Enabled Status = 1 // 启用
|
||||
Running Status = 1 // 运行中
|
||||
Finish Status = 2 // 完成
|
||||
Cancel Status = 3 // 取消
|
||||
Waiting Status = 5 // 等待中
|
||||
)
|
||||
|
||||
const (
|
||||
Page = 1 // 当前页数
|
||||
PageSize = 20 // 每页多少条数据
|
||||
MaxPageSize = 100000 // 每次最多取多少条
|
||||
Page = 1 // 当前页数
|
||||
PageSize = 20 // 每页多少条数据
|
||||
MaxPageSize = 100000 // 每次最多取多少条
|
||||
)
|
||||
|
||||
const DefaultTimeFormat = "2006-01-02 15:04:05"
|
||||
|
||||
type BaseModel struct {
|
||||
Page int `xorm:"-"`
|
||||
PageSize int `xorm:"-"`
|
||||
type BaseModel struct {
|
||||
Page int `xorm:"-"`
|
||||
PageSize int `xorm:"-"`
|
||||
}
|
||||
|
||||
func (model *BaseModel) parsePageAndPageSize(params CommonMap) {
|
||||
page, ok := params["Page"]
|
||||
if ok {
|
||||
model.Page = page.(int)
|
||||
}
|
||||
pageSize, ok := params["PageSize"]
|
||||
if ok {
|
||||
model.PageSize = pageSize.(int)
|
||||
}
|
||||
if model.Page <= 0 {
|
||||
model.Page = Page
|
||||
}
|
||||
if model.PageSize <= 0 {
|
||||
model.PageSize = MaxPageSize
|
||||
}
|
||||
page, ok := params["Page"]
|
||||
if ok {
|
||||
model.Page = page.(int)
|
||||
}
|
||||
pageSize, ok := params["PageSize"]
|
||||
if ok {
|
||||
model.PageSize = pageSize.(int)
|
||||
}
|
||||
if model.Page <= 0 {
|
||||
model.Page = Page
|
||||
}
|
||||
if model.PageSize <= 0 {
|
||||
model.PageSize = MaxPageSize
|
||||
}
|
||||
}
|
||||
|
||||
func (model *BaseModel) pageLimitOffset() int {
|
||||
return (model.Page - 1) * model.PageSize
|
||||
return (model.Page - 1) * model.PageSize
|
||||
}
|
||||
|
||||
// 创建Db
|
||||
func CreateDb() *xorm.Engine {
|
||||
dsn := getDbEngineDSN(app.Setting)
|
||||
engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn)
|
||||
if err != nil {
|
||||
logger.Fatal("创建xorm引擎失败", err)
|
||||
}
|
||||
engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns)
|
||||
engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns)
|
||||
dsn := getDbEngineDSN(app.Setting)
|
||||
engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn)
|
||||
if err != nil {
|
||||
logger.Fatal("创建xorm引擎失败", err)
|
||||
}
|
||||
engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns)
|
||||
engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns)
|
||||
|
||||
if app.Setting.Db.Prefix != "" {
|
||||
// 设置表前缀
|
||||
TablePrefix = app.Setting.Db.Prefix
|
||||
mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix)
|
||||
engine.SetTableMapper(mapper)
|
||||
}
|
||||
// 本地环境开启日志
|
||||
if macaron.Env == macaron.DEV {
|
||||
engine.ShowSQL(true)
|
||||
engine.Logger().SetLevel(core.LOG_DEBUG)
|
||||
}
|
||||
if app.Setting.Db.Prefix != "" {
|
||||
// 设置表前缀
|
||||
TablePrefix = app.Setting.Db.Prefix
|
||||
mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix)
|
||||
engine.SetTableMapper(mapper)
|
||||
}
|
||||
// 本地环境开启日志
|
||||
if macaron.Env == macaron.DEV {
|
||||
engine.ShowSQL(true)
|
||||
engine.Logger().SetLevel(core.LOG_DEBUG)
|
||||
}
|
||||
|
||||
go keepDbAlived(engine)
|
||||
go keepDbAlived(engine)
|
||||
|
||||
return engine
|
||||
return engine
|
||||
}
|
||||
|
||||
// 创建临时数据库连接
|
||||
func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) {
|
||||
dsn := getDbEngineDSN(setting)
|
||||
func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) {
|
||||
dsn := getDbEngineDSN(setting)
|
||||
|
||||
return xorm.NewEngine(setting.Db.Engine, dsn)
|
||||
return xorm.NewEngine(setting.Db.Engine, dsn)
|
||||
}
|
||||
|
||||
// 获取数据库引擎DSN mysql,sqlite
|
||||
func getDbEngineDSN(setting *setting.Setting) string {
|
||||
engine := strings.ToLower(setting.Db.Engine)
|
||||
var dsn string = ""
|
||||
switch engine {
|
||||
case "mysql":
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
|
||||
setting.Db.User,
|
||||
setting.Db.Password,
|
||||
setting.Db.Host,
|
||||
setting.Db.Port ,
|
||||
setting.Db.Database,
|
||||
setting.Db.Charset)
|
||||
}
|
||||
engine := strings.ToLower(setting.Db.Engine)
|
||||
var dsn string = ""
|
||||
switch engine {
|
||||
case "mysql":
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
|
||||
setting.Db.User,
|
||||
setting.Db.Password,
|
||||
setting.Db.Host,
|
||||
setting.Db.Port,
|
||||
setting.Db.Database,
|
||||
setting.Db.Charset)
|
||||
}
|
||||
|
||||
return dsn
|
||||
return dsn
|
||||
}
|
||||
|
||||
func keepDbAlived(engine *xorm.Engine) {
|
||||
t := time.Tick(180 * time.Second)
|
||||
for {
|
||||
<- t
|
||||
engine.Ping()
|
||||
}
|
||||
}
|
||||
func keepDbAlived(engine *xorm.Engine) {
|
||||
t := time.Tick(180 * time.Second)
|
||||
for {
|
||||
<-t
|
||||
engine.Ping()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Setting struct {
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
Code string `xorm:"varchar(32) notnull"`
|
||||
Key string `xorm:"varchar(64) notnull"`
|
||||
Value string `xorm:"varchar(4096) notnull default '' "`
|
||||
type Setting struct {
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
Code string `xorm:"varchar(32) notnull"`
|
||||
Key string `xorm:"varchar(64) notnull"`
|
||||
Value string `xorm:"varchar(4096) notnull default '' "`
|
||||
}
|
||||
|
||||
const SlackCode = "slack"
|
||||
|
@ -21,154 +21,154 @@ const MailUserKey = "user"
|
|||
|
||||
// 初始化基本字段 邮件、slack等
|
||||
func (setting *Setting) InitBasicField() {
|
||||
setting.Code = SlackCode;
|
||||
setting.Key = SlackUrlKey
|
||||
Db.Insert(setting)
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackUrlKey
|
||||
Db.Insert(setting)
|
||||
|
||||
setting.Id = 0
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailServerKey
|
||||
Db.Insert(setting)
|
||||
setting.Id = 0
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailServerKey
|
||||
Db.Insert(setting)
|
||||
}
|
||||
|
||||
// region slack配置
|
||||
|
||||
type Slack struct {
|
||||
Url string
|
||||
Channels []Channel
|
||||
Url string
|
||||
Channels []Channel
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
Id int
|
||||
Name string
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
func (setting *Setting) Slack() (Slack, error) {
|
||||
list := make([]Setting, 0)
|
||||
err := Db.Where("code = ?", SlackCode).Find(&list)
|
||||
slack := Slack{Url:"", Channels:make([]Channel, 0)}
|
||||
if err != nil {
|
||||
return slack, err
|
||||
}
|
||||
func (setting *Setting) Slack() (Slack, error) {
|
||||
list := make([]Setting, 0)
|
||||
err := Db.Where("code = ?", SlackCode).Find(&list)
|
||||
slack := Slack{Url: "", Channels: make([]Channel, 0)}
|
||||
if err != nil {
|
||||
return slack, err
|
||||
}
|
||||
|
||||
setting.formatSlack(list, &slack)
|
||||
setting.formatSlack(list, &slack)
|
||||
|
||||
return slack, err
|
||||
return slack, err
|
||||
}
|
||||
|
||||
func (setting *Setting) formatSlack(list []Setting, slack *Slack) {
|
||||
for _, v := range list {
|
||||
if v.Key == SlackUrlKey {
|
||||
slack.Url = v.Value
|
||||
continue
|
||||
}
|
||||
func (setting *Setting) formatSlack(list []Setting, slack *Slack) {
|
||||
for _, v := range list {
|
||||
if v.Key == SlackUrlKey {
|
||||
slack.Url = v.Value
|
||||
continue
|
||||
}
|
||||
|
||||
slack.Channels = append(slack.Channels, Channel{
|
||||
v.Id, v.Value,
|
||||
})
|
||||
}
|
||||
slack.Channels = append(slack.Channels, Channel{
|
||||
v.Id, v.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 更新slack webhook url
|
||||
func (setting *Setting) UpdateSlackUrl(url string) (int64, error) {
|
||||
setting.Value = url
|
||||
setting.Value = url
|
||||
|
||||
return Db.Cols("value").Update(setting, Setting{Code:SlackCode, Key:SlackUrlKey})
|
||||
return Db.Cols("value").Update(setting, Setting{Code: SlackCode, Key: SlackUrlKey})
|
||||
}
|
||||
|
||||
|
||||
// 创建slack渠道
|
||||
func (setting *Setting) CreateChannel(channel string) (int64, error) {
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Value = channel
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Value = channel
|
||||
|
||||
return Db.Insert(setting)
|
||||
return Db.Insert(setting)
|
||||
}
|
||||
|
||||
func (setting *Setting) IsChannelExist(channel string) (bool) {
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Value = channel
|
||||
func (setting *Setting) IsChannelExist(channel string) bool {
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Value = channel
|
||||
|
||||
count, _ := Db.Count(setting)
|
||||
count, _ := Db.Count(setting)
|
||||
|
||||
return count > 0
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// 删除slack渠道
|
||||
func (setting *Setting) RemoveChannel(id int) (int64, error) {
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Id = id
|
||||
return Db.Delete(setting)
|
||||
func (setting *Setting) RemoveChannel(id int) (int64, error) {
|
||||
setting.Code = SlackCode
|
||||
setting.Key = SlackChannelKey
|
||||
setting.Id = id
|
||||
return Db.Delete(setting)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
type Mail struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
MailUsers []MailUser
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
MailUsers []MailUser
|
||||
}
|
||||
|
||||
type MailUser struct {
|
||||
Id int
|
||||
Username string
|
||||
Email string
|
||||
Id int
|
||||
Username string
|
||||
Email string
|
||||
}
|
||||
|
||||
// region 邮件配置
|
||||
func (setting *Setting) Mail() (Mail, error) {
|
||||
list := make([]Setting, 0)
|
||||
err := Db.Where("code = ?", MailCode).Find(&list)
|
||||
mail := Mail{MailUsers:make([]MailUser, 0)}
|
||||
if err != nil {
|
||||
return mail, err
|
||||
}
|
||||
func (setting *Setting) Mail() (Mail, error) {
|
||||
list := make([]Setting, 0)
|
||||
err := Db.Where("code = ?", MailCode).Find(&list)
|
||||
mail := Mail{MailUsers: make([]MailUser, 0)}
|
||||
if err != nil {
|
||||
return mail, err
|
||||
}
|
||||
|
||||
setting.formatMail(list, &mail)
|
||||
setting.formatMail(list, &mail)
|
||||
|
||||
return mail, err
|
||||
return mail, err
|
||||
}
|
||||
|
||||
func (setting *Setting) formatMail(list []Setting, mail *Mail) {
|
||||
mailUser := MailUser{}
|
||||
for _, v := range list {
|
||||
if v.Key == MailServerKey {
|
||||
json.Unmarshal([]byte(v.Value), mail)
|
||||
continue
|
||||
}
|
||||
json.Unmarshal([]byte(v.Value), &mailUser)
|
||||
mailUser.Id = v.Id
|
||||
mail.MailUsers = append(mail.MailUsers, mailUser)
|
||||
}
|
||||
func (setting *Setting) formatMail(list []Setting, mail *Mail) {
|
||||
mailUser := MailUser{}
|
||||
for _, v := range list {
|
||||
if v.Key == MailServerKey {
|
||||
json.Unmarshal([]byte(v.Value), mail)
|
||||
continue
|
||||
}
|
||||
json.Unmarshal([]byte(v.Value), &mailUser)
|
||||
mailUser.Id = v.Id
|
||||
mail.MailUsers = append(mail.MailUsers, mailUser)
|
||||
}
|
||||
}
|
||||
|
||||
func (setting *Setting) UpdateMailServer(config string) (int64, error) {
|
||||
setting.Value = config
|
||||
return Db.Cols("value").Update(setting, Setting{Code:MailCode, Key:MailServerKey})
|
||||
func (setting *Setting) UpdateMailServer(config string) (int64, error) {
|
||||
setting.Value = config
|
||||
return Db.Cols("value").Update(setting, Setting{Code: MailCode, Key: MailServerKey})
|
||||
}
|
||||
|
||||
func (setting *Setting) CreateMailUser(username, email string) (int64, error) {
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailUserKey
|
||||
mailUser := MailUser{0, username, email}
|
||||
jsonByte, err := json.Marshal(mailUser)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
setting.Value = string(jsonByte)
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailUserKey
|
||||
mailUser := MailUser{0, username, email}
|
||||
jsonByte, err := json.Marshal(mailUser)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
setting.Value = string(jsonByte)
|
||||
|
||||
return Db.Insert(setting)
|
||||
return Db.Insert(setting)
|
||||
}
|
||||
|
||||
func (setting *Setting) RemoveMailUser(id int) (int64, error) {
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailUserKey
|
||||
setting.Id = id
|
||||
return Db.Delete(setting)
|
||||
func (setting *Setting) RemoveMailUser(id int) (int64, error) {
|
||||
setting.Code = MailCode
|
||||
setting.Key = MailUserKey
|
||||
setting.Id = id
|
||||
return Db.Delete(setting)
|
||||
}
|
||||
// endregion
|
||||
|
||||
// endregion
|
||||
|
|
341
models/task.go
341
models/task.go
|
@ -1,272 +1,271 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/go-xorm/xorm"
|
||||
"errors"
|
||||
"strings"
|
||||
"errors"
|
||||
"github.com/go-xorm/xorm"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskProtocol int8
|
||||
|
||||
const (
|
||||
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
||||
TaskRPC // RPC方式执行命令
|
||||
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
||||
TaskRPC // RPC方式执行命令
|
||||
)
|
||||
|
||||
type TaskLevel int8
|
||||
|
||||
const (
|
||||
TaskLevelParent TaskLevel = 1 // 父任务
|
||||
TaskLevelChild TaskLevel = 2 // 子任务(依赖任务)
|
||||
TaskLevelParent TaskLevel = 1 // 父任务
|
||||
TaskLevelChild TaskLevel = 2 // 子任务(依赖任务)
|
||||
)
|
||||
|
||||
type TaskDependencyStatus int8
|
||||
|
||||
const (
|
||||
TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖
|
||||
TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖
|
||||
TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖
|
||||
TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖
|
||||
)
|
||||
|
||||
// 任务
|
||||
type Task struct {
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||
Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务
|
||||
DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔
|
||||
DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖
|
||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令
|
||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
||||
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
|
||||
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
|
||||
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
||||
Tag string `xorm:"varchar(32) notnull default ''"`
|
||||
Remark string `xorm:"varchar(100) notnull default ''"` // 备注
|
||||
Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止
|
||||
Created time.Time `xorm:"datetime notnull created"` // 创建时间
|
||||
Deleted time.Time `xorm:"datetime deleted"` // 删除时间
|
||||
BaseModel `xorm:"-"`
|
||||
Hosts []TaskHostDetail `xorm:"-"`
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||
Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务
|
||||
DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔
|
||||
DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖
|
||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令
|
||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
||||
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
|
||||
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
|
||||
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
||||
Tag string `xorm:"varchar(32) notnull default ''"`
|
||||
Remark string `xorm:"varchar(100) notnull default ''"` // 备注
|
||||
Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止
|
||||
Created time.Time `xorm:"datetime notnull created"` // 创建时间
|
||||
Deleted time.Time `xorm:"datetime deleted"` // 删除时间
|
||||
BaseModel `xorm:"-"`
|
||||
Hosts []TaskHostDetail `xorm:"-"`
|
||||
}
|
||||
|
||||
func taskHostTableName() []string {
|
||||
return []string{TablePrefix + "task_host", "th"}
|
||||
return []string{TablePrefix + "task_host", "th"}
|
||||
}
|
||||
|
||||
// 新增
|
||||
func (task *Task) Create() (insertId int, err error) {
|
||||
_, err = Db.Insert(task)
|
||||
if err == nil {
|
||||
insertId = task.Id
|
||||
}
|
||||
_, err = Db.Insert(task)
|
||||
if err == nil {
|
||||
insertId = task.Id
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 新增测试任务
|
||||
func (task *Task) CreateTestTask() {
|
||||
// HTTP任务
|
||||
task.Name = "测试HTTP任务"
|
||||
task.Level = TaskLevelParent
|
||||
task.Protocol = TaskHTTP
|
||||
task.Spec = "*/30 * * * * *"
|
||||
task.Tag = "test-task"
|
||||
// 查询IP地址区域信息
|
||||
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
|
||||
task.Status = Enabled
|
||||
task.Create()
|
||||
// HTTP任务
|
||||
task.Name = "测试HTTP任务"
|
||||
task.Level = TaskLevelParent
|
||||
task.Protocol = TaskHTTP
|
||||
task.Spec = "*/30 * * * * *"
|
||||
task.Tag = "test-task"
|
||||
// 查询IP地址区域信息
|
||||
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
|
||||
task.Status = Enabled
|
||||
task.Create()
|
||||
}
|
||||
|
||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
||||
return Db.ID(id).
|
||||
Cols("name,spec,protocol,command,timeout,multi,retry_times,remark,notify_status,notify_type,notify_receiver_id, dependency_task_id, dependency_status, tag").
|
||||
Update(task)
|
||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
||||
return Db.ID(id).
|
||||
Cols("name,spec,protocol,command,timeout,multi,retry_times,remark,notify_status,notify_type,notify_receiver_id, dependency_task_id, dependency_status, tag").
|
||||
Update(task)
|
||||
}
|
||||
|
||||
// 更新
|
||||
func (task *Task) Update(id int, data CommonMap) (int64, error) {
|
||||
return Db.Table(task).ID(id).Update(data)
|
||||
return Db.Table(task).ID(id).Update(data)
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (task *Task) Delete(id int) (int64, error) {
|
||||
return Db.Id(id).Delete(task)
|
||||
return Db.Id(id).Delete(task)
|
||||
}
|
||||
|
||||
// 禁用
|
||||
func (task *Task) Disable(id int) (int64, error) {
|
||||
return task.Update(id, CommonMap{"status": Disabled})
|
||||
return task.Update(id, CommonMap{"status": Disabled})
|
||||
}
|
||||
|
||||
// 激活
|
||||
func (task *Task) Enable(id int) (int64, error) {
|
||||
return task.Update(id, CommonMap{"status": Enabled})
|
||||
return task.Update(id, CommonMap{"status": Enabled})
|
||||
}
|
||||
|
||||
// 获取所有激活任务
|
||||
func (task *Task) ActiveList() ([]Task, error) {
|
||||
list := make([]Task, 0)
|
||||
err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
||||
Find(&list)
|
||||
list := make([]Task, 0)
|
||||
err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
||||
Find(&list)
|
||||
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
return task.setHostsForTasks(list)
|
||||
return task.setHostsForTasks(list)
|
||||
}
|
||||
|
||||
// 获取某个主机下的所有激活任务
|
||||
func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) {
|
||||
taskHostModel := new(TaskHost)
|
||||
taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]Task, 0)
|
||||
err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
||||
In("id", taskIds...).
|
||||
Find(&list)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
taskHostModel := new(TaskHost)
|
||||
taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]Task, 0)
|
||||
err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
||||
In("id", taskIds...).
|
||||
Find(&list)
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
return task.setHostsForTasks(list)
|
||||
return task.setHostsForTasks(list)
|
||||
}
|
||||
|
||||
func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) {
|
||||
taskHostModel := new(TaskHost)
|
||||
var err error
|
||||
for i, value := range tasks {
|
||||
taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tasks[i].Hosts = taskHostDetails
|
||||
}
|
||||
taskHostModel := new(TaskHost)
|
||||
var err error
|
||||
for i, value := range tasks {
|
||||
taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tasks[i].Hosts = taskHostDetails
|
||||
}
|
||||
|
||||
return tasks, err
|
||||
return tasks, err
|
||||
}
|
||||
|
||||
// 判断任务名称是否存在
|
||||
func (task *Task) NameExist(name string, id int) (bool, error) {
|
||||
if id > 0 {
|
||||
count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task);
|
||||
return count > 0, err
|
||||
}
|
||||
count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task);
|
||||
func (task *Task) NameExist(name string, id int) (bool, error) {
|
||||
if id > 0 {
|
||||
count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task)
|
||||
return count > 0, err
|
||||
}
|
||||
count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task)
|
||||
|
||||
return count > 0, err
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
func (task *Task) GetStatus(id int) (Status, error) {
|
||||
exist, err := Db.Id(id).Get(task)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !exist {
|
||||
return 0, errors.New("not exist")
|
||||
}
|
||||
exist, err := Db.Id(id).Get(task)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !exist {
|
||||
return 0, errors.New("not exist")
|
||||
}
|
||||
|
||||
return task.Status, nil
|
||||
return task.Status, nil
|
||||
}
|
||||
|
||||
func(task *Task) Detail(id int) (Task, error) {
|
||||
t := Task{}
|
||||
_, err := Db.Where("id=?", id).Get(&t)
|
||||
func (task *Task) Detail(id int) (Task, error) {
|
||||
t := Task{}
|
||||
_, err := Db.Where("id=?", id).Get(&t)
|
||||
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
taskHostModel := new(TaskHost)
|
||||
t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id)
|
||||
taskHostModel := new(TaskHost)
|
||||
t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id)
|
||||
|
||||
return t, err
|
||||
return t, err
|
||||
}
|
||||
|
||||
func (task *Task) List(params CommonMap) ([]Task, error) {
|
||||
task.parsePageAndPageSize(params)
|
||||
list := make([]Task, 0)
|
||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||
task.parseWhere(session, params)
|
||||
err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
||||
task.parsePageAndPageSize(params)
|
||||
list := make([]Task, 0)
|
||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||
task.parseWhere(session, params)
|
||||
err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return task.setHostsForTasks(list)
|
||||
return task.setHostsForTasks(list)
|
||||
}
|
||||
|
||||
// 获取依赖任务列表
|
||||
func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) {
|
||||
list := make([]Task, 0)
|
||||
if ids == "" {
|
||||
return list, nil
|
||||
}
|
||||
idList := strings.Split(ids, ",")
|
||||
taskIds := make([]interface{}, len(idList))
|
||||
for i, v := range idList {
|
||||
taskIds[i] = v
|
||||
}
|
||||
fields := "t.*"
|
||||
err := Db.Alias("t").
|
||||
Where("t.level = ?", TaskLevelChild).
|
||||
In("t.id", taskIds).
|
||||
Cols(fields).
|
||||
Find(&list)
|
||||
list := make([]Task, 0)
|
||||
if ids == "" {
|
||||
return list, nil
|
||||
}
|
||||
idList := strings.Split(ids, ",")
|
||||
taskIds := make([]interface{}, len(idList))
|
||||
for i, v := range idList {
|
||||
taskIds[i] = v
|
||||
}
|
||||
fields := "t.*"
|
||||
err := Db.Alias("t").
|
||||
Where("t.level = ?", TaskLevelChild).
|
||||
In("t.id", taskIds).
|
||||
Cols(fields).
|
||||
Find(&list)
|
||||
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
if err != nil {
|
||||
return list, err
|
||||
}
|
||||
|
||||
return task.setHostsForTasks(list)
|
||||
return task.setHostsForTasks(list)
|
||||
}
|
||||
|
||||
func (task *Task) Total(params CommonMap) (int64, error) {
|
||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||
task.parseWhere(session, params)
|
||||
list := make([]Task, 0)
|
||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||
task.parseWhere(session, params)
|
||||
list := make([]Task, 0)
|
||||
|
||||
err := session.GroupBy("t.id").Find(&list)
|
||||
err := session.GroupBy("t.id").Find(&list)
|
||||
|
||||
return int64(len(list)), err
|
||||
return int64(len(list)), err
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("t.id = ?", id)
|
||||
}
|
||||
hostId, ok := params["HostId"]
|
||||
if ok && hostId.(int) > 0 {
|
||||
session.And("th.host_id = ?", hostId)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("t.name LIKE ?", "%" + name.(string) + "%")
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
session.And("protocol = ?", protocol)
|
||||
}
|
||||
status, ok := params["Status"]
|
||||
if ok && status.(int) > -1 {
|
||||
session.And("status = ?", status)
|
||||
}
|
||||
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
id, ok := params["Id"]
|
||||
if ok && id.(int) > 0 {
|
||||
session.And("t.id = ?", id)
|
||||
}
|
||||
hostId, ok := params["HostId"]
|
||||
if ok && hostId.(int) > 0 {
|
||||
session.And("th.host_id = ?", hostId)
|
||||
}
|
||||
name, ok := params["Name"]
|
||||
if ok && name.(string) != "" {
|
||||
session.And("t.name LIKE ?", "%"+name.(string)+"%")
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
session.And("protocol = ?", protocol)
|
||||
}
|
||||
status, ok := params["Status"]
|
||||
if ok && status.(int) > -1 {
|
||||
session.And("status = ?", status)
|
||||
}
|
||||
|
||||
tag, ok := params["Tag"]
|
||||
if ok && tag.(string) != "" {
|
||||
session.And("tag = ? ", tag)
|
||||
}
|
||||
tag, ok := params["Tag"]
|
||||
if ok && tag.(string) != "" {
|
||||
session.And("tag = ? ", tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,81 +1,80 @@
|
|||
package models
|
||||
|
||||
|
||||
type TaskHost struct {
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
TaskId int `xorm:"int not null index"`
|
||||
HostId int16 `xorm:"smallint not null index"`
|
||||
Id int `xorm:"int pk autoincr"`
|
||||
TaskId int `xorm:"int not null index"`
|
||||
HostId int16 `xorm:"smallint not null index"`
|
||||
}
|
||||
|
||||
type TaskHostDetail struct {
|
||||
TaskHost `xorm:"extends"`
|
||||
Name string
|
||||
Port int
|
||||
Alias string
|
||||
TaskHost `xorm:"extends"`
|
||||
Name string
|
||||
Port int
|
||||
Alias string
|
||||
}
|
||||
|
||||
func (TaskHostDetail) TableName() string {
|
||||
return TablePrefix + "task_host"
|
||||
func (TaskHostDetail) TableName() string {
|
||||
return TablePrefix + "task_host"
|
||||
}
|
||||
|
||||
func hostTableName() []string {
|
||||
return []string{TablePrefix + "host", "h"}
|
||||
return []string{TablePrefix + "host", "h"}
|
||||
}
|
||||
|
||||
func (th *TaskHost) Remove(taskId int) error {
|
||||
_, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost))
|
||||
_, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost))
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
func (th *TaskHost) Add(taskId int, hostIds []int) error {
|
||||
|
||||
err := th.Remove(taskId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := th.Remove(taskId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
taskHosts := make([]TaskHost, len(hostIds))
|
||||
for i, value := range hostIds {
|
||||
taskHosts[i].TaskId = taskId
|
||||
taskHosts[i].HostId = int16(value)
|
||||
}
|
||||
taskHosts := make([]TaskHost, len(hostIds))
|
||||
for i, value := range hostIds {
|
||||
taskHosts[i].TaskId = taskId
|
||||
taskHosts[i].HostId = int16(value)
|
||||
}
|
||||
|
||||
_, err = Db.Insert(&taskHosts)
|
||||
_, err = Db.Insert(&taskHosts)
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
|
||||
list := make([]TaskHostDetail, 0)
|
||||
fields := "th.id,th.host_id,h.alias,h.name,h.port"
|
||||
err := Db.Alias("th").
|
||||
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
||||
Where("th.task_id = ?", taskId).
|
||||
Cols(fields).
|
||||
Find(&list)
|
||||
list := make([]TaskHostDetail, 0)
|
||||
fields := "th.id,th.host_id,h.alias,h.name,h.port"
|
||||
err := Db.Alias("th").
|
||||
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
||||
Where("th.task_id = ?", taskId).
|
||||
Cols(fields).
|
||||
Find(&list)
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) {
|
||||
list := make([]TaskHost, 0)
|
||||
err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) {
|
||||
list := make([]TaskHost, 0)
|
||||
err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
taskIds := make([]interface{}, len(list))
|
||||
for i, value := range list {
|
||||
taskIds[i] = value.TaskId
|
||||
}
|
||||
taskIds := make([]interface{}, len(list))
|
||||
for i, value := range list {
|
||||
taskIds[i] = value.TaskId
|
||||
}
|
||||
|
||||
return taskIds, err
|
||||
return taskIds, err
|
||||
}
|
||||
|
||||
// 判断主机id是否有引用
|
||||
func (th *TaskHost) HostIdExist(hostId int16) (bool, error) {
|
||||
count, err := Db.Where("host_id = ?", hostId).Count(th);
|
||||
count, err := Db.Where("host_id = ?", hostId).Count(th)
|
||||
|
||||
return count > 0, err
|
||||
}
|
||||
return count > 0, err
|
||||
}
|
||||
|
|
|
@ -1,99 +1,98 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/go-xorm/xorm"
|
||||
"github.com/go-xorm/xorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskType int8
|
||||
|
||||
|
||||
// 任务执行日志
|
||||
type TaskLog struct {
|
||||
Id int64 `xorm:"bigint pk autoincr"`
|
||||
TaskId int `xorm:"int notnull index default 0"` // 任务id
|
||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC
|
||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
|
||||
Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔
|
||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
||||
Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
||||
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
|
||||
TotalTime int `xorm:"-"` // 执行总时长
|
||||
BaseModel `xorm:"-"`
|
||||
Id int64 `xorm:"bigint pk autoincr"`
|
||||
TaskId int `xorm:"int notnull index default 0"` // 任务id
|
||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC
|
||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
|
||||
Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔
|
||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
||||
Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
||||
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
|
||||
TotalTime int `xorm:"-"` // 执行总时长
|
||||
BaseModel `xorm:"-"`
|
||||
}
|
||||
|
||||
func (taskLog *TaskLog) Create() (insertId int64, err error) {
|
||||
_, err = Db.Insert(taskLog)
|
||||
if err == nil {
|
||||
insertId = taskLog.Id
|
||||
}
|
||||
_, err = Db.Insert(taskLog)
|
||||
if err == nil {
|
||||
insertId = taskLog.Id
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 更新
|
||||
func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) {
|
||||
return Db.Table(taskLog).ID(id).Update(data)
|
||||
return Db.Table(taskLog).ID(id).Update(data)
|
||||
}
|
||||
|
||||
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
||||
taskLog.parsePageAndPageSize(params)
|
||||
list := make([]TaskLog, 0)
|
||||
session := Db.Desc("id")
|
||||
taskLog.parseWhere(session, params)
|
||||
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||
if len(list) > 0 {
|
||||
for i, item := range list {
|
||||
endTime := item.EndTime
|
||||
if item.Status == Running {
|
||||
endTime = time.Now()
|
||||
}
|
||||
execSeconds := endTime.Sub(item.StartTime).Seconds()
|
||||
list[i].TotalTime = int(execSeconds)
|
||||
}
|
||||
}
|
||||
taskLog.parsePageAndPageSize(params)
|
||||
list := make([]TaskLog, 0)
|
||||
session := Db.Desc("id")
|
||||
taskLog.parseWhere(session, params)
|
||||
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||
if len(list) > 0 {
|
||||
for i, item := range list {
|
||||
endTime := item.EndTime
|
||||
if item.Status == Running {
|
||||
endTime = time.Now()
|
||||
}
|
||||
execSeconds := endTime.Sub(item.StartTime).Seconds()
|
||||
list[i].TotalTime = int(execSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
// 清空表
|
||||
func (taskLog *TaskLog) Clear() (int64, error) {
|
||||
return Db.Where("1=1").Delete(taskLog);
|
||||
func (taskLog *TaskLog) Clear() (int64, error) {
|
||||
return Db.Where("1=1").Delete(taskLog)
|
||||
}
|
||||
|
||||
// 删除N个月前的日志
|
||||
func (taskLog *TaskLog) Remove(id int) (int64, error) {
|
||||
t := time.Now().AddDate(0, -id, 0)
|
||||
return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog)
|
||||
t := time.Now().AddDate(0, -id, 0)
|
||||
return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog)
|
||||
}
|
||||
|
||||
func (taskLog *TaskLog) Total(params CommonMap) (int64, error) {
|
||||
session := Db.NewSession()
|
||||
defer session.Close()
|
||||
taskLog.parseWhere(session, params)
|
||||
return session.Count(taskLog)
|
||||
session := Db.NewSession()
|
||||
defer session.Close()
|
||||
taskLog.parseWhere(session, params)
|
||||
return session.Count(taskLog)
|
||||
}
|
||||
|
||||
// 解析where
|
||||
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
taskId, ok := params["TaskId"]
|
||||
if ok && taskId.(int) > 0 {
|
||||
session.And("task_id = ?", taskId)
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
session.And("protocol = ?", protocol)
|
||||
}
|
||||
status, ok := params["Status"]
|
||||
if ok && status.(int) > -1 {
|
||||
session.And("status = ?", status)
|
||||
}
|
||||
}
|
||||
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
|
||||
if len(params) == 0 {
|
||||
return
|
||||
}
|
||||
taskId, ok := params["TaskId"]
|
||||
if ok && taskId.(int) > 0 {
|
||||
session.And("task_id = ?", taskId)
|
||||
}
|
||||
protocol, ok := params["Protocol"]
|
||||
if ok && protocol.(int) > 0 {
|
||||
session.And("protocol = ?", protocol)
|
||||
}
|
||||
status, ok := params["Status"]
|
||||
if ok && status.(int) > -1 {
|
||||
session.And("status = ?", status)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,110 +1,110 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
const PasswordSaltLength = 6
|
||||
|
||||
// 用户model
|
||||
type User struct {
|
||||
Id int `xorm:"pk autoincr notnull "`
|
||||
Name string `xorm:"varchar(32) notnull unique"` // 用户名
|
||||
Password string `xorm:"char(32) notnull "` // 密码
|
||||
Salt string `xorm:"char(6) notnull "` // 密码盐值
|
||||
Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱
|
||||
Created time.Time `xorm:"datetime notnull created"`
|
||||
Updated time.Time `xorm:"datetime updated"`
|
||||
Deleted time.Time `xorm:"datetime deleted"`
|
||||
IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
|
||||
Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
|
||||
BaseModel `xorm:"-"`
|
||||
Id int `xorm:"pk autoincr notnull "`
|
||||
Name string `xorm:"varchar(32) notnull unique"` // 用户名
|
||||
Password string `xorm:"char(32) notnull "` // 密码
|
||||
Salt string `xorm:"char(6) notnull "` // 密码盐值
|
||||
Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱
|
||||
Created time.Time `xorm:"datetime notnull created"`
|
||||
Updated time.Time `xorm:"datetime updated"`
|
||||
Deleted time.Time `xorm:"datetime deleted"`
|
||||
IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
|
||||
Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
|
||||
BaseModel `xorm:"-"`
|
||||
}
|
||||
|
||||
// 新增
|
||||
func (user *User) Create() (insertId int, err error) {
|
||||
user.Status = Enabled
|
||||
user.Salt = user.generateSalt()
|
||||
user.Password = user.encryptPassword(user.Password, user.Salt)
|
||||
user.Status = Enabled
|
||||
user.Salt = user.generateSalt()
|
||||
user.Password = user.encryptPassword(user.Password, user.Salt)
|
||||
|
||||
_, err = Db.Insert(user)
|
||||
if err == nil {
|
||||
insertId = user.Id
|
||||
}
|
||||
_, err = Db.Insert(user)
|
||||
if err == nil {
|
||||
insertId = user.Id
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// 更新
|
||||
func (user *User) Update(id int, data CommonMap) (int64, error) {
|
||||
return Db.Table(user).ID(id).Update(data)
|
||||
return Db.Table(user).ID(id).Update(data)
|
||||
}
|
||||
|
||||
func (user *User) UpdatePassword(id int, password string) (int64, error) {
|
||||
salt := user.generateSalt()
|
||||
safePassword := user.encryptPassword(password, salt)
|
||||
func (user *User) UpdatePassword(id int, password string) (int64, error) {
|
||||
salt := user.generateSalt()
|
||||
safePassword := user.encryptPassword(password, salt)
|
||||
|
||||
return user.Update(id, CommonMap{"password": safePassword, "salt": salt})
|
||||
return user.Update(id, CommonMap{"password": safePassword, "salt": salt})
|
||||
}
|
||||
|
||||
// 删除
|
||||
func (user *User) Delete(id int) (int64, error) {
|
||||
return Db.Id(id).Delete(user)
|
||||
return Db.Id(id).Delete(user)
|
||||
}
|
||||
|
||||
// 禁用
|
||||
func (user *User) Disable(id int) (int64, error) {
|
||||
return user.Update(id, CommonMap{"status": Disabled})
|
||||
return user.Update(id, CommonMap{"status": Disabled})
|
||||
}
|
||||
|
||||
// 激活
|
||||
func (user *User) Enable(id int) (int64, error) {
|
||||
return user.Update(id, CommonMap{"status": Enabled})
|
||||
return user.Update(id, CommonMap{"status": Enabled})
|
||||
}
|
||||
|
||||
// 验证用户名和密码
|
||||
func (user *User) Match(username, password string) bool {
|
||||
where := "(name = ? OR email = ?)"
|
||||
_, err := Db.Where(where, username, username).Get(user)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
hashPassword := user.encryptPassword(password, user.Salt)
|
||||
if hashPassword != user.Password {
|
||||
return false
|
||||
}
|
||||
where := "(name = ? OR email = ?)"
|
||||
_, err := Db.Where(where, username, username).Get(user)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
hashPassword := user.encryptPassword(password, user.Salt)
|
||||
if hashPassword != user.Password {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
|
||||
// 用户名是否存在
|
||||
func (user *User) UsernameExists(username string) (int64, error) {
|
||||
return Db.Where("name = ?", username).Count(user)
|
||||
return Db.Where("name = ?", username).Count(user)
|
||||
}
|
||||
|
||||
// 邮箱地址是否存在
|
||||
func (user *User) EmailExists(email string) (int64, error) {
|
||||
return Db.Where("email = ?", email).Count(user)
|
||||
return Db.Where("email = ?", email).Count(user)
|
||||
}
|
||||
|
||||
func (user *User) List() ([]User, error) {
|
||||
list := make([]User, 0)
|
||||
err := Db.Desc("id").Find(&list)
|
||||
list := make([]User, 0)
|
||||
err := Db.Desc("id").Find(&list)
|
||||
|
||||
return list, err
|
||||
return list, err
|
||||
}
|
||||
|
||||
func (user *User) Total() (int64, error) {
|
||||
return Db.Count(user)
|
||||
return Db.Count(user)
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
func (user *User) encryptPassword(password, salt string) string {
|
||||
return utils.Md5(password + salt)
|
||||
return utils.Md5(password + salt)
|
||||
}
|
||||
|
||||
// 生成密码盐值
|
||||
func (user *User) generateSalt() string {
|
||||
return utils.RandString(PasswordSaltLength)
|
||||
return utils.RandString(PasswordSaltLength)
|
||||
}
|
||||
|
|
|
@ -1,117 +1,116 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os"
|
||||
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
AppDir string // 应用根目录
|
||||
ConfDir string // 配置目录
|
||||
LogDir string // 日志目录
|
||||
DataDir string // 存放session等
|
||||
AppConfig string // 应用配置文件
|
||||
Installed bool // 应用是否安装过
|
||||
Setting *setting.Setting // 应用配置
|
||||
VersionId int // 版本号
|
||||
VersionFile string // 版本号文件
|
||||
AppDir string // 应用根目录
|
||||
ConfDir string // 配置目录
|
||||
LogDir string // 日志目录
|
||||
DataDir string // 存放session等
|
||||
AppConfig string // 应用配置文件
|
||||
Installed bool // 应用是否安装过
|
||||
Setting *setting.Setting // 应用配置
|
||||
VersionId int // 版本号
|
||||
VersionFile string // 版本号文件
|
||||
)
|
||||
|
||||
|
||||
func InitEnv(versionString string) {
|
||||
logger.InitLogger()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
AppDir = wd
|
||||
ConfDir = AppDir + "/conf"
|
||||
LogDir = AppDir + "/log"
|
||||
DataDir = AppDir + "/data"
|
||||
AppConfig = ConfDir + "/app.ini"
|
||||
VersionFile = ConfDir + "/.version"
|
||||
checkDirExists(ConfDir, LogDir, DataDir)
|
||||
Installed = IsInstalled()
|
||||
VersionId = ToNumberVersion(versionString)
|
||||
logger.InitLogger()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
AppDir = wd
|
||||
ConfDir = AppDir + "/conf"
|
||||
LogDir = AppDir + "/log"
|
||||
DataDir = AppDir + "/data"
|
||||
AppConfig = ConfDir + "/app.ini"
|
||||
VersionFile = ConfDir + "/.version"
|
||||
checkDirExists(ConfDir, LogDir, DataDir)
|
||||
Installed = IsInstalled()
|
||||
VersionId = ToNumberVersion(versionString)
|
||||
}
|
||||
|
||||
// 判断应用是否已安装
|
||||
func IsInstalled() bool {
|
||||
_, err := os.Stat(ConfDir + "/install.lock")
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(ConfDir + "/install.lock")
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
|
||||
// 创建安装锁文件
|
||||
func CreateInstallLock() error {
|
||||
_, err := os.Create(ConfDir + "/install.lock")
|
||||
if err != nil {
|
||||
logger.Error("创建安装锁文件conf/install.lock失败")
|
||||
}
|
||||
_, err := os.Create(ConfDir + "/install.lock")
|
||||
if err != nil {
|
||||
logger.Error("创建安装锁文件conf/install.lock失败")
|
||||
}
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新应用版本号文件
|
||||
func UpdateVersionFile() {
|
||||
err := ioutil.WriteFile(VersionFile,
|
||||
[]byte(strconv.Itoa(VersionId)),
|
||||
0644,
|
||||
)
|
||||
func UpdateVersionFile() {
|
||||
err := ioutil.WriteFile(VersionFile,
|
||||
[]byte(strconv.Itoa(VersionId)),
|
||||
0644,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取应用当前版本号, 从版本号文件中读取
|
||||
func GetCurrentVersionId() int {
|
||||
if !utils.FileExist(VersionFile) {
|
||||
return 0;
|
||||
}
|
||||
if !utils.FileExist(VersionFile) {
|
||||
return 0
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(VersionFile)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
bytes, err := ioutil.ReadFile(VersionFile)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes)))
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes)))
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
return versionId
|
||||
return versionId
|
||||
}
|
||||
|
||||
// 把字符串版本号a.b.c转换为整数版本号abc
|
||||
func ToNumberVersion(versionString string) int {
|
||||
v := strings.Replace(versionString, ".", "", -1)
|
||||
if len(v) < 3 {
|
||||
v += "0"
|
||||
}
|
||||
func ToNumberVersion(versionString string) int {
|
||||
v := strings.Replace(versionString, ".", "", -1)
|
||||
if len(v) < 3 {
|
||||
v += "0"
|
||||
}
|
||||
|
||||
versionId, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
versionId, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
return versionId
|
||||
return versionId
|
||||
}
|
||||
|
||||
// 检测目录是否存在
|
||||
func checkDirExists(path ...string) {
|
||||
for _, value := range path {
|
||||
if !utils.FileExist(value) {
|
||||
logger.Fatal(value + "目录不存在或无权限访问")
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, value := range path {
|
||||
if !utils.FileExist(value) {
|
||||
logger.Fatal(value + "目录不存在或无权限访问")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,81 +3,81 @@ package httpclient
|
|||
// http-client
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
"fmt"
|
||||
"bytes"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ResponseWrapper struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
Header http.Header
|
||||
type ResponseWrapper struct {
|
||||
StatusCode int
|
||||
Body string
|
||||
Header http.Header
|
||||
}
|
||||
|
||||
func Get(url string, timeout int) ResponseWrapper {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
|
||||
return request(req, timeout)
|
||||
return request(req, timeout)
|
||||
}
|
||||
|
||||
func PostParams(url string,params string, timeout int) ResponseWrapper {
|
||||
buf := bytes.NewBufferString(params)
|
||||
req, err := http.NewRequest("POST", url, buf)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||
func PostParams(url string, params string, timeout int) ResponseWrapper {
|
||||
buf := bytes.NewBufferString(params)
|
||||
req, err := http.NewRequest("POST", url, buf)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
return request(req, timeout)
|
||||
return request(req, timeout)
|
||||
}
|
||||
|
||||
func PostJson(url string, body string, timeout int) ResponseWrapper {
|
||||
buf := bytes.NewBufferString(body)
|
||||
req, err := http.NewRequest("POST", url, buf)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
req.Header.Set("Content-type", "application/json")
|
||||
buf := bytes.NewBufferString(body)
|
||||
req, err := http.NewRequest("POST", url, buf)
|
||||
if err != nil {
|
||||
return createRequestError(err)
|
||||
}
|
||||
req.Header.Set("Content-type", "application/json")
|
||||
|
||||
return request(req, timeout)
|
||||
return request(req, timeout)
|
||||
}
|
||||
|
||||
func request(req *http.Request, timeout int) ResponseWrapper {
|
||||
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
|
||||
client := &http.Client{}
|
||||
if timeout > 0 {
|
||||
client.Timeout = time.Duration(timeout) * time.Second
|
||||
}
|
||||
setRequestHeader(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
wrapper.StatusCode = resp.StatusCode
|
||||
wrapper.Body = string(body)
|
||||
wrapper.Header = resp.Header
|
||||
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
|
||||
client := &http.Client{}
|
||||
if timeout > 0 {
|
||||
client.Timeout = time.Duration(timeout) * time.Second
|
||||
}
|
||||
setRequestHeader(req)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
|
||||
return wrapper
|
||||
}
|
||||
wrapper.StatusCode = resp.StatusCode
|
||||
wrapper.Body = string(body)
|
||||
wrapper.Header = resp.Header
|
||||
|
||||
return wrapper
|
||||
return wrapper
|
||||
}
|
||||
|
||||
func setRequestHeader(req *http.Request) {
|
||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron")
|
||||
func setRequestHeader(req *http.Request) {
|
||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron")
|
||||
}
|
||||
|
||||
func createRequestError(err error) ResponseWrapper {
|
||||
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
|
||||
return ResponseWrapper{0, errorMessage, make(http.Header)}
|
||||
}
|
||||
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
|
||||
return ResponseWrapper{0, errorMessage, make(http.Header)}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"github.com/cihub/seelog"
|
||||
"gopkg.in/macaron.v1"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"fmt"
|
||||
"github.com/cihub/seelog"
|
||||
"gopkg.in/macaron.v1"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// 日志库
|
||||
|
@ -22,113 +22,113 @@ const (
|
|||
FATAL
|
||||
)
|
||||
|
||||
func InitLogger() {
|
||||
config := getLogConfig()
|
||||
l, err := seelog.LoggerFromConfigAsString(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logger = l
|
||||
func InitLogger() {
|
||||
config := getLogConfig()
|
||||
l, err := seelog.LoggerFromConfigAsString(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logger = l
|
||||
}
|
||||
|
||||
func Debug(v ...interface{}) {
|
||||
if macaron.Env != macaron.DEV {
|
||||
return
|
||||
}
|
||||
if macaron.Env != macaron.DEV {
|
||||
return
|
||||
}
|
||||
write(DEBUG, v)
|
||||
}
|
||||
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
if macaron.Env != macaron.DEV {
|
||||
return
|
||||
}
|
||||
writef(DEBUG, format, v...)
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
if macaron.Env != macaron.DEV {
|
||||
return
|
||||
}
|
||||
writef(DEBUG, format, v...)
|
||||
}
|
||||
|
||||
func Info(v ...interface{}) {
|
||||
write(INFO, v)
|
||||
}
|
||||
|
||||
func Infof(format string, v ...interface{}) {
|
||||
writef(INFO, format, v...)
|
||||
func Infof(format string, v ...interface{}) {
|
||||
writef(INFO, format, v...)
|
||||
}
|
||||
|
||||
func Warn(v ...interface{}) {
|
||||
write(WARN, v)
|
||||
}
|
||||
|
||||
func Warnf(format string, v ...interface{}) {
|
||||
writef(WARN, format, v...)
|
||||
func Warnf(format string, v ...interface{}) {
|
||||
writef(WARN, format, v...)
|
||||
}
|
||||
|
||||
func Error(v ...interface{}) {
|
||||
write(ERROR, v)
|
||||
}
|
||||
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
writef(ERROR, format, v...)
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
writef(ERROR, format, v...)
|
||||
}
|
||||
|
||||
func Fatal(v ...interface{}) {
|
||||
write(FATAL, v)
|
||||
}
|
||||
|
||||
func Fatalf(format string, v ...interface{}) {
|
||||
writef(FATAL, format, v...)
|
||||
func Fatalf(format string, v ...interface{}) {
|
||||
writef(FATAL, format, v...)
|
||||
}
|
||||
|
||||
func write(level Level, v ...interface{}) {
|
||||
defer logger.Flush()
|
||||
defer logger.Flush()
|
||||
|
||||
content := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
if ok {
|
||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||
}
|
||||
}
|
||||
content := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
if ok {
|
||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||
}
|
||||
}
|
||||
|
||||
switch level {
|
||||
case DEBUG:
|
||||
logger.Debug(content, v)
|
||||
case INFO:
|
||||
logger.Info(content, v)
|
||||
case WARN:
|
||||
logger.Warn(content, v)
|
||||
case FATAL:
|
||||
logger.Critical(content, v)
|
||||
os.Exit(1)
|
||||
case ERROR:
|
||||
logger.Error(content, v)
|
||||
switch level {
|
||||
case DEBUG:
|
||||
logger.Debug(content, v)
|
||||
case INFO:
|
||||
logger.Info(content, v)
|
||||
case WARN:
|
||||
logger.Warn(content, v)
|
||||
case FATAL:
|
||||
logger.Critical(content, v)
|
||||
os.Exit(1)
|
||||
case ERROR:
|
||||
logger.Error(content, v)
|
||||
}
|
||||
}
|
||||
|
||||
func writef(level Level, format string, v ...interface{}) {
|
||||
defer logger.Flush()
|
||||
func writef(level Level, format string, v ...interface{}) {
|
||||
defer logger.Flush()
|
||||
|
||||
content := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
if ok {
|
||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||
}
|
||||
}
|
||||
content := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
pc, file, line, ok := runtime.Caller(2)
|
||||
if ok {
|
||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||
}
|
||||
}
|
||||
|
||||
format = content + format
|
||||
format = content + format
|
||||
|
||||
switch level {
|
||||
case DEBUG:
|
||||
logger.Debugf(format, v...)
|
||||
case INFO:
|
||||
logger.Infof(format, v...)
|
||||
case WARN:
|
||||
logger.Warnf(format, v...)
|
||||
case FATAL:
|
||||
logger.Criticalf(format, v...)
|
||||
os.Exit(1)
|
||||
case ERROR:
|
||||
logger.Errorf(format, v...)
|
||||
}
|
||||
switch level {
|
||||
case DEBUG:
|
||||
logger.Debugf(format, v...)
|
||||
case INFO:
|
||||
logger.Infof(format, v...)
|
||||
case WARN:
|
||||
logger.Warnf(format, v...)
|
||||
case FATAL:
|
||||
logger.Criticalf(format, v...)
|
||||
os.Exit(1)
|
||||
case ERROR:
|
||||
logger.Errorf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func getLogConfig() string {
|
||||
|
@ -145,16 +145,16 @@ func getLogConfig() string {
|
|||
</formats>
|
||||
</seelog>`
|
||||
|
||||
consoleConfig := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
consoleConfig =
|
||||
`
|
||||
consoleConfig := ""
|
||||
if macaron.Env == macaron.DEV {
|
||||
consoleConfig =
|
||||
`
|
||||
<filter levels="info,debug,critical,warn,error">
|
||||
<console />
|
||||
</filter>
|
||||
`
|
||||
}
|
||||
config = fmt.Sprintf(config, consoleConfig)
|
||||
}
|
||||
config = fmt.Sprintf(config, consoleConfig)
|
||||
|
||||
return config
|
||||
return config
|
||||
}
|
||||
|
|
|
@ -1,84 +1,82 @@
|
|||
package notify
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"time"
|
||||
"github.com/go-gomail/gomail"
|
||||
"github.com/go-gomail/gomail"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// @author qiang.ou<qingqianludao@gmail.com>
|
||||
// @date 2017/5/1-00:19
|
||||
|
||||
|
||||
type Mail struct {
|
||||
|
||||
}
|
||||
|
||||
func (mail *Mail) Send(msg Message) {
|
||||
model := new(models.Setting)
|
||||
mailSetting, err := model.Mail()
|
||||
logger.Debugf("%+v", mailSetting)
|
||||
if err != nil {
|
||||
logger.Error("#mail#从数据库获取mail配置失败", err)
|
||||
return
|
||||
}
|
||||
if mailSetting.Host == "" {
|
||||
logger.Error("#mail#Host为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.Port == 0 {
|
||||
logger.Error("#mail#Port为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.User == "" {
|
||||
logger.Error("#mail#User为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.Password == "" {
|
||||
logger.Error("#mail#Password为空")
|
||||
return
|
||||
}
|
||||
toUsers := mail.getActiveMailUsers(mailSetting, msg)
|
||||
mail.send(mailSetting, toUsers, msg)
|
||||
func (mail *Mail) Send(msg Message) {
|
||||
model := new(models.Setting)
|
||||
mailSetting, err := model.Mail()
|
||||
logger.Debugf("%+v", mailSetting)
|
||||
if err != nil {
|
||||
logger.Error("#mail#从数据库获取mail配置失败", err)
|
||||
return
|
||||
}
|
||||
if mailSetting.Host == "" {
|
||||
logger.Error("#mail#Host为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.Port == 0 {
|
||||
logger.Error("#mail#Port为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.User == "" {
|
||||
logger.Error("#mail#User为空")
|
||||
return
|
||||
}
|
||||
if mailSetting.Password == "" {
|
||||
logger.Error("#mail#Password为空")
|
||||
return
|
||||
}
|
||||
toUsers := mail.getActiveMailUsers(mailSetting, msg)
|
||||
mail.send(mailSetting, toUsers, msg)
|
||||
}
|
||||
|
||||
func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) {
|
||||
body := msg["content"].(string)
|
||||
body = strings.Replace(body, "\n", "<br>", -1)
|
||||
gomailMessage := gomail.NewMessage()
|
||||
gomailMessage.SetHeader("From", mailSetting.User)
|
||||
gomailMessage.SetHeader("To", toUsers...)
|
||||
gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知")
|
||||
gomailMessage.SetBody("text/html", body)
|
||||
mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port,
|
||||
mailSetting.User, mailSetting.Password)
|
||||
maxTimes := 3
|
||||
i := 0
|
||||
for i < maxTimes {
|
||||
err := mailer.DialAndSend(gomailMessage)
|
||||
if err == nil {
|
||||
break;
|
||||
}
|
||||
i += 1
|
||||
time.Sleep(2 * time.Second)
|
||||
if i < maxTimes {
|
||||
logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"])
|
||||
}
|
||||
}
|
||||
func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) {
|
||||
body := msg["content"].(string)
|
||||
body = strings.Replace(body, "\n", "<br>", -1)
|
||||
gomailMessage := gomail.NewMessage()
|
||||
gomailMessage.SetHeader("From", mailSetting.User)
|
||||
gomailMessage.SetHeader("To", toUsers...)
|
||||
gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知")
|
||||
gomailMessage.SetBody("text/html", body)
|
||||
mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port,
|
||||
mailSetting.User, mailSetting.Password)
|
||||
maxTimes := 3
|
||||
i := 0
|
||||
for i < maxTimes {
|
||||
err := mailer.DialAndSend(gomailMessage)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
time.Sleep(2 * time.Second)
|
||||
if i < maxTimes {
|
||||
logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string {
|
||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||
users := []string{}
|
||||
for _, v := range(mailSetting.MailUsers) {
|
||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||
users = append(users, v.Email)
|
||||
}
|
||||
}
|
||||
func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string {
|
||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||
users := []string{}
|
||||
for _, v := range mailSetting.MailUsers {
|
||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||
users = append(users, v.Email)
|
||||
}
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
return users
|
||||
}
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
package notify
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"fmt"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Message map[string]interface{}
|
||||
|
||||
type Notifiable interface {
|
||||
Send(msg Message)
|
||||
Send(msg Message)
|
||||
}
|
||||
|
||||
var queue chan Message = make(chan Message, 100)
|
||||
|
||||
func init() {
|
||||
go run()
|
||||
func init() {
|
||||
go run()
|
||||
}
|
||||
|
||||
// 把消息推入队列
|
||||
func Push(msg Message) {
|
||||
queue <- msg
|
||||
queue <- msg
|
||||
}
|
||||
|
||||
func run() {
|
||||
for msg := range queue {
|
||||
// 根据任务配置发送通知
|
||||
taskType, taskTypeOk := msg["task_type"]
|
||||
_, taskReceiverIdOk := msg["task_receiver_id"]
|
||||
_, nameOk := msg["name"]
|
||||
_, outputOk := msg["output"]
|
||||
_, statusOk := msg["status"]
|
||||
if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk {
|
||||
logger.Errorf("#notify#参数不完整#%+v", msg)
|
||||
continue
|
||||
}
|
||||
msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"])
|
||||
logger.Debugf("%+v", msg)
|
||||
switch(taskType.(int8)) {
|
||||
case 1:
|
||||
// 邮件
|
||||
mail := Mail{}
|
||||
go mail.Send(msg)
|
||||
case 2:
|
||||
// Slack
|
||||
slack := Slack{}
|
||||
go slack.Send(msg)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
for msg := range queue {
|
||||
// 根据任务配置发送通知
|
||||
taskType, taskTypeOk := msg["task_type"]
|
||||
_, taskReceiverIdOk := msg["task_receiver_id"]
|
||||
_, nameOk := msg["name"]
|
||||
_, outputOk := msg["output"]
|
||||
_, statusOk := msg["status"]
|
||||
if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk {
|
||||
logger.Errorf("#notify#参数不完整#%+v", msg)
|
||||
continue
|
||||
}
|
||||
msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"])
|
||||
logger.Debugf("%+v", msg)
|
||||
switch taskType.(int8) {
|
||||
case 1:
|
||||
// 邮件
|
||||
mail := Mail{}
|
||||
go mail.Send(msg)
|
||||
case 2:
|
||||
// Slack
|
||||
slack := Slack{}
|
||||
go slack.Send(msg)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +1,79 @@
|
|||
package notify
|
||||
|
||||
// 发送消息到slack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/httpclient"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"strconv"
|
||||
"time"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/httpclient"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Slack struct {}
|
||||
type Slack struct{}
|
||||
|
||||
func (slack *Slack) Send(msg Message) {
|
||||
model := new(models.Setting)
|
||||
slackSetting, err := model.Slack()
|
||||
if err != nil {
|
||||
logger.Error("#slack#从数据库获取slack配置失败", err)
|
||||
return
|
||||
}
|
||||
if slackSetting.Url == "" {
|
||||
logger.Error("#slack#webhook-url为空")
|
||||
return
|
||||
}
|
||||
if len(slackSetting.Channels) == 0 {
|
||||
logger.Error("#slack#channels配置为空")
|
||||
return
|
||||
}
|
||||
logger.Debugf("%+v", slackSetting)
|
||||
channels := slack.getActiveSlackChannels(slackSetting, msg)
|
||||
logger.Debugf("%+v", channels)
|
||||
for _, channel := range(channels) {
|
||||
slack.send(msg, slackSetting.Url, channel)
|
||||
}
|
||||
func (slack *Slack) Send(msg Message) {
|
||||
model := new(models.Setting)
|
||||
slackSetting, err := model.Slack()
|
||||
if err != nil {
|
||||
logger.Error("#slack#从数据库获取slack配置失败", err)
|
||||
return
|
||||
}
|
||||
if slackSetting.Url == "" {
|
||||
logger.Error("#slack#webhook-url为空")
|
||||
return
|
||||
}
|
||||
if len(slackSetting.Channels) == 0 {
|
||||
logger.Error("#slack#channels配置为空")
|
||||
return
|
||||
}
|
||||
logger.Debugf("%+v", slackSetting)
|
||||
channels := slack.getActiveSlackChannels(slackSetting, msg)
|
||||
logger.Debugf("%+v", channels)
|
||||
for _, channel := range channels {
|
||||
slack.send(msg, slackSetting.Url, channel)
|
||||
}
|
||||
}
|
||||
|
||||
func (slack *Slack) send(msg Message, slackUrl string, channel string) {
|
||||
formatBody := slack.format(msg["content"].(string), channel)
|
||||
timeout := 30
|
||||
maxTimes := 3
|
||||
i := 0
|
||||
for i < maxTimes {
|
||||
resp := httpclient.PostJson(slackUrl, formatBody, timeout)
|
||||
if resp.StatusCode == 200 {
|
||||
break;
|
||||
}
|
||||
i += 1
|
||||
time.Sleep(2 * time.Second)
|
||||
if i < maxTimes {
|
||||
logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
|
||||
}
|
||||
}
|
||||
func (slack *Slack) send(msg Message, slackUrl string, channel string) {
|
||||
formatBody := slack.format(msg["content"].(string), channel)
|
||||
timeout := 30
|
||||
maxTimes := 3
|
||||
i := 0
|
||||
for i < maxTimes {
|
||||
resp := httpclient.PostJson(slackUrl, formatBody, timeout)
|
||||
if resp.StatusCode == 200 {
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
time.Sleep(2 * time.Second)
|
||||
if i < maxTimes {
|
||||
logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string {
|
||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||
channels := []string{}
|
||||
for _, v := range(slackSetting.Channels) {
|
||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||
channels = append(channels, v.Name)
|
||||
}
|
||||
}
|
||||
func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string {
|
||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||
channels := []string{}
|
||||
for _, v := range slackSetting.Channels {
|
||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||
channels = append(channels, v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return channels
|
||||
return channels
|
||||
}
|
||||
|
||||
// 格式化消息内容
|
||||
func (slack *Slack) format(content string, channel string) string {
|
||||
content = utils.EscapeJson(content)
|
||||
specialChars := []string{"&", "<", ">"}
|
||||
replaceChars := []string{"&", "<", ">"}
|
||||
content = utils.ReplaceStrings(content, specialChars, replaceChars)
|
||||
func (slack *Slack) format(content string, channel string) string {
|
||||
content = utils.EscapeJson(content)
|
||||
specialChars := []string{"&", "<", ">"}
|
||||
replaceChars := []string{"&", "<", ">"}
|
||||
content = utils.ReplaceStrings(content, specialChars, replaceChars)
|
||||
|
||||
return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel)
|
||||
}
|
||||
return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel)
|
||||
}
|
||||
|
|
|
@ -3,16 +3,16 @@ package auth
|
|||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"errors"
|
||||
"fmt"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type Certificate struct {
|
||||
CAFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
CAFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
ServerName string
|
||||
}
|
||||
|
||||
|
@ -33,14 +33,12 @@ func (c Certificate) GetTLSConfigForServer() (*tls.Config, error) {
|
|||
return nil, errors.New("failed to append client certs")
|
||||
}
|
||||
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
ClientCAs: certPool,
|
||||
}
|
||||
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
|
@ -68,4 +66,4 @@ func (c Certificate) GetTransportCredsForClient() (credentials.TransportCredenti
|
|||
})
|
||||
|
||||
return transportCreds, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,80 +1,65 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"golang.org/x/net/context"
|
||||
"fmt"
|
||||
"time"
|
||||
"errors"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnavailable = errors.New("无法连接远程服务器")
|
||||
errUnavailable = errors.New("无法连接远程服务器")
|
||||
)
|
||||
|
||||
func ExecWithRetry(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||
tryTimes := 60
|
||||
i := 0
|
||||
for i < tryTimes {
|
||||
output, err := Exec(ip, port, taskReq)
|
||||
if err != errUnavailable {
|
||||
return output, err
|
||||
}
|
||||
i++
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error("panic#rpc/client.go:Exec#", err)
|
||||
}
|
||||
}()
|
||||
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||
conn, err := grpcpool.Pool.Get(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
isConnClosed := false
|
||||
defer func() {
|
||||
if !isConnClosed {
|
||||
grpcpool.Pool.Put(addr, conn)
|
||||
}
|
||||
}()
|
||||
c := pb.NewTaskClient(conn)
|
||||
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
||||
taskReq.Timeout = 86400
|
||||
}
|
||||
timeout := time.Duration(taskReq.Timeout) * time.Second
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
resp, err := c.Run(ctx, taskReq)
|
||||
if err != nil {
|
||||
return parseGRPCError(err, conn, &isConnClosed)
|
||||
}
|
||||
|
||||
return "", errUnavailable
|
||||
}
|
||||
if resp.Error == "" {
|
||||
return resp.Output, nil
|
||||
}
|
||||
|
||||
func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error("panic#rpc/client.go:Exec#", err)
|
||||
}
|
||||
} ()
|
||||
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||
conn, err := grpcpool.Pool.Get(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
isConnClosed := false
|
||||
defer func() {
|
||||
if !isConnClosed {
|
||||
grpcpool.Pool.Put(addr, conn)
|
||||
}
|
||||
}()
|
||||
c := pb.NewTaskClient(conn)
|
||||
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
||||
taskReq.Timeout = 86400
|
||||
}
|
||||
timeout := time.Duration(taskReq.Timeout) * time.Second
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
resp, err := c.Run(ctx, taskReq)
|
||||
if err != nil {
|
||||
return parseGRPCError(err, conn, &isConnClosed)
|
||||
}
|
||||
|
||||
if resp.Error == "" {
|
||||
return resp.Output, nil
|
||||
}
|
||||
|
||||
return resp.Output, errors.New(resp.Error)
|
||||
return resp.Output, errors.New(resp.Error)
|
||||
}
|
||||
|
||||
func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) {
|
||||
switch grpc.Code(err) {
|
||||
case codes.Unavailable, codes.Internal:
|
||||
conn.Close()
|
||||
*connClosed = true
|
||||
return "", errUnavailable
|
||||
case codes.DeadlineExceeded:
|
||||
return "", errors.New("执行超时, 强制结束")
|
||||
}
|
||||
return "", err
|
||||
switch grpc.Code(err) {
|
||||
case codes.Unavailable, codes.Internal:
|
||||
conn.Close()
|
||||
*connClosed = true
|
||||
return "", errUnavailable
|
||||
case codes.DeadlineExceeded:
|
||||
return "", errors.New("执行超时, 强制结束")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -1,141 +1,139 @@
|
|||
package grpcpool
|
||||
|
||||
import (
|
||||
"github.com/silenceper/pool"
|
||||
"sync"
|
||||
"time"
|
||||
"google.golang.org/grpc"
|
||||
"errors"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
Pool GRPCPool
|
||||
"errors"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
"github.com/silenceper/pool"
|
||||
"google.golang.org/grpc"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidConn = errors.New("invalid connection")
|
||||
Pool GRPCPool
|
||||
)
|
||||
|
||||
func init() {
|
||||
Pool = GRPCPool{
|
||||
make(map[string]pool.Pool),
|
||||
sync.RWMutex{},
|
||||
}
|
||||
var (
|
||||
ErrInvalidConn = errors.New("invalid connection")
|
||||
)
|
||||
|
||||
func init() {
|
||||
Pool = GRPCPool{
|
||||
make(map[string]pool.Pool),
|
||||
sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
type GRPCPool struct {
|
||||
// map key格式 ip:port
|
||||
conns map[string]pool.Pool
|
||||
sync.RWMutex
|
||||
// map key格式 ip:port
|
||||
conns map[string]pool.Pool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
||||
p.RLock()
|
||||
pool, ok := p.conns[addr]
|
||||
p.RUnlock()
|
||||
if !ok {
|
||||
err := p.newCommonPool(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
||||
p.RLock()
|
||||
pool, ok := p.conns[addr]
|
||||
p.RUnlock()
|
||||
if !ok {
|
||||
err := p.newCommonPool(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p.RLock()
|
||||
pool = p.conns[addr]
|
||||
p.RUnlock()
|
||||
conn, err := pool.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.RLock()
|
||||
pool = p.conns[addr]
|
||||
p.RUnlock()
|
||||
conn, err := pool.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.(*grpc.ClientConn), nil
|
||||
return conn.(*grpc.ClientConn), nil
|
||||
}
|
||||
|
||||
func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
pool, ok := p.conns[addr]
|
||||
if ok {
|
||||
return pool.Put(conn)
|
||||
}
|
||||
p.RLock()
|
||||
defer p.RUnlock()
|
||||
pool, ok := p.conns[addr]
|
||||
if ok {
|
||||
return pool.Put(conn)
|
||||
}
|
||||
|
||||
return ErrInvalidConn
|
||||
return ErrInvalidConn
|
||||
}
|
||||
|
||||
|
||||
// 释放连接池
|
||||
func (p *GRPCPool) Release(addr string) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
pool, ok := p.conns[addr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pool.Release()
|
||||
delete(p.conns, addr)
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
pool, ok := p.conns[addr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
pool.Release()
|
||||
delete(p.conns, addr)
|
||||
}
|
||||
|
||||
// 释放所有连接池
|
||||
func (p *GRPCPool) ReleaseAll() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
for _, pool := range(p.conns) {
|
||||
pool.Release()
|
||||
}
|
||||
func (p *GRPCPool) ReleaseAll() {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
for _, pool := range p.conns {
|
||||
pool.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化底层连接池
|
||||
func (p *GRPCPool) newCommonPool(addr string) (error) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
commonPool, ok := p.conns[addr]
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
poolConfig := &pool.PoolConfig{
|
||||
InitialCap: 1,
|
||||
MaxCap: 30,
|
||||
Factory: func() (interface{}, error) {
|
||||
if !app.Setting.EnableTLS {
|
||||
return grpc.Dial(addr, grpc.WithInsecure())
|
||||
}
|
||||
func (p *GRPCPool) newCommonPool(addr string) error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
commonPool, ok := p.conns[addr]
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
poolConfig := &pool.PoolConfig{
|
||||
InitialCap: 1,
|
||||
MaxCap: 30,
|
||||
Factory: func() (interface{}, error) {
|
||||
if !app.Setting.EnableTLS {
|
||||
return grpc.Dial(addr, grpc.WithInsecure())
|
||||
}
|
||||
|
||||
server := strings.Split(addr, ":")
|
||||
server := strings.Split(addr, ":")
|
||||
|
||||
certificate := auth.Certificate{
|
||||
CAFile: app.Setting.CAFile,
|
||||
CertFile: app.Setting.CertFile,
|
||||
KeyFile: app.Setting.KeyFile,
|
||||
ServerName: server[0],
|
||||
}
|
||||
certificate := auth.Certificate{
|
||||
CAFile: app.Setting.CAFile,
|
||||
CertFile: app.Setting.CertFile,
|
||||
KeyFile: app.Setting.KeyFile,
|
||||
ServerName: server[0],
|
||||
}
|
||||
|
||||
transportCreds, err := certificate.GetTransportCredsForClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transportCreds, err := certificate.GetTransportCredsForClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds))
|
||||
},
|
||||
Close: func(v interface{}) error {
|
||||
conn, ok := v.(*grpc.ClientConn)
|
||||
if ok && conn != nil {
|
||||
return conn.Close()
|
||||
}
|
||||
return ErrInvalidConn
|
||||
},
|
||||
IdleTimeout: 3 * time.Minute,
|
||||
}
|
||||
return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds))
|
||||
},
|
||||
Close: func(v interface{}) error {
|
||||
conn, ok := v.(*grpc.ClientConn)
|
||||
if ok && conn != nil {
|
||||
return conn.Close()
|
||||
}
|
||||
return ErrInvalidConn
|
||||
},
|
||||
IdleTimeout: 3 * time.Minute,
|
||||
}
|
||||
|
||||
commonPool, err := pool.NewChannelPool(poolConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commonPool, err := pool.NewChannelPool(poolConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.conns[addr] = commonPool
|
||||
p.conns[addr] = commonPool
|
||||
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,65 +1,64 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"net"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc"
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"github.com/ouqiang/gocron/modules/rpc/auth"
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"net"
|
||||
)
|
||||
|
||||
type Server struct {}
|
||||
type Server struct{}
|
||||
|
||||
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
grpclog.Println(err)
|
||||
}
|
||||
} ()
|
||||
output, err := utils.ExecShell(ctx, req.Command)
|
||||
resp := new(pb.TaskResponse)
|
||||
resp.Output = output
|
||||
if err != nil {
|
||||
resp.Error = err.Error()
|
||||
} else {
|
||||
resp.Error = ""
|
||||
}
|
||||
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
grpclog.Println(err)
|
||||
}
|
||||
}()
|
||||
output, err := utils.ExecShell(ctx, req.Command)
|
||||
resp := new(pb.TaskResponse)
|
||||
resp.Output = output
|
||||
if err != nil {
|
||||
resp.Error = err.Error()
|
||||
} else {
|
||||
resp.Error = ""
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func Start(addr string, enableTLS bool, certificate auth.Certificate) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
grpclog.Println("panic", err)
|
||||
}
|
||||
} ()
|
||||
func Start(addr string, enableTLS bool, certificate auth.Certificate) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
grpclog.Println("panic", err)
|
||||
}
|
||||
}()
|
||||
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
grpclog.Fatal(err)
|
||||
}
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
grpclog.Fatal(err)
|
||||
}
|
||||
|
||||
var s *grpc.Server
|
||||
if enableTLS {
|
||||
tlsConfig, err := certificate.GetTLSConfigForServer()
|
||||
if err != nil {
|
||||
grpclog.Fatal(err)
|
||||
}
|
||||
opt := grpc.Creds(credentials.NewTLS(tlsConfig))
|
||||
s = grpc.NewServer(opt)
|
||||
pb.RegisterTaskServer(s, Server{})
|
||||
grpclog.Printf("listen %s with TLS", addr)
|
||||
} else {
|
||||
s = grpc.NewServer()
|
||||
pb.RegisterTaskServer(s, Server{})
|
||||
grpclog.Printf("listen %s", addr)
|
||||
}
|
||||
var s *grpc.Server
|
||||
if enableTLS {
|
||||
tlsConfig, err := certificate.GetTLSConfigForServer()
|
||||
if err != nil {
|
||||
grpclog.Fatal(err)
|
||||
}
|
||||
opt := grpc.Creds(credentials.NewTLS(tlsConfig))
|
||||
s = grpc.NewServer(opt)
|
||||
pb.RegisterTaskServer(s, Server{})
|
||||
grpclog.Printf("listen %s with TLS", addr)
|
||||
} else {
|
||||
s = grpc.NewServer()
|
||||
pb.RegisterTaskServer(s, Server{})
|
||||
grpclog.Printf("listen %s", addr)
|
||||
}
|
||||
|
||||
err = s.Serve(l)
|
||||
grpclog.Fatal(err)
|
||||
err = s.Serve(l)
|
||||
grpclog.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,111 +1,111 @@
|
|||
package setting
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gopkg.in/ini.v1"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"errors"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
const DefaultSection = "default"
|
||||
|
||||
type Setting struct {
|
||||
Db struct{
|
||||
Engine string
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
Database string
|
||||
Prefix string
|
||||
Charset string
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
}
|
||||
AllowIps string
|
||||
AppName string
|
||||
ApiKey string
|
||||
ApiSecret string
|
||||
ApiSignEnable bool
|
||||
Db struct {
|
||||
Engine string
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
Database string
|
||||
Prefix string
|
||||
Charset string
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
}
|
||||
AllowIps string
|
||||
AppName string
|
||||
ApiKey string
|
||||
ApiSecret string
|
||||
ApiSignEnable bool
|
||||
|
||||
EnableTLS bool
|
||||
CAFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
EnableTLS bool
|
||||
CAFile string
|
||||
CertFile string
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
// 读取配置
|
||||
func Read(filename string) (*Setting,error) {
|
||||
config, err := ini.Load(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
section := config.Section(DefaultSection)
|
||||
func Read(filename string) (*Setting, error) {
|
||||
config, err := ini.Load(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
section := config.Section(DefaultSection)
|
||||
|
||||
var s Setting
|
||||
var s Setting
|
||||
|
||||
s.Db.Engine = section.Key("db.engine").MustString("mysql")
|
||||
s.Db.Host = section.Key("db.host").MustString("127.0.0.1")
|
||||
s.Db.Port = section.Key("db.port").MustInt(3306)
|
||||
s.Db.User = section.Key("db.user").MustString("")
|
||||
s.Db.Password = section.Key("db.password").MustString("")
|
||||
s.Db.Database = section.Key("db.database").MustString("gocron")
|
||||
s.Db.Prefix = section.Key("db.prefix").MustString("")
|
||||
s.Db.Charset = section.Key("db.charset").MustString("utf8")
|
||||
s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30)
|
||||
s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100)
|
||||
s.Db.Engine = section.Key("db.engine").MustString("mysql")
|
||||
s.Db.Host = section.Key("db.host").MustString("127.0.0.1")
|
||||
s.Db.Port = section.Key("db.port").MustInt(3306)
|
||||
s.Db.User = section.Key("db.user").MustString("")
|
||||
s.Db.Password = section.Key("db.password").MustString("")
|
||||
s.Db.Database = section.Key("db.database").MustString("gocron")
|
||||
s.Db.Prefix = section.Key("db.prefix").MustString("")
|
||||
s.Db.Charset = section.Key("db.charset").MustString("utf8")
|
||||
s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30)
|
||||
s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100)
|
||||
|
||||
s.AllowIps = section.Key("allow_ips").MustString("")
|
||||
s.AppName = section.Key("app.name").MustString("定时任务管理系统")
|
||||
s.ApiKey = section.Key("api.key").MustString("")
|
||||
s.ApiSecret = section.Key("api.secret").MustString("")
|
||||
s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true)
|
||||
s.AllowIps = section.Key("allow_ips").MustString("")
|
||||
s.AppName = section.Key("app.name").MustString("定时任务管理系统")
|
||||
s.ApiKey = section.Key("api.key").MustString("")
|
||||
s.ApiSecret = section.Key("api.secret").MustString("")
|
||||
s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true)
|
||||
|
||||
s.EnableTLS = section.Key("enable_tls").MustBool(false)
|
||||
s.CAFile = section.Key("ca_file").MustString("")
|
||||
s.CertFile = section.Key("cert_file").MustString("")
|
||||
s.KeyFile = section.Key("key_file").MustString("")
|
||||
s.EnableTLS = section.Key("enable_tls").MustBool(false)
|
||||
s.CAFile = section.Key("ca_file").MustString("")
|
||||
s.CertFile = section.Key("cert_file").MustString("")
|
||||
s.KeyFile = section.Key("key_file").MustString("")
|
||||
|
||||
if s.EnableTLS {
|
||||
if !utils.FileExist(s.CAFile) {
|
||||
logger.Fatalf("failed to read ca cert file: %s", s.CAFile)
|
||||
}
|
||||
if s.EnableTLS {
|
||||
if !utils.FileExist(s.CAFile) {
|
||||
logger.Fatalf("failed to read ca cert file: %s", s.CAFile)
|
||||
}
|
||||
|
||||
if !utils.FileExist(s.CertFile) {
|
||||
logger.Fatalf("failed to read client cert file: %s", s.CertFile)
|
||||
}
|
||||
if !utils.FileExist(s.CertFile) {
|
||||
logger.Fatalf("failed to read client cert file: %s", s.CertFile)
|
||||
}
|
||||
|
||||
if !utils.FileExist(s.KeyFile) {
|
||||
logger.Fatalf("failed to read client key file: %s", s.KeyFile)
|
||||
}
|
||||
}
|
||||
if !utils.FileExist(s.KeyFile) {
|
||||
logger.Fatalf("failed to read client key file: %s", s.KeyFile)
|
||||
}
|
||||
}
|
||||
|
||||
return &s, nil
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// 写入配置
|
||||
func Write(config []string, filename string) error {
|
||||
if len(config) == 0 {
|
||||
return errors.New("参数不能为空")
|
||||
}
|
||||
if len(config) % 2 != 0 {
|
||||
return errors.New("参数不匹配")
|
||||
}
|
||||
if len(config) == 0 {
|
||||
return errors.New("参数不能为空")
|
||||
}
|
||||
if len(config)%2 != 0 {
|
||||
return errors.New("参数不匹配")
|
||||
}
|
||||
|
||||
file := ini.Empty()
|
||||
file := ini.Empty()
|
||||
|
||||
section, err := file.NewSection(DefaultSection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0 ;i < len(config); {
|
||||
_, err = section.NewKey(config[i], config[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i += 2
|
||||
}
|
||||
err = file.SaveTo(filename)
|
||||
section, err := file.NewSection(DefaultSection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := 0; i < len(config); {
|
||||
_, err = section.NewKey(config[i], config[i+1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i += 2
|
||||
}
|
||||
err = file.SaveTo(filename)
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,143 +1,139 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"errors"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HostAuthType int8 // 认证方式
|
||||
type HostAuthType int8 // 认证方式
|
||||
|
||||
const (
|
||||
HostPassword = 1 // 密码认证
|
||||
HostPublicKey = 2 // 公钥认证
|
||||
HostPassword = 1 // 密码认证
|
||||
HostPublicKey = 2 // 公钥认证
|
||||
)
|
||||
|
||||
const SSHConnectTimeout = 10
|
||||
|
||||
|
||||
type SSHConfig struct {
|
||||
AuthType HostAuthType
|
||||
User string
|
||||
Password string
|
||||
PrivateKey string
|
||||
Host string
|
||||
Port int
|
||||
ExecTimeout int// 执行超时时间
|
||||
type SSHConfig struct {
|
||||
AuthType HostAuthType
|
||||
User string
|
||||
Password string
|
||||
PrivateKey string
|
||||
Host string
|
||||
Port int
|
||||
ExecTimeout int // 执行超时时间
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Output string
|
||||
Err error
|
||||
Output string
|
||||
Err error
|
||||
}
|
||||
|
||||
func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) {
|
||||
timeout := time.Duration(SSHConnectTimeout) * time.Second
|
||||
// 密码认证
|
||||
if sshConfig.AuthType == HostPassword {
|
||||
config = &ssh.ClientConfig{
|
||||
User: sshConfig.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(sshConfig.Password),
|
||||
},
|
||||
Timeout: timeout,
|
||||
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
timeout := time.Duration(SSHConnectTimeout) * time.Second
|
||||
// 密码认证
|
||||
if sshConfig.AuthType == HostPassword {
|
||||
config = &ssh.ClientConfig{
|
||||
User: sshConfig.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(sshConfig.Password),
|
||||
},
|
||||
Timeout: timeout,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 公钥认证
|
||||
config = &ssh.ClientConfig{
|
||||
User: sshConfig.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
Timeout: timeout,
|
||||
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
// 公钥认证
|
||||
config = &ssh.ClientConfig{
|
||||
User: sshConfig.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
Timeout: timeout,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 执行shell命令
|
||||
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
||||
client, err := getClient(sshConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
client, err := getClient(sshConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, err := client.NewSession()
|
||||
session, err := client.NewSession()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer session.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
// 后台运行
|
||||
if sshConfig.ExecTimeout < 0 {
|
||||
go session.CombinedOutput(cmd)
|
||||
time.Sleep(10 * time.Second)
|
||||
return "", nil
|
||||
}
|
||||
// 不限制超时
|
||||
if sshConfig.ExecTimeout == 0 {
|
||||
outputByte, execErr := session.CombinedOutput(cmd)
|
||||
output = string(outputByte)
|
||||
err = execErr
|
||||
return
|
||||
}
|
||||
// 后台运行
|
||||
if sshConfig.ExecTimeout < 0 {
|
||||
go session.CombinedOutput(cmd)
|
||||
time.Sleep(10 * time.Second)
|
||||
return "", nil
|
||||
}
|
||||
// 不限制超时
|
||||
if sshConfig.ExecTimeout == 0 {
|
||||
outputByte, execErr := session.CombinedOutput(cmd)
|
||||
output = string(outputByte)
|
||||
err = execErr
|
||||
return
|
||||
}
|
||||
|
||||
var resultChan chan Result = make(chan Result)
|
||||
var timeoutChan chan bool = make(chan bool)
|
||||
go func() {
|
||||
output, err := session.CombinedOutput(cmd)
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
// todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性
|
||||
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
|
||||
select {
|
||||
case result := <- resultChan:
|
||||
output = result.Output
|
||||
err = result.Err
|
||||
case <- timeoutChan:
|
||||
output = ""
|
||||
err = errors.New("timeout")
|
||||
}
|
||||
var resultChan chan Result = make(chan Result)
|
||||
var timeoutChan chan bool = make(chan bool)
|
||||
go func() {
|
||||
output, err := session.CombinedOutput(cmd)
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
// todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性
|
||||
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
output = result.Output
|
||||
err = result.Err
|
||||
case <-timeoutChan:
|
||||
output = ""
|
||||
err = errors.New("timeout")
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
||||
config, err := parseSSHConfig(sshConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
||||
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
||||
config, err := parseSSHConfig(sshConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
||||
|
||||
return ssh.Dial("tcp", addr, config)
|
||||
return ssh.Dial("tcp", addr, config)
|
||||
}
|
||||
|
||||
|
||||
func triggerTimeout(ch chan bool, timeout int){
|
||||
// 最长执行时间不能超过24小时
|
||||
if timeout <= 0 || timeout > 86400 {
|
||||
timeout = 86400
|
||||
}
|
||||
time.Sleep(time.Duration(timeout) * time.Second)
|
||||
close(ch)
|
||||
func triggerTimeout(ch chan bool, timeout int) {
|
||||
// 最长执行时间不能超过24小时
|
||||
if timeout <= 0 || timeout > 86400 {
|
||||
timeout = 86400
|
||||
}
|
||||
time.Sleep(time.Duration(timeout) * time.Second)
|
||||
close(ch)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"encoding/json"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
)
|
||||
|
||||
// json 格式输出
|
||||
|
||||
type response struct {
|
||||
Code int `json:"code"` // 状态码 0:成功 非0:失败
|
||||
Message string `json:"message"` // 信息
|
||||
Data interface{} `json:"data"` // 数据
|
||||
Code int `json:"code"` // 状态码 0:成功 非0:失败
|
||||
Message string `json:"message"` // 信息
|
||||
Data interface{} `json:"data"` // 数据
|
||||
}
|
||||
|
||||
type JsonResponse struct{}
|
||||
|
@ -26,40 +26,40 @@ const SuccessContent = "操作成功"
|
|||
const FailureContent = "操作失败"
|
||||
|
||||
func JsonResponseByErr(err error) string {
|
||||
json := JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure(FailureContent, err)
|
||||
}
|
||||
json := JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure(FailureContent, err)
|
||||
}
|
||||
|
||||
return json.Success(SuccessContent, nil)
|
||||
return json.Success(SuccessContent, nil)
|
||||
}
|
||||
|
||||
func (j *JsonResponse) Success(message string, data interface{}) string {
|
||||
return j.response(ResponseSuccess, message, data)
|
||||
return j.response(ResponseSuccess, message, data)
|
||||
}
|
||||
|
||||
func (j *JsonResponse) Failure(code int, message string) string {
|
||||
return j.response(code, message, nil)
|
||||
return j.response(code, message, nil)
|
||||
}
|
||||
|
||||
func (j *JsonResponse) CommonFailure(message string, err... error) string {
|
||||
if len(err) > 0 {
|
||||
logger.Warn(err)
|
||||
}
|
||||
return j.Failure(ResponseFailure, message)
|
||||
func (j *JsonResponse) CommonFailure(message string, err ...error) string {
|
||||
if len(err) > 0 {
|
||||
logger.Warn(err)
|
||||
}
|
||||
return j.Failure(ResponseFailure, message)
|
||||
}
|
||||
|
||||
func (j *JsonResponse) response(code int, message string, data interface{}) string {
|
||||
resp := response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
resp := response{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
result, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
result, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
return string(result)
|
||||
return string(result)
|
||||
}
|
||||
|
|
|
@ -1,107 +1,107 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"math/rand"
|
||||
"time"
|
||||
"runtime"
|
||||
"github.com/Tang-RoseChild/mahonia"
|
||||
"strings"
|
||||
"os"
|
||||
"fmt"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/Tang-RoseChild/mahonia"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 生成长度为length的随机字符串
|
||||
func RandString(length int64) string {
|
||||
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
sourceLength := len(sources)
|
||||
var i int64 = 0
|
||||
for ; i < length; i++ {
|
||||
result = append(result, sources[r.Intn(sourceLength)])
|
||||
}
|
||||
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
sourceLength := len(sources)
|
||||
var i int64 = 0
|
||||
for ; i < length; i++ {
|
||||
result = append(result, sources[r.Intn(sourceLength)])
|
||||
}
|
||||
|
||||
return string(result)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// 生成32位MD5摘要
|
||||
func Md5(str string) string {
|
||||
m := md5.New()
|
||||
m.Write([]byte(str))
|
||||
m := md5.New()
|
||||
m.Write([]byte(str))
|
||||
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
return hex.EncodeToString(m.Sum(nil))
|
||||
}
|
||||
|
||||
// 生成0-max之间随机数
|
||||
func RandNumber(max int) int {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
return r.Intn(max)
|
||||
return r.Intn(max)
|
||||
}
|
||||
|
||||
// 判断当前系统是否是windows
|
||||
func IsWindows() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// GBK编码转换为UTF8
|
||||
func GBK2UTF8(s string) (string, bool) {
|
||||
dec := mahonia.NewDecoder("gbk")
|
||||
dec := mahonia.NewDecoder("gbk")
|
||||
|
||||
return dec.ConvertStringOK(s)
|
||||
return dec.ConvertStringOK(s)
|
||||
}
|
||||
|
||||
// 批量替换字符串
|
||||
func ReplaceStrings(s string, old []string, replace []string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if len(old) != len(replace) {
|
||||
return s
|
||||
}
|
||||
func ReplaceStrings(s string, old []string, replace []string) string {
|
||||
if s == "" {
|
||||
return s
|
||||
}
|
||||
if len(old) != len(replace) {
|
||||
return s
|
||||
}
|
||||
|
||||
for i, v := range old {
|
||||
s = strings.Replace(s, v, replace[i], 1000)
|
||||
}
|
||||
for i, v := range old {
|
||||
s = strings.Replace(s, v, replace[i], 1000)
|
||||
}
|
||||
|
||||
return s
|
||||
return s
|
||||
}
|
||||
|
||||
func InStringSlice(slice []string, element string) bool {
|
||||
element = strings.TrimSpace(element)
|
||||
for _, v := range slice {
|
||||
if strings.TrimSpace(v) == element{
|
||||
return true
|
||||
}
|
||||
}
|
||||
element = strings.TrimSpace(element)
|
||||
for _, v := range slice {
|
||||
if strings.TrimSpace(v) == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
// 转义json特殊字符
|
||||
func EscapeJson(s string) string {
|
||||
specialChars := []string{"\\", "\b","\f", "\n", "\r", "\t", "\"",}
|
||||
replaceChars := []string{ "\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\"",}
|
||||
func EscapeJson(s string) string {
|
||||
specialChars := []string{"\\", "\b", "\f", "\n", "\r", "\t", "\""}
|
||||
replaceChars := []string{"\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\""}
|
||||
|
||||
return ReplaceStrings(s, specialChars, replaceChars)
|
||||
return ReplaceStrings(s, specialChars, replaceChars)
|
||||
}
|
||||
|
||||
// 判断文件是否存在及是否有权限访问
|
||||
func FileExist(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return false
|
||||
}
|
||||
_, err := os.Stat(file)
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return true
|
||||
}
|
||||
|
||||
// 格式化环境变量
|
||||
func FormatUnixEnv(key, value string) string {
|
||||
return fmt.Sprintf("export %s=%s; ", key, value)
|
||||
}
|
||||
return fmt.Sprintf("export %s=%s; ", key, value)
|
||||
}
|
||||
|
|
|
@ -3,22 +3,22 @@ package utils
|
|||
import "testing"
|
||||
|
||||
func TestRandString(t *testing.T) {
|
||||
str := RandString(32)
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||
}
|
||||
str := RandString(32)
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMd5(t *testing.T) {
|
||||
str := Md5("123456")
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||
}
|
||||
str := Md5("123456")
|
||||
if len(str) != 32 {
|
||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandNumber(t *testing.T) {
|
||||
num := RandNumber(10000)
|
||||
if num <= 0 && num >= 10000 {
|
||||
t.Fatalf("随机数不在有效范围内-%d", num)
|
||||
}
|
||||
num := RandNumber(10000)
|
||||
if num <= 0 && num >= 10000 {
|
||||
t.Fatalf("随机数不在有效范围内-%d", num)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,35 +3,35 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"golang.org/x/net/context"
|
||||
"errors"
|
||||
"errors"
|
||||
"golang.org/x/net/context"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
output string
|
||||
err error
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
// 执行shell命令,可设置执行超时时间
|
||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
var resultChan chan Result = make(chan Result)
|
||||
go func() {
|
||||
output ,err := cmd.CombinedOutput()
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
select {
|
||||
case <- ctx.Done():
|
||||
if cmd.Process.Pid > 0 {
|
||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
return "", errors.New("timeout killed")
|
||||
case result := <- resultChan:
|
||||
return result.output, result.err
|
||||
}
|
||||
}
|
||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
var resultChan chan Result = make(chan Result)
|
||||
go func() {
|
||||
output, err := cmd.CombinedOutput()
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if cmd.Process.Pid > 0 {
|
||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
return "", errors.New("timeout killed")
|
||||
case result := <-resultChan:
|
||||
return result.output, result.err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,51 +3,48 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"golang.org/x/net/context"
|
||||
"errors"
|
||||
"errors"
|
||||
"golang.org/x/net/context"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
output string
|
||||
err error
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
// 执行shell命令,可设置执行超时时间
|
||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||
cmd := exec.Command("cmd", "/C", command)
|
||||
// 隐藏cmd窗口
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
}
|
||||
var resultChan chan Result = make(chan Result)
|
||||
go func() {
|
||||
output ,err := cmd.CombinedOutput()
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
select {
|
||||
case <- ctx.Done():
|
||||
if cmd.Process.Pid > 0 {
|
||||
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
return "", errors.New("timeout killed")
|
||||
case result := <- resultChan:
|
||||
return ConvertEncoding(result.output), result.err
|
||||
}
|
||||
|
||||
|
||||
return "", nil
|
||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||
cmd := exec.Command("cmd", "/C", command)
|
||||
// 隐藏cmd窗口
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
}
|
||||
var resultChan chan Result = make(chan Result)
|
||||
go func() {
|
||||
output, err := cmd.CombinedOutput()
|
||||
resultChan <- Result{string(output), err}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if cmd.Process.Pid > 0 {
|
||||
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
return "", errors.New("timeout killed")
|
||||
case result := <-resultChan:
|
||||
return ConvertEncoding(result.output), result.err
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertEncoding(outputGBK string) (string) {
|
||||
// windows平台编码为gbk,需转换为utf8才能入库
|
||||
outputUTF8, ok := GBK2UTF8(outputGBK)
|
||||
if ok {
|
||||
return outputUTF8
|
||||
}
|
||||
func ConvertEncoding(outputGBK string) string {
|
||||
// windows平台编码为gbk,需转换为utf8才能入库
|
||||
outputUTF8, ok := GBK2UTF8(outputGBK)
|
||||
if ok {
|
||||
return outputUTF8
|
||||
}
|
||||
|
||||
return "命令输出转换编码失败(gbk to utf8)"
|
||||
}
|
||||
return "命令输出转换编码失败(gbk to utf8)"
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package base
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) {
|
||||
page := ctx.QueryInt("page")
|
||||
pageSize := ctx.QueryInt("page_size")
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = models.PageSize
|
||||
}
|
||||
func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) {
|
||||
page := ctx.QueryInt("page")
|
||||
pageSize := ctx.QueryInt("page_size")
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = models.PageSize
|
||||
}
|
||||
|
||||
params["Page"] = page
|
||||
params["PageSize"] = pageSize
|
||||
}
|
||||
params["Page"] = page
|
||||
params["PageSize"] = pageSize
|
||||
}
|
||||
|
|
|
@ -3,6 +3,6 @@ package routers
|
|||
import "gopkg.in/macaron.v1"
|
||||
|
||||
// 首页
|
||||
func Home(ctx *macaron.Context) {
|
||||
ctx.Redirect("/task")
|
||||
func Home(ctx *macaron.Context) {
|
||||
ctx.Redirect("/task")
|
||||
}
|
||||
|
|
|
@ -1,194 +1,192 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"strconv"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/Unknwon/paginater"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/rpc/client"
|
||||
"github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"fmt"
|
||||
"github.com/Unknwon/paginater"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/rpc/client"
|
||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
||||
"github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"gopkg.in/macaron.v1"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := hostModel.Total(queryParams)
|
||||
hosts, err := hostModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
name, ok := queryParams["name"].(string)
|
||||
var safeNameHTML = ""
|
||||
if ok {
|
||||
safeNameHTML = template.HTMLEscapeString(name)
|
||||
}
|
||||
PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d",
|
||||
queryParams["Id"], safeNameHTML, queryParams["PageSize"]);
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "主机列表"
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "host/index")
|
||||
func Index(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := hostModel.Total(queryParams)
|
||||
hosts, err := hostModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
name, ok := queryParams["name"].(string)
|
||||
var safeNameHTML = ""
|
||||
if ok {
|
||||
safeNameHTML = template.HTMLEscapeString(name)
|
||||
}
|
||||
PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d",
|
||||
queryParams["Id"], safeNameHTML, queryParams["PageSize"])
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "主机列表"
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "host/index")
|
||||
}
|
||||
|
||||
func Create(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "添加主机"
|
||||
ctx.HTML(200, "host/host_form")
|
||||
func Create(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "添加主机"
|
||||
ctx.HTML(200, "host/host_form")
|
||||
}
|
||||
|
||||
func Edit(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "编辑主机"
|
||||
hostModel := new(models.Host)
|
||||
id := ctx.ParamsInt(":id")
|
||||
err := hostModel.Find(id)
|
||||
if err != nil {
|
||||
logger.Errorf("获取主机详情失败#主机id-%d", id)
|
||||
}
|
||||
ctx.Data["Host"] = hostModel
|
||||
ctx.HTML(200, "host/host_form")
|
||||
func Edit(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "编辑主机"
|
||||
hostModel := new(models.Host)
|
||||
id := ctx.ParamsInt(":id")
|
||||
err := hostModel.Find(id)
|
||||
if err != nil {
|
||||
logger.Errorf("获取主机详情失败#主机id-%d", id)
|
||||
}
|
||||
ctx.Data["Host"] = hostModel
|
||||
ctx.HTML(200, "host/host_form")
|
||||
}
|
||||
|
||||
type HostForm struct {
|
||||
Id int16
|
||||
Name string `binding:"Required;MaxSize(64)"`
|
||||
Alias string `binding:"Required;MaxSize(32)"`
|
||||
Port int `binding:"Required;Range(1-65535)"`
|
||||
Remark string
|
||||
Id int16
|
||||
Name string `binding:"Required;MaxSize(64)"`
|
||||
Alias string `binding:"Required;MaxSize(32)"`
|
||||
Port int `binding:"Required;Range(1-65535)"`
|
||||
Remark string
|
||||
}
|
||||
|
||||
func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
|
||||
ctx.Resp.Write([]byte(content))
|
||||
ctx.Resp.Write([]byte(content))
|
||||
}
|
||||
|
||||
func Store(ctx *macaron.Context, form HostForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
hostModel := new(models.Host)
|
||||
id := form.Id
|
||||
nameExist, err := hostModel.NameExists(form.Name, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
if nameExist {
|
||||
return json.CommonFailure("主机名已存在")
|
||||
}
|
||||
func Store(ctx *macaron.Context, form HostForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
hostModel := new(models.Host)
|
||||
id := form.Id
|
||||
nameExist, err := hostModel.NameExists(form.Name, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
if nameExist {
|
||||
return json.CommonFailure("主机名已存在")
|
||||
}
|
||||
|
||||
hostModel.Name = strings.TrimSpace(form.Name)
|
||||
hostModel.Alias = strings.TrimSpace(form.Alias)
|
||||
hostModel.Port = form.Port
|
||||
hostModel.Remark = strings.TrimSpace(form.Remark)
|
||||
isCreate := false
|
||||
oldHostModel := new(models.Host)
|
||||
err = oldHostModel.Find(int(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("主机不存在")
|
||||
}
|
||||
hostModel.Name = strings.TrimSpace(form.Name)
|
||||
hostModel.Alias = strings.TrimSpace(form.Alias)
|
||||
hostModel.Port = form.Port
|
||||
hostModel.Remark = strings.TrimSpace(form.Remark)
|
||||
isCreate := false
|
||||
oldHostModel := new(models.Host)
|
||||
err = oldHostModel.Find(int(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("主机不存在")
|
||||
}
|
||||
|
||||
if id > 0 {
|
||||
_, err = hostModel.UpdateBean(id)
|
||||
} else {
|
||||
isCreate = true
|
||||
id, err = hostModel.Create()
|
||||
}
|
||||
if err != nil {
|
||||
return json.CommonFailure("保存失败", err)
|
||||
}
|
||||
if id > 0 {
|
||||
_, err = hostModel.UpdateBean(id)
|
||||
} else {
|
||||
isCreate = true
|
||||
id, err = hostModel.Create()
|
||||
}
|
||||
if err != nil {
|
||||
return json.CommonFailure("保存失败", err)
|
||||
}
|
||||
|
||||
if !isCreate {
|
||||
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
|
||||
newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||
if oldAddr != newAddr {
|
||||
grpcpool.Pool.Release(oldAddr)
|
||||
}
|
||||
if !isCreate {
|
||||
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
|
||||
newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||
if oldAddr != newAddr {
|
||||
grpcpool.Pool.Release(oldAddr)
|
||||
}
|
||||
|
||||
taskModel := new(models.Task)
|
||||
tasks, err := taskModel.ActiveListByHostId(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("刷新任务主机信息失败", err)
|
||||
}
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.BatchAdd(tasks)
|
||||
}
|
||||
taskModel := new(models.Task)
|
||||
tasks, err := taskModel.ActiveListByHostId(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("刷新任务主机信息失败", err)
|
||||
}
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.BatchAdd(tasks)
|
||||
}
|
||||
|
||||
return json.Success("保存成功", nil)
|
||||
return json.Success("保存成功", nil)
|
||||
}
|
||||
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
id, err := strconv.Atoi(ctx.Params(":id"))
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure("参数错误", err)
|
||||
}
|
||||
taskHostModel := new(models.TaskHost)
|
||||
exist,err := taskHostModel.HostIdExist(int16(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
if exist {
|
||||
return json.CommonFailure("有任务引用此主机,不能删除")
|
||||
}
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
id, err := strconv.Atoi(ctx.Params(":id"))
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure("参数错误", err)
|
||||
}
|
||||
taskHostModel := new(models.TaskHost)
|
||||
exist, err := taskHostModel.HostIdExist(int16(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
if exist {
|
||||
return json.CommonFailure("有任务引用此主机,不能删除")
|
||||
}
|
||||
|
||||
hostModel := new(models.Host)
|
||||
err = hostModel.Find(int(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("主机不存在")
|
||||
}
|
||||
hostModel := new(models.Host)
|
||||
err = hostModel.Find(int(id))
|
||||
if err != nil {
|
||||
return json.CommonFailure("主机不存在")
|
||||
}
|
||||
|
||||
_, err =hostModel.Delete(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
_, err = hostModel.Delete(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure("操作失败", err)
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||
grpcpool.Pool.Release(addr)
|
||||
addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||
grpcpool.Pool.Release(addr)
|
||||
|
||||
|
||||
return json.Success("操作成功", nil)
|
||||
return json.Success("操作成功", nil)
|
||||
}
|
||||
|
||||
func Ping(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
hostModel := new(models.Host)
|
||||
err := hostModel.Find(id)
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil || hostModel.Id <= 0{
|
||||
return json.CommonFailure("主机不存在", err)
|
||||
}
|
||||
func Ping(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
hostModel := new(models.Host)
|
||||
err := hostModel.Find(id)
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil || hostModel.Id <= 0 {
|
||||
return json.CommonFailure("主机不存在", err)
|
||||
}
|
||||
|
||||
taskReq := &rpc.TaskRequest{}
|
||||
taskReq.Command = "echo hello"
|
||||
taskReq.Timeout = 10
|
||||
output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq)
|
||||
if err != nil {
|
||||
return json.CommonFailure("连接失败-"+err.Error()+" "+output, err)
|
||||
}
|
||||
|
||||
taskReq := &rpc.TaskRequest{}
|
||||
taskReq.Command = "echo hello"
|
||||
taskReq.Timeout = 10
|
||||
output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq)
|
||||
if err != nil {
|
||||
return json.CommonFailure("连接失败-" + err.Error() + " " + output, err)
|
||||
}
|
||||
|
||||
return json.Success("连接成功", nil)
|
||||
return json.Success("连接成功", nil)
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
|
||||
return params
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
|
|
@ -1,163 +1,163 @@
|
|||
package install
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"gopkg.in/macaron.v1"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"github.com/go-macaron/binding"
|
||||
"fmt"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/setting"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"gopkg.in/macaron.v1"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 系统安装
|
||||
|
||||
type InstallForm struct {
|
||||
DbType string `binding:"In(mysql)"`
|
||||
DbHost string `binding:"Required;MaxSize(50)"`
|
||||
DbPort int `binding:"Required;Range(1,65535)"`
|
||||
DbUsername string `binding:"Required;MaxSize(50)"`
|
||||
DbPassword string `binding:"Required;MaxSize(30)"`
|
||||
DbName string `binding:"Required;MaxSize(50)"`
|
||||
DbTablePrefix string `binding:"MaxSize(20)"`
|
||||
AdminUsername string `binding:"Required;MinSize(3)"`
|
||||
AdminPassword string `binding:"Required;MinSize(6)"`
|
||||
ConfirmAdminPassword string `binding:"Required;MinSize(6)"`
|
||||
AdminEmail string `binding:"Required;Email;MaxSize(50)"`
|
||||
DbType string `binding:"In(mysql)"`
|
||||
DbHost string `binding:"Required;MaxSize(50)"`
|
||||
DbPort int `binding:"Required;Range(1,65535)"`
|
||||
DbUsername string `binding:"Required;MaxSize(50)"`
|
||||
DbPassword string `binding:"Required;MaxSize(30)"`
|
||||
DbName string `binding:"Required;MaxSize(50)"`
|
||||
DbTablePrefix string `binding:"MaxSize(20)"`
|
||||
AdminUsername string `binding:"Required;MinSize(3)"`
|
||||
AdminPassword string `binding:"Required;MinSize(6)"`
|
||||
ConfirmAdminPassword string `binding:"Required;MinSize(6)"`
|
||||
AdminEmail string `binding:"Required;Email;MaxSize(50)"`
|
||||
}
|
||||
|
||||
func (f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
|
||||
ctx.Resp.Write([]byte(content))
|
||||
ctx.Resp.Write([]byte(content))
|
||||
}
|
||||
|
||||
func Create(ctx *macaron.Context) {
|
||||
if app.Installed {
|
||||
ctx.Redirect("/")
|
||||
}
|
||||
ctx.Data["Title"] = "安装"
|
||||
ctx.Data["DisableNav"] = true
|
||||
ctx.HTML(200, "install/create")
|
||||
if app.Installed {
|
||||
ctx.Redirect("/")
|
||||
}
|
||||
ctx.Data["Title"] = "安装"
|
||||
ctx.Data["DisableNav"] = true
|
||||
ctx.HTML(200, "install/create")
|
||||
}
|
||||
|
||||
// 安装
|
||||
func Store(ctx *macaron.Context, form InstallForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
if app.Installed {
|
||||
return json.CommonFailure("系统已安装!")
|
||||
}
|
||||
if form.AdminPassword != form.ConfirmAdminPassword {
|
||||
return json.CommonFailure("两次输入密码不匹配")
|
||||
}
|
||||
err := testDbConnection(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("数据库连接失败", err)
|
||||
}
|
||||
// 写入数据库配置
|
||||
err = writeConfig(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("数据库配置写入文件失败", err)
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
if app.Installed {
|
||||
return json.CommonFailure("系统已安装!")
|
||||
}
|
||||
if form.AdminPassword != form.ConfirmAdminPassword {
|
||||
return json.CommonFailure("两次输入密码不匹配")
|
||||
}
|
||||
err := testDbConnection(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("数据库连接失败", err)
|
||||
}
|
||||
// 写入数据库配置
|
||||
err = writeConfig(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("数据库配置写入文件失败", err)
|
||||
}
|
||||
|
||||
appConfig, err := setting.Read(app.AppConfig)
|
||||
if err != nil {
|
||||
return json.CommonFailure("读取应用配置失败", err)
|
||||
}
|
||||
app.Setting = appConfig
|
||||
appConfig, err := setting.Read(app.AppConfig)
|
||||
if err != nil {
|
||||
return json.CommonFailure("读取应用配置失败", err)
|
||||
}
|
||||
app.Setting = appConfig
|
||||
|
||||
models.Db = models.CreateDb()
|
||||
// 创建数据库表
|
||||
migration := new(models.Migration)
|
||||
err = migration.Install(form.DbName)
|
||||
if err != nil {
|
||||
return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err)
|
||||
}
|
||||
models.Db = models.CreateDb()
|
||||
// 创建数据库表
|
||||
migration := new(models.Migration)
|
||||
err = migration.Install(form.DbName)
|
||||
if err != nil {
|
||||
return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err)
|
||||
}
|
||||
|
||||
// 创建管理员账号
|
||||
err = createAdminUser(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("创建管理员账号失败", err)
|
||||
}
|
||||
// 创建管理员账号
|
||||
err = createAdminUser(form)
|
||||
if err != nil {
|
||||
return json.CommonFailure("创建管理员账号失败", err)
|
||||
}
|
||||
|
||||
// 创建安装锁
|
||||
err = app.CreateInstallLock()
|
||||
if err != nil {
|
||||
return json.CommonFailure("创建文件安装锁失败", err)
|
||||
}
|
||||
// 创建安装锁
|
||||
err = app.CreateInstallLock()
|
||||
if err != nil {
|
||||
return json.CommonFailure("创建文件安装锁失败", err)
|
||||
}
|
||||
|
||||
// 更新版本号文件
|
||||
app.UpdateVersionFile()
|
||||
// 更新版本号文件
|
||||
app.UpdateVersionFile()
|
||||
|
||||
app.Installed = true
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
app.Installed = true
|
||||
// 初始化定时任务
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Initialize()
|
||||
|
||||
return json.Success("安装成功", nil)
|
||||
return json.Success("安装成功", nil)
|
||||
}
|
||||
|
||||
// 配置写入文件
|
||||
func writeConfig(form InstallForm) error {
|
||||
dbConfig := []string{
|
||||
"db.engine", form.DbType,
|
||||
"db.host", form.DbHost,
|
||||
"db.port", strconv.Itoa(form.DbPort),
|
||||
"db.user", form.DbUsername,
|
||||
"db.password",form.DbPassword,
|
||||
"db.database", form.DbName,
|
||||
"db.prefix", form.DbTablePrefix,
|
||||
"db.charset", "utf8",
|
||||
"db.max.idle.conns", "30",
|
||||
"db.max.open.conns", "100",
|
||||
"allow_ips", "",
|
||||
"app.name", "定时任务管理系统", // 应用名称
|
||||
"api.key", "",
|
||||
"api.secret", "",
|
||||
"enable_tls", "false",
|
||||
"ca_file", "",
|
||||
"cert_file", "",
|
||||
"key_file", "",
|
||||
}
|
||||
dbConfig := []string{
|
||||
"db.engine", form.DbType,
|
||||
"db.host", form.DbHost,
|
||||
"db.port", strconv.Itoa(form.DbPort),
|
||||
"db.user", form.DbUsername,
|
||||
"db.password", form.DbPassword,
|
||||
"db.database", form.DbName,
|
||||
"db.prefix", form.DbTablePrefix,
|
||||
"db.charset", "utf8",
|
||||
"db.max.idle.conns", "30",
|
||||
"db.max.open.conns", "100",
|
||||
"allow_ips", "",
|
||||
"app.name", "定时任务管理系统", // 应用名称
|
||||
"api.key", "",
|
||||
"api.secret", "",
|
||||
"enable_tls", "false",
|
||||
"ca_file", "",
|
||||
"cert_file", "",
|
||||
"key_file", "",
|
||||
}
|
||||
|
||||
return setting.Write(dbConfig, app.AppConfig)
|
||||
return setting.Write(dbConfig, app.AppConfig)
|
||||
}
|
||||
|
||||
// 创建管理员账号
|
||||
func createAdminUser(form InstallForm) error {
|
||||
user := new(models.User)
|
||||
user.Name = form.AdminUsername
|
||||
user.Password = form.AdminPassword
|
||||
user.Email = form.AdminEmail
|
||||
user.IsAdmin = 1
|
||||
_, err := user.Create()
|
||||
user := new(models.User)
|
||||
user.Name = form.AdminUsername
|
||||
user.Password = form.AdminPassword
|
||||
user.Email = form.AdminEmail
|
||||
user.IsAdmin = 1
|
||||
_, err := user.Create()
|
||||
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
// 测试数据库连接
|
||||
func testDbConnection(form InstallForm) error {
|
||||
var s setting.Setting
|
||||
s.Db.Engine = form.DbType
|
||||
s.Db.Host = form.DbHost
|
||||
s.Db.Port = form.DbPort
|
||||
s.Db.User = form.DbUsername
|
||||
s.Db.Password = form.DbPassword
|
||||
s.Db.Charset = "utf8"
|
||||
db, err := models.CreateTmpDb(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var s setting.Setting
|
||||
s.Db.Engine = form.DbType
|
||||
s.Db.Host = form.DbHost
|
||||
s.Db.Port = form.DbPort
|
||||
s.Db.User = form.DbUsername
|
||||
s.Db.Password = form.DbPassword
|
||||
s.Db.Charset = "utf8"
|
||||
db, err := models.CreateTmpDb(&s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer db.Close()
|
||||
err = db.Ping()
|
||||
defer db.Close()
|
||||
err = db.Ping()
|
||||
|
||||
return err
|
||||
return err
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
package loginlog
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/Unknwon/paginater"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"html/template"
|
||||
"fmt"
|
||||
"github.com/Unknwon/paginater"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"gopkg.in/macaron.v1"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
loginLogModel := new(models.LoginLog)
|
||||
params := models.CommonMap{}
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
total, err := loginLogModel.Total()
|
||||
loginLogs, err := loginLogModel.List(params)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
PageParams := fmt.Sprintf("page_size=%d", params["PageSize"]);
|
||||
params["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "登录日志"
|
||||
ctx.Data["LoginLogs"] = loginLogs
|
||||
ctx.Data["Params"] = params
|
||||
ctx.HTML(200, "manage/login_log")
|
||||
}
|
||||
func Index(ctx *macaron.Context) {
|
||||
loginLogModel := new(models.LoginLog)
|
||||
params := models.CommonMap{}
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
total, err := loginLogModel.Total()
|
||||
loginLogs, err := loginLogModel.List(params)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
PageParams := fmt.Sprintf("page_size=%d", params["PageSize"])
|
||||
params["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "登录日志"
|
||||
ctx.Data["LoginLogs"] = loginLogs
|
||||
ctx.Data["Params"] = params
|
||||
ctx.HTML(200, "manage/login_log")
|
||||
}
|
||||
|
|
|
@ -1,138 +1,136 @@
|
|||
package manage
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"encoding/json"
|
||||
"encoding/json"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
|
||||
// region slack
|
||||
|
||||
func EditSlack(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "Slack配置"
|
||||
settingModel := new(models.Setting)
|
||||
slack, err := settingModel.Slack()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Slack"] = slack
|
||||
ctx.HTML(200, "manage/slack")
|
||||
func EditSlack(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "Slack配置"
|
||||
settingModel := new(models.Setting)
|
||||
slack, err := settingModel.Slack()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Slack"] = slack
|
||||
ctx.HTML(200, "manage/slack")
|
||||
}
|
||||
|
||||
func Slack(ctx *macaron.Context) string {
|
||||
settingModel := new(models.Setting)
|
||||
slack, err := settingModel.Slack()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
settingModel := new(models.Setting)
|
||||
slack, err := settingModel.Slack()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
|
||||
return json.Success("", slack)
|
||||
return json.Success("", slack)
|
||||
}
|
||||
|
||||
func UpdateSlackUrl(ctx *macaron.Context) string {
|
||||
url := ctx.QueryTrim("url")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateSlackUrl(url)
|
||||
url := ctx.QueryTrim("url")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateSlackUrl(url)
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
func CreateSlackChannel(ctx *macaron.Context) string {
|
||||
channel := ctx.QueryTrim("channel")
|
||||
settingModel := new(models.Setting)
|
||||
if settingModel.IsChannelExist(channel) {
|
||||
json := utils.JsonResponse{}
|
||||
func CreateSlackChannel(ctx *macaron.Context) string {
|
||||
channel := ctx.QueryTrim("channel")
|
||||
settingModel := new(models.Setting)
|
||||
if settingModel.IsChannelExist(channel) {
|
||||
json := utils.JsonResponse{}
|
||||
|
||||
return json.CommonFailure("Channel已存在")
|
||||
}
|
||||
_, err := settingModel.CreateChannel(channel)
|
||||
return json.CommonFailure("Channel已存在")
|
||||
}
|
||||
_, err := settingModel.CreateChannel(channel)
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
func RemoveSlackChannel(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.RemoveChannel(id)
|
||||
func RemoveSlackChannel(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.RemoveChannel(id)
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region 邮件
|
||||
|
||||
func EditMail(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "邮件配置"
|
||||
settingModel := new(models.Setting)
|
||||
mail, err := settingModel.Mail()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Mail"] = mail
|
||||
ctx.HTML(200, "manage/mail")
|
||||
func EditMail(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "邮件配置"
|
||||
settingModel := new(models.Setting)
|
||||
mail, err := settingModel.Mail()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Mail"] = mail
|
||||
ctx.HTML(200, "manage/mail")
|
||||
}
|
||||
|
||||
func Mail(ctx *macaron.Context) string {
|
||||
settingModel := new(models.Setting)
|
||||
mail, err := settingModel.Mail()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
settingModel := new(models.Setting)
|
||||
mail, err := settingModel.Mail()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
json := utils.JsonResponse{}
|
||||
json := utils.JsonResponse{}
|
||||
|
||||
return json.Success("", mail)
|
||||
return json.Success("", mail)
|
||||
}
|
||||
|
||||
|
||||
type MailServerForm struct {
|
||||
Host string `binding:"Required;MaxSize(100)"`
|
||||
Port int `binding:"Required;Range(1-65535)"`
|
||||
User string `binding:"Required;MaxSize(64);Email"`
|
||||
Password string `binding:"Required;MaxSize(64)"`
|
||||
Host string `binding:"Required;MaxSize(100)"`
|
||||
Port int `binding:"Required;Range(1-65535)"`
|
||||
User string `binding:"Required;MaxSize(64);Email"`
|
||||
Password string `binding:"Required;MaxSize(64)"`
|
||||
}
|
||||
|
||||
func UpdateMailServer(ctx *macaron.Context, form MailServerForm) string {
|
||||
jsonByte, _ := json.Marshal(form)
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateMailServer(string(jsonByte))
|
||||
jsonByte, _ := json.Marshal(form)
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateMailServer(string(jsonByte))
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
func ClearMailServer(ctx *macaron.Context) string {
|
||||
jsonByte, _ := json.Marshal(MailServerForm{})
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateMailServer(string(jsonByte))
|
||||
jsonByte, _ := json.Marshal(MailServerForm{})
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.UpdateMailServer(string(jsonByte))
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
func CreateMailUser(ctx *macaron.Context) string {
|
||||
username := ctx.QueryTrim("username")
|
||||
email := ctx.QueryTrim("email")
|
||||
settingModel := new(models.Setting)
|
||||
if username == "" || email == "" {
|
||||
json := utils.JsonResponse{}
|
||||
func CreateMailUser(ctx *macaron.Context) string {
|
||||
username := ctx.QueryTrim("username")
|
||||
email := ctx.QueryTrim("email")
|
||||
settingModel := new(models.Setting)
|
||||
if username == "" || email == "" {
|
||||
json := utils.JsonResponse{}
|
||||
|
||||
return json.CommonFailure("用户名、邮箱均不能为空")
|
||||
}
|
||||
_, err := settingModel.CreateMailUser(username, email)
|
||||
return json.CommonFailure("用户名、邮箱均不能为空")
|
||||
}
|
||||
_, err := settingModel.CreateMailUser(username, email)
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
func RemoveMailUser(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.RemoveMailUser(id)
|
||||
func RemoveMailUser(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
settingModel := new(models.Setting)
|
||||
_, err := settingModel.RemoveMailUser(id)
|
||||
|
||||
return utils.JsonResponseByErr(err)
|
||||
return utils.JsonResponseByErr(err)
|
||||
}
|
||||
|
||||
// endregion
|
||||
// endregion
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
package routers
|
||||
|
||||
import (
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/ouqiang/gocron/routers/install"
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/routers/task"
|
||||
"github.com/ouqiang/gocron/routers/host"
|
||||
"github.com/ouqiang/gocron/routers/tasklog"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"strings"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/routers/user"
|
||||
"github.com/go-macaron/gzip"
|
||||
"github.com/ouqiang/gocron/routers/manage"
|
||||
"github.com/ouqiang/gocron/routers/loginlog"
|
||||
"time"
|
||||
"strconv"
|
||||
"html/template"
|
||||
"github.com/go-macaron/cache"
|
||||
"github.com/go-macaron/captcha"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/go-macaron/cache"
|
||||
"github.com/go-macaron/captcha"
|
||||
"github.com/go-macaron/gzip"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/go-macaron/toolbox"
|
||||
"github.com/ouqiang/gocron/modules/app"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/routers/host"
|
||||
"github.com/ouqiang/gocron/routers/install"
|
||||
"github.com/ouqiang/gocron/routers/loginlog"
|
||||
"github.com/ouqiang/gocron/routers/manage"
|
||||
"github.com/ouqiang/gocron/routers/task"
|
||||
"github.com/ouqiang/gocron/routers/tasklog"
|
||||
"github.com/ouqiang/gocron/routers/user"
|
||||
"gopkg.in/macaron.v1"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 静态文件目录
|
||||
|
@ -29,261 +29,261 @@ const StaticDir = "public"
|
|||
|
||||
// 路由注册
|
||||
func Register(m *macaron.Macaron) {
|
||||
// 所有GET方法,自动注册HEAD方法
|
||||
m.SetAutoHead(true)
|
||||
// 首页
|
||||
m.Get("/", Home)
|
||||
// 系统安装
|
||||
m.Group("/install", func() {
|
||||
m.Get("", install.Create)
|
||||
m.Post("/store", binding.Bind(install.InstallForm{}), install.Store)
|
||||
})
|
||||
// 所有GET方法,自动注册HEAD方法
|
||||
m.SetAutoHead(true)
|
||||
// 首页
|
||||
m.Get("/", Home)
|
||||
// 系统安装
|
||||
m.Group("/install", func() {
|
||||
m.Get("", install.Create)
|
||||
m.Post("/store", binding.Bind(install.InstallForm{}), install.Store)
|
||||
})
|
||||
|
||||
// 用户
|
||||
m.Group("/user", func() {
|
||||
m.Get("/login", user.Login)
|
||||
m.Post("/login", user.ValidateLogin)
|
||||
m.Get("/logout", user.Logout)
|
||||
m.Get("/editPassword", user.EditPassword)
|
||||
m.Post("/editPassword", user.UpdatePassword)
|
||||
})
|
||||
// 用户
|
||||
m.Group("/user", func() {
|
||||
m.Get("/login", user.Login)
|
||||
m.Post("/login", user.ValidateLogin)
|
||||
m.Get("/logout", user.Logout)
|
||||
m.Get("/editPassword", user.EditPassword)
|
||||
m.Post("/editPassword", user.UpdatePassword)
|
||||
})
|
||||
|
||||
// 定时任务
|
||||
m.Group("/task", func() {
|
||||
m.Get("/create", task.Create)
|
||||
m.Post("/store", binding.Bind(task.TaskForm{}), task.Store)
|
||||
m.Get("/edit/:id", task.Edit)
|
||||
m.Get("", task.Index)
|
||||
m.Get("/log", tasklog.Index)
|
||||
m.Post("/log/clear", tasklog.Clear)
|
||||
m.Post("/remove/:id", task.Remove)
|
||||
m.Post("/enable/:id", task.Enable)
|
||||
m.Post("/disable/:id", task.Disable)
|
||||
m.Get("/run/:id", task.Run)
|
||||
})
|
||||
// 定时任务
|
||||
m.Group("/task", func() {
|
||||
m.Get("/create", task.Create)
|
||||
m.Post("/store", binding.Bind(task.TaskForm{}), task.Store)
|
||||
m.Get("/edit/:id", task.Edit)
|
||||
m.Get("", task.Index)
|
||||
m.Get("/log", tasklog.Index)
|
||||
m.Post("/log/clear", tasklog.Clear)
|
||||
m.Post("/remove/:id", task.Remove)
|
||||
m.Post("/enable/:id", task.Enable)
|
||||
m.Post("/disable/:id", task.Disable)
|
||||
m.Get("/run/:id", task.Run)
|
||||
})
|
||||
|
||||
// 主机
|
||||
m.Group("/host", func() {
|
||||
m.Get("/create", host.Create)
|
||||
m.Get("/edit/:id", host.Edit)
|
||||
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
||||
m.Get("", host.Index)
|
||||
m.Get("/ping/:id", host.Ping)
|
||||
m.Post("/remove/:id", host.Remove)
|
||||
})
|
||||
// 主机
|
||||
m.Group("/host", func() {
|
||||
m.Get("/create", host.Create)
|
||||
m.Get("/edit/:id", host.Edit)
|
||||
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
||||
m.Get("", host.Index)
|
||||
m.Get("/ping/:id", host.Ping)
|
||||
m.Post("/remove/:id", host.Remove)
|
||||
})
|
||||
|
||||
// 管理
|
||||
m.Group("/manage", func() {
|
||||
m.Group("/slack", func() {
|
||||
m.Get("/", manage.Slack)
|
||||
m.Get("/edit", manage.EditSlack)
|
||||
m.Post("/url", manage.UpdateSlackUrl)
|
||||
m.Post("/channel", manage.CreateSlackChannel)
|
||||
m.Post("/channel/remove/:id", manage.RemoveSlackChannel)
|
||||
})
|
||||
m.Group("/mail", func() {
|
||||
m.Get("/", manage.Mail)
|
||||
m.Get("/edit", manage.EditMail)
|
||||
m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer)
|
||||
m.Post("/server/clear", manage.ClearMailServer)
|
||||
m.Post("/user", manage.CreateMailUser)
|
||||
m.Post("/user/remove/:id", manage.RemoveMailUser)
|
||||
})
|
||||
m.Get("/login-log", loginlog.Index)
|
||||
})
|
||||
// 管理
|
||||
m.Group("/manage", func() {
|
||||
m.Group("/slack", func() {
|
||||
m.Get("/", manage.Slack)
|
||||
m.Get("/edit", manage.EditSlack)
|
||||
m.Post("/url", manage.UpdateSlackUrl)
|
||||
m.Post("/channel", manage.CreateSlackChannel)
|
||||
m.Post("/channel/remove/:id", manage.RemoveSlackChannel)
|
||||
})
|
||||
m.Group("/mail", func() {
|
||||
m.Get("/", manage.Mail)
|
||||
m.Get("/edit", manage.EditMail)
|
||||
m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer)
|
||||
m.Post("/server/clear", manage.ClearMailServer)
|
||||
m.Post("/user", manage.CreateMailUser)
|
||||
m.Post("/user/remove/:id", manage.RemoveMailUser)
|
||||
})
|
||||
m.Get("/login-log", loginlog.Index)
|
||||
})
|
||||
|
||||
// API
|
||||
m.Group("/api/v1", func() {
|
||||
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
||||
m.Post("/task/enable/:id", task.Enable)
|
||||
m.Post("/task/disable/:id", task.Disable)
|
||||
}, apiAuth);
|
||||
// API
|
||||
m.Group("/api/v1", func() {
|
||||
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
||||
m.Post("/task/enable/:id", task.Enable)
|
||||
m.Post("/task/disable/:id", task.Disable)
|
||||
}, apiAuth)
|
||||
|
||||
// 404错误
|
||||
m.NotFound(func(ctx *macaron.Context) {
|
||||
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
|
||||
ctx.Data["Title"] = "404 - NOT FOUND"
|
||||
ctx.HTML(404, "error/404")
|
||||
} else {
|
||||
json := utils.JsonResponse{}
|
||||
ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在")))
|
||||
}
|
||||
})
|
||||
// 50x错误
|
||||
m.InternalServerError(func(ctx *macaron.Context) {
|
||||
logger.Debug("500错误")
|
||||
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
|
||||
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
|
||||
ctx.HTML(500, "error/500")
|
||||
} else {
|
||||
json := utils.JsonResponse{}
|
||||
ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试")))
|
||||
}
|
||||
})
|
||||
// 404错误
|
||||
m.NotFound(func(ctx *macaron.Context) {
|
||||
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
|
||||
ctx.Data["Title"] = "404 - NOT FOUND"
|
||||
ctx.HTML(404, "error/404")
|
||||
} else {
|
||||
json := utils.JsonResponse{}
|
||||
ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在")))
|
||||
}
|
||||
})
|
||||
// 50x错误
|
||||
m.InternalServerError(func(ctx *macaron.Context) {
|
||||
logger.Debug("500错误")
|
||||
if isGetRequest(ctx) && !isAjaxRequest(ctx) {
|
||||
ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR"
|
||||
ctx.HTML(500, "error/500")
|
||||
} else {
|
||||
json := utils.JsonResponse{}
|
||||
ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试")))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 中间件注册
|
||||
func RegisterMiddleware(m *macaron.Macaron) {
|
||||
m.Use(macaron.Logger())
|
||||
m.Use(macaron.Recovery())
|
||||
if macaron.Env != macaron.DEV {
|
||||
m.Use(gzip.Gziper())
|
||||
}
|
||||
m.Use(macaron.Static(StaticDir))
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: "templates",
|
||||
Extensions: []string{".html"},
|
||||
// 模板语法分隔符,默认为 ["{{", "}}"]
|
||||
Delims: macaron.Delims{"{{{", "}}}"},
|
||||
// 追加的 Content-Type 头信息,默认为 "UTF-8"
|
||||
Charset: "UTF-8",
|
||||
// 渲染具有缩进格式的 JSON,默认为不缩进
|
||||
IndentJSON: true,
|
||||
// 渲染具有缩进格式的 XML,默认为不缩进
|
||||
IndentXML: true,
|
||||
Funcs: []template.FuncMap{map[string]interface{} {
|
||||
"HostFormat": func(index int) bool {
|
||||
return (index + 1) % 3 == 0
|
||||
},
|
||||
"unescape": func(str string) template.HTML {
|
||||
return template.HTML(str)
|
||||
},
|
||||
}},
|
||||
}))
|
||||
m.Use(cache.Cacher())
|
||||
m.Use(captcha.Captchaer())
|
||||
m.Use(session.Sessioner(session.Options{
|
||||
Provider: "file",
|
||||
ProviderConfig: app.DataDir + "/sessions",
|
||||
}))
|
||||
m.Use(toolbox.Toolboxer(m))
|
||||
checkAppInstall(m)
|
||||
m.Use(func(ctx *macaron.Context, sess session.Store){
|
||||
if app.Installed {
|
||||
ipAuth(ctx)
|
||||
userAuth(ctx, sess)
|
||||
setShareData(ctx, sess)
|
||||
}
|
||||
})
|
||||
m.Use(macaron.Logger())
|
||||
m.Use(macaron.Recovery())
|
||||
if macaron.Env != macaron.DEV {
|
||||
m.Use(gzip.Gziper())
|
||||
}
|
||||
m.Use(macaron.Static(StaticDir))
|
||||
m.Use(macaron.Renderer(macaron.RenderOptions{
|
||||
Directory: "templates",
|
||||
Extensions: []string{".html"},
|
||||
// 模板语法分隔符,默认为 ["{{", "}}"]
|
||||
Delims: macaron.Delims{"{{{", "}}}"},
|
||||
// 追加的 Content-Type 头信息,默认为 "UTF-8"
|
||||
Charset: "UTF-8",
|
||||
// 渲染具有缩进格式的 JSON,默认为不缩进
|
||||
IndentJSON: true,
|
||||
// 渲染具有缩进格式的 XML,默认为不缩进
|
||||
IndentXML: true,
|
||||
Funcs: []template.FuncMap{map[string]interface{}{
|
||||
"HostFormat": func(index int) bool {
|
||||
return (index+1)%3 == 0
|
||||
},
|
||||
"unescape": func(str string) template.HTML {
|
||||
return template.HTML(str)
|
||||
},
|
||||
}},
|
||||
}))
|
||||
m.Use(cache.Cacher())
|
||||
m.Use(captcha.Captchaer())
|
||||
m.Use(session.Sessioner(session.Options{
|
||||
Provider: "file",
|
||||
ProviderConfig: app.DataDir + "/sessions",
|
||||
}))
|
||||
m.Use(toolbox.Toolboxer(m))
|
||||
checkAppInstall(m)
|
||||
m.Use(func(ctx *macaron.Context, sess session.Store) {
|
||||
if app.Installed {
|
||||
ipAuth(ctx)
|
||||
userAuth(ctx, sess)
|
||||
setShareData(ctx, sess)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// region 自定义中间件
|
||||
|
||||
/** 系统未安装,重定向到安装页面 **/
|
||||
func checkAppInstall(m *macaron.Macaron) {
|
||||
m.Use(func(ctx *macaron.Context) {
|
||||
installUrl := "/install"
|
||||
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
|
||||
return
|
||||
}
|
||||
if !app.Installed {
|
||||
ctx.Redirect(installUrl)
|
||||
}
|
||||
})
|
||||
func checkAppInstall(m *macaron.Macaron) {
|
||||
m.Use(func(ctx *macaron.Context) {
|
||||
installUrl := "/install"
|
||||
if strings.HasPrefix(ctx.Req.URL.Path, installUrl) {
|
||||
return
|
||||
}
|
||||
if !app.Installed {
|
||||
ctx.Redirect(installUrl)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// IP验证, 通过反向代理访问gocron,需设置Header X-Real-IP才能获取到客户端真实IP
|
||||
func ipAuth(ctx *macaron.Context) {
|
||||
allowIpsStr := app.Setting.AllowIps
|
||||
if allowIpsStr == "" {
|
||||
return
|
||||
}
|
||||
clientIp := ctx.RemoteAddr()
|
||||
allowIps := strings.Split(allowIpsStr, ",")
|
||||
if !utils.InStringSlice(allowIps, clientIp) {
|
||||
logger.Warnf("非法IP访问-%s", clientIp)
|
||||
ctx.Status(403)
|
||||
}
|
||||
func ipAuth(ctx *macaron.Context) {
|
||||
allowIpsStr := app.Setting.AllowIps
|
||||
if allowIpsStr == "" {
|
||||
return
|
||||
}
|
||||
clientIp := ctx.RemoteAddr()
|
||||
allowIps := strings.Split(allowIpsStr, ",")
|
||||
if !utils.InStringSlice(allowIps, clientIp) {
|
||||
logger.Warnf("非法IP访问-%s", clientIp)
|
||||
ctx.Status(403)
|
||||
}
|
||||
}
|
||||
|
||||
// 用户认证
|
||||
func userAuth(ctx *macaron.Context, sess session.Store) {
|
||||
if user.IsLogin(sess) {
|
||||
return
|
||||
}
|
||||
uri := ctx.Req.URL.Path
|
||||
found := false
|
||||
excludePaths := []string{"/install", "/user/login", "/api"}
|
||||
for _, path := range excludePaths {
|
||||
if strings.HasPrefix(uri, path) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
ctx.Redirect("/user/login")
|
||||
}
|
||||
func userAuth(ctx *macaron.Context, sess session.Store) {
|
||||
if user.IsLogin(sess) {
|
||||
return
|
||||
}
|
||||
uri := ctx.Req.URL.Path
|
||||
found := false
|
||||
excludePaths := []string{"/install", "/user/login", "/api"}
|
||||
for _, path := range excludePaths {
|
||||
if strings.HasPrefix(uri, path) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
ctx.Redirect("/user/login")
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置共享数据 **/
|
||||
func setShareData(ctx *macaron.Context, sess session.Store) {
|
||||
ctx.Data["URI"] = ctx.Req.URL.Path
|
||||
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
|
||||
paths := strings.Split(urlPath, "/")
|
||||
ctx.Data["Controller"] = ""
|
||||
ctx.Data["Action"] = ""
|
||||
if len(paths) > 0 {
|
||||
ctx.Data["Controller"] = paths[0]
|
||||
}
|
||||
if len(paths) > 1 {
|
||||
ctx.Data["Action"] = paths[1]
|
||||
}
|
||||
ctx.Data["LoginUsername"] = user.Username(sess)
|
||||
ctx.Data["LoginUid"] = user.Uid(sess)
|
||||
ctx.Data["AppName"] = app.Setting.AppName
|
||||
func setShareData(ctx *macaron.Context, sess session.Store) {
|
||||
ctx.Data["URI"] = ctx.Req.URL.Path
|
||||
urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/")
|
||||
paths := strings.Split(urlPath, "/")
|
||||
ctx.Data["Controller"] = ""
|
||||
ctx.Data["Action"] = ""
|
||||
if len(paths) > 0 {
|
||||
ctx.Data["Controller"] = paths[0]
|
||||
}
|
||||
if len(paths) > 1 {
|
||||
ctx.Data["Action"] = paths[1]
|
||||
}
|
||||
ctx.Data["LoginUsername"] = user.Username(sess)
|
||||
ctx.Data["LoginUid"] = user.Uid(sess)
|
||||
ctx.Data["AppName"] = app.Setting.AppName
|
||||
}
|
||||
|
||||
/** API接口签名验证 **/
|
||||
func apiAuth(ctx *macaron.Context) {
|
||||
if !app.Setting.ApiSignEnable {
|
||||
return
|
||||
}
|
||||
apiKey := strings.TrimSpace(app.Setting.ApiKey)
|
||||
apiSecret := strings.TrimSpace(app.Setting.ApiSecret)
|
||||
json := utils.JsonResponse{}
|
||||
if apiKey == "" || apiSecret == "" {
|
||||
msg := json.CommonFailure("使用API前, 请先配置密钥")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
currentTimestamp := time.Now().Unix()
|
||||
time := ctx.QueryInt64("time")
|
||||
if time <= 0 {
|
||||
msg := json.CommonFailure("参数time不能为空")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
if time < (currentTimestamp - 1800) {
|
||||
msg := json.CommonFailure("time无效")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
sign := ctx.QueryTrim("sign")
|
||||
if sign == "" {
|
||||
msg := json.CommonFailure("参数sign不能为空")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret
|
||||
realSign := utils.Md5(raw)
|
||||
if sign != realSign {
|
||||
msg := json.CommonFailure("签名验证失败")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
func apiAuth(ctx *macaron.Context) {
|
||||
if !app.Setting.ApiSignEnable {
|
||||
return
|
||||
}
|
||||
apiKey := strings.TrimSpace(app.Setting.ApiKey)
|
||||
apiSecret := strings.TrimSpace(app.Setting.ApiSecret)
|
||||
json := utils.JsonResponse{}
|
||||
if apiKey == "" || apiSecret == "" {
|
||||
msg := json.CommonFailure("使用API前, 请先配置密钥")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
currentTimestamp := time.Now().Unix()
|
||||
time := ctx.QueryInt64("time")
|
||||
if time <= 0 {
|
||||
msg := json.CommonFailure("参数time不能为空")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
if time < (currentTimestamp - 1800) {
|
||||
msg := json.CommonFailure("time无效")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
sign := ctx.QueryTrim("sign")
|
||||
if sign == "" {
|
||||
msg := json.CommonFailure("参数sign不能为空")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret
|
||||
realSign := utils.Md5(raw)
|
||||
if sign != realSign {
|
||||
msg := json.CommonFailure("签名验证失败")
|
||||
ctx.Write([]byte(msg))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
func isAjaxRequest(ctx *macaron.Context) bool {
|
||||
req := ctx.Req.Header.Get("X-Requested-With")
|
||||
if req == "XMLHttpRequest" {
|
||||
return true
|
||||
}
|
||||
req := ctx.Req.Header.Get("X-Requested-With")
|
||||
if req == "XMLHttpRequest" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
func isGetRequest(ctx *macaron.Context) bool {
|
||||
return ctx.Req.Method == "GET"
|
||||
}
|
||||
return ctx.Req.Method == "GET"
|
||||
}
|
||||
|
|
|
@ -1,335 +1,334 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"strconv"
|
||||
"github.com/jakecoffman/cron"
|
||||
"github.com/Unknwon/paginater"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"github.com/go-macaron/binding"
|
||||
"strings"
|
||||
"fmt"
|
||||
"github.com/Unknwon/paginater"
|
||||
"github.com/go-macaron/binding"
|
||||
"github.com/jakecoffman/cron"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"github.com/ouqiang/gocron/service"
|
||||
"gopkg.in/macaron.v1"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TaskForm struct {
|
||||
Id int
|
||||
Level models.TaskLevel `binding:"Required;In(1,2)"`
|
||||
DependencyStatus models.TaskDependencyStatus
|
||||
DependencyTaskId string
|
||||
Name string `binding:"Required;MaxSize(32)"`
|
||||
Spec string
|
||||
Protocol models.TaskProtocol `binding:"In(1,2)"`
|
||||
Command string `binding:"Required;MaxSize(256)"`
|
||||
Timeout int `binding:"Range(0,86400)"`
|
||||
Multi int8 `binding:"In(1,2)"`
|
||||
RetryTimes int8
|
||||
HostId string
|
||||
Tag string
|
||||
Remark string
|
||||
NotifyStatus int8 `binding:"In(1,2,3)"`
|
||||
NotifyType int8 `binding:"In(1,2,3)"`
|
||||
NotifyReceiverId string
|
||||
Id int
|
||||
Level models.TaskLevel `binding:"Required;In(1,2)"`
|
||||
DependencyStatus models.TaskDependencyStatus
|
||||
DependencyTaskId string
|
||||
Name string `binding:"Required;MaxSize(32)"`
|
||||
Spec string
|
||||
Protocol models.TaskProtocol `binding:"In(1,2)"`
|
||||
Command string `binding:"Required;MaxSize(256)"`
|
||||
Timeout int `binding:"Range(0,86400)"`
|
||||
Multi int8 `binding:"In(1,2)"`
|
||||
RetryTimes int8
|
||||
HostId string
|
||||
Tag string
|
||||
Remark string
|
||||
NotifyStatus int8 `binding:"In(1,2,3)"`
|
||||
NotifyType int8 `binding:"In(1,2,3)"`
|
||||
NotifyReceiverId string
|
||||
}
|
||||
|
||||
|
||||
func (f TaskForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
}
|
||||
json := utils.JsonResponse{}
|
||||
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||
|
||||
ctx.Resp.Write([]byte(content))
|
||||
ctx.Resp.Write([]byte(content))
|
||||
}
|
||||
|
||||
// 首页
|
||||
func Index(ctx *macaron.Context) {
|
||||
taskModel := new(models.Task)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := taskModel.Total(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
tasks, err := taskModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
name, ok := queryParams["name"].(string)
|
||||
var safeNameHTML = ""
|
||||
if ok {
|
||||
safeNameHTML = template.HTMLEscapeString(name)
|
||||
}
|
||||
PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&tag=%s&status=%d&page_size=%d",
|
||||
queryParams["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Tag"], queryParams["Status"], queryParams["PageSize"]);
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
setHostsToTemplate(ctx)
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.Data["Title"] = "任务列表"
|
||||
ctx.Data["Tasks"] = tasks
|
||||
ctx.HTML(200, "task/index")
|
||||
func Index(ctx *macaron.Context) {
|
||||
taskModel := new(models.Task)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := taskModel.Total(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
tasks, err := taskModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
name, ok := queryParams["name"].(string)
|
||||
var safeNameHTML = ""
|
||||
if ok {
|
||||
safeNameHTML = template.HTMLEscapeString(name)
|
||||
}
|
||||
PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&tag=%s&status=%d&page_size=%d",
|
||||
queryParams["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Tag"], queryParams["Status"], queryParams["PageSize"])
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
setHostsToTemplate(ctx)
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.Data["Title"] = "任务列表"
|
||||
ctx.Data["Tasks"] = tasks
|
||||
ctx.HTML(200, "task/index")
|
||||
}
|
||||
|
||||
// 新增页面
|
||||
func Create(ctx *macaron.Context) {
|
||||
setHostsToTemplate(ctx)
|
||||
ctx.Data["Title"] = "添加任务"
|
||||
ctx.HTML(200, "task/task_form")
|
||||
func Create(ctx *macaron.Context) {
|
||||
setHostsToTemplate(ctx)
|
||||
ctx.Data["Title"] = "添加任务"
|
||||
ctx.HTML(200, "task/task_form")
|
||||
}
|
||||
|
||||
// 编辑页面
|
||||
func Edit(ctx *macaron.Context) {
|
||||
id := ctx.ParamsInt(":id")
|
||||
taskModel := new(models.Task)
|
||||
task, err := taskModel.Detail(id)
|
||||
if err != nil || task.Id != id {
|
||||
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error())
|
||||
ctx.Redirect("/task")
|
||||
}
|
||||
hostModel := new(models.Host)
|
||||
hostModel.PageSize = -1
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
} else {
|
||||
for i, host := range(hosts) {
|
||||
if inHosts(task.Hosts, host.Id) {
|
||||
hosts[i].Selected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
func Edit(ctx *macaron.Context) {
|
||||
id := ctx.ParamsInt(":id")
|
||||
taskModel := new(models.Task)
|
||||
task, err := taskModel.Detail(id)
|
||||
if err != nil || task.Id != id {
|
||||
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error())
|
||||
ctx.Redirect("/task")
|
||||
}
|
||||
hostModel := new(models.Host)
|
||||
hostModel.PageSize = -1
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
} else {
|
||||
for i, host := range hosts {
|
||||
if inHosts(task.Hosts, host.Id) {
|
||||
hosts[i].Selected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["Task"] = task
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["Title"] = "编辑"
|
||||
ctx.HTML(200, "task/task_form")
|
||||
ctx.Data["Task"] = task
|
||||
ctx.Data["Hosts"] = hosts
|
||||
ctx.Data["Title"] = "编辑"
|
||||
ctx.HTML(200, "task/task_form")
|
||||
}
|
||||
|
||||
// 保存任务
|
||||
func Store(ctx *macaron.Context, form TaskForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := models.Task{}
|
||||
var id int = form.Id
|
||||
nameExists, err := taskModel.NameExist(form.Name, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
if nameExists {
|
||||
return json.CommonFailure("任务名称已存在")
|
||||
}
|
||||
func Store(ctx *macaron.Context, form TaskForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := models.Task{}
|
||||
var id int = form.Id
|
||||
nameExists, err := taskModel.NameExist(form.Name, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
if nameExists {
|
||||
return json.CommonFailure("任务名称已存在")
|
||||
}
|
||||
|
||||
if form.Protocol == models.TaskRPC && form.HostId == "" {
|
||||
return json.CommonFailure("请选择主机名")
|
||||
}
|
||||
if form.Protocol == models.TaskRPC && form.HostId == "" {
|
||||
return json.CommonFailure("请选择主机名")
|
||||
}
|
||||
|
||||
taskModel.Name = form.Name
|
||||
taskModel.Protocol = form.Protocol
|
||||
taskModel.Command = form.Command
|
||||
taskModel.Timeout = form.Timeout
|
||||
taskModel.Tag = form.Tag
|
||||
taskModel.Remark = form.Remark
|
||||
taskModel.Multi = form.Multi
|
||||
taskModel.RetryTimes = form.RetryTimes
|
||||
if taskModel.Multi != 1 {
|
||||
taskModel.Multi = 0
|
||||
}
|
||||
taskModel.NotifyStatus = form.NotifyStatus - 1
|
||||
taskModel.NotifyType = form.NotifyType - 1
|
||||
taskModel.NotifyReceiverId = form.NotifyReceiverId
|
||||
taskModel.Spec = form.Spec
|
||||
taskModel.Level = form.Level
|
||||
taskModel.DependencyStatus = form.DependencyStatus
|
||||
taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId)
|
||||
if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" {
|
||||
return json.CommonFailure("至少选择一个通知接收者")
|
||||
}
|
||||
if taskModel.Protocol == models.TaskHTTP {
|
||||
command := strings.ToLower(taskModel.Command)
|
||||
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
|
||||
return json.CommonFailure("请输入正确的URL地址")
|
||||
}
|
||||
if taskModel.Timeout > 300 {
|
||||
return json.CommonFailure("HTTP任务超时时间不能超过300秒")
|
||||
}
|
||||
}
|
||||
taskModel.Name = form.Name
|
||||
taskModel.Protocol = form.Protocol
|
||||
taskModel.Command = form.Command
|
||||
taskModel.Timeout = form.Timeout
|
||||
taskModel.Tag = form.Tag
|
||||
taskModel.Remark = form.Remark
|
||||
taskModel.Multi = form.Multi
|
||||
taskModel.RetryTimes = form.RetryTimes
|
||||
if taskModel.Multi != 1 {
|
||||
taskModel.Multi = 0
|
||||
}
|
||||
taskModel.NotifyStatus = form.NotifyStatus - 1
|
||||
taskModel.NotifyType = form.NotifyType - 1
|
||||
taskModel.NotifyReceiverId = form.NotifyReceiverId
|
||||
taskModel.Spec = form.Spec
|
||||
taskModel.Level = form.Level
|
||||
taskModel.DependencyStatus = form.DependencyStatus
|
||||
taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId)
|
||||
if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" {
|
||||
return json.CommonFailure("至少选择一个通知接收者")
|
||||
}
|
||||
if taskModel.Protocol == models.TaskHTTP {
|
||||
command := strings.ToLower(taskModel.Command)
|
||||
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
|
||||
return json.CommonFailure("请输入正确的URL地址")
|
||||
}
|
||||
if taskModel.Timeout > 300 {
|
||||
return json.CommonFailure("HTTP任务超时时间不能超过300秒")
|
||||
}
|
||||
}
|
||||
|
||||
if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 {
|
||||
return json.CommonFailure("任务重试次数取值0-10")
|
||||
}
|
||||
if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 {
|
||||
return json.CommonFailure("任务重试次数取值0-10")
|
||||
}
|
||||
|
||||
if (taskModel.DependencyStatus != models.TaskDependencyStatusStrong &&
|
||||
taskModel.DependencyStatus != models.TaskDependencyStatusWeak) {
|
||||
return json.CommonFailure("请选择依赖关系")
|
||||
}
|
||||
if taskModel.DependencyStatus != models.TaskDependencyStatusStrong &&
|
||||
taskModel.DependencyStatus != models.TaskDependencyStatusWeak {
|
||||
return json.CommonFailure("请选择依赖关系")
|
||||
}
|
||||
|
||||
if taskModel.Level == models.TaskLevelParent {
|
||||
_, err = cron.Parse(form.Spec)
|
||||
if err != nil {
|
||||
return json.CommonFailure("crontab表达式解析失败", err)
|
||||
}
|
||||
} else {
|
||||
taskModel.DependencyTaskId = ""
|
||||
taskModel.Spec = ""
|
||||
}
|
||||
if taskModel.Level == models.TaskLevelParent {
|
||||
_, err = cron.Parse(form.Spec)
|
||||
if err != nil {
|
||||
return json.CommonFailure("crontab表达式解析失败", err)
|
||||
}
|
||||
} else {
|
||||
taskModel.DependencyTaskId = ""
|
||||
taskModel.Spec = ""
|
||||
}
|
||||
|
||||
if id > 0 && taskModel.DependencyTaskId != "" {
|
||||
dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",")
|
||||
if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) {
|
||||
return json.CommonFailure("不允许设置当前任务为子任务")
|
||||
}
|
||||
}
|
||||
if id > 0 && taskModel.DependencyTaskId != "" {
|
||||
dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",")
|
||||
if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) {
|
||||
return json.CommonFailure("不允许设置当前任务为子任务")
|
||||
}
|
||||
}
|
||||
|
||||
if id == 0 {
|
||||
// 任务添加后开始调度执行
|
||||
taskModel.Status = models.Running
|
||||
id, err = taskModel.Create()
|
||||
} else {
|
||||
_, err = taskModel.UpdateBean(id)
|
||||
}
|
||||
if id == 0 {
|
||||
// 任务添加后开始调度执行
|
||||
taskModel.Status = models.Running
|
||||
id, err = taskModel.Create()
|
||||
} else {
|
||||
_, err = taskModel.UpdateBean(id)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return json.CommonFailure("保存失败", err)
|
||||
}
|
||||
if err != nil {
|
||||
return json.CommonFailure("保存失败", err)
|
||||
}
|
||||
|
||||
taskHostModel := new(models.TaskHost)
|
||||
if form.Protocol == models.TaskRPC {
|
||||
hostIdStrList := strings.Split(form.HostId, ",")
|
||||
hostIds := make([]int, len(hostIdStrList))
|
||||
for i, hostIdStr := range hostIdStrList {
|
||||
hostIds[i], _ = strconv.Atoi(hostIdStr)
|
||||
}
|
||||
taskHostModel.Add(id, hostIds)
|
||||
} else {
|
||||
taskHostModel.Remove(id)
|
||||
}
|
||||
taskHostModel := new(models.TaskHost)
|
||||
if form.Protocol == models.TaskRPC {
|
||||
hostIdStrList := strings.Split(form.HostId, ",")
|
||||
hostIds := make([]int, len(hostIdStrList))
|
||||
for i, hostIdStr := range hostIdStrList {
|
||||
hostIds[i], _ = strconv.Atoi(hostIdStr)
|
||||
}
|
||||
taskHostModel.Add(id, hostIds)
|
||||
} else {
|
||||
taskHostModel.Remove(id)
|
||||
}
|
||||
|
||||
status, err := taskModel.GetStatus(id)
|
||||
if status == models.Enabled && taskModel.Level == models.TaskLevelParent {
|
||||
addTaskToTimer(id)
|
||||
}
|
||||
status, err := taskModel.GetStatus(id)
|
||||
if status == models.Enabled && taskModel.Level == models.TaskLevelParent {
|
||||
addTaskToTimer(id)
|
||||
}
|
||||
|
||||
return json.Success("保存成功", nil)
|
||||
return json.Success("保存成功", nil)
|
||||
}
|
||||
|
||||
// 删除任务
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
_, err := taskModel.Delete(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
_, err := taskModel.Delete(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
|
||||
taskHostModel := new(models.TaskHost)
|
||||
taskHostModel.Remove(id)
|
||||
taskHostModel := new(models.TaskHost)
|
||||
taskHostModel.Remove(id)
|
||||
|
||||
service.Cron.RemoveJob(strconv.Itoa(id))
|
||||
service.Cron.RemoveJob(strconv.Itoa(id))
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// 激活任务
|
||||
func Enable(ctx *macaron.Context) string {
|
||||
return changeStatus(ctx, models.Enabled)
|
||||
return changeStatus(ctx, models.Enabled)
|
||||
}
|
||||
|
||||
// 暂停任务
|
||||
func Disable(ctx *macaron.Context) string {
|
||||
return changeStatus(ctx, models.Disabled)
|
||||
return changeStatus(ctx, models.Disabled)
|
||||
}
|
||||
|
||||
// 手动运行任务
|
||||
func Run(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
task , err := taskModel.Detail(id)
|
||||
if err != nil || task.Id <= 0 {
|
||||
return json.CommonFailure("获取任务详情失败", err)
|
||||
}
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
task, err := taskModel.Detail(id)
|
||||
if err != nil || task.Id <= 0 {
|
||||
return json.CommonFailure("获取任务详情失败", err)
|
||||
}
|
||||
|
||||
task.Spec = "手动运行"
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Run(task)
|
||||
task.Spec = "手动运行"
|
||||
serviceTask := new(service.Task)
|
||||
serviceTask.Run(task)
|
||||
|
||||
return json.Success("任务已开始运行, 请到任务日志中查看结果", nil);
|
||||
return json.Success("任务已开始运行, 请到任务日志中查看结果", nil)
|
||||
}
|
||||
|
||||
// 改变任务状态
|
||||
func changeStatus(ctx *macaron.Context, status models.Status) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
_, err := taskModel.Update(id, models.CommonMap{
|
||||
"Status": status,
|
||||
})
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
taskModel := new(models.Task)
|
||||
_, err := taskModel.Update(id, models.CommonMap{
|
||||
"Status": status,
|
||||
})
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
|
||||
if status == models.Enabled {
|
||||
addTaskToTimer(id)
|
||||
} else {
|
||||
service.Cron.RemoveJob(strconv.Itoa(id))
|
||||
}
|
||||
if status == models.Enabled {
|
||||
addTaskToTimer(id)
|
||||
} else {
|
||||
service.Cron.RemoveJob(strconv.Itoa(id))
|
||||
}
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// 添加任务到定时器
|
||||
func addTaskToTimer(id int) {
|
||||
taskModel := new(models.Task)
|
||||
task, err := taskModel.Detail(id)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
func addTaskToTimer(id int) {
|
||||
taskModel := new(models.Task)
|
||||
task, err := taskModel.Detail(id)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
taskService := service.Task{}
|
||||
taskService.Add(task)
|
||||
taskService := service.Task{}
|
||||
taskService.Add(task)
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["HostId"] = ctx.QueryInt("host_id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
params["Tag"] = ctx.QueryTrim("tag")
|
||||
status := ctx.QueryInt("status")
|
||||
if status >=0 {
|
||||
status -= 1
|
||||
}
|
||||
params["Status"] = status
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["Id"] = ctx.QueryInt("id")
|
||||
params["HostId"] = ctx.QueryInt("host_id")
|
||||
params["Name"] = ctx.QueryTrim("name")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
params["Tag"] = ctx.QueryTrim("tag")
|
||||
status := ctx.QueryInt("status")
|
||||
if status >= 0 {
|
||||
status -= 1
|
||||
}
|
||||
params["Status"] = status
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
|
||||
return params
|
||||
return params
|
||||
}
|
||||
|
||||
func setHostsToTemplate(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hostModel.PageSize = -1
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Hosts"] = hosts
|
||||
func setHostsToTemplate(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
hostModel.PageSize = -1
|
||||
hosts, err := hostModel.List(models.CommonMap{})
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["Hosts"] = hosts
|
||||
}
|
||||
|
||||
func inHosts(slice []models.TaskHostDetail, element int16) bool {
|
||||
for _, v := range slice {
|
||||
if v.HostId == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, v := range slice {
|
||||
if v.HostId == element {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -3,78 +3,78 @@ package tasklog
|
|||
// 任务日志
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/Unknwon/paginater"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"fmt"
|
||||
"github.com/Unknwon/paginater"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/routers/base"
|
||||
"gopkg.in/macaron.v1"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
func Index(ctx *macaron.Context) {
|
||||
logModel := new(models.TaskLog)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := logModel.Total(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
logs, err := logModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d",
|
||||
queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"],
|
||||
queryParams["PageSize"]);
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "任务日志"
|
||||
ctx.Data["Logs"] = logs
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "task/log")
|
||||
func Index(ctx *macaron.Context) {
|
||||
logModel := new(models.TaskLog)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
total, err := logModel.Total(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
logs, err := logModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d",
|
||||
queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"],
|
||||
queryParams["PageSize"])
|
||||
queryParams["PageParams"] = template.URL(PageParams)
|
||||
p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5)
|
||||
ctx.Data["Pagination"] = p
|
||||
ctx.Data["Title"] = "任务日志"
|
||||
ctx.Data["Logs"] = logs
|
||||
ctx.Data["Params"] = queryParams
|
||||
ctx.HTML(200, "task/log")
|
||||
}
|
||||
|
||||
// 清空日志
|
||||
func Clear(ctx *macaron.Context) string {
|
||||
taskLogModel := new(models.TaskLog)
|
||||
_, err := taskLogModel.Clear()
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent)
|
||||
}
|
||||
func Clear(ctx *macaron.Context) string {
|
||||
taskLogModel := new(models.TaskLog)
|
||||
_, err := taskLogModel.Clear()
|
||||
json := utils.JsonResponse{}
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent)
|
||||
}
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// 删除N个月前的日志
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
month := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
if month < 1 || month > 12 {
|
||||
return json.CommonFailure("参数取值范围1-12")
|
||||
}
|
||||
taskLogModel := new(models.TaskLog)
|
||||
_, err := taskLogModel.Remove(month)
|
||||
if err != nil {
|
||||
return json.CommonFailure("删除失败", err)
|
||||
}
|
||||
month := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
if month < 1 || month > 12 {
|
||||
return json.CommonFailure("参数取值范围1-12")
|
||||
}
|
||||
taskLogModel := new(models.TaskLog)
|
||||
_, err := taskLogModel.Remove(month)
|
||||
if err != nil {
|
||||
return json.CommonFailure("删除失败", err)
|
||||
}
|
||||
|
||||
return json.Success("删除成功", nil)
|
||||
return json.Success("删除成功", nil)
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) (models.CommonMap) {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["TaskId"] = ctx.QueryInt("task_id")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
status := ctx.QueryInt("status")
|
||||
if status >=0 {
|
||||
status -= 1
|
||||
}
|
||||
params["Status"] = status
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
params["TaskId"] = ctx.QueryInt("task_id")
|
||||
params["Protocol"] = ctx.QueryInt("protocol")
|
||||
status := ctx.QueryInt("status")
|
||||
if status >= 0 {
|
||||
status -= 1
|
||||
}
|
||||
params["Status"] = status
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
|
||||
return params
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
|
|
@ -1,127 +1,126 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/go-macaron/captcha"
|
||||
"github.com/go-macaron/captcha"
|
||||
"github.com/go-macaron/session"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/utils"
|
||||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// @author qiang.ou<qingqianludao@gmail.com>
|
||||
// @date 2017/4/23-14:16
|
||||
|
||||
func Login(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "用户登录"
|
||||
ctx.HTML(200, "user/login")
|
||||
func Login(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "用户登录"
|
||||
ctx.HTML(200, "user/login")
|
||||
}
|
||||
|
||||
func EditPassword(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "修改密码"
|
||||
ctx.HTML(200, "user/editPassword")
|
||||
func EditPassword(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "修改密码"
|
||||
ctx.HTML(200, "user/editPassword")
|
||||
}
|
||||
|
||||
func UpdatePassword(ctx *macaron.Context, sess session.Store) string {
|
||||
oldPassword := ctx.QueryTrim("old_password")
|
||||
newPassword := ctx.QueryTrim("new_password")
|
||||
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||
json := utils.JsonResponse{}
|
||||
if oldPassword == "" || newPassword == "" || confirmNewPassword == "" {
|
||||
return json.CommonFailure("原密码和新密码均不能为空")
|
||||
}
|
||||
if newPassword != confirmNewPassword {
|
||||
return json.CommonFailure("两次输入密码不一致")
|
||||
}
|
||||
if oldPassword == newPassword {
|
||||
return json.CommonFailure("原密码与新密码不能相同")
|
||||
}
|
||||
userModel := new(models.User)
|
||||
if !userModel.Match(Username(sess), oldPassword) {
|
||||
return json.CommonFailure("原密码输入错误")
|
||||
}
|
||||
_, err := userModel.UpdatePassword(Uid(sess), newPassword)
|
||||
if err != nil {
|
||||
return json.CommonFailure("修改失败")
|
||||
}
|
||||
func UpdatePassword(ctx *macaron.Context, sess session.Store) string {
|
||||
oldPassword := ctx.QueryTrim("old_password")
|
||||
newPassword := ctx.QueryTrim("new_password")
|
||||
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||
json := utils.JsonResponse{}
|
||||
if oldPassword == "" || newPassword == "" || confirmNewPassword == "" {
|
||||
return json.CommonFailure("原密码和新密码均不能为空")
|
||||
}
|
||||
if newPassword != confirmNewPassword {
|
||||
return json.CommonFailure("两次输入密码不一致")
|
||||
}
|
||||
if oldPassword == newPassword {
|
||||
return json.CommonFailure("原密码与新密码不能相同")
|
||||
}
|
||||
userModel := new(models.User)
|
||||
if !userModel.Match(Username(sess), oldPassword) {
|
||||
return json.CommonFailure("原密码输入错误")
|
||||
}
|
||||
_, err := userModel.UpdatePassword(Uid(sess), newPassword)
|
||||
if err != nil {
|
||||
return json.CommonFailure("修改失败")
|
||||
}
|
||||
|
||||
return json.Success("修改成功", nil)
|
||||
return json.Success("修改成功", nil)
|
||||
}
|
||||
|
||||
func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captcha) string {
|
||||
username := ctx.QueryTrim("username")
|
||||
password := ctx.QueryTrim("password")
|
||||
json := utils.JsonResponse{}
|
||||
if username == "" || password == "" {
|
||||
return json.CommonFailure("用户名、密码不能为空")
|
||||
}
|
||||
userModel := new (models.User)
|
||||
if !userModel.Match(username, password) {
|
||||
return json.CommonFailure("用户名或密码错误")
|
||||
}
|
||||
if !cpt.VerifyReq(ctx.Req) {
|
||||
return json.Failure(utils.CaptchaError, "验证码错误")
|
||||
}
|
||||
username := ctx.QueryTrim("username")
|
||||
password := ctx.QueryTrim("password")
|
||||
json := utils.JsonResponse{}
|
||||
if username == "" || password == "" {
|
||||
return json.CommonFailure("用户名、密码不能为空")
|
||||
}
|
||||
userModel := new(models.User)
|
||||
if !userModel.Match(username, password) {
|
||||
return json.CommonFailure("用户名或密码错误")
|
||||
}
|
||||
if !cpt.VerifyReq(ctx.Req) {
|
||||
return json.Failure(utils.CaptchaError, "验证码错误")
|
||||
}
|
||||
|
||||
loginLogModel := new(models.LoginLog)
|
||||
loginLogModel.Username = userModel.Name
|
||||
loginLogModel.Ip = ctx.RemoteAddr()
|
||||
_, err := loginLogModel.Create()
|
||||
if err != nil {
|
||||
logger.Error("记录用户登录日志失败", err)
|
||||
}
|
||||
loginLogModel := new(models.LoginLog)
|
||||
loginLogModel.Username = userModel.Name
|
||||
loginLogModel.Ip = ctx.RemoteAddr()
|
||||
_, err := loginLogModel.Create()
|
||||
if err != nil {
|
||||
logger.Error("记录用户登录日志失败", err)
|
||||
}
|
||||
|
||||
sess.Set("username", userModel.Name)
|
||||
sess.Set("uid", userModel.Id)
|
||||
sess.Set("isAdmin", userModel.IsAdmin)
|
||||
|
||||
sess.Set("username", userModel.Name)
|
||||
sess.Set("uid", userModel.Id)
|
||||
sess.Set("isAdmin", userModel.IsAdmin)
|
||||
|
||||
return json.Success("登录成功", nil)
|
||||
return json.Success("登录成功", nil)
|
||||
}
|
||||
|
||||
func Logout(ctx *macaron.Context, sess session.Store) {
|
||||
if IsLogin(sess) {
|
||||
err := sess.Destory(ctx)
|
||||
if err != nil {
|
||||
logger.Error("用户退出登录失败", err)
|
||||
}
|
||||
}
|
||||
if IsLogin(sess) {
|
||||
err := sess.Destory(ctx)
|
||||
if err != nil {
|
||||
logger.Error("用户退出登录失败", err)
|
||||
}
|
||||
}
|
||||
|
||||
Login(ctx)
|
||||
Login(ctx)
|
||||
}
|
||||
|
||||
func Username(sess session.Store) string {
|
||||
username,ok := sess.Get("username").(string)
|
||||
if ok {
|
||||
return username
|
||||
}
|
||||
func Username(sess session.Store) string {
|
||||
username, ok := sess.Get("username").(string)
|
||||
if ok {
|
||||
return username
|
||||
}
|
||||
|
||||
return ""
|
||||
return ""
|
||||
}
|
||||
|
||||
func Uid(sess session.Store) int {
|
||||
uid,ok := sess.Get("uid").(int)
|
||||
if ok {
|
||||
return uid
|
||||
}
|
||||
func Uid(sess session.Store) int {
|
||||
uid, ok := sess.Get("uid").(int)
|
||||
if ok {
|
||||
return uid
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
func IsLogin(sess session.Store) bool {
|
||||
uid, ok := sess.Get("uid").(int)
|
||||
if ok && uid > 0 {
|
||||
return true
|
||||
}
|
||||
func IsLogin(sess session.Store) bool {
|
||||
uid, ok := sess.Get("uid").(int)
|
||||
if ok && uid > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
func IsAdmin(sess session.Store) bool {
|
||||
isAdmin, ok := sess.Get("isAdmin").(int8)
|
||||
if ok && isAdmin > 0 {
|
||||
return true
|
||||
}
|
||||
func IsAdmin(sess session.Store) bool {
|
||||
isAdmin, ok := sess.Get("isAdmin").(int8)
|
||||
if ok && isAdmin > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
595
service/task.go
595
service/task.go
|
@ -1,154 +1,155 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"strconv"
|
||||
"time"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/jakecoffman/cron"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ouqiang/gocron/modules/httpclient"
|
||||
"github.com/ouqiang/gocron/modules/notify"
|
||||
"sync"
|
||||
rpcClient "github.com/ouqiang/gocron/modules/rpc/client"
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"strings"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jakecoffman/cron"
|
||||
"github.com/ouqiang/gocron/models"
|
||||
"github.com/ouqiang/gocron/modules/httpclient"
|
||||
"github.com/ouqiang/gocron/modules/logger"
|
||||
"github.com/ouqiang/gocron/modules/notify"
|
||||
rpcClient "github.com/ouqiang/gocron/modules/rpc/client"
|
||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 定时任务调度管理器
|
||||
var Cron *cron.Cron
|
||||
|
||||
// 同一任务是否有实例处于运行中
|
||||
var runInstance Instance
|
||||
|
||||
// 任务计数-正在运行中的任务
|
||||
var TaskNum TaskCount
|
||||
|
||||
// 任务计数
|
||||
type TaskCount struct {
|
||||
num int
|
||||
sync.RWMutex
|
||||
num int
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *TaskCount) Add() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.num += 1
|
||||
func (c *TaskCount) Add() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.num += 1
|
||||
}
|
||||
|
||||
func (c *TaskCount) Done() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.num -= 1
|
||||
func (c *TaskCount) Done() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.num -= 1
|
||||
}
|
||||
|
||||
func (c *TaskCount) Num() int {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
func (c *TaskCount) Num() int {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
||||
return c.num
|
||||
return c.num
|
||||
}
|
||||
|
||||
// 任务ID作为Key
|
||||
type Instance struct {
|
||||
Status map[int]bool
|
||||
sync.RWMutex
|
||||
Status map[int]bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// 是否有任务处于运行中
|
||||
func (i *Instance) has(key int) bool {
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
running, ok := i.Status[key]
|
||||
if ok && running {
|
||||
return true
|
||||
}
|
||||
i.RLock()
|
||||
defer i.RUnlock()
|
||||
running, ok := i.Status[key]
|
||||
if ok && running {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Instance) add(key int) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.Status[key] = true
|
||||
func (i *Instance) add(key int) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
i.Status[key] = true
|
||||
}
|
||||
|
||||
func (i *Instance) done(key int) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
delete(i.Status, key)
|
||||
func (i *Instance) done(key int) {
|
||||
i.Lock()
|
||||
defer i.Unlock()
|
||||
delete(i.Status, key)
|
||||
}
|
||||
|
||||
type Task struct{}
|
||||
|
||||
type TaskResult struct {
|
||||
Result string
|
||||
Err error
|
||||
RetryTimes int8
|
||||
Result string
|
||||
Err error
|
||||
RetryTimes int8
|
||||
}
|
||||
|
||||
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
||||
func (task *Task) Initialize() {
|
||||
Cron = cron.New()
|
||||
Cron.Start()
|
||||
runInstance = Instance{make(map[int]bool), sync.RWMutex{}}
|
||||
TaskNum = TaskCount{0, sync.RWMutex{}}
|
||||
Cron = cron.New()
|
||||
Cron.Start()
|
||||
runInstance = Instance{make(map[int]bool), sync.RWMutex{}}
|
||||
TaskNum = TaskCount{0, sync.RWMutex{}}
|
||||
|
||||
taskModel := new(models.Task)
|
||||
taskList, err := taskModel.ActiveList()
|
||||
if err != nil {
|
||||
logger.Error("定时任务初始化#获取任务列表错误-", err.Error())
|
||||
return
|
||||
}
|
||||
if len(taskList) == 0 {
|
||||
logger.Debug("任务列表为空")
|
||||
return
|
||||
}
|
||||
task.BatchAdd(taskList)
|
||||
taskModel := new(models.Task)
|
||||
taskList, err := taskModel.ActiveList()
|
||||
if err != nil {
|
||||
logger.Error("定时任务初始化#获取任务列表错误-", err.Error())
|
||||
return
|
||||
}
|
||||
if len(taskList) == 0 {
|
||||
logger.Debug("任务列表为空")
|
||||
return
|
||||
}
|
||||
task.BatchAdd(taskList)
|
||||
}
|
||||
|
||||
// 批量添加任务
|
||||
func (task *Task) BatchAdd(tasks []models.Task) {
|
||||
for _, item := range tasks {
|
||||
task.Add(item)
|
||||
}
|
||||
func (task *Task) BatchAdd(tasks []models.Task) {
|
||||
for _, item := range tasks {
|
||||
task.Add(item)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加任务
|
||||
func (task *Task) Add(taskModel models.Task) {
|
||||
if taskModel.Level == models.TaskLevelChild {
|
||||
logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id);
|
||||
return
|
||||
}
|
||||
taskFunc := createJob(taskModel)
|
||||
if taskFunc == nil {
|
||||
logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol)
|
||||
return
|
||||
}
|
||||
if taskModel.Level == models.TaskLevelChild {
|
||||
logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id)
|
||||
return
|
||||
}
|
||||
taskFunc := createJob(taskModel)
|
||||
if taskFunc == nil {
|
||||
logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol)
|
||||
return
|
||||
}
|
||||
|
||||
cronName := strconv.Itoa(taskModel.Id)
|
||||
// Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题
|
||||
Cron.RemoveJob(cronName)
|
||||
err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
||||
if err != nil {
|
||||
logger.Error("添加任务到调度器失败#", err)
|
||||
}
|
||||
cronName := strconv.Itoa(taskModel.Id)
|
||||
// Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题
|
||||
Cron.RemoveJob(cronName)
|
||||
err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
||||
if err != nil {
|
||||
logger.Error("添加任务到调度器失败#", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 停止所有任务
|
||||
func (task *Task) StopAll() {
|
||||
Cron.Stop()
|
||||
func (task *Task) StopAll() {
|
||||
Cron.Stop()
|
||||
}
|
||||
|
||||
// 直接运行任务
|
||||
func (task *Task) Run(taskModel models.Task) {
|
||||
go createJob(taskModel)()
|
||||
func (task *Task) Run(taskModel models.Task) {
|
||||
go createJob(taskModel)()
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
Run(taskModel models.Task) (string, error)
|
||||
Run(taskModel models.Task) (string, error)
|
||||
}
|
||||
|
||||
|
||||
// HTTP任务
|
||||
type HTTPHandler struct{}
|
||||
|
||||
|
@ -156,259 +157,257 @@ type HTTPHandler struct{}
|
|||
const HttpExecTimeout = 300
|
||||
|
||||
func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) {
|
||||
if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout {
|
||||
taskModel.Timeout = HttpExecTimeout
|
||||
}
|
||||
resp := httpclient.Get(taskModel.Command, taskModel.Timeout)
|
||||
// 返回状态码非200,均为失败
|
||||
if resp.StatusCode != 200 {
|
||||
return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
|
||||
}
|
||||
if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout {
|
||||
taskModel.Timeout = HttpExecTimeout
|
||||
}
|
||||
resp := httpclient.Get(taskModel.Command, taskModel.Timeout)
|
||||
// 返回状态码非200,均为失败
|
||||
if resp.StatusCode != 200 {
|
||||
return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
|
||||
}
|
||||
|
||||
return resp.Body, err
|
||||
return resp.Body, err
|
||||
}
|
||||
|
||||
// RPC调用执行任务
|
||||
type RPCHandler struct {}
|
||||
type RPCHandler struct{}
|
||||
|
||||
func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) {
|
||||
taskRequest := new(pb.TaskRequest)
|
||||
taskRequest.Timeout = int32(taskModel.Timeout)
|
||||
taskRequest.Command = taskModel.Command
|
||||
var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts))
|
||||
for _, taskHost := range taskModel.Hosts {
|
||||
go func(th models.TaskHostDetail) {
|
||||
output, err := rpcClient.ExecWithRetry(th.Name, th.Port, taskRequest)
|
||||
var errorMessage string = ""
|
||||
if err != nil {
|
||||
errorMessage = err.Error()
|
||||
}
|
||||
outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n",
|
||||
th.Alias, th.Name, errorMessage, output,
|
||||
)
|
||||
resultChan <- TaskResult{Err:err, Result: outputMessage}
|
||||
}(taskHost)
|
||||
}
|
||||
func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) {
|
||||
taskRequest := new(pb.TaskRequest)
|
||||
taskRequest.Timeout = int32(taskModel.Timeout)
|
||||
taskRequest.Command = taskModel.Command
|
||||
var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts))
|
||||
for _, taskHost := range taskModel.Hosts {
|
||||
go func(th models.TaskHostDetail) {
|
||||
output, err := rpcClient.Exec(th.Name, th.Port, taskRequest)
|
||||
var errorMessage string = ""
|
||||
if err != nil {
|
||||
errorMessage = err.Error()
|
||||
}
|
||||
outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n",
|
||||
th.Alias, th.Name, errorMessage, output,
|
||||
)
|
||||
resultChan <- TaskResult{Err: err, Result: outputMessage}
|
||||
}(taskHost)
|
||||
}
|
||||
|
||||
var aggregationErr error = nil
|
||||
var aggregationResult string = ""
|
||||
for i := 0; i < len(taskModel.Hosts); i++ {
|
||||
taskResult := <- resultChan
|
||||
aggregationResult += taskResult.Result
|
||||
if taskResult.Err != nil {
|
||||
aggregationErr = taskResult.Err
|
||||
}
|
||||
}
|
||||
var aggregationErr error = nil
|
||||
var aggregationResult string = ""
|
||||
for i := 0; i < len(taskModel.Hosts); i++ {
|
||||
taskResult := <-resultChan
|
||||
aggregationResult += taskResult.Result
|
||||
if taskResult.Err != nil {
|
||||
aggregationErr = taskResult.Err
|
||||
}
|
||||
}
|
||||
|
||||
return aggregationResult, aggregationErr
|
||||
return aggregationResult, aggregationErr
|
||||
}
|
||||
|
||||
|
||||
// 创建任务日志
|
||||
func createTaskLog(taskModel models.Task, status models.Status) (int64, error) {
|
||||
taskLogModel := new(models.TaskLog)
|
||||
taskLogModel.TaskId = taskModel.Id
|
||||
taskLogModel.Name = taskModel.Name
|
||||
taskLogModel.Spec = taskModel.Spec
|
||||
taskLogModel.Protocol = taskModel.Protocol
|
||||
taskLogModel.Command = taskModel.Command
|
||||
taskLogModel.Timeout = taskModel.Timeout
|
||||
if taskModel.Protocol == models.TaskRPC {
|
||||
var aggregationHost string = ""
|
||||
for _, host := range taskModel.Hosts {
|
||||
aggregationHost += fmt.Sprintf("%s-%s<br>", host.Alias, host.Name)
|
||||
}
|
||||
taskLogModel.Hostname = aggregationHost
|
||||
}
|
||||
taskLogModel.StartTime = time.Now()
|
||||
taskLogModel.Status = status
|
||||
insertId, err := taskLogModel.Create()
|
||||
taskLogModel := new(models.TaskLog)
|
||||
taskLogModel.TaskId = taskModel.Id
|
||||
taskLogModel.Name = taskModel.Name
|
||||
taskLogModel.Spec = taskModel.Spec
|
||||
taskLogModel.Protocol = taskModel.Protocol
|
||||
taskLogModel.Command = taskModel.Command
|
||||
taskLogModel.Timeout = taskModel.Timeout
|
||||
if taskModel.Protocol == models.TaskRPC {
|
||||
var aggregationHost string = ""
|
||||
for _, host := range taskModel.Hosts {
|
||||
aggregationHost += fmt.Sprintf("%s-%s<br>", host.Alias, host.Name)
|
||||
}
|
||||
taskLogModel.Hostname = aggregationHost
|
||||
}
|
||||
taskLogModel.StartTime = time.Now()
|
||||
taskLogModel.Status = status
|
||||
insertId, err := taskLogModel.Create()
|
||||
|
||||
return insertId, err
|
||||
return insertId, err
|
||||
}
|
||||
|
||||
// 更新任务日志
|
||||
func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) {
|
||||
taskLogModel := new(models.TaskLog)
|
||||
var status models.Status
|
||||
var result string = taskResult.Result
|
||||
if taskResult.Err != nil {
|
||||
status = models.Failure
|
||||
} else {
|
||||
status = models.Finish
|
||||
}
|
||||
return taskLogModel.Update(taskLogId, models.CommonMap{
|
||||
"retry_times": taskResult.RetryTimes,
|
||||
"status": status,
|
||||
"result": result,
|
||||
})
|
||||
taskLogModel := new(models.TaskLog)
|
||||
var status models.Status
|
||||
var result string = taskResult.Result
|
||||
if taskResult.Err != nil {
|
||||
status = models.Failure
|
||||
} else {
|
||||
status = models.Finish
|
||||
}
|
||||
return taskLogModel.Update(taskLogId, models.CommonMap{
|
||||
"retry_times": taskResult.RetryTimes,
|
||||
"status": status,
|
||||
"result": result,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func createJob(taskModel models.Task) cron.FuncJob {
|
||||
var handler Handler = createHandler(taskModel)
|
||||
if handler == nil {
|
||||
return nil
|
||||
}
|
||||
taskFunc := func() {
|
||||
TaskNum.Add()
|
||||
defer TaskNum.Done()
|
||||
taskLogId := beforeExecJob(taskModel)
|
||||
if taskLogId <= 0 {
|
||||
return
|
||||
}
|
||||
logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||
taskResult := execJob(handler, taskModel)
|
||||
logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||
afterExecJob(taskModel, taskResult, taskLogId)
|
||||
}
|
||||
var handler Handler = createHandler(taskModel)
|
||||
if handler == nil {
|
||||
return nil
|
||||
}
|
||||
taskFunc := func() {
|
||||
TaskNum.Add()
|
||||
defer TaskNum.Done()
|
||||
taskLogId := beforeExecJob(taskModel)
|
||||
if taskLogId <= 0 {
|
||||
return
|
||||
}
|
||||
logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||
taskResult := execJob(handler, taskModel)
|
||||
logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||
afterExecJob(taskModel, taskResult, taskLogId)
|
||||
}
|
||||
|
||||
return taskFunc
|
||||
return taskFunc
|
||||
}
|
||||
|
||||
func createHandler(taskModel models.Task) Handler {
|
||||
var handler Handler = nil
|
||||
switch taskModel.Protocol {
|
||||
case models.TaskHTTP:
|
||||
handler = new(HTTPHandler)
|
||||
case models.TaskRPC:
|
||||
handler = new(RPCHandler)
|
||||
}
|
||||
func createHandler(taskModel models.Task) Handler {
|
||||
var handler Handler = nil
|
||||
switch taskModel.Protocol {
|
||||
case models.TaskHTTP:
|
||||
handler = new(HTTPHandler)
|
||||
case models.TaskRPC:
|
||||
handler = new(RPCHandler)
|
||||
}
|
||||
|
||||
|
||||
return handler;
|
||||
return handler
|
||||
}
|
||||
|
||||
// 任务前置操作
|
||||
func beforeExecJob(taskModel models.Task) (taskLogId int64) {
|
||||
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
||||
createTaskLog(taskModel, models.Cancel)
|
||||
return
|
||||
}
|
||||
if taskModel.Multi == 0 {
|
||||
runInstance.add(taskModel.Id)
|
||||
}
|
||||
taskLogId, err := createTaskLog(taskModel, models.Running)
|
||||
if err != nil {
|
||||
logger.Error("任务开始执行#写入任务日志失败-", err)
|
||||
return
|
||||
}
|
||||
func beforeExecJob(taskModel models.Task) (taskLogId int64) {
|
||||
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
||||
createTaskLog(taskModel, models.Cancel)
|
||||
return
|
||||
}
|
||||
if taskModel.Multi == 0 {
|
||||
runInstance.add(taskModel.Id)
|
||||
}
|
||||
taskLogId, err := createTaskLog(taskModel, models.Running)
|
||||
if err != nil {
|
||||
logger.Error("任务开始执行#写入任务日志失败-", err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debugf("任务命令-%s", taskModel.Command)
|
||||
logger.Debugf("任务命令-%s", taskModel.Command)
|
||||
|
||||
return taskLogId
|
||||
return taskLogId
|
||||
}
|
||||
|
||||
// 任务执行后置操作
|
||||
func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) {
|
||||
_, err := updateTaskLog(taskLogId, taskResult)
|
||||
if err != nil {
|
||||
logger.Error("任务结束#更新任务日志失败-", err)
|
||||
}
|
||||
func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) {
|
||||
_, err := updateTaskLog(taskLogId, taskResult)
|
||||
if err != nil {
|
||||
logger.Error("任务结束#更新任务日志失败-", err)
|
||||
}
|
||||
|
||||
// 发送邮件
|
||||
go SendNotification(taskModel, taskResult)
|
||||
// 执行依赖任务
|
||||
go execDependencyTask(taskModel, taskResult)
|
||||
// 发送邮件
|
||||
go SendNotification(taskModel, taskResult)
|
||||
// 执行依赖任务
|
||||
go execDependencyTask(taskModel, taskResult)
|
||||
}
|
||||
|
||||
// 执行依赖任务, 多个任务并发执行
|
||||
func execDependencyTask(taskModel models.Task, taskResult TaskResult) {
|
||||
// 父任务才能执行子任务
|
||||
if taskModel.Level != models.TaskLevelParent {
|
||||
return
|
||||
}
|
||||
func execDependencyTask(taskModel models.Task, taskResult TaskResult) {
|
||||
// 父任务才能执行子任务
|
||||
if taskModel.Level != models.TaskLevelParent {
|
||||
return
|
||||
}
|
||||
|
||||
// 是否存在子任务
|
||||
dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId)
|
||||
if dependencyTaskId == "" {
|
||||
return
|
||||
}
|
||||
// 是否存在子任务
|
||||
dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId)
|
||||
if dependencyTaskId == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务
|
||||
if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil {
|
||||
logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id)
|
||||
return
|
||||
}
|
||||
// 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务
|
||||
if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil {
|
||||
logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取子任务
|
||||
model := new(models.Task)
|
||||
tasks , err := model.GetDependencyTaskList(dependencyTaskId)
|
||||
if err != nil {
|
||||
logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error())
|
||||
return
|
||||
}
|
||||
if len(tasks) == 0 {
|
||||
logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id)
|
||||
}
|
||||
serviceTask := new(Task)
|
||||
for _, task := range tasks {
|
||||
task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id)
|
||||
serviceTask.Run(task)
|
||||
}
|
||||
// 获取子任务
|
||||
model := new(models.Task)
|
||||
tasks, err := model.GetDependencyTaskList(dependencyTaskId)
|
||||
if err != nil {
|
||||
logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error())
|
||||
return
|
||||
}
|
||||
if len(tasks) == 0 {
|
||||
logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id)
|
||||
}
|
||||
serviceTask := new(Task)
|
||||
for _, task := range tasks {
|
||||
task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id)
|
||||
serviceTask.Run(task)
|
||||
}
|
||||
}
|
||||
|
||||
// 发送任务结果通知
|
||||
func SendNotification(taskModel models.Task, taskResult TaskResult) {
|
||||
var statusName string
|
||||
// 未开启通知
|
||||
if taskModel.NotifyStatus == 0 {
|
||||
return
|
||||
}
|
||||
if taskModel.NotifyStatus == 1 && taskResult.Err == nil {
|
||||
// 执行失败才发送通知
|
||||
return
|
||||
}
|
||||
if taskModel.NotifyReceiverId == "" {
|
||||
return
|
||||
}
|
||||
if taskResult.Err != nil {
|
||||
statusName = "失败"
|
||||
} else {
|
||||
statusName = "成功"
|
||||
}
|
||||
// 发送通知
|
||||
msg := notify.Message{
|
||||
"task_type": taskModel.NotifyType,
|
||||
"task_receiver_id": taskModel.NotifyReceiverId,
|
||||
"name": taskModel.Name,
|
||||
"output": taskResult.Result,
|
||||
"status": statusName,
|
||||
"taskId": taskModel.Id,
|
||||
};
|
||||
notify.Push(msg)
|
||||
func SendNotification(taskModel models.Task, taskResult TaskResult) {
|
||||
var statusName string
|
||||
// 未开启通知
|
||||
if taskModel.NotifyStatus == 0 {
|
||||
return
|
||||
}
|
||||
if taskModel.NotifyStatus == 1 && taskResult.Err == nil {
|
||||
// 执行失败才发送通知
|
||||
return
|
||||
}
|
||||
if taskModel.NotifyReceiverId == "" {
|
||||
return
|
||||
}
|
||||
if taskResult.Err != nil {
|
||||
statusName = "失败"
|
||||
} else {
|
||||
statusName = "成功"
|
||||
}
|
||||
// 发送通知
|
||||
msg := notify.Message{
|
||||
"task_type": taskModel.NotifyType,
|
||||
"task_receiver_id": taskModel.NotifyReceiverId,
|
||||
"name": taskModel.Name,
|
||||
"output": taskResult.Result,
|
||||
"status": statusName,
|
||||
"taskId": taskModel.Id,
|
||||
}
|
||||
notify.Push(msg)
|
||||
}
|
||||
|
||||
// 执行具体任务
|
||||
func execJob(handler Handler, taskModel models.Task) TaskResult {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error("panic#service/task.go:execJob#", err)
|
||||
}
|
||||
} ()
|
||||
if taskModel.Multi == 0 {
|
||||
defer runInstance.done(taskModel.Id)
|
||||
}
|
||||
// 默认只运行任务一次
|
||||
var execTimes int8 = 1
|
||||
if (taskModel.RetryTimes > 0) {
|
||||
execTimes += taskModel.RetryTimes
|
||||
}
|
||||
var i int8 = 0
|
||||
var output string
|
||||
var err error
|
||||
for i < execTimes {
|
||||
output, err = handler.Run(taskModel)
|
||||
if err == nil {
|
||||
return TaskResult{Result: output, Err: err, RetryTimes: i}
|
||||
}
|
||||
i++
|
||||
if i < execTimes {
|
||||
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
|
||||
// 重试间隔时间,每次递增1分钟
|
||||
time.Sleep( time.Duration(i) * time.Minute)
|
||||
}
|
||||
}
|
||||
func execJob(handler Handler, taskModel models.Task) TaskResult {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error("panic#service/task.go:execJob#", err)
|
||||
}
|
||||
}()
|
||||
if taskModel.Multi == 0 {
|
||||
defer runInstance.done(taskModel.Id)
|
||||
}
|
||||
// 默认只运行任务一次
|
||||
var execTimes int8 = 1
|
||||
if taskModel.RetryTimes > 0 {
|
||||
execTimes += taskModel.RetryTimes
|
||||
}
|
||||
var i int8 = 0
|
||||
var output string
|
||||
var err error
|
||||
for i < execTimes {
|
||||
output, err = handler.Run(taskModel)
|
||||
if err == nil {
|
||||
return TaskResult{Result: output, Err: err, RetryTimes: i}
|
||||
}
|
||||
i++
|
||||
if i < execTimes {
|
||||
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
|
||||
// 重试间隔时间,每次递增1分钟
|
||||
time.Sleep(time.Duration(i) * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes}
|
||||
}
|
||||
return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes}
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
|
||||
package com
|
||||
|
||||
// PowInt is int type of math.Pow function.
|
||||
// PowInt is int type of math.Pow function.
|
||||
func PowInt(x int, y int) int {
|
||||
if y <= 0 {
|
||||
return 1
|
||||
} else {
|
||||
if y % 2 == 0 {
|
||||
if y%2 == 0 {
|
||||
sqrt := PowInt(x, y/2)
|
||||
return sqrt * sqrt
|
||||
} else {
|
||||
|
|
|
@ -124,7 +124,7 @@ type Options struct {
|
|||
// Cookie value used to set and get token.
|
||||
Cookie string
|
||||
// Cookie path.
|
||||
CookiePath string
|
||||
CookiePath string
|
||||
CookieHttpOnly bool
|
||||
// Key used for getting the unique ID per user.
|
||||
SessionKey string
|
||||
|
|
|
@ -1,217 +1,217 @@
|
|||
// Copyright 2013 Beego Authors
|
||||
// Copyright 2014 The Macaron Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MemStore represents a in-memory session store implementation.
|
||||
type MemStore struct {
|
||||
sid string
|
||||
lock sync.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
lastAccess time.Time
|
||||
}
|
||||
|
||||
// NewMemStore creates and returns a memory session store.
|
||||
func NewMemStore(sid string) *MemStore {
|
||||
return &MemStore{
|
||||
sid: sid,
|
||||
data: make(map[interface{}]interface{}),
|
||||
lastAccess: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets value to given key in session.
|
||||
func (s *MemStore) Set(key, val interface{}) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets value by given key in session.
|
||||
func (s *MemStore) Get(key interface{}) interface{} {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.data[key]
|
||||
}
|
||||
|
||||
// Delete deletes a key from session.
|
||||
func (s *MemStore) Delete(key interface{}) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
delete(s.data, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns current session ID.
|
||||
func (s *MemStore) ID() string {
|
||||
return s.sid
|
||||
}
|
||||
|
||||
// Release releases resource and save data to provider.
|
||||
func (_ *MemStore) Release() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush deletes all session data.
|
||||
func (s *MemStore) Flush() error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data = make(map[interface{}]interface{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// MemProvider represents a in-memory session provider implementation.
|
||||
type MemProvider struct {
|
||||
lock sync.RWMutex
|
||||
maxLifetime int64
|
||||
data map[string]*list.Element
|
||||
// A priority list whose lastAccess newer gets higer priority.
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// Init initializes memory session provider.
|
||||
func (p *MemProvider) Init(maxLifetime int64, _ string) error {
|
||||
p.lock.Lock()
|
||||
p.maxLifetime = maxLifetime
|
||||
p.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// update expands time of session store by given ID.
|
||||
func (p *MemProvider) update(sid string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
if e, ok := p.data[sid]; ok {
|
||||
e.Value.(*MemStore).lastAccess = time.Now()
|
||||
p.list.MoveToFront(e)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read returns raw session store by session ID.
|
||||
func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
|
||||
p.lock.RLock()
|
||||
e, ok := p.data[sid]
|
||||
p.lock.RUnlock()
|
||||
|
||||
if ok {
|
||||
if err = p.update(sid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Value.(*MemStore), nil
|
||||
}
|
||||
|
||||
// Create a new session.
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
s := NewMemStore(sid)
|
||||
p.data[sid] = p.list.PushBack(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Exist returns true if session with given ID exists.
|
||||
func (p *MemProvider) Exist(sid string) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
_, ok := p.data[sid]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Destory deletes a session by session ID.
|
||||
func (p *MemProvider) Destory(sid string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
e, ok := p.data[sid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.list.Remove(e)
|
||||
delete(p.data, sid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Regenerate regenerates a session store from old session ID to new one.
|
||||
func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) {
|
||||
if p.Exist(sid) {
|
||||
return nil, fmt.Errorf("new sid '%s' already exists", sid)
|
||||
}
|
||||
|
||||
s, err := p.Read(oldsid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = p.Destory(oldsid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.(*MemStore).sid = sid
|
||||
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.data[sid] = p.list.PushBack(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Count counts and returns number of sessions.
|
||||
func (p *MemProvider) Count() int {
|
||||
return p.list.Len()
|
||||
}
|
||||
|
||||
// GC calls GC to clean expired sessions.
|
||||
func (p *MemProvider) GC() {
|
||||
p.lock.RLock()
|
||||
for {
|
||||
// No session in the list.
|
||||
e := p.list.Back()
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
|
||||
p.lock.RUnlock()
|
||||
p.lock.Lock()
|
||||
p.list.Remove(e)
|
||||
delete(p.data, e.Value.(*MemStore).sid)
|
||||
p.lock.Unlock()
|
||||
p.lock.RLock()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.lock.RUnlock()
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)})
|
||||
}
|
||||
// Copyright 2013 Beego Authors
|
||||
// Copyright 2014 The Macaron Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||
// not use this file except in compliance with the License. You may obtain
|
||||
// a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package session
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MemStore represents a in-memory session store implementation.
|
||||
type MemStore struct {
|
||||
sid string
|
||||
lock sync.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
lastAccess time.Time
|
||||
}
|
||||
|
||||
// NewMemStore creates and returns a memory session store.
|
||||
func NewMemStore(sid string) *MemStore {
|
||||
return &MemStore{
|
||||
sid: sid,
|
||||
data: make(map[interface{}]interface{}),
|
||||
lastAccess: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets value to given key in session.
|
||||
func (s *MemStore) Set(key, val interface{}) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data[key] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets value by given key in session.
|
||||
func (s *MemStore) Get(key interface{}) interface{} {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.data[key]
|
||||
}
|
||||
|
||||
// Delete deletes a key from session.
|
||||
func (s *MemStore) Delete(key interface{}) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
delete(s.data, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns current session ID.
|
||||
func (s *MemStore) ID() string {
|
||||
return s.sid
|
||||
}
|
||||
|
||||
// Release releases resource and save data to provider.
|
||||
func (_ *MemStore) Release() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush deletes all session data.
|
||||
func (s *MemStore) Flush() error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data = make(map[interface{}]interface{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// MemProvider represents a in-memory session provider implementation.
|
||||
type MemProvider struct {
|
||||
lock sync.RWMutex
|
||||
maxLifetime int64
|
||||
data map[string]*list.Element
|
||||
// A priority list whose lastAccess newer gets higer priority.
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// Init initializes memory session provider.
|
||||
func (p *MemProvider) Init(maxLifetime int64, _ string) error {
|
||||
p.lock.Lock()
|
||||
p.maxLifetime = maxLifetime
|
||||
p.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// update expands time of session store by given ID.
|
||||
func (p *MemProvider) update(sid string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
if e, ok := p.data[sid]; ok {
|
||||
e.Value.(*MemStore).lastAccess = time.Now()
|
||||
p.list.MoveToFront(e)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read returns raw session store by session ID.
|
||||
func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
|
||||
p.lock.RLock()
|
||||
e, ok := p.data[sid]
|
||||
p.lock.RUnlock()
|
||||
|
||||
if ok {
|
||||
if err = p.update(sid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Value.(*MemStore), nil
|
||||
}
|
||||
|
||||
// Create a new session.
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
s := NewMemStore(sid)
|
||||
p.data[sid] = p.list.PushBack(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Exist returns true if session with given ID exists.
|
||||
func (p *MemProvider) Exist(sid string) bool {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
_, ok := p.data[sid]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Destory deletes a session by session ID.
|
||||
func (p *MemProvider) Destory(sid string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
e, ok := p.data[sid]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.list.Remove(e)
|
||||
delete(p.data, sid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Regenerate regenerates a session store from old session ID to new one.
|
||||
func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) {
|
||||
if p.Exist(sid) {
|
||||
return nil, fmt.Errorf("new sid '%s' already exists", sid)
|
||||
}
|
||||
|
||||
s, err := p.Read(oldsid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = p.Destory(oldsid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.(*MemStore).sid = sid
|
||||
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.data[sid] = p.list.PushBack(s)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Count counts and returns number of sessions.
|
||||
func (p *MemProvider) Count() int {
|
||||
return p.list.Len()
|
||||
}
|
||||
|
||||
// GC calls GC to clean expired sessions.
|
||||
func (p *MemProvider) GC() {
|
||||
p.lock.RLock()
|
||||
for {
|
||||
// No session in the list.
|
||||
e := p.list.Back()
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
|
||||
p.lock.RUnlock()
|
||||
p.lock.Lock()
|
||||
p.list.Remove(e)
|
||||
delete(p.data, e.Value.(*MemStore).sid)
|
||||
p.lock.Unlock()
|
||||
p.lock.RLock()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.lock.RUnlock()
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -55,7 +55,6 @@ func encodeIds(ids []PK) (string, error) {
|
|||
return buf.String(), err
|
||||
}
|
||||
|
||||
|
||||
func decodeIds(s string) ([]PK, error) {
|
||||
pks := make([]PK, 0)
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ type FuncJob func()
|
|||
func (f FuncJob) Run() { f() }
|
||||
|
||||
// AddFunc adds a func to the Cron to be run on the given schedule.
|
||||
func (c *Cron) AddFunc(spec string, cmd func(), name string) error {
|
||||
func (c *Cron) AddFunc(spec string, cmd func(), name string) error {
|
||||
return c.AddJob(spec, FuncJob(cmd), name)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package cron
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Parse returns a new crontab schedule representing the given spec.
|
||||
|
@ -160,7 +160,7 @@ func all(r bounds) uint64 {
|
|||
|
||||
// parseDescriptor returns a pre-defined schedule for the expression, or panics
|
||||
// if none matches.
|
||||
func parseDescriptor(spec string) (Schedule,error) {
|
||||
func parseDescriptor(spec string) (Schedule, error) {
|
||||
switch spec {
|
||||
case "@yearly", "@annually":
|
||||
return &SpecSchedule{
|
||||
|
@ -217,10 +217,10 @@ func parseDescriptor(spec string) (Schedule,error) {
|
|||
if strings.HasPrefix(spec, every) {
|
||||
duration, err := time.ParseDuration(spec[len(every):])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err)
|
||||
}
|
||||
return Every(duration),nil
|
||||
return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err)
|
||||
}
|
||||
return Every(duration), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unrecognized descriptor: %s", spec)
|
||||
return nil, fmt.Errorf("Unrecognized descriptor: %s", spec)
|
||||
}
|
||||
|
|
|
@ -383,8 +383,8 @@ func init() {
|
|||
// 4253 and Oakley Group 2 in RFC 2409.
|
||||
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
|
||||
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||
}
|
||||
|
||||
|
@ -393,8 +393,8 @@ func init() {
|
|||
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
|
||||
|
||||
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
g: new(big.Int).SetInt64(2),
|
||||
p: p,
|
||||
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue