tab替换为空格

pull/21/merge
ouqiang 2017-04-02 10:38:49 +08:00
parent 702db588f2
commit d7461a794f
19 changed files with 766 additions and 766 deletions

View File

@ -1,13 +1,13 @@
package cmd package cmd
import ( import (
"github.com/go-macaron/csrf" "github.com/go-macaron/csrf"
"github.com/go-macaron/gzip" "github.com/go-macaron/gzip"
"github.com/go-macaron/session" "github.com/go-macaron/session"
"github.com/ouqiang/cron-scheduler/modules/app" "github.com/ouqiang/cron-scheduler/modules/app"
"github.com/ouqiang/cron-scheduler/routers" "github.com/ouqiang/cron-scheduler/routers"
"github.com/urfave/cli" "github.com/urfave/cli"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
// web服务器默认端口 // web服务器默认端口
@ -17,60 +17,60 @@ const DefaultPort = 5920
const StaticDir = "public" const StaticDir = "public"
var CmdWeb = cli.Command{ var CmdWeb = cli.Command{
Name: "server", Name: "server",
Usage: "start scheduler web server", Usage: "start scheduler web server",
Action: run, Action: run,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.IntFlag{ cli.IntFlag{
Name: "port,p", Name: "port,p",
Value: DefaultPort, Value: DefaultPort,
Usage: "bind port number", Usage: "bind port number",
}, },
}, },
} }
func run(ctx *cli.Context) { func run(ctx *cli.Context) {
app.InitEnv() app.InitEnv()
m := macaron.Classic() m := macaron.Classic()
// 注册路由 // 注册路由
routers.Register(m) routers.Register(m)
// 注册中间件 // 注册中间件
registerMiddleware(m) registerMiddleware(m)
port := parsePort(ctx) port := parsePort(ctx)
m.Run(port) m.Run(port)
} }
// 中间件注册 // 中间件注册
func registerMiddleware(m *macaron.Macaron) { func registerMiddleware(m *macaron.Macaron) {
m.Use(macaron.Logger()) m.Use(macaron.Logger())
m.Use(macaron.Recovery()) m.Use(macaron.Recovery())
m.Use(gzip.Gziper()) m.Use(gzip.Gziper())
m.Use(macaron.Static(StaticDir)) m.Use(macaron.Static(StaticDir))
m.Use(macaron.Renderer(macaron.RenderOptions{ m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: "templates", Directory: "templates",
Extensions: []string{".html"}, Extensions: []string{".html"},
// 模板语法分隔符,默认为 ["{{", "}}"] // 模板语法分隔符,默认为 ["{{", "}}"]
Delims: macaron.Delims{"{{{", "}}}"}, Delims: macaron.Delims{"{{{", "}}}"},
// 追加的 Content-Type 头信息,默认为 "UTF-8" // 追加的 Content-Type 头信息,默认为 "UTF-8"
Charset: "UTF-8", Charset: "UTF-8",
// 渲染具有缩进格式的 JSON默认为不缩进 // 渲染具有缩进格式的 JSON默认为不缩进
IndentJSON: true, IndentJSON: true,
// 渲染具有缩进格式的 XML默认为不缩进 // 渲染具有缩进格式的 XML默认为不缩进
IndentXML: true, IndentXML: true,
})) }))
m.Use(session.Sessioner()) m.Use(session.Sessioner())
m.Use(csrf.Csrfer()) m.Use(csrf.Csrfer())
} }
// 解析端口 // 解析端口
func parsePort(ctx *cli.Context) int { func parsePort(ctx *cli.Context) int {
var port int var port int
if ctx.IsSet("port") { if ctx.IsSet("port") {
port = ctx.Int("port") port = ctx.Int("port")
} }
if port <= 0 || port >= 65535 { if port <= 0 || port >= 65535 {
port = DefaultPort port = DefaultPort
} }
return port return port
} }

View File

@ -1,7 +1,7 @@
package main package main
/*-------------------------------------------------------- /*--------------------------------------------------------
Linux crontab Linux crontab
HTTPSSH HTTPSSH
--------------------------------------------------------*/ --------------------------------------------------------*/

View File

@ -2,66 +2,66 @@ package models
// 主机 // 主机
type Host struct { type Host struct {
Id int16 `xorm:"smallint pk autoincr"` Id int16 `xorm:"smallint pk autoincr"`
Name string `xorm:"varchar(128) notnull"` // 主机名称 Name string `xorm:"varchar(128) notnull"` // 主机名称
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名 Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名 Username string `xorm:"varchar(32) notnull default '' "` // ssh 用户名
Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码 Password string `xorm:"varchar(64) notnull default ''"` // ssh 密码
Port int `xorm:"notnull default 22"` // 主机端口 Port int `xorm:"notnull default 22"` // 主机端口
LoginType LoginType `xorm:"tinyint notnull default 1"` // ssh登录方式 1:公钥认证 2:账号密码 LoginType LoginType `xorm:"tinyint notnull default 1"` // ssh登录方式 1:公钥认证 2:账号密码
Remark string `xorm:"varchar(512) notnull default '' "` // 备注 Remark string `xorm:"varchar(512) notnull default '' "` // 备注
Page int `xorm:"-"` Page int `xorm:"-"`
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
type LoginType int8 type LoginType int8
const ( const (
PublicKey = 1 PublicKey = 1
UserPassword = 2 UserPassword = 2
) )
// 新增 // 新增
func (host *Host) Create() (insertId int16, err error) { func (host *Host) Create() (insertId int16, err error) {
_, err = Db.Insert(host) _, err = Db.Insert(host)
if err == nil { if err == nil {
insertId = host.Id insertId = host.Id
} }
return return
} }
// 更新 // 更新
func (host *Host) Update(id int, data CommonMap) (int64, error) { 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) { func (host *Host) Delete(id int) (int64, error) {
return Db.Id(id).Delete(host) return Db.Id(id).Delete(host)
} }
func (host *Host) List() ([]Host, error) { func (host *Host) List() ([]Host, error) {
host.parsePageAndPageSize() host.parsePageAndPageSize()
list := make([]Host, 0) list := make([]Host, 0)
err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list) err := Db.Desc("id").Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (host *Host) Total() (int64, error) { func (host *Host) Total() (int64, error) {
return Db.Count(host) return Db.Count(host)
} }
func (host *Host) parsePageAndPageSize() { func (host *Host) parsePageAndPageSize() {
if host.Page <= 0 { if host.Page <= 0 {
host.Page = Page host.Page = Page
} }
if host.PageSize >= 0 || host.PageSize > MaxPageSize { if host.PageSize >= 0 || host.PageSize > MaxPageSize {
host.PageSize = PageSize host.PageSize = PageSize
} }
} }
func (host *Host) pageLimitOffset() int { func (host *Host) pageLimitOffset() int {
return (host.Page - 1) * host.PageSize return (host.Page - 1) * host.PageSize
} }

View File

@ -7,25 +7,25 @@ import "errors"
type Migration struct{} type Migration struct{}
func (migration *Migration) Exec(dbName string) error { func (migration *Migration) Exec(dbName string) error {
if !isDatabaseExist(dbName) { if !isDatabaseExist(dbName) {
return errors.New("数据库不存在") return errors.New("数据库不存在")
} }
tables := []interface{}{ tables := []interface{}{
&User{}, &Task{}, &TaskLog{}, &Host{}, &User{}, &Task{}, &TaskLog{}, &Host{},
} }
for _, table := range tables { for _, table := range tables {
err := Db.Sync2(table) err := Db.Sync2(table)
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
// 创建数据库 // 创建数据库
func isDatabaseExist(name string) bool { func isDatabaseExist(name string) bool {
_, err := Db.Exec("use ?", name) _, err := Db.Exec("use ?", name)
return err != nil return err != nil
} }

View File

@ -1,13 +1,13 @@
package models package models
import ( import (
"fmt" "fmt"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/core" "github.com/go-xorm/core"
"github.com/go-xorm/xorm" "github.com/go-xorm/xorm"
"github.com/ouqiang/cron-scheduler/modules/setting" "github.com/ouqiang/cron-scheduler/modules/setting"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"strings" "strings"
) )
type Status int8 type Status int8
@ -16,78 +16,78 @@ type CommonMap map[string]interface{}
var Db *xorm.Engine var Db *xorm.Engine
const ( const (
Disabled Status = 0 // 禁用 Disabled Status = 0 // 禁用
Failure Status = 0 // 失败 Failure Status = 0 // 失败
Enabled Status = 1 // 启用 Enabled Status = 1 // 启用
Running Status = 1 // 运行中 Running Status = 1 // 运行中
Finish Status = 2 // 完成 Finish Status = 2 // 完成
) )
const ( const (
Page = 1 // 当前页数 Page = 1 // 当前页数
PageSize = 20 // 每页多少条数据 PageSize = 20 // 每页多少条数据
MaxPageSize = 100000 // 每次最多取多少条 MaxPageSize = 100000 // 每次最多取多少条
) )
// 创建Db // 创建Db
func CreateDb(configFile string) *xorm.Engine { func CreateDb(configFile string) *xorm.Engine {
config := getDbConfig(configFile) config := getDbConfig(configFile)
dsn := getDbEngineDSN(config["engine"], config) dsn := getDbEngineDSN(config["engine"], config)
engine, err := xorm.NewEngine(config["engine"], dsn) engine, err := xorm.NewEngine(config["engine"], dsn)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if config["prefix"] != "" { if config["prefix"] != "" {
// 设置表前缀 // 设置表前缀
mapper := core.NewPrefixMapper(core.SnakeMapper{}, config["prefix"]) mapper := core.NewPrefixMapper(core.SnakeMapper{}, config["prefix"])
engine.SetTableMapper(mapper) engine.SetTableMapper(mapper)
} }
// 本地环境开始日志 // 本地环境开始日志
if macaron.Env == macaron.DEV { if macaron.Env == macaron.DEV {
engine.ShowSQL(true) engine.ShowSQL(true)
engine.Logger().SetLevel(core.LOG_DEBUG) engine.Logger().SetLevel(core.LOG_DEBUG)
} }
return engine return engine
} }
// 获取数据库引擎DSN mysql,sqlite // 获取数据库引擎DSN mysql,sqlite
func getDbEngineDSN(engine string, config map[string]string) string { func getDbEngineDSN(engine string, config map[string]string) string {
engine = strings.ToLower(engine) engine = strings.ToLower(engine)
var dsn string = "" var dsn string = ""
switch engine { switch engine {
case "mysql": case "mysql":
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s", dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
config["user"], config["user"],
config["password"], config["password"],
config["host"], config["host"],
config["port"], config["port"],
config["database"], config["database"],
config["charset"]) config["charset"])
} }
return dsn return dsn
} }
// 获取数据库配置 // 获取数据库配置
func getDbConfig(configFile string) map[string]string { func getDbConfig(configFile string) map[string]string {
config, err := setting.Read(configFile) config, err := setting.Read(configFile)
if err != nil { if err != nil {
panic(err) panic(err)
} }
section := config.Section("db") section := config.Section("db")
if err != nil { if err != nil {
panic(err) panic(err)
} }
var db map[string]string = make(map[string]string) var db map[string]string = make(map[string]string)
db["user"] = section.Key("user").String() db["user"] = section.Key("user").String()
db["password"] = section.Key("password").String() db["password"] = section.Key("password").String()
db["host"] = section.Key("host").String() db["host"] = section.Key("host").String()
db["port"] = section.Key("port").String() db["port"] = section.Key("port").String()
db["database"] = section.Key("database").String() db["database"] = section.Key("database").String()
db["charset"] = section.Key("charset").String() db["charset"] = section.Key("charset").String()
db["prefix"] = section.Key("prefix").String() db["prefix"] = section.Key("prefix").String()
db["engine"] = section.Key("engine").String() db["engine"] = section.Key("engine").String()
return db return db
} }

View File

@ -1,7 +1,7 @@
package models package models
import ( import (
"time" "time"
) )
type Protocol int8 type Protocol int8
@ -9,96 +9,96 @@ type Protocol int8
type TaskType int8 type TaskType int8
const ( const (
HTTP Protocol = 1 HTTP Protocol = 1
SSHCommand Protocol = 2 SSHCommand Protocol = 2
SSHScript Protocol = 3 SSHScript Protocol = 3
) )
const ( const (
Timing TaskType = 1 Timing TaskType = 1
Delay TaskType = 2 Delay TaskType = 2
) )
// 任务 // 任务
type Task struct { type Task struct {
Id int `xorm:"int pk autoincr"` Id int `xorm:"int pk autoincr"`
Name string `xorm:"varchar(64) notnull"` // 任务名称 Name string `xorm:"varchar(64) notnull"` // 任务名称
Spec string `xorm:"varchar(64) notnull"` // crontab 时间格式 Spec string `xorm:"varchar(64) notnull"` // crontab 时间格式
Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3:ssh-script Protocol Protocol `xorm:"tinyint notnull"` // 协议 1:http 2:ssh-command 3:ssh-script
Type TaskType `xorm:"tinyint notnull default 1"` // 任务类型 1: 定时任务 2: 延时任务 Type TaskType `xorm:"tinyint notnull default 1"` // 任务类型 1: 定时任务 2: 延时任务
Command string `xorm:"varchar(512) notnull"` // URL地址或shell命令 Command string `xorm:"varchar(512) notnull"` // URL地址或shell命令
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
Delay int `xorm:"int notnull default 0"` // 延时任务,延时时间(单位秒) Delay int `xorm:"int notnull default 0"` // 延时任务,延时时间(单位秒)
SshHosts string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名, host id逗号分隔 SshHosts string `xorm:"varchar(512) notnull defalut '' "` // SSH主机名, host id逗号分隔
Remark string `xorm:"varchar(512) notnull default ''"` // 备注 Remark string `xorm:"varchar(512) notnull default ''"` // 备注
Created time.Time `xorm:"datetime notnull created"` // 创建时间 Created time.Time `xorm:"datetime notnull created"` // 创建时间
Deleted time.Time `xorm:"datetime deleted"` // 删除时间 Deleted time.Time `xorm:"datetime deleted"` // 删除时间
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:正常 0:停止 Status Status `xorm:"tinyint notnull default 1"` // 状态 1:正常 0:停止
Page int `xorm:"-"` Page int `xorm:"-"`
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
// 新增 // 新增
func (task *Task) Create() (insertId int, err error) { func (task *Task) Create() (insertId int, err error) {
task.Status = Enabled task.Status = Enabled
_, err = Db.Insert(task) _, err = Db.Insert(task)
if err == nil { if err == nil {
insertId = task.Id insertId = task.Id
} }
return return
} }
// 更新 // 更新
func (task *Task) Update(id int, data CommonMap) (int64, error) { 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) { 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) { 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) { 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) { func (task *Task) ActiveList() ([]Task, error) {
task.parsePageAndPageSize() task.parsePageAndPageSize()
list := make([]Task, 0) list := make([]Task, 0)
err := Db.Where("status = ?", Enabled).Desc("id").Find(&list) err := Db.Where("status = ?", Enabled).Desc("id").Find(&list)
return list, err return list, err
} }
func (task *Task) List() ([]Task, error) { func (task *Task) List() ([]Task, error) {
task.parsePageAndPageSize() task.parsePageAndPageSize()
list := make([]Task, 0) list := make([]Task, 0)
err := Db.Desc("id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list) err := Db.Desc("id").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (taskLog *TaskLog) Total() (int64, error) { func (taskLog *TaskLog) Total() (int64, error) {
return Db.Count(taskLog) return Db.Count(taskLog)
} }
func (taskLog *TaskLog) parsePageAndPageSize() { func (taskLog *TaskLog) parsePageAndPageSize() {
if taskLog.Page <= 0 { if taskLog.Page <= 0 {
taskLog.Page = Page taskLog.Page = Page
} }
if taskLog.PageSize >= 0 || taskLog.PageSize > MaxPageSize { if taskLog.PageSize >= 0 || taskLog.PageSize > MaxPageSize {
taskLog.PageSize = PageSize taskLog.PageSize = PageSize
} }
} }
func (taskLog *TaskLog) pageLimitOffset() int { func (taskLog *TaskLog) pageLimitOffset() int {
return (taskLog.Page - 1) * taskLog.PageSize return (taskLog.Page - 1) * taskLog.PageSize
} }

View File

@ -1,62 +1,62 @@
package models package models
import ( import (
"time" "time"
) )
// 任务执行日志 // 任务执行日志
type TaskLog struct { type TaskLog struct {
Id int `xorm:"int pk autoincr"` Id int `xorm:"int pk autoincr"`
TaskId int `xorm:"int not null"` // 任务ID TaskId int `xorm:"int not null"` // 任务ID
StartTime time.Time `xorm:"datetime created"` // 开始执行时间 StartTime time.Time `xorm:"datetime created"` // 开始执行时间
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间 EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
Status Status `xorm:"tinyint notnull default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败 Status Status `xorm:"tinyint notnull default 1"` // 状态 1:执行中 2:执行完毕 0:执行失败
Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果 Result string `xorm:"varchar(65535) notnull defalut '' "` // 执行结果
Page int `xorm:"-"` Page int `xorm:"-"`
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
func (taskLog *TaskLog) Create() (insertId int, err error) { func (taskLog *TaskLog) Create() (insertId int, err error) {
taskLog.Status = Running taskLog.Status = Running
_, err = Db.Insert(taskLog) _, err = Db.Insert(taskLog)
if err == nil { if err == nil {
insertId = taskLog.Id insertId = taskLog.Id
} }
return return
} }
// 更新 // 更新
func (taskLog *TaskLog) Update(id int, data CommonMap) (int64, error) { func (taskLog *TaskLog) Update(id int, data CommonMap) (int64, error) {
return Db.Table(taskLog).ID(id).Update(data) return Db.Table(taskLog).ID(id).Update(data)
} }
func (taskLog *TaskLog) setStatus(id int, status Status) (int64, error) { func (taskLog *TaskLog) setStatus(id int, status Status) (int64, error) {
return taskLog.Update(id, CommonMap{"status": status}) return taskLog.Update(id, CommonMap{"status": status})
} }
func (taskLog *TaskLog) List() ([]TaskLog, error) { func (taskLog *TaskLog) List() ([]TaskLog, error) {
taskLog.parsePageAndPageSize() taskLog.parsePageAndPageSize()
list := make([]TaskLog, 0) list := make([]TaskLog, 0)
err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list) err := Db.Desc("id").Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (task *Task) Total() (int64, error) { func (task *Task) Total() (int64, error) {
return Db.Count(task) return Db.Count(task)
} }
func (task *Task) parsePageAndPageSize() { func (task *Task) parsePageAndPageSize() {
if task.Page <= 0 { if task.Page <= 0 {
task.Page = Page task.Page = Page
} }
if task.PageSize >= 0 || task.PageSize > MaxPageSize { if task.PageSize >= 0 || task.PageSize > MaxPageSize {
task.PageSize = PageSize task.PageSize = PageSize
} }
} }
func (task *Task) pageLimitOffset() int { func (task *Task) pageLimitOffset() int {
return (task.Page - 1) * task.PageSize return (task.Page - 1) * task.PageSize
} }

View File

@ -1,118 +1,118 @@
package models package models
import ( import (
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
"time" "time"
) )
const PasswordSaltLength = 6 const PasswordSaltLength = 6
// 用户model // 用户model
type User struct { type User struct {
Id int `xorm:"pk autoincr notnull "` Id int `xorm:"pk autoincr notnull "`
Name string `xorm:"varchar(32) notnull unique"` // 用户名 Name string `xorm:"varchar(32) notnull unique"` // 用户名
Password string `xorm:"char(32) notnull "` // 密码 Password string `xorm:"char(32) notnull "` // 密码
Salt string `xorm:"char(6) notnull "` // 密码盐值 Salt string `xorm:"char(6) notnull "` // 密码盐值
Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱 Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱
Created time.Time `xorm:"datetime notnull created"` Created time.Time `xorm:"datetime notnull created"`
Updated time.Time `xorm:"datetime updated"` Updated time.Time `xorm:"datetime updated"`
Deleted time.Time `xorm:"datetime deleted"` Deleted time.Time `xorm:"datetime deleted"`
IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户 IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用 Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
Page int `xorm:"-"` Page int `xorm:"-"`
PageSize int `xorm:"-"` PageSize int `xorm:"-"`
} }
// 新增 // 新增
func (user *User) Create() (insertId int, err error) { func (user *User) Create() (insertId int, err error) {
user.Status = Enabled user.Status = Enabled
user.Salt = user.generateSalt() user.Salt = user.generateSalt()
user.Password = user.encryptPassword(user.Password, user.Salt) user.Password = user.encryptPassword(user.Password, user.Salt)
_, err = Db.Insert(user) _, err = Db.Insert(user)
if err == nil { if err == nil {
insertId = user.Id insertId = user.Id
} }
return return
} }
// 更新 // 更新
func (user *User) Update(id int, data CommonMap) (int64, error) { 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) Delete(id int) (int64, error) { 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) { 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) { 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 { func (user *User) Match(username, password string) bool {
where := "(name = ? OR email = ?)" where := "(name = ? OR email = ?)"
_, err := Db.Where(where, username, username).Get(user) _, err := Db.Where(where, username, username).Get(user)
if err != nil { if err != nil {
return false return false
} }
hashPassword := user.encryptPassword(password, user.Salt) hashPassword := user.encryptPassword(password, user.Salt)
if hashPassword != user.Password { if hashPassword != user.Password {
return false return false
} }
return true return true
} }
// 用户名是否存在 // 用户名是否存在
func (user *User) UsernameExists(username string) (int64, error) { 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) { 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) { func (user *User) List() ([]User, error) {
user.parsePageAndPageSize() user.parsePageAndPageSize()
list := make([]User, 0) list := make([]User, 0)
err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).Find(&list) err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).Find(&list)
return list, err return list, err
} }
func (user *User) Total() (int64, error) { func (user *User) Total() (int64, error) {
return Db.Count(user) return Db.Count(user)
} }
func (user *User) parsePageAndPageSize() { func (user *User) parsePageAndPageSize() {
if user.Page <= 0 { if user.Page <= 0 {
user.Page = Page user.Page = Page
} }
if user.PageSize >= 0 || user.PageSize > MaxPageSize { if user.PageSize >= 0 || user.PageSize > MaxPageSize {
user.PageSize = PageSize user.PageSize = PageSize
} }
} }
func (user *User) pageLimitOffset() int { func (user *User) pageLimitOffset() int {
return (user.Page - 1) * user.PageSize return (user.Page - 1) * user.PageSize
} }
// 密码加密 // 密码加密
func (user *User) encryptPassword(password, salt string) string { func (user *User) encryptPassword(password, salt string) string {
return utils.Md5(password + salt) return utils.Md5(password + salt)
} }
// 生成密码盐值 // 生成密码盐值
func (user *User) generateSalt() string { func (user *User) generateSalt() string {
return utils.RandString(PasswordSaltLength) return utils.RandString(PasswordSaltLength)
} }

View File

@ -3,8 +3,8 @@ package ansible
// ansible ad-hoc 命令封装 // ansible ad-hoc 命令封装
import ( import (
"errors" "errors"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
) )
/** /**
@ -15,30 +15,30 @@ import (
* args module * args module
*/ */
func ExecCommand(hosts string, hostFile string, module string, args ...string) (output string, err error) { func ExecCommand(hosts string, hostFile string, module string, args ...string) (output string, err error) {
if hosts == "" || hostFile == "" || module == "" { if hosts == "" || hostFile == "" || module == "" {
err = errors.New("参数不完整") err = errors.New("参数不完整")
return return
} }
commandArgs := []string{hosts, "-i", hostFile, "-m", module} commandArgs := []string{hosts, "-i", hostFile, "-m", module}
if len(args) > 0 { if len(args) > 0 {
commandArgs = append(commandArgs, args...) commandArgs = append(commandArgs, args...)
} }
output, err = utils.ExecShell("ansible", commandArgs...) output, err = utils.ExecShell("ansible", commandArgs...)
return return
} }
// 执行shell命令 // 执行shell命令
func Shell(hosts string, hostFile string, args ...string) (output string, err error) { func Shell(hosts string, hostFile string, args ...string) (output string, err error) {
return ExecCommand(hosts, hostFile, "shell", args...) return ExecCommand(hosts, hostFile, "shell", args...)
} }
// 复制本地脚本到远程执行 // 复制本地脚本到远程执行
func Script(hosts string, hostFile string, args ...string) (output string, err error) { func Script(hosts string, hostFile string, args ...string) (output string, err error) {
return ExecCommand(hosts, hostFile, "script", args...) return ExecCommand(hosts, hostFile, "script", args...)
} }
// 测试主机是否可通 // 测试主机是否可通
func Ping(hosts string, hostFile string) (output string, err error) { func Ping(hosts string, hostFile string) (output string, err error) {
return ExecCommand(hosts, hostFile, "ping") return ExecCommand(hosts, hostFile, "ping")
} }

View File

@ -1,67 +1,67 @@
package ansible package ansible
import ( import (
"bytes" "bytes"
"github.com/ouqiang/cron-scheduler/models" "github.com/ouqiang/cron-scheduler/models"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
"sync" "sync"
) )
// 主机名 // 主机名
var DefaultHosts *Hosts var DefaultHosts *Hosts
type Hosts struct { type Hosts struct {
sync.RWMutex sync.RWMutex
filename string filename string
} }
func NewHosts(hostFilename string) *Hosts { func NewHosts(hostFilename string) *Hosts {
h := &Hosts{sync.RWMutex{}, hostFilename} h := &Hosts{sync.RWMutex{}, hostFilename}
h.Write() h.Write()
return h return h
} }
// 获取hosts文件名 // 获取hosts文件名
func (h *Hosts) GetFilename() string { func (h *Hosts) GetFilename() string {
h.RLock() h.RLock()
defer h.RUnlock() defer h.RUnlock()
return h.filename return h.filename
} }
// 写入hosts // 写入hosts
func (h *Hosts) Write() { func (h *Hosts) Write() {
host := new(models.Host) host := new(models.Host)
hostModels, err := host.List() hostModels, err := host.List()
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
return return
} }
if len(hostModels) == 0 { if len(hostModels) == 0 {
utils.RecordLog("hosts内容为空") utils.RecordLog("hosts内容为空")
return return
} }
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
for _, hostModel := range hostModels { for _, hostModel := range hostModels {
buffer.WriteString(strconv.Itoa(int(hostModel.Id))) buffer.WriteString(strconv.Itoa(int(hostModel.Id)))
buffer.WriteString(" ansible_ssh_host=") buffer.WriteString(" ansible_ssh_host=")
buffer.WriteString(hostModel.Name) buffer.WriteString(hostModel.Name)
buffer.WriteString(" ansible_ssh_port=") buffer.WriteString(" ansible_ssh_port=")
buffer.WriteString(strconv.Itoa(hostModel.Port)) buffer.WriteString(strconv.Itoa(hostModel.Port))
buffer.WriteString(" ansible_ssh_user=") buffer.WriteString(" ansible_ssh_user=")
buffer.WriteString(hostModel.Username) buffer.WriteString(hostModel.Username)
if hostModel.LoginType != models.PublicKey && hostModel.Password != "" { if hostModel.LoginType != models.PublicKey && hostModel.Password != "" {
buffer.WriteString(" ansible_ssh_pass=") buffer.WriteString(" ansible_ssh_pass=")
buffer.WriteString(hostModel.Password) buffer.WriteString(hostModel.Password)
} }
buffer.WriteString("\n") buffer.WriteString("\n")
} }
h.Lock() h.Lock()
defer h.Unlock() defer h.Unlock()
err = ioutil.WriteFile(h.filename, buffer.Bytes(), 0644) err = ioutil.WriteFile(h.filename, buffer.Bytes(), 0644)
return return
} }

View File

@ -1,108 +1,108 @@
package app package app
import ( import (
"os" "os"
"runtime" "runtime"
"github.com/ouqiang/cron-scheduler/models" "github.com/ouqiang/cron-scheduler/models"
"github.com/ouqiang/cron-scheduler/modules/ansible" "github.com/ouqiang/cron-scheduler/modules/ansible"
"github.com/ouqiang/cron-scheduler/modules/crontask" "github.com/ouqiang/cron-scheduler/modules/crontask"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
"github.com/ouqiang/cron-scheduler/service" "github.com/ouqiang/cron-scheduler/service"
) )
var ( var (
AppDir string // 应用根目录 AppDir string // 应用根目录
ConfDir string // 配置目录 ConfDir string // 配置目录
LogDir string // 日志目录 LogDir string // 日志目录
DataDir string // 数据目录存放session文件等 DataDir string // 数据目录存放session文件等
AppConfig string // 应用配置文件 AppConfig string // 应用配置文件
AnsibleHosts string // ansible hosts文件 AnsibleHosts string // ansible hosts文件
Installed bool // 应用是否安装过 Installed bool // 应用是否安装过
) )
func InitEnv() { func InitEnv() {
CheckEnv() CheckEnv()
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
panic(err) panic(err)
} }
AppDir = wd AppDir = wd
ConfDir = AppDir + "/conf" ConfDir = AppDir + "/conf"
LogDir = AppDir + "/log" LogDir = AppDir + "/log"
DataDir = AppDir + "/data" DataDir = AppDir + "/data"
AppConfig = ConfDir + "/app.ini" AppConfig = ConfDir + "/app.ini"
AnsibleHosts = ConfDir + "/ansible_hosts.ini" AnsibleHosts = ConfDir + "/ansible_hosts.ini"
checkDirExists(ConfDir, LogDir, DataDir) checkDirExists(ConfDir, LogDir, DataDir)
// ansible配置文件目录 // ansible配置文件目录
os.Setenv("ANSIBLE_CONFIG", ConfDir) os.Setenv("ANSIBLE_CONFIG", ConfDir)
Installed = IsInstalled() Installed = IsInstalled()
if Installed { if Installed {
InitDb() InitDb()
InitResource() InitResource()
} }
} }
// 判断应用是否安装过 // 判断应用是否安装过
func IsInstalled() bool { func IsInstalled() bool {
_, err := os.Stat(ConfDir + "/install.lock") _, err := os.Stat(ConfDir + "/install.lock")
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
} }
return true return true
} }
// 检测环境 // 检测环境
func CheckEnv() { func CheckEnv() {
// ansible不支持安装在windows上, windows只能作为被控机 // ansible不支持安装在windows上, windows只能作为被控机
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
panic("不支持在windows上运行") panic("不支持在windows上运行")
} }
_, err := utils.ExecShell("ansible", "--version") _, err := utils.ExecShell("ansible", "--version")
if err != nil { if err != nil {
panic(err) panic(err)
} }
_, err = utils.ExecShell("ansible-playbook", "--version") _, err = utils.ExecShell("ansible-playbook", "--version")
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
// 创建安装锁文件 // 创建安装锁文件
func CreateInstallLock() error { func CreateInstallLock() error {
_, err := os.Create(ConfDir + "/install.lock") _, err := os.Create(ConfDir + "/install.lock")
if err != nil { if err != nil {
utils.RecordLog("创建安装锁文件失败") utils.RecordLog("创建安装锁文件失败")
} }
return err return err
} }
// 初始化资源 // 初始化资源
func InitResource() { func InitResource() {
// 初始化ansible Hosts // 初始化ansible Hosts
ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts) ansible.DefaultHosts = ansible.NewHosts(AnsibleHosts)
// 初始化定时任务 // 初始化定时任务
crontask.DefaultCronTask = crontask.NewCronTask() crontask.DefaultCronTask = crontask.NewCronTask()
serviceTask := new(service.Task) serviceTask := new(service.Task)
serviceTask.Initialize() serviceTask.Initialize()
} }
// 初始化DB // 初始化DB
func InitDb() { func InitDb() {
models.Db = models.CreateDb(AppConfig) models.Db = models.CreateDb(AppConfig)
} }
// 检测目录是否存在 // 检测目录是否存在
func checkDirExists(path ...string) { func checkDirExists(path ...string) {
for _, value := range path { for _, value := range path {
_, err := os.Stat(value) _, err := os.Stat(value)
if os.IsNotExist(err) { if os.IsNotExist(err) {
panic(value + "目录不存在") panic(value + "目录不存在")
} }
if os.IsPermission(err) { if os.IsPermission(err) {
panic(value + "目录无权限操作") panic(value + "目录无权限操作")
} }
} }
} }

View File

@ -1,10 +1,10 @@
package crontask package crontask
import ( import (
"errors" "errors"
"github.com/robfig/cron" "github.com/robfig/cron"
"strings" "strings"
"sync" "sync"
) )
var DefaultCronTask *CronTask var DefaultCronTask *CronTask
@ -12,76 +12,76 @@ var DefaultCronTask *CronTask
type CronMap map[string]*cron.Cron type CronMap map[string]*cron.Cron
type CronTask struct { type CronTask struct {
sync.RWMutex sync.RWMutex
tasks CronMap tasks CronMap
} }
func NewCronTask() *CronTask { func NewCronTask() *CronTask {
return &CronTask{ return &CronTask{
sync.RWMutex{}, sync.RWMutex{},
make(CronMap), make(CronMap),
} }
} }
// 新增定时任务,如果name存在则添加失败 // 新增定时任务,如果name存在则添加失败
// name 任务名称 // name 任务名称
// spec crontab时间格式定义 可定义多个时间\n分隔 // spec crontab时间格式定义 可定义多个时间\n分隔
func (cronTask *CronTask) Add(name string, spec string, cmd cron.FuncJob) (err error) { func (cronTask *CronTask) Add(name string, spec string, cmd cron.FuncJob) (err error) {
if name == "" || spec == "" || cmd == nil { if name == "" || spec == "" || cmd == nil {
return errors.New("参数不完整") return errors.New("参数不完整")
} }
if cronTask.IsExist(name) { if cronTask.IsExist(name) {
return errors.New("任务已存在") return errors.New("任务已存在")
} }
spec = strings.TrimSpace(spec) spec = strings.TrimSpace(spec)
cronTask.Lock() cronTask.Lock()
defer cronTask.Unlock() defer cronTask.Unlock()
cronTask.tasks[name] = cron.New() cronTask.tasks[name] = cron.New()
specs := strings.Split(spec, "|||") specs := strings.Split(spec, "|||")
for _, item := range specs { for _, item := range specs {
_, err = cron.Parse(item) _, err = cron.Parse(item)
if err != nil { if err != nil {
return err return err
} }
} }
for _, item := range specs { for _, item := range specs {
err = cronTask.tasks[name].AddFunc(item, cmd) err = cronTask.tasks[name].AddFunc(item, cmd)
} }
cronTask.tasks[name].Start() cronTask.tasks[name].Start()
return err return err
} }
// 任务不存在则新增,任务已存在则删除后新增 // 任务不存在则新增,任务已存在则删除后新增
func (cronTask *CronTask) AddOrReplace(name string, spec string, cmd cron.FuncJob) error { func (cronTask *CronTask) AddOrReplace(name string, spec string, cmd cron.FuncJob) error {
if cronTask.IsExist(name) { if cronTask.IsExist(name) {
cronTask.Delete(name) cronTask.Delete(name)
} }
return cronTask.Add(name, spec, cmd) return cronTask.Add(name, spec, cmd)
} }
// 判断任务是否存在 // 判断任务是否存在
func (cronTask *CronTask) IsExist(name string) bool { func (cronTask *CronTask) IsExist(name string) bool {
cronTask.RLock() cronTask.RLock()
defer cronTask.RUnlock() defer cronTask.RUnlock()
_, ok := cronTask.tasks[name] _, ok := cronTask.tasks[name]
return ok return ok
} }
// 停止任务 // 停止任务
func (cronTask *CronTask) Stop(name string) { func (cronTask *CronTask) Stop(name string) {
if cronTask.IsExist(name) { if cronTask.IsExist(name) {
cronTask.tasks[name].Stop() cronTask.tasks[name].Stop()
} }
} }
// 删除任务 // 删除任务
func (cronTask *CronTask) Delete(name string) { func (cronTask *CronTask) Delete(name string) {
cronTask.Stop(name) cronTask.Stop(name)
cronTask.Lock() cronTask.Lock()
defer cronTask.Unlock() defer cronTask.Unlock()
delete(cronTask.tasks, name) delete(cronTask.tasks, name)
} }

View File

@ -1,43 +1,43 @@
package setting package setting
import ( import (
"errors" "errors"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
) )
// 读取配置 // 读取配置
func Read(filename string) (config *ini.File, err error) { func Read(filename string) (config *ini.File, err error) {
config, err = ini.Load(filename) config, err = ini.Load(filename)
if err != nil { if err != nil {
return return
} }
return return
} }
// 写入配置 // 写入配置
func Write(config map[string]map[string]string, filename string) error { func Write(config map[string]map[string]string, filename string) error {
if len(config) == 0 { if len(config) == 0 {
return errors.New("参数不能为空") return errors.New("参数不能为空")
} }
file := ini.Empty() file := ini.Empty()
for sectionName, items := range config { for sectionName, items := range config {
if sectionName == "" { if sectionName == "" {
return errors.New("节名称不能为空") return errors.New("节名称不能为空")
} }
section, err := file.NewSection(sectionName) section, err := file.NewSection(sectionName)
if err != nil { if err != nil {
return err return err
} }
for key, value := range items { for key, value := range items {
_, err = section.NewKey(key, value) _, err = section.NewKey(key, value)
if err != nil { if err != nil {
return err return err
} }
} }
} }
err := file.SaveTo(filename) err := file.SaveTo(filename)
return err return err
} }

View File

@ -5,9 +5,9 @@ import "encoding/json"
// json 格式输出 // json 格式输出
type response struct { type response struct {
Code int `json:"code"` // 状态码 0:成功 非0:失败 Code int `json:"code"` // 状态码 0:成功 非0:失败
Message string `json:"message"` // 信息 Message string `json:"message"` // 信息
Data interface{} `json:"data"` // 数据 Data interface{} `json:"data"` // 数据
} }
type Json struct{} type Json struct{}
@ -16,24 +16,24 @@ const ResponseSuccess = 0
const ResponseFailure = 1 const ResponseFailure = 1
func (j *Json) Success(message string, data interface{}) string { func (j *Json) Success(message string, data interface{}) string {
return j.response(ResponseSuccess, message, data) return j.response(ResponseSuccess, message, data)
} }
func (j *Json) Failure(code int, message string) string { func (j *Json) Failure(code int, message string) string {
return j.response(code, message, nil) return j.response(code, message, nil)
} }
func (j *Json) response(code int, message string, data interface{}) string { func (j *Json) response(code int, message string, data interface{}) string {
resp := response{ resp := response{
Code: code, Code: code,
Message: message, Message: message,
Data: data, Data: data,
} }
result, err := json.Marshal(resp) result, err := json.Marshal(resp)
if err != nil { if err != nil {
RecordLog(err) RecordLog(err)
} }
return string(result) return string(result)
} }

View File

@ -1,52 +1,52 @@
package utils package utils
import ( import (
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
"log" "log"
"math/rand" "math/rand"
"os/exec" "os/exec"
"time" "time"
) )
// 执行shell命令 // 执行shell命令
func ExecShell(command string, args ...string) (string, error) { func ExecShell(command string, args ...string) (string, error) {
result, err := exec.Command(command, args...).CombinedOutput() result, err := exec.Command(command, args...).CombinedOutput()
return string(result), err return string(result), err
} }
// 生成长度为length的随机字符串 // 生成长度为length的随机字符串
func RandString(length int64) string { func RandString(length int64) string {
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
result := []byte{} result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
sourceLength := len(sources) sourceLength := len(sources)
var i int64 = 0 var i int64 = 0
for ; i < length; i++ { for ; i < length; i++ {
result = append(result, sources[r.Intn(sourceLength)]) result = append(result, sources[r.Intn(sourceLength)])
} }
return string(result) return string(result)
} }
// 生成32位MD5摘要 // 生成32位MD5摘要
func Md5(str string) string { func Md5(str string) string {
m := md5.New() m := md5.New()
m.Write([]byte(str)) m.Write([]byte(str))
return hex.EncodeToString(m.Sum(nil)) return hex.EncodeToString(m.Sum(nil))
} }
// 生成0-max之间随机数 // 生成0-max之间随机数
func RandNumber(max int) int { 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)
} }
// 日志记录 // 日志记录
// todo 保存到哪里 文件,数据库还是elasticsearch?,暂时输出到终端 // todo 保存到哪里 文件,数据库还是elasticsearch?,暂时输出到终端
func RecordLog(v ...interface{}) { func RecordLog(v ...interface{}) {
log.Println(v) log.Println(v)
} }

View File

@ -3,29 +3,29 @@ package utils
import "testing" import "testing"
func TestExecShell(t *testing.T) { func TestExecShell(t *testing.T) {
_, err := ExecShell("ls") _, err := ExecShell("ls")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestRandString(t *testing.T) { func TestRandString(t *testing.T) {
str := RandString(32) str := RandString(32)
if len(str) != 32 { if len(str) != 32 {
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
} }
} }
func TestMd5(t *testing.T) { func TestMd5(t *testing.T) {
str := Md5("123456") str := Md5("123456")
if len(str) != 32 { if len(str) != 32 {
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
} }
} }
func TestRandNumber(t *testing.T) { func TestRandNumber(t *testing.T) {
num := RandNumber(10000) num := RandNumber(10000)
if num <= 0 && num >= 10000 { if num <= 0 && num >= 10000 {
t.Fatalf("随机数不在有效范围内-%d", num) t.Fatalf("随机数不在有效范围内-%d", num)
} }
} }

View File

@ -1,108 +1,108 @@
package install package install
import ( import (
"github.com/ouqiang/cron-scheduler/models" "github.com/ouqiang/cron-scheduler/models"
"github.com/ouqiang/cron-scheduler/modules/app" "github.com/ouqiang/cron-scheduler/modules/app"
"github.com/ouqiang/cron-scheduler/modules/setting" "github.com/ouqiang/cron-scheduler/modules/setting"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
"strconv" "strconv"
) )
// 系统安装 // 系统安装
type InstallForm struct { type InstallForm struct {
DbType string `binding:"IN(mysql)"` DbType string `binding:"IN(mysql)"`
DbHost string `binding:"Required"` DbHost string `binding:"Required"`
DbPort int `binding:"Required"` DbPort int `binding:"Required"`
DbUsername string `binding:"Required"` DbUsername string `binding:"Required"`
DbPassword string `binding:"Required"` DbPassword string `binding:"Required"`
DbName string `binding:"Required"` DbName string `binding:"Required"`
DbTablePrefix string DbTablePrefix string
AdminUsername string `binding:"Required;MinSize(3)"` AdminUsername string `binding:"Required;MinSize(3)"`
AdminPassword string `binding:"Required;MinSize(6)"` AdminPassword string `binding:"Required;MinSize(6)"`
AdminEmail string `binding:"Email"` AdminEmail string `binding:"Email"`
} }
// 显示安装页面 // 显示安装页面
func Show(ctx *macaron.Context) { func Show(ctx *macaron.Context) {
if app.Installed { if app.Installed {
ctx.Redirect("/") ctx.Redirect("/")
} }
ctx.Data["Title"] = "安装" ctx.Data["Title"] = "安装"
ctx.Data["DisableNav"] = true ctx.Data["DisableNav"] = true
ctx.HTML(200, "install/show") ctx.HTML(200, "install/show")
} }
// 安装, // 安装,
func Install(ctx *macaron.Context, form InstallForm) string { func Install(ctx *macaron.Context, form InstallForm) string {
json := utils.Json{} json := utils.Json{}
if app.Installed { if app.Installed {
return json.Failure(utils.ResponseFailure, "系统已安装成功") return json.Failure(utils.ResponseFailure, "系统已安装成功")
} }
// 写入数据库配置 // 写入数据库配置
err := writeConfig(form) err := writeConfig(form)
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
return json.Failure(utils.ResponseFailure, "数据库配置写入文件失败") return json.Failure(utils.ResponseFailure, "数据库配置写入文件失败")
} }
// 初始化Db // 初始化Db
app.InitDb() app.InitDb()
// 创建数据库表 // 创建数据库表
migration := new(models.Migration) migration := new(models.Migration)
err = migration.Exec(form.DbName) err = migration.Exec(form.DbName)
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
return json.Failure(utils.ResponseFailure, "创建数据库表失败") return json.Failure(utils.ResponseFailure, "创建数据库表失败")
} }
// 创建管理员账号 // 创建管理员账号
err = createAdminUser(form) err = createAdminUser(form)
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
return json.Failure(utils.ResponseFailure, "创建管理员账号失败") return json.Failure(utils.ResponseFailure, "创建管理员账号失败")
} }
// 创建安装锁 // 创建安装锁
err = app.CreateInstallLock() err = app.CreateInstallLock()
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
return json.Failure(utils.ResponseFailure, "创建文件安装锁失败") return json.Failure(utils.ResponseFailure, "创建文件安装锁失败")
} }
// 初始化定时任务等 // 初始化定时任务等
app.InitResource() app.InitResource()
return json.Success("安装成功", nil) return json.Success("安装成功", nil)
} }
// 数据库配置写入文件 // 数据库配置写入文件
func writeConfig(form InstallForm) error { func writeConfig(form InstallForm) error {
dbConfig := map[string]map[string]string{ dbConfig := map[string]map[string]string{
"db": map[string]string{ "db": map[string]string{
"engine": form.DbType, "engine": form.DbType,
"host": form.DbHost, "host": form.DbHost,
"port": strconv.Itoa(form.DbPort), "port": strconv.Itoa(form.DbPort),
"user": form.DbUsername, "user": form.DbUsername,
"password": form.DbPassword, "password": form.DbPassword,
"database": form.DbName, "database": form.DbName,
"prefix": form.DbTablePrefix, "prefix": form.DbTablePrefix,
"charset": "utf8", "charset": "utf8",
}, },
} }
return setting.Write(dbConfig, app.AppConfig) return setting.Write(dbConfig, app.AppConfig)
} }
// 创建管理员账号 // 创建管理员账号
func createAdminUser(form InstallForm) error { func createAdminUser(form InstallForm) error {
user := new(models.User) user := new(models.User)
user.Name = form.AdminUsername user.Name = form.AdminUsername
user.Password = form.AdminPassword user.Password = form.AdminPassword
user.Email = form.AdminEmail user.Email = form.AdminEmail
user.IsAdmin = 1 user.IsAdmin = 1
_, err := user.Create() _, err := user.Create()
return err return err
} }

View File

@ -1,30 +1,30 @@
package routers package routers
import ( import (
"github.com/go-macaron/binding" "github.com/go-macaron/binding"
"github.com/ouqiang/cron-scheduler/routers/install" "github.com/ouqiang/cron-scheduler/routers/install"
"gopkg.in/macaron.v1" "gopkg.in/macaron.v1"
) )
// 路由注册 // 路由注册
func Register(m *macaron.Macaron) { func Register(m *macaron.Macaron) {
// 所有GET方法自动注册HEAD方法 // 所有GET方法自动注册HEAD方法
m.SetAutoHead(true) m.SetAutoHead(true)
// 404错误 // 404错误
m.NotFound(func(ctx *macaron.Context) { m.NotFound(func(ctx *macaron.Context) {
ctx.HTML(404, "error/404") ctx.HTML(404, "error/404")
}) })
// 50x错误 // 50x错误
m.InternalServerError(func(ctx *macaron.Context) { m.InternalServerError(func(ctx *macaron.Context) {
ctx.HTML(500, "error/500") ctx.HTML(500, "error/500")
}) })
// 首页 // 首页
m.Get("/", func(ctx *macaron.Context) string { m.Get("/", func(ctx *macaron.Context) string {
return "go home" return "go home"
}) })
// 系统安装 // 系统安装
m.Group("/install", func() { m.Group("/install", func() {
m.Get("", install.Show) m.Get("", install.Show)
m.Post("", binding.Bind(install.InstallForm{}), install.Install) m.Post("", binding.Bind(install.InstallForm{}), install.Install)
}) })
} }

View File

@ -1,175 +1,175 @@
package service package service
import ( import (
"fmt" "fmt"
"github.com/ouqiang/cron-scheduler/models" "github.com/ouqiang/cron-scheduler/models"
"github.com/ouqiang/cron-scheduler/modules/ansible" "github.com/ouqiang/cron-scheduler/modules/ansible"
"github.com/ouqiang/cron-scheduler/modules/crontask" "github.com/ouqiang/cron-scheduler/modules/crontask"
"github.com/ouqiang/cron-scheduler/modules/utils" "github.com/ouqiang/cron-scheduler/modules/utils"
"github.com/robfig/cron" "github.com/robfig/cron"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
) )
type Task struct{} type Task struct{}
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行 // 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
func (task *Task) Initialize() { func (task *Task) Initialize() {
taskModel := new(models.Task) taskModel := new(models.Task)
taskList, err := taskModel.ActiveList() taskList, err := taskModel.ActiveList()
if err != nil { if err != nil {
utils.RecordLog("获取任务列表错误-", err.Error()) utils.RecordLog("获取任务列表错误-", err.Error())
return return
} }
if len(taskList) == 0 { if len(taskList) == 0 {
utils.RecordLog("任务列表为空") utils.RecordLog("任务列表为空")
return return
} }
for _, item := range taskList { for _, item := range taskList {
task.Add(item) task.Add(item)
} }
} }
// 添加任务 // 添加任务
func (task *Task) Add(taskModel models.Task) { func (task *Task) Add(taskModel models.Task) {
taskFunc := createHandlerJob(taskModel) taskFunc := createHandlerJob(taskModel)
if taskFunc == nil { if taskFunc == nil {
utils.RecordLog("添加任务#不存在的任务协议编号", taskModel.Protocol) utils.RecordLog("添加任务#不存在的任务协议编号", taskModel.Protocol)
return return
} }
// 定时任务 // 定时任务
if taskModel.Type == models.Timing { if taskModel.Type == models.Timing {
err := crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc) err := crontask.DefaultCronTask.AddOrReplace(strconv.Itoa(taskModel.Id), taskModel.Spec, taskFunc)
if err != nil { if err != nil {
utils.RecordLog(err) utils.RecordLog(err)
} }
} else if taskModel.Type == models.Delay { } else if taskModel.Type == models.Delay {
// 延时任务 // 延时任务
time.AfterFunc(time.Duration(taskModel.Delay)*time.Second, taskFunc) time.AfterFunc(time.Duration(taskModel.Delay)*time.Second, taskFunc)
} }
} }
type Handler interface { type Handler interface {
Run(taskModel models.Task) (string, error) Run(taskModel models.Task) (string, error)
} }
// HTTP任务 // HTTP任务
type HTTPHandler struct{} type HTTPHandler struct{}
func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) { func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) {
client := &http.Client{} client := &http.Client{}
if taskModel.Timeout > 0 { if taskModel.Timeout > 0 {
client.Timeout = time.Duration(taskModel.Timeout) * time.Second client.Timeout = time.Duration(taskModel.Timeout) * time.Second
} }
req, err := http.NewRequest("POST", taskModel.Command, nil) req, err := http.NewRequest("POST", taskModel.Command, nil)
if err != nil { if err != nil {
utils.RecordLog("创建HTTP请求错误-", err.Error()) utils.RecordLog("创建HTTP请求错误-", err.Error())
return return
} }
req.Header.Set("Content-type", "application/x-www-form-urlencoded") req.Header.Set("Content-type", "application/x-www-form-urlencoded")
req.Header.Set("User-Agent", "golang-cron/scheduler") req.Header.Set("User-Agent", "golang-cron/scheduler")
resp, err := client.Do(req) resp, err := client.Do(req)
defer func() { defer func() {
if resp != nil { if resp != nil {
resp.Body.Close() resp.Body.Close()
} }
}() }()
if err != nil { if err != nil {
utils.RecordLog("HTTP请求错误-", err.Error()) utils.RecordLog("HTTP请求错误-", err.Error())
return return
} }
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
utils.RecordLog("读取HTTP请求返回值失败-", err.Error()) utils.RecordLog("读取HTTP请求返回值失败-", err.Error())
} }
return string(body), err return string(body), err
} }
// SSH-command任务 // SSH-command任务
type SSHCommandHandler struct{} type SSHCommandHandler struct{}
func (ssh *SSHCommandHandler) Run(taskModel models.Task) (string, error) { func (ssh *SSHCommandHandler) Run(taskModel models.Task) (string, error) {
return execSSHHandler("shell", taskModel) return execSSHHandler("shell", taskModel)
} }
// SSH-script任务 // SSH-script任务
type SSHScriptHandler struct{} type SSHScriptHandler struct{}
func (ssh *SSHScriptHandler) Run(taskModel models.Task) (string, error) { func (ssh *SSHScriptHandler) Run(taskModel models.Task) (string, error) {
return execSSHHandler("script", taskModel) return execSSHHandler("script", taskModel)
} }
// SSH任务 // SSH任务
func execSSHHandler(module string, taskModel models.Task) (string, error) { func execSSHHandler(module string, taskModel models.Task) (string, error) {
var args []string = []string{taskModel.Command} var args []string = []string{taskModel.Command}
if taskModel.Timeout > 0 { if taskModel.Timeout > 0 {
// -B 异步执行超时时间, -P 轮询时间 // -B 异步执行超时时间, -P 轮询时间
args = append(args, "-B", strconv.Itoa(taskModel.Timeout), "-P", "10") args = append(args, "-B", strconv.Itoa(taskModel.Timeout), "-P", "10")
} }
if module == "shell" { if module == "shell" {
return ansible.Shell(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...) return ansible.Shell(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
} }
if module == "script" { if module == "script" {
return ansible.Script(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...) return ansible.Script(taskModel.SshHosts, ansible.DefaultHosts.GetFilename(), args...)
} }
return "", nil return "", nil
} }
func createTaskLog(taskId int) (int, error) { func createTaskLog(taskId int) (int, error) {
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
taskLogModel.TaskId = taskId taskLogModel.TaskId = taskId
taskLogModel.StartTime = time.Now() taskLogModel.StartTime = time.Now()
taskLogModel.Status = models.Running taskLogModel.Status = models.Running
insertId, err := taskLogModel.Create() insertId, err := taskLogModel.Create()
return insertId, err return insertId, err
} }
func updateTaskLog(taskLogId int, result string, err error) (int64, error) { func updateTaskLog(taskLogId int, result string, err error) (int64, error) {
fmt.Println(taskLogId) fmt.Println(taskLogId)
taskLogModel := new(models.TaskLog) taskLogModel := new(models.TaskLog)
var status models.Status var status models.Status
if err != nil { if err != nil {
result = err.Error() + " " + result result = err.Error() + " " + result
status = models.Failure status = models.Failure
} else { } else {
status = models.Finish status = models.Finish
} }
return taskLogModel.Update(taskLogId, models.CommonMap{ return taskLogModel.Update(taskLogId, models.CommonMap{
"status": status, "status": status,
"result": result, "result": result,
}) })
} }
func createHandlerJob(taskModel models.Task) cron.FuncJob { func createHandlerJob(taskModel models.Task) cron.FuncJob {
var handler Handler = nil var handler Handler = nil
switch taskModel.Protocol { switch taskModel.Protocol {
case models.HTTP: case models.HTTP:
handler = new(HTTPHandler) handler = new(HTTPHandler)
case models.SSHCommand: case models.SSHCommand:
handler = new(SSHCommandHandler) handler = new(SSHCommandHandler)
case models.SSHScript: case models.SSHScript:
handler = new(SSHScriptHandler) handler = new(SSHScriptHandler)
} }
taskFunc := func() { taskFunc := func() {
taskLogId, err := createTaskLog(taskModel.Id) taskLogId, err := createTaskLog(taskModel.Id)
if err != nil { if err != nil {
utils.RecordLog("写入任务日志失败-", err) utils.RecordLog("写入任务日志失败-", err)
return return
} }
// err != nil 执行失败 // err != nil 执行失败
result, err := handler.Run(taskModel) result, err := handler.Run(taskModel)
_, err = updateTaskLog(int(taskLogId), result, err) _, err = updateTaskLog(int(taskLogId), result, err)
if err != nil { if err != nil {
utils.RecordLog("更新任务日志失败-", err) utils.RecordLog("更新任务日志失败-", err)
} }
} }
return taskFunc return taskFunc
} }