go开发多云资产管理平台cmdb
代码仓库github.com/yunixiangfeng/gocmdb
云主机管理
主机资源监控
开发流程
Welcome to Beego | Beego
bee new gocmdb/server
cd gocmdb/server
go mod tidy
go get -u github.com/beego/beego/v2
go get -u "github.com/astaxie/beego/orm"
go get -u "github.com/go-sql-driver/mysql"
bee run
http://127.0.0.1:8080/
项目结构
修改配置文件
D:\gocmdb\server\conf\app.conf
appname=CMDB
runmode=${RUNMODE||dev}
sessionon=true
sessionprovider=file
sessionproviderconfig=temp/session
sessionname=sid
enablexsrf=true
xsrfexpire=3600
xsrfkey=ac2e5a098492610c97ccd28ffb621014
login=AuthController.Login
home=TestController.Test
include "db.conf"
[dev]
httpport=8888
[prod]
httpport=80
D:\gocmdb\server\conf\db.conf
dsn=root:1234@tcp(192.168.204.130:3306)/gocmdb?charset=utf8mb4&loc=Local&parseTime=True
编写启动文件
D:\gocmdb\server\web.go
package main
import (
"flag"
"fmt"
"os"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
"github.com/yunixiangfeng/gocmdb/server/models"
"github.com/yunixiangfeng/gocmdb/server/utils"
_ "github.com/yunixiangfeng/gocmdb/server/routers"
)
func main() {
// 初始化命令行参数
h := flag.Bool("h", false, "help")
help := flag.Bool("help", false, "help")
init := flag.Bool("init", false, "init server")
syncdb := flag.Bool("syncdb", false, "sync db")
force := flag.Bool("force", false, "force sync db(drop table)")
verbose := flag.Bool("v", false, "verbose")
flag.Usage = func() {
fmt.Println("usage: web -h")
flag.PrintDefaults()
}
// 解析命令行参数
flag.Parse()
if *h || *help {
flag.Usage()
os.Exit(0)
}
// 设置日志到文件
beego.SetLogger("file", `{
"filename" : "logs/web.log",
"level" : 7}`,
)
if !*verbose {
//删除控制台日志
beego.BeeLogger.DelLogger("console")
} else {
orm.Debug = true
}
// 初始化orm
orm.RegisterDriver("mysql", orm.DRMySQL)
orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))
// 测试数据库连接是否正常
if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
beego.Error("数据库连接错误")
os.Exit(-1)
}
// 根据参数选择执行流程
switch {
case *init:
orm.RunSyncdb("default", *force, *verbose)
ormer := orm.NewOrm()
admin := &models.User{Name: "admin", IsSuperman: true}
if err := ormer.Read(admin, "Name"); err == orm.ErrNoRows {
password := utils.RandString(6)
admin.SetPassword(password)
if _, err := ormer.Insert(admin); err == nil {
beego.Informational("初始化admin成功, 默认密码:", password)
} else {
beego.Error("初始化用户失败, 错误:", err)
}
} else {
beego.Informational("admin用户已存在, 跳过")
}
case *syncdb:
orm.RunSyncdb("default", *force, *verbose)
beego.Informational("同步数据库")
default:
beego.Run()
}
}
登陆
用户/Token模型定义
登陆页面加载
提交用户名/密码登陆验证
验证结果处理
基础控制器
BaseController 基础控制器,用于改写默认模板(默认:controllerName/actionName.tpl)
D:\Workspace\Go\src\gocmdb\server\controllers\base\base.go
package base
import (
"github.com/astaxie/beego"
)
type BaseController struct {
beego.Controller
}
func (c *BaseController) Prepare() {
c.Data["xsrf_token"] = c.XSRFToken()
}
LoginRequiredController 认证控制器,用于API调用认证(session/token)
D:\Workspace\Go\src\gocmdb\server\controllers\auth\auth.go
package auth
import (
"gocmdb/server/controllers/base"
"gocmdb/server/models"
)
type LoginRequiredController struct {
base.BaseController
User *models.User
}
func (c *LoginRequiredController) Prepare() {
c.BaseController.Prepare()
if user := DefaultManger.IsLogin(c); user == nil {
// 未登陆
DefaultManger.GoToLoginPage(c) // todo 需要修改参数
c.StopRun()
} else {
// 已登陆
c.User = user
c.Data["user"] = user
}
}
type AuthController struct {
base.BaseController
}
func (c *AuthController) Login() {
DefaultManger.Login(c)
}
func (c *AuthController) Logout() {
DefaultManger.Logout(c)
}
D:\Workspace\Go\src\gocmdb\server\controllers\auth\manager.go
package auth
import (
"github.com/astaxie/beego/context"
"gocmdb/server/models"
)
type AuthPlugin interface {
Name() string
Is(*context.Context) bool
IsLogin(*LoginRequiredController) *models.User
GoToLoginPage(*LoginRequiredController)
Login(*AuthController) bool
Logout(*AuthController)
}
type Manager struct {
plugins map[string]AuthPlugin
}
func NewManager() *Manager {
return &Manager{
plugins: map[string]AuthPlugin{},
}
}
func (m *Manager) Register(p AuthPlugin) {
m.plugins[p.Name()] = p
}
func (m *Manager) GetPlugin(c *context.Context) AuthPlugin {
for _, plugin := range m.plugins {
if plugin.Is(c) {
return plugin
}
}
return nil
}
func (m *Manager) IsLogin(c *LoginRequiredController) *models.User {
if plugin := m.GetPlugin(c.Ctx); plugin != nil {
return plugin.IsLogin(c)
}
return nil
}
func (m *Manager) GoToLoginPage(c *LoginRequiredController) {
if plugin := m.GetPlugin(c.Ctx); plugin != nil {
plugin.GoToLoginPage(c)
}
}
func (m *Manager) Login(c *AuthController) bool {
if plugin := m.GetPlugin(c.Ctx); plugin != nil {
return plugin.Login(c)
}
return false
}
func (m *Manager) Logout(c *AuthController) {
if plugin := m.GetPlugin(c.Ctx); plugin != nil {
plugin.Logout(c)
}
}
var DefaultManger = NewManager()
D:\Workspace\Go\src\gocmdb\server\controllers\auth\plugin.go
package auth
import (
"github.com/astaxie/beego/context"
"gocmdb/server/models"
"net/http"
"strings"
"github.com/beego/beego"
"github.com/beego/beego/validation"
"gocmdb/server/forms"
)
type Session struct {
}
func (s *Session) Name() string {
return "session"
}
func (s *Session) Is(c *context.Context) bool {
return c.Input.Header("Authentication") == ""
}
func (s *Session) IsLogin(c *LoginRequiredController) *models.User {
if session := c.GetSession("user"); session != nil {
if uid, ok := session.(int); ok {
return models.DefaultUserManager.GetById(uid)
}
}
return nil
}
func (s *Session) GoToLoginPage(c *LoginRequiredController) {
c.Redirect(beego.URLFor(beego.AppConfig.String("login")), http.StatusFound)
}
func (s *Session) Login(c *AuthController) bool {
form := &forms.LoginForm{}
valid := &validation.Validation{}
if c.Ctx.Input.IsPost() {
if err := c.ParseForm(form); err != nil {
valid.SetError("error", err.Error())
} else {
if ok, err := valid.Valid(form); err != nil {
valid.SetError("error", err.Error())
} else if ok {
c.SetSession("user", form.User.Id)
c.Redirect(beego.URLFor(beego.AppConfig.String("home")), http.StatusFound)
return true
}
}
}
c.TplName = "auth/login.html"
c.Data["form"] = form
c.Data["valid"] = valid
return false
}
func (s *Session) Logout(c *AuthController) {
c.DestroySession()
c.Redirect(beego.URLFor(beego.AppConfig.String("login")), http.StatusFound)
}
type Token struct {
}
func (t *Token) Name() string {
return "token"
}
func (t *Token) Is(c *context.Context) bool {
return strings.ToLower(strings.TrimSpace(c.Input.Header("Authentication"))) == "token"
}
func (t *Token) IsLogin(c *LoginRequiredController) *models.User {
accessKey := strings.TrimSpace(c.Ctx.Input.Header("AccessKey"))
secrectKey := strings.TrimSpace(c.Ctx.Input.Header("SecrectKey"))
if token := models.DefaultTokenManager.GetByKey(accessKey, secrectKey); token != nil && token.User.DeletedTime == nil {
return token.User
}
return nil
}
func (t *Token) GoToLoginPage(c *LoginRequiredController) {
c.Data["json"] = map[string]interface{}{
"code": 403,
"text": "请使用正确Token发起请求",
"result": nil,
}
c.ServeJSON()
}
func (t *Token) Login(c *AuthController) bool {
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "请使用Token请求API",
"result": nil,
}
c.ServeJSON()
return false
}
func (t *Token) Logout(c *AuthController) {
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "退出登陆成功",
"result": nil,
}
c.ServeJSON()
}
func init() {
DefaultManger.Register(new(Session))
DefaultManger.Register(new(Token))
}
D:\Workspace\Go\src\gocmdb\server\forms\auth.go
package forms
import (
"gocmdb/server/models"
"strings"
"github.com/beego/beego/validation"
)
type LoginForm struct {
Name string `form:"name"`
Password string `form:"password"`
User *models.User
}
func (f *LoginForm) Valid(v *validation.Validation) {
f.Name = strings.TrimSpace(f.Name)
f.Password = strings.TrimSpace(f.Password)
if f.Name == "" || f.Password == "" {
v.SetError("error", "用户名或密码错误")
} else {
if user := models.DefaultUserManager.GetByName(f.Name); user == nil || !user.ValidatePassword(f.Password) {
v.SetError("error", "用户名或密码错误")
} else if user.IsLock() {
v.SetError("error", "用户名被锁定")
} else {
f.User = user
}
}
}
D:\Workspace\Go\src\gocmdb\server\models\user.go
package models
import (
"time"
"gocmdb/server/utils"
"github.com/beego/beego/orm"
)
type User struct {
Id int `orm:"column(id);"`
Name string `orm:"column(name);size(32);"`
Password string `orm:"column(password);size(1024);"`
Gender int `orm:"column(gender);default(0);"`
Birthday *time.Time `orm:"column(birthday);null;default(null);"`
Tel string `orm:"column(tel);size(1024);"`
Email string `orm:"column(email);size(1024);"`
Addr string `orm:"column(addr);size(1024);"`
Remark string `orm:"column(remark);size(1024);"`
IsSuperman bool `orm:"column(is_superman);default(false);"`
Status int `orm:"column(status);"`
CreatedTime *time.Time `orm:"column(created_time);auto_now_add;"`
UpdatedTime *time.Time `orm:"column(update_time);auto_now;"`
DeletedTime *time.Time `orm:"column(deleted_time);null;default(null);"`
Token *Token `orm:"reverse(one);"`
}
func (u *User) SetPassword(password string) {
u.Password = utils.Md5Salt(password, "")
}
func (u *User) ValidatePassword(password string) bool {
salt, _ := utils.SplitMd5Salt(u.Password)
return utils.Md5Salt(password, salt) == u.Password
}
func (u *User) IsLock() bool {
return u.Status == StatusLock
}
type UserManager struct{}
func NewUserManager() *UserManager {
return &UserManager{}
}
func (m *UserManager) GetById(id int) *User {
user := &User{}
err := orm.NewOrm().QueryTable(user).Filter("Id__exact", id).Filter("DeletedTime__isnull", true).One(user)
if err == nil {
return user
}
return nil
}
func (m *UserManager) GetByName(name string) *User {
user := &User{}
err := orm.NewOrm().QueryTable(user).Filter("Name__exact", name).Filter("DeletedTime__isnull", true).One(user)
if err == nil {
return user
}
return nil
}
type Token struct {
Id int `orm:"column(id);"`
User *User `orm:"column(user);rel(one);"`
AccessKey string `orm:"column(access_key);size(1024);"`
SecrectKey string `orm:"column(secrect_key);size(1024);"`
CreatedTime *time.Time `orm:"column(created_time);auto_now_add;"`
UpdateTime *time.Time `orm:"column(updated_time);auto_now;"`
}
type TokenManager struct {
}
func NewTokenManager() *TokenManager {
return &TokenManager{}
}
func (m *TokenManager) GetByKey(accessKey, secrectKey string) *Token {
token := &Token{AccessKey: accessKey, SecrectKey: secrectKey}
ormer := orm.NewOrm()
if err := ormer.Read(token, "AccessKey", "SecrectKey"); err == nil {
ormer.LoadRelated(token, "User")
return token
}
return nil
}
var DefaultUserManager = NewUserManager()
var DefaultTokenManager = NewTokenManager()
func init() {
orm.RegisterModel(new(User), new(Token))
}
D:\Workspace\Go\src\gocmdb\server\utils\crypto.go
package utils
import (
"crypto/md5"
"fmt"
"strings"
)
func Md5Salt(text string, salt string) string {
if salt == "" {
salt = RandString(8)
}
return fmt.Sprintf("%s:%x", salt, md5.Sum([]byte(fmt.Sprintf("%s:%s", salt, text))))
}
func SplitMd5Salt(text string) (string, string) {
nodes := strings.SplitN(text, ":", 2)
if len(nodes) >= 2 {
return nodes[0], nodes[1]
} else {
return "", nodes[0]
}
}
D:\Workspace\Go\src\gocmdb\server\utils\rand.go
package utils
import (
"math/rand"
"time"
)
func RandString(length int) string {
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
count := len(letters)
chars := make([]byte, length)
for i := 0; i < length; i++ {
chars[i] = letters[rand.Int()%count]
// rand.Intn(count)
}
return string(chars)
}
func init() {
rand.Seed(time.Now().UnixNano())
}
D:\Workspace\Go\src\gocmdb\server\models\enum.go
package models
const (
StatusUnlock = 0
StatusLock = 1
)
D:\Workspace\Go\src\gocmdb\server\routers\router.go
package routers
import (
"github.com/astaxie/beego"
"gocmdb/server/controllers"
"gocmdb/server/controllers/auth"
)
func init() {
beego.AutoRouter(&auth.AuthController{})
beego.AutoRouter(&controllers.TestController{})
}
D:\Workspace\Go\src\gocmdb\server\views\auth\login.html
bee run
http://localhost:8888/auth/login
输入用户名和密码,跳转测试页
基础控制器
LayoutController Layout控制器,用于设置layout,layoutSections,memu及expand
D:\Workspace\Go\src\gocmdb\server\controllers\layout.go
package controllers
import "gocmdb/server/controllers/auth"
type LayoutController struct {
auth.LoginRequiredController
}
func (c *LayoutController) Prepare() {
c.LoginRequiredController.Prepare()
c.Layout = "layouts/base.html"
c.LayoutSections = map[string]string{
"LayoutStyle": "",
"LayoutScript": "",
}
c.Data["menu"] = ""
c.Data["expand"] = ""
}
请求
模板
用户管理:登陆验证、管理页加载、用户数据加载、增/删/改/锁定/解锁、Token查看/生成
用户属性
Id
name varchar(32) 默认空字符串
password varchar(1024) 默认空字符串
gender int 默认0, 0 女 1 男
birthday date 允许为null, 默认值为null
tel varchar(32) 默认为空字符串
email varchar(64) 默认为空字符
addr varchar(512) 默认为空字符串
remark varchar(1024) 默认为空字符串
is_superuser bool 默认为false, true超级管理员,false普通管理员
status int 默认为0, 0表示正常,1表示锁定
created_time datetime 添加时初始化事件
updated_time datetime 更新时间,允许为null,默认为null
逻辑删除
deleted_time datetime 删除时间,允许为null,默认为null, null未删除,非null已删除
用户管理:
用户管理(增/删(逻辑删除)/改/查/锁定/解锁)
Token管理(生成/重新生成)
登陆认证(web session/api token)
D:\Workspace\Go\src\gocmdb\server\controllers\user.go
用户管理页面控制器
package controllers
type UserPageController struct {
LayoutController
}
func (c *UserPageController) Index() {
c.Data["menu"] = "user_management"
c.Data["expand"] = "system_management"
c.TplName = "user_page/index.html"
c.LayoutSections["LayoutScript"] = "user_page/index.script.html"
}
添加路由
D:\Workspace\Go\src\gocmdb\server\routers\router.go
beego.AutoRouter(&controllers.UserPageController{})
D:\Workspace\Go\src\gocmdb\server\views\user_page\index.html
D:\Workspace\Go\src\gocmdb\server\views\user_page\index.script.html
http://localhost:8888/userpage/index
用户管理控制器
D:\Workspace\Go\src\gocmdb\server\controllers\user.go
用户管理(增/删(逻辑删除)/改/查/锁定/解锁)
type UserController struct {
auth.LoginRequiredController
}
func (c *UserController) List() {
//draw,start, length, q
draw, _ := c.GetInt("draw")
start, _ := c.GetInt64("start")
length, _ := c.GetInt("length")
q := strings.TrimSpace(c.GetString("q"))
// []*User, total, queryTotal
users, total, queryTotal := models.DefaultUserManager.Query(q, start, length)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "获取成功",
"result": users,
"draw": draw,
"recordsTotal": total,
"recordsFiltered": queryTotal,
}
c.ServeJSON()
}
func (c *UserController) Create() {
if c.Ctx.Input.IsPost() {
json := map[string]interface{}{
"code": 400,
"text": "提交数据错误",
}
form := &forms.UserCreateForm{}
valid := &validation.Validation{}
if err := c.ParseForm(form); err == nil {
if ok, err := valid.Valid(form); err != nil {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
} else if ok {
user, err := models.DefaultUserManager.Create(form.Name, form.Password, form.Gender, form.BirthdayTime, form.Tel, form.Email, form.Addr, form.Remark)
if err == nil {
json = map[string]interface{}{
"code": 200,
"text": "创建成功",
"result": user,
}
} else {
json = map[string]interface{}{
"code": 500,
"text": "服务器错误",
}
}
} else {
json["result"] = valid.Errors
}
} else {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
}
c.Data["json"] = json
c.ServeJSON()
} else {
//get
c.TplName = "user/create.html"
}
}
func (c *UserController) Modify() {
if c.Ctx.Input.IsPost() {
json := map[string]interface{}{
"code": 400,
"text": "提交数据错误",
}
form := &forms.UserModifyForm{}
valid := &validation.Validation{}
if err := c.ParseForm(form); err == nil {
if ok, err := valid.Valid(form); err != nil {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
} else if ok {
user, err := models.DefaultUserManager.Modify(form.Id, form.Name, form.Gender, form.BirthdayTime, form.Tel, form.Email, form.Addr, form.Remark)
if err == nil {
json = map[string]interface{}{
"code": 200,
"text": "更新成功",
"result": user,
}
} else {
json = map[string]interface{}{
"code": 500,
"text": "服务器错误",
}
}
} else {
json["result"] = valid.Errors
}
} else {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
}
c.Data["json"] = json
c.ServeJSON()
} else {
//get
pk, _ := c.GetInt("pk")
c.TplName = "user/modify.html"
c.Data["object"] = models.DefaultUserManager.GetById(pk)
}
}
func (c *UserController) Delete() {
pk, _ := c.GetInt("pk")
models.DefaultUserManager.DeleteById(pk)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "删除成功",
"result": nil, //可以返回删除的用户
}
c.ServeJSON()
}
func (c *UserController) Lock() {
pk, _ := c.GetInt("pk")
models.DefaultUserManager.SetStatusById(pk, 1)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "锁定成功",
"result": nil, //可以返回删除的用户
}
c.ServeJSON()
}
func (c *UserController) UnLock() {
pk, _ := c.GetInt("pk")
models.DefaultUserManager.SetStatusById(pk, 0)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "解锁成功",
"result": nil, //可以返回删除的用户
}
c.ServeJSON()
}
func (c *UserController) Password() {
if c.Ctx.Input.IsPost() {
json := map[string]interface{}{
"code": 400,
"text": "提交数据错误",
}
form := &forms.UserPasswordForm{User: c.User}
valid := &validation.Validation{}
if err := c.ParseForm(form); err == nil {
if ok, err := valid.Valid(form); err != nil {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
} else if ok {
err := models.DefaultUserManager.UpdatePassword(c.User.Id, form.Password)
if err == nil {
json = map[string]interface{}{
"code": 200,
"text": "修改密码成功",
}
} else {
json = map[string]interface{}{
"code": 500,
"text": "服务器错误",
}
}
} else {
json["result"] = valid.Errors
}
} else {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
}
c.Data["json"] = json
c.ServeJSON()
} else {
c.TplName = "user/password.html"
}
}
D:\Workspace\Go\src\gocmdb\server\forms\user.go
package forms
import (
"strings"
"time"
"gocmdb/server/models"
"github.com/astaxie/beego/validation"
)
type UserCreateForm struct {
Name string `form:"name"`
Password string `form:"password"`
PasswordVerify string `form:"passwordVerify"`
Gender int `form:"gender"`
Birthday string `form:"birthday"`
Tel string `form:"tel"`
Email string `form:"email"`
Addr string `form:"addr"`
Remark string `form:"remark"`
BirthdayTime *time.Time
}
func (f *UserCreateForm) Valid(v *validation.Validation) {
f.Name = strings.TrimSpace(f.Name)
f.Password = strings.TrimSpace(f.Password)
f.PasswordVerify = strings.TrimSpace(f.PasswordVerify)
f.Tel = strings.TrimSpace(f.Tel)
f.Email = strings.TrimSpace(f.Email)
f.Addr = strings.TrimSpace(f.Addr)
f.Remark = strings.TrimSpace(f.Remark)
v.AlphaDash(f.Name, "name.name").Message("用户名只能由数字、英文字母、中划线和下划线组成")
v.MinSize(f.Name, 5, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
v.MaxSize(f.Name, 32, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
if _, ok := v.ErrorsMap["name"]; !ok && models.DefaultUserManager.GetByName(f.Name) != nil {
v.SetError("name", "用户名已存在")
}
v.MinSize(f.Password, 6, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
v.MaxSize(f.Password, 32, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
if f.PasswordVerify != f.PasswordVerify {
v.SetError("passwordVerify", "两次输入密码不一致")
}
v.Range(f.Gender, 0, 1, "gender.gender").Message("性别选择不正确")
if birthday, err := time.Parse("2006-01-02", f.Birthday); err != nil {
v.SetError("birthday", "出生日期不正确")
} else {
f.BirthdayTime = &birthday
}
v.Phone(f.Tel, "tel.tel").Message("电话格式不正确")
v.Email(f.Email, "email.email").Message("邮箱格式不正确")
v.MaxSize(f.Addr, 512, "addr.addr").Message("住址长度必须在512个字符之内")
v.MaxSize(f.Remark, 512, "remark.remark").Message("备注长度必须在512个字符之内")
}
type UserModifyForm struct {
Id int `form:"id"`
Name string `form:"name"`
Gender int `form:"gender"`
Birthday string `form:"birthday"`
Tel string `form:"tel"`
Email string `form:"email"`
Addr string `form:"addr"`
Remark string `form:"remark"`
BirthdayTime *time.Time
}
func (f *UserModifyForm) Valid(v *validation.Validation) {
f.Name = strings.TrimSpace(f.Name)
f.Tel = strings.TrimSpace(f.Tel)
f.Email = strings.TrimSpace(f.Email)
f.Addr = strings.TrimSpace(f.Addr)
f.Remark = strings.TrimSpace(f.Remark)
if models.DefaultUserManager.GetById(f.Id) == nil {
v.SetError("error", "操作对象不存在")
return
}
v.AlphaDash(f.Name, "name.name").Message("用户名只能由数字、英文字母、中划线和下划线组成")
v.MinSize(f.Name, 5, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
v.MaxSize(f.Name, 32, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
if _, ok := v.ErrorsMap["name"]; !ok {
if user := models.DefaultUserManager.GetByName(f.Name); user != nil && user.Id != f.Id {
v.SetError("name", "用户名已存在")
}
}
v.Range(f.Gender, 0, 1, "gender.gender").Message("性别选择不正确")
if birthday, err := time.Parse("2006-01-02", f.Birthday); err != nil {
v.SetError("birthday", "出生日期不正确")
} else {
f.BirthdayTime = &birthday
}
v.Phone(f.Tel, "tel.tel").Message("电话格式不正确")
v.Email(f.Email, "email.email").Message("邮箱格式不正确")
v.MaxSize(f.Addr, 512, "addr.addr").Message("住址长度必须在512个字符之内")
v.MaxSize(f.Remark, 512, "remark.remark").Message("备注长度必须在512个字符之内")
}
type UserPasswordForm struct {
OldPassword string `form:"oldPassword"`
Password string `form:"password"`
PasswordVerify string `form:"passwordVerify"`
User *models.User
}
func (f *UserPasswordForm) Valid(v *validation.Validation) {
f.OldPassword = strings.TrimSpace(f.OldPassword)
f.Password = strings.TrimSpace(f.Password)
f.PasswordVerify = strings.TrimSpace(f.PasswordVerify)
if !f.User.ValidatePassword(f.OldPassword) {
v.SetError("oldPassword", "密码不正确")
}
v.MinSize(f.Password, 6, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
v.MaxSize(f.Password, 32, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
if f.PasswordVerify != f.PasswordVerify {
v.SetError("passwordVerify", "两次输入密码不一致")
}
}
D:\Workspace\Go\src\gocmdb\server\routers\router.go
beego.AutoRouter(&controllers.UserController{})
D:\Workspace\Go\src\gocmdb\server\views\user\create.html
D:\Workspace\Go\src\gocmdb\server\views\user\modify.html
D:\Workspace\Go\src\gocmdb\server\views\user\password.html
D:\Workspace\Go\src\gocmdb\server\controllers\user.go
type TokenController struct {
auth.LoginRequiredController
}
func (c *TokenController) Generate() {
if c.Ctx.Input.IsPost() {
pk, _ := c.GetInt("pk")
models.DefaultTokenManager.GenerateByUser(models.DefaultUserManager.GetById(pk))
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "生成Token成功",
"result": nil, //可以返回Token
}
c.ServeJSON()
} else {
pk, _ := c.GetInt("pk")
c.Data["object"] = models.DefaultUserManager.GetById(pk)
c.TplName = "token/index.html"
}
}
D:\Workspace\Go\src\gocmdb\server\routers\router.go
beego.AutoRouter(&controllers.TokenController{})
D:\Workspace\Go\src\gocmdb\server\views\token\index.html
用户管理
一个 dialog
创建/编辑 流程做完
锁定/解锁/删除 流程做完
当前登陆用户不能锁定/删除/解锁 自己
当前用户只能查看和生成自己的Token
修改密码
alert => sweetalert
1. ajax请求未登录返回json
2. 创建
多云管理
管理多个云平台
阿里云
腾讯云
aws
azure
华为云
京东云
青云
Openstack
...
获取虚拟机
启动
停止
重启
server/cloud
plugins/aliyun
tenant
aws
manager
instance => vm
Type string
Name string
Init(addr region accessKey, secrectKey)
TestConnect() error
GetInstances() []*Instance
StartInstance(uuid) error
StopInstance(uuid) error
RebootInstance(uuid) error
配置信息
认证
地址
Region配置
Platform
Id
Name
Type
Addr
AccessKey
SecrectKey
Region
Remark
CreatedTime
DeletedTime
SyncTime
CreateUser rel,reverse
Status
VirtualMachine
Platform 1: n
UUID
Name
CPU
Memeory
OS
PrivateAddrs
PublicAddrs
Status string
VmCreatedTime
VmExpiredTime
CreatedTime
DeletedTime
UpdatedTime
云平台管理页面
D:\Workspace\Go\src\gocmdb\server\conf\app.conf
home=UserPageController.Index
云平台管理页面控制器
D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go
package controllers
type CloudPlatformPageController struct {
LayoutController
}
func (c *CloudPlatformPageController) Index() {
c.Data["expand"] = "cloud_management"
c.Data["menu"] = "cloud_platform_management"
c.TplName = "cloud_platform_page/index.html"
c.LayoutSections["LayoutScript"] = "cloud_platform_page/index.script.html"
}
D:\Workspace\Go\src\gocmdb\server\views\cloud_platform_page\index.html
D:\Workspace\Go\src\gocmdb\server\views\cloud_platform_page\index.script.html
D:\Workspace\Go\src\gocmdb\server\routers\router.go
// 认证
beego.AutoRouter(&auth.AuthController{})
// 用户页面
beego.AutoRouter(&controllers.UserPageController{})
// 用户
beego.AutoRouter(&controllers.UserController{})
beego.AutoRouter(&controllers.TokenController{})
// 云平台页面
beego.AutoRouter(&controllers.CloudPlatformPageController{})
云平台管理控制器
D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go
type CloudPlatformController struct {
auth.LoginRequiredController
}
func (c *CloudPlatformController) List() {
//draw,start, length, q
draw, _ := c.GetInt("draw")
start, _ := c.GetInt64("start")
length, _ := c.GetInt("length")
q := strings.TrimSpace(c.GetString("q"))
result, total, queryTotal := models.DefaultCloudPlatformManager.Query(q, start, length)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "获取成功",
"result": result,
"draw": draw,
"recordsTotal": total,
"recordsFiltered": queryTotal,
}
c.ServeJSON()
}
func (c *CloudPlatformController) Create() {
if c.Ctx.Input.IsPost() {
form := &forms.CloudPlatformCreateForm{}
valid := &validation.Validation{}
json := map[string]interface{}{
"code": 400,
"text": "提交数据错误",
"result": nil,
}
if err := c.ParseForm(form); err != nil {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
} else {
if ok, err := valid.Valid(form); err != nil {
valid.SetError("error", err.Error())
json["result"] = valid.Errors
} else if ok {
result, err := models.DefaultCloudPlatformManager.Create(
form.Name,
form.Type,
form.Addr,
form.Region,
form.AccessKey,
form.SecrectKey,
form.Remark,
c.User,
)
if err == nil {
json = map[string]interface{}{
"code": 200,
"text": "创建成功",
"result": result,
}
} else {
json = map[string]interface{}{
"code": 500,
"text": "创建失败, 请重试",
"result": nil,
}
}
} else {
json["result"] = valid.Errors
}
}
c.Data["json"] = json
c.ServeJSON()
} else {
c.TplName = "cloud_platform/create.html"
c.Data["types"] = cloud.DefaultManager.Plugins
}
}
func (c *CloudPlatformController) Delete() {
if c.Ctx.Input.IsPost() {
pk, _ := c.GetInt("pk")
models.DefaultCloudPlatformManager.DeleteById(pk)
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "删除成功",
"result": nil,
}
c.ServeJSON()
}
云平台数据显示list
云平台创建create
云平台修改modify
云平台操作禁用disable、启用enable、删除delete
D:\Workspace\Go\src\gocmdb\server\models\cloud.go
package models
import (
"time"
"github.com/astaxie/beego/orm"
)
type CloudPlatform struct {
Id int `orm:"column(id);" json:"id"`
Name string `orm:"column(name);size(64);" json:"name"`
Type string `orm:"column(type);size(32);" json:"type"`
Addr string `orm:"column(addr);size(1024);" json:"addr"`
AccessKey string `orm:"column(access_key);size(1024);" json:"-"`
SecrectKey string `orm:"column(secrect_key);size(1024);" json:"-"`
Region string `orm:"column(region);size(64);" json:"region"`
Remark string `orm:"column(remark);size(1024);" json:"remark"`
CreatedTime *time.Time `orm:"column(created_time);type(datetime);auto_now_add;" json:"created_time"`
DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null;" json:"deleted_time"`
SyncTime *time.Time `orm:"column(sync_time);type(datetime);null;" json:"sync_time"`
User *User `orm:"column(user);rel(fk);" json:"user"`
Status int `orm:"column(status);" json:"status"`
VirtualMachines []*VirtualMachine `orm:"reverse(many);" json:"virtual_machines"`
}
func (p *CloudPlatform) IsEnable() bool {
return p.Status == 0
}
type CloudPlatformManager struct{}
func (m *CloudPlatformManager) Query(q string, start int64, length int) ([]*CloudPlatform, int64, int64) {
ormer := orm.NewOrm()
queryset := ormer.QueryTable(&CloudPlatform{})
condition := orm.NewCondition()
condition = condition.And("deleted_time__isnull", true)
total, _ := queryset.SetCond(condition).Count()
qtotal := total
if q != "" {
query := orm.NewCondition()
query = query.Or("name__icontains", q)
query = query.Or("addr__icontains", q)
query = query.Or("remark__icontains", q)
query = query.Or("region__icontains", q)
condition = condition.AndCond(query)
qtotal, _ = queryset.SetCond(condition).Count()
}
var result []*CloudPlatform
queryset.SetCond(condition).Limit(length).Offset(start).All(&result)
return result, total, qtotal
}
func NewCloudPlatformManager() *CloudPlatformManager {
return &CloudPlatformManager{}
}
func (m *CloudPlatformManager) GetByName(name string) *CloudPlatform {
ormer := orm.NewOrm()
// var result CloudPlatform
result := &CloudPlatform{}
err := ormer.QueryTable(&CloudPlatform{}).Filter("deleted_time__isnull", true).Filter("name__exact", name).One(result)
if err == nil {
return result
}
return nil
}
func (m *CloudPlatformManager) Create(name, typ, addr, region, accessKey, secrectKey, remark string, user *User) (*CloudPlatform, error) {
ormer := orm.NewOrm()
result := &CloudPlatform{
Name: name,
Type: typ,
Addr: addr,
Region: region,
AccessKey: accessKey,
SecrectKey: secrectKey,
Remark: remark,
User: user,
Status: 0,
}
if _, err := ormer.Insert(result); err != nil {
return nil, err
}
return result, nil
}
func (m *CloudPlatformManager) DeleteById(id int) error {
orm.NewOrm().QueryTable(&CloudPlatform{}).Filter("Id__exact", id).Update(orm.Params{"DeletedTime": time.Now()})
return nil
}
type VirtualMachine struct {
Id int `orm:"column(id)" json:"id"`
Platform *CloudPlatform `orm:"column(platform);rel(fk);" json:"platform"`
UUID string `orm:"column(uuid);size(128);" json:"uuid"`
Name string `orm:"column(name);size(64);" json:"name"`
CPU int `orm:"column(cpu);" json:"cpu"`
Mem int64 `orm:"column(mem);" json:"mem"`
OS string `orm:"column(os);size(128);" json:"os"`
PrivateAddrs string `orm:"column(private_addrs);size(1024);" json:"private_addrs"`
PublicAddrs string `orm:"column(public_addrs);size(1024);" json:"public_addrs"`
Status string `orm:"column(status);size(32);" json:"status"`
VmCreatedTime string `orm:"column(vm_created_time);" json:"vm_created_time"`
VmExpiredTime string `orm:"column(vm_expired_time);" json:"vm_expired_time"`
CreatedTime *time.Time `orm:"column(created_time);auto_now_add;type(datetime);" json:"created_time"`
DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null" json:"deleted_time"`
UpdatedTime *time.Time `orm:"column(updated_time);auto_now;type(datetime);" json:"updated_time"`
}
type VirtualMachineManager struct{}
func NewVirtualMachineManager() *VirtualMachineManager {
return &VirtualMachineManager{}
}
func (m *VirtualMachineManager) Query(q string, start int64, length int) ([]*VirtualMachine, int64, int64) {
ormer := orm.NewOrm()
queryset := ormer.QueryTable(&VirtualMachine{})
condition := orm.NewCondition()
condition = condition.And("deleted_time__isnull", true)
total, _ := queryset.SetCond(condition).Count()
qtotal := total
if q != "" {
query := orm.NewCondition()
query = query.Or("name__icontains", q)
query = query.Or("public_addrs__icontains", q)
query = query.Or("private_addrs__icontains", q)
query = query.Or("os__icontains", q)
condition = condition.AndCond(query)
qtotal, _ = queryset.SetCond(condition).Count()
}
var result []*VirtualMachine
queryset.SetCond(condition).Limit(length).Offset(start).All(&result)
return result, total, qtotal
}
var DefaultCloudPlatformManager = NewCloudPlatformManager()
var DefaultVirtualMachineManager = NewVirtualMachineManager()
func init() {
orm.RegisterModel(&CloudPlatform{}, new(VirtualMachine))
}
D:\Workspace\Go\src\gocmdb\server\forms\cloud.go
package forms
import (
"strings"
"github.com/astaxie/beego/validation"
"gocmdb/server/cloud"
"gocmdb/server/models"
)
type CloudPlatformCreateForm struct {
Name string `form:"name"`
Type string `form:"type"`
Addr string `form:"addr"`
AccessKey string `form:"access_key"`
SecrectKey string `form:"secrect_key"`
Region string `form:"region"`
Remark string `form:"remark"`
}
func (f *CloudPlatformCreateForm) Valid(v *validation.Validation) {
f.Name = strings.TrimSpace(f.Name)
f.Type = strings.TrimSpace(f.Type)
f.Addr = strings.TrimSpace(f.Addr)
f.AccessKey = strings.TrimSpace(f.AccessKey)
f.SecrectKey = strings.TrimSpace(f.SecrectKey)
f.Region = strings.TrimSpace(f.Region)
f.Remark = strings.TrimSpace(f.Remark)
v.AlphaDash(f.Name, "name.name").Message("名字只能由大小写英文、数字、下划线和中划线组成")
v.MinSize(f.Name, 5, "name.name").Message("名字长度必须在%d-%d之内", 5, 32)
v.MaxSize(f.Name, 32, "name.name").Message("名字长度必须在%d-%d之内", 5, 32)
if _, ok := v.ErrorsMap["name"]; !ok && models.DefaultCloudPlatformManager.GetByName(f.Name) != nil {
v.SetError("name", "名称已存在")
}
v.MinSize(f.Addr, 1, "addr.addr").Message("地址不能为空且长度必须在%d之内", 1024)
v.MaxSize(f.Addr, 1024, "addr.addr").Message("地址不能为空且长度必须在%d之内", 1024)
v.MinSize(f.Region, 1, "region.region").Message("区域不能为空且长度必须在%d之内", 64)
v.MaxSize(f.Region, 64, "region.region").Message("区域不能为空且长度必须在%d之内", 64)
v.MinSize(f.AccessKey, 1, "access_key.access_key").Message("AccessKey不能为空且长度必须在%d之内", 1024)
v.MaxSize(f.AccessKey, 1024, "access_key.access_key").Message("AccessKey不能为空不能为空且长度必须在%d之内", 1024)
v.MinSize(f.SecrectKey, 1, "secrect_key.secrect_key").Message("SecrectKey不能为空且长度必须在%d之内", 1024)
v.MaxSize(f.SecrectKey, 1024, "secrect_key.secrect_key").Message("SecrectKey不能为空且长度必须在%d之内", 1024)
v.MaxSize(f.Remark, 1024, "remark.remark").Message("备注长度必须在%d之内", 1024)
if sdk, ok := cloud.DefaultManager.Cloud(f.Type); !ok {
v.SetError("type", "类型错误")
} else if !v.HasErrors() {
sdk.Init(f.Addr, f.Region, f.AccessKey, f.SecrectKey)
if sdk.TestConnect() != nil {
v.SetError("type", "配置参数错误")
}
}
}
D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go
type VirtualMachinePageController struct {
LayoutController
}
func (c *VirtualMachinePageController) Index() {
c.Data["expand"] = "cloud_management"
c.Data["menu"] = "virtual_machine_management"
c.TplName = "virtual_machine_page/index.html"
c.LayoutSections["LayoutScript"] = "virtual_machine_page/index.script.html"
}
type VirtualMachineController struct {
auth.LoginRequiredController
}
func (c *VirtualMachineController) List() {
//draw,start, length, q
draw, _ := c.GetInt("draw")
start, _ := c.GetInt64("start")
length, _ := c.GetInt("length")
q := strings.TrimSpace(c.GetString("q"))
result, total, queryTotal := models.DefaultVirtualMachineManager.Query(q, start, length)
c.Data["json"] = map[string]interface{}{
"code": 200,
"text": "获取成功",
"result": result,
"draw": draw,
"recordsTotal": total,
"recordsFiltered": queryTotal,
}
c.ServeJSON()
}
D:\Workspace\Go\src\gocmdb\server\routers\router.go
package routers
import (
"github.com/astaxie/beego"
"gocmdb/server/controllers"
"gocmdb/server/controllers/auth"
)
func init() {
// 认证
beego.AutoRouter(&auth.AuthController{})
// 用户页面
beego.AutoRouter(&controllers.UserPageController{})
// 用户
beego.AutoRouter(&controllers.UserController{})
beego.AutoRouter(&controllers.TokenController{})
// 云平台页面
beego.AutoRouter(&controllers.CloudPlatformPageController{})
// 云平台
beego.AutoRouter(&controllers.CloudPlatformController{})
// 云主机页面
beego.AutoRouter(&controllers.VirtualMachinePageController{})
// 云主机
beego.AutoRouter(&controllers.VirtualMachineController{})
}
虚拟机页面
虚拟机操作
D:\Workspace\Go\src\gocmdb\server\cloud\base.go
package cloud
type Instance struct {
}
type ICloud interface {
Type() string
Name() string
Init(string, string, string, string)
TestConnect() error
GetInstance() []*Instance
StartInstance(string) error
StopInstance(string) error
RebootInstance(string) error
}
D:\Workspace\Go\src\gocmdb\server\cloud\manager.go
package cloud
type Manager struct {
Plugins map[string]ICloud
}
func NewManager() *Manager {
return &Manager{
Plugins: make(map[string]ICloud),
}
}
func (m *Manager) Register(c ICloud) {
m.Plugins[c.Type()] = c
}
func (m *Manager)Cloud(typ string) (ICloud, bool) {
cloud, ok := m.Plugins[typ]
return cloud, ok
}
var DefaultManager = NewManager()
D:\Workspace\Go\src\gocmdb\server\cloud\plugins\init.go
package plugins
import (
_ "gocmdb/server/cloud/plugins/tenant"
)
腾讯云操作
D:\Workspace\Go\src\gocmdb\server\cloud\plugins\tenant\tenant.go
package tenant
import (
"fmt"
"gocmdb/server/cloud"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)
type TenantCloud struct {
addr string
region string
accessKey string
secrectKey string
credential *common.Credential
profile *profile.ClientProfile
}
func (c *TenantCloud) Type() string {
return "tenant"
}
func (c *TenantCloud) Name() string {
return "腾讯云"
}
func (c *TenantCloud) Init(addr, region, accessKey, secrectKey string) {
c.addr = addr
c.region = region
c.accessKey = accessKey
c.secrectKey = secrectKey
c.credential = common.NewCredential(c.accessKey, c.secrectKey)
c.profile = profile.NewClientProfile()
c.profile.HttpProfile.Endpoint = c.addr
}
func (c *TenantCloud) TestConnect() error {
client, err := cvm.NewClient(c.credential, c.region, c.profile)
if err != nil {
fmt.Println(err)
return err
}
request := cvm.NewDescribeRegionsRequest()
_, err = client.DescribeRegions(request)
fmt.Println(err)
return err
}
func (c *TenantCloud) GetInstance() []*cloud.Instance {
return nil
}
func (c *TenantCloud) StartInstance(uuid string) error {
return nil
}
func (c *TenantCloud) StopInstance(uuid string) error {
return nil
}
func (c *TenantCloud) RebootInstance(uuid string) error {
return nil
}
func init() {
cloud.DefaultManager.Register(new(TenantCloud))
}
D:\Workspace\Go\src\gocmdb\server\views\cloud_platform\create.html
D:\Workspace\Go\src\gocmdb\server\views\virtual_machine_page\index.html
D:\Workspace\Go\src\gocmdb\server\views\virtual_machine_page\index.script.html
阿里云操作
虚拟机同步
用户操作虚拟机
D:\Workspace\Go\src\gocmdb\server\cloud.go
package main
import (
"flag"
"fmt"
"os"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
"gocmdb/server/models"
)
func main() {
// 初始化命令行参数
h := flag.Bool("h", false, "help")
help := flag.Bool("help", false, "help")
verbose := flag.Bool("v", false, "verbose")
flag.Usage = func() {
fmt.Println("usage: cloud -h")
flag.PrintDefaults()
}
// 解析命令行参数
flag.Parse()
if *h || *help {
flag.Usage()
os.Exit(0)
}
// 设置日志到文件
beego.SetLogger("file", `{
"filename" : "logs/cloud.log",
"level" : 7}`,
)
if !*verbose {
//删除控制台日志
beego.BeeLogger.DelLogger("console")
} else {
orm.Debug = true
}
// 初始化orm
orm.RegisterDriver("mysql", orm.DRMySQL)
orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))
// 测试数据库连接是否正常
if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
beego.Error("数据库连接错误")
os.Exit(-1)
}
for now := range time.Tick(10 * time.Second) {
fmt.Println(now)
platforms, _, _ := models.DefaultCloudPlatformManager.Query("", 0, 0)
for _, platform := range platforms {
if !platform.IsEnable() {
continue
}
fmt.Println(platform)
}
}
}
10点
腾讯云
a 10点
b 10点
=>
a
b
删了b
11 点
a 11点
11 点之前 更新未删除
多个云平台
A.
for 云平台
B.
for 虚拟机
C.
D. 统一将当前平台中的状态更新(platform过滤)
E. 统一将数据库中的更新
b B KB MB GB TB PB
/8
b
function FileSizeb(size) {
if(size < 8) {
return size.toFixed(2) + "b";
}
size /= 8;
var index = 0;
var units = ["B", "KB", "MB", "GB", "TB", "PB"];
while(size >= 1024) {
size /= 1024;
index += 1;
}
return size.toFixed(2) + units[index];
}