This commit is contained in:
arraykeys
2019-08-08 17:13:34 +08:00
parent e8e5966a8c
commit f0bf2d5fec
2885 changed files with 1195993 additions and 12 deletions
+202
View File
@@ -0,0 +1,202 @@
package controllers
import (
"anytunnel/at-admin/app/utils"
"encoding/json"
"strings"
"github.com/astaxie/beego"
)
type BaseController struct {
beego.Controller
}
//验证登录
func (this *BaseController) isLogin() bool {
passport := beego.AppConfig.String("author.passport")
//fmt.Println(passport)
cookie := this.Ctx.GetCookie(passport)
//cookie 失效
if cookie == "" {
//fmt.Println("cookie 失效")
return false
}
user := this.GetSession("author")
//fmt.Println(user)
//session 失效
if user == nil {
//fmt.Println("session 失效")
return false
}
encrypt := new(utils.Encrypt)
cookieValue, _ := encrypt.Base64Decode(cookie)
//fmt.Println("get cookie " + cookie)
identifyList := strings.Split(cookieValue, "@")
//fmt.Println(cookieValue, identifyList)
if cookieValue == "" || len(identifyList) != 2 {
//fmt.Println("cookieValue 无效")
return false
}
name := identifyList[0]
identify := identifyList[1]
userValue := user.(map[string]string)
//对比cookie 和 session name
if name != userValue["username"] {
//fmt.Println("对比cookie 和 session name 无效")
//fmt.Println(userValue)
return false
}
//对比客户端UAG and IP
if identify != utils.NewEncrypt().Md5Encode(this.Ctx.Request.UserAgent()+this.getClientIp()+userValue["password"]) {
//fmt.Println("对比客户端UAG and IP 无效")
return false
}
//success
return true
}
func (this *BaseController) viewLayoutTitle(title, viewName, layout string) {
this.ViewLayoutTitle("", title, viewName, layout)
}
func (this *BaseController) viewLayout(viewName, layout string) {
this.ViewLayout("", viewName, layout)
}
func (this *BaseController) view(viewName string) {
this.View("", viewName)
}
func (this *BaseController) viewTitle(title, viewName string) {
this.ViewTitle("", title, viewName)
}
func (this *BaseController) ViewLayoutTitle(module, title, viewName, layout string) {
if module != "" {
this.Layout = "layout/modules/" + module + "/" + layout + ".html"
this.TplName = "modules/" + module + "/" + viewName + ".html"
} else {
this.Layout = "layout/" + layout + ".html"
this.TplName = viewName + ".html"
}
this.Data["title"] = title
this.Render()
}
func (this *BaseController) ViewLayout(module, viewName, layout string) {
if module != "" {
this.Layout = "layout/modules/" + module + "/" + layout + ".html"
this.TplName = "modules/" + module + "/" + viewName + ".html"
} else {
this.Layout = "layout/" + layout + ".html"
this.TplName = viewName + ".html"
}
this.Data["title"] = ""
this.Render()
}
func (this *BaseController) View(module, viewName string) {
if module != "" {
this.Layout = "layout/modules/" + module + "/default.html"
this.TplName = "modules/" + module + "/" + viewName + ".html"
} else {
this.Layout = "layout/default.html"
this.TplName = viewName + ".html"
}
this.Data["title"] = ""
this.Render()
}
func (this *BaseController) ViewTitle(module, title, viewName string) {
if module != "" {
this.Layout = "layout/modules/" + module + "/default.html"
this.TplName = "modules/" + module + "/" + viewName + ".html"
} else {
this.Layout = "layout/default.html"
this.TplName = viewName + ".html"
}
this.Data["title"] = title
this.Render()
}
func (this *BaseController) ViewError(errorMessage string, data ...interface{}) {
this.viewError(errorMessage, data...)
}
func (this *BaseController) viewError(errorMessage string, data ...interface{}) {
this.Layout = "layout/default.html"
errorType := "500"
if len(data) > 0 {
errorType = data[0].(string)
}
this.TplName = "error/" + errorType + ".html"
this.Data["title"] = "system error"
this.Data["errorMessage"] = errorMessage
this.Render()
}
func (this *BaseController) JsonSuccess(message interface{}, data ...interface{}) {
this.jsonSuccess(message, data...)
}
func (this *BaseController) jsonSuccess(message interface{}, data ...interface{}) {
url := ""
sleep := 500
var _data interface{}
if len(data) > 0 {
_data = data[0]
}
if len(data) > 1 {
url = data[1].(string)
}
if len(data) > 2 {
sleep = data[2].(int)
}
this.Data["json"] = JSONResponse{
Code: 1,
Message: message,
Data: _data,
Redirect: map[string]interface{}{
"url": url,
"sleep": sleep,
},
}
//this.ServeJSON()
j, err := json.MarshalIndent(this.Data["json"], "", "\t")
if err != nil {
this.Abort(err.Error())
} else {
this.Abort(string(j))
}
}
func (this *BaseController) JsonError(message interface{}, data ...interface{}) {
this.jsonError(message, data...)
}
func (this *BaseController) jsonError(message interface{}, data ...interface{}) {
url := ""
sleep := 500
var _data interface{}
if len(data) > 0 {
_data = data[0]
}
if len(data) > 1 {
url = data[1].(string)
}
if len(data) > 2 {
sleep = data[2].(int)
}
this.Data["json"] = JSONResponse{
Code: 0,
Message: message,
Data: _data,
Redirect: map[string]interface{}{
"url": url,
"sleep": sleep,
},
}
j, err := json.MarshalIndent(this.Data["json"], "", " \t")
if err != nil {
this.Abort(err.Error())
} else {
this.Abort(string(j))
}
}
//获取用户IP地址
func (this *BaseController) getClientIp() string {
s := strings.Split(this.Ctx.Request.RemoteAddr, ":")
return s[0]
}
+66
View File
@@ -0,0 +1,66 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"strings"
"time"
)
type JSONResponse struct {
Code int `json:"code"`
Message interface{} `json:"message"`
Data interface{} `json:"data"`
Redirect map[string]interface{} `json:"redirect"`
}
type BaseAdminController struct {
BaseController
loginUser map[string]string
}
func (this *BaseAdminController) Prepare() {
if !this.isLogin() {
this.Redirect("/login/index.html", 302)
this.StopRun()
}
user := this.GetSession("author").(map[string]string)
this.Data["loginUser"] = user
this.loginUser = user
this.checkAccess()
this.Data["TimeNowYear"] = time.Now().Format("2006")
this.Layout = "layout/default.html"
}
func (this *BaseAdminController) checkAccess() {
controllerName, actionName := this.GetControllerAndAction()
controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10])
actionName = strings.ToLower(actionName)
if (controllerName == "main" && actionName == "index") || controllerName == "main" && actionName == "default" {
return
}
//检查权限
if "1" != this.loginUser["user_id"] {
privilege := models.Privilege{}
_, _, controllers, err := privilege.GetTypedPrivileges(this.loginUser["user_id"], "-1")
if err != nil {
this.jsonError(err.Error(), "")
}
found := false
for _, c := range controllers {
action := strings.ToLower(c["action"])
if strings.Contains(action, ".") {
action = action[:strings.LastIndex(action, ".")]
}
if controllerName == strings.ToLower(c["controller"]) && actionName == action {
found = true
break
}
}
if !found {
if this.IsAjax() {
this.jsonError("您无权限进行此操作")
} else {
this.viewError("您无权限进行此操作")
}
}
}
}
File diff suppressed because one or more lines are too long
+96
View File
@@ -0,0 +1,96 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"anytunnel/at-admin/app/utils"
"image/color"
"image/png"
"strings"
"github.com/afocus/captcha"
"github.com/astaxie/beego"
)
var (
cap = captcha.New()
)
func init() {
bs, _ := utils.NewEncrypt().Base64DecodeBytes(fontData)
cap.AddFontFromBytes(bs)
}
type LoginController struct {
BaseController
}
func (this *LoginController) Index() {
this.Layout = "layout/login.html"
this.TplName = "login/login.html"
this.Data["title"] = beego.AppConfig.String("sys.name") + "登录"
this.Render()
}
//login
func (this *LoginController) Login() {
if this.isLogin() {
return
}
userModel := models.User{}
name := strings.TrimSpace(this.GetString("username"))
password := strings.TrimSpace(this.GetString("password"))
captcha := strings.TrimSpace(this.GetString("captcha"))
captchaSession := this.GetSession("captcha")
if captchaSession == nil || captcha == "" || captchaSession != strings.ToLower(captcha) {
this.SetSession("captcha", "")
this.jsonError("验证码错误!")
}
this.SetSession("captcha", "")
user, err := userModel.GetUserByName(name)
if err != nil {
this.jsonError(err)
return
}
if len(user) == 0 {
this.jsonError("账号错误!")
}
encrypt := new(utils.Encrypt)
password = userModel.EncodePassword(password)
if user["password"] != password {
this.jsonError("账号或密码错误!")
}
//加载权限列表
//保存 session
this.SetSession("author", user)
//保存 cookie
identify := encrypt.Md5Encode(this.Ctx.Request.UserAgent() + this.getClientIp() + password)
passportValue := encrypt.Base64Encode(name + "@" + identify)
passport := beego.AppConfig.String("author.passport")
//fmt.Println("set cookie " + passportValue)
this.Ctx.SetCookie(passport, passportValue, 3600)
this.jsonSuccess("登录成功", "", "/main/index.html")
}
//logout
func (this *LoginController) Logout() {
passport := beego.AppConfig.String("author.passport")
this.Ctx.SetCookie(passport, "")
this.SetSession("author", "")
this.Redirect("/login/index.html", 302)
this.StopRun()
}
func (this *LoginController) Captcha() {
//bs, _ := ioutil.ReadFile("static/index/comic.ttf")
//ioutil.WriteFile("a.txt", utils.NewEncrypt().Base64EncodeBytes(bs), os.ModeAppend)
cap.SetSize(80, 28)
cap.SetDisturbance(captcha.NORMAL)
cap.SetFrontColor(color.RGBA{255, 255, 255, 255})
cap.SetBkgColor(color.RGBA{255, 100, 100, 100})
img, str := cap.Create(4, captcha.ALL)
this.SetSession("captcha", strings.ToLower(str))
png.Encode(this.Ctx.ResponseWriter, img)
}
+103
View File
@@ -0,0 +1,103 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"fmt"
"strconv"
"time"
"github.com/dustin/go-humanize"
"github.com/astaxie/beego"
)
type MainController struct {
BaseAdminController
}
func (this *MainController) Default() {
if this.Ctx.Input.IsGet() {
this.view("main/default")
} else {
data := map[string]interface{}{}
db := models.G.DB("base")
rs, err := db.Query(db.AR().Select("count(*) as total,cs_type").From("online").GroupBy("cs_type"))
if err != nil {
this.jsonError(err.Error())
}
d := rs.MapValues("cs_type", "total")
clients := "0"
servers := "0"
if v, ok := d["client"]; ok {
clients = v
}
if v, ok := d["server"]; ok {
servers = v
}
rs, err = db.Query(db.AR().Select("count(*) as total").From("cluster").Where(map[string]interface{}{
"update_time >": time.Now().Unix() - 60,
"is_disable": 0,
"is_delete": 0,
}))
if err != nil {
this.jsonError(err.Error())
}
clusters := rs.Value("total")
now := time.Now().Unix()
rs, err = db.Query(db.AR().Select("sum(bytes_total) as total").From("package").Where(map[string]interface{}{
"start_time <=": now,
"end_time >=": now,
}))
if err != nil {
this.jsonError(err.Error())
}
traffic := rs.Value("total")
if traffic == "" {
traffic = "0"
}
rs, err = db.Query(db.AR().Select("sum(bytes_left) as total").From("package").Where(map[string]interface{}{
"start_time <=": now,
"end_time >=": now,
}))
if err != nil {
this.jsonError(err.Error())
}
trafficLeft := rs.Value("total")
if trafficLeft == "" {
trafficLeft = "0"
}
data["online"] = map[string]interface{}{
"client": clients,
"server": servers,
"cluster": clusters,
}
_traffic, _ := strconv.Atoi(traffic)
_trafficLeft, _ := strconv.Atoi(trafficLeft)
_trafficUse := _traffic - _trafficLeft
data["traffic"] = map[string]interface{}{
"total": traffic,
"total_human": humanize.Bytes(uint64(_traffic)),
"total_left": _trafficLeft,
"total_left_human": humanize.Bytes(uint64(_trafficLeft)),
"total_use": _trafficUse,
"total_use_human": humanize.Bytes(uint64(_trafficUse)),
}
this.jsonSuccess("", data)
}
}
func (this *MainController) Index() {
this.Layout = "layout/main.html"
this.TplName = "main/index.html"
this.Data["title"] = fmt.Sprintf("%s - %s", beego.AppConfig.String("sys.name"), beego.AppConfig.String("sys.fullname"))
privilege := models.Privilege{}
navigators, menus, controllers, err := privilege.GetTypedPrivileges(this.loginUser["user_id"], "1")
if err != nil {
this.jsonError(err, "")
}
this.Data["navigators"] = navigators
this.Data["menus"] = menus
this.Data["controllers"] = controllers
this.Render()
}
+144
View File
@@ -0,0 +1,144 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"regexp"
"time"
"github.com/go-ozzo/ozzo-validation"
)
type PrivilegeController struct {
BaseAdminController
}
func (this *PrivilegeController) Delete() {
privilegeModel := models.Privilege{}
privilegeId := this.GetString("privilege_id")
privilege, err := privilegeModel.GetPrivilegeByPrivilegeId(privilegeId)
if err != nil {
this.jsonError(err)
}
hasSub, err := privilegeModel.HasSub(privilegeId)
if err != nil {
this.jsonError(err)
}
if privilege["type"] != "controller" && hasSub {
this.jsonError("子菜单非空,不能删除")
}
err = privilegeModel.Delete(privilegeId)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
}
func (this *PrivilegeController) Add() {
privilegeModel := models.Privilege{}
if this.Ctx.Input.IsPost() {
_, data := this.getPrivilegeFromPost(false)
_, err := privilegeModel.Insert(data)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
} else {
navigators, menus, _, err := privilegeModel.GetTypedPrivileges("1", "-1")
if err != nil {
this.jsonError(err, "")
}
this.Data["navigators"] = navigators
this.Data["menus"] = menus
this.Data["action"] = "add"
this.viewLayout("privilege/form", "form")
}
}
func (this *PrivilegeController) Edit() {
privilegeModel := models.Privilege{}
privilegeId := this.GetString("privilege_id")
if this.Ctx.Input.IsPost() {
_, data := this.getPrivilegeFromPost(true)
_, err := privilegeModel.Update(privilegeId, data)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
} else {
navigators, menus, _, err := privilegeModel.GetTypedPrivileges("1", "-1")
if err != nil {
this.jsonError(err, "")
}
privilege, err := privilegeModel.GetPrivilegeByPrivilegeId(privilegeId)
if err != nil {
this.jsonError(err, "")
}
if len(privilege) == 0 {
this.jsonError("权限不存在")
}
this.Data["privilege"] = privilege
this.Data["navigators"] = navigators
this.Data["menus"] = menus
this.Data["action"] = "edit"
this.viewLayout("privilege/form", "form")
}
}
func (this *PrivilegeController) getPrivilegeFromPost(isUpdate bool) (privilegeId string, privilege map[string]interface{}) {
parentId := 0
if this.GetString("type") == "menu" {
parentId, _ = this.GetInt("parent_n")
} else if this.GetString("type") == "controller" {
parentId, _ = this.GetInt("parent_m")
}
privilege = map[string]interface{}{
"name": this.GetString("name"),
"parent_id": parentId,
"type": this.GetString("type"),
"controller": this.GetString("controller"),
"action": this.GetString("action"),
"icon": this.GetString("icon"),
"is_display": this.GetString("is_display"),
"sequence": this.GetString("sequence"),
"target": this.GetString("target"),
}
err := validation.Errors{
"名称": validation.Validate(privilege["name"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("长度必须是1-15字符")),
"类型": validation.Validate(privilege["type"],
validation.Required.Error("不能为空"),
validation.In("navigator", "menu", "controller").Error("错误")),
"排序": validation.Validate(privilege["sequence"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^[1-9]{1}[0-9]*$")).Error("必须是大于0的数字")),
}.Filter()
if err != nil {
this.jsonError(err)
}
if isUpdate {
privilege["update_time"] = time.Now().Unix()
} else {
privilege["create_time"] = time.Now().Unix()
}
if this.GetString("type") != "controller" {
privilege["is_display"] = 1
}
return
}
// 权限列表
func (this *PrivilegeController) List() {
privilegeModel := new(models.Privilege)
navigators, menus, controllers, err := privilegeModel.GetTypedPrivileges("1", "-1")
if err != nil {
this.viewError(err.Error())
}
this.Data["navigators"] = navigators
this.Data["menus"] = menus
this.Data["controllers"] = controllers
this.view("privilege/list")
}
+102
View File
@@ -0,0 +1,102 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"regexp"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type RoleController struct {
BaseAdminController
}
func (this *RoleController) Delete() {
roleModel := models.Role{}
roleId := this.GetString("role_id")
hasUser, err := roleModel.HasUser(roleId)
if err != nil {
this.jsonError(err)
}
if hasUser {
this.jsonError("角色用户非空,不能删除")
}
err = roleModel.Delete(roleId)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
}
func (this *RoleController) Add() {
roleModel := models.Role{}
if this.Ctx.Input.IsPost() {
_, data := this.getRoleFromPost(false)
_, err := roleModel.Insert(data)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
} else {
this.Data["action"] = "add"
this.viewLayout("role/form", "form")
}
}
func (this *RoleController) Edit() {
roleModel := models.Role{}
roleId := this.GetString("role_id")
if this.Ctx.Input.IsPost() {
_, data := this.getRoleFromPost(true)
_, err := roleModel.Update(roleId, data)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
} else {
role, err := roleModel.GetRoleByRoleId(roleId)
if err != nil {
this.jsonError(err, "")
}
if len(role) == 0 {
this.jsonError("角色不存在")
}
this.Data["role"] = role
this.Data["action"] = "edit"
this.viewLayout("role/form", "form")
}
}
func (this *RoleController) List() {
roleModel := models.Role{}
roles, err := roleModel.GetAllRoles()
if err != nil {
this.viewError(err.Error())
}
this.Data["roles"] = roles
this.view("role/list")
}
func (this *RoleController) getRoleFromPost(isUpdate bool) (roleId string, role map[string]interface{}) {
role = map[string]interface{}{
"name": this.GetString("name"),
"is_delete": 0,
}
err := validation.Validate(role["name"],
validation.Required.Error("名称不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("名称长度必须是1-15字符"))
if err != nil {
this.jsonError(err.Error())
}
if isUpdate {
role["update_time"] = time.Now().Unix()
} else {
role["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,62 @@
package controllers
import "anytunnel/at-admin/app/models"
type Role_PrivilegeController struct {
BaseAdminController
}
//角色授权
func (this *Role_PrivilegeController) Add() {
roleId, err := this.GetInt("role_id", 0)
if roleId == 0 {
this.viewError("role_id is error! ")
}
rolePrivilegeModel := models.RolePrivilege{}
rolePrivileges, err := rolePrivilegeModel.GetRolePrivilegesByRoleId(roleId)
if err != nil {
this.viewError(err.Error())
}
privilegeModel := models.Privilege{}
navigators, menus, controllers, err := privilegeModel.GetTypedPrivileges("1", "-1")
if err != nil {
this.viewError(err.Error())
}
this.Data["navigators"] = navigators
this.Data["menus"] = menus
this.Data["controllers"] = controllers
this.Data["rolePrivileges"] = rolePrivileges
this.Data["privileges"] = rolePrivileges
this.Data["role_id"] = roleId
this.viewLayout("role/privilege", "form")
}
func (this *Role_PrivilegeController) Save() {
privilegeIds := this.GetStrings("privilege_id", []string{})
roleId, err := this.GetInt("role_id", 0)
if err != nil {
this.jsonError("角色授权失败:role_id error " + err.Error())
}
if roleId == 0 {
this.jsonError("角色授权失败:role_id is error!")
}
if len(privilegeIds) == 0 {
this.jsonError("角色授权失败:no select privilege!")
}
rolePrivilegeModel := models.RolePrivilege{}
res, err := rolePrivilegeModel.GrantRolePrivileges(roleId, privilegeIds)
if !res {
this.jsonError("角色授权失败:" + err.Error())
}
this.jsonSuccess("角色授权成功")
}
+11
View File
@@ -0,0 +1,11 @@
package controllers
type SystemController struct {
BaseAdminController
}
func (this *SystemController) Base() {
this.Layout = "layout/page.html"
this.TplName = "system/base.html"
this.Render()
}
+299
View File
@@ -0,0 +1,299 @@
package controllers
import (
"anytunnel/at-admin/app/models"
"anytunnel/at-admin/app/utils"
"regexp"
"strings"
"time"
"fmt"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
)
type UserController struct {
BaseAdminController
}
func (this *UserController) Forbidden() {
userModel := models.User{}
userId := this.GetString("user_id")
if userId == "1" {
this.jsonError("系统用户不能禁用")
}
err := userModel.Forbidden(userId)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
}
func (this *UserController) Review() {
userModel := models.User{}
userId := this.GetString("user_id")
if userId == "1" {
this.jsonError("系统用户不能操作")
}
err := userModel.Review(userId)
if err != nil {
this.jsonError(err)
}
this.jsonSuccess("")
}
func (this *UserController) ChangePassword() {
userModel := models.User{}
if this.Ctx.Input.IsPost() {
newpassword := this.GetString("password")
oldpassword := this.GetString("password_old")
errs := validation.Errors{
"旧密码": validation.Validate(oldpassword,
validation.Required.Error("不能为空")),
"新密码": validation.Validate(newpassword,
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^([0-9]+[a-zA-Z]+[_]*){1,16}$")).Error("必须同时包含数字和字母,且1-15字符")),
}
err := errs.Filter()
if err != nil {
this.jsonError(err)
}
err = userModel.ChangePassword(this.loginUser["user_id"], newpassword, oldpassword)
if err != nil {
this.jsonError("修改密码失败:" + err.Error())
}
this.jsonSuccess("")
} else {
this.viewLayout("user/changepassword", "form")
}
}
func (this *UserController) Add() {
userModel := models.User{}
roleModel := models.Role{}
userRoleModel := models.UserRole{}
if this.Ctx.Input.IsPost() {
_, data := this.getUserFromPost(false)
username := this.GetString("username")
roleIds := this.GetStrings("role_ids", []string{})
if len(roleIds) == 0 {
this.jsonError("没有选择角色")
}
HasUsername, err := userModel.HasUsername(username)
if err != nil {
this.jsonError(err)
}
if HasUsername {
this.jsonError("用户名已经存在")
}
userId, err := userModel.Insert(data)
if err != nil {
this.jsonError("添加用户失败:" + err.Error())
}
//添加用户与角色对应关系
_, err = userRoleModel.Insert(utils.NewConvert().IntToString(userId, 10), roleIds)
if err != nil {
this.jsonError("添加用户角色失败:" + err.Error())
}
this.jsonSuccess("")
} else {
roles := []map[string]string{}
allRoles, _ := roleModel.GetAllRoles()
for _, allRole := range allRoles {
role := allRole
role["is_default"] = "0"
roles = append(roles, role)
}
this.Data["action"] = "add"
this.Data["roles"] = roles
this.viewLayout("user/form", "form")
}
}
func (this *UserController) Edit() {
userModel := models.User{}
roleModel := models.Role{}
userRoleModel := models.UserRole{}
userId := this.GetString("user_id")
if this.Ctx.Input.IsPost() {
_, data := this.getUserFromPost(true)
username := this.GetString("username")
roleIds := this.GetStrings("role_ids", []string{})
if len(roleIds) == 0 {
this.jsonError("没有选择角色")
}
HasSameUsername, err := userModel.HasSameUsername(userId, username)
if err != nil {
this.jsonError(err)
}
if HasSameUsername {
this.jsonError("用户名已经存在")
}
_, err = userModel.Update(userId, data)
if err != nil {
this.jsonError("修改用户失败:" + err.Error())
}
//添加用户与角色对应关系
_, err = userRoleModel.Insert(userId, roleIds)
if err != nil {
this.jsonError("修改用户角色失败:" + err.Error())
}
this.jsonSuccess("")
} else {
roles := []map[string]string{}
user, err := userModel.GetUserByUserId(userId)
allRoles, _ := roleModel.GetAllRoles()
userRoles, _ := userRoleModel.GetUserRolesByUserId(userId)
for _, allRole := range allRoles {
role := allRole
if len(userRoles) == 0 {
role["is_default"] = "0"
} else {
for _, userRoles := range userRoles {
if allRole["role_id"] == userRoles["role_id"] {
role["is_default"] = "1"
break
}
role["is_default"] = "0"
}
}
roles = append(roles, role)
}
if err != nil {
this.jsonError(err, "")
}
if len(user) == 0 {
this.jsonError("用户不存在")
}
this.Data["user"] = user
this.Data["roles"] = roles
this.Data["action"] = "edit"
this.viewLayout("user/form", "form")
}
}
func (this *UserController) getUserFromPost(isUpdate bool) (userId string, user map[string]interface{}) {
userModel := models.User{}
user = map[string]interface{}{
"username": this.GetString("username"),
"given_name": this.GetString("given_name"),
"email": this.GetString("email"),
"mobile": this.GetString("mobile"),
}
errs := validation.Errors{
"手机号": validation.Validate(user["mobile"],
validation.Match(regexp.MustCompile("^1[3|4|5|7|8][0-9]{9}$")).Error("格式错误")),
"邮箱": validation.Validate(user["email"],
validation.Required.Error("不能为空"),
is.Email.Error("格式错误")),
"姓名": validation.Validate(user["given_name"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("长度必须是1-15字符")),
//"角色": validation.Validate(user["role_ids"]),
// validation.Required.Error("没有选择角色"),
}
if !isUpdate {
errs["用户名"] = validation.Validate(user["username"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^[0-9_a-zA-Z]{1,15}$")).Error("只能包含数字字母和下划线,且1-15字符"))
// errs["密码"] = validation.Validate(this.GetString("password"),
// validation.Required.Error("不能为空"),
// validation.Match(regexp.MustCompile("^([0-9]+[a-zA-Z]+[_]*){1,16}$")).Error("必须同时包含数字和字母,且1-15字符"))
}
err := errs.Filter()
if err != nil {
this.jsonError(err)
}
if isUpdate {
// if this.GetString("password") != "" {
// user["password"] = userModel.EncodePassword(this.GetString("password"))
// }
user["update_time"] = time.Now().Unix()
delete(user, "username")
} else {
user["is_forbidden"] = 0
user["password"] = userModel.EncodePassword(this.GetString("password"))
user["create_time"] = time.Now().Unix()
}
return
}
func (this *UserController) List() {
keyword := strings.Trim(this.GetString("keyword", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
userModel := models.User{}
var users = []map[string]string{}
var userCount = 0
if keyword == "" {
userCount, err = userModel.CountUsers()
users, err = userModel.GetUsersByLimit(limit, pageSize)
} else {
userCount, err = userModel.CountUsersByKeyword(keyword)
users, err = userModel.GetUsersByKeywordAndLimit(keyword, limit, pageSize)
}
if err != nil {
this.viewError(err.Error())
}
roleModel := models.Role{}
userRoles := map[string]string{}
//用户角色
for _, user := range users {
userId := user["user_id"]
var names = ""
roles, err := roleModel.GetRolesByUserId(userId)
if err != nil {
this.viewError(err.Error())
}
for _, role := range roles {
names += "," + role["name"]
}
userRoles[user["user_id"]] = strings.Replace(names, ",", "", 1)
}
this.Data["users"] = users
this.Data["userRoles"] = userRoles
this.Data["keyword"] = keyword
this.Data["page"] = utils.NewMisc().Page(userCount, page, pageSize, "/user/list?page={page}")
this.viewLayoutTitle("用户列表", "user/list", "form")
}
//个人资料
func (this *UserController) Profile() {
userModel := models.User{}
if this.Ctx.Input.IsPost() {
_, data := this.getUserFromPost(true)
fmt.Println(data)
userId := this.GetString("user_id")
_, err := userModel.Update(userId, data)
if err != nil {
this.jsonError("修改个人资料失败:" + err.Error())
}
this.jsonSuccess("")
} else {
user := this.GetSession("author").(map[string]string)
this.Data["user"] = user
this.viewLayout("user/profile", "form")
}
}
+44
View File
@@ -0,0 +1,44 @@
package models
import (
"fmt"
"os"
"github.com/astaxie/beego"
"github.com/snail007/go-activerecord/mysql"
)
var G *mysql.DBGroup
func init() {
host := beego.AppConfig.String("db_host")
port, _ := beego.AppConfig.Int("db_port")
user := beego.AppConfig.String("db_user")
pass := beego.AppConfig.String("db_pass")
dbname := beego.AppConfig.String("db_name")
dbTablePrefix := beego.AppConfig.String("db_table_prefix")
maxIdle, _ := beego.AppConfig.Int("db_conn_max_idle")
maxConn, _ := beego.AppConfig.Int("db_conn_max_connection")
G = mysql.NewDBGroup("default")
cfg := mysql.NewDBConfigWith(host, port, dbname, user, pass)
cfg.SetMaxIdleConns = maxIdle
cfg.SetMaxOpenConns = maxConn
cfg.TablePrefix = dbTablePrefix
cfg.TablePrefixSqlIdentifier = "__PREFIX__"
//add database anytunnel_system
err := G.Regist("default", cfg)
if err != nil {
beego.Error(fmt.Errorf("regist db error:%s,with config : %v", err, cfg))
os.Exit(100)
}
//add database anytunnel_base
cfg.Database = beego.AppConfig.String("db.base.name")
cfg.TablePrefix = beego.AppConfig.String("db.base.table_prefix")
err = G.Regist("base", cfg)
if err != nil {
beego.Error(fmt.Errorf("regist db error:%s,with config : %v", err, cfg))
os.Exit(100)
}
}
+127
View File
@@ -0,0 +1,127 @@
package models
import "github.com/snail007/go-activerecord/mysql"
type Privilege struct {
}
func (p *Privilege) GetTypedPrivileges(userId string, isDisplay string) (navigators, menus, controllers []map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
if userId != "1" {
roldIds := []string{}
rs, err = db.Query(db.AR().Select("role_id").From("user_role").Where(map[string]interface{}{
"user_id": userId,
"is_delete": 0,
}))
if err != nil {
return
}
roldIds = rs.Values("role_id")
privilegeIds := []string{}
rs, err = db.Query(db.AR().Select("privilege_id").From("role_privilege").Where(map[string]interface{}{
"role_id": roldIds,
"is_delete": 0,
}))
if err != nil {
return
}
privilegeIds = rs.Values("privilege_id")
rs, err = db.Query(db.AR().From("privilege").Where(map[string]interface{}{
"privilege_id": privilegeIds,
}).OrderBy("parent_id", "ASC").
OrderBy("sequence", "ASC"))
if err != nil {
return
}
} else {
rs, err = db.Query(db.AR().From("privilege").OrderBy("parent_id", "ASC").OrderBy("sequence", "ASC"))
if err != nil {
return
}
}
navigators = []map[string]string{}
menus = []map[string]string{}
controllers = []map[string]string{}
allPrivileges := rs.MapRows("privilege_id")
for _, row := range rs.Rows() {
if isDisplay != "-1" {
if row["is_display"] != isDisplay {
continue
}
}
switch row["type"] {
case "navigator":
navigators = append(navigators, row)
case "menu":
row["parent"] = allPrivileges[row["parent_id"]]["name"]
menus = append(menus, row)
case "controller":
row["parent"] = allPrivileges[row["parent_id"]]["name"]
controllers = append(controllers, row)
}
}
return
}
func (p *Privilege) GetPrivilegeByPrivilegeId(privilegeId string) (privilege map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("privilege").Where(map[string]interface{}{
"privilege_id": privilegeId,
}))
if err != nil {
return
}
privilege = rs.Row()
return
}
func (p *Privilege) HasSub(privilegeId string) (has bool, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("privilege").Where(map[string]interface{}{
"parent_id": privilegeId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Privilege) Delete(privilegeId string) (err error) {
db := G.DB()
_, err = db.Exec(db.AR().Delete("privilege", map[string]interface{}{
"privilege_id": privilegeId,
}))
if err != nil {
return
}
return
}
func (p *Privilege) Insert(privilege map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("privilege", privilege))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Privilege) Update(privilegeId string, privilege map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("privilege", privilege, map[string]interface{}{
"privilege_id": privilegeId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
+98
View File
@@ -0,0 +1,98 @@
package models
import "github.com/snail007/go-activerecord/mysql"
type Role struct {
}
func (p *Role) GetRoleByRoleId(roleId string) (role map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("role").Where(map[string]interface{}{
"role_id": roleId,
"is_delete": 0,
}))
if err != nil {
return
}
role = rs.Row()
return
}
func (p *Role) HasUser(roleId string) (has bool, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user_role").Where(map[string]interface{}{
"role_id": roleId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Role) Delete(roleId string) (err error) {
db := G.DB()
_, err = db.Exec(db.AR().Update("role", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
return
}
func (p *Role) Insert(role map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("role", role))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Role) Update(roleId string, role map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("role", role, map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//获取所有的角色
func (this *Role) GetAllRoles() (roles []map[string]string, err error) {
db := G.DB();
res, err := db.Query(db.AR().From("role").OrderBy("role_id", "ASC"));
if(err != nil) {
return
}
roles = res.Rows()
return
}
// 根据用户名查找所有的角色
func (this *Role) GetRolesByUserId (userId string) (roles []map[string]string, err error) {
userRoleModel := UserRole{}
userRoles, err := userRoleModel.GetUserRolesByUserId(userId)
if(err != nil) {
return
}
for _, userRole := range userRoles {
role, _ := this.GetRoleByRoleId(userRole["role_id"])
roles = append(roles, role)
}
return
}
+56
View File
@@ -0,0 +1,56 @@
package models
type RolePrivilege struct {
RolePrivilegeID uint `orm:"pk"` // role_privilege_id
RoleID uint // role_id
PrivilegeID uint // privilege_id
IsDelete int8 // is_delete
CreateTime uint // create_time
UpdateTime uint // update_time
}
//根据 role_id 获取权限
func (rolePrivilege *RolePrivilege) GetRolePrivilegesByRoleId(roleId int) (rolePrivileges []map[string]string, err error) {
db := G.DB()
res, err := db.Query(db.AR().From("role_privilege").Where(map[string]interface{}{
"role_id": roleId,
}))
if(err != nil) {
return
}
rolePrivileges = res.Rows()
return
}
//角色授权
func (rolePrivilege *RolePrivilege) GrantRolePrivileges(roleId int, privilegeIds []string) (res bool, err error) {
res = false
db := G.DB()
//先删除
_, err = db.Exec(db.AR().Delete("role_privilege", map[string]interface{}{
"role_id": roleId,
}))
if(err != nil) {
return
}
rolePrivileges := []map[string]interface{}{}
for _, privilegeId := range privilegeIds {
rolePrivilege := map[string]interface{}{
"role_id": roleId,
"privilege_id": privilegeId,
}
rolePrivileges = append(rolePrivileges, rolePrivilege)
}
//批量插入
_, err = db.Exec(db.AR().InsertBatch("role_privilege", rolePrivileges))
if err != nil {
return
}
res = true
return
}
+206
View File
@@ -0,0 +1,206 @@
package models
import (
"anytunnel/at-admin/app/utils"
"crypto/md5"
"encoding/hex"
"errors"
"strings"
"github.com/snail007/go-activerecord/mysql"
)
type User struct {
}
func (p *User) GetUserByUserId(userId string) (user map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
user = rs.Row()
return
}
func (p *User) HasSameUsername(userId, username string) (has bool, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"user_id <>": userId,
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *User) HasUsername(username string) (has bool, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *User) GetUserByName(username string) (user map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
user = rs.Row()
return
}
//禁用
func (p *User) Forbidden(userId string) (err error) {
db := G.DB()
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"is_forbidden": 1,
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
//恢复
func (p *User) Review(userId string) (err error) {
db := G.DB()
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"is_forbidden": 0,
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
func (p *User) Insert(user map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("user", user))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *User) Update(userId string, user map[string]interface{}) (id int64, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("user", user, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *User) ChangePassword(userId, newpassword, oldpassword string) (err error) {
db := G.DB()
user, err := p.GetUserByUserId(userId)
if user["password"] != p.EncodePassword(oldpassword) {
return errors.New("旧密码错误")
}
if err != nil {
return
}
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"password": p.EncodePassword(newpassword),
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
func (p *User) EncodePassword(password string) (passwordHash string) {
hasher := md5.New()
hasher.Write([]byte(password))
passwordHash = strings.ToLower(hex.EncodeToString(hasher.Sum(nil)))
return
}
//根据关键字分页获取用户
func (user *User) GetUsersByKeywordAndLimit(keyword string, limit int, number int) (users []map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username LIKE": "%" + keyword + "%",
}).Limit(limit, number))
if err != nil {
return
}
users = rs.Rows()
return
}
//分页获取用户
func (user *User) GetUsersByLimit(limit int, number int) (users []map[string]string, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Limit(limit, number))
if err != nil {
return
}
users = rs.Rows()
return
}
func (user *User) CountUsers() (count int, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("user"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (user *User) CountUsersByKeyword(keyword string) (count int, err error) {
db := G.DB()
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("user").
Where(map[string]interface{}{
"username LIKE": "%" + keyword + "%",
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+64
View File
@@ -0,0 +1,64 @@
package models
import (
"github.com/snail007/go-activerecord/mysql"
"time"
)
type UserRole struct {
UserRoleID uint `orm:"pk"` // user_role_id
UserID uint64 // user_id
RoleID uint // role_id
IsDelete int8 // is_delete
CreateTime uint // create_time
UpdateTime uint // update_time
}
func (this *UserRole) GetUserRolesByUserId(userId string) (userRoles []map[string]string, err error) {
db := G.DB();
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user_role").Where(map[string]interface{}{
"user_id": userId,
"is_delete": 0,
}))
if err != nil {
return
}
userRoles = rs.Rows()
return
}
// 插入 user_id 和 role_id 对应关系
func (this *UserRole) Insert(userId string, roleIds []string) (res bool, err error) {
res = false
db := G.DB()
//先删除
_, err = db.Exec(db.AR().Delete("user_role", map[string]interface{}{
"user_id": userId,
}))
if(err != nil) {
return
}
//添加
userRoles := []map[string]interface{}{}
for _, roleId := range roleIds {
userRole := map[string]interface{}{
"role_id" : roleId,
"user_id" : userId,
"is_delete" : 0,
"create_time": time.Now().Unix(),
"update_time": time.Now().Unix(),
}
userRoles = append(userRoles, userRole)
}
_, err = db.Exec(db.AR().InsertBatch("user_role", userRoles))
if err != nil {
return
}
res = true
return
}
@@ -0,0 +1,135 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type AreaController struct {
BaseController
}
func (this *AreaController) Delete() {
areaModel := models.Area{}
areaId := this.GetString("area_id")
err := areaModel.Delete(areaId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *AreaController) Add() {
areaModel := models.Area{}
if this.Ctx.Input.IsPost() {
_, data := this.getAreaFromPost(false)
_, err := areaModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["action"] = "add"
this.viewLayout("area/form", "form")
}
}
func (this *AreaController) Edit() {
areaModel := models.Area{}
areaId := this.GetString("area_id")
if this.Ctx.Input.IsPost() {
_, data := this.getAreaFromPost(true)
_, err := areaModel.Update(areaId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
area, err := areaModel.GetAreaByAreaId(areaId)
if err != nil {
this.JsonError(err, "")
}
if len(area) == 0 {
this.ViewError("Area不存在")
}
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["area"] = area
this.Data["action"] = "edit"
this.viewLayout("area/form", "form")
}
}
func (this *AreaController) List() {
column := strings.Trim(this.GetString("type", ""), " ")
keyword := strings.Trim(this.GetString("id", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
areaModel := models.Area{}
var areas = []map[string]string{}
var areaCount = 0
if keyword == "" {
areaCount, err = areaModel.CountAreas()
areas, err = areaModel.GetAreasByLimit(limit, pageSize)
} else {
areaCount, err = areaModel.CountAreasByID(column, keyword)
areas, err = areaModel.GetAreasByIDAndLimit(column, keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["areas"] = areas
this.Data["page"] = utils.NewMisc().Page(areaCount, page, pageSize, fmt.Sprintf("/web/area/list?page={page}&type=%s&id=%s", column, keyword))
this.viewLayoutTitle("用户列表", "area/list", "form")
}
func (this *AreaController) getAreaFromPost(isUpdate bool) (areaId string, area map[string]interface{}) {
area = map[string]interface{}{
"name": this.GetString("name"),
"cs_type": this.GetString("cs_type"),
"is_forbidden": this.GetString("is_forbidden"),
}
errs := validation.Errors{
"区域名称": validation.Validate(area["name"],
validation.Required.Error("不能为空")),
"类型": validation.Validate(area["cs_type"],
validation.Required.Error("不能为空"),
validation.In("server", "client").Error("错误"),
),
"访问控制": validation.Validate(area["is_forbidden"],
validation.Required.Error("不能为空"),
validation.In("0", "1").Error("错误"),
),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
if isUpdate {
area["update_time"] = time.Now().Unix()
} else {
area["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,24 @@
package controllers
import (
system "anytunnel/at-admin/app/controllers"
)
const moduleName = "web"
type BaseController struct {
system.BaseAdminController
}
func (this *BaseController) viewLayoutTitle(title, viewName, layout string) {
this.ViewLayoutTitle(moduleName, title, viewName, layout)
}
func (this *BaseController) viewLayout(viewName, layout string) {
this.ViewLayout(moduleName, viewName, layout)
}
func (this *BaseController) view(viewName string) {
this.View(moduleName, viewName)
}
func (this *BaseController) viewTitle(title, viewName string) {
this.ViewTitle(moduleName, title, viewName)
}
@@ -0,0 +1,148 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"regexp"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type ClientController struct {
BaseController
}
func (this *ClientController) Reset() {
clientModel := models.Client{}
clientId := this.GetString("client_id")
err := clientModel.Offline(clientId)
if err != nil {
this.JsonError(err)
}
err = clientModel.Reset(clientId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ClientController) Delete() {
clientModel := models.Client{}
clientId := this.GetString("client_id")
hasTunnel, err := clientModel.HasTunnelRef(clientId)
if err != nil {
this.JsonError(err)
}
if hasTunnel {
this.JsonError("Tunnel引用,不能删除")
}
err = clientModel.Delete(clientId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ClientController) Add() {
clientModel := models.Client{}
if this.Ctx.Input.IsPost() {
_, data := this.getClientFromPost(false)
_, err := clientModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
this.Data["action"] = "add"
this.viewLayout("client/form", "form")
}
}
func (this *ClientController) Edit() {
clientModel := models.Client{}
clientId := this.GetString("client_id")
if this.Ctx.Input.IsPost() {
_, data := this.getClientFromPost(true)
_, err := clientModel.Update(clientId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
client, err := clientModel.GetClientByClientId(clientId)
if err != nil {
this.JsonError(err, "")
}
if len(client) == 0 {
this.ViewError("Client不存在")
}
this.Data["client"] = client
this.Data["action"] = "edit"
this.viewLayout("client/form", "form")
}
}
func (this *ClientController) List() {
userID := strings.Trim(this.GetString("user_id"), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
clientModel := models.Client{}
var clients = []map[string]string{}
var clientCount = 0
if userID == "" {
clientCount, err = clientModel.CountClients()
clients, err = clientModel.GetClientsByLimit(limit, pageSize)
} else {
clientCount, err = clientModel.CountClientsByUserID(userID)
clients, err = clientModel.GetClientsByUserIDAndLimit(userID, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["userID"] = userID
this.Data["clients"] = clients
this.Data["page"] = utils.NewMisc().Page(clientCount, page, pageSize, "/web/client/list?page={page}&user_id="+userID)
this.viewLayoutTitle("Client列表", "client/list", "form")
}
func (this *ClientController) getClientFromPost(isUpdate bool) (clientId string, client map[string]interface{}) {
port, _ := this.GetInt("local_port")
client = map[string]interface{}{
"name": this.GetString("name"),
"local_host": this.GetString("local_host"),
"local_port": port,
"user_id": 0,
"is_delete": 0,
}
errs := validation.Errors{
"名称": validation.Validate(client["name"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("名称长度必须是1-15字符")),
"本地网络Host": validation.Validate(client["local_host"],
validation.Required.Error("不能为空")),
"本地网络端口": validation.Validate(client["local_port"],
validation.Required.Error("不能为空"),
validation.Min(1).Error("最小值1"),
validation.Max(65535).Error("最大值65535")),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
if isUpdate {
client["update_time"] = time.Now().Unix()
} else {
client["token"] = utils.NewMisc().RandString(32)
client["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,202 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
common "anytunnel/at-common"
"encoding/json"
"fmt"
"regexp"
"time"
"github.com/astaxie/beego"
validation "github.com/go-ozzo/ozzo-validation"
is "github.com/go-ozzo/ozzo-validation/is"
)
type ClusterController struct {
BaseController
}
func (this *ClusterController) Statistic() {
clusterID := this.GetString("cluster_id")
if this.Ctx.Input.IsGet() {
this.Data["clusterID"] = clusterID
this.viewLayout("cluster/statistic", "default")
} else {
clusterModel := models.Cluster{}
cluster, err := clusterModel.GetClusterByClusterId(clusterID)
if err != nil {
this.JsonError(err)
}
clusterIP := cluster["ip"]
url := fmt.Sprintf("https://%s:%s/traffic/count", clusterIP, beego.AppConfig.String("cluster.api.port"))
body, _, err := common.HttpGet(url)
if err != nil {
this.JsonError(err)
}
status := map[string]interface{}{}
err = json.Unmarshal(body, &status)
if err != nil {
this.JsonError(err)
}
_data, ok := status["data"]
if !ok {
this.JsonError("no data")
}
data := _data.(map[string]interface{})
rs, err := models.DB.Query(models.DB.AR().From("cluster").Where(map[string]interface{}{
"cluster_id": clusterID,
}))
if err != nil {
this.JsonError(err)
}
if rs.Len() == 0 {
this.JsonError("no cluster")
}
data["cluster"] = rs.Row()
status["data"] = data
bytes, _ := json.Marshal(status)
this.Ctx.WriteString(string(bytes))
}
}
func (this *ClusterController) Forbidden() {
clusterModel := models.Cluster{}
clusterId := this.GetString("cluster_id")
err := clusterModel.Forbidden(clusterId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ClusterController) Review() {
clusterModel := models.Cluster{}
clusterId := this.GetString("cluster_id")
err := clusterModel.Review(clusterId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ClusterController) Delete() {
clusterModel := models.Cluster{}
clusterId := this.GetString("cluster_id")
HasTunnel, err := clusterModel.HasTunnel(clusterId)
if err != nil {
this.JsonError(err)
}
if HasTunnel {
this.JsonError("Tunnel引用非空,不能删除")
}
err = clusterModel.Delete(clusterId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ClusterController) Add() {
clusterModel := models.Cluster{}
if this.Ctx.Input.IsPost() {
_, data := this.getClusterFromPost(false)
_, err := clusterModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["action"] = "add"
this.viewLayout("cluster/form", "form")
}
}
func (this *ClusterController) Edit() {
clusterModel := models.Cluster{}
clusterId := this.GetString("cluster_id")
if this.Ctx.Input.IsPost() {
_, data := this.getClusterFromPost(true)
_, err := clusterModel.Update(clusterId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
cluster, err := clusterModel.GetClusterByClusterId(clusterId)
if err != nil {
this.JsonError(err, "")
}
if len(cluster) == 0 {
this.ViewError("Cluster不存在")
}
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["cluster"] = cluster
this.Data["action"] = "edit"
this.viewLayout("cluster/form", "form")
}
}
func (this *ClusterController) List() {
clusterModel := models.Cluster{}
clusters, err := clusterModel.GetAllClusters()
if err != nil {
this.ViewError(err.Error())
}
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
r := map[string]string{
"region_id": "0",
}
regions = append(regions, r)
this.Data["regions"] = regions
this.Data["clusters"] = clusters
this.view("cluster/list")
}
func (this *ClusterController) getClusterFromPost(isUpdate bool) (clusterId string, cluster map[string]interface{}) {
cluster = map[string]interface{}{
"name": this.GetString("name"),
"ip": this.GetString("ip"),
"region_id": this.GetString("region_id"),
}
errs := validation.Errors{
"名称": validation.Validate(cluster["name"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("长度必须是1-15字符")),
"IP": validation.Validate(cluster["ip"],
validation.Required.Error("不能为空"),
is.IPv4),
"区域": validation.Validate(cluster["region_id"],
validation.Match(regexp.MustCompile("^[1-9][0-9]*$")).Error("格式错误")),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
if isUpdate {
cluster["update_time"] = time.Now().Unix()
} else {
cluster["is_disable"] = 1
cluster["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,50 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"strings"
)
type ConnController struct {
BaseController
}
func (this *ConnController) List() {
orderBy := strings.Trim(this.GetString("orderby"), " ")
if orderBy == "" {
orderBy = "count"
}
col := strings.Trim(this.GetString("col"), " ")
keyword := strings.Trim(this.GetString("keyword"), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
connModel := models.Conn{}
var conns = []map[string]string{}
var connCount = 0
if keyword == "" {
connCount, err = connModel.CountConns()
conns, err = connModel.GetConnsByLimit(limit, pageSize, orderBy)
} else {
connCount, err = connModel.CountConnsByUserID(col, keyword)
conns, err = connModel.GetConnsByUserIDAndLimit(col, keyword, orderBy, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["conns"] = conns
this.Data["col"] = col
this.Data["keyword"] = keyword
this.Data["orderby"] = orderBy
this.Data["p"] = page
this.Data["page"] = utils.NewMisc().Page(connCount, page, pageSize, fmt.Sprintf("/web/conn/list?page={page}&col=%s&keyword=%s&orderby=%s", col, keyword, orderBy))
this.viewLayoutTitle("隧道连接数列表", "conn/list", "form")
}
@@ -0,0 +1,137 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
)
type IpListController struct {
BaseController
}
func (this *IpListController) Delete() {
ip_listModel := models.IpList{}
ip_listId := this.GetString("ip_list_id")
err := ip_listModel.Delete(ip_listId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *IpListController) Add() {
ip_listModel := models.IpList{}
if this.Ctx.Input.IsPost() {
_, data := this.getIpListFromPost(false)
_, err := ip_listModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["action"] = "add"
this.viewLayout("ip_list/form", "form")
}
}
func (this *IpListController) Edit() {
ip_listModel := models.IpList{}
ip_listId := this.GetString("ip_list_id")
if this.Ctx.Input.IsPost() {
_, data := this.getIpListFromPost(true)
_, err := ip_listModel.Update(ip_listId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
ip_list, err := ip_listModel.GetIpListByIpListId(ip_listId)
if err != nil {
this.JsonError(err, "")
}
if len(ip_list) == 0 {
this.ViewError("IpList不存在")
}
regionModel := models.Region{}
regions, err := regionModel.GetSubRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["ip_list"] = ip_list
this.Data["action"] = "edit"
this.viewLayout("ip_list/form", "form")
}
}
func (this *IpListController) List() {
column := strings.Trim(this.GetString("type", ""), " ")
keyword := strings.Trim(this.GetString("id", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
ip_listModel := models.IpList{}
var ip_lists = []map[string]string{}
var ip_listCount = 0
if keyword == "" {
ip_listCount, err = ip_listModel.CountIpLists()
ip_lists, err = ip_listModel.GetIpListsByLimit(limit, pageSize)
} else {
ip_listCount, err = ip_listModel.CountIpListsByID(column, keyword)
ip_lists, err = ip_listModel.GetIpListsByIDAndLimit(column, keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["ip_lists"] = ip_lists
this.Data["page"] = utils.NewMisc().Page(ip_listCount, page, pageSize, fmt.Sprintf("/web/ip_list/list?page={page}&type=%s&id=%s", column, keyword))
this.viewLayoutTitle("用户列表", "ip_list/list", "form")
}
func (this *IpListController) getIpListFromPost(isUpdate bool) (ip_listId string, ip_list map[string]interface{}) {
ip_list = map[string]interface{}{
"ip": this.GetString("ip"),
"cs_type": this.GetString("cs_type"),
"is_forbidden": this.GetString("is_forbidden"),
}
errs := validation.Errors{
"IP": validation.Validate(ip_list["ip"],
validation.Required.Error("不能为空"),
is.IPv4.Error("格式错误")),
"类型": validation.Validate(ip_list["cs_type"],
validation.Required.Error("不能为空"),
validation.In("server", "client").Error("错误"),
),
"访问控制": validation.Validate(ip_list["is_forbidden"],
validation.Required.Error("不能为空"),
validation.In("0", "1").Error("错误"),
),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
if isUpdate {
ip_list["update_time"] = time.Now().Unix()
} else {
ip_list["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,44 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"strings"
)
type OnlineController struct {
BaseController
}
func (this *OnlineController) List() {
cs := strings.Trim(this.GetString("cs", ""), " ")
column := strings.Trim(this.GetString("type", ""), " ")
keyword := strings.Trim(this.GetString("keyword", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
onlineModel := models.Online{}
var onlines = []map[string]string{}
var onlineCount = 0
if keyword == "" {
onlineCount, err = onlineModel.CountOnlines(cs)
onlines, err = onlineModel.GetOnlinesByLimit(cs, limit, pageSize)
} else {
onlineCount, err = onlineModel.CountOnlinesByID(cs, column, keyword)
onlines, err = onlineModel.GetOnlinesByIDAndLimit(cs, column, keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["cs"] = cs
this.Data["onlines"] = onlines
this.Data["page"] = utils.NewMisc().Page(onlineCount, page, pageSize, fmt.Sprintf("/web/online/list?page={page}&type=%s&keyword=%s&cs=%s", column, keyword, cs))
this.viewLayoutTitle(cs+"列表", "online/list", "form")
}
@@ -0,0 +1,135 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"regexp"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type PackageController struct {
BaseController
}
func (this *PackageController) List() {
column := "user_id"
keyword := strings.Trim(this.GetString("keyword", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
packageModel := models.PackageModel{}
var packages = []map[string]string{}
var packageCount = 0
if keyword == "" {
packageCount, err = packageModel.CountPackages()
packages, err = packageModel.GetPackagesByLimit(limit, pageSize)
} else {
packageCount, err = packageModel.CountPackagesByID(column, keyword)
packages, err = packageModel.GetPackagesByIDAndLimit(column, keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["packages"] = packages
this.Data["page"] = utils.NewMisc().Page(packageCount, page, pageSize, fmt.Sprintf("/web/package/list?page={page}&type=%s&keyword=%s", column, keyword))
this.viewLayoutTitle("流量列表", "package/list", "form")
}
func (this *PackageController) Add() {
packageModel := models.PackageModel{}
if this.Ctx.Input.IsPost() {
_, data := this.getPackageFromPost(false)
_, err := packageModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
this.Data["action"] = "add"
this.viewLayout("package/form", "form")
}
}
func (this *PackageController) Edit() {
packageModel := models.PackageModel{}
packageId := this.GetString("package_id")
if this.Ctx.Input.IsPost() {
_, data := this.getPackageFromPost(true)
_, err := packageModel.Update(packageId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
_package, err := packageModel.GetPackageByPackageId(packageId)
if err != nil {
this.JsonError(err, "")
}
if len(_package) == 0 {
this.ViewError("Package不存在")
}
this.Data["package"] = _package
this.Data["action"] = "edit"
this.viewLayout("package/form", "form")
}
}
func (this *PackageController) getPackageFromPost(isUpdate bool) (packageId string, _package map[string]interface{}) {
_package = map[string]interface{}{
"comment": this.GetString("comment"),
"bytes_left": this.GetString("bytes_left"),
"user_id": this.GetString("user_id"),
"start_time": this.GetString("start_time"),
"end_time": this.GetString("end_time"),
}
errs := validation.Errors{
"来源": validation.Validate(_package["comment"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,10}$")).Error("长度必须是1-10字符")),
"字节数": validation.Validate(_package["bytes_left"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^[0-9]+$")).Error("必须是数字")),
"用户ID": validation.Validate(_package["user_id"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^[1-9][0-9]*$")).Error("必须是数字")),
"生效时间": validation.Validate(_package["start_time"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)).Error("格式错误")),
"过期时间": validation.Validate(_package["end_time"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`)).Error("格式错误")),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
userModel := models.User{}
user, err := userModel.GetUserByUserId(_package["user_id"].(string))
if err != nil {
this.JsonError(err)
}
if len(user) == 0 {
this.JsonError("用户不存在")
}
_start, _ := time.ParseInLocation("2006-01-02 15:04:05", _package["start_time"].(string)+" 00:00:00", time.Local)
_end, _ := time.ParseInLocation("2006-01-02 15:04:05", _package["end_time"].(string)+" 23:59:59", time.Local)
_package["start_time"] = _start.Unix()
_package["end_time"] = _end.Unix()
if isUpdate {
_package["update_time"] = time.Now().Unix()
} else {
_package["bytes_total"] = _package["bytes_left"]
_package["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,124 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"regexp"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type RegionController struct {
BaseController
}
func (this *RegionController) Delete() {
regionModel := models.Region{}
regionId := this.GetString("region_id")
hasRole, err := regionModel.HasRole(regionId)
if err != nil {
this.JsonError(err)
}
if hasRole {
this.JsonError("角色引用非空,不能删除")
}
HasCluster, err := regionModel.HasCluster(regionId)
if err != nil {
this.JsonError(err)
}
if HasCluster {
this.JsonError("Cluster引用非空,不能删除")
}
HasSubRegion, err := regionModel.HasSubRegion(regionId)
if err != nil {
this.JsonError(err)
}
if HasSubRegion {
this.JsonError("子区域非空,不能删除")
}
err = regionModel.Delete(regionId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *RegionController) Add() {
regionModel := models.Region{}
if this.Ctx.Input.IsPost() {
_, data := this.getRegionFromPost(false)
_, err := regionModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
regions, err := regionModel.GetTopRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["action"] = "add"
this.viewLayout("region/form", "form")
}
}
func (this *RegionController) Edit() {
regionModel := models.Region{}
regionId := this.GetString("region_id")
if this.Ctx.Input.IsPost() {
_, data := this.getRegionFromPost(true)
_, err := regionModel.Update(regionId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
region, err := regionModel.GetRegionByRegionId(regionId)
if err != nil {
this.JsonError(err, "")
}
if len(region) == 0 {
this.ViewError("区域不存在")
}
regions, err := regionModel.GetTopRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.Data["region"] = region
this.Data["action"] = "edit"
this.viewLayout("region/form", "form")
}
}
func (this *RegionController) List() {
regionModel := models.Region{}
regions, err := regionModel.GetAllRegions()
if err != nil {
this.ViewError(err.Error())
}
this.Data["regions"] = regions
this.view("region/list")
}
func (this *RegionController) getRegionFromPost(isUpdate bool) (regionId string, region map[string]interface{}) {
region = map[string]interface{}{
"name": this.GetString("name"),
"parent_id": this.GetString("parent_id"),
"is_delete": 0,
}
err := validation.Validate(region["name"],
validation.Required.Error("名称不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("名称长度必须是1-15字符"))
if err != nil {
this.JsonError(err.Error())
}
if isUpdate {
region["update_time"] = time.Now().Unix()
} else {
region["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,155 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"regexp"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type RoleController struct {
BaseController
}
func (this *RoleController) Delete() {
roleModel := models.Role{}
roleId := this.GetString("role_id")
// if roleId == "0" {
// this.JsonError("系统角色不能删除")
// }
if roleId == "1" {
this.JsonError("默认角色不能删除")
}
hasUser, err := roleModel.HasUser(roleId)
if err != nil {
this.JsonError(err)
}
if hasUser {
this.JsonError("角色用户非空,不能删除")
}
err = roleModel.SetRegions(roleId, []string{})
if err != nil {
this.JsonError(err)
}
err = roleModel.Delete(roleId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *RoleController) Add() {
roleModel := models.Role{}
if this.Ctx.Input.IsPost() {
_, data := this.getRoleFromPost(false)
_, err := roleModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
this.Data["action"] = "add"
this.viewLayout("role/form", "form")
}
}
func (this *RoleController) Edit() {
roleModel := models.Role{}
roleId := this.GetString("role_id")
if this.Ctx.Input.IsPost() {
_, data := this.getRoleFromPost(true)
_, err := roleModel.Update(roleId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
role, err := roleModel.GetRoleByRoleId(roleId)
if err != nil {
this.JsonError(err, "")
}
if len(role) == 0 {
this.ViewError("角色不存在")
}
this.Data["tunnel_mode_arr"] = strings.Split(role["tunnel_mode"], ",")
this.Data["role"] = role
this.Data["action"] = "edit"
this.viewLayout("role/form", "form")
}
}
func (this *RoleController) List() {
roleModel := models.Role{}
roles, err := roleModel.GetAllRoles()
if err != nil {
this.ViewError(err.Error())
}
this.Data["roles"] = roles
this.view("role/list")
}
func (this *RoleController) Regions() {
roleModel := models.Role{}
roleID := this.GetString("role_id")
if this.Ctx.Input.IsPost() {
regionIds := this.GetStrings("region-ids")
err := roleModel.SetRegions(roleID, regionIds)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
regionModel := models.Region{}
regions, err := regionModel.GetAllRegions()
if err != nil {
this.ViewError(err.Error())
}
regionIds, err := roleModel.GetRoleRegionIds(roleID)
if err != nil {
this.ViewError(err.Error())
}
this.Data["regionIds"] = regionIds
this.Data["roleId"] = this.GetString("role_id")
this.Data["regions"] = regions
this.view("role/region")
}
}
func (this *RoleController) getRoleFromPost(isUpdate bool) (roleId string, role map[string]interface{}) {
role = map[string]interface{}{
"name": this.GetString("name"),
"server_area": this.GetString("server_area"),
"client_area": this.GetString("client_area"),
"bandwidth": this.GetString("bandwidth"),
"is_delete": 0,
}
errs := validation.Errors{
"名称": validation.Validate(role["name"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("长度必须是1-15字符")),
"Server区域": validation.Validate(role["server_area"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^(china|foreign|all)$")).Error("错误")),
"Client区域": validation.Validate(role["client_area"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^(china|foreign|all)$")).Error("错误")),
"带宽": validation.Validate(role["bandwidth"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^(0|([1-9][0-9]*))$")).Error("必须是大于等于0的整数")),
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
role["tunnel_mode"] = strings.Join(this.GetStrings("tunnel_mode"), ",")
if isUpdate {
role["update_time"] = time.Now().Unix()
} else {
role["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,137 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"regexp"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
)
type ServerController struct {
BaseController
}
func (this *ServerController) Reset() {
serverModel := models.Server{}
serverId := this.GetString("server_id")
err := serverModel.Offline(serverId)
if err != nil {
this.JsonError(err)
}
err = serverModel.Reset(serverId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ServerController) Delete() {
serverModel := models.Server{}
serverId := this.GetString("server_id")
hasTunnel, err := serverModel.HasTunnelRef(serverId)
if err != nil {
this.JsonError(err)
}
if hasTunnel {
this.JsonError("Tunnel引用,不能删除")
}
err = serverModel.Delete(serverId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *ServerController) Add() {
serverModel := models.Server{}
if this.Ctx.Input.IsPost() {
_, data := this.getServerFromPost(false)
_, err := serverModel.Insert(data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
this.Data["action"] = "add"
this.viewLayout("server/form", "form")
}
}
func (this *ServerController) Edit() {
serverModel := models.Server{}
serverId := this.GetString("server_id")
if this.Ctx.Input.IsPost() {
_, data := this.getServerFromPost(true)
_, err := serverModel.Update(serverId, data)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
} else {
server, err := serverModel.GetServerByServerId(serverId)
if err != nil {
this.JsonError(err, "")
}
if len(server) == 0 {
this.ViewError("Server不存在")
}
this.Data["server"] = server
this.Data["action"] = "edit"
this.viewLayout("server/form", "form")
}
}
func (this *ServerController) List() {
userID := strings.Trim(this.GetString("user_id"), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
serverModel := models.Server{}
var servers = []map[string]string{}
var serverCount = 0
if userID == "" {
serverCount, err = serverModel.CountServers()
servers, err = serverModel.GetServersByLimit(limit, pageSize)
} else {
serverCount, err = serverModel.CountServersByUserID(userID)
servers, err = serverModel.GetServersByUserIDAndLimit(userID, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["userID"] = userID
this.Data["servers"] = servers
this.Data["page"] = utils.NewMisc().Page(serverCount, page, pageSize, "/web/server/list?page={page}&user_id="+userID)
this.viewLayoutTitle("Server列表", "server/list", "form")
}
func (this *ServerController) getServerFromPost(isUpdate bool) (serverId string, server map[string]interface{}) {
server = map[string]interface{}{
"name": this.GetString("name"),
// "user_id": this.GetString("user_id"),
"user_id": 0,
"is_delete": 0,
}
err := validation.Validate(server["name"],
validation.Required.Error("名称不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("名称长度必须是1-15字符"))
if err != nil {
this.JsonError(err.Error())
}
if isUpdate {
server["update_time"] = time.Now().Unix()
} else {
server["token"] = utils.NewMisc().RandString(32)
server["create_time"] = time.Now().Unix()
}
return
}
@@ -0,0 +1,42 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"fmt"
"strings"
)
type TunnelController struct {
BaseController
}
func (this *TunnelController) List() {
column := strings.Trim(this.GetString("type", ""), " ")
keyword := strings.Trim(this.GetString("id", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
tunnelModel := models.Tunnel{}
var tunnels = []map[string]string{}
var tunnelCount = 0
if keyword == "" {
tunnelCount, err = tunnelModel.CountTunnels()
tunnels, err = tunnelModel.GetTunnelsByLimit(limit, pageSize)
} else {
tunnelCount, err = tunnelModel.CountTunnelsByID(column, keyword)
tunnels, err = tunnelModel.GetTunnelsByIDAndLimit(column, keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
this.Data["tunnels"] = tunnels
this.Data["page"] = utils.NewMisc().Page(tunnelCount, page, pageSize, fmt.Sprintf("/web/tunnel/list?page={page}&type=%s&id=%s", column, keyword))
this.viewLayoutTitle("用户列表", "tunnel/list", "form")
}
@@ -0,0 +1,275 @@
package controllers
import (
"anytunnel/at-admin/app/modules/web/models"
"anytunnel/at-admin/app/utils"
"regexp"
"strings"
"time"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
)
type UserController struct {
BaseController
}
func (this *UserController) Forbidden() {
userModel := models.User{}
userId := this.GetString("user_id")
// if userId == "0" {
// this.JsonError("系统用户不能禁用")
// }
err := userModel.Forbidden(userId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
func (this *UserController) Review() {
userModel := models.User{}
userId := this.GetString("user_id")
// if userId == "0" {
// this.JsonError("系统用户不能操作")
// }
err := userModel.Review(userId)
if err != nil {
this.JsonError(err)
}
this.JsonSuccess("")
}
// func (this *UserController) ChangePassword() {
// userModel := models.User{}
// if this.Ctx.Input.IsPost() {
// newpassword := this.GetString("password")
// oldpassword := this.GetString("password_old")
// errs := validation.Errors{
// "旧密码": validation.Validate(oldpassword,
// validation.Required.Error("不能为空")),
// "新密码": validation.Validate(newpassword,
// validation.Required.Error("不能为空"),
// validation.Match(regexp.MustCompile("^([0-9]+[a-zA-Z]+[_]*){1,16}$")).Error("必须同时包含数字和字母,且1-15字符")),
// }
// err := errs.Filter()
// if err != nil {
// this.JsonError(err)
// }
// err = userModel.ChangePassword(this.loginUser["user_id"], newpassword, oldpassword)
// if err != nil {
// this.JsonError("修改密码失败:" + err.Error())
// }
// this.JsonSuccess("")
// } else {
// this.viewLayout("user/changepassword", "form")
// }
// }
func (this *UserController) Add() {
return
userModel := models.User{}
roleModel := models.Role{}
userRoleModel := models.UserRole{}
if this.Ctx.Input.IsPost() {
_, data := this.getUserFromPost(false)
username := this.GetString("username")
roleIds := this.GetStrings("role_ids", []string{})
if len(roleIds) == 0 {
this.JsonError("没有选择角色")
}
HasUsername, err := userModel.HasUsername(username)
if err != nil {
this.JsonError(err)
}
if HasUsername {
this.JsonError("用户名已经存在")
}
userId, err := userModel.Insert(data)
if err != nil {
this.JsonError("添加用户失败:" + err.Error())
}
//添加用户与角色对应关系
_, err = userRoleModel.Insert(utils.NewConvert().IntToString(userId, 10), roleIds)
if err != nil {
this.JsonError("添加用户角色失败:" + err.Error())
}
this.JsonSuccess("")
} else {
roles := []map[string]string{}
allRoles, _ := roleModel.GetAllRoles()
for _, allRole := range allRoles {
role := allRole
role["is_default"] = "0"
roles = append(roles, role)
}
this.Data["action"] = "add"
this.Data["roles"] = roles
this.viewLayout("user/form", "form")
}
}
func (this *UserController) Edit() {
userModel := models.User{}
roleModel := models.Role{}
userRoleModel := models.UserRole{}
userId := this.GetString("user_id")
if this.Ctx.Input.IsPost() {
// _, data := this.getUserFromPost(true)
// username := this.GetString("username")
roleIds := this.GetStrings("role_ids", []string{})
if len(roleIds) == 0 {
this.JsonError("没有选择角色")
}
// HasSameUsername, err := userModel.HasSameUsername(userId, username)
// if err != nil {
// this.JsonError(err)
// }
// if HasSameUsername {
// this.JsonError("用户名已经存在")
// }
// _, err = userModel.Update(userId, data)
// if err != nil {
// this.JsonError("修改用户失败:" + err.Error())
// }
//添加用户与角色对应关系
_, err := userRoleModel.Insert(userId, roleIds)
if err != nil {
this.JsonError("修改用户角色失败:" + err.Error())
}
this.JsonSuccess("")
} else {
roles := []map[string]string{}
user, err := userModel.GetUserByUserId(userId)
allRoles, _ := roleModel.GetAllRoles()
userRoles, _ := userRoleModel.GetUserRolesByUserId(userId)
for _, allRole := range allRoles {
role := allRole
if len(userRoles) == 0 {
role["is_default"] = "0"
} else {
for _, userRoles := range userRoles {
if allRole["role_id"] == userRoles["role_id"] {
role["is_default"] = "1"
break
}
role["is_default"] = "0"
}
}
roles = append(roles, role)
}
if err != nil {
this.JsonError(err, "")
}
if len(user) == 0 {
this.JsonError("用户不存在")
}
this.Data["user"] = user
this.Data["roles"] = roles
this.Data["action"] = "edit"
this.viewLayout("user/form", "form")
}
}
func (this *UserController) getUserFromPost(isUpdate bool) (userId string, user map[string]interface{}) {
userModel := models.User{}
user = map[string]interface{}{
"username": this.GetString("username"),
"nickname": this.GetString("nickname"),
"email": this.GetString("email"),
}
errs := validation.Errors{
"邮箱": validation.Validate(user["email"],
validation.Required.Error("不能为空"),
is.Email.Error("格式错误")),
"昵称": validation.Validate(user["nickname"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^.{1,15}$")).Error("长度必须是1-15字符")),
//"角色": validation.Validate(user["role_ids"]),
// validation.Required.Error("没有选择角色"),
}
if !isUpdate {
errs["用户名"] = validation.Validate(user["username"],
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^[0-9_a-zA-Z]{1,15}$")).Error("只能包含数字字母和下划线,且1-15字符"))
errs["密码"] = validation.Validate(this.GetString("password"),
validation.Required.Error("不能为空"),
validation.Match(regexp.MustCompile("^([0-9]+[a-zA-Z]+[_]*){1,16}$")).Error("必须同时包含数字和字母,且1-15字符"))
}
err := errs.Filter()
if err != nil {
this.JsonError(err)
}
if isUpdate {
if this.GetString("password") != "" {
user["password"] = userModel.EncodePassword(this.GetString("password"))
}
user["update_time"] = time.Now().Unix()
delete(user, "username")
} else {
user["is_forbidden"] = 0
user["password"] = userModel.EncodePassword(this.GetString("password"))
user["create_time"] = time.Now().Unix()
}
return
}
func (this *UserController) List() {
keyword := strings.Trim(this.GetString("keyword", ""), " ")
page, err := this.GetInt("page", 1)
if err != nil {
page = 1
}
//每页的条数
pageSize := 10
limit := (page - 1) * pageSize
userModel := models.User{}
var users = []map[string]string{}
var userCount = 0
if keyword == "" {
userCount, err = userModel.CountUsers()
users, err = userModel.GetUsersByLimit(limit, pageSize)
} else {
userCount, err = userModel.CountUsersByKeyword(keyword)
users, err = userModel.GetUsersByKeywordAndLimit(keyword, limit, pageSize)
}
if err != nil {
this.ViewError(err.Error())
}
roleModel := models.Role{}
userRoles := map[string]string{}
//用户角色
for _, user := range users {
userId := user["user_id"]
var names = ""
roles, err := roleModel.GetRolesByUserId(userId)
if err != nil {
this.ViewError(err.Error())
}
for _, role := range roles {
names += "," + role["name"]
}
userRoles[user["user_id"]] = strings.Replace(names, ",", "", 1)
}
this.Data["users"] = users
this.Data["userRoles"] = userRoles
this.Data["keyword"] = keyword
this.Data["page"] = utils.NewMisc().Page(userCount, page, pageSize, "/user/list?page={page}")
this.viewLayoutTitle("用户列表", "user/list", "form")
}
+116
View File
@@ -0,0 +1,116 @@
package models
import (
"anytunnel/at-admin/app/utils"
"github.com/snail007/go-activerecord/mysql"
)
type Area struct {
}
func (p *Area) GetAreaByAreaId(areaId string) (area map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("area").Where(map[string]interface{}{
"area_id": areaId,
}))
if err != nil {
return
}
area = rs.Row()
return
}
func (p *Area) Delete(areaId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Delete("area", map[string]interface{}{
"area_id": areaId,
}))
if err != nil {
return
}
return
}
func (p *Area) Insert(area map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("area", area))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Area) Update(areaId string, area map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("area", area, map[string]interface{}{
"area_id": areaId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据搜索分页获取Area
func (area *Area) GetAreasByIDAndLimit(column, id string, limit int, number int) (areas []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("area").Where(map[string]interface{}{
column: id,
}).OrderBy("area_id", "desc").Limit(limit, number))
if err != nil {
return
}
areas = rs.Rows()
return
}
//分页获取Area
func (area *Area) GetAreasByLimit(limit int, number int) (areas []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("area").
OrderBy("area_id", "desc").
Limit(limit, number))
if err != nil {
return
}
areas = rs.Rows()
return
}
func (area *Area) CountAreas() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("area"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (area *Area) CountAreasByID(column, id string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("area").
Where(map[string]interface{}{
column: id,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+201
View File
@@ -0,0 +1,201 @@
package models
import (
"anytunnel/at-admin/app/utils"
common "anytunnel/at-common"
"fmt"
"github.com/astaxie/beego"
"github.com/snail007/go-activerecord/mysql"
)
type Client struct {
}
func (p *Client) GetClientByClientId(clientId string) (client map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("client").Where(map[string]interface{}{
"client_id": clientId,
}))
if err != nil {
return
}
client = rs.Row()
return
}
func (p *Client) HasTunnelRef(clientId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"client_id": clientId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Client) Reset(clientId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("client", map[string]interface{}{
"token": utils.NewMisc().RandString(32),
}, map[string]interface{}{
"client_id": clientId,
}))
if err != nil {
return
}
return
}
func (p *Client) Delete(clientId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("client", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"client_id": clientId,
}))
if err != nil {
return
}
return
}
func (p *Client) Insert(client map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("client", client))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Client) Update(clientId string, client map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("client", client, map[string]interface{}{
"client_id": clientId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//获取所有的Client
func (this *Client) GetAllClients() (clients []map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("client").Where(map[string]interface{}{
"is_delete": 0,
}).OrderBy("client_id", "ASC"))
if err != nil {
return
}
clients = res.Rows()
return
}
//根据user_id分页获取Client
func (client *Client) GetClientsByUserIDAndLimit(userID string, limit int, number int) (clients []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("client").Where(map[string]interface{}{
"user_id": userID,
"is_delete": 0,
}).OrderBy("client_id", "desc").Limit(limit, number))
if err != nil {
return
}
clients = rs.Rows()
return
}
//分页获取Client
func (client *Client) GetClientsByLimit(limit int, number int) (clients []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("client").Where(map[string]interface{}{
"is_delete": 0,
"user_id <>": 0,
}).OrderBy("client_id", "desc").Limit(limit, number))
if err != nil {
return
}
clients = rs.Rows()
return
}
func (client *Client) CountClients() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").Where(map[string]interface{}{
"is_delete": 0,
"user_id <>": 0,
}).From("client"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (client *Client) CountClientsByUserID(userID string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("client").
Where(map[string]interface{}{
"user_id": userID,
"is_delete": 0,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (p *Client) Offline(serverId string) (err error) {
db := DB
rs, err := db.Query(db.AR().From("client").Where(map[string]interface{}{
"client_id": serverId,
"is_delete": 0,
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
token := rs.Value("token")
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
"token": token,
"type": "client",
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
clusterIP := rs.Value("ip")
url := fmt.Sprintf("https://%s:%s/client/offline/%s", clusterIP, beego.AppConfig.String("cluster.api.port"), token)
body, code, err := common.HttpGet(url)
if err != nil {
return
}
if code != 200 {
err = fmt.Errorf("access client offline url %s fail,code:%d ,body:%s", url, code, body)
}
return
}
+119
View File
@@ -0,0 +1,119 @@
package models
import (
"strconv"
humanize "github.com/dustin/go-humanize"
"github.com/snail007/go-activerecord/mysql"
)
type Cluster struct {
}
func (p *Cluster) GetClusterByClusterId(clusterId string) (cluster map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("cluster").Where(map[string]interface{}{
"cluster_id": clusterId,
"is_delete": 0,
}))
if err != nil {
return
}
cluster = rs.Row()
return
}
func (p *Cluster) HasTunnel(clusterId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"cluster_id": clusterId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Cluster) Delete(clusterId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("cluster", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"cluster_id": clusterId,
}))
if err != nil {
return
}
return
}
func (p *Cluster) Insert(cluster map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("cluster", cluster))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Cluster) Update(clusterId string, cluster map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("cluster", cluster, map[string]interface{}{
"cluster_id": clusterId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//禁用
func (p *Cluster) Forbidden(clusterId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("cluster", map[string]interface{}{
"is_disable": 1,
}, map[string]interface{}{
"cluster_id": clusterId,
}))
if err != nil {
return
}
return
}
//恢复
func (p *Cluster) Review(clusterId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("cluster", map[string]interface{}{
"is_disable": 0,
}, map[string]interface{}{
"cluster_id": clusterId,
}))
if err != nil {
return
}
return
}
//获取所有的
func (this *Cluster) GetAllClusters() (clusters []map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("cluster").Where(map[string]interface{}{
"is_delete": 0,
}).OrderBy("name", "ASC"))
if err != nil {
return
}
clusters = res.Rows()
for k, v := range clusters {
_bandwidth, _ := strconv.ParseUint(v["bandwidth"], 10, 64)
clusters[k]["bandwidth_human"] = humanize.Bytes(_bandwidth)
}
return
}
+181
View File
@@ -0,0 +1,181 @@
package models
import (
"anytunnel/at-admin/app/utils"
common "anytunnel/at-common"
"fmt"
"github.com/astaxie/beego"
"github.com/snail007/go-activerecord/mysql"
)
type Conn struct {
}
func (p *Conn) GetConnByConnId(connId string) (conn map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("conn").Where(map[string]interface{}{
"conn_id": connId,
}))
if err != nil {
return
}
conn = rs.Row()
return
}
func (p *Conn) HasTunnelRef(connId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"conn_id": connId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Conn) Reset(connId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("conn", map[string]interface{}{
"token": utils.NewMisc().RandString(32),
}, map[string]interface{}{
"conn_id": connId,
}))
if err != nil {
return
}
return
}
func (p *Conn) Offline(connId string) (err error) {
db := DB
rs, err := db.Query(db.AR().From("conn").Where(map[string]interface{}{
"conn_id": connId,
"is_delete": 0,
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
token := rs.Value("token")
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
"token": token,
"type": "conn",
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
clusterIP := rs.Value("ip")
url := fmt.Sprintf("https://%s:%s/conn/offline/%s", clusterIP, beego.AppConfig.String("cluster.api.port"), token)
body, code, err := common.HttpGet(url)
if err != nil {
return
}
if code != 200 {
err = fmt.Errorf("access conn offline url %s fail,code:%d ,body:%s", url, code, body)
}
return
}
func (p *Conn) Delete(connId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("conn", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"conn_id": connId,
}))
if err != nil {
return
}
return
}
func (p *Conn) Insert(conn map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("conn", conn))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Conn) Update(connId string, conn map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("conn", conn, map[string]interface{}{
"conn_id": connId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据user_id分页获取Conn
func (conn *Conn) GetConnsByUserIDAndLimit(col, keyword, orderby string, limit int, number int) (conns []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("conn").Where(map[string]interface{}{
col: keyword,
}).OrderBy(orderby, "desc").Limit(limit, number))
if err != nil {
return
}
conns = rs.Rows()
return
}
//分页获取Conn
func (conn *Conn) GetConnsByLimit(limit int, number int, orderby string) (conns []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("conn").
OrderBy(orderby, "desc").
Limit(limit, number))
if err != nil {
return
}
conns = rs.Rows()
return
}
func (conn *Conn) CountConns() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("conn"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (conn *Conn) CountConnsByUserID(col, keyword string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("conn").
Where(map[string]interface{}{
col: keyword,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+15
View File
@@ -0,0 +1,15 @@
package models
import (
"anytunnel/at-admin/app/models"
"github.com/snail007/go-activerecord/mysql"
)
var G *mysql.DBGroup
var DB *mysql.DB
func init() {
G = models.G
DB = G.DB("base")
}
+116
View File
@@ -0,0 +1,116 @@
package models
import (
"anytunnel/at-admin/app/utils"
"github.com/snail007/go-activerecord/mysql"
)
type IpList struct {
}
func (p *IpList) GetIpListByIpListId(ip_listId string) (ip_list map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("ip_list").Where(map[string]interface{}{
"ip_list_id": ip_listId,
}))
if err != nil {
return
}
ip_list = rs.Row()
return
}
func (p *IpList) Delete(ip_listId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Delete("ip_list", map[string]interface{}{
"ip_list_id": ip_listId,
}))
if err != nil {
return
}
return
}
func (p *IpList) Insert(ip_list map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("ip_list", ip_list))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *IpList) Update(ip_listId string, ip_list map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("ip_list", ip_list, map[string]interface{}{
"ip_list_id": ip_listId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据搜索分页获取IpList
func (ip_list *IpList) GetIpListsByIDAndLimit(column, id string, limit int, number int) (ip_lists []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("ip_list").Where(map[string]interface{}{
column: id,
}).OrderBy("ip_list_id", "desc").Limit(limit, number))
if err != nil {
return
}
ip_lists = rs.Rows()
return
}
//分页获取IpList
func (ip_list *IpList) GetIpListsByLimit(limit int, number int) (ip_lists []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("ip_list").
OrderBy("ip_list_id", "desc").
Limit(limit, number))
if err != nil {
return
}
ip_lists = rs.Rows()
return
}
func (ip_list *IpList) CountIpLists() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("ip_list"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (ip_list *IpList) CountIpListsByID(column, id string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("ip_list").
Where(map[string]interface{}{
column: id,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+126
View File
@@ -0,0 +1,126 @@
package models
import (
"anytunnel/at-admin/app/utils"
"github.com/snail007/go-activerecord/mysql"
)
type Online struct {
}
func (p *Online) GetOnlineByOnlineId(onlineId string) (online map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
"online_id": onlineId,
}))
if err != nil {
return
}
online = rs.Row()
return
}
func (p *Online) Delete(onlineId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("online", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"online_id": onlineId,
}))
if err != nil {
return
}
return
}
func (p *Online) Insert(online map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("online", online))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Online) Update(onlineId string, online map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("online", online, map[string]interface{}{
"online_id": onlineId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据搜索分页获取Online
func (online *Online) GetOnlinesByIDAndLimit(cs, column, id string, limit int, number int) (onlines []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
column: id,
"cs_type": cs,
}).OrderBy("online_id", "desc").Limit(limit, number))
if err != nil {
return
}
onlines = rs.Rows()
return
}
//分页获取Online
func (online *Online) GetOnlinesByLimit(cs string, limit int, number int) (onlines []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
"cs_type": cs,
}).
OrderBy("online_id", "desc").
Limit(limit, number))
if err != nil {
return
}
onlines = rs.Rows()
return
}
func (online *Online) CountOnlines(cs string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").Where(map[string]interface{}{
"cs_type": cs,
}).From("online"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (online *Online) CountOnlinesByID(cs, column, id string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("online").
Where(map[string]interface{}{
column: id,
"cs_type": cs,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+159
View File
@@ -0,0 +1,159 @@
package models
import (
"anytunnel/at-admin/app/utils"
"strconv"
"time"
"github.com/dustin/go-humanize"
"github.com/snail007/go-activerecord/mysql"
)
type PackageModel struct {
}
func (p *PackageModel) GetPackageByPackageId(packageId string) (PackageModel map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("package").Where(map[string]interface{}{
"package_id": packageId,
}))
if err != nil {
return
}
PackageModel = rs.Row()
return
}
func (p *PackageModel) Delete(packageId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("package", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"package_id": packageId,
}))
if err != nil {
return
}
return
}
func (p *PackageModel) Insert(_package map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("package", _package))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *PackageModel) Update(packageId string, _package map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("package", _package, map[string]interface{}{
"package_id": packageId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据搜索分页获取Package
func (p *PackageModel) GetPackagesByIDAndLimit(column, id string, limit int, number int) (packages []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("package").Where(map[string]interface{}{
column: id,
}).OrderBy("end_time", "asc").Limit(limit, number))
if err != nil {
return
}
packages = rs.Rows()
for k, v := range packages {
_total, _ := strconv.ParseUint(v["bytes_total"], 10, 64)
_totalLeft, _ := strconv.ParseUint(v["bytes_left"], 10, 64)
_totalUse := _total - _totalLeft
packages[k]["bytes_total_human"] = humanize.Bytes(_total)
packages[k]["bytes_left_human"] = humanize.Bytes(_totalLeft)
packages[k]["bytes_use_human"] = humanize.Bytes(_totalUse)
packages[k]["status"] = `<span class="label label-success">有效</span>`
endtime, _ := strconv.Atoi(v["end_time"])
starttime, _ := strconv.Atoi(v["start_time"])
if int64(endtime) <= time.Now().Unix() {
packages[k]["status"] = `<span class="label label-danger">已过期</span>`
}
if int64(starttime) > time.Now().Unix() {
packages[k]["status"] = `<span class="label label-info">未生效</span>`
}
if _totalLeft == 0 {
packages[k]["status"] = `<span class="label label-warning">已用完</span>`
}
}
return
}
//分页获取Package
func (p *PackageModel) GetPackagesByLimit(limit int, number int) (packages []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("package").OrderBy("package_id", "desc").Limit(limit, number))
if err != nil {
return
}
packages = rs.Rows()
for k, v := range packages {
_total, _ := strconv.ParseUint(v["bytes_total"], 10, 64)
_totalLeft, _ := strconv.ParseUint(v["bytes_left"], 10, 64)
_totalUse := _total - _totalLeft
packages[k]["bytes_total_human"] = humanize.Bytes(_total)
packages[k]["bytes_left_human"] = humanize.Bytes(_totalLeft)
packages[k]["bytes_use_human"] = humanize.Bytes(_totalUse)
packages[k]["status"] = `<span class="label label-success">有效</span>`
endtime, _ := strconv.Atoi(v["end_time"])
starttime, _ := strconv.Atoi(v["start_time"])
if int64(endtime) <= time.Now().Unix() {
packages[k]["status"] = `<span class="label label-danger">已过期</span>`
}
if int64(starttime) > time.Now().Unix() {
packages[k]["status"] = `<span class="label label-info">未生效</span>`
}
if _totalLeft == 0 {
packages[k]["status"] = `<span class="label label-warning">已用完</span>`
}
}
return
}
func (p *PackageModel) CountPackages() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("package"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (p *PackageModel) CountPackagesByID(column, id string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("package").
Where(map[string]interface{}{
column: id,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+152
View File
@@ -0,0 +1,152 @@
package models
import (
"github.com/snail007/go-activerecord/mysql"
)
type Region struct {
}
func (p *Region) GetRegionByRegionId(regionId string) (region map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("region").Where(map[string]interface{}{
"region_id": regionId,
"is_delete": 0,
}))
if err != nil {
return
}
region = rs.Row()
return
}
func (p *Region) HasRole(regionId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("role_region").Where(map[string]interface{}{
"region_id": regionId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Region) HasSubRegion(regionId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("region").Where(map[string]interface{}{
"parent_id": regionId,
"is_delete": 0,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Region) HasCluster(regionId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("region_cluster").Where(map[string]interface{}{
"region_id": regionId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Region) Delete(regionId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("region", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"region_id": regionId,
}))
if err != nil {
return
}
return
}
func (p *Region) Insert(region map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("region", region))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Region) Update(regionId string, region map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("region", region, map[string]interface{}{
"region_id": regionId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//获取所有的Region
func (this *Region) GetAllRegions() (regions []map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("region").Where(map[string]interface{}{
"is_delete": 0,
}).OrderBy("parent_id", "ASC").OrderBy("region_id", "ASC"))
if err != nil {
return
}
regions = res.Rows()
return
}
//获取所有顶级Region
func (this *Region) GetTopRegions() (regions map[string]map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("region").Where(map[string]interface{}{
"is_delete": 0,
"parent_id": 0,
}).OrderBy("region_id", "ASC"))
if err != nil {
return
}
regions = res.MapRows("region_id")
return
}
//获取所有二级Region
func (this *Region) GetSubRegions() (regions []map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("region").Where(map[string]interface{}{
"is_delete": 0,
}).OrderBy("parent_id", "ASC"))
if err != nil {
return
}
regionsAll := res.Rows()
for _, r := range regionsAll {
if r["parent_id"] != "0" {
r["parent"] = ""
for _, r1 := range regionsAll {
if r["parent_id"] == r1["region_id"] {
r["parent"] = r1["name"]
break
}
}
regions = append(regions, r)
}
}
return
}
+150
View File
@@ -0,0 +1,150 @@
package models
import (
"strconv"
"strings"
"time"
humanize "github.com/dustin/go-humanize"
"github.com/snail007/go-activerecord/mysql"
)
type Role struct {
}
func (p *Role) GetRoleByRoleId(roleId string) (role map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("role").Where(map[string]interface{}{
"role_id": roleId,
"is_delete": 0,
}))
if err != nil {
return
}
role = rs.Row()
return
}
func (p *Role) HasUser(roleId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user_role").Where(map[string]interface{}{
"role_id": roleId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Role) Delete(roleId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("role", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
return
}
func (p *Role) Insert(role map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("role", role))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Role) Update(roleId string, role map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("role", role, map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//获取所有的角色
func (this *Role) GetAllRoles() (roles []map[string]string, err error) {
db := DB
res, err := db.Query(db.AR().From("role").Where(map[string]interface{}{
"is_delete": 0,
}).OrderBy("role_id", "ASC"))
if err != nil {
return
}
roles = res.Rows()
for k, v := range roles {
c, _ := strconv.ParseUint(v["bandwidth"], 10, 64)
v["bandwidth_human"] = humanize.Bytes(c)
m := strings.Split(v["tunnel_mode"], ",")
mmap := map[string]string{"0": "普通", "1": "高级", "2": "特殊"}
_m := []string{}
for _, val := range m {
_m = append(_m, mmap[val])
}
v["tunnel_mode_human"] = strings.Join(_m, ",")
roles[k] = v
}
return
}
// 根据用户名查找所有的角色
func (this *Role) GetRolesByUserId(userId string) (roles []map[string]string, err error) {
userRoleModel := UserRole{}
userRoles, err := userRoleModel.GetUserRolesByUserId(userId)
if err != nil {
return
}
for _, userRole := range userRoles {
role, _ := this.GetRoleByRoleId(userRole["role_id"])
roles = append(roles, role)
}
return
}
func (this *Role) SetRegions(roleId string, region_ids []string) (err error) {
db := DB
_, err = db.Exec(db.AR().Delete("role_region", map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
data := []map[string]interface{}{}
for _, v := range region_ids {
data = append(data, map[string]interface{}{
"role_id": roleId,
"region_id": v,
"create_time": time.Now().Unix(),
})
}
if len(data) > 0 {
_, err = db.Exec(db.AR().InsertBatch("role_region", data))
}
return
}
func (this *Role) GetRoleRegionIds(roleId string) (ids []string, err error) {
db := DB
rs, err := db.Query(db.AR().From("role_region").Where(map[string]interface{}{
"role_id": roleId,
}))
if err != nil {
return
}
ids = rs.Values("region_id")
return
}
+189
View File
@@ -0,0 +1,189 @@
package models
import (
"anytunnel/at-admin/app/utils"
common "anytunnel/at-common"
"fmt"
"github.com/astaxie/beego"
"github.com/snail007/go-activerecord/mysql"
)
type Server struct {
}
func (p *Server) GetServerByServerId(serverId string) (server map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("server").Where(map[string]interface{}{
"server_id": serverId,
}))
if err != nil {
return
}
server = rs.Row()
return
}
func (p *Server) HasTunnelRef(serverId string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"server_id": serverId,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *Server) Reset(serverId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("server", map[string]interface{}{
"token": utils.NewMisc().RandString(32),
}, map[string]interface{}{
"server_id": serverId,
}))
if err != nil {
return
}
return
}
func (p *Server) Offline(serverId string) (err error) {
db := DB
rs, err := db.Query(db.AR().From("server").Where(map[string]interface{}{
"server_id": serverId,
"is_delete": 0,
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
token := rs.Value("token")
rs, err = db.Query(db.AR().From("online").Where(map[string]interface{}{
"token": token,
"type": "server",
}))
if err != nil {
return
}
if rs.Len() == 0 {
return
}
clusterIP := rs.Value("ip")
url := fmt.Sprintf("https://%s:%s/server/offline/%s", clusterIP, beego.AppConfig.String("cluster.api.port"), token)
body, code, err := common.HttpGet(url)
if err != nil {
return
}
if code != 200 {
err = fmt.Errorf("access server offline url %s fail,code:%d ,body:%s", url, code, body)
}
return
}
func (p *Server) Delete(serverId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("server", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"server_id": serverId,
}))
if err != nil {
return
}
return
}
func (p *Server) Insert(server map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("server", server))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Server) Update(serverId string, server map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("server", server, map[string]interface{}{
"server_id": serverId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据user_id分页获取Server
func (server *Server) GetServersByUserIDAndLimit(userID string, limit int, number int) (servers []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("server").Where(map[string]interface{}{
"user_id": userID,
"is_delete": 0,
}).OrderBy("server_id", "desc").Limit(limit, number))
if err != nil {
return
}
servers = rs.Rows()
return
}
//分页获取Server
func (server *Server) GetServersByLimit(limit int, number int) (servers []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("server").Where(map[string]interface{}{
"is_delete": 0,
"user_id <>": 0,
}).
OrderBy("server_id", "desc").
Limit(limit, number))
if err != nil {
return
}
servers = rs.Rows()
return
}
func (server *Server) CountServers() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").Where(map[string]interface{}{
"is_delete": 0,
"user_id <>": 0,
}).From("server"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (server *Server) CountServersByUserID(userID string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("server").
Where(map[string]interface{}{
"user_id": userID,
"is_delete": 0,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+124
View File
@@ -0,0 +1,124 @@
package models
import (
"anytunnel/at-admin/app/utils"
"github.com/snail007/go-activerecord/mysql"
)
type Tunnel struct {
}
func (p *Tunnel) GetTunnelByTunnelId(tunnelId string) (tunnel map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"tunnel_id": tunnelId,
}))
if err != nil {
return
}
tunnel = rs.Row()
return
}
func (p *Tunnel) Delete(tunnelId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("tunnel", map[string]interface{}{
"is_delete": 1,
}, map[string]interface{}{
"tunnel_id": tunnelId,
}))
if err != nil {
return
}
return
}
func (p *Tunnel) Insert(tunnel map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("tunnel", tunnel))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *Tunnel) Update(tunnelId string, tunnel map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("tunnel", tunnel, map[string]interface{}{
"tunnel_id": tunnelId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
//根据搜索分页获取Tunnel
func (tunnel *Tunnel) GetTunnelsByIDAndLimit(column, id string, limit int, number int) (tunnels []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
column: id,
"is_delete": 0,
}).OrderBy("tunnel_id", "desc").Limit(limit, number))
if err != nil {
return
}
tunnels = rs.Rows()
return
}
//分页获取Tunnel
func (tunnel *Tunnel) GetTunnelsByLimit(limit int, number int) (tunnels []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("tunnel").Where(map[string]interface{}{
"is_delete": 0,
}).
OrderBy("tunnel_id", "desc").
Limit(limit, number))
if err != nil {
return
}
tunnels = rs.Rows()
return
}
func (tunnel *Tunnel) CountTunnels() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").Where(map[string]interface{}{
"is_delete": 0,
}).From("tunnel"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (tunnel *Tunnel) CountTunnelsByID(column, id string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("tunnel").
Where(map[string]interface{}{
column: id,
"is_delete": 0,
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
+206
View File
@@ -0,0 +1,206 @@
package models
import (
"anytunnel/at-admin/app/utils"
"crypto/md5"
"encoding/hex"
"errors"
"strings"
"github.com/snail007/go-activerecord/mysql"
)
type User struct {
}
func (p *User) GetUserByUserId(userId string) (user map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
user = rs.Row()
return
}
func (p *User) HasSameUsername(userId, username string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"user_id <>": userId,
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *User) HasUsername(username string) (has bool, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
if rs.Len() > 0 {
has = true
}
return
}
func (p *User) GetUserByName(username string) (user map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username": username,
}).Limit(0, 1))
if err != nil {
return
}
user = rs.Row()
return
}
//禁用
func (p *User) Forbidden(userId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"is_forbidden": 1,
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
//恢复
func (p *User) Review(userId string) (err error) {
db := DB
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"is_forbidden": 0,
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
func (p *User) Insert(user map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Insert("user", user))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *User) Update(userId string, user map[string]interface{}) (id int64, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Exec(db.AR().Update("user", user, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
id = rs.LastInsertId
return
}
func (p *User) ChangePassword(userId, newpassword, oldpassword string) (err error) {
db := DB
user, err := p.GetUserByUserId(userId)
if user["password"] != p.EncodePassword(oldpassword) {
return errors.New("旧密码错误")
}
if err != nil {
return
}
_, err = db.Exec(db.AR().Update("user", map[string]interface{}{
"password": p.EncodePassword(newpassword),
}, map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
return
}
func (p *User) EncodePassword(password string) (passwordHash string) {
hasher := md5.New()
hasher.Write([]byte(password))
passwordHash = strings.ToLower(hex.EncodeToString(hasher.Sum(nil)))
return
}
//根据关键字分页获取用户
func (user *User) GetUsersByKeywordAndLimit(keyword string, limit int, number int) (users []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Where(map[string]interface{}{
"username LIKE": "%" + keyword + "%",
}).Limit(limit, number))
if err != nil {
return
}
users = rs.Rows()
return
}
//分页获取用户
func (user *User) GetUsersByLimit(limit int, number int) (users []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user").Limit(limit, number))
if err != nil {
return
}
users = rs.Rows()
return
}
func (user *User) CountUsers() (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().Select("count(*) as total").From("user"))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
func (user *User) CountUsersByKeyword(keyword string) (count int, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().
Select("count(*) as total").
From("user").
Where(map[string]interface{}{
"username LIKE": "%" + keyword + "%",
}))
if err != nil {
return
}
count = utils.NewConvert().StringToInt(rs.Value("total"))
return
}
@@ -0,0 +1,62 @@
package models
import (
"time"
"github.com/snail007/go-activerecord/mysql"
)
type UserRole struct {
UserRoleID uint `orm:"pk"` // user_role_id
UserID uint64 // user_id
RoleID uint // role_id
CreateTime uint // create_time
UpdateTime uint // update_time
}
func (this *UserRole) GetUserRolesByUserId(userId string) (userRoles []map[string]string, err error) {
db := DB
var rs *mysql.ResultSet
rs, err = db.Query(db.AR().From("user_role").Where(map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
userRoles = rs.Rows()
return
}
// 插入 user_id 和 role_id 对应关系
func (this *UserRole) Insert(userId string, roleIds []string) (res bool, err error) {
res = false
db := DB
//先删除
_, err = db.Exec(db.AR().Delete("user_role", map[string]interface{}{
"user_id": userId,
}))
if err != nil {
return
}
//添加
userRoles := []map[string]interface{}{}
for _, roleId := range roleIds {
userRole := map[string]interface{}{
"role_id": roleId,
"user_id": userId,
"create_time": time.Now().Unix(),
"update_time": time.Now().Unix(),
}
userRoles = append(userRoles, userRole)
}
_, err = db.Exec(db.AR().InsertBatch("user_role", userRoles))
if err != nil {
return
}
res = true
return
}
+63
View File
@@ -0,0 +1,63 @@
package routers
import (
"anytunnel/at-admin/app/controllers"
web_controllers "anytunnel/at-admin/app/modules/web/controllers"
"anytunnel/at-admin/app/utils"
"html/template"
"math/rand"
"net/http"
"github.com/astaxie/beego"
)
func init() {
beego.AppConfig.Set("author.passport", "usmpassport")
beego.AppConfig.Set("sys.name", "ATCAS")
beego.AppConfig.Set("sys.fullname", "AnyTunnel Cloud Admin System")
beego.BConfig.ServerName = beego.AppConfig.String("sys.name")
beego.SetStaticPath("/static/", "static")
beego.BConfig.WebConfig.AutoRender = false
beego.BConfig.WebConfig.Session.SessionName = "ssid"
beego.BConfig.WebConfig.Session.SessionOn = true
beego.BConfig.RouterCaseSensitive = false
beego.Router("/", &controllers.MainController{}, "*:Index")
beego.AutoRouter(&controllers.MainController{})
beego.AutoRouter(&controllers.SystemController{})
beego.AutoRouter(&controllers.LoginController{})
beego.AutoRouter(&controllers.UserController{})
beego.AutoRouter(&controllers.PrivilegeController{})
beego.AutoRouter(&controllers.Role_PrivilegeController{})
beego.AutoRouter(&controllers.RoleController{})
beego.AddFuncMap("randInt", randInt)
beego.AddFuncMap("dateFormat", utils.NewDate().Format)
beego.ErrorHandler("404", page_not_found)
beego.ErrorHandler("500", page_not_found)
beego.BConfig.WebConfig.ViewsPath = "app/views"
userNS := beego.NewNamespace("/web",
beego.NSAutoRouter(&web_controllers.UserController{}),
beego.NSAutoRouter(&web_controllers.RoleController{}),
beego.NSAutoRouter(&web_controllers.ServerController{}),
beego.NSAutoRouter(&web_controllers.ClientController{}),
beego.NSAutoRouter(&web_controllers.RegionController{}),
beego.NSAutoRouter(&web_controllers.ClusterController{}),
beego.NSAutoRouter(&web_controllers.TunnelController{}),
beego.NSAutoRouter(&web_controllers.OnlineController{}),
beego.NSAutoRouter(&web_controllers.PackageController{}),
beego.NSAutoRouter(&web_controllers.ConnController{}),
beego.NSAutoRouter(&web_controllers.IpListController{}),
beego.NSAutoRouter(&web_controllers.AreaController{}),
)
beego.AddNamespace(userNS)
}
func page_not_found(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("500-full.html").ParseFiles(beego.BConfig.WebConfig.ViewsPath + "/error/500-full.html")
data := make(map[string]interface{})
data["content"] = ""
t.Execute(rw, data)
}
func randInt(start, end int) int {
return rand.Intn(end) + start
}
+64
View File
@@ -0,0 +1,64 @@
package utils
import "strconv"
type Convert struct{}
func NewConvert() *Convert {
return &Convert{}
}
// bool 转化为字符串
func (convert *Convert) BoolToString(boolValue bool) string {
if boolValue == true {
return "true"
} else {
return "false"
}
}
//bool 转化为 int
func (convert *Convert) BoolToInt(boolValue bool) int {
if boolValue == true {
return 1
} else {
return 0
}
}
//int 转化为 bool
func (convert *Convert) IntToBool(number int) bool {
if number == 0 {
return false
} else {
return true
}
}
//int 转化为字符串
//base 范围 2-32 进制
func (convert *Convert) IntToString(number int64, base int) string {
return strconv.FormatInt(number, base)
}
//string to int(10进制)
func (convert *Convert) StringToInt(str string) int {
intValue, _ := strconv.Atoi(str)
return intValue
}
// string to int64(10进制)
func (convert *Convert) StringToInt64(str string) int64 {
intValue, _ := strconv.ParseInt(str, 10, 64)
return intValue
}
//int 转化为10进制字符串 IntToString(number, 10)
func (convert *Convert) IntToTenString(number int) string {
return strconv.Itoa(number)
}
// float 转化为字符串
func (convert *Convert) FloatToString(f float64, fmt byte, prec, bitSize int) string {
return strconv.FormatFloat(f, fmt, prec, bitSize)
}
+34
View File
@@ -0,0 +1,34 @@
package utils
import (
"github.com/astaxie/beego"
"time"
)
type Date struct{}
func NewDate() *Date {
return &Date{}
}
//格式化 unix 时间戳
func (date *Date) Format(unixTime interface{}, format string) string {
convert := NewConvert();
var convertTime int64
switch unixTime.(type) {
case string:
convertTime = convert.StringToInt64(unixTime.(string))
case int:
convertTime = int64(unixTime.(int))
case int8:
convertTime = int64(unixTime.(int8))
case int16:
convertTime = int64(unixTime.(int16))
case int32:
convertTime = int64(unixTime.(int32))
}
return beego.Date(time.Unix(convertTime, 0), format);
}
+51
View File
@@ -0,0 +1,51 @@
package utils
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
)
const (
BASE_64_TABLE = "1234567890poiuytreqwasdfghjklmnbvcxzQWERTYUIOPLKJHGFDSAZXCVBNM-_"
)
type Encrypt struct{}
func NewEncrypt() *Encrypt {
return &Encrypt{}
}
//base64 加密
func (encrypt *Encrypt) Base64Encode(str string) string {
var coder = base64.NewEncoding(BASE_64_TABLE)
var src []byte = []byte(str)
return string([]byte(coder.EncodeToString(src)))
}
//base64 加密
func (encrypt *Encrypt) Base64EncodeBytes(bytes []byte) []byte {
var coder = base64.NewEncoding(BASE_64_TABLE)
return []byte(coder.EncodeToString(bytes))
}
//base64 解密
func (encrypt *Encrypt) Base64Decode(str string) (string, error) {
var src []byte = []byte(str)
var coder = base64.NewEncoding(BASE_64_TABLE)
by, err := coder.DecodeString(string(src))
return string(by), err
}
//base64 解密
func (encrypt *Encrypt) Base64DecodeBytes(str string) ([]byte, error) {
var coder = base64.NewEncoding(BASE_64_TABLE)
return coder.DecodeString(str)
}
//md5加密
func (encrypt *Encrypt) Md5Encode(str string) string {
hash := md5.New()
hash.Write([]byte(str))
return hex.EncodeToString(hash.Sum(nil))
}
+110
View File
@@ -0,0 +1,110 @@
package utils
import (
"fmt"
"math"
"math/rand"
"strings"
"time"
)
type Misc struct{}
func NewMisc() *Misc {
return &Misc{}
}
func (m *Misc) RandString(strlen int) string {
codes := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
codeLen := len(codes)
data := make([]byte, strlen)
rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63())
for i := 0; i < strlen; i++ {
idx := rand.Intn(codeLen)
data[i] = byte(codes[idx])
}
return string(data)
}
/**
* 分页方法
* @param type $total 一共多少记录
* @param type $page 当前是第几页
* @param type $pagesize 每页多少
* @param type $url url是什么,url里面的{page}会被替换成页码
* @param array $order 分页条的组成,是一个数组,可以按着1-6的序号,选择分页条组成部分和每个部分的顺序
* @param int $a_count 分页条中a页码链接的总数量,不包含当前页的a标签,默认10个。
* @return type String
* echo Sr::page(100,3,10,'?article/list/{page}',array(3,4,5,1,2,6));
*/
func (m *Misc) Page(total, page, pagesize int, url string, args ...interface{}) string {
order := []int{1, 2, 3, 4, 5, 6}
a_count := 10
if len(args) >= 1 {
order = args[0].([]int)
}
if len(args) >= 2 {
a_count = args[1].(int)
}
a_num := a_count
first := "首页"
last := "尾页"
pre := "上页"
next := "下页"
if a_num%2 == 0 {
a_num++
}
pages := int(math.Ceil(float64(total) / float64(pagesize)))
curpage := page
if curpage > pages || curpage <= 0 {
curpage = 1
}
body := `<span class="page_body">`
prefix := ""
subfix := ""
start := curpage - ((a_num - 1) / 2)
end := curpage + ((a_num - 1) / 2)
if start <= 0 {
start = 1
}
if end > pages {
end = pages
}
if pages >= a_num {
if curpage <= (a_num-1)/2 {
end = a_num
}
if end-curpage <= (a_num-1)/2 {
start -= int(math.Floor(float64(a_num)/float64(2))) - (end - curpage)
}
}
for i := start; i <= end; i++ {
if i == curpage {
body += fmt.Sprintf(`<a class="page_cur_page" href="javascript:void(0);"><b>%d</b></a>`, i)
} else {
body += fmt.Sprintf(`<a href="%s">%d</a>`, strings.Replace(url, "{page}", fmt.Sprintf("%d", i), 1), i)
}
}
body += "</span>"
if curpage > 1 {
prefix = fmt.Sprintf(`<span class="page_bar_prefix"><a href="%s">%s</a><a href="%s">%s</a></span>`, strings.Replace(url, "{page}", fmt.Sprintf("%d", 1), 1), first, strings.Replace(url, "{page}", fmt.Sprintf("%d", curpage-1), 1), pre)
}
if curpage != pages {
subfix = fmt.Sprintf(`<span class="page_bar_subfix"><a href="%s">%s</a><a href="%s">%s</a></span>`, strings.Replace(url, "{page}", fmt.Sprintf("%d", curpage+1), 1), next, strings.Replace(url, "{page}", fmt.Sprintf("%d", pages), 1), last)
}
info := fmt.Sprintf(`<span class="page_cur">第%d/%d页</span>`, curpage, pages)
id := fmt.Sprintf("gsd09fhas9d%d%d%d", rand.Intn(1000), rand.Intn(1000), rand.Intn(1000))
gostr := fmt.Sprintf(`<script>function ekup(){if(event.keyCode==13){clkyup();}}function clkyup(){var num=document.getElementById('%s').value;if(!/^\d+$/.test(num)||num<=0||num>%d){alert('请输入正确页码!');return;};location='%s'.replace(/\{page\}/,document.getElementById('%s').value);}</script><span class="page_input_num"><input onkeyup="ekup()" type="text" id="%s" style="width:40px;vertical-align:text-baseline;padding:0 2px;font-size:10px;border:1px solid gray;"/></span><span class="page_btn_go" onclick="clkyup();" style="cursor:pointer;">转到</span>`, id, pages, url, id, id)
totalstr := fmt.Sprintf(`<span class="page_total">共%d条</span>`, total)
pagenation := []string{totalstr, info, prefix, body, subfix, gostr}
output := []string{}
for _, v := range order {
if v-1 < len(pagenation) && v-1 >= 0 {
output = append(output, pagenation[v-1])
}
}
if pages > 1 {
return strings.Join(output, "")
}
return ""
}
+21
View File
@@ -0,0 +1,21 @@
package utils
import (
"fmt"
"net/url"
)
type Urls struct {}
func NewUrls() *Urls {
return &Urls{}
}
func (urls *Urls) UrlEncode(urlStr string) string {
fmt.Println(urlStr)
urlParse, _ := url.Parse(urlStr)
urlEncode := urlParse.Query().Encode()
fmt.Println(urlParse.EscapedPath());
return "http://" + urlParse.Host + urlParse.EscapedPath() + "?" + urlEncode;
}
+2
View File
@@ -0,0 +1,2 @@
<script>
</script>
+35
View File
@@ -0,0 +1,35 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/index/jquery-2.1.4.min.js"></script>
<link rel="stylesheet" href="/static/index/fonts.googleapis.com.css">
<link rel="stylesheet" href="/static/style/awesome/css/font-awesome.min.css">
<!--bootstrap default theme-->
<!-- <link rel="stylesheet" href="/static/style/bootstrap/default/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/default/css/bootstrap-theme.min.css">
<script src="/static/style/bootstrap/default/js/bootstrap.min.js"></script> -->
<!--end-->
<!--bootstrap todc theme-->
<!-- <link rel="stylesheet" href="/static/style/bootstrap/todc/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/todc/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/todc/css/todc-bootstrap.min.css">
<script src="/static/style/bootstrap/todc/js/bootstrap.min.js"></script> -->
<!--end-->
<!--bootstrap google plus theme-->
<link rel="stylesheet" href="/static/style/bootstrap/default/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/googleplus/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/googleplus/css/styles.css">
<script src="/static/style/bootstrap/googleplus/js/bootstrap.min.js"></script>
<script src="/static/style/bootstrap/googleplus/js/scripts.js"></script>
<!--end-->
<!--common stuff-->
<script src="/static/style/plugins/notify.min.js"></script>
<script src="/static/style/plugins/bootstrap-confirmation.min.js"></script>
<script src="/static/style/common/common.js"></script>
<link rel="stylesheet" href="/static/style/common/common.css">
<!--end-->
+53
View File
@@ -0,0 +1,53 @@
<!DOCTYPE HTML>
<html>
<head>
<title>出错啦!</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/static/index/jquery-2.1.4.min.js"></script>
<link rel="stylesheet" href="/static/index/fonts.googleapis.com.css">
<link rel="stylesheet" href="/static/index/common.css">
<link rel="stylesheet" href="/static/style/awesome/css/font-awesome.min.css">
<!--bootstrap google plus theme-->
<link rel="stylesheet" href="/static/style/bootstrap/default/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/googleplus/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/style/bootstrap/googleplus/css/styles.css">
<script src="/static/style/bootstrap/googleplus/js/bootstrap.min.js"></script>
<script src="/static/style/bootstrap/googleplus/js/scripts.js"></script>
<!--end-->
<!--common stuff-->
<script src="/static/style/plugins/notify.min.js"></script>
<script src="/static/style/plugins/bootstrap-confirmation.min.js"></script>
<script src="/static/style/common/common.js"></script>
<link rel="stylesheet" href="/static/style/common/common.css">
<!--end-->
</head>
<body>
<div class="panel panel-default">
<div class="panel-body">
<div class="alert alert-danger" role="alert">
<strong><span class="blue bigger-125">
<i class="ace-icon fa fa-random"></i>
</span></strong> 很抱歉,服务器返回错误!
</div>
<div class="space"></div>
<hr>
<div class="space"></div>
<div class="center">
<a href="javascript:history.back()" class="btn btn-default">
<i class="ace-icon fa fa-arrow-left"></i> 返回
</a>
<a target="_parent" href="/main/index" class="btn btn-primary">
<i class="ace-icon fa fa-tachometer"></i> 首页
</a>
</div>
</div>
</div>
</body>
</html>
+31
View File
@@ -0,0 +1,31 @@
<div class="panel panel-default">
<div class="panel-body">
<div class="alert alert-danger" role="alert">
<strong><span class="blue bigger-125">
<i class="ace-icon fa fa-random"></i>
</span></strong> 很抱歉,服务器返回错误!
</div>
<div class="space"></div>
<div>
<h4 class="lighter smaller">以下是具体的错误信息:</h4>
<ul class="list-unstyled spaced inline bigger-110 margin-15">
<li>
<i class="ace-icon fa fa-hand-o-right blue"></i>
<span class="text-danger">{{.errorMessage}}</span>
</li>
</ul>
</div>
<hr>
<div class="space"></div>
<div class="center">
<a href="javascript:history.back()" class="btn btn-default">
<i class="ace-icon fa fa-arrow-left"></i>
返回
</a>
<a target="_parent" href="/main/index" class="btn btn-primary">
<i class="ace-icon fa fa-tachometer"></i>
首页
</a>
</div>
</div>
</div>
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>{{.title}}</title>
{{template "common/head.html" .}}
</head>
<body>
{{.LayoutContent}} {{template "common/foot.html" .}}
</body>
</html>
+24
View File
@@ -0,0 +1,24 @@
<!DOCTYPE HTML>
<html>
<head>
<title>{{.titles}}</title>
{{template "common/head.html" .}}
<script src="/static/style/plugins/jquery.form.min.js"></script>
<link rel="stylesheet" href="/static/style/plugins/icon/icon-selector.css">
<link rel="stylesheet" href="/static/style/plugins/selectaor/fm.selectator.jquery.css">
<script src="/static/style/plugins/icon/icon-selector.js"></script>
<script src="/static/style/plugins/selectaor/fm.selectator.jquery.js"></script>
<link rel="stylesheet" href="/static/style/plugins/datepicker/bootstrap-datepicker3.css">
<script src="/static/style/plugins/datepicker/bootstrap-datepicker.min.js"></script>
<script src="/static/style/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js"></script>
</head>
<body>
{{.LayoutContent}} {{template "common/foot.html" .}}
<script src="/static/style/common/form.js"></script>
</body>
</html>
+225
View File
@@ -0,0 +1,225 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
<link rel="shortcut icon" href="/static/index/favicon2.ico">
<style>
@font-face {
font-family: 'Exo';
font-style: normal;
font-weight: 200;
src: local('Exo ExtraLight'), local('Exo-ExtraLight'), url(/static/index/D3Kro_SQxlEekR5WQVBxG-vvDin1pK8aKteLpeZ5c0A.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Exo';
font-style: normal;
font-weight: 400;
src: local('Exo Regular'), local('Exo-Regular'), url(/static/index/7V_Hf8uqhTnCHU5pdwV73A.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
body {
margin: 0;
padding: 0;
background: #fff;
color: #fff;
font-family: Arial;
font-size: 12px;
}
.body {
position: absolute;
top: -20px;
left: -20px;
right: 0px;
bottom: 0px;
width: auto;
height: auto;
background-image: url(/static/index/login-bg{{randInt 1 3}}.jpg);
background-size: cover;
-webkit-filter: blur(5px);
z-index: 0;
}
.grad {
position: absolute;
top: -20px;
left: -20px;
right: 0px;
bottom: 0px;
width: auto;
height: auto;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(0, 0, 0, 0)), color-stop(100%, rgba(0, 0, 0, 0.65)));
/* Chrome,Safari4+ */
z-index: 1;
opacity: 0.7;
}
.header {
position: absolute;
top: calc(50% - 35px);
left: calc(50% - 455px);
z-index: 2;
}
.header div {
float: left;
color: #fff;
font-family: 'Exo', sans-serif;
font-size: 35px;
font-weight: 200;
}
.header div span {
color: #5379fa !important;
}
.login {
position: absolute;
top: calc(50% - 75px);
left: calc(50% - 50px);
height: 150px;
width: 350px;
padding: 10px;
z-index: 2;
}
.login input[type=text] {
width: 250px;
height: 30px;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: 2px;
color: #fff;
font-family: 'Exo', sans-serif;
font-size: 16px;
font-weight: 400;
padding: 4px;
}
.login input[type=password] {
width: 250px;
height: 30px;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.6);
border-radius: 2px;
color: #fff;
font-family: 'Exo', sans-serif;
font-size: 16px;
font-weight: 400;
padding: 4px;
margin-top: 10px;
}
.login input[type=submit] {
width: 260px;
height: 35px;
background: #fff;
border: 1px solid #fff;
cursor: pointer;
border-radius: 2px;
color: #a18d6c;
font-family: 'Exo', sans-serif;
font-size: 16px;
font-weight: 400;
padding: 6px;
margin-top: 10px;
}
.login input[type=submit]:hover {
opacity: 0.8;
}
.login input[type=submit]:active {
opacity: 0.6;
}
.login input[type=text]:focus {
outline: none;
border: 1px solid rgba(255, 255, 255, 0.9);
}
.login input[type=password]:focus {
outline: none;
border: 1px solid rgba(255, 255, 255, 0.9);
}
.login input[type=submit] {
width: 252px;
}
::-webkit-input-placeholder {
color: rgba(255, 255, 255, 0.6);
}
::-moz-input-placeholder {
color: rgba(255, 255, 255, 0.6);
}
.login .captcha {
border: none;
width: 80px;
height: 28px;
margin-top: -2px;
}
.login input[type=text].captcha-value {
margin: 10px 10px 0 0;
width: 160px;
}
</style>
<script src="/static/index/prefixfree.min.js"></script>
</head>
<body>
<div class="body"></div>
<div class="grad"></div>
<div class="header">
<div>{{config "String" "sys.name" "" }}<span>Admin</span></div>
</div>
<br>
<form action="/login/login" class="ajaxform" ajaxSuccess="ajaxSuccess">
<div class="login">
<input type="text" placeholder="用户名" name="username" value="root"><br>
<input type="password" placeholder="密码" name="password" value="123abc"><br>
<input type="text" placeholder="验证码" name="captcha" class="captcha-value" value=""><img class="captcha" src="/login/captcha" /><br>
<input type="submit" value="Login">
</div>
</form>
<script src='/static/index/jquery-2.1.4.min.js'></script>
<script src="/static/style/plugins/notify.min.js"></script>
<script src="/static/style/plugins/jquery.form.min.js"></script>
<script src="/static/style/plugins/icon/icon-selector.js"></script>
<link rel="stylesheet" href="/static/style/plugins/icon/icon-selector.css">
<script src="/static/style/common/form.js"></script>
<script>
if (top != self) {
top.location = location;
}
$(".captcha").click(function() {
refreshCode();
});
function ajaxSuccess(data) {
if (!data.code) {
refreshCode();
}
}
function refreshCode() {
$(".captcha-value").val("")
$(".captcha").attr("src", "/login/captcha?_=" + (Math.random() + "").substring(2))
}
</script>
</body>
</html>
+255
View File
@@ -0,0 +1,255 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="UTF-8">
<title>{{.title}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="/static/index/bootstrap.min.css">
<link rel="stylesheet" href="/static/index/font-awesome.min.css">
<link rel="stylesheet" href="/static/index/fonts.googleapis.com.css">
<link rel="stylesheet" href="/static/index/ace.min.css" class="ace-main-stylesheet" id="main-ace-style">
<link rel="stylesheet" href="/static/index/ace-skins.min.css">
<link rel="stylesheet" href="/static/index/ace-rtl.min.css">
<script src="/static/index/ace-extra.min.js"></script>
<link rel="shortcut icon" href="/static/index/favicon2.ico">
<style>
ul.nav-list {
display: none;
}
ul.nav-list.showul {
display: block
}
.ace-save-state {
animation-duration: 10ms;
animation-delay: 0s;
animation-name: nodeInserted;
}
.nav.navbar-nav li:focus,
.nav.navbar-nav a:focus,
.navbar-header a:focus {
outline: none;
}
.nav-active {
background-color: rgb(40, 40, 40);
}
</style>
</head>
<body class="skin-3 no-skin">
<div id="navbar" class="navbar navbar-default ace-save-state navbar-fixed-top">
<div class="navbar-container ace-save-state" id="navbar-container">
<div class="navbar-header pull-left">
<a href="/main/index.html" class="navbar-brand">
<small>
<img width="26px" style="margin-top:-4px" src="/static/index/favicon.png">
{{config "String" "sys.name" ""}} Admin
</small>
</a>
</div>
<nav role="navigation" class="navbar-menu pull-left collapse navbar-collapse">
<ul class="nav navbar-nav">
{{range .navigators}}
<li>
<a href="javascript:void(0)">
<i class="ace-icon {{.icon}}"></i> {{.name}}
</a>
</li>
{{end}}
</ul>
</nav>
<div class="navbar-buttons navbar-header pull-right" role="navigation">
<ul class="nav ace-nav">
<li class="dark dropdown-modal">
<a data-toggle="dropdown" href="/static/index/#" class="dropdown-toggle">
<img class="nav-user-photo" src="/static/index/user.jpg" alt="Jason's Photo">
<span class="user-info">
<small>Welcome,</small>
{{.loginUser.given_name}}
</span>
<i class="ace-icon fa fa-caret-down"></i>
</a>
<ul class="user-menu dropdown-menu-right dropdown-menu dropdown-yellow dropdown-caret dropdown-close">
<li class="divider"></li>
<li>
<a href="/login/logout.html">
<i class="ace-icon fa fa-power-off"></i> Logout
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<!-- /.navbar-container -->
</div>
<div class="main-container ace-save-state" id="main-container">
<div id="sidebar" class="sidebar responsive ace-save-state sidebar-fixed sidebar-scroll" data-sidebar="true" data-sidebar-scroll="true" data-sidebar-hover="true">
{{$navigators := .navigators}} {{$menus := .menus}} {{$controllers := .controllers}} {{range $i,$nav := $navigators}}
<ul class="nav nav-list {{if eq $i 0}}showul{{end}}">
{{range $menu := $menus}} {{if eq $nav.privilege_id $menu.parent_id}}
<li class="">
<a href="#" class="dropdown-toggle">
<i class="menu-icon {{.icon}}"></i>
<span class="menu-text">
{{$menu.name}}
</span>
<b class="arrow fa fa-angle-down"></b>
</a>
<b class="arrow"></b>
<ul class="submenu">
{{range $controller := $controllers}} {{if eq $menu.privilege_id $controller.parent_id}}
<li class="">
<a href="/{{$controller.controller}}/{{$controller.action}}">
<i class="menu-icon fa fa-caret-right"></i> {{$controller.name}}
</a>
<b class="arrow"></b>
</li>
{{end}} {{end}}
</ul>
</li>
{{end}} {{end}}
</ul>
<!-- /.nav-list -->
{{end}}
<div class="sidebar-toggle sidebar-collapse" id="sidebar-collapse">
<i id="sidebar-toggle-icon" class="ace-icon fa fa-angle-double-left ace-save-state" data-icon1="ace-icon fa fa-angle-double-left" data-icon2="ace-icon fa fa-angle-double-right"></i>
</div>
</div>
<div class="main-content">
<div class="main-content-inner">
<div style="margin:0;background-color:white;" class="breadcrumbs ace-save-state breadcrumbs-fixed" id="breadcrumbs">
<ul class="breadcrumb" style="border-radius:4px;padding:8px;margin:8px 8px 0;background-color:rgb(231, 242, 248);width:100%;height:100%;">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="javascript:void(0)" class="navText">后台</a>
</li>
<li>
<a href="javascript:void(0)" class="menuText">首页</a>
</li>
<li class="active controllerText">Welcome</li>
</ul>
<!-- /.breadcrumb -->
</div>
<div class="page-content" style="padding-top: 43px;padding-bottom:0px;">
<div class="row">
<!--<div class="col-xs-12">-->
<!-- PAGE CONTENT BEGINS -->
<iframe id="main" name="main" src="/main/default.html" scrolling="yes" style="overflow-y:auto;border:none;" frameborder="yes" width="100%" height="100%"></iframe>
<!-- PAGE CONTENT ENDS -->
<!--</div>-->
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- /.page-content -->
</div>
</div>
<!-- /.main-content -->
<div class="footer">
<div class="footer-inner">
<div class="footer-content">
<span class="bigger-120">
<span class="blue bolder">{{config "String" "sys.name" ""}}</span> Application © 2017-{{.TimeNowYear}}
</span>
<!-- &nbsp; &nbsp; -->
<!-- <span class="action-buttons">
<a href="/static/index/#">
<i class="ace-icon fa fa-twitter-square light-blue bigger-150"></i>
</a>
<a href="/static/index/#">
<i class="ace-icon fa fa-facebook-square text-primary bigger-150"></i>
</a>
<a href="/static/index/#">
<i class="ace-icon fa fa-rss-square orange bigger-150"></i>
</a>
</span> -->
</div>
</div>
</div>
<a href="/static/index/#" id="btn-scroll-up" class="btn-scroll-up btn btn-sm btn-inverse">
<i class="ace-icon fa fa-angle-double-up icon-only bigger-110"></i>
</a>
</div>
<script src="/static/index/jquery-2.1.4.min.js"></script>
<script src="/static/index/bootstrap.min.js"></script>
<script src="/static/index/ace-elements.min.js"></script>
<script src="/static/index/ace.min.js"></script>
<script type="text/javascript">
$(function() {
var prefix = location.protocol + "//" + location.host
setInterval(function() {
var mainheight = $("#main").contents().find("body").height();
var minHeight = $(window).height() - 200
if (mainheight < minHeight) {
mainheight = minHeight
}
$("#main").height(mainheight);
}, 200);
$('ul.nav-list a').not('.dropdown-toggle').click(function() {
$("ul.nav-list li.active").removeClass('active');
$(this).parent().addClass("active");
var controller = $.trim($(this).text());
var menu = $.trim($(this).parent().parent().parent().find('>a').text());
var index = $('ul.nav-list').index($(this).parent().parent().parent().parent());
var nav = $.trim($('.navbar-menu a').eq(index).text());
if (nav && menu && controller) {
$('.navText').text(nav);
$('.menuText').text(menu);
$('.controllerText').text(controller);
$('.breadcrumbs').removeClass("hide").show()
}
location.href = '/main/index.html#' + (this.href + "").replace(prefix, "");
$('#main').attr('src', this.href);
return false
});
var hash = location.hash;
if (hash) {
var found = false
$('ul.nav-list a').not('.dropdown-toggle').each(function() {
if ((this.href + "").replace(prefix, "") == hash.substring(1)) {
if (found) {
return
}
var index = $('ul.nav-list').index($(this).parent().parent().parent().parent())
$(".sidebar ul.nav").removeClass("showul").eq(index).addClass("showul")
$(this).parent().parent().parent().find('>a').click();
$("ul.nav-list li.active").removeClass('active');
$(this).parent().addClass("active");
$(".navbar-menu>ul>li").eq(index).addClass('nav-active');
$(this).click();
found = true
}
});
}
$(".navbar-menu a").click(function(index) {
$(".navbar-menu>ul>li").not($(this).parent()).removeClass('nav-active');
$(this).parent().addClass('nav-active');
$(".sidebar ul.nav").removeClass("showul").eq($(".navbar-menu a").index(this)).addClass("showul")
});
});
setTimeout(function() {
$("#sidebar").addClass("sidebar-fixed");
$("#breadcrumbs").addClass("breadcrumbs-fixed");
}, 1000);
</script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
{{template "layout/default.html" .}}
+1
View File
@@ -0,0 +1 @@
{{template "layout/form.html" .}}
View File
+86
View File
@@ -0,0 +1,86 @@
<script src="/static/style/plugins/chart.js"></script>
<div class="panel panel-default container">
<h3>统计情况</h3>
<div class="row">
<div class="col-md-6">
<div style="width:300px;height:300px;">
<canvas id="online"></canvas>
</div>
</div>
<div class="col-md-6">
<div style="width:300px;height:300px;">
<canvas id="traffic"></canvas>
</div>
</div>
</div>
</div>
<script>
var onlineCfg = {
type: 'pie',
data: {
datasets: [{
data: [0, 0, 0, ],
backgroundColor: ["#999", "#123", "#456", ],
}],
labels: ["client", "server", "cluster", ]
},
options: {
title: {
display: true,
text: "在线情况"
},
responsive: true
}
};
var onlineChart = new Chart($("#online"), onlineCfg);
var trafficCfg = {
type: 'pie',
data: {
datasets: [{
data: [0, 0],
backgroundColor: ["#289", "#006621"],
}],
labels: ["已用流量", "剩余流量"]
},
options: {
responsive: true,
title: {
display: true,
text: "流量包使用情况"
},
tooltips: {
callbacks: {
label: function(tooltipItem, chart) {
console.log(tooltipItem, chart)
var label = chart.labels[tooltipItem.index]
return label + ":" + humanFileSize(chart.datasets[tooltipItem.datasetIndex].data[tooltipItem.index])
}
}
}
}
};
var trafficChart = new Chart($("#traffic"), trafficCfg);
$(function() {
var req = function() {
$.post("/main/default", {}, function(data) {
if (data.code) {
var online = data.data["online"]
onlineCfg.data.datasets[0].data = [online["client"], online["server"], online["cluster"]]
onlineChart.update();
var traffic = data.data["traffic"]
trafficCfg.data.datasets[0].data = [traffic["total_use"], traffic["total_left"]]
trafficChart.update();
setTimeout(req, 30000)
}
}, "json");
}
req();
});
function humanFileSize(size) {
var i = parseInt(Math.floor(Math.log(size) / Math.log(1024)));
i = size > 0 ? i : 0;
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i];
}
</script>
View File
+53
View File
@@ -0,0 +1,53 @@
<style>
.forx {
display: none;
}
</style>
{{$area:=.area}} {{$regions:=.regions}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/area/{{.action}}">
<input type="hidden" name="area_id" value="{{$area.area_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>区域名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$area.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>类型</label>
<div class="col-sm-4">
<select name="cs_type" class="form-control" default="{{$area.cs_type}}">
<option value="server">Server</option>
<option value="client">Client</option>
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>访问控制</label>
<div class="col-sm-4">
<select name="is_forbidden" class="form-control" default="{{$area.is_forbidden}}">
<option value="1">禁止</option>
<option value="0">允许</option>
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,67 @@
{{$areas:=.areas}}
<div class="panel panel-default">
<div class="panel-body">
<form action="/web/area/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-3">
<select class="form-control" name="type">
<option value="name">区域名称</option>
<option value="cs_type">类型[server|client]</option>
<option value="is_forbidden">访问控制[0禁止|1允许]</option>
</select>
</div>
<div class="col-md-3">
<input class="form-control" name="id" type="text" placeholder="">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w15p">区域名称</th>
<th class="center w10p">类型</th>
<th class="center w15p">访问控制</th>
<th class="center w15p">创建时间</th>
<th class="center w15p">更新时间</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $area := $areas}}
<tr>
<td class="center">{{$area.area_id}}</td>
<td class="center">{{$area.name}}</td>
<td class="center">{{$area.cs_type}}</td>
<td class="center">{{if eq $area.is_forbidden "1"}}
<span class="label label-warning">禁止</span> {{else}}
<span class="label label-success">允许</span> {{end}}
</td>
<td class="center">
{{dateFormat $area.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
{{dateFormat $area.update_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="edit" href="/web/area/edit?area_id={{$area.area_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/area/delete?area_id={{$area.area_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer">
</div>
</div>
+45
View File
@@ -0,0 +1,45 @@
<style>
.forx {
display: none;
}
</style>
{{$client:=.client}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/client/{{.action}}">
<input type="hidden" name="client_id" value="{{$client.client_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>Client名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$client.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>本地网络Host</label>
<div class="col-sm-4">
<input type="text" name="local_host" class="form-control" value="{{$client.local_host}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 需要暴露的Client所在网络的host,只要是Client可以访问的IP或者域名都可以</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>本地网络端口</label>
<div class="col-sm-4">
<input type="text" name="local_port" class="form-control" value="{{$client.local_port}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 对应上面的:本地网络Host,需要暴露的端口</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,76 @@
<style>
.token{word-break: break-all;}
</style>
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<i class="label label-default">类型</i>
<a class="label {{if ne $.userID `0`}}label-danger{{else}}label-default{{end}}" href="/web/client/list">用户</a>
<a class="label {{if eq $.userID `0`}}label-danger{{else}}label-default{{end}}" href="/web/client/list?user_id=0">系统</a>
</div>
{{if ne $.userID "0"}}
<div class="col-md-3 pull-right">
<form action="/web/client/list" method="get">
<div class="input-group">
<input class="form-control" name="user_id" type="text" placeholder="UserID">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</form>
</div>
{{end}}
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w10p">ID</th>
<th class="center w10p">名称</th>
<th class="center w20p">Token</th>
<th class="center w10p">{{if ne $.userID "0"}}UserID{{else}}类型{{end}}</th>
{{if eq $.userID "0"}}
<th class="center w15p">本地网络</th>
<th class="center w15p">创建时间</th>
<th class="center">操作</th>
{{else}}
<th class="center w15p">创建时间</th>
{{end}}
</tr>
</thead>
<tbody>
{{range $client := .clients}}
<tr>
<td class="center">{{$client.client_id}}</td>
<td class="center">{{$client.name}}</td>
<td class="center token">{{$client.token}}</td>
<td class="center">
{{if eq $client.user_id "0"}} <span class="label label-info">系统</span> {{else}} {{$client.user_id}} {{end}}
</td>
{{if eq $client.user_id "0"}}
<td class="center">{{$client.local_host}}:{{$client.local_port}}</td>
<td class="center">
{{dateFormat $client.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="edit" href="/web/client/edit?client_id={{$client.client_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a data-toggle="confirmation" data-placement="left" data-title="确定重置token吗?(Client会被下线)" href="/web/client/reset?client_id={{$client.client_id}}"><i class="glyphicon glyphicon-refresh"> </i>重置</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/client/delete?client_id={{$client.client_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
{{else}}
<td class="center">
{{dateFormat $client.create_time "Y-m-d H:i:s"}}
</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
+51
View File
@@ -0,0 +1,51 @@
<style>
.forx {
display: none;
}
</style>
{{$cluster:=.cluster}} {{$regions:=.regions}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/cluster/{{.action}}">
<input type="hidden" name="cluster_id" value="{{$cluster.cluster_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$cluster.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>IP(内)</label>
<div class="col-sm-4">
<input type="text" name="ip" class="form-control" value="{{$cluster.ip}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>区域</label>
<div class="col-sm-4">
<select name="region_id" class="form-control" default="{{$cluster.region_id}}">
<option value="">请选择区域</option>
{{range $k,$item := $regions}}
<option value="{{$item.region_id}}">{{$item.parent}} -> {{$item.name}}</option>
{{end}}
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,55 @@
{{$regions:=.regions}} {{$clusters:=.clusters}}
<div class="panel panel-default">
<div class="panel-body">
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w15p">区域</th>
<th class="center w10p">名称</th>
<th class="center w10p">IP</th>
<th class="center w8p">SYSCONN</th>
<th class="center w5p">CONN</th>
<th class="center w7p">网速</th>
<th class="center w15p">更新时间</th>
<th class="center w5p">状态</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $k,$region:=$regions}} {{range $cluster := $clusters}} {{if eq $region.region_id $cluster.region_id}}
<tr>
<td class="center">{{$cluster.cluster_id}}</td>
<td class="center">
{{$region.parent}}:{{$region.name}}
</td>
<td class="center">{{$cluster.name}}</td>
<td class="center">{{$cluster.ip}}</td>
<td class="center">{{$cluster.system_conn_number}}</td>
<td class="center">{{$cluster.tunnel_conn_number}}</td>
<td class="center">{{$cluster.bandwidth_human}}/s</td>
<td class="center">
{{dateFormat $cluster.update_time "Y-m-d H:i:s"}}
</td>
<td class="center">
{{if eq $cluster.is_disable "0"}}
<span class="label label-success">正常</span> {{else}}
<span class="label label-danger">禁用</span> {{end}}
</td>
<td class="center">
<a name="edit" href="/web/cluster/statistic?cluster_id={{$cluster.cluster_id}}"><i class="glyphicon glyphicon-pencil"> </i>统计</a>
<a name="edit" href="/web/cluster/edit?cluster_id={{$cluster.cluster_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/cluster/delete?cluster_id={{$cluster.cluster_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a> {{if eq $cluster.is_disable "0"}}
<a name="forbidden" data-toggle="confirmation" data-placement="left" data-title="确定禁用吗?" href="/web/cluster/forbidden?cluster_id={{$cluster.cluster_id}}"><i class="fa fa-ban"> </i>禁用</a> {{else}}
<a name="review" data-toggle="confirmation" data-placement="left" data-title="确定恢复吗?" href="/web/cluster/review?cluster_id={{$cluster.cluster_id}}"><i class="glyphicon glyphicon-ok"> </i>恢复</a> {{end}}
</td>
</tr>
{{end}} {{end}} {{end}}
</tbody>
</table>
</div>
<div class="panel-footer">
</div>
</div>
@@ -0,0 +1,429 @@
<table class="table table-bordered table-hover">
<caption class="h3">系统全局统计</caption>
<thead>
<tr>
<th class="center w15p">系统连接数</th>
<th class="center w15p">Cluster连接数</th>
<th class="center w15p">网速</th>
<th class="center w15p">更新时间</th>
</tr>
</thead>
<tbody class="system">
</tbody>
</table>
<table class="table table-bordered table-hover">
<caption class="h3">隧道全局统计</caption>
<thead>
<tr>
<th class="center w5p">Tunnels</th>
<th class="center w5p">Servers</th>
<th class="center w5p">Clients</th>
<th class="center w10p">Connections</th>
<th class="center w20p">Upload</th>
<th class="center w20p">Download</th>
<th class="center w20p">Total</th>
<th class="center w15p">更新时间</th>
</tr>
</thead>
<tbody class="total">
</tbody>
</table>
<table class="table table-bordered table-hover">
<caption class="h3">隧道统计<small>速度排列,前20个</small></caption>
<thead>
<tr>
<th class="center w8p">TunnelID</th>
<th class="center w10p">Connections</th>
<th class="center w20p">Upload</th>
<th class="center w25p">Download</th>
<th class="center w25p">Total</th>
<th class="center w12p">Limit</th>
<th class="center w15p">更新时间</th>
</tr>
</thead>
<tbody class="list">
</tbody>
</table>
<table class="table table-bordered table-hover">
<caption class="h3">隧道统计<small>连接数排列,前20个</small></caption>
<thead>
<tr>
<th class="center w8p">TunnelID</th>
<th class="center w10p">Connections</th>
<th class="center w20p">Upload</th>
<th class="center w25p">Download</th>
<th class="center w25p">Total</th>
<th class="center w12p">Limit</th>
<th class="center w15p">更新时间</th>
</tr>
</thead>
<tbody class="list-conn">
</tbody>
</table>
<script>
var clasterID = '{{$.clusterID}}';
$(function() {
var req = function(lastData) {
$.post("/web/cluster/statistic", {
cluster_id: clasterID
}, function(data) {
if (!data.code) {
return
}
data = data.data
if (!lastData) {
lastData = data
}
systemRender(lastData, data);
totalRender(lastData, data);
listRender(lastData, data,false);
listRender(lastData, data,true);
setTimeout(function() {
req(data)
}, interval * 1000)
}, "json");
}
req(null);
});
var interval = 3
var listShowLimit = 20
function totalRender(lastData, data) {
var total = data.Total.UploadBytes + data.Total.DownloadBytes;
var lastTotal = lastData.Total.UploadBytes + lastData.Total.DownloadBytes;
var lastUploadBytes = lastData.Total.UploadBytes
var lastDownloadBytes = lastData.Total.DownloadBytes
var uploadSpeed = humanFileSize(parseInt((data.Total.UploadBytes - lastUploadBytes) / interval)) + "/s"
var downloadSpeed = humanFileSize(parseInt((data.Total.DownloadBytes - lastDownloadBytes) / interval)) + "/s"
var totalSpeed = humanFileSize(parseInt((data.Total.UploadBytes + data.Total.DownloadBytes - lastTotal) / interval)) + "/s"
var html = '<tr>\
<td class="center">' + data.Total.Tunnels + '</td>\
<td class="center">' + data.Total.Servers + '</td>\
<td class="center">' + data.Total.Clients + '</td>\
<td class="center">' + data.Total.Connections + '</td>\
<td class="center">' + data.Total.UploadBytes + " / " + humanFileSize(data.Total.UploadBytes) + " (" + uploadSpeed + ')</td>\
<td class="center">' + data.Total.DownloadBytes + " / " + humanFileSize(data.Total.DownloadBytes) + " (" + downloadSpeed + ')</td>\
<td class="center">' + total + " / " + humanFileSize(total) + " (" + totalSpeed + ')</td>\
<td class="center">' + date("Y/m/d H:i:s", new Date().getTime() / 1000) + '</td>\
</tr>';
$(".total").html(html);
}
function listRender(lastData, data,isConn) {
if(isConn){
$(".list-conn").html("");
}else{
$(".list").html("");
}
var html = "";
var list = sortMap(lastData.Traffic, data.Traffic, listShowLimit,isConn)
for (var i = 0; i < list.length; i++) {
var TunnelID = list[i]["TunnelID"]
var item = data.Traffic[TunnelID]
var lastItem = lastData.Traffic[TunnelID]
if (!lastItem) {
continue;
}
var total = item["positive"] + item["negative"];
var lastTotal = lastItem["positive"] + lastItem["negative"];
var lastUploadBytes = lastItem["positive"];
var lastDownloadBytes = lastItem["negative"];
var uploadSpeed = humanFileSize(parseInt((item["positive"] - lastUploadBytes) / interval));
var downloadSpeed = humanFileSize(parseInt((item["negative"] - lastDownloadBytes) / interval));
var totalSpeed = humanFileSize(parseInt((total - lastTotal) / interval));
var conns = item["connections"];
html += '<tr>\
<td class="center">' + TunnelID + '</td>\
<td class="center">' + conns + '</td>\
<td class="center">' + item["positive"] + " / " + humanFileSize(item["positive"]) + " (" + uploadSpeed + '/s)</td>\
<td class="center">' + item["negative"] + " / " + humanFileSize(item["negative"]) + " (" + downloadSpeed + '/s)</td>\
<td class="center">' + total + " / " + humanFileSize(total) + " (" + totalSpeed + '/s)</td>\
<td class="center">' + item["limit"] + " / " + humanFileSize(item["limit"]) + '</td>\
<td class="center">' + date("Y/m/d H:i:s", new Date().getTime() / 1000) + '</td>\
</tr>';
}
if (html) {
if(isConn){
$(".list-conn").html(html);
}else{
$(".list").html(html);
}
}
}
function systemRender(lastData, data) {
var sysconn = data.cluster.system_conn_number;
var clusterconn = data.cluster.tunnel_conn_number;
var speed = humanFileSize(parseInt(data.cluster.bandwidth)) + "/s"
var updatetime = data.cluster.update_time;
var html = '<tr>\
<td class="center">' + sysconn + '</td>\
<td class="center">' + clusterconn + '</td>\
<td class="center">' + speed + '</td>\
<td class="center">' + date("Y/m/d H:i:s", updatetime) + '</td>\
</tr>';
$(".system").html(html);
}
function humanFileSize(size) {
var i = parseInt(Math.floor(Math.log(size) / Math.log(1024)));
i = size > 0 ? i : 0;
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i];
}
function sortMap(lastTraffic, traffic, limit,isConn) {
var TunnelIDMap = {}
var totalMap = {}
for (var TunnelID in traffic) {
if (!traffic.hasOwnProperty(TunnelID)) {
continue;
}
var total =0
var p = 0
if (lastTraffic[TunnelID]) {
if(!isConn){
p = lastTraffic[TunnelID]["positive"] + lastTraffic[TunnelID]["negative"];
total= traffic[TunnelID]["positive"] + traffic[TunnelID]["negative"] - p;
}else{
total=traffic[TunnelID]["connections"];
}
}
TunnelIDMap[TunnelID] = total
totalMap[total] = true
}
var totalArr = []
for (var total in totalMap) {
if (!totalMap.hasOwnProperty(total)) {
continue;
}
totalArr.push(total)
}
totalArr = totalArr.sort(function(a, b) {
return b - a;
});
var newTraffic = []
for (var i = 0; i < totalArr.length; i++) {
for (var TunnelID in TunnelIDMap) {
if (!TunnelIDMap.hasOwnProperty(TunnelID)) {
continue;
}
if (totalArr[i] == TunnelIDMap[TunnelID]) {
var item = traffic[TunnelID]
item["TunnelID"] = TunnelID
newTraffic.push(item)
}
}
}
return newTraffic.slice(0, limit)
}
function date(format, timestamp) {
var that = this;
var jsdate, f;
// Keep this here (works, but for code commented-out below for file size reasons)
// var tal= [];
var txt_words = [
'Sun', 'Mon', 'Tues', 'Wednes', 'Thurs', 'Fri', 'Satur',
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
// trailing backslash -> (dropped)
// a backslash followed by any character (including backslash) -> the character
// empty string -> empty string
var formatChr = /\\?(.?)/gi;
var formatChrCb = function(t, s) {
return f[t] ? f[t]() : s;
};
var _pad = function(n, c) {
n = String(n);
while (n.length < c) {
n = '0' + n;
}
return n;
};
f = {
// Day
d: function() { // Day of month w/leading 0; 01..31
return _pad(f.j(), 2);
},
D: function() { // Shorthand day name; Mon...Sun
return f.l()
.slice(0, 3);
},
j: function() { // Day of month; 1..31
return jsdate.getDate();
},
l: function() { // Full day name; Monday...Sunday
return txt_words[f.w()] + 'day';
},
N: function() { // ISO-8601 day of week; 1[Mon]..7[Sun]
return f.w() || 7;
},
S: function() { // Ordinal suffix for day of month; st, nd, rd, th
var j = f.j();
var i = j % 10;
if (i <= 3 && parseInt((j % 100) / 10, 10) == 1) {
i = 0;
}
return ['st', 'nd', 'rd'][i - 1] || 'th';
},
w: function() { // Day of week; 0[Sun]..6[Sat]
return jsdate.getDay();
},
z: function() { // Day of year; 0..365
var a = new Date(f.Y(), f.n() - 1, f.j());
var b = new Date(f.Y(), 0, 1);
return Math.round((a - b) / 864e5);
},
// Week
W: function() { // ISO-8601 week number
var a = new Date(f.Y(), f.n() - 1, f.j() - f.N() + 3);
var b = new Date(a.getFullYear(), 0, 4);
return _pad(1 + Math.round((a - b) / 864e5 / 7), 2);
},
// Month
F: function() { // Full month name; January...December
return txt_words[6 + f.n()];
},
m: function() { // Month w/leading 0; 01...12
return _pad(f.n(), 2);
},
M: function() { // Shorthand month name; Jan...Dec
return f.F()
.slice(0, 3);
},
n: function() { // Month; 1...12
return jsdate.getMonth() + 1;
},
t: function() { // Days in month; 28...31
return (new Date(f.Y(), f.n(), 0))
.getDate();
},
// Year
L: function() { // Is leap year?; 0 or 1
var j = f.Y();
return j % 4 === 0 & j % 100 !== 0 | j % 400 === 0;
},
o: function() { // ISO-8601 year
var n = f.n();
var W = f.W();
var Y = f.Y();
return Y + (n === 12 && W < 9 ? 1 : n === 1 && W > 9 ? -1 : 0);
},
Y: function() { // Full year; e.g. 1980...2010
return jsdate.getFullYear();
},
y: function() { // Last two digits of year; 00...99
return f.Y()
.toString()
.slice(-2);
},
// Time
a: function() { // am or pm
return jsdate.getHours() > 11 ? 'pm' : 'am';
},
A: function() { // AM or PM
return f.a()
.toUpperCase();
},
B: function() { // Swatch Internet time; 000..999
var H = jsdate.getUTCHours() * 36e2;
// Hours
var i = jsdate.getUTCMinutes() * 60;
// Minutes
var s = jsdate.getUTCSeconds(); // Seconds
return _pad(Math.floor((H + i + s + 36e2) / 86.4) % 1e3, 3);
},
g: function() { // 12-Hours; 1..12
return f.G() % 12 || 12;
},
G: function() { // 24-Hours; 0..23
return jsdate.getHours();
},
h: function() { // 12-Hours w/leading 0; 01..12
return _pad(f.g(), 2);
},
H: function() { // 24-Hours w/leading 0; 00..23
return _pad(f.G(), 2);
},
i: function() { // Minutes w/leading 0; 00..59
return _pad(jsdate.getMinutes(), 2);
},
s: function() { // Seconds w/leading 0; 00..59
return _pad(jsdate.getSeconds(), 2);
},
u: function() { // Microseconds; 000000-999000
return _pad(jsdate.getMilliseconds() * 1000, 6);
},
// Timezone
e: function() { // Timezone identifier; e.g. Atlantic/Azores, ...
// The following works, but requires inclusion of the very large
// timezone_abbreviations_list() function.
/* return that.date_default_timezone_get();
*/
throw 'Not supported (see source code of date() for timezone on how to add support)';
},
I: function() { // DST observed?; 0 or 1
// Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
// If they are not equal, then DST is observed.
var a = new Date(f.Y(), 0);
// Jan 1
var c = Date.UTC(f.Y(), 0);
// Jan 1 UTC
var b = new Date(f.Y(), 6);
// Jul 1
var d = Date.UTC(f.Y(), 6); // Jul 1 UTC
return ((a - c) !== (b - d)) ? 1 : 0;
},
O: function() { // Difference to GMT in hour format; e.g. +0200
var tzo = jsdate.getTimezoneOffset();
var a = Math.abs(tzo);
return (tzo > 0 ? '-' : '+') + _pad(Math.floor(a / 60) * 100 + a % 60, 4);
},
P: function() { // Difference to GMT w/colon; e.g. +02:00
var O = f.O();
return (O.substr(0, 3) + ':' + O.substr(3, 2));
},
T: function() {
return 'PRC';
},
Z: function() { // Timezone offset in seconds (-43200...50400)
return -jsdate.getTimezoneOffset() * 60;
},
// Full Date/Time
c: function() { // ISO-8601 date.
return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb);
},
r: function() { // RFC 2822
return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb);
},
U: function() { // Seconds since UNIX epoch
return jsdate / 1000 | 0;
}
};
this.date = function(format, timestamp) {
that = this;
jsdate = (timestamp === undefined ? new Date() : // Not provided
(timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
);
return format.replace(formatChr, formatChrCb);
};
return this.date(format, timestamp);
}
</script>
@@ -0,0 +1,71 @@
<style>
.btn-warning,
.label-warning,
.progress-bar-warning {
color: white;
}
</style>
{{$conns:=.conns}}
<div class="panel panel-default" style="margin-bottom:80px;">
<form action="/web/conn/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-2">
</div>
<div class="col-md-2">
<select class="form-control" name="col">
<option value="user_id">UserID</option>
<option value="cluster_id">ClusterID</option>
<option value="server_id">ServerID</option>
</select>
</div>
<div class="col-md-2">
<input class="form-control" name="keyword" type="text" placeholder="">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">隧道ID</th>
<th class="center w10p">ClusterID</th>
<th class="center w5p">ServerID</th>
<th class="center w5p">UserID</th>
<th class="center w5p"><a href="?orderby=count&col={{$.col}}&keyword={{$.keyword}}&page={{$.p}}" title="连接数倒序排序">连接数</a></th>
<th class="center w10p"><a href="?orderby=upload&col={{$.col}}&keyword={{$.keyword}}&page={{$.p}}" title="上传速度倒序排序">上传速度</a></th>
<th class="center w10p"><a href="?orderby=download&col={{$.col}}&keyword={{$.keyword}}&page={{$.p}}" title="下载速度倒序排序">下载速度</a></th>
<th class="center w10p"><a href="?orderby=update_time&col={{$.col}}&keyword={{$.keyword}}&page={{$.p}}" title="更新时间倒序排序">更新时间</a></th>
</tr>
</thead>
<tbody>
{{range $conn := .conns}}
<tr>
<td class="center">{{$conn.tunnel_id}}</td>
<td class="center">{{$conn.cluster_id}}</td>
<td class="center">{{$conn.server_id}}</td>
<td class="center">{{$conn.user_id}}</td>
<td class="center">{{$conn.count}}</td>
<td class="center">{{$conn.upload}}</td>
<td class="center">{{$conn.download}}</td>
<td class="center">
{{dateFormat $conn.update_time "Y-m-d H:i:s"}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
+53
View File
@@ -0,0 +1,53 @@
<style>
.forx {
display: none;
}
</style>
{{$ip_list:=.ip_list}} {{$regions:=.regions}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/IpList/{{.action}}">
<input type="hidden" name="ip_list_id" value="{{$ip_list.ip_list_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>IP</label>
<div class="col-sm-4">
<input type="text" name="ip" class="form-control" value="{{$ip_list.ip}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>类型</label>
<div class="col-sm-4">
<select name="cs_type" class="form-control" default="{{$ip_list.cs_type}}">
<option value="server">Server</option>
<option value="client">Client</option>
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>访问控制</label>
<div class="col-sm-4">
<select name="is_forbidden" class="form-control" default="{{$ip_list.is_forbidden}}">
<option value="1">禁止</option>
<option value="0">允许</option>
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,67 @@
{{$ip_lists:=.ip_lists}}
<div class="panel panel-default">
<div class="panel-body">
<form action="/web/IpList/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-3">
<select class="form-control" name="type">
<option value="ip">IP</option>
<option value="cs_type">类型[server|client]</option>
<option value="is_forbidden">访问控制[0禁止|1允许]</option>
</select>
</div>
<div class="col-md-3">
<input class="form-control" name="id" type="text" placeholder="">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w15p">IP</th>
<th class="center w10p">类型</th>
<th class="center w15p">访问控制</th>
<th class="center w15p">创建时间</th>
<th class="center w15p">更新时间</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $ip_list := $ip_lists}}
<tr>
<td class="center">{{$ip_list.ip_list_id}}</td>
<td class="center">{{$ip_list.ip}}</td>
<td class="center">{{$ip_list.cs_type}}</td>
<td class="center">{{if eq $ip_list.is_forbidden "1"}}
<span class="label label-warning">禁止</span> {{else}}
<span class="label label-success">允许</span> {{end}}
</td>
<td class="center">
{{dateFormat $ip_list.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
{{dateFormat $ip_list.update_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="edit" href="/web/IpList/edit?ip_list_id={{$ip_list.ip_list_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/IpList/delete?ip_list_id={{$ip_list.ip_list_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer">
</div>
</div>
@@ -0,0 +1,65 @@
{{$onlines:=.onlines}}
<div class="panel panel-default" style="margin-bottom:80px;">
<form action="/web/online/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-2">
<input class="form-control" name="cs" type="hidden" value="{{.cs}}">
</div>
<div class="col-md-2">
<select class="form-control" name="type">
<option value="cluster_id">ClusterID</option>
<option value="user_id">UserID</option>
<option value="cs_id">{{if eq $.cs "server"}}ServerID{{else}}ClientID{{end}}</option>
<option value="cs_ip">上线IP</option>
</select>
</div>
<div class="col-md-2">
<input class="form-control" name="keyword" type="text" placeholder="">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w5p">UserID</th>
<th class="center w5p">ClusterID</th>
<th class="center w5p">上线IP</th>
<th class="center w10p">{{if eq $.cs "server"}}ServerID{{else}}ClientID{{end}}</th>
<!-- <th class="center w10p">类型</th> -->
<th class="center w15p">上线时间</th>
</tr>
</thead>
<tbody>
{{range $online := .onlines}}
<tr>
<td class="center">{{$online.online_id}}</td>
<td class="center">{{$online.user_id}}{{if eq $online.user_id "0"}}<small> (系统)</small>{{end}}</td>
<td class="center">{{$online.cluster_id}}</td>
<td class="center">{{$online.cs_ip}}</td>
<td class="center">{{$online.cs_id}}</td>
<!-- <td class="center">{{$online.cs_type}}</td> -->
<td class="center">
{{dateFormat $online.create_time "Y-m-d H:i:s"}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
+86
View File
@@ -0,0 +1,86 @@
<style>
.forx {
display: none;
}
</style>
{{$package:=.package}}
<div class="panel panel-default" style="padding-bottom:200px;">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/package/{{.action}}">
<input type="hidden" name="package_id" value="{{$package.package_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>流量包来源</label>
<div class="col-sm-4">
<input type="text" name="comment" class="form-control" value="{{$package.comment}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在10个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>UserID</label>
<div class="col-sm-4">
<input type="text" name="user_id" class="form-control" value="{{$package.user_id}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>流量剩余字节数</label>
<div class="col-sm-4">
<input type="text" name="bytes_left" class="form-control bytes_left" value="{{$package.bytes_left}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * <span class="bytes_left_human"></span></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>生效时间</label>
<div class="col-sm-4">
<input type="text" name="start_time" class="form-control datepicker" value='{{if $package.start_time}}{{dateFormat $package.start_time "Y-m-d"}}{{end}}'>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>失效时间</label>
<div class="col-sm-4">
<input type="text" name="end_time" class="form-control datepicker" value='{{if $package.end_time}}{{dateFormat $package.end_time "Y-m-d"}}{{end}}'>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script>
$(function() {
$('.datepicker').datepicker({
language: 'zh-CN',
format: 'yyyy-mm-dd',
});
var format = function() {
var val = $(".bytes_left").val();
$(".bytes_left_human").html(humanFileSize(val ? parseInt(val) : 0));
};
$(".bytes_left").keyup(function() {
format();
});
format();
});
function humanFileSize(size) {
var i = parseInt(Math.floor(Math.log(size) / Math.log(1024)));
i = size > 0 ? i : 0;
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i];
}
</script>
@@ -0,0 +1,78 @@
<style>
.btn-warning,
.label-warning,
.progress-bar-warning {
color: white;
}
</style>
{{$packages:=.packages}}
<div class="panel panel-default" style="margin-bottom:80px;">
<form action="/web/package/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-2">
</div>
<div class="col-md-2">
</div>
<div class="col-md-2">
<input class="form-control" name="keyword" type="text" placeholder="UserID">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w5p">UserID</th>
<th class="center w10p">流量包大小</th>
<th class="center w5p">已用流量</th>
<th class="center w5p">状态</th>
<th class="center w10p">生效时间</th>
<th class="center w10p">过期时间</th>
<th class="center w10p">更新时间</th>
<th class="center w5p">获取方式</th>
<th class="center w5p">操作</th>
</tr>
</thead>
<tbody>
{{range $package := .packages}}
<tr>
<td class="center">{{$package.package_id}}</td>
<td class="center">{{$package.user_id}}</td>
<td class="center">{{$package.bytes_total_human}}</td>
<td class="center">{{$package.bytes_use_human}}</td>
<td class="center">{{str2html $package.status}}</td>
<td class="center">
{{dateFormat $package.start_time "Y-m-d H:i:s"}}
</td>
<td class="center">
{{dateFormat $package.end_time "Y-m-d H:i:s"}}
</td>
<td class="center">
{{dateFormat $package.update_time "Y-m-d H:i:s"}}
</td>
<td class="center">{{$package.comment}}</td>
<td class="center">
<a name="edit" href="/web/package/edit?package_id={{$package.package_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<!-- <a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/package/delete?package_id={{$package.package_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a> -->
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
+48
View File
@@ -0,0 +1,48 @@
<style>
.forx {
display: none;
}
</style>
{{$region:=.region}} {{$regions:=.regions}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/region/{{.action}}">
<input type="hidden" name="region_id" value="{{$region.region_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>区域名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$region.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>上级区域</label>
<div class="col-sm-4">
<select name="parent_id" class="form-control" default="{{$region.parent_id}}">
<option value="0">顶级区域</option>
{{range $k,$item := $regions}}
{{if $region.region_id}}
{{if ne $item.region_id $region.region_id}}
<option value="{{$item.region_id}}">{{$item.name}}</option>
{{end}}
{{else}}
<option value="{{$item.region_id}}">{{$item.name}}</option>
{{end}}
{{end}}
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,14 @@
{{$regions:=.regions}}
<div class="panel panel-default" style="margin-bottom:80px;">
<div class="panel-body">
{{range $region := $regions}} {{if eq $region.parent_id "0"}}
<ul class="list-group w240 inline-group">
<li class="list-group-item"><strong>{{$region.name}}</strong> <span class="pull-right"><a name='edit' href='/web/region/edit?region_id={{$region.region_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/web/region/delete?region_id={{$region.region_id}}'></a></span></li>
{{range $item := $regions}} {{if eq $item.parent_id $region.region_id}}
<li class="list-group-item">{{$item.name}}<span class="pull-right"><a name='edit' href='/web/region/edit?region_id={{$item.region_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/web/region/delete?region_id={{$item.region_id}}'></a></span></li>
{{end}} {{end}}
</ul>
{{end}} {{end}}
</div>
</div>
+91
View File
@@ -0,0 +1,91 @@
<style>
.forx {
display: none;
}
</style>
{{$role:=.role}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/role/{{.action}}">
<input type="hidden" name="role_id" value="{{$role.role_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>角色名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$role.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>带宽</label>
<div class="col-sm-4">
<input type="text" name="bandwidth" class="form-control bytes_limit" value="{{$role.bandwidth}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 带宽限制,单位字节byte,0不限制</span>
<br>
<span class="bytes_limit_human"></span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"><span class="text-danger"> * </span>Server登录区域</label>
<div class="col-md-4">
<select class="form-control" name="server_area" size="1" default="{{$role.server_area}}">
<option value="all">全部</option>
<option value="china">中国</option>
<option value="foreign">国外</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"><span class="text-danger"> * </span>Client登录区域</label>
<div class="col-md-4">
<select id="roles_selector" class="form-control" name="client_area" size="1" default="{{$role.client_area}}">
<option value="all">全部</option>
<option value="china">中国</option>
<option value="foreign">国外</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"><span class="text-danger"> * </span>隧道模式</label>
<div class="col-md-4">
<label for="tunnel_mode01">普通
<input {{if $.tunnel_mode_arr}} {{range $v:=$.tunnel_mode_arr}} {{if eq $v "0"}}checked{{end}} {{end}} {{end}} type="checkbox" id="tunnel_mode01" value="0" class="form-control" name="tunnel_mode">
</label>&nbsp;&nbsp;&nbsp;&nbsp;
<label for="tunnel_mode02">特殊
<input {{if $.tunnel_mode_arr}} {{range $v:=$.tunnel_mode_arr}} {{if eq $v "2"}}checked{{end}} {{end}} {{end}} type="checkbox" id="tunnel_mode02" value="2" class="form-control" name="tunnel_mode">
</label>&nbsp;&nbsp;&nbsp;&nbsp;
<label for="tunnel_mode03">高级
<input {{if $.tunnel_mode_arr}} {{range $v:=$.tunnel_mode_arr}} {{if eq $v "1"}}checked{{end}} {{end}} {{end}} type="checkbox" id="tunnel_mode03" value="1" class="form-control" name="tunnel_mode">
</label>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script>
$(function() {
var format = function() {
var val = $(".bytes_limit").val();
$(".bytes_limit_human").html(humanFileSize(val ? parseInt(val) : 0));
};
$(".bytes_limit").keyup(function() {
format();
});
format();
});
function humanFileSize(size) {
var i = parseInt(Math.floor(Math.log(size) / Math.log(1024)));
i = size > 0 ? i : 0;
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i];
}
</script>
@@ -0,0 +1,48 @@
<div class="panel panel-default">
<div class="panel-body">
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w10p">ID</th>
<th class="center w10p">角色名</th>
<th class="center w10p">带宽限制</th>
<th class="center w10p">Server区域</th>
<th class="center w10p">Client区域</th>
<th class="center w10p">隧道模式</th>
<th class="center w15p">创建时间</th>
<th class="center w20p">操作</th>
</tr>
</thead>
<tbody>
{{range $role := .roles}}
<tr>
<td class="center">{{$role.role_id}}</td>
<td class="center">{{$role.name}}</td>
<td class="center">{{$role.bandwidth_human}}</td>
<td class="center">
{{if eq $role.server_area "china"}}中国{{end}} {{if eq $role.server_area "foreign"}}国外{{end}} {{if eq $role.server_area "all"}}全部{{end}}
</td>
<td class="center">
{{if eq $role.client_area "china"}}中国{{end}} {{if eq $role.client_area "foreign"}}国外{{end}} {{if eq $role.client_area "all"}}全部{{end}}
</td>
<td class="center">
{{$role.tunnel_mode_human}}
</td>
<td class="center">
{{dateFormat $role.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="privilege" href="/web/role/regions?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-lock"> </i>区域</a>
<a name="edit" href="/web/role/edit?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/role/delete?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer">
</div>
</div>
@@ -0,0 +1,44 @@
<script src="/static/style/plugins/jquery.form.min.js"></script>
<script src="/static/style/common/form.js"></script>
{{$regions:=.regions}}
<form method="post" class="ajaxform" action="/web/role/regions" ajaxPre="checkForm">
<input type="hidden" name="role_id" value="{{.roleId}}">
<div class="panel panel-default" style="margin-bottom:80px;">
<div class="panel-body">
{{range $region := $regions}} {{if eq $region.parent_id "0"}}
<ul class="list-group w240 inline-group">
<li class="list-group-item">
<strong><input type="checkbox" class="region-group" style="margin-bottom:3px;" />
{{$region.name}}</strong> <span class="pull-right"><a name='edit' href='/web/region/edit?region_id={{$region.region_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/web/region/delete?region_id={{$region.region_id}}'></a></span>
</li>
{{range $item := $regions}} {{if eq $item.parent_id $region.region_id}}
<li class="list-group-item">
<input type="checkbox" class="region-id" {{range $k,$v:=$.regionIds}} {{if eq $v $item.region_id}}checked="checked" {{end}} {{end}} name="region-ids" style="margin-bottom:3px;" value="{{$item.region_id}}" /> {{$item.name}}
<span class="pull-right"><a name='edit' href='/web/region/edit?region_id={{$item.region_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/web/region/delete?region_id={{$item.region_id}}'></a></span></li>
{{end}} {{end}}
</ul>
{{end}} {{end}}
<div style="width:90%;text-align:center;"><input type="submit" class="btn btn-primary save" value="保存"></div>
</div>
</div>
</form>
<script>
function checkForm() {
var regionIds = [];
$(".region-id").each(function() {
if (this.checked) {
regionIds.push(this.value)
}
});
return true
}
$(".region-group").click(function() {
$(this).parent().parent().parent().find(".region-id").prop("checked", this.checked);
});
$(".region-group").each(function() {
if ($(this).parent().parent().parent().find(".region-id").not(":checked").length == 0) {
$(this).prop("checked", true)
}
});
</script>
+36
View File
@@ -0,0 +1,36 @@
<style>
.forx {
display: none;
}
</style>
{{$server:=.server}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/server/{{.action}}">
<input type="hidden" name="server_id" value="{{$server.server_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>Server名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$server.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<!-- <div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>UserID</label>
<div class="col-sm-4">
<input type="text" name="user_id" class="form-control" value="{{$server.user_id}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 留空为系统Server</span>
</div>
</div> -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
@@ -0,0 +1,66 @@
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<i class="label label-default">类型</i>
<a class="label {{if ne $.userID `0`}}label-danger{{else}}label-default{{end}}" href="/web/server/list">用户</a>
<a class="label {{if eq $.userID `0`}}label-danger{{else}}label-default{{end}}" href="/web/server/list?user_id=0">系统</a>
</div>
{{if ne $.userID "0"}}
<div class="col-md-3 pull-right">
<form action="/web/server/list" method="get">
<div class="input-group">
<input class="form-control" name="user_id" type="text" placeholder="UserID">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</form>
</div>
{{end}}
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w10p">ID</th>
<th class="center w15p">名称</th>
<th class="center w30p">Token</th>
<th class="center w10p">UserID</th>
<th class="center w15p">创建时间</th>
{{if eq $.userID "0"}}
<th class="center">操作</th>
{{end}}
</tr>
</thead>
<tbody>
{{range $server := .servers}}
<tr>
<td class="center">{{$server.server_id}}</td>
<td class="center">{{$server.name}}</td>
<td class="center">{{$server.token}}</td>
<td class="center">
{{if eq $server.user_id "0"}} <span class="label label-info">系统</span> {{else}} {{$server.user_id}} {{end}}
</td>
<td class="center">
{{dateFormat $server.create_time "Y-m-d H:i:s"}}
</td>
{{if eq $server.user_id "0"}}
<td class="center">
<a name="edit" href="/web/server/edit?server_id={{$server.server_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a data-toggle="confirmation" data-placement="left" data-title="确定重置token吗?(Server会被下线)" href="/web/server/reset?server_id={{$server.server_id}}"><i class="glyphicon glyphicon-refresh"> </i>重置</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/web/server/delete?server_id={{$server.server_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
@@ -0,0 +1,67 @@
{{$tunnels:=.tunnels}}
<div class="panel panel-default" style="margin-bottom:80px;">
<form action="/web/tunnel/list" method="get">
<div class="panel-body">
<div class="row">
<div class="col-md-5"></div>
<div class="col-md-3">
<select class="form-control" name="type">
<option value="user_id">用户ID</option>
<option value="server_id">ServerID</option>
<option value="client_id">ClientID</option>
<option value="cluster_id">ClusterID</option>
<option value="mode">模式[0普通|1高级|2特殊]</option>
</select>
</div>
<div class="col-md-3">
<input class="form-control" name="id" type="text" placeholder="ID">
</div>
<div class="col-md-1">
<span class="input-group-btn">
<button type="submit" class="btn btn-default"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w5p">ID</th>
<th class="center w5p">模式</th>
<th class="center w5p">用户ID</th>
<th class="center w5p">ServerID</th>
<th class="center w5p">ClientID</th>
<th class="center w5p">ClusterID</th>
<th class="center w5p">协议</th>
<th class="center w10p">监听</th>
<th class="center w10p">本地</th>
<th class="center w5p">带宽</th>
<th class="center w15p">更新时间</th>
</tr>
</thead>
<tbody>
{{range $tunnel := .tunnels}}
<tr>
<td class="center">{{$tunnel.tunnel_id}}</td>
<td class="center">{{if eq $tunnel.mode "0"}}普通{{else}}{{if eq $tunnel.mode "1"}}高级{{else}}特殊{{end}}{{end}}</td>
<td class="center">{{$tunnel.user_id}}</td>
<td class="center">{{$tunnel.server_id}}</td>
<td class="center">{{$tunnel.client_id}}</td>
<td class="center">{{$tunnel.cluster_id}}</td>
<td class="center">{{$tunnel.protocol}}</td>
<td class="center">{{$tunnel.server_listen_ip}}{{$tunnel.server_listen_port}}</td>
<td class="center">{{$tunnel.client_local_host}}{{$tunnel.client_local_port}}</td>
<td class="center">{{$tunnel.bandwidth_human}}</td>
<td class="center">{{dateFormat $tunnel.update_time "Y-m-d H:i:s"}}</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
<div class="pull-right">{{str2html .page}}</div>
<div style="clear:both;"></div>
</div>
</div>
+75
View File
@@ -0,0 +1,75 @@
<style>
.forx {
display: none;
}
.selectator_element {
border-radius: 0;
}
</style>
{{$user:=.user}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/web/user/{{.action}}">
<input type="hidden" name="user_id" value="{{$user.user_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>登录名</label>
<div class="col-sm-4">
<input type="text" name="username" class="form-control" {{if eq .action "edit"}}disabled{{end}} value="{{$user.username}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请限制在15个字符以内</span>
</div>
</div>
<!-- <div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>昵称</label>
<div class="col-sm-4">
<input type="text" name="nickname" class="form-control" value="{{$user.nickname}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>密码</label>
<div class="col-sm-4">
<input type="password" name="password" class="form-control" value="">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>邮箱</label>
<div class="col-sm-4">
<input type="text" name="email" class="form-control" value="{{$user.email}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div> -->
<div class="form-group">
<label class="col-md-2 control-label"><span class="text-danger"> * </span>角色</label>
<div class="col-md-4">
<select id="roles_selector" class="form-control" name="role_ids" size="1" multiple style="padding: 0;">
{{range $role := .roles}}
<option value="{{$role.role_id}}" {{if eq $role.is_default "1"}} selected {{end}}>{{$role.name}}</option>
{{end}}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script type="text/javascript">
$(function() {
$('#roles_selector').selectator({
showAllOptionsOnFocus: true
});
})
</script>
@@ -0,0 +1,61 @@
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<form action="/web/user/list" method="get">
<div class="col-md-3 col-md-offset-9">
<div class="input-group">
<input class="form-control" name="keyword" type="text" value="{{.keyword}}" placeholder="用户名">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</form>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w8p">ID</th>
<th class="center w11p">昵称</th>
<th class="center w11p">登录名</th>
<th class="center w20p">角色</th>
<th class="center w15p">邮箱</th>
<th class="center w8p">状态</th>
<th class="center w11p">创建时间</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $user := .users}}
<tr>
<td class="center">{{$user.user_id}}</td>
<td class="center">{{$user.nickname}}</td>
<td class="center">{{$user.username}}</td>
<td>
{{range $userId, $roleName := $.userRoles}} {{if eq $userId $user.user_id}} {{$roleName}} {{end}} {{end}}
</td>
<td class="center">{{$user.email}}</td>
<td class="center">
{{if eq $user.is_forbidden "0"}}
<span class="label label-success">正常</span> {{else}}
<span class="label label-danger">屏蔽</span> {{end}}
</td>
<td class="center">
{{dateFormat $user.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="edit" href="/web/user/edit?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a> {{if eq $user.is_forbidden "0"}}
<a name="forbidden" data-toggle="confirmation" data-placement="left" data-title="确定禁用吗?" href="/web/user/forbidden?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-remove"> </i>禁用</a> {{else}}
<a name="review" data-toggle="confirmation" data-placement="left" data-title="确定恢复吗?" href="/web/user/review?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-ok"> </i>恢复</a> {{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
{{str2html .page}}
</div>
</div>
+149
View File
@@ -0,0 +1,149 @@
<style>
.forx {
display: none;
}
</style>
{{$privilege:=.privilege}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/privilege/{{.action}}">
<input type="hidden" name="privilege_id" value="{{$privilege.privilege_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>权限名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$privilege.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>类型</label>
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="type" value="navigator">
导航
</label>
<label class="radio-inline">
<input type="radio" name="type" value="menu">
菜单
</label>
<label class="radio-inline">
<input type="radio" name="type" value="controller">
控制器
</label>
</div>
</div>
<div class="form-group forx for-menu">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>上级导航</label>
<div class="col-sm-4">
<select name="parent_n" class="form-control" default="{{$privilege.parent_id}}">
{{range .navigators}}
<option value="{{.privilege_id}}">{{.name}}</option>
{{end}}
</select>
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group forx for-controller">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>上级菜单</label>
<div class="col-sm-4">
<select name="parent_m" class="form-control" default="{{$privilege.parent_id}}">
{{range .menus}}
<option value="{{.privilege_id}}">{{.parent}} -> {{.name}}</option>
{{end}}
</select>
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group forx for-controller">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>控制器名称</label>
<div class="col-sm-4">
<input type="text" name="controller" class="form-control" value="{{$privilege.controller}}">
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group forx for-controller">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>方法名称</label>
<div class="col-sm-4">
<input type="text" name="action" class="form-control" value="{{$privilege.action}}">
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group forx for-controller">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>目标地址</label>
<div class="col-sm-4">
<input type="text" name="target" class="form-control" value="{{$privilege.target}}">
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group forx for-controller">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>菜单项</label>
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="is_display" value="1">
</label>
<label class="radio-inline">
<input type="radio" name="is_display" value="0">
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>排序数字</label>
<div class="col-sm-2">
<input type="text" name="sequence" class="form-control" value="{{$privilege.sequence}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 数字小的在前面</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>图标</label>
<div class="col-sm-4">
<input name="icon" class="icon-placeholder icon" type="hidden" value="{{$privilege.icon}}" />
<a class="btn btn-md btn-default icon-selector">
<span class="icon icon-placeholder-preview">
<i class="{{$privilege.icon}}"></i>
</span> &nbsp;&nbsp;选择图标...
</a>
</div>
<div class="col-md-4">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script>
$('[name="type"]').click(function() {
$(".forx").hide();
$('.for-' + this.value).show();
});
$('[name="type"]').each(function() {
if (this.value == '{{$privilege.type}}') {
$(this).attr('checked', true).trigger('click');
}
});
$('[name="is_display"]').each(function() {
if (this.value == '{{$privilege.is_display}}') {
$(this).attr('checked', true).trigger('click');
}
})
</script>
+17
View File
@@ -0,0 +1,17 @@
<div class="panel panel-default" style="margin-bottom:80px;">
<div class="panel-body">
{{range $navigator := .navigators}}
<h4><span class="label label-default">{{$navigator.name}}</span> <span class="small"><a name='edit' href="/privilege/edit?privilege_id={{$navigator.privilege_id}}"></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/privilege/delete?privilege_id={{$navigator.privilege_id}}'></a></span></h4>
{{range $menu := $.menus}} {{if eq $menu.parent_id $navigator.privilege_id}}
<ul class="list-group w240 inline-group">
<li class="list-group-item"><strong>{{$menu.name}}</strong> <span class="pull-right"><a name='edit' href='/privilege/edit?privilege_id={{$menu.privilege_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/privilege/delete?privilege_id={{$menu.privilege_id}}'></a></span></li>
{{range $controller := $.controllers}} {{if eq $controller.parent_id $menu.privilege_id}}
<li class="list-group-item">{{$controller.name}}<span class="pull-right"><a name='edit' href='/privilege/edit?privilege_id={{$controller.privilege_id}}'></a> | <a data-toggle="confirmation" data-placement="bottom" data-title="确定删除吗?" href='/privilege/delete?privilege_id={{$controller.privilege_id}}'></a></span></li>
{{end}} {{end}}
</ul>
{{end}} {{end}} {{end}}
</div>
</div>
<script type="text/javascript">
// Popup.bindIframe('[name="edit"]', '修改权限信息');
</script>
+27
View File
@@ -0,0 +1,27 @@
<style>
.forx {
display: none;
}
</style>
{{$role:=.role}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/role/{{.action}}">
<input type="hidden" name="role_id" value="{{$role.role_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>角色名称</label>
<div class="col-sm-4">
<input type="text" name="name" class="form-control" value="{{$role.name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
+40
View File
@@ -0,0 +1,40 @@
<div class="panel panel-default">
<div class="panel-body">
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w10p">ID</th>
<th class="center w35p">角色名</th>
<th class="center w20p">状态</th>
<th class="center w20p">创建时间</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $role := .roles}}
<tr>
<td class="center">{{$role.role_id}}</td>
<td class="center">{{$role.name}}</td>
<td class="center">
{{if eq $role.is_delete "0"}}
<span class="label label-success">正常</span> {{else}}
<span class="label label-danger">删除</span> {{end}}
</td>
<td class="center">
{{dateFormat $role.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="privilege" href="/role_privilege/add?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-lock"> </i>权限</a>
<a name="edit" href="/role/edit?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a>
<a name="delete" data-toggle="confirmation" data-placement="left" data-title="确定删除吗?" href="/role/delete?role_id={{$role.role_id}}"><i class="glyphicon glyphicon-remove"> </i>删除</a>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer">
</div>
</div>
+52
View File
@@ -0,0 +1,52 @@
<div class="panel panel-default" style="margin-bottom:80px;">
<div class="panel-body">
<ul class="nav">
<h4>修改权限</h4>
</ul>
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/role_privilege/save">
<input type="hidden" name="role_id" value="{{.role_id}}" />
{{range $navigator := .navigators}}
<h4><span class="label label-default">{{$navigator.name}}</span> <span class="small"><input name="privilege_id" type="checkbox" data-type="navigator" value="{{$navigator.privilege_id}}" onclick="Role.privilege(this)" /></span></h4>
{{range $menu := $.menus}} {{if eq $menu.parent_id $navigator.privilege_id}}
<ul class="list-group w240 inline-group">
<li class="list-group-item"><strong>{{$menu.name}}</strong> <span class="pull-right"><input name="privilege_id" type="checkbox" data-type="menu" value="{{$menu.privilege_id}}" onclick="Role.privilege(this)" /></span></li>
{{range $controller := $.controllers}} {{if eq $controller.parent_id $menu.privilege_id}}
<li class="list-group-item">{{$controller.name}}<span class="pull-right"><input name="privilege_id" type="checkbox" data-type="controller" value="{{$controller.privilege_id}}" onclick="Role.privilege(this)" /></span></li>
{{end}} {{end}}
</ul>
{{end}} {{end}} {{end}}
<div class="form-group">
<div class="col-md-8" style="margin-left:10px;">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script type="text/javascript" src="/static/style/module/role.js"></script>
<script type="text/javascript">
<!--
var navigators = [];
var menus = [];
var controllers = [];
var defaultPrivileges = [];
{{range $navigator := .navigators }}
navigators.push({'privilegeId':'{{$navigator.privilege_id}}', 'name':'{{$navigator.name}}', 'parentId':'{{$navigator.parent_id}}', 'type':'{{$navigator.type}}'});
{{end}}
{{range $menu := .menus }}
menus.push({'privilegeId':'{{$menu.privilege_id}}', 'name':'{{$menu.name}}', 'parentId':'{{$menu.parent_id}}', 'type':'{{$menu.type}}'});
{{end}}
{{range $controller := .controllers }}
controllers.push({'privilegeId':'{{$controller.privilege_id}}', 'name':'{{$controller.name}}', 'parentId':'{{$controller.parent_id}}', 'type':'{{$controller.type}}'});
{{end}}
{{range $rolePrivilege := .rolePrivileges}}
defaultPrivileges.push(parseInt({{$rolePrivilege.privilege_id}}))
{{end}}
Role.defaults(defaultPrivileges);
//-->
</script>
+152
View File
@@ -0,0 +1,152 @@
<div class="page-container">
<form class="form form-horizontal" id="form-article-add">
<div id="tab-system" class="HuiTab">
<div class="tabBar cl">
<span>基本设置</span>
<span>安全设置</span>
<span>邮件设置</span>
<span>其他设置</span>
</div>
<div class="tabCon">
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
网站名称:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-title" placeholder="控制在25个字、50个字节以内" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
关键词:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-Keywords" placeholder="5个左右,8汉字以内,用英文,隔开" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
描述:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-description" placeholder="空制在80个汉字,160个字符以内" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
css、js、images路径配置:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-static" placeholder="默认为空,为相对路径" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
上传目录配置:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-uploadfile" placeholder="默认为uploadfile" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">
<span class="c-red">*</span>
底部版权信息:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-copyright" placeholder="&copy; 2016 H-ui.net" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">备案号:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="website-icp" placeholder="京ICP备00000000号" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">统计代码:</label>
<div class="formControls col-xs-8 col-sm-9">
<textarea class="textarea"></textarea>
</div>
</div>
</div>
<div class="tabCon">
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">允许访问后台的IP列表:</label>
<div class="formControls col-xs-8 col-sm-9">
<textarea class="textarea" name="" id=""></textarea>
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">后台登录失败最大次数:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" class="input-text" value="5" id="" name="">
</div>
</div>
</div>
<div class="tabCon">
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">邮件发送模式:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" class="input-text" value="" id="" name="">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">SMTP服务器:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">SMTP 端口:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" class="input-text" value="25" id="" name="">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">邮箱帐号:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" class="input-text" value="5" id="emailName" name="emailName">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">邮箱密码:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="password" id="email-password" value="" class="input-text">
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-4 col-sm-2">收件邮箱地址:</label>
<div class="formControls col-xs-8 col-sm-9">
<input type="text" id="email-address" value="" class="input-text">
</div>
</div>
</div>
<div class="tabCon">
</div>
</div>
<div class="row cl">
<div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-2">
<button onClick="article_save_submit();" class="btn btn-primary radius" type="submit"><i class="Hui-iconfont">&#xe632;</i> 保存</button>
<button onClick="layer_close();" class="btn btn-default radius" type="button">&nbsp;&nbsp;取消&nbsp;&nbsp;</button>
</div>
</div>
</form>
</div>
<!--请在下方写此页面业务相关的脚本-->
<script type="text/javascript" src="/lib/My97DatePicker/4.8/WdatePicker.js"></script>
<script type="text/javascript" src="/lib/jquery.validation/1.14.0/jquery.validate.js"></script>
<script type="text/javascript" src="/lib/jquery.validation/1.14.0/validate-methods.js"></script>
<script type="text/javascript" src="/lib/jquery.validation/1.14.0/messages_zh.js"></script>
<script type="text/javascript">
$(function() {
$('.skin-minimal input').iCheck({
checkboxClass: 'icheckbox-blue',
radioClass: 'iradio-blue',
increaseArea: '20%'
});
$("#tab-system").Huitab({
index: 0
});
});
</script>
<!--/请在上方写此页面业务相关的脚本-->
+182
View File
@@ -0,0 +1,182 @@
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<div class="panel-body" style="padding: 15px 0;">
<ul class="nav nav-tabs">
<li class="active"><a href="javascript:;">Tab</a></li>
</ul>
</div>
<div class="panel-body" style="padding-left: 9px;">
<div class="row">
<form>
<div class="col-md-3">
<div class="input-group">
<span class="input-group-addon">选择节点</span>
<select class="form-control">
<option value="ad" >jadhgjashdasgdsjagdj</option>
<option value="da" >jadhgjashdasgdsjagdj</option>
</select>
</div>
</div>
<div class="col-md-5">
<div class="form-group">
<button type="button" class="btn btn-primary" name="add_message" data-link="/message/add?node_id={{.node_id}}"><i class="glyphicon glyphicon-plus"></i> 添加消息 </button>
<button type="button" class="btn btn-success" name="test_message" data-link="/message/test?node_id={{.node_id}}" style="margin-left: 5px;"><i class="glyphicon glyphicon-send"></i> 测试消息 </button>
<button onclick="Common.confirm('确定要重启该节点吗?', '/node/reload?node_id={{.node_id}}')" type="button" class="btn btn-danger" style="margin-left: 5px;"><i class="glyphicon glyphicon-refresh"></i> 重启节点 </button>
</div>
</div>
</form>
</div>
</div>
<div class="panel-body" style="padding-left: 9px;">
<div class="row">
<form action="/log/list" method="post">
<div class="col-md-3">
<div class="input-group">
<span class="input-group-addon">节点</span>
<select class="form-control" name="node_id">
<option value="1">dlasdljalks</option>
<option value="1">dlasdljalks</option>
<option value="1">dlasdljalks</option>
</select>
</div>
</div>
<div class="col-md-2">
<div class="input-group">
<span class="input-group-addon">日志类型</span>
<select class="form-control" name="type">
<option value="errror" >ERROR</option>
<option value="info" >INFO</option>
<option value="debug">DEBUG</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="input-group">
<input class="form-control" name="keyword" type="text" value="adas" placeholder="关键字">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<button type="button" class="btn btn-primary" name="log_download" data-link="/log/download?node_id={{.node_id}}"><i class="glyphicon glyphicon-download-alt"></i> 下载日志 </button>
</div>
</div>
</form>
</div>
</div>
<div class="panel-body">
<form class="form-horizontal" method="post" action="" onsubmit="return false">
<input type="hidden" name="node_id" value="">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>表单</label>
<div class="col-sm-6">
<input type="text" name="url" class="form-control" placeholder="消费接口" required>
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>下拉框</label>
<div class="col-sm-6">
<select name="message" class="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<div class="col-md-4">
<span class="text-danger"> * 请将汉字限制在15个以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>单选框</label>
<div class="col-sm-4">
<label class="radio-inline">
<input type="radio" name="check_code" value="1" checked="checked">
</label>
<label class="radio-inline">
<input type="radio" name="check_code" value="0">
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>复选框</label>
<div class="col-sm-4">
<label class="checkbox-inline">
<input type="checkbox" name="check_code" value="1" checked="checked">
</label>
<label class="radio-inline">
<input type="checkbox" name="check_code" value="0">
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span> textarea </label>
<div class="col-sm-6">
<textarea name="data" class="form-control" rows="6"></textarea><br/>
</div>
<div class="col-md-4">
<span class="text-danger"> * 数据格式如:name=pppp&id=16&[email protected] </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">备注</label>
<div class="col-sm-6">
<input type="text" name="comment" class="form-control" placeholder="备注" value="">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" name="submit" onclick="Form.ajaxSubmit(this.form, true)" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
<hr>
<div class="panel panel-default">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th class="w10p">消息名称</th>
<th class="w8p">交换器模式</th>
<th>消费接口</th>
<th class="w80">RouteKey</th>
<th class="w90">状态</th>
<th class="w60">超时</th>
<th class="w200">备注</th>
<th class="w100">操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>ad</td>
<td>da</td>
<td>da</td>
<td class="w80">da</td>
<td class="center w90 consumer_status">
<label class="text-info">waiting</label>
</td>
<td class="w60">ad</td>
<td class="w200">ad</td>
<td class="w100 center">
<a name="edit" class="glyphicon glyphicon-edit" data-link="/consumer/edit?node_id=1">修改</a>
<a onclick="Common.confirm('确定要删除吗?', '/consumer/delete?node_id=')" class="remove glyphicon glyphicon-trash">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<div class="panel-footer"></div>
</div>
</div>
</div>
</div>
+29
View File
@@ -0,0 +1,29 @@
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" method="post" action="/user/changepassword">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>旧密码</label>
<div class="col-sm-4">
<input type="password" name="password_old" class="form-control" value="">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>新密码</label>
<div class="col-sm-4">
<input type="password" name="password" class="form-control" value="">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">修改</button>
</div>
</div>
</form>
</div>
</div>
+84
View File
@@ -0,0 +1,84 @@
<style>
.forx {
display: none;
}
.selectator_element {
border-radius: 0;
}
</style>
{{$user:=.user}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/user/{{.action}}">
<input type="hidden" name="user_id" value="{{$user.user_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>用户名</label>
<div class="col-sm-4">
<input type="text" name="username" class="form-control" {{if eq .action "edit"}}disabled{{end}} value="{{$user.username}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * 请限制在15个字符以内</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>姓名</label>
<div class="col-sm-4">
<input type="text" name="given_name" class="form-control" value="{{$user.given_name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<!-- <div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>密码</label>
<div class="col-sm-4">
<input type="password" name="password" class="form-control" value="">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div> -->
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>邮箱</label>
<div class="col-sm-4">
<input type="text" name="email" class="form-control" value="{{$user.email}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>手机号</label>
<div class="col-sm-4">
<input type="text" name="mobile" class="form-control" value="{{$user.mobile}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"><span class="text-danger"> * </span>角色</label>
<div class="col-md-4">
<select id="roles_selector" class="form-control" name="role_ids" size="1" multiple style="padding: 0;">
{{range $role := .roles}}
<option value="{{$role.role_id}}" {{if eq $role.is_default "1"}} selected {{end}}>{{$role.name}}</option>
{{end}}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>
<script type="text/javascript">
$(function() {
$('#roles_selector').selectator({
showAllOptionsOnFocus: true
});
})
</script>
+61
View File
@@ -0,0 +1,61 @@
<div class="panel panel-default">
<div class="panel-body">
<div class="row">
<form action="/user/list" method="get">
<div class="col-md-3 col-md-offset-9">
<div class="input-group">
<input class="form-control" name="keyword" type="text" value="{{.keyword}}" placeholder="用户名">
<span class="input-group-btn">
<button type="submit" class="btn btn-primary"><i class="glyphicon glyphicon-search"></i></button>
</span>
</div>
</div>
</form>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="center w8p">ID</th>
<th class="center w11p">用户名</th>
<th class="center w11p">真实姓名</th>
<th class="center w20p">角色</th>
<th class="center w15p">邮箱</th>
<th class="center w8p">状态</th>
<th class="center w11p">创建时间</th>
<th class="center">操作</th>
</tr>
</thead>
<tbody>
{{range $user := .users}}
<tr>
<td class="center">{{$user.user_id}}</td>
<td class="center">{{$user.username}}</td>
<td class="center">{{$user.given_name}}</td>
<td>
{{range $userId, $roleName := $.userRoles}} {{if eq $userId $user.user_id}} {{$roleName}} {{end}} {{end}}
</td>
<td class="center">{{$user.email}}</td>
<td class="center">
{{if eq $user.is_forbidden "0"}}
<span class="label label-success">正常</span> {{else}}
<span class="label label-danger">屏蔽</span> {{end}}
</td>
<td class="center">
{{dateFormat $user.create_time "Y-m-d H:i:s"}}
</td>
<td class="center">
<a name="edit" href="/user/edit?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-pencil"> </i>修改</a> {{if eq $user.is_forbidden "0"}}
<a name="forbidden" data-toggle="confirmation" data-placement="left" data-title="确定禁用吗?" href="/user/forbidden?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-remove"> </i>禁用</a> {{else}}
<a name="review" data-toggle="confirmation" data-placement="left" data-title="确定恢复吗?" href="/user/review?user_id={{$user.user_id}}"><i class="glyphicon glyphicon-ok"> </i>恢复</a> {{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="panel-footer pagebar">
{{str2html .page}}
</div>
</div>
+127
View File
@@ -0,0 +1,127 @@
<div class="page-container">
<div class="text-c"> 日期范围:
<input type="text" onfocus="WdatePicker({ maxDate:'#F{$dp.$D(\'datemax\')||\'%y-%M-%d\'}' })" id="datemin" class="input-text Wdate" style="width:120px;"> -
<input type="text" onfocus="WdatePicker({ minDate:'#F{$dp.$D(\'datemin\')}',maxDate:'%y-%M-%d' })" id="datemax" class="input-text Wdate" style="width:120px;">
<input type="text" class="input-text" style="width:250px" placeholder="输入管理员名称" id="" name="">
<button type="submit" class="btn btn-success" id="" name=""><i class="Hui-iconfont">&#xe665;</i> 搜用户</button>
</div>
<div class="cl pd-5 bg-1 bk-gray mt-20"> <span class="l"><a href="javascript:;" onclick="datadel()" class="btn btn-danger radius"><i class="Hui-iconfont">&#xe6e2;</i> 批量删除</a> <a href="javascript:;" onclick="admin_add('添加管理员','/user/add.html','800','500')" class="btn btn-primary radius"><i class="Hui-iconfont">&#xe600;</i> 添加管理员</a></span> <span class="r">共有数据:<strong>54</strong> 条</span> </div>
<table class="table table-border table-bordered table-bg">
<thead>
<tr>
<th scope="col" colspan="9">员工列表</th>
</tr>
<tr class="text-c">
<th width="25"><input type="checkbox" name="" value=""></th>
<th width="40">ID</th>
<th width="150">登录名</th>
<th width="90">手机</th>
<th width="150">邮箱</th>
<th>角色</th>
<th width="130">加入时间</th>
<th width="100">是否已启用</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
<tr class="text-c">
<td><input type="checkbox" value="1" name=""></td>
<td>1</td>
<td>admin</td>
<td>13000000000</td>
<td>[email protected]</td>
<td>超级管理员</td>
<td>2014-6-11 11:11:42</td>
<td class="td-status"><span class="label label-success radius">已启用</span></td>
<td class="td-manage"><a style="text-decoration:none" onClick="admin_stop(this,'10001')" href="javascript:;" title="停用"><i class="Hui-iconfont">&#xe631;</i></a> <a title="编辑" href="javascript:;" onclick="admin_edit('管理员编辑','admin-add.html','1','800','500')"
class="ml-5" style="text-decoration:none"><i class="Hui-iconfont">&#xe6df;</i></a> <a title="删除" href="javascript:;" onclick="admin_del(this,'1')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont">&#xe6e2;</i></a></td>
</tr>
<tr class="text-c">
<td><input type="checkbox" value="2" name=""></td>
<td>2</td>
<td>zhangsan</td>
<td>13000000000</td>
<td>[email protected]</td>
<td>栏目编辑</td>
<td>2014-6-11 11:11:42</td>
<td class="td-status"><span class="label radius">已停用</span></td>
<td class="td-manage"><a style="text-decoration:none" onClick="admin_start(this,'10001')" href="javascript:;" title="启用"><i class="Hui-iconfont">&#xe615;</i></a> <a title="编辑" href="javascript:;" onclick="admin_edit('管理员编辑','admin-add.html','2','800','500')"
class="ml-5" style="text-decoration:none"><i class="Hui-iconfont">&#xe6df;</i></a> <a title="删除" href="javascript:;" onclick="admin_del(this,'1')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont">&#xe6e2;</i></a></td>
</tr>
</tbody>
</table>
</div>
<!--请在下方写此页面业务相关的脚本-->
<script type="text/javascript" src="/lib/My97DatePicker/4.8/WdatePicker.js"></script>
<script type="text/javascript" src="/lib/datatables/1.10.0/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="/lib/laypage/1.2/laypage.js"></script>
<script type="text/javascript">
/*
参数解释:
title 标题
url 请求的url
id 需要操作的数据id
w 弹出层宽度(缺省调默认值)
h 弹出层高度(缺省调默认值)
*/
/*管理员-增加*/
function admin_add(title, url, w, h) {
layer_show(title, url, w, h);
}
/*管理员-删除*/
function admin_del(obj, id) {
layer.confirm('确认要删除吗?', function(index) {
$.ajax({
type: 'POST',
url: '',
dataType: 'json',
success: function(data) {
$(obj).parents("tr").remove();
layer.msg('已删除!', {
icon: 1,
time: 1000
});
},
error: function(data) {
console.log(data.msg);
},
});
});
}
/*管理员-编辑*/
function admin_edit(title, url, id, w, h) {
layer_show(title, url, w, h);
}
/*管理员-停用*/
function admin_stop(obj, id) {
layer.confirm('确认要停用吗?', function(index) {
//此处请求后台程序,下方是成功后的前台处理……
$(obj).parents("tr").find(".td-manage").prepend('<a onClick="admin_start(this,id)" href="javascript:;" title="启用" style="text-decoration:none"><i class="Hui-iconfont">&#xe615;</i></a>');
$(obj).parents("tr").find(".td-status").html('<span class="label label-default radius">已禁用</span>');
$(obj).remove();
layer.msg('已停用!', {
icon: 5,
time: 1000
});
});
}
/*管理员-启用*/
function admin_start(obj, id) {
layer.confirm('确认要启用吗?', function(index) {
//此处请求后台程序,下方是成功后的前台处理……
$(obj).parents("tr").find(".td-manage").prepend('<a onClick="admin_stop(this,id)" href="javascript:;" title="停用" style="text-decoration:none"><i class="Hui-iconfont">&#xe631;</i></a>');
$(obj).parents("tr").find(".td-status").html('<span class="label label-success radius">已启用</span>');
$(obj).remove();
layer.msg('已启用!', {
icon: 6,
time: 1000
});
});
}
</script>
+58
View File
@@ -0,0 +1,58 @@
<style>
.forx {
display: none;
}
.selectator_element {
border-radius: 0;
}
</style>
{{$user:=.user}}
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal ajaxform" ajaxSuccess="success" ajaxAlways="always" method="post" action="/user/profile">
<input type="hidden" name="user_id" value="{{$user.user_id}}">
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>用户名</label>
<div class="col-sm-4">
<input type="text" name="username" class="form-control" disabled value="{{$user.username}}">
</div>
<div class="col-md-4">
<span class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>姓名</label>
<div class="col-sm-4">
<input type="text" name="given_name" class="form-control" value="{{$user.given_name}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>邮箱</label>
<div class="col-sm-4">
<input type="text" name="email" class="form-control" value="{{$user.email}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label"><span class="text-danger"> * </span>手机号</label>
<div class="col-sm-4">
<input type="text" name="mobile" class="form-control" value="{{$user.mobile}}">
</div>
<div class="col-md-4">
<span class="text-danger"> * </span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">保存</button>
</div>
</div>
</form>
</div>
</div>