mirror of https://github.com/ouqiang/gocron
parent
f0ff9a88a7
commit
3474636e23
10
README.md
10
README.md
|
@ -1,5 +1,8 @@
|
|||
[![Build Status](https://travis-ci.org/ouqiang/gocron.png)](https://travis-ci.org/ouqiang/gocron)
|
||||
# gocron - 定时任务管理系统
|
||||
[![Build Status](https://travis-ci.org/ouqiang/gocron.png)](https://travis-ci.org/ouqiang/gocron)
|
||||
[![Downloads](https://img.shields.io/github/downloads/ouqiang/gocron/total.svg)](https://github.com/ouqiang/gocron/releases)
|
||||
[![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/ouqiang/gocron/blob/master/LICENSE)
|
||||
[![Release](https://img.shields.io/github/release/ouqiang/gocron.svg?label=Release)](https://github.com/ouqiang/gocron/releases)
|
||||
|
||||
# 项目简介
|
||||
使用Go语言开发的定时任务集中调度和管理系统, 用于替代Linux-crontab [查看文档](https://github.com/ouqiang/gocron/wiki)
|
||||
|
@ -33,7 +36,7 @@
|
|||
|
||||
|
||||
## 下载
|
||||
[v1.2.2](https://github.com/ouqiang/gocron/releases/tag/v1.2.2)
|
||||
[releases](https://github.com/ouqiang/gocron/releases)
|
||||
|
||||
[版本升级](https://github.com/ouqiang/gocron/wiki/版本升级)
|
||||
|
||||
|
@ -84,9 +87,8 @@
|
|||
- [x] 批量开启、关闭、删除任务
|
||||
- [x] 调度器与任务节点通信支持https
|
||||
- [x] 任务分组
|
||||
- [ ] 多用户
|
||||
- [x] 多用户
|
||||
- [ ] 权限控制
|
||||
- [ ] 新增任务API接口
|
||||
|
||||
## 程序使用的组件
|
||||
* Web框架 [Macaron](http://go-macaron.com/)
|
||||
|
|
15
build.sh
15
build.sh
|
@ -16,6 +16,8 @@ OS=''
|
|||
ARCH=''
|
||||
# 应用名称
|
||||
APP_NAME='gocron'
|
||||
# 版本号
|
||||
VERSION=''
|
||||
# 可执行文件名
|
||||
EXEC_NAME=''
|
||||
# 压缩包名称
|
||||
|
@ -23,13 +25,15 @@ COMPRESS_FILE=''
|
|||
|
||||
|
||||
# -p 平台 -a 架构
|
||||
while getopts "p:a:" OPT;
|
||||
while getopts "p:a:v:" OPT;
|
||||
do
|
||||
case $OPT in
|
||||
p) OS=$OPTARG
|
||||
;;
|
||||
a) ARCH=$OPTARG
|
||||
;;
|
||||
v) VERSION=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
@ -52,6 +56,11 @@ if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $VERSION ]];then
|
||||
echo '版本号不能为空'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo '开始编译调度器'
|
||||
if [[ $OS = 'windows' ]];then
|
||||
GOOS=$OS GOARCH=$ARCH go build -tags gocron -ldflags '-w'
|
||||
|
@ -66,10 +75,10 @@ echo '编译完成'
|
|||
|
||||
if [[ $OS = 'windows' ]];then
|
||||
EXEC_NAME=${APP_NAME}.exe
|
||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.zip
|
||||
COMPRESS_FILE=${APP_NAME}-v${VERSION}-${OS}-${ARCH}.zip
|
||||
else
|
||||
EXEC_NAME=${APP_NAME}
|
||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz
|
||||
COMPRESS_FILE=${APP_NAME}-v${VERSION}-${OS}-${ARCH}.tar.gz
|
||||
fi
|
||||
|
||||
mkdir -p $TEMP_DIR/$APP_NAME
|
||||
|
|
|
@ -14,6 +14,8 @@ OS=''
|
|||
ARCH=''
|
||||
# 应用名称
|
||||
APP_NAME='gocron-node'
|
||||
# 版本号
|
||||
VERSION=''
|
||||
# 可执行文件名
|
||||
EXEC_NAME=''
|
||||
# 压缩包名称
|
||||
|
@ -21,13 +23,15 @@ COMPRESS_FILE=''
|
|||
|
||||
|
||||
# -p 平台 -a 架构
|
||||
while getopts "p:a:" OPT;
|
||||
while getopts "p:a:v:" OPT;
|
||||
do
|
||||
case $OPT in
|
||||
p) OS=$OPTARG
|
||||
;;
|
||||
a) ARCH=$OPTARG
|
||||
;;
|
||||
v) VERSION=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
@ -50,12 +54,17 @@ if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z $VERSION ]];then
|
||||
echo '版本号不能为空'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $OS = 'windows' ]];then
|
||||
EXEC_NAME=${APP_NAME}.exe
|
||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.zip
|
||||
COMPRESS_FILE=${APP_NAME}-v${VERSION}-${OS}-${ARCH}.zip
|
||||
else
|
||||
EXEC_NAME=${APP_NAME}
|
||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz
|
||||
COMPRESS_FILE=${APP_NAME}-v${VERSION}-${OS}-${ARCH}.tar.gz
|
||||
fi
|
||||
|
||||
echo '开始编译任务节点'
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/ouqiang/gocron/cmd"
|
||||
)
|
||||
|
||||
const AppVersion = "1.2.2"
|
||||
const AppVersion = "1.3"
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
|
|
@ -53,10 +53,11 @@ func (migration *Migration) Upgrade(oldVersionId int) {
|
|||
return
|
||||
}
|
||||
|
||||
versionIds := []int{110, 122}
|
||||
versionIds := []int{110, 122, 130}
|
||||
upgradeFuncs := []func(*xorm.Session) error{
|
||||
migration.upgradeFor110,
|
||||
migration.upgradeFor122,
|
||||
migration.upgradeFor130,
|
||||
}
|
||||
|
||||
startIndex := -1
|
||||
|
@ -155,3 +156,16 @@ func (migration *Migration) upgradeFor122(session *xorm.Session) error {
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
// 升级到1.2.3版本
|
||||
func (migration *Migration) upgradeFor130(session *xorm.Session) error {
|
||||
logger.Info("开始升级到v1.3")
|
||||
|
||||
tableName := TablePrefix + "user"
|
||||
// 删除user表deleted字段
|
||||
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN deleted", tableName))
|
||||
|
||||
logger.Info("已升级到v1.3\n")
|
||||
|
||||
return err
|
||||
}
|
|
@ -26,7 +26,6 @@ const (
|
|||
Running Status = 1 // 运行中
|
||||
Finish Status = 2 // 完成
|
||||
Cancel Status = 3 // 取消
|
||||
Waiting Status = 5 // 等待中
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -16,7 +16,6 @@ type User struct {
|
|||
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:"-"`
|
||||
|
@ -65,8 +64,8 @@ func (user *User) Enable(id int) (int64, error) {
|
|||
|
||||
// 验证用户名和密码
|
||||
func (user *User) Match(username, password string) bool {
|
||||
where := "(name = ? OR email = ?)"
|
||||
_, err := Db.Where(where, username, username).Get(user)
|
||||
where := "(name = ? OR email = ?) AND status =? "
|
||||
_, err := Db.Where(where, username, username, Enabled).Get(user)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -78,19 +77,35 @@ func (user *User) Match(username, password string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// 获取用户详情
|
||||
func (user *User) Find(id int) (error) {
|
||||
_, err := Db.Id(id).Get(user)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// 用户名是否存在
|
||||
func (user *User) UsernameExists(username string) (int64, error) {
|
||||
func (user *User) UsernameExists(username string, uid int) (int64, error) {
|
||||
if uid > 0 {
|
||||
return Db.Where("name = ? AND id != ?", username, uid).Count(user)
|
||||
}
|
||||
|
||||
return Db.Where("name = ?", username).Count(user)
|
||||
}
|
||||
|
||||
// 邮箱地址是否存在
|
||||
func (user *User) EmailExists(email string) (int64, error) {
|
||||
func (user *User) EmailExists(email string, uid int) (int64, error) {
|
||||
if uid > 0 {
|
||||
return Db.Where("email = ? AND id != ?", email, uid).Count(user)
|
||||
}
|
||||
|
||||
return Db.Where("email = ?", email).Count(user)
|
||||
}
|
||||
|
||||
func (user *User) List() ([]User, error) {
|
||||
func (user *User) List(params CommonMap) ([]User, error) {
|
||||
user.parsePageAndPageSize(params)
|
||||
list := make([]User, 0)
|
||||
err := Db.Desc("id").Find(&list)
|
||||
err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).Find(&list)
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// ParsePageAndPageSize 解析查询参数中的页数和每页数量
|
||||
func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) {
|
||||
page := ctx.QueryInt("page")
|
||||
pageSize := ctx.QueryInt("page_size")
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// Index 主机列表
|
||||
func Index(ctx *macaron.Context) {
|
||||
hostModel := new(models.Host)
|
||||
queryParams := parseQueryParams(ctx)
|
||||
|
@ -42,11 +43,13 @@ func Index(ctx *macaron.Context) {
|
|||
ctx.HTML(200, "host/index")
|
||||
}
|
||||
|
||||
// Create 创建主机页面
|
||||
func Create(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "添加主机"
|
||||
ctx.HTML(200, "host/host_form")
|
||||
}
|
||||
|
||||
// Edit 修改主机页面
|
||||
func Edit(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "编辑主机"
|
||||
hostModel := new(models.Host)
|
||||
|
@ -67,6 +70,7 @@ type HostForm struct {
|
|||
Remark string
|
||||
}
|
||||
|
||||
// Error 表单验证错误处理
|
||||
func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||
if len(errs) == 0 {
|
||||
return
|
||||
|
@ -77,6 +81,7 @@ func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
|||
ctx.Resp.Write([]byte(content))
|
||||
}
|
||||
|
||||
// Store 保存、修改主机信息
|
||||
func Store(ctx *macaron.Context, form HostForm) string {
|
||||
json := utils.JsonResponse{}
|
||||
hostModel := new(models.Host)
|
||||
|
@ -129,6 +134,7 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
|||
return json.Success("保存成功", nil)
|
||||
}
|
||||
|
||||
// Remove 删除主机
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
id, err := strconv.Atoi(ctx.Params(":id"))
|
||||
json := utils.JsonResponse{}
|
||||
|
@ -161,6 +167,7 @@ func Remove(ctx *macaron.Context) string {
|
|||
return json.Success("操作成功", nil)
|
||||
}
|
||||
|
||||
// Ping 测试主机是否可连接
|
||||
func Ping(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
hostModel := new(models.Host)
|
||||
|
|
|
@ -41,11 +41,20 @@ func Register(m *macaron.Macaron) {
|
|||
|
||||
// 用户
|
||||
m.Group("/user", func() {
|
||||
m.Get("", user.Index)
|
||||
m.Get("/create", user.Create)
|
||||
m.Get("/edit/:id", user.Edit)
|
||||
m.Post("/store", binding.Bind(user.UserForm{}), user.Store)
|
||||
m.Post("/remove/:id", user.Remove)
|
||||
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.Post("/enable/:id", user.Enable)
|
||||
m.Post("/disable/:id", user.Disable)
|
||||
m.Get("/editMyPassword", user.EditMyPassword)
|
||||
m.Post("/editMyPassword", user.UpdateMyPassword)
|
||||
m.Get("/editPassword/:id", user.EditPassword)
|
||||
m.Post("/editPassword/:id", user.UpdatePassword)
|
||||
})
|
||||
|
||||
// 定时任务
|
||||
|
|
|
@ -208,7 +208,7 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
|||
taskHostModel.Remove(id)
|
||||
}
|
||||
|
||||
status, err := taskModel.GetStatus(id)
|
||||
status, _ := taskModel.GetStatus(id)
|
||||
if status == models.Enabled && taskModel.Level == models.TaskLevelParent {
|
||||
addTaskToTimer(id)
|
||||
}
|
||||
|
|
|
@ -6,23 +6,222 @@ import (
|
|||
"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"
|
||||
"github.com/Unknwon/paginater"
|
||||
"html/template"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// @author qiang.ou<qingqianludao@gmail.com>
|
||||
// @date 2017/4/23-14:16
|
||||
|
||||
// UserForm 用户表单
|
||||
type UserForm struct {
|
||||
Id int
|
||||
Name string `binding:"Required;MaxSize(32)"` // 用户名
|
||||
Password string // 密码
|
||||
ConfirmPassword string // 确认密码
|
||||
Email string `binding:"Required;MaxSize(50)"` // 邮箱
|
||||
IsAdmin int8 // 是否是管理员 1:管理员 0:普通用户
|
||||
Status models.Status
|
||||
}
|
||||
|
||||
// Index 用户列表页
|
||||
func Index(ctx *macaron.Context) {
|
||||
queryParams := parseQueryParams(ctx)
|
||||
userModel := new(models.User)
|
||||
users, err := userModel.List(queryParams)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
total, err := userModel.Total()
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
PageParams := fmt.Sprintf("page_size=%d", 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["Params"] = queryParams
|
||||
ctx.Data["Title"] = "用户列表"
|
||||
ctx.Data["Users"] = users
|
||||
ctx.HTML(200, "user/index")
|
||||
}
|
||||
|
||||
// 解析查询参数
|
||||
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||
var params models.CommonMap = models.CommonMap{}
|
||||
base.ParsePageAndPageSize(ctx, params)
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// Create 新增用户页
|
||||
func Create(ctx *macaron.Context) {
|
||||
userModel := new(models.User)
|
||||
userModel.Status = models.Enabled
|
||||
userModel.IsAdmin = 0
|
||||
ctx.Data["User"] = userModel
|
||||
ctx.Data["Title"] = "添加用户"
|
||||
ctx.HTML(200, "user/user_form")
|
||||
}
|
||||
|
||||
// 编辑页面
|
||||
func Edit(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "编辑用户"
|
||||
userModel := new(models.User)
|
||||
id := ctx.ParamsInt(":id")
|
||||
err := userModel.Find(id)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
ctx.Data["User"] = userModel
|
||||
ctx.HTML(200, "user/user_form")
|
||||
}
|
||||
|
||||
// 保存任务
|
||||
func Store(ctx *macaron.Context, form UserForm) string {
|
||||
form.Name = strings.TrimSpace(form.Name)
|
||||
form.Email = strings.TrimSpace(form.Email)
|
||||
form.Password = strings.TrimSpace(form.Password)
|
||||
form.ConfirmPassword = strings.TrimSpace(form.ConfirmPassword)
|
||||
json := utils.JsonResponse{}
|
||||
userModel := models.User{}
|
||||
nameExists, err := userModel.UsernameExists(form.Name, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
if nameExists > 0 {
|
||||
return json.CommonFailure("用户名已存在")
|
||||
}
|
||||
|
||||
emailExists, err := userModel.EmailExists(form.Email, form.Id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
if emailExists > 0 {
|
||||
return json.CommonFailure("邮箱已存在")
|
||||
}
|
||||
|
||||
if form.Id == 0 {
|
||||
if form.Password == "" {
|
||||
return json.CommonFailure("请输入密码")
|
||||
}
|
||||
if form.ConfirmPassword == "" {
|
||||
return json.CommonFailure("请再次输入密码")
|
||||
}
|
||||
if form.Password != form.ConfirmPassword {
|
||||
return json.CommonFailure("两次密码输入不一致")
|
||||
}
|
||||
}
|
||||
userModel.Name = form.Name
|
||||
userModel.Email = form.Email
|
||||
userModel.Password = form.Password
|
||||
userModel.IsAdmin = form.IsAdmin
|
||||
userModel.Status = form.Status
|
||||
|
||||
if form.Id == 0 {
|
||||
_, err = userModel.Create()
|
||||
if err != nil {
|
||||
return json.CommonFailure("添加失败", err)
|
||||
}
|
||||
} else {
|
||||
_, err = userModel.Update(form.Id, models.CommonMap{
|
||||
"name": form.Name,
|
||||
"email": form.Email,
|
||||
"status": form.Status,
|
||||
"is_admin": form.IsAdmin,
|
||||
})
|
||||
if err != nil {
|
||||
return json.CommonFailure("修改失败", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return json.Success("保存成功", nil)
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
func Remove(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
|
||||
userModel := new(models.User)
|
||||
_, err := userModel.Delete(id)
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// 激活用户
|
||||
func Enable(ctx *macaron.Context) string {
|
||||
return changeStatus(ctx, models.Enabled)
|
||||
}
|
||||
|
||||
// 禁用用户
|
||||
func Disable(ctx *macaron.Context) string {
|
||||
return changeStatus(ctx, models.Disabled)
|
||||
}
|
||||
|
||||
// 改变任务状态
|
||||
func changeStatus(ctx *macaron.Context, status models.Status) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
json := utils.JsonResponse{}
|
||||
userModel := new(models.User)
|
||||
_, err := userModel.Update(id, models.CommonMap{
|
||||
"Status": status,
|
||||
})
|
||||
if err != nil {
|
||||
return json.CommonFailure(utils.FailureContent, err)
|
||||
}
|
||||
|
||||
return json.Success(utils.SuccessContent, nil)
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func Login(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "用户登录"
|
||||
ctx.HTML(200, "user/login")
|
||||
}
|
||||
|
||||
// EditPassword 修改密码页面
|
||||
func EditPassword(ctx *macaron.Context) {
|
||||
id := ctx.ParamsInt(":id")
|
||||
ctx.Data["Title"] = "修改密码"
|
||||
ctx.Data["Id"] = id
|
||||
ctx.HTML(200, "user/editPassword")
|
||||
}
|
||||
|
||||
func UpdatePassword(ctx *macaron.Context, sess session.Store) string {
|
||||
// UpdatePassword 更新我的密码
|
||||
func UpdatePassword(ctx *macaron.Context) string {
|
||||
id := ctx.ParamsInt(":id")
|
||||
newPassword := ctx.QueryTrim("new_password")
|
||||
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||
json := utils.JsonResponse{}
|
||||
if newPassword == "" || confirmNewPassword == "" {
|
||||
return json.CommonFailure("请输入密码")
|
||||
}
|
||||
userModel := new(models.User)
|
||||
_, err := userModel.UpdatePassword(id, newPassword)
|
||||
if err != nil {
|
||||
return json.CommonFailure("修改失败")
|
||||
}
|
||||
|
||||
return json.Success("修改成功", nil)
|
||||
}
|
||||
|
||||
// EditMyPassword 修改我的密码页面
|
||||
func EditMyPassword(ctx *macaron.Context) {
|
||||
ctx.Data["Title"] = "修改密码"
|
||||
ctx.HTML(200, "user/editMyPassword")
|
||||
}
|
||||
|
||||
// UpdateMyPassword 更新我的密码
|
||||
func UpdateMyPassword(ctx *macaron.Context, sess session.Store) string {
|
||||
oldPassword := ctx.QueryTrim("old_password")
|
||||
newPassword := ctx.QueryTrim("new_password")
|
||||
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||
|
@ -48,6 +247,7 @@ func UpdatePassword(ctx *macaron.Context, sess session.Store) string {
|
|||
return json.Success("修改成功", nil)
|
||||
}
|
||||
|
||||
// ValidateLogin 验证用户登录
|
||||
func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captcha) string {
|
||||
username := ctx.QueryTrim("username")
|
||||
password := ctx.QueryTrim("password")
|
||||
|
@ -78,6 +278,7 @@ func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captch
|
|||
return json.Success("登录成功", nil)
|
||||
}
|
||||
|
||||
// Logout 用户退出
|
||||
func Logout(ctx *macaron.Context, sess session.Store) {
|
||||
if IsLogin(sess) {
|
||||
err := sess.Destory(ctx)
|
||||
|
@ -89,6 +290,7 @@ func Logout(ctx *macaron.Context, sess session.Store) {
|
|||
Login(ctx)
|
||||
}
|
||||
|
||||
// Username 获取session中的用户名
|
||||
func Username(sess session.Store) string {
|
||||
username, ok := sess.Get("username").(string)
|
||||
if ok {
|
||||
|
@ -98,6 +300,7 @@ func Username(sess session.Store) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Uid 获取session中的Uid
|
||||
func Uid(sess session.Store) int {
|
||||
uid, ok := sess.Get("uid").(int)
|
||||
if ok {
|
||||
|
@ -107,6 +310,7 @@ func Uid(sess session.Store) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// IsLogin 判断用户是否已登录
|
||||
func IsLogin(sess session.Store) bool {
|
||||
uid, ok := sess.Get("uid").(int)
|
||||
if ok && uid > 0 {
|
||||
|
@ -116,6 +320,7 @@ func IsLogin(sess session.Store) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// IsAdmin 判断当前用户是否是管理员
|
||||
func IsAdmin(sess session.Store) bool {
|
||||
isAdmin, ok := sess.Get("isAdmin").(int8)
|
||||
if ok && isAdmin > 0 {
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
你好,{{{.LoginUsername}}}
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="menu">
|
||||
<a href="/user/editPassword" class="item"><i class="edit icon"></i>修改密码</a>
|
||||
<a href="/user/editMyPassword" class="item"><i class="edit icon"></i>修改密码</a>
|
||||
<a class="item" href="/user/logout"><i class="sign out icon"></i>退出</a>
|
||||
</div>
|
||||
{{{end}}}
|
||||
|
@ -62,7 +62,7 @@
|
|||
<div class="right menu">
|
||||
<a class="item {{{if or (eq .Controller "task") (eq .Controller "delaytask")}}}active{{{end}}}" href="/task"><i class="tasks icon"></i>任务</a>
|
||||
<a class="item {{{if eq .Controller "host"}}}active{{{end}}}" href="/host"><i class="linux icon"></i>任务节点</a>
|
||||
<!-- <a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>账户</a> -->
|
||||
<a class="item {{{if eq .Controller "user"}}}active{{{end}}}" href="/user"><i class="user icon"></i>用户管理</a>
|
||||
{{{if gt .LoginUid 0}}}
|
||||
<a class="item {{{if eq .Controller "manage"}}}active{{{end}}}" href="/manage/slack/edit"><i class="settings icon"></i>管理</a>
|
||||
{{{end}}}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
</div>
|
||||
|
||||
<div class="ui primary submit button">保存</div>
|
||||
<a class="ui button" onclick="location.href='/task';">取消</a>
|
||||
<a class="ui button" onclick="location.href='/host';">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
{{{ template "common/header" . }}}
|
||||
<div class="ui small modal">
|
||||
<div class="header">修改密码</div>
|
||||
<div class="content">
|
||||
<form class="ui form">
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>原密码</label>
|
||||
<input class="small input" type="password" name="old_password" placeholder="原密码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>新密码</label>
|
||||
<input type="password" name="new_password" placeholder="新密码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>确认新密码</label>
|
||||
<input type="password" name="confirm_new_password" placeholder="确认新密码">
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui button primary button" >确定</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('.ui.modal').modal('setting', 'closable', false).modal('show');
|
||||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/user/editMyPassword', fields, function(code, message) {
|
||||
swal("操作成功", '修改成功', 'success');
|
||||
location.href = "/task"
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
oldPassword: {
|
||||
identifier : 'old_password',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入原密码'
|
||||
}
|
||||
]
|
||||
},
|
||||
newPassword: {
|
||||
identifier : 'new_password',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入新密码'
|
||||
}
|
||||
]
|
||||
},
|
||||
confirmNewPassword: {
|
||||
identifier : 'confirm_new_password',
|
||||
rules: [
|
||||
{
|
||||
type : 'match[new_password]',
|
||||
prompt : '两次输入密码不匹配'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
{{{ template "common/footer" . }}}
|
|
@ -3,12 +3,6 @@
|
|||
<div class="header">修改密码</div>
|
||||
<div class="content">
|
||||
<form class="ui form">
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>原密码</label>
|
||||
<input class="small input" type="password" name="old_password" placeholder="原密码">
|
||||
</div>
|
||||
</div>
|
||||
<div class="two fields">
|
||||
<div class="field">
|
||||
<label>新密码</label>
|
||||
|
@ -21,7 +15,7 @@
|
|||
<input type="password" name="confirm_new_password" placeholder="确认新密码">
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui button primary button" >登录</button>
|
||||
<button class="ui button primary button" >确定</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,23 +25,14 @@
|
|||
$('.ui.form').form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/user/editPassword', fields, function(code, message) {
|
||||
util.post('/user/editPassword/{{{.Id}}}', fields, function(code, message) {
|
||||
swal("操作成功", '修改成功', 'success');
|
||||
location.href = "/task"
|
||||
location.href = "/user"
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
oldPassword: {
|
||||
identifier : 'old_password',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入原密码'
|
||||
}
|
||||
]
|
||||
},
|
||||
newPassword: {
|
||||
identifier : 'new_password',
|
||||
rules: [
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
{{{ template "user/menu" . }}}
|
||||
|
||||
<div class="twelve wide column">
|
||||
<div class="pageHeader">
|
||||
<div class="segment">
|
||||
<h3 class="ui dividing header">
|
||||
<a href="/user/create">
|
||||
<i class="large add icon"></i>
|
||||
<div class="content">
|
||||
添加用户
|
||||
</div>
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<table class="ui celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户ID</th>
|
||||
<th>用户名</th>
|
||||
<th>邮箱</th>
|
||||
<th>角色</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{{range $i, $v := .Users}}}
|
||||
<tr>
|
||||
<td>{{{.Id}}}</td>
|
||||
<td>{{{.Name}}}</td>
|
||||
<td>{{{.Email}}}</td>
|
||||
<td>{{{if .IsAdmin}}}管理员{{{else}}}普通用户{{{end}}}</td>
|
||||
<td>{{{if .Status}}}启用{{{else}}}禁用{{{end}}}</td>
|
||||
<td class="operation">
|
||||
<a class="ui purple button" href="/user/edit/{{{.Id}}}">编辑</a>
|
||||
{{{if eq .Status 1}}}
|
||||
<button class="ui button red" onclick="changeStatus({{{.Id}}},{{{.Status}}})">禁用</button>
|
||||
{{{else}}}
|
||||
<button class="ui button twitter" onclick="changeStatus({{{.Id}}},{{{.Status}}})">启用</button>
|
||||
{{{end}}}
|
||||
<a href="/user/editPassword/{{{.Id}}}">
|
||||
<button class="ui button facebook">修改密码</button>
|
||||
</a>
|
||||
<button class="ui positive button" onclick="util.removeConfirm('/user/remove/{{{.Id}}}')">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{{end}}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{{ template "common/pagination" .}}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function changeStatus(id ,status) {
|
||||
var url = '';
|
||||
if (status) {
|
||||
url = '/user/disable';
|
||||
} else {
|
||||
url = '/user/enable';
|
||||
}
|
||||
url += '/' + id;
|
||||
util.post(url,{}, function() {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
|
@ -0,0 +1,9 @@
|
|||
<div class="four wide column">
|
||||
<div class="verticalMenu">
|
||||
<div class="ui vertical pointing menu fluid">
|
||||
<a class="{{{if eq .URI "/user"}}}active teal{{{end}}} item" href="/user">
|
||||
<i class="user icon"></i> 用户列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,142 @@
|
|||
{{{ template "common/header" . }}}
|
||||
|
||||
<div class="ui grid">
|
||||
{{{ template "user/menu" . }}}
|
||||
|
||||
<div class="twelve wide column">
|
||||
<div class="pageHeader">
|
||||
<div class="segment">
|
||||
<h3 class="ui dividing header">
|
||||
<div class="content">
|
||||
添加用户
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<form class="ui form fluid vertical segment">
|
||||
<input type="hidden" name="id" value="{{{.User.Id}}}">
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>用户名</label>
|
||||
<div class="ui small input">
|
||||
<input type="text" name="name" value="{{{.User.Name}}}" placeholder="请输入用户名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>邮箱</label>
|
||||
<div class="ui small input">
|
||||
<input type="text" name="email" value="{{{.User.Email}}}" placeholder="请输入邮箱">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{{if eq .User.Id 0}}}
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>密码</label>
|
||||
<input type="password" name="password" placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>确认密码</label>
|
||||
<input type="password" name="confirm_password" placeholder="请再次输入密码">
|
||||
</div>
|
||||
</div>
|
||||
{{{end}}}
|
||||
|
||||
<div class="three fields">
|
||||
<div class="field">
|
||||
<label>角色</label>
|
||||
<input type="radio" name="is_admin" value="0" {{{if eq .User.IsAdmin 0}}}checked{{{end}}}> 普通用户
|
||||
<input type="radio" name="is_admin" value="1" {{{if eq .User.IsAdmin 1}}}checked{{{end}}}> 管理员
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label>状态</label>
|
||||
<input type="radio" name="status" value="1" {{{if eq .User.Status 1}}}checked{{{end}}}> 启用
|
||||
<input type="radio" name="status" value="0"
|
||||
{{{if eq .User.Status 0}}}checked{{{end}}}
|
||||
> 禁用
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui primary submit button">保存</div>
|
||||
<a class="ui button" onclick="location.href='/user';">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var $uiForm = $('.ui.form');
|
||||
$($uiForm).form(
|
||||
{
|
||||
onSuccess: function(event, fields) {
|
||||
util.post('/user/store', fields, function(code, message) {
|
||||
location.href = "/user"
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
fields: {
|
||||
name: {
|
||||
identifier : 'name',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入用户名'
|
||||
},
|
||||
{
|
||||
type : 'minLength[3]',
|
||||
prompt : '长度不能少于3'
|
||||
},
|
||||
{
|
||||
type : 'maxLength[32]',
|
||||
prompt : '长度不能超过32'
|
||||
}
|
||||
]
|
||||
},
|
||||
password: {
|
||||
identifier : 'password',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入密码'
|
||||
},
|
||||
{
|
||||
type : 'minLength[6]',
|
||||
prompt : '长度不能少于6位'
|
||||
}
|
||||
]
|
||||
},
|
||||
confirmPassword: {
|
||||
identifier : 'confirm_password',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请再次输入密码'
|
||||
},
|
||||
{
|
||||
type : 'match[password]',
|
||||
prompt : '两次输入密码不匹配'
|
||||
}
|
||||
]
|
||||
},
|
||||
email: {
|
||||
identifier : 'email',
|
||||
rules: [
|
||||
{
|
||||
type : 'empty',
|
||||
prompt : '请输入邮箱'
|
||||
},
|
||||
{
|
||||
type : 'email',
|
||||
prompt : '邮箱格式错误'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
inline : true
|
||||
});
|
||||
</script>
|
||||
|
||||
{{{ template "common/footer" . }}}
|
Loading…
Reference in New Issue