gocron/routers/routers.go

293 lines
8.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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"
)
// 静态文件目录
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)
})
// 用户
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("/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)
})
// 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, "网站暂时无法访问,请稍后再试")))
}
})
}
// 中间件注册
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)
}
})
}
// 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)
}
})
}
// IP验证, 通过反向代理访问gocron需设置Header X-Real-IP才能获取到客户端真实IP
func ipAuth(ctx *macaron.Context) {
allowIpsStr := app.Setting.Key("allow_ips").String()
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 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.Key("app.name").String()
}
/** API接口签名验证 **/
func apiAuth(ctx *macaron.Context) {
apiSignEnable := app.Setting.Key("api.sign.enable").String()
apiSignEnable = strings.TrimSpace(apiSignEnable)
if apiSignEnable == "false" {
return
}
apiKey := app.Setting.Key("api.key").String()
apiSecret := app.Setting.Key("api.secret").String()
apiKey = strings.TrimSpace(apiKey)
apiSecret = strings.TrimSpace(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
}
return false
}
func isGetRequest(ctx *macaron.Context) bool {
return ctx.Req.Method == "GET"
}