├── .gitignore
├── app
├── gateway
│ └── main.go
├── storage
│ ├── handler
│ │ ├── basic.go
│ │ ├── net.go
│ │ ├── storage.go
│ │ ├── http.go
│ │ └── node.go
│ ├── main.go
│ └── conf
│ │ └── conf.go
├── master
│ ├── admin
│ │ ├── static
│ │ │ └── vendor
│ │ │ │ └── bootstrap
│ │ │ │ ├── fonts
│ │ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ │ └── glyphicons-halflings-regular.woff2
│ │ │ │ ├── js
│ │ │ │ └── npm.js
│ │ │ │ └── css
│ │ │ │ └── bootstrap-theme.min.css
│ │ └── views
│ │ │ └── console.html
│ ├── main.go
│ ├── handler
│ │ ├── admin.go
│ │ ├── node.go
│ │ └── net.go
│ └── conf
│ │ └── conf.go
└── api
│ ├── handler
│ ├── basic.go
│ ├── node.go
│ └── api.go
│ ├── main.go
│ └── conf
│ └── conf.go
├── test
├── 1.gif
├── 2.png
├── timg.jpeg
├── index.html
└── main.go
├── lib
├── packet
│ ├── auth.go
│ ├── packet.go
│ └── node.go
├── code
│ └── code.go
├── hardware
│ ├── linux
│ │ ├── cpu.go
│ │ └── memory.go
│ └── hardware.go
├── filetype
│ └── filetype.go
├── command
│ └── command.go
├── cmd
│ └── cmd.go
├── protocol
│ └── protocol.go
├── ini
│ └── ini.go
├── utils.go
└── logd
│ └── log.go
├── vendor
├── github.com
│ └── jinzhu
│ │ └── gorm
│ │ ├── dialects
│ │ └── mysql
│ │ │ └── mysql.go
│ │ ├── test_all.sh
│ │ ├── model.go
│ │ ├── interface.go
│ │ ├── callback_row_query.go
│ │ ├── License
│ │ ├── field.go
│ │ ├── wercker.yml
│ │ ├── errors.go
│ │ ├── README.md
│ │ ├── callback_delete.go
│ │ ├── callback_query.go
│ │ ├── dialect_sqlite3.go
│ │ ├── logger.go
│ │ ├── callback_save.go
│ │ ├── callback_update.go
│ │ ├── dialect.go
│ │ ├── dialect_postgres.go
│ │ ├── search.go
│ │ ├── dialect_common.go
│ │ ├── dialect_mysql.go
│ │ ├── callback_create.go
│ │ ├── join_table_handler.go
│ │ ├── utils.go
│ │ ├── callback.go
│ │ ├── callback_query_preload.go
│ │ └── association.go
└── vendor.json
├── README.md
├── conf
├── master.template
├── store.ini.template
└── api.template
├── db
├── user.go
├── model.go
├── bucket.go
├── metadata.go
└── db.go
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/*
2 | conf/*.ini
3 | log/
4 | .idea
--------------------------------------------------------------------------------
/app/gateway/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 |
5 | }
--------------------------------------------------------------------------------
/test/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/test/1.gif
--------------------------------------------------------------------------------
/test/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/test/2.png
--------------------------------------------------------------------------------
/test/timg.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/test/timg.jpeg
--------------------------------------------------------------------------------
/lib/packet/auth.go:
--------------------------------------------------------------------------------
1 | package packet
2 |
3 | //Auth 建立授权.
4 | func Auth() {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialects/mysql/mysql.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import _ "github.com/go-sql-driver/mysql"
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # goss
2 | 分布式对象存储
3 |
4 | ### api
5 | `api` 为上传和访问服务
6 | ### storage
7 | `storage` 为存储服务
8 | ### master
9 | `master` 为集群管理服务
--------------------------------------------------------------------------------
/lib/code/code.go:
--------------------------------------------------------------------------------
1 | package code
2 |
3 | const (
4 | SUCCESS = 1000 //状态正常.
5 | READ_TCP_ERROR = 1001 //读取tcp内容失败.
6 | PARSE_JSON_ERROR = 1002 //解析json失败.
7 | )
8 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/test_all.sh:
--------------------------------------------------------------------------------
1 | dialects=("postgres" "mysql" "mssql" "sqlite")
2 |
3 | for dialect in "${dialects[@]}" ; do
4 | GORM_DIALECT=${dialect} go test
5 | done
6 |
--------------------------------------------------------------------------------
/app/storage/handler/basic.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | //StorageService 存储节点信息.
4 | type StorageService struct {
5 | Port string
6 | Addr string
7 | MasterNode string
8 | }
9 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Hello Goss
9 |
10 |
--------------------------------------------------------------------------------
/conf/master.template:
--------------------------------------------------------------------------------
1 | #节点配置
2 | node_name=master_node
3 | node_ip = 127.0.0.1
4 | node_port = 8000
5 | node_web_port=8080
6 |
7 | #基础配置
8 | log_path = ../log/
9 | token = 202cb962ac59075b964b07152d234b70
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goss-io/goss/HEAD/app/master/admin/static/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/app/api/handler/basic.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | //APIService Api服务信息.
4 | type APIService struct {
5 | Port string
6 | Addr string
7 | MasterNode string
8 | Storage []string
9 | }
10 |
--------------------------------------------------------------------------------
/db/user.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | type User struct {
4 | Model
5 | Name string
6 | Account string
7 | Password string
8 | }
9 |
10 | //TableName.
11 | func (User) TableName() string {
12 | return "user"
13 | }
14 |
--------------------------------------------------------------------------------
/conf/store.ini.template:
--------------------------------------------------------------------------------
1 |
2 | #节点配置
3 | node_name=storage_node_1
4 | node_ip = 127.0.0.1
5 | node_port = 9001
6 |
7 | #基础配置
8 | master_node=127.0.0.1:8000
9 | storage_root=/disk/storage/
10 | log_path = ../log/
11 | token = 202cb962ac59075b964b07152d234b70
--------------------------------------------------------------------------------
/conf/api.template:
--------------------------------------------------------------------------------
1 | #数据库配置
2 | db_host = 127.0.0.1
3 | db_user = root
4 | db_name = goss
5 | db_pwd = 123
6 | db_port = 3306
7 |
8 | #节点配置
9 | node_name=api_node_1
10 | node_ip = 127.0.0.1
11 | node_port = 80
12 |
13 | #基础配置
14 | log_path = ../log/
15 | master_node=127.0.0.1:8000
16 | token = 202cb962ac59075b964b07152d234b70
--------------------------------------------------------------------------------
/db/model.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import "time"
4 |
5 | //Model .
6 | type Model struct {
7 | ID int `gorm:"primary_key" json:"id"`
8 | CreatedAt time.Time `gorm:"created_at"`
9 | UpdatedAt time.Time `gorm:"created_at"`
10 | CreateTime string `gorm:"-"`
11 | UpdateTime string `gorm:"-"`
12 | }
13 |
--------------------------------------------------------------------------------
/lib/hardware/linux/cpu.go:
--------------------------------------------------------------------------------
1 | package linux
2 |
3 | import "github.com/Goss-io/goss/lib/command"
4 |
5 | //CpuNum Cpu核数.
6 | func CpuNum() string {
7 | num, err := command.Exec("cat /proc/cpuinfo | grep 'cpu cores' | head -1 | awk -F ':' '{print $2}'")
8 | if err != nil {
9 | num = "0"
10 | }
11 |
12 | num = num + "核"
13 | return num
14 | }
15 |
--------------------------------------------------------------------------------
/app/master/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/Goss-io/goss/app/master/conf"
7 | "github.com/Goss-io/goss/app/master/handler"
8 | "github.com/Goss-io/goss/lib/cmd"
9 | )
10 |
11 | func main() {
12 | cmd := cmd.New()
13 |
14 | //加载配置文件.
15 | conf.Load(cmd)
16 | log.Println("node name:", conf.Conf.Node.Name)
17 |
18 | master := handler.NewMaster()
19 | master.Start()
20 | }
21 |
--------------------------------------------------------------------------------
/lib/filetype/filetype.go:
--------------------------------------------------------------------------------
1 | package filetype
2 |
3 | import "strings"
4 |
5 | var tyeList = map[string]string{
6 | "47494638": "image/gif",
7 | "ffd8ffe": "image/jpeg",
8 | "89504e47": "image/png",
9 | "4740111": "video/mp2t",
10 | "23455854": "audio/mpegurl",
11 | }
12 |
13 | func Parse(str string) string {
14 | for k, v := range tyeList {
15 | if strings.Contains(str, k) {
16 | return v
17 | }
18 | }
19 | return "other"
20 | }
21 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/model.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import "time"
4 |
5 | // Model base model definition, including fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, which could be embedded in your models
6 | // type User struct {
7 | // gorm.Model
8 | // }
9 | type Model struct {
10 | ID uint `gorm:"primary_key"`
11 | CreatedAt time.Time
12 | UpdatedAt time.Time
13 | DeletedAt *time.Time `sql:"index"`
14 | }
15 |
--------------------------------------------------------------------------------
/lib/hardware/linux/memory.go:
--------------------------------------------------------------------------------
1 | package linux
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/Goss-io/goss/lib"
7 | "github.com/Goss-io/goss/lib/command"
8 | )
9 |
10 | //MemSize 内存大小.
11 | func MemSize() string {
12 | num, err := command.Exec("cat /proc/meminfo | grep 'MemTotal' | awk -F ':' '{print $2}'")
13 | if err != nil {
14 | num = "0"
15 | }
16 |
17 | size := (lib.ParseInt(num) / (1000 * 1000))
18 | mem := fmt.Sprintf("%dG", size)
19 |
20 | return mem
21 | }
22 |
--------------------------------------------------------------------------------
/app/storage/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/Goss-io/goss/app/storage/conf"
7 | "github.com/Goss-io/goss/app/storage/handler"
8 | "github.com/Goss-io/goss/lib/cmd"
9 | )
10 |
11 | func main() {
12 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
13 |
14 | cmd := cmd.New()
15 |
16 | //加载配置文件.
17 | conf.Load(cmd)
18 | log.Println("node name:", conf.Conf.Node.Name)
19 |
20 | storage := handler.NewStorageService()
21 | storage.Start()
22 | }
23 |
--------------------------------------------------------------------------------
/lib/command/command.go:
--------------------------------------------------------------------------------
1 | package command
2 |
3 | import (
4 | "os/exec"
5 | "strings"
6 | )
7 |
8 | func Exec(shell string) (msg string, err error) {
9 | cmd := exec.Command("/bin/bash", "-c", shell)
10 | b, err := cmd.Output()
11 | if err != nil {
12 | return msg, err
13 | }
14 |
15 | msg = string(b)
16 | msg = strings.Replace(msg, " ", "", -1)
17 | msg = strings.Replace(msg, "\n", "", -1)
18 | msg = strings.Replace(msg, "\t", "", -1)
19 | msg = strings.Replace(msg, "kB", "", -1)
20 | return msg, nil
21 | }
22 |
--------------------------------------------------------------------------------
/db/bucket.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/jinzhu/gorm"
5 | )
6 |
7 | type Bucket struct {
8 | Model
9 | AccessKey string
10 | SecretKey string
11 | Name string
12 | UserID int
13 | Host string
14 | }
15 |
16 | func (Bucket) TableName() string {
17 | return "bucket"
18 | }
19 |
20 | //Query .
21 | func (b *Bucket) Query() error {
22 | err := Db.Model(&b).Where("host = ?", b.Host).First(&b).Error
23 | if err != nil && err != gorm.ErrRecordNotFound {
24 | return err
25 | }
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/lib/cmd/cmd.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "flag"
5 | "log"
6 | "os"
7 | )
8 |
9 | func (c *Command) parse() {
10 |
11 | var confPath string
12 | flag.StringVar(&confPath, "conf", "", "conf")
13 |
14 | flag.Parse()
15 |
16 | //conf.
17 | c.Conf = confPath
18 | if confPath == "" {
19 | log.Println("请指定配置文件 -conf")
20 | os.Exit(0)
21 | }
22 | }
23 |
24 | type Command struct {
25 | Conf string
26 | }
27 |
28 | func New() *Command {
29 | command := &Command{}
30 | command.parse()
31 |
32 | return command
33 | }
34 |
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/app/api/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/Goss-io/goss/app/api/conf"
7 | "github.com/Goss-io/goss/app/api/handler"
8 | "github.com/Goss-io/goss/db"
9 | "github.com/Goss-io/goss/lib/cmd"
10 | )
11 |
12 | func main() {
13 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
14 | cmd := cmd.New()
15 |
16 | //加载配置文件.
17 | conf.Load(cmd)
18 | log.Println("node name:", conf.Conf.Node.Name)
19 |
20 | if err := db.Connection(); err != nil {
21 | log.Panicln(err)
22 | }
23 |
24 | apiSrv := handler.NewAPI()
25 | apiSrv.Start()
26 | }
27 |
--------------------------------------------------------------------------------
/vendor/vendor.json:
--------------------------------------------------------------------------------
1 | {
2 | "comment": "",
3 | "ignore": "test",
4 | "package": [
5 | {
6 | "checksumSHA1": "IqQmIPZbwgzbZD4k7YX6Rj230pY=",
7 | "path": "github.com/jinzhu/gorm",
8 | "revision": "2a1463811ee1dc85d168fd639a2d4251d030e6e5",
9 | "revisionTime": "2017-07-03T13:49:54Z"
10 | },
11 | {
12 | "checksumSHA1": "eECxSkwh0WmhLwaxHfkvcLOWS08=",
13 | "path": "github.com/jinzhu/gorm/dialects/mysql",
14 | "revision": "2a1463811ee1dc85d168fd639a2d4251d030e6e5",
15 | "revisionTime": "2017-07-03T13:49:54Z"
16 | }
17 | ],
18 | "rootPath": "goss.io/goss"
19 | }
20 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/interface.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import "database/sql"
4 |
5 | // SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB.
6 | type SQLCommon interface {
7 | Exec(query string, args ...interface{}) (sql.Result, error)
8 | Prepare(query string) (*sql.Stmt, error)
9 | Query(query string, args ...interface{}) (*sql.Rows, error)
10 | QueryRow(query string, args ...interface{}) *sql.Row
11 | }
12 |
13 | type sqlDb interface {
14 | Begin() (*sql.Tx, error)
15 | }
16 |
17 | type sqlTx interface {
18 | Commit() error
19 | Rollback() error
20 | }
21 |
--------------------------------------------------------------------------------
/lib/protocol/protocol.go:
--------------------------------------------------------------------------------
1 | package protocol
2 |
3 | //GossProtocol 传输协议号.
4 | type GossProtocol int
5 |
6 | const (
7 | CONN_AUTH GossProtocol = 1000 //连接授权.
8 | REPORT_NODE_INFO GossProtocol = 1001 //上报节点信息.
9 | MSG GossProtocol = 1002 //发送消息.
10 | ADD_NODE GossProtocol = 1003 //新增节点.
11 | REMOVE_NODE GossProtocol = 1004 //删除节点.
12 | SEND_FILE GossProtocol = 1005 //发送文件.
13 | READ_FILE GossProtocol = 1006 //读取文件.
14 | )
15 |
16 | //NodeInfo 节点信息.
17 | type NodeInfo struct {
18 | Types string `json:"types"`
19 | CpuNum string `json:"cpu_num"`
20 | MemSize string `json:"mem_size"`
21 | SourceIP string `json:"source_ip"`
22 | Name string `json:"name"`
23 | }
24 |
--------------------------------------------------------------------------------
/lib/hardware/hardware.go:
--------------------------------------------------------------------------------
1 | package hardware
2 |
3 | import (
4 | "runtime"
5 |
6 | "github.com/Goss-io/goss/lib/hardware/linux"
7 | )
8 |
9 | type cpu struct {
10 | Num string
11 | }
12 | type mem struct {
13 | Total string
14 | }
15 |
16 | //Hardware .
17 | type Hardware struct {
18 | Cpu cpu
19 | Mem mem
20 | }
21 |
22 | func New() Hardware {
23 | h := Hardware{
24 | Cpu: cpu{
25 | Num: "8核",
26 | },
27 | Mem: mem{
28 | Total: "16G",
29 | },
30 | }
31 |
32 | system := checkSystem()
33 | if system == "linux" {
34 | h.Cpu.Num = linux.CpuNum()
35 | h.Mem.Total = linux.MemSize()
36 | }
37 |
38 | return h
39 | }
40 |
41 | func checkSystem() string {
42 | system := runtime.GOOS
43 | return system
44 | }
45 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_row_query.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import "database/sql"
4 |
5 | // Define callbacks for row query
6 | func init() {
7 | DefaultCallback.RowQuery().Register("gorm:row_query", rowQueryCallback)
8 | }
9 |
10 | type RowQueryResult struct {
11 | Row *sql.Row
12 | }
13 |
14 | type RowsQueryResult struct {
15 | Rows *sql.Rows
16 | Error error
17 | }
18 |
19 | // queryCallback used to query data from database
20 | func rowQueryCallback(scope *Scope) {
21 | if result, ok := scope.InstanceGet("row_query_result"); ok {
22 | scope.prepareQuerySQL()
23 |
24 | if rowResult, ok := result.(*RowQueryResult); ok {
25 | rowResult.Row = scope.SQLDB().QueryRow(scope.SQL, scope.SQLVars...)
26 | } else if rowsResult, ok := result.(*RowsQueryResult); ok {
27 | rowsResult.Rows, rowsResult.Error = scope.SQLDB().Query(scope.SQL, scope.SQLVars...)
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/storage/handler/net.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/Goss-io/goss/app/storage/conf"
7 | "github.com/Goss-io/goss/lib/ini"
8 | "github.com/Goss-io/goss/lib/logd"
9 | )
10 |
11 | //NewStorageService 初始化存储服务.
12 | func NewStorageService() *StorageService {
13 | s := &StorageService{
14 | Port: fmt.Sprintf(":%d", conf.Conf.Node.Port),
15 | Addr: fmt.Sprintf("%s:%d", ini.GetString("node_ip"), ini.GetInt("node_port")),
16 | MasterNode: ini.GetString("master_node"),
17 | }
18 | return s
19 | }
20 |
21 | //Start .
22 | func (s *StorageService) Start() {
23 | s.checkStoragePath()
24 | go s.connMaster()
25 | s.httpSrv()
26 | }
27 |
28 | //checkStoragePath 检查存储路径.
29 | func (s *StorageService) checkStoragePath() {
30 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "初始化存储路径")
31 | if err := s.InitStoragePath(conf.Conf.Node.StorageRoot); err != nil {
32 | panic(err)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/test/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "time"
10 | )
11 |
12 | func main() {
13 | st := time.Now().Unix()
14 | for i := 1; i <= 1; i++ {
15 | upload()
16 | }
17 |
18 | et := time.Now().Unix()
19 |
20 | log.Println("共耗时:", et-st, "秒")
21 | }
22 |
23 | func upload() {
24 | filename := "./index.html"
25 | b, err := ioutil.ReadFile(filename)
26 | if err != nil {
27 | log.Printf("%+v\n", err)
28 | return
29 | }
30 |
31 | client := http.Client{}
32 | fname := fmt.Sprintf("http://blog.houzhongjian.com/%s", "index.html")
33 | req, err := http.NewRequest("PUT", fname, bytes.NewBuffer(b))
34 | if err != nil {
35 | log.Printf("%+v\n", err)
36 | return
37 | }
38 | req.Header.Add("AccessKey", "202cb962ac59075b964b07152d234b70")
39 | req.Header.Add("SecretKey", "202cb962ac59075b964b07152d234b70")
40 | _, err = client.Do(req)
41 | if err != nil {
42 | log.Printf("%+v\n", err)
43 | return
44 | }
45 |
46 | log.Println(fname)
47 | }
48 |
--------------------------------------------------------------------------------
/db/metadata.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "github.com/jinzhu/gorm"
5 | )
6 |
7 | //Metadata 元数据.
8 | type Metadata struct {
9 | Model
10 | Name string `gorm:"index"`
11 | Type string
12 | Size int64
13 | Hash string `gorm:"index"`
14 | StoreNode string
15 | StorePath string
16 | Usable bool `gorm:"index"` //节点是否可用.
17 | BucketID int `gorm:"index"`
18 | }
19 |
20 | //TableName .
21 | func (Metadata) TableName() string {
22 | return "metadata"
23 | }
24 |
25 | //Create 创建.
26 | func (m *Metadata) Create() error {
27 | return Db.Create(&m).Error
28 | }
29 |
30 | //Query.
31 | func (m *Metadata) QueryNodeIP() (list []string, err error) {
32 | metaList := []Metadata{}
33 | err = Db.Where("name = ? and bucket_id = ?", m.Name, m.BucketID).Find(&metaList).Error
34 | if err != nil && err != gorm.ErrRecordNotFound {
35 | return list, err
36 | }
37 |
38 | for _, v := range metaList {
39 | list = append(list, v.StoreNode)
40 | m.Size = v.Size
41 | m.Hash = v.Hash
42 | m.StorePath = v.StorePath
43 | m.Type = v.Type
44 | }
45 |
46 | return list, nil
47 | }
48 |
--------------------------------------------------------------------------------
/app/master/handler/admin.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 |
8 | "github.com/Goss-io/goss/lib/ini"
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | type AdminService struct {
13 | WebPort string
14 | }
15 |
16 | func NewAdmin() {
17 | adm := &AdminService{
18 | WebPort: fmt.Sprintf(":%d", ini.GetInt("node_web_port")),
19 | }
20 | adm.Start()
21 | }
22 |
23 | //Start .
24 | func (a *AdminService) Start() {
25 | r := gin.Default()
26 | r.Static("/img", "./admin/static/img/")
27 | r.Static("/css", "./admin/static/css/")
28 | r.Static("/vendor", "./admin/static/vendor/")
29 | r.LoadHTMLGlob("./admin/views/*")
30 |
31 | r.GET("/console", a.handleConsole)
32 | if err := r.Run(a.WebPort); err != nil {
33 | log.Panicln(err)
34 | }
35 | }
36 |
37 | //handleConsole .
38 | func (a *AdminService) handleConsole(c *gin.Context) {
39 |
40 | //获取所有的api节点.
41 | apiList := GetApiList()
42 |
43 | //获取所有的存储节点.
44 | storageList := GetStorageList()
45 |
46 | c.HTML(http.StatusOK, "console.html", map[string]interface{}{"apiList": apiList, "storageList": storageList})
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Goss-io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/License:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-NOW Jinzhu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/master/handler/node.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/Goss-io/goss/lib"
5 | "github.com/Goss-io/goss/lib/packet"
6 | "github.com/Goss-io/goss/lib/protocol"
7 | )
8 |
9 | //GossNode 节点信息.
10 | var GossNode = []Node{}
11 |
12 | type Node struct {
13 | Types packet.NodeTypes
14 | IP string
15 | SourceIP string //所属ip.
16 | CreateAt string
17 | Name string
18 | CpuNum string
19 | MemSize string
20 | }
21 |
22 | //GetStoreList 获取所有的存储节点.
23 | func GetStorageList() []Node {
24 | list := []Node{}
25 | for _, v := range GossNode {
26 | if v.Types == packet.NodeTypes_Storage {
27 | list = append(list, v)
28 | }
29 | }
30 |
31 | return list
32 | }
33 |
34 | //获取所有的api节点.
35 | func GetApiList() []Node {
36 | list := []Node{}
37 | for _, v := range GossNode {
38 | if v.Types == packet.NodeTypes_Api {
39 | list = append(list, v)
40 | }
41 | }
42 |
43 | return list
44 | }
45 |
46 | //RemoveNode 移除某一个切片.
47 | func RemoveNode(n *MasterService, ip string) {
48 | //根据访问ip获取节点ip.
49 | for index, v := range GossNode {
50 | if v.IP == ip {
51 | //通知对应的节点与故障节点断开连接.
52 | pkt := packet.New([]byte(v.SourceIP), lib.Hash(v.SourceIP), protocol.REMOVE_NODE)
53 | n.Conn[v.SourceIP].Write(pkt)
54 |
55 | //从NodeInfo中移除当前.
56 | GossNode = append(GossNode[:index], GossNode[index+1:]...)
57 |
58 | //删除对应的连接数据.
59 | delete(n.Conn, v.SourceIP)
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/db/db.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/Goss-io/goss/lib/ini"
8 | _ "github.com/go-sql-driver/mysql"
9 | "github.com/jinzhu/gorm"
10 | )
11 |
12 | //Db .
13 | var Db *gorm.DB
14 |
15 | //DbConfig .
16 | type DbConfig struct {
17 | Host string
18 | User string
19 | Password string
20 | Name string
21 | Port int
22 | Charset string
23 | }
24 |
25 | //Init .
26 | func Connection() error {
27 | cf := DbConfig{
28 | Host: ini.GetString("db_host"),
29 | User: ini.GetString("db_user"),
30 | Password: ini.GetString("db_pwd"),
31 | Name: ini.GetString("db_name"),
32 | Port: ini.GetInt("db_port"),
33 | Charset: ini.GetString("db_charset"),
34 | }
35 |
36 | return conndb(cf)
37 | }
38 |
39 | //conndb .
40 | func conndb(cf DbConfig) error {
41 | args := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=true",
42 | cf.User,
43 | cf.Password,
44 | cf.Host,
45 | cf.Port,
46 | cf.Name,
47 | )
48 |
49 | log.Println("args:", args)
50 | db, err := gorm.Open("mysql", args)
51 | if err != nil {
52 | log.Printf("%+v\n", err)
53 | return err
54 | }
55 |
56 | db.SingularTable(true)
57 | db.LogMode(true)
58 |
59 | autoMigrate(db)
60 | Db = db
61 |
62 | return nil
63 | }
64 |
65 | //autoMigrate .
66 | func autoMigrate(db *gorm.DB) {
67 | db.AutoMigrate(
68 | &User{},
69 | &Bucket{},
70 | &Metadata{},
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/field.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "database/sql"
5 | "errors"
6 | "fmt"
7 | "reflect"
8 | )
9 |
10 | // Field model field definition
11 | type Field struct {
12 | *StructField
13 | IsBlank bool
14 | Field reflect.Value
15 | }
16 |
17 | // Set set a value to the field
18 | func (field *Field) Set(value interface{}) (err error) {
19 | if !field.Field.IsValid() {
20 | return errors.New("field value not valid")
21 | }
22 |
23 | if !field.Field.CanAddr() {
24 | return ErrUnaddressable
25 | }
26 |
27 | reflectValue, ok := value.(reflect.Value)
28 | if !ok {
29 | reflectValue = reflect.ValueOf(value)
30 | }
31 |
32 | fieldValue := field.Field
33 | if reflectValue.IsValid() {
34 | if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
35 | fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
36 | } else {
37 | if fieldValue.Kind() == reflect.Ptr {
38 | if fieldValue.IsNil() {
39 | fieldValue.Set(reflect.New(field.Struct.Type.Elem()))
40 | }
41 | fieldValue = fieldValue.Elem()
42 | }
43 |
44 | if reflectValue.Type().ConvertibleTo(fieldValue.Type()) {
45 | fieldValue.Set(reflectValue.Convert(fieldValue.Type()))
46 | } else if scanner, ok := fieldValue.Addr().Interface().(sql.Scanner); ok {
47 | err = scanner.Scan(reflectValue.Interface())
48 | } else {
49 | err = fmt.Errorf("could not convert argument of field %s from %s to %s", field.Name, reflectValue.Type(), fieldValue.Type())
50 | }
51 | }
52 | } else {
53 | field.Field.Set(reflect.Zero(field.Field.Type()))
54 | }
55 |
56 | field.IsBlank = isBlank(field.Field)
57 | return err
58 | }
59 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/wercker.yml:
--------------------------------------------------------------------------------
1 | # use the default golang container from Docker Hub
2 | box: golang
3 |
4 | services:
5 | - id: mariadb:10.0
6 | env:
7 | MYSQL_DATABASE: gorm
8 | MYSQL_USER: gorm
9 | MYSQL_PASSWORD: gorm
10 | MYSQL_RANDOM_ROOT_PASSWORD: "yes"
11 | - id: postgres
12 | env:
13 | POSTGRES_USER: gorm
14 | POSTGRES_PASSWORD: gorm
15 | POSTGRES_DB: gorm
16 |
17 | # The steps that will be executed in the build pipeline
18 | build:
19 | # The steps that will be executed on build
20 | steps:
21 | # Sets the go workspace and places you package
22 | # at the right place in the workspace tree
23 | - setup-go-workspace
24 |
25 | # Gets the dependencies
26 | - script:
27 | name: go get
28 | code: |
29 | cd $WERCKER_SOURCE_DIR
30 | go version
31 | go get -t ./...
32 |
33 | # Build the project
34 | - script:
35 | name: go build
36 | code: |
37 | go build ./...
38 |
39 | # Test the project
40 | - script:
41 | name: test sqlite
42 | code: |
43 | go test ./...
44 |
45 | - script:
46 | name: test mysql
47 | code: |
48 | GORM_DIALECT=mysql GORM_DBADDRESS=mariadb:3306 go test ./...
49 |
50 | - script:
51 | name: test postgres
52 | code: |
53 | GORM_DIALECT=postgres GORM_DBHOST=postgres go test ./...
54 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/errors.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "strings"
6 | )
7 |
8 | var (
9 | // ErrRecordNotFound record not found error, happens when haven't find any matched data when looking up with a struct
10 | ErrRecordNotFound = errors.New("record not found")
11 | // ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL
12 | ErrInvalidSQL = errors.New("invalid SQL")
13 | // ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback`
14 | ErrInvalidTransaction = errors.New("no valid transaction")
15 | // ErrCantStartTransaction can't start transaction when you are trying to start one with `Begin`
16 | ErrCantStartTransaction = errors.New("can't start transaction")
17 | // ErrUnaddressable unaddressable value
18 | ErrUnaddressable = errors.New("using unaddressable value")
19 | )
20 |
21 | // Errors contains all happened errors
22 | type Errors []error
23 |
24 | // GetErrors gets all happened errors
25 | func (errs Errors) GetErrors() []error {
26 | return errs
27 | }
28 |
29 | // Add adds an error
30 | func (errs Errors) Add(newErrors ...error) Errors {
31 | for _, err := range newErrors {
32 | if errors, ok := err.(Errors); ok {
33 | errs = errs.Add(errors...)
34 | } else {
35 | ok = true
36 | for _, e := range errs {
37 | if err == e {
38 | ok = false
39 | }
40 | }
41 | if ok {
42 | errs = append(errs, err)
43 | }
44 | }
45 | }
46 | return errs
47 | }
48 |
49 | // Error format happened errors
50 | func (errs Errors) Error() string {
51 | var errors = []string{}
52 | for _, e := range errs {
53 | errors = append(errors, e.Error())
54 | }
55 | return strings.Join(errors, "; ")
56 | }
57 |
--------------------------------------------------------------------------------
/lib/ini/ini.go:
--------------------------------------------------------------------------------
1 | package ini
2 |
3 | import (
4 | "bufio"
5 | "io"
6 | "log"
7 | "os"
8 | "strconv"
9 | "strings"
10 | )
11 |
12 | var config map[string]string
13 |
14 | //Load 加载.
15 | func Load(name string) error {
16 | conf := map[string]string{}
17 | f, err := os.Open(name)
18 | if err != nil {
19 | return err
20 | }
21 | defer f.Close()
22 | br := bufio.NewReader(f)
23 | for {
24 | b, _, err := br.ReadLine()
25 | if err != nil && err == io.EOF {
26 | break
27 | }
28 | line := string(b)
29 |
30 | //替换掉当前行里面所有的空格.
31 | line = strings.Replace(line, " ", "", -1)
32 |
33 | //判断是否为注释.
34 | if strings.HasPrefix(line, "#") {
35 | continue
36 | }
37 |
38 | //去掉换行符.
39 | if len(line) < 1 {
40 | continue
41 | }
42 |
43 | key, value := parseLine(line)
44 | if err != nil {
45 | return err
46 | }
47 | conf[key] = value
48 | }
49 | config = conf
50 | return nil
51 | }
52 |
53 | //解析每一行的配置.
54 | func parseLine(line string) (key string, value string) {
55 | key = line
56 | index := strings.Index(line, "=")
57 | if index > 0 {
58 | key = line[0:index]
59 | value = line[index+1 : len(line)]
60 | }
61 | return key, value
62 | }
63 |
64 | func GetString(key string) string {
65 | return config[key]
66 | }
67 |
68 | func GetInt(key string) int {
69 | if len(config[key]) < 1 {
70 | return 0
71 | }
72 | num, err := strconv.Atoi(config[key])
73 | if err != nil {
74 | log.Printf("获取int错误:%+v\n", err)
75 | }
76 | return num
77 | }
78 |
79 | func GetBool(key string) bool {
80 | if len(config[key]) < 1 {
81 | return false
82 | }
83 | b, err := strconv.ParseBool(config[key])
84 | if err != nil {
85 | log.Printf("获取bool错误%+v\n", err)
86 | }
87 | return b
88 | }
89 |
--------------------------------------------------------------------------------
/lib/packet/packet.go:
--------------------------------------------------------------------------------
1 | package packet
2 |
3 | import (
4 | "encoding/binary"
5 | "io"
6 | "net"
7 |
8 | "github.com/Goss-io/goss/lib/protocol"
9 | )
10 |
11 | const PROROCOL_LEN = 4
12 | const HEADER_LEN = 4
13 | const HASH_LEN = 32
14 |
15 | type Packet struct {
16 | Protocol protocol.GossProtocol
17 | Size int64
18 | Hash string
19 | Body []byte
20 | }
21 |
22 | func New(content, fileHash []byte, num protocol.GossProtocol) []byte {
23 | buffer := make([]byte, HEADER_LEN+len(content)+len(fileHash)+PROROCOL_LEN)
24 | //0-4 为协议号.
25 | //4-8 为文件大小.
26 | //8-40 为文件hash.
27 | //>40 为文件内容.
28 | binary.BigEndian.PutUint32(buffer[0:4], uint32(num))
29 | binary.BigEndian.PutUint32(buffer[4:8], uint32(len(content)))
30 | copy(buffer[8:40], fileHash)
31 | copy(buffer[40:], content)
32 | return buffer
33 | }
34 |
35 | //Parse 解析网络数据包.
36 | func Parse(conn net.Conn) (pkt Packet, err error) {
37 | //获取协议号.
38 | var num = make([]byte, PROROCOL_LEN)
39 | _, err = io.ReadFull(conn, num)
40 | if err != nil {
41 | return pkt, err
42 | }
43 | pkt.Protocol = protocol.GossProtocol(int(binary.BigEndian.Uint32(num)))
44 |
45 | //获取文件长度.
46 | var header = make([]byte, HEADER_LEN)
47 | _, err = io.ReadFull(conn, header)
48 | if err != nil {
49 | return pkt, err
50 | }
51 | pkt.Size = int64(binary.BigEndian.Uint32(header))
52 |
53 | //获取hash.
54 | var fhash = make([]byte, HASH_LEN)
55 | _, err = io.ReadFull(conn, fhash)
56 | if err != nil {
57 | return pkt, err
58 | }
59 | pkt.Hash = string(fhash)
60 |
61 | //获取文件
62 | var buf = make([]byte, pkt.Size)
63 | _, err = io.ReadFull(conn, buf)
64 | if err != nil {
65 | return pkt, err
66 | }
67 | pkt.Body = buf
68 |
69 | return pkt, nil
70 | }
71 |
--------------------------------------------------------------------------------
/lib/utils.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "encoding/json"
7 | "log"
8 | "net/http"
9 | "os"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | func FileHash(body []byte) string {
15 | h := md5.New()
16 | h.Write(body)
17 | b := h.Sum(nil)
18 | return hex.EncodeToString(b)
19 | }
20 |
21 | //Hash .
22 | func Hash(body string) []byte {
23 | h := md5.New()
24 | h.Write([]byte(body))
25 | b := h.Sum(nil)
26 | return []byte(hex.EncodeToString(b))
27 | }
28 |
29 | //IsExists 判断ini是否存在.
30 | func IsExists(ini string) bool {
31 | _, err := os.Stat(ini)
32 | if err != nil {
33 | return false
34 | }
35 | return true
36 | }
37 |
38 | //Time 获取当前时间.
39 | func Time() string {
40 | return time.Now().Format("2006-01-02 15:04:05")
41 | }
42 |
43 | //ParseInt .
44 | func ParseInt(str string) int {
45 | num, err := strconv.Atoi(str)
46 | if err != nil {
47 | return 0
48 | }
49 | return num
50 | }
51 |
52 | func InArray(item string, list []string) bool {
53 | for _, v := range list {
54 | if v == item {
55 | return true
56 | }
57 | }
58 | return false
59 | }
60 |
61 | type Resp struct {
62 | Status bool
63 | Msg interface{}
64 | Body []byte
65 | }
66 |
67 | //Response .
68 | func Response(w http.ResponseWriter, status bool, msg interface{}, body ...[]byte) {
69 | w.Header().Set("Content-Type", "application/json")
70 | dist := map[string]interface{}{
71 | "status": status,
72 | "msg": msg,
73 | }
74 | if len(body) > 0 {
75 | dist["body"] = body[0]
76 | }
77 | b, err := json.Marshal(dist)
78 | if err != nil {
79 | log.Printf("err:%+v\n", err)
80 | return
81 | }
82 |
83 | w.Write(b)
84 | }
85 |
86 | //ParseMsg .
87 | func ParseMsg(b []byte) Resp {
88 | r := Resp{}
89 | json.Unmarshal(b, &r)
90 | return r
91 | }
92 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/README.md:
--------------------------------------------------------------------------------
1 | # GORM
2 |
3 | The fantastic ORM library for Golang, aims to be developer friendly.
4 |
5 | [](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6 | [](https://app.wercker.com/project/bykey/0cb7bb1039e21b74f8274941428e0921)
7 | [](https://godoc.org/github.com/jinzhu/gorm)
8 |
9 | ## Overview
10 |
11 | * Full-Featured ORM (almost)
12 | * Associations (Has One, Has Many, Belongs To, Many To Many, Polymorphism)
13 | * Callbacks (Before/After Create/Save/Update/Delete/Find)
14 | * Preloading (eager loading)
15 | * Transactions
16 | * Composite Primary Key
17 | * SQL Builder
18 | * Auto Migrations
19 | * Logger
20 | * Extendable, write Plugins based on GORM callbacks
21 | * Every feature comes with tests
22 | * Developer Friendly
23 |
24 | ## Getting Started
25 |
26 | * GORM Guides [jinzhu.github.com/gorm](http://jinzhu.github.io/gorm)
27 |
28 | ## Upgrading To V1.0
29 |
30 | * [CHANGELOG](http://jinzhu.github.io/gorm/changelog.html)
31 |
32 | ## Supporting the project
33 |
34 | [](http://patreon.com/jinzhu)
35 |
36 | ## Author
37 |
38 | **jinzhu**
39 |
40 | *
41 | *
42 | *
43 |
44 | ## Contributors
45 |
46 | https://github.com/jinzhu/gorm/graphs/contributors
47 |
48 | ## License
49 |
50 | Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License).
51 |
--------------------------------------------------------------------------------
/lib/logd/log.go:
--------------------------------------------------------------------------------
1 | package logd
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "runtime"
8 | "time"
9 |
10 | "github.com/Goss-io/goss/lib"
11 | "github.com/Goss-io/goss/lib/ini"
12 | )
13 |
14 | //Level .
15 | type LogLevel string
16 |
17 | const (
18 | Level_INFO LogLevel = "INFO"
19 | Level_DEBUG = "DEBUG"
20 | Level_WARNING = "WARNING"
21 | Level_ERROR = "ERROR"
22 | )
23 |
24 | var logList = make(chan string, 4096)
25 |
26 | func init() {
27 | go func() {
28 | for {
29 | select {
30 | case logmsg := <-logList:
31 | logFile := logFile()
32 | f, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_RDONLY|os.O_WRONLY, 0777)
33 | if err != nil {
34 | log.Printf("%+v\n", err)
35 | f.Close()
36 | return
37 | }
38 |
39 | _, err = f.WriteString(logmsg)
40 | if err != nil {
41 | log.Printf("%+v\n", err)
42 | }
43 | f.Close()
44 | }
45 | }
46 | }()
47 | }
48 |
49 | func Make(level LogLevel, logpath string, msg interface{}) {
50 | makelog(level, logpath, msg)
51 | }
52 |
53 | func makelog(level LogLevel, logpath string, msg interface{}) {
54 | nodename := ini.GetString("node_name")
55 | content := fmt.Sprintf("%s %s:[%s] %s [%v]\n", lib.Time(), nodename, level, logpath, msg)
56 |
57 | println(content)
58 | logList <- content
59 | }
60 |
61 | //getLogpath 获取产生日志的路径.
62 | func GetLogpath() string {
63 | _, file, line, _ := runtime.Caller(1)
64 | return fmt.Sprintf("%s:%d", file, line)
65 | }
66 |
67 | func logFile() string {
68 | now := time.Now()
69 | year := now.Year()
70 | month := int(now.Month())
71 | day := now.Day()
72 | hour := now.Hour()
73 |
74 | path := fmt.Sprintf("%s%d/%d/%d/", ini.GetString("log_path"), year, month, day)
75 |
76 | //判断存储路径是否存在.
77 | if !lib.IsExists(path) {
78 | os.MkdirAll(path, 0777)
79 | }
80 |
81 | return fmt.Sprintf("%s%d.log", path, hour)
82 | }
83 |
--------------------------------------------------------------------------------
/lib/packet/node.go:
--------------------------------------------------------------------------------
1 | package packet
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "io"
7 | "log"
8 | "net"
9 | "strings"
10 |
11 | "github.com/Goss-io/goss/lib/protocol"
12 | )
13 |
14 | type NodeTypes string
15 |
16 | const (
17 | NodeTypes_Api NodeTypes = "api"
18 | NodeTypes_Storage = "stprage"
19 | NodeTypes_Master = "master"
20 | )
21 |
22 | //NodePacket 节点管理数据.
23 | type NodePacket struct {
24 | Protocol protocol.GossProtocol //协议号.
25 | Types NodeTypes //节点类型.
26 | IP string //节点ip.
27 | Size int64
28 | }
29 |
30 | //NewNode.
31 | func NewNode(types NodeTypes, ip string, proto protocol.GossProtocol) []byte {
32 | body := fmt.Sprintf("%s,%s", types, ip)
33 | buffer := make([]byte, HEADER_LEN+len(body)+PROROCOL_LEN)
34 | //0-4 为协议号.
35 | //4-8 为数据大小.
36 | //>8 为节点数据.
37 | binary.BigEndian.PutUint32(buffer[0:4], uint32(proto))
38 | binary.BigEndian.PutUint32(buffer[4:8], uint32(len(body)))
39 | copy(buffer[8:], body)
40 | return buffer
41 | }
42 |
43 | func ParseNode(conn net.Conn) (pkt NodePacket, err error) {
44 | pkt = NodePacket{}
45 | //获取协议号.
46 | var num = make([]byte, PROROCOL_LEN)
47 | _, err = io.ReadFull(conn, num)
48 | if err != nil {
49 | log.Printf("%+v\n", err)
50 | return pkt, err
51 | }
52 | pkt.Protocol = protocol.GossProtocol(int(binary.BigEndian.Uint32(num)))
53 |
54 | //获取数据长度.
55 | var bodyBuf = make([]byte, 4)
56 | _, err = io.ReadFull(conn, bodyBuf)
57 | if err != nil {
58 | log.Printf("%+v\n", err)
59 | return pkt, err
60 | }
61 | pkt.Size = int64(binary.BigEndian.Uint32(bodyBuf))
62 |
63 | //获取数据内容.
64 | var body = make([]byte, pkt.Size)
65 | _, err = io.ReadFull(conn, body)
66 | if err != nil {
67 | log.Printf("%+v\n", err)
68 | return pkt, err
69 | }
70 |
71 | //拆分字符串.
72 | sArr := strings.Split(string(body), ",")
73 | pkt.Types = NodeTypes(sArr[0])
74 | pkt.IP = sArr[1]
75 |
76 | return pkt, nil
77 | }
78 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_delete.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | // Define callbacks for deleting
9 | func init() {
10 | DefaultCallback.Delete().Register("gorm:begin_transaction", beginTransactionCallback)
11 | DefaultCallback.Delete().Register("gorm:before_delete", beforeDeleteCallback)
12 | DefaultCallback.Delete().Register("gorm:delete", deleteCallback)
13 | DefaultCallback.Delete().Register("gorm:after_delete", afterDeleteCallback)
14 | DefaultCallback.Delete().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback)
15 | }
16 |
17 | // beforeDeleteCallback will invoke `BeforeDelete` method before deleting
18 | func beforeDeleteCallback(scope *Scope) {
19 | if scope.DB().HasBlockGlobalUpdate() && !scope.hasConditions() {
20 | scope.Err(errors.New("Missing WHERE clause while deleting"))
21 | return
22 | }
23 | if !scope.HasError() {
24 | scope.CallMethod("BeforeDelete")
25 | }
26 | }
27 |
28 | // deleteCallback used to delete data from database or set deleted_at to current time (when using with soft delete)
29 | func deleteCallback(scope *Scope) {
30 | if !scope.HasError() {
31 | var extraOption string
32 | if str, ok := scope.Get("gorm:delete_option"); ok {
33 | extraOption = fmt.Sprint(str)
34 | }
35 |
36 | deletedAtField, hasDeletedAtField := scope.FieldByName("DeletedAt")
37 |
38 | if !scope.Search.Unscoped && hasDeletedAtField {
39 | scope.Raw(fmt.Sprintf(
40 | "UPDATE %v SET %v=%v%v%v",
41 | scope.QuotedTableName(),
42 | scope.Quote(deletedAtField.DBName),
43 | scope.AddToVars(NowFunc()),
44 | addExtraSpaceIfExist(scope.CombinedConditionSql()),
45 | addExtraSpaceIfExist(extraOption),
46 | )).Exec()
47 | } else {
48 | scope.Raw(fmt.Sprintf(
49 | "DELETE FROM %v%v%v",
50 | scope.QuotedTableName(),
51 | addExtraSpaceIfExist(scope.CombinedConditionSql()),
52 | addExtraSpaceIfExist(extraOption),
53 | )).Exec()
54 | }
55 | }
56 | }
57 |
58 | // afterDeleteCallback will invoke `AfterDelete` method after deleting
59 | func afterDeleteCallback(scope *Scope) {
60 | if !scope.HasError() {
61 | scope.CallMethod("AfterDelete")
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/storage/handler/storage.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "hash/crc32"
6 | "io/ioutil"
7 | "log"
8 | "os"
9 | "strings"
10 |
11 | "github.com/Goss-io/goss/app/storage/conf"
12 | "github.com/Goss-io/goss/lib"
13 | )
14 |
15 | var storageList []string
16 |
17 | //SelectPath 选择存储路径.
18 | func (s *StorageService) SelectPath(hash string) string {
19 | n := crc32.ChecksumIEEE([]byte(hash))
20 | num := int64(n) % int64(len(storageList))
21 | return storageList[num]
22 | }
23 |
24 | //InitStoragePath 初始化存储目录.
25 | func (s *StorageService) InitStoragePath(path string) error {
26 | //判断路径是否存在.
27 | if !lib.IsExists(path) {
28 | if err := os.MkdirAll(path, 0777); err != nil {
29 | return err
30 | }
31 | }
32 | if _, err := s.makeDir(path); err != nil {
33 | log.Printf("%+v\n", err)
34 | return err
35 | }
36 | files, err := ioutil.ReadDir(path)
37 | if err != nil {
38 | log.Printf("%+v\n", err)
39 | return err
40 | }
41 |
42 | list := []string{}
43 | for _, f := range files {
44 | if f.IsDir() {
45 | //创建文件夹.
46 | path := fmt.Sprintf("%s%s/", path, f.Name())
47 | dirList, err := s.makeDir(path)
48 | if err != nil {
49 | log.Printf("%+v\n", err)
50 | return err
51 | }
52 | list = append(list, dirList...)
53 | }
54 | }
55 |
56 | storageList = list
57 | return nil
58 | }
59 |
60 | //makeDir 创建存储目录.
61 | func (s *StorageService) makeDir(path string) (dirList []string, err error) {
62 | str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
63 | sarr := strings.Split(str, "")
64 | var num = 0
65 | for _, v := range sarr {
66 | for _, val := range sarr {
67 | if num > 255 {
68 | break
69 | }
70 | num++
71 | dirname := fmt.Sprintf("%s%s%s/", path, v, val)
72 | if lib.IsExists(dirname) {
73 | dirname = strings.Replace(dirname, conf.Conf.Node.StorageRoot, "", -1)
74 | dirList = append(dirList, dirname)
75 | continue
76 | }
77 | if err := os.Mkdir(dirname, 0777); err != nil {
78 | log.Printf("%+v\n", err)
79 | return dirList, err
80 | }
81 |
82 | dirname = strings.Replace(dirname, conf.Conf.Node.StorageRoot, "", -1)
83 | dirList = append(dirList, dirname)
84 | }
85 | }
86 | return dirList, nil
87 | }
88 |
--------------------------------------------------------------------------------
/app/master/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/Goss-io/goss/lib"
8 |
9 | "github.com/Goss-io/goss/lib/cmd"
10 | "github.com/Goss-io/goss/lib/ini"
11 | )
12 |
13 | type Config struct {
14 | Node *nodeConfig
15 | Base *baseConfig
16 | }
17 |
18 | type nodeConfig struct {
19 | IP string
20 | Port int
21 | WebPort int
22 | Name string
23 | Token string
24 | }
25 |
26 | type baseConfig struct {
27 | LogPath string
28 | }
29 |
30 | var Conf *Config
31 |
32 | //Load .
33 | func Load(cmd *cmd.Command) {
34 | iniPath := cmd.Conf
35 | if !lib.IsExists(cmd.Conf) {
36 | log.Println("配置文件不存在=>", iniPath)
37 | os.Exit(0)
38 | return
39 | }
40 |
41 | if err := ini.Load(iniPath); err != nil {
42 | log.Printf("%+v\n", err)
43 | return
44 | }
45 |
46 | cf := &Config{
47 | Node: parseNodeConfig(cmd),
48 | Base: parseBaseConfig(),
49 | }
50 |
51 | Conf = cf
52 | }
53 |
54 | //node.
55 | func parseNodeConfig(cmd *cmd.Command) *nodeConfig {
56 | name := ini.GetString("node_name")
57 | if len(name) < 1 {
58 | log.Println("node_name 不能为空")
59 | os.Exit(0)
60 | }
61 |
62 | storeip := ini.GetString("node_ip")
63 | if len(storeip) < 1 {
64 | log.Println("node_ip 不能为空")
65 | os.Exit(0)
66 | }
67 | storeport := ini.GetInt("node_port")
68 | if storeport < 1 {
69 | log.Println("node_port 不能为空")
70 | os.Exit(0)
71 | }
72 | webport := ini.GetInt("node_web_port")
73 | if webport < 1 {
74 | log.Println("node_web_port 不能为空")
75 | os.Exit(0)
76 | }
77 | token := ini.GetString("token")
78 | if len(token) < 1 {
79 | log.Println("token 不能为空")
80 | os.Exit(0)
81 | }
82 |
83 | nodeconf := &nodeConfig{
84 | IP: storeip,
85 | Port: storeport,
86 | WebPort: webport,
87 | Name: name,
88 | Token: token,
89 | }
90 |
91 | return nodeconf
92 | }
93 |
94 | //parseBaseConfig 解析基础配置.
95 | func parseBaseConfig() *baseConfig {
96 | logpath := ini.GetString("log_path")
97 | if len(logpath) < 1 {
98 | log.Println("log_path 不能为空")
99 | os.Exit(0)
100 | }
101 | base := baseConfig{
102 | LogPath: logpath,
103 | }
104 | return &base
105 | }
106 |
--------------------------------------------------------------------------------
/app/storage/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/Goss-io/goss/lib"
8 | "github.com/Goss-io/goss/lib/cmd"
9 | "github.com/Goss-io/goss/lib/ini"
10 | )
11 |
12 | type Config struct {
13 | Node *nodeConfig
14 | Base *baseConfig
15 | }
16 |
17 | type nodeConfig struct {
18 | IP string
19 | Port int
20 | Name string
21 | StorageRoot string
22 | MasterNode string
23 | Token string
24 | }
25 |
26 | type baseConfig struct {
27 | LogPath string
28 | }
29 |
30 | var Conf *Config
31 |
32 | //Load .
33 | func Load(cmd *cmd.Command) {
34 | iniPath := cmd.Conf
35 | if !lib.IsExists(cmd.Conf) {
36 | log.Println("配置文件不存在=>", iniPath)
37 | os.Exit(0)
38 | return
39 | }
40 |
41 | if err := ini.Load(iniPath); err != nil {
42 | log.Printf("%+v\n", err)
43 | return
44 | }
45 |
46 | cf := &Config{
47 | Node: parseNodeConfig(cmd),
48 | Base: parseBaseConfig(),
49 | }
50 |
51 | Conf = cf
52 | }
53 |
54 | //node.
55 | func parseNodeConfig(cmd *cmd.Command) *nodeConfig {
56 | name := ini.GetString("node_name")
57 | if len(name) < 1 {
58 | log.Println("node_name 不能为空")
59 | os.Exit(0)
60 | }
61 |
62 | storageip := ini.GetString("node_ip")
63 | if len(storageip) < 1 {
64 | log.Println("node_ip 不能为空")
65 | os.Exit(0)
66 | }
67 | storageport := ini.GetInt("node_port")
68 | if storageport < 1 {
69 | log.Println("node_port 不能为空")
70 | os.Exit(0)
71 | }
72 |
73 | storageRoot := ini.GetString("storage_root")
74 | if len(storageRoot) < 1 {
75 | log.Println("storage_root 不能为空")
76 | os.Exit(0)
77 | }
78 | masterNode := ini.GetString("master_node")
79 | if len(masterNode) < 1 {
80 | log.Println("master_node 不能为空")
81 | os.Exit(0)
82 | }
83 | token := ini.GetString("token")
84 | if len(token) < 1 {
85 | log.Println("token 不能为空")
86 | os.Exit(0)
87 | }
88 |
89 | nodeconf := &nodeConfig{
90 | IP: storageip,
91 | Port: storageport,
92 | Name: name,
93 | StorageRoot: storageRoot,
94 | MasterNode: masterNode,
95 | Token: token,
96 | }
97 |
98 | return nodeconf
99 | }
100 |
101 | //parseBaseConfig 解析基础配置.
102 | func parseBaseConfig() *baseConfig {
103 | logpath := ini.GetString("log_path")
104 | if len(logpath) < 1 {
105 | log.Println("log_path 不能为空")
106 | os.Exit(0)
107 | }
108 | base := &baseConfig{
109 | LogPath: logpath,
110 | }
111 | return base
112 | }
113 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_query.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "reflect"
7 | )
8 |
9 | // Define callbacks for querying
10 | func init() {
11 | DefaultCallback.Query().Register("gorm:query", queryCallback)
12 | DefaultCallback.Query().Register("gorm:preload", preloadCallback)
13 | DefaultCallback.Query().Register("gorm:after_query", afterQueryCallback)
14 | }
15 |
16 | // queryCallback used to query data from database
17 | func queryCallback(scope *Scope) {
18 | defer scope.trace(NowFunc())
19 |
20 | var (
21 | isSlice, isPtr bool
22 | resultType reflect.Type
23 | results = scope.IndirectValue()
24 | )
25 |
26 | if orderBy, ok := scope.Get("gorm:order_by_primary_key"); ok {
27 | if primaryField := scope.PrimaryField(); primaryField != nil {
28 | scope.Search.Order(fmt.Sprintf("%v.%v %v", scope.QuotedTableName(), scope.Quote(primaryField.DBName), orderBy))
29 | }
30 | }
31 |
32 | if value, ok := scope.Get("gorm:query_destination"); ok {
33 | results = indirect(reflect.ValueOf(value))
34 | }
35 |
36 | if kind := results.Kind(); kind == reflect.Slice {
37 | isSlice = true
38 | resultType = results.Type().Elem()
39 | results.Set(reflect.MakeSlice(results.Type(), 0, 0))
40 |
41 | if resultType.Kind() == reflect.Ptr {
42 | isPtr = true
43 | resultType = resultType.Elem()
44 | }
45 | } else if kind != reflect.Struct {
46 | scope.Err(errors.New("unsupported destination, should be slice or struct"))
47 | return
48 | }
49 |
50 | scope.prepareQuerySQL()
51 |
52 | if !scope.HasError() {
53 | scope.db.RowsAffected = 0
54 | if str, ok := scope.Get("gorm:query_option"); ok {
55 | scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str))
56 | }
57 |
58 | if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {
59 | defer rows.Close()
60 |
61 | columns, _ := rows.Columns()
62 | for rows.Next() {
63 | scope.db.RowsAffected++
64 |
65 | elem := results
66 | if isSlice {
67 | elem = reflect.New(resultType).Elem()
68 | }
69 |
70 | scope.scan(rows, columns, scope.New(elem.Addr().Interface()).Fields())
71 |
72 | if isSlice {
73 | if isPtr {
74 | results.Set(reflect.Append(results, elem.Addr()))
75 | } else {
76 | results.Set(reflect.Append(results, elem))
77 | }
78 | }
79 | }
80 |
81 | if err := rows.Err(); err != nil {
82 | scope.Err(err)
83 | } else if scope.db.RowsAffected == 0 && !isSlice {
84 | scope.Err(ErrRecordNotFound)
85 | }
86 | }
87 | }
88 | }
89 |
90 | // afterQueryCallback will invoke `AfterFind` method after querying
91 | func afterQueryCallback(scope *Scope) {
92 | if !scope.HasError() {
93 | scope.CallMethod("AfterFind")
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/storage/handler/http.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "errors"
5 | "io/ioutil"
6 | "log"
7 | "net/http"
8 |
9 | "github.com/Goss-io/goss/app/storage/conf"
10 | "github.com/Goss-io/goss/lib"
11 | "github.com/Goss-io/goss/lib/logd"
12 | )
13 |
14 | //httpSrv .
15 | func (s *StorageService) httpSrv() {
16 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
17 | //检查token.
18 | if !s.checkAuth(r.Header.Get("token")) {
19 | lib.Response(w, false, "未授权的访问")
20 | return
21 | }
22 | if r.Method == "PUT" {
23 | //文件内容.
24 | fbody, err := ioutil.ReadAll(r.Body)
25 | if err != nil {
26 | log.Printf("%+v\n", err)
27 | return
28 | }
29 | log.Printf("r.Body:%+v\n", string(fbody))
30 |
31 | fhash := r.Header.Get("fhash")
32 | fPath, err := s.put(fhash, fbody)
33 | if err != nil {
34 | lib.Response(w, false, err.Error())
35 | return
36 | }
37 |
38 | lib.Response(w, true, fPath)
39 | return
40 | }
41 |
42 | if r.Method == "GET" {
43 | fpath := r.Header.Get("fpath")
44 | b, err := s.get(fpath)
45 | if err != nil {
46 | log.Printf("err:%+v\n", err)
47 | lib.Response(w, false, err.Error())
48 | return
49 | }
50 | lib.Response(w, true, "获取成功", b)
51 | return
52 | }
53 |
54 | if r.Method == "DELETE" {
55 | // s.delete(fname)
56 | return
57 | }
58 |
59 | lib.Response(w, false, "禁止访问")
60 | })
61 | http.ListenAndServe(s.Port, nil)
62 | }
63 |
64 | //checkAuth .
65 | func (s *StorageService) checkAuth(token string) bool {
66 | if token != conf.Conf.Node.Token {
67 | return false
68 | }
69 | return true
70 | }
71 |
72 | //put 记录文件.
73 | func (s *StorageService) put(hash string, fbody []byte) (fPath string, err error) {
74 | //计算文件hash.
75 | fHash := lib.FileHash(fbody)
76 | //验证文件是否损坏.
77 | if fHash != hash {
78 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "文件hash不一致")
79 | return fPath, errors.New("文件hash不一致")
80 | }
81 |
82 | fPath = s.SelectPath(fHash) + fHash
83 | storePath := conf.Conf.Node.StorageRoot + fPath
84 | err = ioutil.WriteFile(storePath, fbody, 0777)
85 | if err != nil {
86 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "创建文件失败"+err.Error())
87 | return fPath, err
88 | }
89 |
90 | return fPath, nil
91 | }
92 |
93 | //获取文件.
94 | func (s *StorageService) get(fpath string) (fbody []byte, err error) {
95 | fpath = conf.Conf.Node.StorageRoot + fpath
96 | fbody, err = ioutil.ReadFile(fpath)
97 | if err != nil {
98 | log.Printf("err:%+v\n", err)
99 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "读取文件失败:"+err.Error())
100 | return fbody, err
101 | }
102 |
103 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "文件发送成功")
104 | return fbody, nil
105 | }
106 |
--------------------------------------------------------------------------------
/app/storage/handler/node.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "net"
7 | "time"
8 |
9 | "github.com/Goss-io/goss/lib"
10 | "github.com/Goss-io/goss/lib/hardware"
11 | "github.com/Goss-io/goss/lib/ini"
12 | "github.com/Goss-io/goss/lib/logd"
13 | "github.com/Goss-io/goss/lib/packet"
14 | "github.com/Goss-io/goss/lib/protocol"
15 | )
16 |
17 | //connMaster 连接管理节点.
18 | func (s *StorageService) connMaster() {
19 | //上报节点信息
20 | conn := s.conn(s.MasterNode)
21 |
22 | //连接初始化.
23 | if err := s.connInit(conn); err != nil {
24 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), err.Error())
25 | s.connMaster()
26 | return
27 | }
28 |
29 | for {
30 | var buf = make([]byte, 1024)
31 | _, err := conn.Read(buf)
32 | if err != nil {
33 | s.connMaster()
34 | return
35 | }
36 | }
37 | }
38 |
39 | func (s *StorageService) conn(node string) net.Conn {
40 | conn, err := net.Dial("tcp4", node)
41 | if err != nil {
42 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "master节点连接失败,稍后重新连接")
43 | time.Sleep(time.Second * 1)
44 | return s.conn(node)
45 | }
46 |
47 | return conn
48 | }
49 |
50 | //connInit 连接初始化.
51 | func (s *StorageService) connInit(conn net.Conn) error {
52 | if err := s.sendAuth(conn); err != nil {
53 | return err
54 | }
55 |
56 | if err := s.sendNodeInfo(conn); err != nil {
57 | return err
58 | }
59 | return nil
60 | }
61 |
62 | //auth 发送授权信息.
63 | func (s *StorageService) sendAuth(conn net.Conn) error {
64 | tokenBuf := []byte(ini.GetString("token"))
65 | buf := packet.New(tokenBuf, tokenBuf, protocol.CONN_AUTH)
66 | _, err := conn.Write(buf)
67 | if err != nil {
68 | return errors.New("授权信息发送失败")
69 | }
70 |
71 | pkt, err := packet.Parse(conn)
72 | if err != nil {
73 | return errors.New("授权失败")
74 | }
75 |
76 | if string(pkt.Body) == "fail" {
77 | return errors.New("授权信息验证失败")
78 | }
79 |
80 | return nil
81 | }
82 |
83 | //sendNodeInfo 上报节点信息.
84 | func (s *StorageService) sendNodeInfo(conn net.Conn) error {
85 | h := hardware.New()
86 | nodeInfo := protocol.NodeInfo{
87 | Types: string(packet.NodeTypes_Storage),
88 | CpuNum: h.Cpu.Num,
89 | MemSize: h.Mem.Total,
90 | SourceIP: s.Addr,
91 | Name: ini.GetString("node_name"),
92 | }
93 |
94 | content, err := json.Marshal(nodeInfo)
95 | if err != nil {
96 | return err
97 | }
98 |
99 | hashBuf := lib.Hash(string(content))
100 | buf := packet.New(content, hashBuf, protocol.REPORT_NODE_INFO)
101 | _, err = conn.Write(buf)
102 | if err != nil {
103 | return err
104 | }
105 |
106 | pkt, err := packet.Parse(conn)
107 | if err != nil {
108 | return err
109 | }
110 |
111 | if string(pkt.Body) == "fail" {
112 | return errors.New("发送节点信息失败")
113 | }
114 |
115 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "上报节点信息成功")
116 | return nil
117 | }
118 |
--------------------------------------------------------------------------------
/app/api/conf/conf.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/Goss-io/goss/lib"
8 | "github.com/Goss-io/goss/lib/cmd"
9 | "github.com/Goss-io/goss/lib/ini"
10 | )
11 |
12 | type Config struct {
13 | Node *nodeConfig
14 | Db *dbConfig
15 | Base *baseConfig
16 | }
17 |
18 | type dbConfig struct {
19 | Host string
20 | User string
21 | Port int
22 | Name string
23 | Password string
24 | }
25 |
26 | type nodeConfig struct {
27 | IP string
28 | Port int
29 | Name string
30 | Token string
31 | // StoreAddrs []string
32 | }
33 |
34 | type baseConfig struct {
35 | LogPath string //日志存放路径.
36 | }
37 |
38 | var Conf *Config
39 |
40 | //Load .
41 | func Load(cmd *cmd.Command) {
42 | iniPath := cmd.Conf
43 | if !lib.IsExists(cmd.Conf) {
44 | log.Println("配置文件不存在=>", iniPath)
45 | os.Exit(0)
46 | return
47 | }
48 |
49 | if err := ini.Load(iniPath); err != nil {
50 | log.Printf("%+v\n", err)
51 | return
52 | }
53 |
54 | cf := &Config{
55 | Db: parseDbConfig(),
56 | Node: parseNodeConfig(cmd),
57 | Base: parseBaseConfig(),
58 | }
59 |
60 | Conf = cf
61 | }
62 |
63 | //node.
64 | func parseNodeConfig(cmd *cmd.Command) *nodeConfig {
65 | masterip := ini.GetString("node_ip")
66 | if len(masterip) < 1 {
67 | log.Println("node_ip 不能为空")
68 | os.Exit(0)
69 | }
70 |
71 | masterport := ini.GetInt("node_port")
72 | if masterport < 1 {
73 | log.Println("node_port 不能为空")
74 | os.Exit(0)
75 | }
76 |
77 | name := ini.GetString("node_name")
78 | if len(name) < 1 {
79 | log.Println("node_name 不能为空")
80 | os.Exit(0)
81 | }
82 |
83 | token := ini.GetString("token")
84 | if len(token) < 1 {
85 | log.Println("token 不能为空")
86 | os.Exit(0)
87 | }
88 | // storeAddrs := strings.Split(nodeStoreAddr, ",")
89 |
90 | nodeconf := &nodeConfig{
91 | IP: masterip,
92 | Port: masterport,
93 | Name: name,
94 | Token: token,
95 | // StoreAddrs: storeAddrs,
96 | }
97 |
98 | return nodeconf
99 | }
100 |
101 | //db.
102 | func parseDbConfig() *dbConfig {
103 | dbHost := ini.GetString("db_host")
104 | if len(dbHost) < 1 {
105 | log.Println("db_host 不能为空")
106 | os.Exit(0)
107 | }
108 |
109 | dbUser := ini.GetString("db_user")
110 | if len(dbUser) < 1 {
111 | log.Println("db_user 不能为空")
112 | os.Exit(0)
113 | }
114 |
115 | dbPort := ini.GetInt("db_port")
116 | if dbPort < 1 {
117 | log.Println("db_port 不能为空")
118 | os.Exit(0)
119 | }
120 |
121 | dbName := ini.GetString("db_name")
122 | if len(dbName) < 1 {
123 | log.Println("db_name 不能为空")
124 | os.Exit(0)
125 | }
126 |
127 | dbPwd := ini.GetString("db_pwd")
128 | if len(dbPwd) < 1 {
129 | log.Println("db_pwd 不能为空")
130 | os.Exit(0)
131 | }
132 |
133 | dbconf := &dbConfig{
134 | Host: dbHost,
135 | User: dbUser,
136 | Port: dbPort,
137 | Name: dbName,
138 | Password: dbPwd,
139 | }
140 | return dbconf
141 | }
142 |
143 | //parseBaseConfig 解析基础配置.
144 | func parseBaseConfig() *baseConfig {
145 | logpath := ini.GetString("log_path")
146 | if len(logpath) < 1 {
147 | log.Println("log_path 不能为空")
148 | os.Exit(0)
149 | }
150 | base := baseConfig{
151 | LogPath: logpath,
152 | }
153 | return &base
154 | }
155 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialect_sqlite3.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "strings"
7 | "time"
8 | )
9 |
10 | type sqlite3 struct {
11 | commonDialect
12 | }
13 |
14 | func init() {
15 | RegisterDialect("sqlite3", &sqlite3{})
16 | }
17 |
18 | func (sqlite3) GetName() string {
19 | return "sqlite3"
20 | }
21 |
22 | // Get Data Type for Sqlite Dialect
23 | func (s *sqlite3) DataTypeOf(field *StructField) string {
24 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s)
25 |
26 | if sqlType == "" {
27 | switch dataValue.Kind() {
28 | case reflect.Bool:
29 | sqlType = "bool"
30 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
31 | if field.IsPrimaryKey {
32 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
33 | sqlType = "integer primary key autoincrement"
34 | } else {
35 | sqlType = "integer"
36 | }
37 | case reflect.Int64, reflect.Uint64:
38 | if field.IsPrimaryKey {
39 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
40 | sqlType = "integer primary key autoincrement"
41 | } else {
42 | sqlType = "bigint"
43 | }
44 | case reflect.Float32, reflect.Float64:
45 | sqlType = "real"
46 | case reflect.String:
47 | if size > 0 && size < 65532 {
48 | sqlType = fmt.Sprintf("varchar(%d)", size)
49 | } else {
50 | sqlType = "text"
51 | }
52 | case reflect.Struct:
53 | if _, ok := dataValue.Interface().(time.Time); ok {
54 | sqlType = "datetime"
55 | }
56 | default:
57 | if IsByteArrayOrSlice(dataValue) {
58 | sqlType = "blob"
59 | }
60 | }
61 | }
62 |
63 | if sqlType == "" {
64 | panic(fmt.Sprintf("invalid sql type %s (%s) for sqlite3", dataValue.Type().Name(), dataValue.Kind().String()))
65 | }
66 |
67 | if strings.TrimSpace(additionalType) == "" {
68 | return sqlType
69 | }
70 | return fmt.Sprintf("%v %v", sqlType, additionalType)
71 | }
72 |
73 | func (s sqlite3) HasIndex(tableName string, indexName string) bool {
74 | var count int
75 | s.db.QueryRow(fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND sql LIKE '%%INDEX %v ON%%'", indexName), tableName).Scan(&count)
76 | return count > 0
77 | }
78 |
79 | func (s sqlite3) HasTable(tableName string) bool {
80 | var count int
81 | s.db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", tableName).Scan(&count)
82 | return count > 0
83 | }
84 |
85 | func (s sqlite3) HasColumn(tableName string, columnName string) bool {
86 | var count int
87 | s.db.QueryRow(fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND (sql LIKE '%%\"%v\" %%' OR sql LIKE '%%%v %%');\n", columnName, columnName), tableName).Scan(&count)
88 | return count > 0
89 | }
90 |
91 | func (s sqlite3) CurrentDatabase() (name string) {
92 | var (
93 | ifaces = make([]interface{}, 3)
94 | pointers = make([]*string, 3)
95 | i int
96 | )
97 | for i = 0; i < 3; i++ {
98 | ifaces[i] = &pointers[i]
99 | }
100 | if err := s.db.QueryRow("PRAGMA database_list").Scan(ifaces...); err != nil {
101 | return
102 | }
103 | if pointers[1] != nil {
104 | name = *pointers[1]
105 | }
106 | return
107 | }
108 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/logger.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "database/sql/driver"
5 | "fmt"
6 | "log"
7 | "os"
8 | "reflect"
9 | "regexp"
10 | "time"
11 | "unicode"
12 | )
13 |
14 | var (
15 | defaultLogger = Logger{log.New(os.Stdout, "\r\n", 0)}
16 | sqlRegexp = regexp.MustCompile(`\?`)
17 | numericPlaceHolderRegexp = regexp.MustCompile(`\$\d+`)
18 | )
19 |
20 | func isPrintable(s string) bool {
21 | for _, r := range s {
22 | if !unicode.IsPrint(r) {
23 | return false
24 | }
25 | }
26 | return true
27 | }
28 |
29 | var LogFormatter = func(values ...interface{}) (messages []interface{}) {
30 | if len(values) > 1 {
31 | var (
32 | sql string
33 | formattedValues []string
34 | level = values[0]
35 | currentTime = "\n\033[33m[" + NowFunc().Format("2006-01-02 15:04:05") + "]\033[0m"
36 | source = fmt.Sprintf("\033[35m(%v)\033[0m", values[1])
37 | )
38 |
39 | messages = []interface{}{source, currentTime}
40 |
41 | if level == "sql" {
42 | // duration
43 | messages = append(messages, fmt.Sprintf(" \033[36;1m[%.2fms]\033[0m ", float64(values[2].(time.Duration).Nanoseconds()/1e4)/100.0))
44 | // sql
45 |
46 | for _, value := range values[4].([]interface{}) {
47 | indirectValue := reflect.Indirect(reflect.ValueOf(value))
48 | if indirectValue.IsValid() {
49 | value = indirectValue.Interface()
50 | if t, ok := value.(time.Time); ok {
51 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
52 | } else if b, ok := value.([]byte); ok {
53 | if str := string(b); isPrintable(str) {
54 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
55 | } else {
56 | formattedValues = append(formattedValues, "''")
57 | }
58 | } else if r, ok := value.(driver.Valuer); ok {
59 | if value, err := r.Value(); err == nil && value != nil {
60 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
61 | } else {
62 | formattedValues = append(formattedValues, "NULL")
63 | }
64 | } else {
65 | formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
66 | }
67 | } else {
68 | formattedValues = append(formattedValues, "NULL")
69 | }
70 | }
71 |
72 | // differentiate between $n placeholders or else treat like ?
73 | if numericPlaceHolderRegexp.MatchString(values[3].(string)) {
74 | sql = values[3].(string)
75 | for index, value := range formattedValues {
76 | placeholder := fmt.Sprintf(`\$%d([^\d]|$)`, index+1)
77 | sql = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1")
78 | }
79 | } else {
80 | formattedValuesLength := len(formattedValues)
81 | for index, value := range sqlRegexp.Split(values[3].(string), -1) {
82 | sql += value
83 | if index < formattedValuesLength {
84 | sql += formattedValues[index]
85 | }
86 | }
87 | }
88 |
89 | messages = append(messages, sql)
90 | } else {
91 | messages = append(messages, "\033[31;1m")
92 | messages = append(messages, values[2:]...)
93 | messages = append(messages, "\033[0m")
94 | }
95 | }
96 |
97 | return
98 | }
99 |
100 | type logger interface {
101 | Print(v ...interface{})
102 | }
103 |
104 | // LogWriter log writer interface
105 | type LogWriter interface {
106 | Println(v ...interface{})
107 | }
108 |
109 | // Logger default logger
110 | type Logger struct {
111 | LogWriter
112 | }
113 |
114 | // Print format & print log
115 | func (logger Logger) Print(values ...interface{}) {
116 | logger.Println(LogFormatter(values...)...)
117 | }
118 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_save.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import "reflect"
4 |
5 | func beginTransactionCallback(scope *Scope) {
6 | scope.Begin()
7 | }
8 |
9 | func commitOrRollbackTransactionCallback(scope *Scope) {
10 | scope.CommitOrRollback()
11 | }
12 |
13 | func saveFieldAsAssociation(scope *Scope, field *Field) (bool, *Relationship) {
14 | if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored {
15 | if value, ok := field.TagSettings["SAVE_ASSOCIATIONS"]; !ok || (value != "false" && value != "skip") {
16 | if relationship := field.Relationship; relationship != nil {
17 | return true, relationship
18 | }
19 | }
20 | }
21 | return false, nil
22 | }
23 |
24 | func saveBeforeAssociationsCallback(scope *Scope) {
25 | if !scope.shouldSaveAssociations() {
26 | return
27 | }
28 | for _, field := range scope.Fields() {
29 | if ok, relationship := saveFieldAsAssociation(scope, field); ok && relationship.Kind == "belongs_to" {
30 | fieldValue := field.Field.Addr().Interface()
31 | scope.Err(scope.NewDB().Save(fieldValue).Error)
32 | if len(relationship.ForeignFieldNames) != 0 {
33 | // set value's foreign key
34 | for idx, fieldName := range relationship.ForeignFieldNames {
35 | associationForeignName := relationship.AssociationForeignDBNames[idx]
36 | if foreignField, ok := scope.New(fieldValue).FieldByName(associationForeignName); ok {
37 | scope.Err(scope.SetColumn(fieldName, foreignField.Field.Interface()))
38 | }
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
45 | func saveAfterAssociationsCallback(scope *Scope) {
46 | if !scope.shouldSaveAssociations() {
47 | return
48 | }
49 | for _, field := range scope.Fields() {
50 | if ok, relationship := saveFieldAsAssociation(scope, field); ok &&
51 | (relationship.Kind == "has_one" || relationship.Kind == "has_many" || relationship.Kind == "many_to_many") {
52 | value := field.Field
53 |
54 | switch value.Kind() {
55 | case reflect.Slice:
56 | for i := 0; i < value.Len(); i++ {
57 | newDB := scope.NewDB()
58 | elem := value.Index(i).Addr().Interface()
59 | newScope := newDB.NewScope(elem)
60 |
61 | if relationship.JoinTableHandler == nil && len(relationship.ForeignFieldNames) != 0 {
62 | for idx, fieldName := range relationship.ForeignFieldNames {
63 | associationForeignName := relationship.AssociationForeignDBNames[idx]
64 | if f, ok := scope.FieldByName(associationForeignName); ok {
65 | scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
66 | }
67 | }
68 | }
69 |
70 | if relationship.PolymorphicType != "" {
71 | scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
72 | }
73 |
74 | scope.Err(newDB.Save(elem).Error)
75 |
76 | if joinTableHandler := relationship.JoinTableHandler; joinTableHandler != nil {
77 | scope.Err(joinTableHandler.Add(joinTableHandler, newDB, scope.Value, newScope.Value))
78 | }
79 | }
80 | default:
81 | elem := value.Addr().Interface()
82 | newScope := scope.New(elem)
83 | if len(relationship.ForeignFieldNames) != 0 {
84 | for idx, fieldName := range relationship.ForeignFieldNames {
85 | associationForeignName := relationship.AssociationForeignDBNames[idx]
86 | if f, ok := scope.FieldByName(associationForeignName); ok {
87 | scope.Err(newScope.SetColumn(fieldName, f.Field.Interface()))
88 | }
89 | }
90 | }
91 |
92 | if relationship.PolymorphicType != "" {
93 | scope.Err(newScope.SetColumn(relationship.PolymorphicType, relationship.PolymorphicValue))
94 | }
95 | scope.Err(scope.NewDB().Save(elem).Error)
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_update.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | // Define callbacks for updating
10 | func init() {
11 | DefaultCallback.Update().Register("gorm:assign_updating_attributes", assignUpdatingAttributesCallback)
12 | DefaultCallback.Update().Register("gorm:begin_transaction", beginTransactionCallback)
13 | DefaultCallback.Update().Register("gorm:before_update", beforeUpdateCallback)
14 | DefaultCallback.Update().Register("gorm:save_before_associations", saveBeforeAssociationsCallback)
15 | DefaultCallback.Update().Register("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
16 | DefaultCallback.Update().Register("gorm:update", updateCallback)
17 | DefaultCallback.Update().Register("gorm:save_after_associations", saveAfterAssociationsCallback)
18 | DefaultCallback.Update().Register("gorm:after_update", afterUpdateCallback)
19 | DefaultCallback.Update().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback)
20 | }
21 |
22 | // assignUpdatingAttributesCallback assign updating attributes to model
23 | func assignUpdatingAttributesCallback(scope *Scope) {
24 | if attrs, ok := scope.InstanceGet("gorm:update_interface"); ok {
25 | if updateMaps, hasUpdate := scope.updatedAttrsWithValues(attrs); hasUpdate {
26 | scope.InstanceSet("gorm:update_attrs", updateMaps)
27 | } else {
28 | scope.SkipLeft()
29 | }
30 | }
31 | }
32 |
33 | // beforeUpdateCallback will invoke `BeforeSave`, `BeforeUpdate` method before updating
34 | func beforeUpdateCallback(scope *Scope) {
35 | if scope.DB().HasBlockGlobalUpdate() && !scope.hasConditions() {
36 | scope.Err(errors.New("Missing WHERE clause while updating"))
37 | return
38 | }
39 | if _, ok := scope.Get("gorm:update_column"); !ok {
40 | if !scope.HasError() {
41 | scope.CallMethod("BeforeSave")
42 | }
43 | if !scope.HasError() {
44 | scope.CallMethod("BeforeUpdate")
45 | }
46 | }
47 | }
48 |
49 | // updateTimeStampForUpdateCallback will set `UpdatedAt` when updating
50 | func updateTimeStampForUpdateCallback(scope *Scope) {
51 | if _, ok := scope.Get("gorm:update_column"); !ok {
52 | scope.SetColumn("UpdatedAt", NowFunc())
53 | }
54 | }
55 |
56 | // updateCallback the callback used to update data to database
57 | func updateCallback(scope *Scope) {
58 | if !scope.HasError() {
59 | var sqls []string
60 |
61 | if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok {
62 | for column, value := range updateAttrs.(map[string]interface{}) {
63 | sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(column), scope.AddToVars(value)))
64 | }
65 | } else {
66 | for _, field := range scope.Fields() {
67 | if scope.changeableField(field) {
68 | if !field.IsPrimaryKey && field.IsNormal {
69 | sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface())))
70 | } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" {
71 | for _, foreignKey := range relationship.ForeignDBNames {
72 | if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) {
73 | sqls = append(sqls,
74 | fmt.Sprintf("%v = %v", scope.Quote(foreignField.DBName), scope.AddToVars(foreignField.Field.Interface())))
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
82 | var extraOption string
83 | if str, ok := scope.Get("gorm:update_option"); ok {
84 | extraOption = fmt.Sprint(str)
85 | }
86 |
87 | if len(sqls) > 0 {
88 | scope.Raw(fmt.Sprintf(
89 | "UPDATE %v SET %v%v%v",
90 | scope.QuotedTableName(),
91 | strings.Join(sqls, ", "),
92 | addExtraSpaceIfExist(scope.CombinedConditionSql()),
93 | addExtraSpaceIfExist(extraOption),
94 | )).Exec()
95 | }
96 | }
97 | }
98 |
99 | // afterUpdateCallback will invoke `AfterUpdate`, `AfterSave` method after updating
100 | func afterUpdateCallback(scope *Scope) {
101 | if _, ok := scope.Get("gorm:update_column"); !ok {
102 | if !scope.HasError() {
103 | scope.CallMethod("AfterUpdate")
104 | }
105 | if !scope.HasError() {
106 | scope.CallMethod("AfterSave")
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialect.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | "reflect"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | // Dialect interface contains behaviors that differ across SQL database
12 | type Dialect interface {
13 | // GetName get dialect's name
14 | GetName() string
15 |
16 | // SetDB set db for dialect
17 | SetDB(db SQLCommon)
18 |
19 | // BindVar return the placeholder for actual values in SQL statements, in many dbs it is "?", Postgres using $1
20 | BindVar(i int) string
21 | // Quote quotes field name to avoid SQL parsing exceptions by using a reserved word as a field name
22 | Quote(key string) string
23 | // DataTypeOf return data's sql type
24 | DataTypeOf(field *StructField) string
25 |
26 | // HasIndex check has index or not
27 | HasIndex(tableName string, indexName string) bool
28 | // HasForeignKey check has foreign key or not
29 | HasForeignKey(tableName string, foreignKeyName string) bool
30 | // RemoveIndex remove index
31 | RemoveIndex(tableName string, indexName string) error
32 | // HasTable check has table or not
33 | HasTable(tableName string) bool
34 | // HasColumn check has column or not
35 | HasColumn(tableName string, columnName string) bool
36 |
37 | // LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case
38 | LimitAndOffsetSQL(limit, offset interface{}) string
39 | // SelectFromDummyTable return select values, for most dbs, `SELECT values` just works, mysql needs `SELECT value FROM DUAL`
40 | SelectFromDummyTable() string
41 | // LastInsertIdReturningSuffix most dbs support LastInsertId, but postgres needs to use `RETURNING`
42 | LastInsertIDReturningSuffix(tableName, columnName string) string
43 |
44 | // BuildForeignKeyName returns a foreign key name for the given table, field and reference
45 | BuildForeignKeyName(tableName, field, dest string) string
46 |
47 | // CurrentDatabase return current database name
48 | CurrentDatabase() string
49 | }
50 |
51 | var dialectsMap = map[string]Dialect{}
52 |
53 | func newDialect(name string, db SQLCommon) Dialect {
54 | if value, ok := dialectsMap[name]; ok {
55 | dialect := reflect.New(reflect.TypeOf(value).Elem()).Interface().(Dialect)
56 | dialect.SetDB(db)
57 | return dialect
58 | }
59 |
60 | fmt.Printf("`%v` is not officially supported, running under compatibility mode.\n", name)
61 | commontDialect := &commonDialect{}
62 | commontDialect.SetDB(db)
63 | return commontDialect
64 | }
65 |
66 | // RegisterDialect register new dialect
67 | func RegisterDialect(name string, dialect Dialect) {
68 | dialectsMap[name] = dialect
69 | }
70 |
71 | // ParseFieldStructForDialect get field's sql data type
72 | var ParseFieldStructForDialect = func(field *StructField, dialect Dialect) (fieldValue reflect.Value, sqlType string, size int, additionalType string) {
73 | // Get redirected field type
74 | var (
75 | reflectType = field.Struct.Type
76 | dataType = field.TagSettings["TYPE"]
77 | )
78 |
79 | for reflectType.Kind() == reflect.Ptr {
80 | reflectType = reflectType.Elem()
81 | }
82 |
83 | // Get redirected field value
84 | fieldValue = reflect.Indirect(reflect.New(reflectType))
85 |
86 | if gormDataType, ok := fieldValue.Interface().(interface {
87 | GormDataType(Dialect) string
88 | }); ok {
89 | dataType = gormDataType.GormDataType(dialect)
90 | }
91 |
92 | // Get scanner's real value
93 | var getScannerValue func(reflect.Value)
94 | getScannerValue = func(value reflect.Value) {
95 | fieldValue = value
96 | if _, isScanner := reflect.New(fieldValue.Type()).Interface().(sql.Scanner); isScanner && fieldValue.Kind() == reflect.Struct {
97 | getScannerValue(fieldValue.Field(0))
98 | }
99 | }
100 | getScannerValue(fieldValue)
101 |
102 | // Default Size
103 | if num, ok := field.TagSettings["SIZE"]; ok {
104 | size, _ = strconv.Atoi(num)
105 | } else {
106 | size = 255
107 | }
108 |
109 | // Default type from tag setting
110 | additionalType = field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"]
111 | if value, ok := field.TagSettings["DEFAULT"]; ok {
112 | additionalType = additionalType + " DEFAULT " + value
113 | }
114 |
115 | return fieldValue, dataType, size, strings.TrimSpace(additionalType)
116 | }
117 |
--------------------------------------------------------------------------------
/app/master/admin/views/console.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Goss控制台
5 |
6 |
7 |
8 |
9 |
10 |
33 |
34 |
api节点
35 |
36 |
37 |
38 |
39 | | 节点名称 |
40 | 节点IP |
41 | cpu数量 |
42 | 内存大小 |
43 | 操作 |
44 |
45 |
46 |
47 | {{range .apiList}}
48 |
49 | | {{.Name}} |
50 | {{.SourceIP}} |
51 | {{.CpuNum}} |
52 | {{.MemSize}} |
53 |
54 |
55 |
56 |
57 | |
58 |
59 | {{end}}
60 |
61 |
62 |
63 |
64 |
存储节点
65 |
66 |
67 |
68 |
69 | | 节点名称 |
70 | 节点IP |
71 | cpu数量 |
72 | 内存大小 |
73 | 操作 |
74 |
75 |
76 |
77 | {{range .storageList}}
78 |
79 | | {{.Name}} |
80 | {{.SourceIP}} |
81 | {{.CpuNum}} |
82 | {{.MemSize}} |
83 |
84 |
85 |
86 |
87 | |
88 |
89 | {{end}}
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialect_postgres.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "strings"
7 | "time"
8 | )
9 |
10 | type postgres struct {
11 | commonDialect
12 | }
13 |
14 | func init() {
15 | RegisterDialect("postgres", &postgres{})
16 | }
17 |
18 | func (postgres) GetName() string {
19 | return "postgres"
20 | }
21 |
22 | func (postgres) BindVar(i int) string {
23 | return fmt.Sprintf("$%v", i)
24 | }
25 |
26 | func (s *postgres) DataTypeOf(field *StructField) string {
27 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s)
28 |
29 | if sqlType == "" {
30 | switch dataValue.Kind() {
31 | case reflect.Bool:
32 | sqlType = "boolean"
33 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
34 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
35 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
36 | sqlType = "serial"
37 | } else {
38 | sqlType = "integer"
39 | }
40 | case reflect.Int64, reflect.Uint64:
41 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
42 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
43 | sqlType = "bigserial"
44 | } else {
45 | sqlType = "bigint"
46 | }
47 | case reflect.Float32, reflect.Float64:
48 | sqlType = "numeric"
49 | case reflect.String:
50 | if _, ok := field.TagSettings["SIZE"]; !ok {
51 | size = 0 // if SIZE haven't been set, use `text` as the default type, as there are no performance different
52 | }
53 |
54 | if size > 0 && size < 65532 {
55 | sqlType = fmt.Sprintf("varchar(%d)", size)
56 | } else {
57 | sqlType = "text"
58 | }
59 | case reflect.Struct:
60 | if _, ok := dataValue.Interface().(time.Time); ok {
61 | sqlType = "timestamp with time zone"
62 | }
63 | case reflect.Map:
64 | if dataValue.Type().Name() == "Hstore" {
65 | sqlType = "hstore"
66 | }
67 | default:
68 | if IsByteArrayOrSlice(dataValue) {
69 | sqlType = "bytea"
70 | } else if isUUID(dataValue) {
71 | sqlType = "uuid"
72 | }
73 | }
74 | }
75 |
76 | if sqlType == "" {
77 | panic(fmt.Sprintf("invalid sql type %s (%s) for postgres", dataValue.Type().Name(), dataValue.Kind().String()))
78 | }
79 |
80 | if strings.TrimSpace(additionalType) == "" {
81 | return sqlType
82 | }
83 | return fmt.Sprintf("%v %v", sqlType, additionalType)
84 | }
85 |
86 | func (s postgres) HasIndex(tableName string, indexName string) bool {
87 | var count int
88 | s.db.QueryRow("SELECT count(*) FROM pg_indexes WHERE tablename = $1 AND indexname = $2", tableName, indexName).Scan(&count)
89 | return count > 0
90 | }
91 |
92 | func (s postgres) HasForeignKey(tableName string, foreignKeyName string) bool {
93 | var count int
94 | s.db.QueryRow("SELECT count(con.conname) FROM pg_constraint con WHERE $1::regclass::oid = con.conrelid AND con.conname = $2 AND con.contype='f'", tableName, foreignKeyName).Scan(&count)
95 | return count > 0
96 | }
97 |
98 | func (s postgres) HasTable(tableName string) bool {
99 | var count int
100 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_name = $1 AND table_type = 'BASE TABLE'", tableName).Scan(&count)
101 | return count > 0
102 | }
103 |
104 | func (s postgres) HasColumn(tableName string, columnName string) bool {
105 | var count int
106 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_name = $1 AND column_name = $2", tableName, columnName).Scan(&count)
107 | return count > 0
108 | }
109 |
110 | func (s postgres) CurrentDatabase() (name string) {
111 | s.db.QueryRow("SELECT CURRENT_DATABASE()").Scan(&name)
112 | return
113 | }
114 |
115 | func (s postgres) LastInsertIDReturningSuffix(tableName, key string) string {
116 | return fmt.Sprintf("RETURNING %v.%v", tableName, key)
117 | }
118 |
119 | func (postgres) SupportLastInsertID() bool {
120 | return false
121 | }
122 |
123 | func isUUID(value reflect.Value) bool {
124 | if value.Kind() != reflect.Array || value.Type().Len() != 16 {
125 | return false
126 | }
127 | typename := value.Type().Name()
128 | lower := strings.ToLower(typename)
129 | return "uuid" == lower || "guid" == lower
130 | }
131 |
--------------------------------------------------------------------------------
/app/api/handler/node.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "io"
7 | "math/rand"
8 | "net"
9 | "time"
10 |
11 | "github.com/Goss-io/goss/lib"
12 | "github.com/Goss-io/goss/lib/hardware"
13 | "github.com/Goss-io/goss/lib/ini"
14 | "github.com/Goss-io/goss/lib/logd"
15 | "github.com/Goss-io/goss/lib/packet"
16 | "github.com/Goss-io/goss/lib/protocol"
17 | )
18 |
19 | //connMaster .
20 | func (a *APIService) connMaster() {
21 | conn := a.conn(a.MasterNode)
22 | //连接初始化
23 | if err := a.conninit(conn); err != nil {
24 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), err.Error())
25 | time.Sleep(time.Second * 1)
26 | a.connMaster()
27 | return
28 | }
29 |
30 | for {
31 | pkt, err := packet.Parse(conn)
32 | if err != nil && err == io.EOF {
33 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "master节点断开连接,稍后重新连接master节点")
34 | a.connMaster()
35 | return
36 | }
37 |
38 | //判断协议类型.
39 | if pkt.Protocol == protocol.ADD_NODE {
40 | ip := string(pkt.Body)
41 | //新增节点.
42 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "新增存储节点:"+ip)
43 | a.Storage = append(a.Storage, ip)
44 | }
45 |
46 | if pkt.Protocol == protocol.REMOVE_NODE {
47 | ip := string(pkt.Body)
48 | //删除节点.
49 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "收到master节点要求与:"+ip+"节点断开的消息")
50 | a.RemoveStorageNode(ip)
51 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "断开成功")
52 | }
53 | }
54 | }
55 |
56 | func (a *APIService) RemoveStorageNode(nodeip string) {
57 | for index, v := range a.Storage {
58 | if v == nodeip {
59 | a.Storage = append(a.Storage[:index], a.Storage[index+1:]...)
60 | }
61 | }
62 | }
63 |
64 | //conn .
65 | func (a *APIService) conn(node string) net.Conn {
66 | conn, err := net.Dial("tcp4", node)
67 | if err != nil {
68 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "master节点连接失败,稍后重新连接")
69 | time.Sleep(time.Second * 1)
70 | return a.conn(node)
71 | }
72 |
73 | return conn
74 | }
75 |
76 | //connInit 连接初始化.
77 | func (a *APIService) conninit(conn net.Conn) error {
78 | //向主节点发送授权信息.
79 | if err := a.sendAuth(conn); err != nil {
80 | return err
81 | }
82 |
83 | //发送节点信息.
84 | if err := a.sendNodeInfo(conn); err != nil {
85 | return err
86 | }
87 | return nil
88 | }
89 |
90 | //auth 发送授权信息.
91 | func (a *APIService) sendAuth(conn net.Conn) error {
92 | tokenBuf := []byte(ini.GetString("token"))
93 | buf := packet.New(tokenBuf, tokenBuf, protocol.CONN_AUTH)
94 | _, err := conn.Write(buf)
95 | if err != nil {
96 | return errors.New("授权信息发送失败")
97 | }
98 |
99 | pkt, err := packet.Parse(conn)
100 | if err != nil {
101 | return errors.New("授权失败")
102 | }
103 |
104 | if string(pkt.Body) == "fail" {
105 | return errors.New("授权信息验证失败")
106 | }
107 |
108 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "授权成功")
109 | return nil
110 | }
111 |
112 | //sendNodeInfo 上报节点信息.
113 | func (a *APIService) sendNodeInfo(conn net.Conn) error {
114 | h := hardware.New()
115 | nodeInfo := protocol.NodeInfo{
116 | Types: string(packet.NodeTypes_Api),
117 | CpuNum: h.Cpu.Num,
118 | MemSize: h.Mem.Total,
119 | SourceIP: a.Addr,
120 | Name: ini.GetString("node_name"),
121 | }
122 |
123 | content, err := json.Marshal(nodeInfo)
124 | if err != nil {
125 | return err
126 | }
127 |
128 | hashBuf := lib.Hash(string(content))
129 | buf := packet.New(content, hashBuf, protocol.REPORT_NODE_INFO)
130 | _, err = conn.Write(buf)
131 | if err != nil {
132 | return err
133 | }
134 |
135 | pkt, err := packet.Parse(conn)
136 | if err != nil {
137 | return err
138 | }
139 |
140 | if string(pkt.Body) == "fail" {
141 | return errors.New("发送节点信息失败")
142 | }
143 |
144 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "上报节点信息成功")
145 | return nil
146 | }
147 |
148 | func (a *APIService) SelectNode(nodenum int) []string {
149 | rand.Seed(time.Now().UnixNano())
150 | list := []string{}
151 | for _, v := range a.Storage {
152 | list = append(list, v)
153 | }
154 |
155 | nodeipList := []string{}
156 | num := 0
157 | for {
158 | if num >= nodenum {
159 | break
160 | }
161 | index := rand.Int() % len(list)
162 | addr := list[index]
163 |
164 | if !lib.InArray(addr, nodeipList) {
165 | num++
166 | nodeipList = append(nodeipList, addr)
167 | }
168 | }
169 | return nodeipList
170 | }
171 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/search.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | "regexp"
6 | )
7 |
8 | type search struct {
9 | db *DB
10 | whereConditions []map[string]interface{}
11 | orConditions []map[string]interface{}
12 | notConditions []map[string]interface{}
13 | havingConditions []map[string]interface{}
14 | joinConditions []map[string]interface{}
15 | initAttrs []interface{}
16 | assignAttrs []interface{}
17 | selects map[string]interface{}
18 | omits []string
19 | orders []interface{}
20 | preload []searchPreload
21 | offset interface{}
22 | limit interface{}
23 | group string
24 | tableName string
25 | raw bool
26 | Unscoped bool
27 | ignoreOrderQuery bool
28 | }
29 |
30 | type searchPreload struct {
31 | schema string
32 | conditions []interface{}
33 | }
34 |
35 | func (s *search) clone() *search {
36 | clone := *s
37 | return &clone
38 | }
39 |
40 | func (s *search) Where(query interface{}, values ...interface{}) *search {
41 | s.whereConditions = append(s.whereConditions, map[string]interface{}{"query": query, "args": values})
42 | return s
43 | }
44 |
45 | func (s *search) Not(query interface{}, values ...interface{}) *search {
46 | s.notConditions = append(s.notConditions, map[string]interface{}{"query": query, "args": values})
47 | return s
48 | }
49 |
50 | func (s *search) Or(query interface{}, values ...interface{}) *search {
51 | s.orConditions = append(s.orConditions, map[string]interface{}{"query": query, "args": values})
52 | return s
53 | }
54 |
55 | func (s *search) Attrs(attrs ...interface{}) *search {
56 | s.initAttrs = append(s.initAttrs, toSearchableMap(attrs...))
57 | return s
58 | }
59 |
60 | func (s *search) Assign(attrs ...interface{}) *search {
61 | s.assignAttrs = append(s.assignAttrs, toSearchableMap(attrs...))
62 | return s
63 | }
64 |
65 | func (s *search) Order(value interface{}, reorder ...bool) *search {
66 | if len(reorder) > 0 && reorder[0] {
67 | s.orders = []interface{}{}
68 | }
69 |
70 | if value != nil && value != "" {
71 | s.orders = append(s.orders, value)
72 | }
73 | return s
74 | }
75 |
76 | var distinctSQLRegexp = regexp.MustCompile(`(?i)distinct[^a-z]+[a-z]+`)
77 |
78 | func (s *search) Select(query interface{}, args ...interface{}) *search {
79 | if distinctSQLRegexp.MatchString(fmt.Sprint(query)) {
80 | s.ignoreOrderQuery = true
81 | }
82 |
83 | s.selects = map[string]interface{}{"query": query, "args": args}
84 | return s
85 | }
86 |
87 | func (s *search) Omit(columns ...string) *search {
88 | s.omits = columns
89 | return s
90 | }
91 |
92 | func (s *search) Limit(limit interface{}) *search {
93 | s.limit = limit
94 | return s
95 | }
96 |
97 | func (s *search) Offset(offset interface{}) *search {
98 | s.offset = offset
99 | return s
100 | }
101 |
102 | func (s *search) Group(query string) *search {
103 | s.group = s.getInterfaceAsSQL(query)
104 | return s
105 | }
106 |
107 | func (s *search) Having(query string, values ...interface{}) *search {
108 | s.havingConditions = append(s.havingConditions, map[string]interface{}{"query": query, "args": values})
109 | return s
110 | }
111 |
112 | func (s *search) Joins(query string, values ...interface{}) *search {
113 | s.joinConditions = append(s.joinConditions, map[string]interface{}{"query": query, "args": values})
114 | return s
115 | }
116 |
117 | func (s *search) Preload(schema string, values ...interface{}) *search {
118 | var preloads []searchPreload
119 | for _, preload := range s.preload {
120 | if preload.schema != schema {
121 | preloads = append(preloads, preload)
122 | }
123 | }
124 | preloads = append(preloads, searchPreload{schema, values})
125 | s.preload = preloads
126 | return s
127 | }
128 |
129 | func (s *search) Raw(b bool) *search {
130 | s.raw = b
131 | return s
132 | }
133 |
134 | func (s *search) unscoped() *search {
135 | s.Unscoped = true
136 | return s
137 | }
138 |
139 | func (s *search) Table(name string) *search {
140 | s.tableName = name
141 | return s
142 | }
143 |
144 | func (s *search) getInterfaceAsSQL(value interface{}) (str string) {
145 | switch value.(type) {
146 | case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
147 | str = fmt.Sprintf("%v", value)
148 | default:
149 | s.db.AddError(ErrInvalidSQL)
150 | }
151 |
152 | if str == "-1" {
153 | return ""
154 | }
155 | return
156 | }
157 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialect_common.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "regexp"
7 | "strconv"
8 | "strings"
9 | "time"
10 | )
11 |
12 | // DefaultForeignKeyNamer contains the default foreign key name generator method
13 | type DefaultForeignKeyNamer struct {
14 | }
15 |
16 | type commonDialect struct {
17 | db SQLCommon
18 | DefaultForeignKeyNamer
19 | }
20 |
21 | func init() {
22 | RegisterDialect("common", &commonDialect{})
23 | }
24 |
25 | func (commonDialect) GetName() string {
26 | return "common"
27 | }
28 |
29 | func (s *commonDialect) SetDB(db SQLCommon) {
30 | s.db = db
31 | }
32 |
33 | func (commonDialect) BindVar(i int) string {
34 | return "$$$" // ?
35 | }
36 |
37 | func (commonDialect) Quote(key string) string {
38 | return fmt.Sprintf(`"%s"`, key)
39 | }
40 |
41 | func (s *commonDialect) DataTypeOf(field *StructField) string {
42 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s)
43 |
44 | if sqlType == "" {
45 | switch dataValue.Kind() {
46 | case reflect.Bool:
47 | sqlType = "BOOLEAN"
48 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
49 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
50 | sqlType = "INTEGER AUTO_INCREMENT"
51 | } else {
52 | sqlType = "INTEGER"
53 | }
54 | case reflect.Int64, reflect.Uint64:
55 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
56 | sqlType = "BIGINT AUTO_INCREMENT"
57 | } else {
58 | sqlType = "BIGINT"
59 | }
60 | case reflect.Float32, reflect.Float64:
61 | sqlType = "FLOAT"
62 | case reflect.String:
63 | if size > 0 && size < 65532 {
64 | sqlType = fmt.Sprintf("VARCHAR(%d)", size)
65 | } else {
66 | sqlType = "VARCHAR(65532)"
67 | }
68 | case reflect.Struct:
69 | if _, ok := dataValue.Interface().(time.Time); ok {
70 | sqlType = "TIMESTAMP"
71 | }
72 | default:
73 | if _, ok := dataValue.Interface().([]byte); ok {
74 | if size > 0 && size < 65532 {
75 | sqlType = fmt.Sprintf("BINARY(%d)", size)
76 | } else {
77 | sqlType = "BINARY(65532)"
78 | }
79 | }
80 | }
81 | }
82 |
83 | if sqlType == "" {
84 | panic(fmt.Sprintf("invalid sql type %s (%s) for commonDialect", dataValue.Type().Name(), dataValue.Kind().String()))
85 | }
86 |
87 | if strings.TrimSpace(additionalType) == "" {
88 | return sqlType
89 | }
90 | return fmt.Sprintf("%v %v", sqlType, additionalType)
91 | }
92 |
93 | func (s commonDialect) HasIndex(tableName string, indexName string) bool {
94 | var count int
95 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = ? AND table_name = ? AND index_name = ?", s.CurrentDatabase(), tableName, indexName).Scan(&count)
96 | return count > 0
97 | }
98 |
99 | func (s commonDialect) RemoveIndex(tableName string, indexName string) error {
100 | _, err := s.db.Exec(fmt.Sprintf("DROP INDEX %v", indexName))
101 | return err
102 | }
103 |
104 | func (s commonDialect) HasForeignKey(tableName string, foreignKeyName string) bool {
105 | return false
106 | }
107 |
108 | func (s commonDialect) HasTable(tableName string) bool {
109 | var count int
110 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_name = ?", s.CurrentDatabase(), tableName).Scan(&count)
111 | return count > 0
112 | }
113 |
114 | func (s commonDialect) HasColumn(tableName string, columnName string) bool {
115 | var count int
116 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = ? AND table_name = ? AND column_name = ?", s.CurrentDatabase(), tableName, columnName).Scan(&count)
117 | return count > 0
118 | }
119 |
120 | func (s commonDialect) CurrentDatabase() (name string) {
121 | s.db.QueryRow("SELECT DATABASE()").Scan(&name)
122 | return
123 | }
124 |
125 | func (commonDialect) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
126 | if limit != nil {
127 | if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
128 | sql += fmt.Sprintf(" LIMIT %d", parsedLimit)
129 | }
130 | }
131 | if offset != nil {
132 | if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 {
133 | sql += fmt.Sprintf(" OFFSET %d", parsedOffset)
134 | }
135 | }
136 | return
137 | }
138 |
139 | func (commonDialect) SelectFromDummyTable() string {
140 | return ""
141 | }
142 |
143 | func (commonDialect) LastInsertIDReturningSuffix(tableName, columnName string) string {
144 | return ""
145 | }
146 |
147 | func (DefaultForeignKeyNamer) BuildForeignKeyName(tableName, field, dest string) string {
148 | keyName := fmt.Sprintf("%s_%s_%s_foreign", tableName, field, dest)
149 | keyName = regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(keyName, "_")
150 | return keyName
151 | }
152 |
153 | // IsByteArrayOrSlice returns true of the reflected value is an array or slice
154 | func IsByteArrayOrSlice(value reflect.Value) bool {
155 | return (value.Kind() == reflect.Array || value.Kind() == reflect.Slice) && value.Type().Elem() == reflect.TypeOf(uint8(0))
156 | }
157 |
--------------------------------------------------------------------------------
/app/master/handler/net.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "io"
8 | "log"
9 | "net"
10 |
11 | "github.com/Goss-io/goss/lib"
12 | "github.com/Goss-io/goss/lib/ini"
13 | "github.com/Goss-io/goss/lib/logd"
14 | "github.com/Goss-io/goss/lib/packet"
15 | "github.com/Goss-io/goss/lib/protocol"
16 | )
17 |
18 | type NodeParams struct {
19 | Conn net.Conn
20 | Types packet.NodeTypes
21 | }
22 |
23 | type MasterService struct {
24 | Conn map[string]net.Conn
25 | Auth map[string]bool
26 | Port string
27 | }
28 |
29 | //NewMaster .
30 | func NewMaster() *MasterService {
31 | return &MasterService{
32 | Conn: make(map[string]net.Conn),
33 | Auth: make(map[string]bool),
34 | Port: fmt.Sprintf(":%d", ini.GetInt("node_port")),
35 | }
36 | }
37 |
38 | //Start.
39 | func (m *MasterService) Start() {
40 | go NewAdmin()
41 | m.listen()
42 | select {}
43 | }
44 |
45 | //listen .
46 | func (m *MasterService) listen() {
47 | listener, err := net.Listen("tcp4", m.Port)
48 | if err != nil {
49 | log.Panicln(err)
50 | }
51 |
52 | defer listener.Close()
53 | for {
54 | conn, err := listener.Accept()
55 | if err != nil {
56 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), err.Error())
57 | continue
58 | }
59 |
60 | //验证授权信息.
61 | ip := conn.RemoteAddr().String()
62 | if err := m.connInit(conn, ip); err != nil {
63 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), err.Error())
64 | continue
65 | }
66 |
67 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "收到来自:"+ip+"的连接请求")
68 | go m.handler(ip, conn)
69 | }
70 | }
71 |
72 | //connInit 连接初始化.
73 | func (m *MasterService) connInit(conn net.Conn, ip string) error {
74 | //验证授权信息.
75 | if err := m.checkAuth(conn, ip); err != nil {
76 | buf := packet.New([]byte("fail"), lib.Hash("fail"), protocol.MSG)
77 | conn.Write(buf)
78 | return err
79 | }
80 | buf := packet.New([]byte("success"), lib.Hash("success"), protocol.MSG)
81 | _, err := conn.Write(buf)
82 | if err != nil {
83 | return err
84 | }
85 |
86 | //接收节点信息.
87 | if err := m.parseNodeInfo(conn, ip); err != nil {
88 | buf := packet.New([]byte("fail"), lib.Hash("fail"), protocol.MSG)
89 | conn.Write(buf)
90 | return err
91 | }
92 | return nil
93 | }
94 |
95 | //parseNodeInfo .
96 | func (m *MasterService) parseNodeInfo(conn net.Conn, ip string) error {
97 | pkt, err := packet.Parse(conn)
98 | if err != nil {
99 | return err
100 | }
101 | //判读协议类型.
102 | if pkt.Protocol == protocol.REPORT_NODE_INFO {
103 | n := protocol.NodeInfo{}
104 | if err = json.Unmarshal(pkt.Body, &n); err != nil {
105 | return err
106 | }
107 |
108 | node := Node{
109 | Name: n.Name,
110 | SourceIP: n.SourceIP,
111 | CpuNum: n.CpuNum,
112 | MemSize: n.MemSize,
113 | IP: ip,
114 | CreateAt: lib.Time(),
115 | Types: packet.NodeTypes(n.Types),
116 | }
117 |
118 | GossNode = append(GossNode, node)
119 | m.Conn[node.SourceIP] = conn
120 |
121 | buf := packet.New([]byte("success"), lib.Hash("success"), protocol.MSG)
122 | _, err = conn.Write(buf)
123 | if err != nil {
124 | return err
125 | }
126 |
127 | //新存储节点上线,通知所有的api节点.
128 | if node.Types == packet.NodeTypes_Storage {
129 | //通知api节点.
130 | apiList := GetApiList()
131 | for _, v := range apiList {
132 | pkt := packet.New([]byte(node.SourceIP), lib.Hash(node.SourceIP), protocol.ADD_NODE)
133 | _, err = m.Conn[v.SourceIP].Write(pkt)
134 | if err != nil {
135 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "通知api节点:"+node.SourceIP+"新增storage节点失败,稍后重新通知")
136 | RemoveNode(m, ip)
137 | return err
138 | }
139 |
140 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "通知api节点,新增存储节点:"+node.SourceIP+"成功")
141 | }
142 | }
143 |
144 | if node.Types == packet.NodeTypes_Api {
145 | //告知新上线的api节点多有的storage节点ip.
146 | storageList := GetStorageList()
147 | for _, v := range storageList {
148 | pktMsg := packet.New([]byte(v.SourceIP), lib.Hash(v.SourceIP), protocol.ADD_NODE)
149 | _, err = m.Conn[node.SourceIP].Write(pktMsg)
150 | if err != nil {
151 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), "通知api节点:"+v.SourceIP+"storage节点失败,稍后重新通知")
152 | RemoveNode(m, ip)
153 | return err
154 | }
155 |
156 | logd.Make(logd.Level_INFO, logd.GetLogpath(), "通知api节点,新增存储节点:"+v.SourceIP+"成功")
157 | }
158 | }
159 | }
160 | return nil
161 | }
162 |
163 | //checkAuth .
164 | func (m *MasterService) checkAuth(conn net.Conn, ip string) error {
165 | pkt, err := packet.Parse(conn)
166 | if err != nil {
167 | return err
168 | }
169 |
170 | //判读协议.
171 | if pkt.Protocol != protocol.CONN_AUTH {
172 | return errors.New("协议错误")
173 | }
174 |
175 | //验证授权信息是否正确.
176 | if string(pkt.Body) != ini.GetString("token") {
177 | return errors.New("授权失败")
178 | }
179 |
180 | m.Auth[ip] = true
181 | return nil
182 | }
183 |
184 | //handler .
185 | func (m *MasterService) handler(ip string, conn net.Conn) {
186 | defer conn.Close()
187 | for {
188 | //验证是否已经授权.
189 | if !m.Auth[ip] {
190 | conn.Write([]byte("fail"))
191 | return
192 | }
193 |
194 | pkt, err := packet.ParseNode(conn)
195 | if err != nil && err == io.EOF {
196 | logd.Make(logd.Level_WARNING, logd.GetLogpath(), ip+"断开连接")
197 | //从节点列表中移除.
198 | RemoveNode(m, ip)
199 | return
200 | }
201 |
202 | //判断协议.
203 | if pkt.Protocol == protocol.ADD_NODE {
204 | //新增节点信息.
205 | info := Node{
206 | Types: pkt.Types,
207 | IP: ip,
208 | SourceIP: pkt.IP,
209 | CreateAt: lib.Time(),
210 | }
211 | GossNode = append(GossNode, info)
212 | }
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/dialect_mysql.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "crypto/sha1"
5 | "fmt"
6 | "reflect"
7 | "regexp"
8 | "strconv"
9 | "strings"
10 | "time"
11 | "unicode/utf8"
12 | )
13 |
14 | type mysql struct {
15 | commonDialect
16 | }
17 |
18 | func init() {
19 | RegisterDialect("mysql", &mysql{})
20 | }
21 |
22 | func (mysql) GetName() string {
23 | return "mysql"
24 | }
25 |
26 | func (mysql) Quote(key string) string {
27 | return fmt.Sprintf("`%s`", key)
28 | }
29 |
30 | // Get Data Type for MySQL Dialect
31 | func (s *mysql) DataTypeOf(field *StructField) string {
32 | var dataValue, sqlType, size, additionalType = ParseFieldStructForDialect(field, s)
33 |
34 | // MySQL allows only one auto increment column per table, and it must
35 | // be a KEY column.
36 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok {
37 | if _, ok = field.TagSettings["INDEX"]; !ok && !field.IsPrimaryKey {
38 | delete(field.TagSettings, "AUTO_INCREMENT")
39 | }
40 | }
41 |
42 | if sqlType == "" {
43 | switch dataValue.Kind() {
44 | case reflect.Bool:
45 | sqlType = "boolean"
46 | case reflect.Int8:
47 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
48 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
49 | sqlType = "tinyint AUTO_INCREMENT"
50 | } else {
51 | sqlType = "tinyint"
52 | }
53 | case reflect.Int, reflect.Int16, reflect.Int32:
54 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
55 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
56 | sqlType = "int AUTO_INCREMENT"
57 | } else {
58 | sqlType = "int"
59 | }
60 | case reflect.Uint8:
61 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
62 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
63 | sqlType = "tinyint unsigned AUTO_INCREMENT"
64 | } else {
65 | sqlType = "tinyint unsigned"
66 | }
67 | case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uintptr:
68 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
69 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
70 | sqlType = "int unsigned AUTO_INCREMENT"
71 | } else {
72 | sqlType = "int unsigned"
73 | }
74 | case reflect.Int64:
75 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
76 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
77 | sqlType = "bigint AUTO_INCREMENT"
78 | } else {
79 | sqlType = "bigint"
80 | }
81 | case reflect.Uint64:
82 | if _, ok := field.TagSettings["AUTO_INCREMENT"]; ok || field.IsPrimaryKey {
83 | field.TagSettings["AUTO_INCREMENT"] = "AUTO_INCREMENT"
84 | sqlType = "bigint unsigned AUTO_INCREMENT"
85 | } else {
86 | sqlType = "bigint unsigned"
87 | }
88 | case reflect.Float32, reflect.Float64:
89 | sqlType = "double"
90 | case reflect.String:
91 | if size > 0 && size < 65532 {
92 | sqlType = fmt.Sprintf("varchar(%d)", size)
93 | } else {
94 | sqlType = "longtext"
95 | }
96 | case reflect.Struct:
97 | if _, ok := dataValue.Interface().(time.Time); ok {
98 | if _, ok := field.TagSettings["NOT NULL"]; ok {
99 | sqlType = "timestamp"
100 | } else {
101 | sqlType = "timestamp NULL"
102 | }
103 | }
104 | default:
105 | if IsByteArrayOrSlice(dataValue) {
106 | if size > 0 && size < 65532 {
107 | sqlType = fmt.Sprintf("varbinary(%d)", size)
108 | } else {
109 | sqlType = "longblob"
110 | }
111 | }
112 | }
113 | }
114 |
115 | if sqlType == "" {
116 | panic(fmt.Sprintf("invalid sql type %s (%s) for mysql", dataValue.Type().Name(), dataValue.Kind().String()))
117 | }
118 |
119 | if strings.TrimSpace(additionalType) == "" {
120 | return sqlType
121 | }
122 | return fmt.Sprintf("%v %v", sqlType, additionalType)
123 | }
124 |
125 | func (s mysql) RemoveIndex(tableName string, indexName string) error {
126 | _, err := s.db.Exec(fmt.Sprintf("DROP INDEX %v ON %v", indexName, s.Quote(tableName)))
127 | return err
128 | }
129 |
130 | func (s mysql) LimitAndOffsetSQL(limit, offset interface{}) (sql string) {
131 | if limit != nil {
132 | if parsedLimit, err := strconv.ParseInt(fmt.Sprint(limit), 0, 0); err == nil && parsedLimit >= 0 {
133 | sql += fmt.Sprintf(" LIMIT %d", parsedLimit)
134 |
135 | if offset != nil {
136 | if parsedOffset, err := strconv.ParseInt(fmt.Sprint(offset), 0, 0); err == nil && parsedOffset >= 0 {
137 | sql += fmt.Sprintf(" OFFSET %d", parsedOffset)
138 | }
139 | }
140 | }
141 | }
142 | return
143 | }
144 |
145 | func (s mysql) HasForeignKey(tableName string, foreignKeyName string) bool {
146 | var count int
147 | s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=? AND TABLE_NAME=? AND CONSTRAINT_NAME=? AND CONSTRAINT_TYPE='FOREIGN KEY'", s.CurrentDatabase(), tableName, foreignKeyName).Scan(&count)
148 | return count > 0
149 | }
150 |
151 | func (s mysql) CurrentDatabase() (name string) {
152 | s.db.QueryRow("SELECT DATABASE()").Scan(&name)
153 | return
154 | }
155 |
156 | func (mysql) SelectFromDummyTable() string {
157 | return "FROM DUAL"
158 | }
159 |
160 | func (s mysql) BuildForeignKeyName(tableName, field, dest string) string {
161 | keyName := s.commonDialect.BuildForeignKeyName(tableName, field, dest)
162 | if utf8.RuneCountInString(keyName) <= 64 {
163 | return keyName
164 | }
165 | h := sha1.New()
166 | h.Write([]byte(keyName))
167 | bs := h.Sum(nil)
168 |
169 | // sha1 is 40 digits, keep first 24 characters of destination
170 | destRunes := []rune(regexp.MustCompile("(_*[^a-zA-Z]+_*|_+)").ReplaceAllString(dest, "_"))
171 | if len(destRunes) > 24 {
172 | destRunes = destRunes[:24]
173 | }
174 |
175 | return fmt.Sprintf("%s%x", string(destRunes), bs)
176 | }
177 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_create.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // Define callbacks for creating
9 | func init() {
10 | DefaultCallback.Create().Register("gorm:begin_transaction", beginTransactionCallback)
11 | DefaultCallback.Create().Register("gorm:before_create", beforeCreateCallback)
12 | DefaultCallback.Create().Register("gorm:save_before_associations", saveBeforeAssociationsCallback)
13 | DefaultCallback.Create().Register("gorm:update_time_stamp", updateTimeStampForCreateCallback)
14 | DefaultCallback.Create().Register("gorm:create", createCallback)
15 | DefaultCallback.Create().Register("gorm:force_reload_after_create", forceReloadAfterCreateCallback)
16 | DefaultCallback.Create().Register("gorm:save_after_associations", saveAfterAssociationsCallback)
17 | DefaultCallback.Create().Register("gorm:after_create", afterCreateCallback)
18 | DefaultCallback.Create().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback)
19 | }
20 |
21 | // beforeCreateCallback will invoke `BeforeSave`, `BeforeCreate` method before creating
22 | func beforeCreateCallback(scope *Scope) {
23 | if !scope.HasError() {
24 | scope.CallMethod("BeforeSave")
25 | }
26 | if !scope.HasError() {
27 | scope.CallMethod("BeforeCreate")
28 | }
29 | }
30 |
31 | // updateTimeStampForCreateCallback will set `CreatedAt`, `UpdatedAt` when creating
32 | func updateTimeStampForCreateCallback(scope *Scope) {
33 | if !scope.HasError() {
34 | now := NowFunc()
35 | scope.SetColumn("CreatedAt", now)
36 | scope.SetColumn("UpdatedAt", now)
37 | }
38 | }
39 |
40 | // createCallback the callback used to insert data into database
41 | func createCallback(scope *Scope) {
42 | if !scope.HasError() {
43 | defer scope.trace(NowFunc())
44 |
45 | var (
46 | columns, placeholders []string
47 | blankColumnsWithDefaultValue []string
48 | )
49 |
50 | for _, field := range scope.Fields() {
51 | if scope.changeableField(field) {
52 | if field.IsNormal {
53 | if field.IsBlank && field.HasDefaultValue {
54 | blankColumnsWithDefaultValue = append(blankColumnsWithDefaultValue, scope.Quote(field.DBName))
55 | scope.InstanceSet("gorm:blank_columns_with_default_value", blankColumnsWithDefaultValue)
56 | } else if !field.IsPrimaryKey || !field.IsBlank {
57 | columns = append(columns, scope.Quote(field.DBName))
58 | placeholders = append(placeholders, scope.AddToVars(field.Field.Interface()))
59 | }
60 | } else if field.Relationship != nil && field.Relationship.Kind == "belongs_to" {
61 | for _, foreignKey := range field.Relationship.ForeignDBNames {
62 | if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) {
63 | columns = append(columns, scope.Quote(foreignField.DBName))
64 | placeholders = append(placeholders, scope.AddToVars(foreignField.Field.Interface()))
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | var (
72 | returningColumn = "*"
73 | quotedTableName = scope.QuotedTableName()
74 | primaryField = scope.PrimaryField()
75 | extraOption string
76 | )
77 |
78 | if str, ok := scope.Get("gorm:insert_option"); ok {
79 | extraOption = fmt.Sprint(str)
80 | }
81 |
82 | if primaryField != nil {
83 | returningColumn = scope.Quote(primaryField.DBName)
84 | }
85 |
86 | lastInsertIDReturningSuffix := scope.Dialect().LastInsertIDReturningSuffix(quotedTableName, returningColumn)
87 |
88 | if len(columns) == 0 {
89 | scope.Raw(fmt.Sprintf(
90 | "INSERT INTO %v DEFAULT VALUES%v%v",
91 | quotedTableName,
92 | addExtraSpaceIfExist(extraOption),
93 | addExtraSpaceIfExist(lastInsertIDReturningSuffix),
94 | ))
95 | } else {
96 | scope.Raw(fmt.Sprintf(
97 | "INSERT INTO %v (%v) VALUES (%v)%v%v",
98 | scope.QuotedTableName(),
99 | strings.Join(columns, ","),
100 | strings.Join(placeholders, ","),
101 | addExtraSpaceIfExist(extraOption),
102 | addExtraSpaceIfExist(lastInsertIDReturningSuffix),
103 | ))
104 | }
105 |
106 | // execute create sql
107 | if lastInsertIDReturningSuffix == "" || primaryField == nil {
108 | if result, err := scope.SQLDB().Exec(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {
109 | // set rows affected count
110 | scope.db.RowsAffected, _ = result.RowsAffected()
111 |
112 | // set primary value to primary field
113 | if primaryField != nil && primaryField.IsBlank {
114 | if primaryValue, err := result.LastInsertId(); scope.Err(err) == nil {
115 | scope.Err(primaryField.Set(primaryValue))
116 | }
117 | }
118 | }
119 | } else {
120 | if primaryField.Field.CanAddr() {
121 | if err := scope.SQLDB().QueryRow(scope.SQL, scope.SQLVars...).Scan(primaryField.Field.Addr().Interface()); scope.Err(err) == nil {
122 | primaryField.IsBlank = false
123 | scope.db.RowsAffected = 1
124 | }
125 | } else {
126 | scope.Err(ErrUnaddressable)
127 | }
128 | }
129 | }
130 | }
131 |
132 | // forceReloadAfterCreateCallback will reload columns that having default value, and set it back to current object
133 | func forceReloadAfterCreateCallback(scope *Scope) {
134 | if blankColumnsWithDefaultValue, ok := scope.InstanceGet("gorm:blank_columns_with_default_value"); ok {
135 | db := scope.DB().New().Table(scope.TableName()).Select(blankColumnsWithDefaultValue.([]string))
136 | for _, field := range scope.Fields() {
137 | if field.IsPrimaryKey && !field.IsBlank {
138 | db = db.Where(fmt.Sprintf("%v = ?", field.DBName), field.Field.Interface())
139 | }
140 | }
141 | db.Scan(scope.Value)
142 | }
143 | }
144 |
145 | // afterCreateCallback will invoke `AfterCreate`, `AfterSave` method after creating
146 | func afterCreateCallback(scope *Scope) {
147 | if !scope.HasError() {
148 | scope.CallMethod("AfterCreate")
149 | }
150 | if !scope.HasError() {
151 | scope.CallMethod("AfterSave")
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/app/api/handler/api.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "net/http"
10 | "strings"
11 | "time"
12 |
13 | "github.com/Goss-io/goss/app/api/conf"
14 | "github.com/Goss-io/goss/db"
15 | "github.com/Goss-io/goss/lib"
16 | "github.com/Goss-io/goss/lib/filetype"
17 | "github.com/Goss-io/goss/lib/ini"
18 | )
19 |
20 | //NewAPI .
21 | func NewAPI() *APIService {
22 | cf := conf.Conf.Node
23 | apiSrv := APIService{
24 | Port: fmt.Sprintf(":%d", cf.Port),
25 | Addr: fmt.Sprintf("%s:%d", ini.GetString("node_ip"), ini.GetInt("node_port")),
26 | MasterNode: ini.GetString("master_node"),
27 | }
28 | return &apiSrv
29 | }
30 |
31 | //Start .
32 | func (a *APIService) Start() {
33 | go a.connMaster()
34 | a.httpSrv()
35 | }
36 |
37 | //httpSrv .
38 | func (a *APIService) httpSrv() {
39 | http.HandleFunc("/", a.handler)
40 | if err := http.ListenAndServe(a.Port, nil); err != nil {
41 | log.Panicf("%+v\n", err)
42 | }
43 | }
44 |
45 | //handler .
46 | func (a *APIService) handler(w http.ResponseWriter, r *http.Request) {
47 | if r.Method == http.MethodGet {
48 | a.get(w, r)
49 | return
50 | }
51 |
52 | if r.Method == http.MethodPut {
53 | a.put(w, r)
54 | return
55 | }
56 |
57 | if r.Method == http.MethodDelete {
58 | a.delete(w, r)
59 | return
60 | }
61 |
62 | w.WriteHeader(http.StatusNotFound)
63 | }
64 |
65 | //get.
66 | func (a *APIService) get(w http.ResponseWriter, r *http.Request) {
67 | //验证bucket是否存在.
68 | bkt := db.Bucket{
69 | Host: r.Host,
70 | }
71 | if err := bkt.Query(); err != nil {
72 | log.Printf("err:%+v\n", err)
73 | w.Write([]byte(err.Error()))
74 | return
75 | }
76 | if bkt.ID < 1 {
77 | w.Write([]byte("不存在"))
78 | return
79 | }
80 |
81 | //获取访问的文件.
82 | name, err := a.getParse(r.URL.EscapedPath())
83 | if err != nil {
84 | w.Write([]byte(err.Error()))
85 | return
86 | }
87 |
88 | meta := db.Metadata{
89 | Name: name,
90 | BucketID: bkt.ID,
91 | }
92 | list, err := meta.QueryNodeIP()
93 | if err != nil {
94 | log.Printf("%+v\n", err)
95 | w.Write([]byte(err.Error()))
96 | return
97 | }
98 | if len(list) < 1 {
99 | w.Write([]byte("not found"))
100 | return
101 | }
102 |
103 | buf := make(chan []byte, meta.Size)
104 | var errnum = 0
105 | for _, nodeip := range list {
106 | b, err := a.Read(meta.StorePath, nodeip)
107 | if err != nil {
108 | errnum++
109 | log.Printf("%+v\n", err)
110 | continue
111 | }
112 | buf <- b
113 | break
114 | }
115 |
116 | msg := <-buf
117 | //如果msg为空的话,则判断是否errnum > 0.
118 | if len(msg) > 0 {
119 | w.Header().Set("Content-Type", meta.Type)
120 | _, err = w.Write(msg)
121 | if err != nil {
122 | log.Printf("err:%+v\n", err)
123 | }
124 | return
125 | }
126 | if errnum > 0 {
127 | w.Write([]byte("获取失败"))
128 | return
129 | }
130 | w.Write([]byte("not found"))
131 | }
132 |
133 | //getParse get请求解析文件名.
134 | //兼容目录结构,host以后的路径都为文件名.
135 | func (a *APIService) getParse(url string) (name string, err error) {
136 | path := strings.TrimLeft(url, "/")
137 | if len(path) < 1 {
138 | return name, errors.New("not fount")
139 | }
140 |
141 | return path, nil
142 | }
143 |
144 | //put.
145 | func (a *APIService) put(w http.ResponseWriter, r *http.Request) {
146 | //验证bucket是否存在.
147 | bkt := db.Bucket{
148 | Host: r.Host,
149 | }
150 | if err := bkt.Query(); err != nil {
151 | log.Printf("err:%+v\n", err)
152 | w.Write([]byte(err.Error()))
153 | w.WriteHeader(http.StatusNotFound)
154 | return
155 | }
156 | if bkt.ID < 1 {
157 | w.Write([]byte("不存在"))
158 | w.WriteHeader(http.StatusNotFound)
159 | return
160 | }
161 |
162 | //验证AccessKey和SecretKey是否正确.
163 | ak := r.Header.Get("AccessKey")
164 | sk := r.Header.Get("SecretKey")
165 | if len(ak) != 32 || len(sk) != 32 || bkt.AccessKey != ak || bkt.SecretKey != sk {
166 | w.Write([]byte("授权失败"))
167 | w.WriteHeader(http.StatusForbidden)
168 | return
169 | }
170 |
171 | //获取文件名称,文件大小,文件类型,文件hash.
172 | //元数据.
173 | name, err := a.getParse(r.URL.EscapedPath())
174 | if err != nil {
175 | w.Write([]byte(err.Error()))
176 | w.WriteHeader(http.StatusNotFound)
177 | return
178 | }
179 |
180 | fBody, err := ioutil.ReadAll(r.Body)
181 | if err != nil {
182 | log.Printf("%+v\n", err)
183 | w.Write([]byte("fail"))
184 | return
185 | }
186 |
187 | //获取文件类型.
188 | f16 := fmt.Sprintf("%x", fBody)
189 | ft := filetype.Parse(f16[:10])
190 |
191 | //计算文件hash.
192 | fhash := lib.FileHash(fBody)
193 |
194 | //采用用强一致性来记录文件.
195 | nodeipList := a.SelectNode(3)
196 | log.Printf("nodeipList:%+v\n", nodeipList)
197 |
198 | //开启事物操作,防止节点数据不一致.
199 | tx := db.Db.Begin()
200 | for _, nodeip := range nodeipList {
201 | storePath, err := a.Write(fhash, fBody, nodeip)
202 | if err != nil {
203 | log.Printf("%+v\n", err)
204 | w.Write([]byte("fail"))
205 | tx.Rollback()
206 | //todo 删除已经记录的文件.
207 | return
208 | }
209 |
210 | //记录文件元数据.
211 | metadata := db.Metadata{
212 | Name: name,
213 | Type: ft,
214 | Size: int64(len(fBody)),
215 | Hash: fhash,
216 | StoreNode: nodeip,
217 | StorePath: storePath,
218 | Usable: true,
219 | BucketID: bkt.ID,
220 | }
221 | if err = tx.Create(&metadata).Error; err != nil {
222 | log.Printf("%+v\n", err)
223 | w.Write([]byte("fail"))
224 | tx.Rollback()
225 | return
226 | }
227 | }
228 |
229 | if err := tx.Commit().Error; err != nil {
230 | log.Printf("%+v\n", err)
231 | w.Write([]byte("fail"))
232 | return
233 | }
234 |
235 | w.Write([]byte("success"))
236 | }
237 |
238 | //delete.
239 | func (a *APIService) delete(w http.ResponseWriter, r *http.Request) {
240 |
241 | }
242 |
243 | //Write 发送消息.
244 | func (a *APIService) Write(fhash string, body []byte, nodeip string) (storePath string, err error) {
245 | url := fmt.Sprintf("http://%s/", nodeip)
246 | req, err := http.NewRequest(http.MethodPut, url, bytes.NewReader(body))
247 | if err != nil {
248 | log.Printf("err:%+v\n", err)
249 | return storePath, err
250 | }
251 | req.Header.Set("token", conf.Conf.Node.Token)
252 | req.Header.Set("fhash", fhash)
253 | client := http.Client{}
254 | response, err := client.Do(req)
255 | if err != nil {
256 | log.Printf("err:%+v\n", err)
257 | return storePath, err
258 | }
259 |
260 | b, err := ioutil.ReadAll(response.Body)
261 | if err != nil {
262 | log.Printf("err:%+v\n", err)
263 | return storePath, err
264 | }
265 |
266 | resp := lib.ParseMsg(b)
267 | if !resp.Status {
268 | return storePath, errors.New(resp.Msg.(string))
269 | }
270 |
271 | return resp.Msg.(string), nil
272 | }
273 |
274 | //Read 读取消息.
275 | func (a *APIService) Read(fpath, nodeip string) (fbody []byte, err error) {
276 | url := fmt.Sprintf("http://%s/", nodeip)
277 | req, err := http.NewRequest(http.MethodGet, url, nil)
278 | if err != nil {
279 | log.Printf("err:%+v\n", err)
280 | return fbody, err
281 | }
282 | req.Header.Set("fpath", fpath)
283 | req.Header.Set("token", conf.Conf.Node.Token)
284 | client := http.Client{
285 | Timeout: time.Second * 1,
286 | }
287 | response, err := client.Do(req)
288 | if err != nil {
289 | log.Printf("err:%+v\n", err)
290 | return fbody, nil
291 | }
292 |
293 | b, err := ioutil.ReadAll(response.Body)
294 | if err != nil {
295 | log.Printf("err:%+v\n", err)
296 | return fbody, nil
297 | }
298 |
299 | resp := lib.ParseMsg(b)
300 | if !resp.Status {
301 | return fbody, errors.New(resp.Msg.(string))
302 | }
303 | return resp.Body, nil
304 | }
305 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/join_table_handler.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "reflect"
7 | "strings"
8 | )
9 |
10 | // JoinTableHandlerInterface is an interface for how to handle many2many relations
11 | type JoinTableHandlerInterface interface {
12 | // initialize join table handler
13 | Setup(relationship *Relationship, tableName string, source reflect.Type, destination reflect.Type)
14 | // Table return join table's table name
15 | Table(db *DB) string
16 | // Add create relationship in join table for source and destination
17 | Add(handler JoinTableHandlerInterface, db *DB, source interface{}, destination interface{}) error
18 | // Delete delete relationship in join table for sources
19 | Delete(handler JoinTableHandlerInterface, db *DB, sources ...interface{}) error
20 | // JoinWith query with `Join` conditions
21 | JoinWith(handler JoinTableHandlerInterface, db *DB, source interface{}) *DB
22 | // SourceForeignKeys return source foreign keys
23 | SourceForeignKeys() []JoinTableForeignKey
24 | // DestinationForeignKeys return destination foreign keys
25 | DestinationForeignKeys() []JoinTableForeignKey
26 | }
27 |
28 | // JoinTableForeignKey join table foreign key struct
29 | type JoinTableForeignKey struct {
30 | DBName string
31 | AssociationDBName string
32 | }
33 |
34 | // JoinTableSource is a struct that contains model type and foreign keys
35 | type JoinTableSource struct {
36 | ModelType reflect.Type
37 | ForeignKeys []JoinTableForeignKey
38 | }
39 |
40 | // JoinTableHandler default join table handler
41 | type JoinTableHandler struct {
42 | TableName string `sql:"-"`
43 | Source JoinTableSource `sql:"-"`
44 | Destination JoinTableSource `sql:"-"`
45 | }
46 |
47 | // SourceForeignKeys return source foreign keys
48 | func (s *JoinTableHandler) SourceForeignKeys() []JoinTableForeignKey {
49 | return s.Source.ForeignKeys
50 | }
51 |
52 | // DestinationForeignKeys return destination foreign keys
53 | func (s *JoinTableHandler) DestinationForeignKeys() []JoinTableForeignKey {
54 | return s.Destination.ForeignKeys
55 | }
56 |
57 | // Setup initialize a default join table handler
58 | func (s *JoinTableHandler) Setup(relationship *Relationship, tableName string, source reflect.Type, destination reflect.Type) {
59 | s.TableName = tableName
60 |
61 | s.Source = JoinTableSource{ModelType: source}
62 | s.Source.ForeignKeys = []JoinTableForeignKey{}
63 | for idx, dbName := range relationship.ForeignFieldNames {
64 | s.Source.ForeignKeys = append(s.Source.ForeignKeys, JoinTableForeignKey{
65 | DBName: relationship.ForeignDBNames[idx],
66 | AssociationDBName: dbName,
67 | })
68 | }
69 |
70 | s.Destination = JoinTableSource{ModelType: destination}
71 | s.Destination.ForeignKeys = []JoinTableForeignKey{}
72 | for idx, dbName := range relationship.AssociationForeignFieldNames {
73 | s.Destination.ForeignKeys = append(s.Destination.ForeignKeys, JoinTableForeignKey{
74 | DBName: relationship.AssociationForeignDBNames[idx],
75 | AssociationDBName: dbName,
76 | })
77 | }
78 | }
79 |
80 | // Table return join table's table name
81 | func (s JoinTableHandler) Table(db *DB) string {
82 | return s.TableName
83 | }
84 |
85 | func (s JoinTableHandler) getSearchMap(db *DB, sources ...interface{}) map[string]interface{} {
86 | values := map[string]interface{}{}
87 |
88 | for _, source := range sources {
89 | scope := db.NewScope(source)
90 | modelType := scope.GetModelStruct().ModelType
91 |
92 | if s.Source.ModelType == modelType {
93 | for _, foreignKey := range s.Source.ForeignKeys {
94 | if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
95 | values[foreignKey.DBName] = field.Field.Interface()
96 | }
97 | }
98 | } else if s.Destination.ModelType == modelType {
99 | for _, foreignKey := range s.Destination.ForeignKeys {
100 | if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
101 | values[foreignKey.DBName] = field.Field.Interface()
102 | }
103 | }
104 | }
105 | }
106 | return values
107 | }
108 |
109 | // Add create relationship in join table for source and destination
110 | func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source interface{}, destination interface{}) error {
111 | scope := db.NewScope("")
112 | searchMap := s.getSearchMap(db, source, destination)
113 |
114 | var assignColumns, binVars, conditions []string
115 | var values []interface{}
116 | for key, value := range searchMap {
117 | assignColumns = append(assignColumns, scope.Quote(key))
118 | binVars = append(binVars, `?`)
119 | conditions = append(conditions, fmt.Sprintf("%v = ?", scope.Quote(key)))
120 | values = append(values, value)
121 | }
122 |
123 | for _, value := range values {
124 | values = append(values, value)
125 | }
126 |
127 | quotedTable := scope.Quote(handler.Table(db))
128 | sql := fmt.Sprintf(
129 | "INSERT INTO %v (%v) SELECT %v %v WHERE NOT EXISTS (SELECT * FROM %v WHERE %v)",
130 | quotedTable,
131 | strings.Join(assignColumns, ","),
132 | strings.Join(binVars, ","),
133 | scope.Dialect().SelectFromDummyTable(),
134 | quotedTable,
135 | strings.Join(conditions, " AND "),
136 | )
137 |
138 | return db.Exec(sql, values...).Error
139 | }
140 |
141 | // Delete delete relationship in join table for sources
142 | func (s JoinTableHandler) Delete(handler JoinTableHandlerInterface, db *DB, sources ...interface{}) error {
143 | var (
144 | scope = db.NewScope(nil)
145 | conditions []string
146 | values []interface{}
147 | )
148 |
149 | for key, value := range s.getSearchMap(db, sources...) {
150 | conditions = append(conditions, fmt.Sprintf("%v = ?", scope.Quote(key)))
151 | values = append(values, value)
152 | }
153 |
154 | return db.Table(handler.Table(db)).Where(strings.Join(conditions, " AND "), values...).Delete("").Error
155 | }
156 |
157 | // JoinWith query with `Join` conditions
158 | func (s JoinTableHandler) JoinWith(handler JoinTableHandlerInterface, db *DB, source interface{}) *DB {
159 | var (
160 | scope = db.NewScope(source)
161 | tableName = handler.Table(db)
162 | quotedTableName = scope.Quote(tableName)
163 | joinConditions []string
164 | values []interface{}
165 | )
166 |
167 | if s.Source.ModelType == scope.GetModelStruct().ModelType {
168 | destinationTableName := db.NewScope(reflect.New(s.Destination.ModelType).Interface()).QuotedTableName()
169 | for _, foreignKey := range s.Destination.ForeignKeys {
170 | joinConditions = append(joinConditions, fmt.Sprintf("%v.%v = %v.%v", quotedTableName, scope.Quote(foreignKey.DBName), destinationTableName, scope.Quote(foreignKey.AssociationDBName)))
171 | }
172 |
173 | var foreignDBNames []string
174 | var foreignFieldNames []string
175 |
176 | for _, foreignKey := range s.Source.ForeignKeys {
177 | foreignDBNames = append(foreignDBNames, foreignKey.DBName)
178 | if field, ok := scope.FieldByName(foreignKey.AssociationDBName); ok {
179 | foreignFieldNames = append(foreignFieldNames, field.Name)
180 | }
181 | }
182 |
183 | foreignFieldValues := scope.getColumnAsArray(foreignFieldNames, scope.Value)
184 |
185 | var condString string
186 | if len(foreignFieldValues) > 0 {
187 | var quotedForeignDBNames []string
188 | for _, dbName := range foreignDBNames {
189 | quotedForeignDBNames = append(quotedForeignDBNames, tableName+"."+dbName)
190 | }
191 |
192 | condString = fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, quotedForeignDBNames), toQueryMarks(foreignFieldValues))
193 |
194 | keys := scope.getColumnAsArray(foreignFieldNames, scope.Value)
195 | values = append(values, toQueryValues(keys))
196 | } else {
197 | condString = fmt.Sprintf("1 <> 1")
198 | }
199 |
200 | return db.Joins(fmt.Sprintf("INNER JOIN %v ON %v", quotedTableName, strings.Join(joinConditions, " AND "))).
201 | Where(condString, toQueryValues(foreignFieldValues)...)
202 | }
203 |
204 | db.Error = errors.New("wrong source type for join table handler")
205 | return db
206 | }
207 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/utils.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "bytes"
5 | "database/sql/driver"
6 | "fmt"
7 | "reflect"
8 | "regexp"
9 | "runtime"
10 | "strings"
11 | "sync"
12 | "time"
13 | )
14 |
15 | // NowFunc returns current time, this function is exported in order to be able
16 | // to give the flexibility to the developer to customize it according to their
17 | // needs, e.g:
18 | // gorm.NowFunc = func() time.Time {
19 | // return time.Now().UTC()
20 | // }
21 | var NowFunc = func() time.Time {
22 | return time.Now()
23 | }
24 |
25 | // Copied from golint
26 | var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
27 | var commonInitialismsReplacer *strings.Replacer
28 |
29 | var goSrcRegexp = regexp.MustCompile(`jinzhu/gorm/.*.go`)
30 | var goTestRegexp = regexp.MustCompile(`jinzhu/gorm/.*test.go`)
31 |
32 | func init() {
33 | var commonInitialismsForReplacer []string
34 | for _, initialism := range commonInitialisms {
35 | commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
36 | }
37 | commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
38 | }
39 |
40 | type safeMap struct {
41 | m map[string]string
42 | l *sync.RWMutex
43 | }
44 |
45 | func (s *safeMap) Set(key string, value string) {
46 | s.l.Lock()
47 | defer s.l.Unlock()
48 | s.m[key] = value
49 | }
50 |
51 | func (s *safeMap) Get(key string) string {
52 | s.l.RLock()
53 | defer s.l.RUnlock()
54 | return s.m[key]
55 | }
56 |
57 | func newSafeMap() *safeMap {
58 | return &safeMap{l: new(sync.RWMutex), m: make(map[string]string)}
59 | }
60 |
61 | var smap = newSafeMap()
62 |
63 | type strCase bool
64 |
65 | const (
66 | lower strCase = false
67 | upper strCase = true
68 | )
69 |
70 | // ToDBName convert string to db name
71 | func ToDBName(name string) string {
72 | if v := smap.Get(name); v != "" {
73 | return v
74 | }
75 |
76 | if name == "" {
77 | return ""
78 | }
79 |
80 | var (
81 | value = commonInitialismsReplacer.Replace(name)
82 | buf = bytes.NewBufferString("")
83 | lastCase, currCase, nextCase strCase
84 | )
85 |
86 | for i, v := range value[:len(value)-1] {
87 | nextCase = strCase(value[i+1] >= 'A' && value[i+1] <= 'Z')
88 | if i > 0 {
89 | if currCase == upper {
90 | if lastCase == upper && nextCase == upper {
91 | buf.WriteRune(v)
92 | } else {
93 | if value[i-1] != '_' && value[i+1] != '_' {
94 | buf.WriteRune('_')
95 | }
96 | buf.WriteRune(v)
97 | }
98 | } else {
99 | buf.WriteRune(v)
100 | if i == len(value)-2 && nextCase == upper {
101 | buf.WriteRune('_')
102 | }
103 | }
104 | } else {
105 | currCase = upper
106 | buf.WriteRune(v)
107 | }
108 | lastCase = currCase
109 | currCase = nextCase
110 | }
111 |
112 | buf.WriteByte(value[len(value)-1])
113 |
114 | s := strings.ToLower(buf.String())
115 | smap.Set(name, s)
116 | return s
117 | }
118 |
119 | // SQL expression
120 | type expr struct {
121 | expr string
122 | args []interface{}
123 | }
124 |
125 | // Expr generate raw SQL expression, for example:
126 | // DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
127 | func Expr(expression string, args ...interface{}) *expr {
128 | return &expr{expr: expression, args: args}
129 | }
130 |
131 | func indirect(reflectValue reflect.Value) reflect.Value {
132 | for reflectValue.Kind() == reflect.Ptr {
133 | reflectValue = reflectValue.Elem()
134 | }
135 | return reflectValue
136 | }
137 |
138 | func toQueryMarks(primaryValues [][]interface{}) string {
139 | var results []string
140 |
141 | for _, primaryValue := range primaryValues {
142 | var marks []string
143 | for range primaryValue {
144 | marks = append(marks, "?")
145 | }
146 |
147 | if len(marks) > 1 {
148 | results = append(results, fmt.Sprintf("(%v)", strings.Join(marks, ",")))
149 | } else {
150 | results = append(results, strings.Join(marks, ""))
151 | }
152 | }
153 | return strings.Join(results, ",")
154 | }
155 |
156 | func toQueryCondition(scope *Scope, columns []string) string {
157 | var newColumns []string
158 | for _, column := range columns {
159 | newColumns = append(newColumns, scope.Quote(column))
160 | }
161 |
162 | if len(columns) > 1 {
163 | return fmt.Sprintf("(%v)", strings.Join(newColumns, ","))
164 | }
165 | return strings.Join(newColumns, ",")
166 | }
167 |
168 | func toQueryValues(values [][]interface{}) (results []interface{}) {
169 | for _, value := range values {
170 | for _, v := range value {
171 | results = append(results, v)
172 | }
173 | }
174 | return
175 | }
176 |
177 | func fileWithLineNum() string {
178 | for i := 2; i < 15; i++ {
179 | _, file, line, ok := runtime.Caller(i)
180 | if ok && (!goSrcRegexp.MatchString(file) || goTestRegexp.MatchString(file)) {
181 | return fmt.Sprintf("%v:%v", file, line)
182 | }
183 | }
184 | return ""
185 | }
186 |
187 | func isBlank(value reflect.Value) bool {
188 | switch value.Kind() {
189 | case reflect.String:
190 | return value.Len() == 0
191 | case reflect.Bool:
192 | return !value.Bool()
193 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
194 | return value.Int() == 0
195 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
196 | return value.Uint() == 0
197 | case reflect.Float32, reflect.Float64:
198 | return value.Float() == 0
199 | case reflect.Interface, reflect.Ptr:
200 | return value.IsNil()
201 | }
202 |
203 | return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
204 | }
205 |
206 | func toSearchableMap(attrs ...interface{}) (result interface{}) {
207 | if len(attrs) > 1 {
208 | if str, ok := attrs[0].(string); ok {
209 | result = map[string]interface{}{str: attrs[1]}
210 | }
211 | } else if len(attrs) == 1 {
212 | if attr, ok := attrs[0].(map[string]interface{}); ok {
213 | result = attr
214 | }
215 |
216 | if attr, ok := attrs[0].(interface{}); ok {
217 | result = attr
218 | }
219 | }
220 | return
221 | }
222 |
223 | func equalAsString(a interface{}, b interface{}) bool {
224 | return toString(a) == toString(b)
225 | }
226 |
227 | func toString(str interface{}) string {
228 | if values, ok := str.([]interface{}); ok {
229 | var results []string
230 | for _, value := range values {
231 | results = append(results, toString(value))
232 | }
233 | return strings.Join(results, "_")
234 | } else if bytes, ok := str.([]byte); ok {
235 | return string(bytes)
236 | } else if reflectValue := reflect.Indirect(reflect.ValueOf(str)); reflectValue.IsValid() {
237 | return fmt.Sprintf("%v", reflectValue.Interface())
238 | }
239 | return ""
240 | }
241 |
242 | func makeSlice(elemType reflect.Type) interface{} {
243 | if elemType.Kind() == reflect.Slice {
244 | elemType = elemType.Elem()
245 | }
246 | sliceType := reflect.SliceOf(elemType)
247 | slice := reflect.New(sliceType)
248 | slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
249 | return slice.Interface()
250 | }
251 |
252 | func strInSlice(a string, list []string) bool {
253 | for _, b := range list {
254 | if b == a {
255 | return true
256 | }
257 | }
258 | return false
259 | }
260 |
261 | // getValueFromFields return given fields's value
262 | func getValueFromFields(value reflect.Value, fieldNames []string) (results []interface{}) {
263 | // If value is a nil pointer, Indirect returns a zero Value!
264 | // Therefor we need to check for a zero value,
265 | // as FieldByName could panic
266 | if indirectValue := reflect.Indirect(value); indirectValue.IsValid() {
267 | for _, fieldName := range fieldNames {
268 | if fieldValue := indirectValue.FieldByName(fieldName); fieldValue.IsValid() {
269 | result := fieldValue.Interface()
270 | if r, ok := result.(driver.Valuer); ok {
271 | result, _ = r.Value()
272 | }
273 | results = append(results, result)
274 | }
275 | }
276 | }
277 | return
278 | }
279 |
280 | func addExtraSpaceIfExist(str string) string {
281 | if str != "" {
282 | return " " + str
283 | }
284 | return ""
285 | }
286 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // DefaultCallback default callbacks defined by gorm
8 | var DefaultCallback = &Callback{}
9 |
10 | // Callback is a struct that contains all CRUD callbacks
11 | // Field `creates` contains callbacks will be call when creating object
12 | // Field `updates` contains callbacks will be call when updating object
13 | // Field `deletes` contains callbacks will be call when deleting object
14 | // Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association...
15 | // Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
16 | // Field `processors` contains all callback processors, will be used to generate above callbacks in order
17 | type Callback struct {
18 | creates []*func(scope *Scope)
19 | updates []*func(scope *Scope)
20 | deletes []*func(scope *Scope)
21 | queries []*func(scope *Scope)
22 | rowQueries []*func(scope *Scope)
23 | processors []*CallbackProcessor
24 | }
25 |
26 | // CallbackProcessor contains callback informations
27 | type CallbackProcessor struct {
28 | name string // current callback's name
29 | before string // register current callback before a callback
30 | after string // register current callback after a callback
31 | replace bool // replace callbacks with same name
32 | remove bool // delete callbacks with same name
33 | kind string // callback type: create, update, delete, query, row_query
34 | processor *func(scope *Scope) // callback handler
35 | parent *Callback
36 | }
37 |
38 | func (c *Callback) clone() *Callback {
39 | return &Callback{
40 | creates: c.creates,
41 | updates: c.updates,
42 | deletes: c.deletes,
43 | queries: c.queries,
44 | rowQueries: c.rowQueries,
45 | processors: c.processors,
46 | }
47 | }
48 |
49 | // Create could be used to register callbacks for creating object
50 | // db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
51 | // // business logic
52 | // ...
53 | //
54 | // // set error if some thing wrong happened, will rollback the creating
55 | // scope.Err(errors.New("error"))
56 | // })
57 | func (c *Callback) Create() *CallbackProcessor {
58 | return &CallbackProcessor{kind: "create", parent: c}
59 | }
60 |
61 | // Update could be used to register callbacks for updating object, refer `Create` for usage
62 | func (c *Callback) Update() *CallbackProcessor {
63 | return &CallbackProcessor{kind: "update", parent: c}
64 | }
65 |
66 | // Delete could be used to register callbacks for deleting object, refer `Create` for usage
67 | func (c *Callback) Delete() *CallbackProcessor {
68 | return &CallbackProcessor{kind: "delete", parent: c}
69 | }
70 |
71 | // Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
72 | // Refer `Create` for usage
73 | func (c *Callback) Query() *CallbackProcessor {
74 | return &CallbackProcessor{kind: "query", parent: c}
75 | }
76 |
77 | // RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
78 | func (c *Callback) RowQuery() *CallbackProcessor {
79 | return &CallbackProcessor{kind: "row_query", parent: c}
80 | }
81 |
82 | // After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
83 | func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
84 | cp.after = callbackName
85 | return cp
86 | }
87 |
88 | // Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
89 | func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
90 | cp.before = callbackName
91 | return cp
92 | }
93 |
94 | // Register a new callback, refer `Callbacks.Create`
95 | func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
96 | if cp.kind == "row_query" {
97 | if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
98 | fmt.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
99 | cp.before = "gorm:row_query"
100 | }
101 | }
102 |
103 | cp.name = callbackName
104 | cp.processor = &callback
105 | cp.parent.processors = append(cp.parent.processors, cp)
106 | cp.parent.reorder()
107 | }
108 |
109 | // Remove a registered callback
110 | // db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
111 | func (cp *CallbackProcessor) Remove(callbackName string) {
112 | fmt.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
113 | cp.name = callbackName
114 | cp.remove = true
115 | cp.parent.processors = append(cp.parent.processors, cp)
116 | cp.parent.reorder()
117 | }
118 |
119 | // Replace a registered callback with new callback
120 | // db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
121 | // scope.SetColumn("Created", now)
122 | // scope.SetColumn("Updated", now)
123 | // })
124 | func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
125 | fmt.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
126 | cp.name = callbackName
127 | cp.processor = &callback
128 | cp.replace = true
129 | cp.parent.processors = append(cp.parent.processors, cp)
130 | cp.parent.reorder()
131 | }
132 |
133 | // Get registered callback
134 | // db.Callback().Create().Get("gorm:create")
135 | func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
136 | for _, p := range cp.parent.processors {
137 | if p.name == callbackName && p.kind == cp.kind && !cp.remove {
138 | return *p.processor
139 | }
140 | }
141 | return nil
142 | }
143 |
144 | // getRIndex get right index from string slice
145 | func getRIndex(strs []string, str string) int {
146 | for i := len(strs) - 1; i >= 0; i-- {
147 | if strs[i] == str {
148 | return i
149 | }
150 | }
151 | return -1
152 | }
153 |
154 | // sortProcessors sort callback processors based on its before, after, remove, replace
155 | func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
156 | var (
157 | allNames, sortedNames []string
158 | sortCallbackProcessor func(c *CallbackProcessor)
159 | )
160 |
161 | for _, cp := range cps {
162 | // show warning message the callback name already exists
163 | if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
164 | fmt.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
165 | }
166 | allNames = append(allNames, cp.name)
167 | }
168 |
169 | sortCallbackProcessor = func(c *CallbackProcessor) {
170 | if getRIndex(sortedNames, c.name) == -1 { // if not sorted
171 | if c.before != "" { // if defined before callback
172 | if index := getRIndex(sortedNames, c.before); index != -1 {
173 | // if before callback already sorted, append current callback just after it
174 | sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
175 | } else if index := getRIndex(allNames, c.before); index != -1 {
176 | // if before callback exists but haven't sorted, append current callback to last
177 | sortedNames = append(sortedNames, c.name)
178 | sortCallbackProcessor(cps[index])
179 | }
180 | }
181 |
182 | if c.after != "" { // if defined after callback
183 | if index := getRIndex(sortedNames, c.after); index != -1 {
184 | // if after callback already sorted, append current callback just before it
185 | sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
186 | } else if index := getRIndex(allNames, c.after); index != -1 {
187 | // if after callback exists but haven't sorted
188 | cp := cps[index]
189 | // set after callback's before callback to current callback
190 | if cp.before == "" {
191 | cp.before = c.name
192 | }
193 | sortCallbackProcessor(cp)
194 | }
195 | }
196 |
197 | // if current callback haven't been sorted, append it to last
198 | if getRIndex(sortedNames, c.name) == -1 {
199 | sortedNames = append(sortedNames, c.name)
200 | }
201 | }
202 | }
203 |
204 | for _, cp := range cps {
205 | sortCallbackProcessor(cp)
206 | }
207 |
208 | var sortedFuncs []*func(scope *Scope)
209 | for _, name := range sortedNames {
210 | if index := getRIndex(allNames, name); !cps[index].remove {
211 | sortedFuncs = append(sortedFuncs, cps[index].processor)
212 | }
213 | }
214 |
215 | return sortedFuncs
216 | }
217 |
218 | // reorder all registered processors, and reset CRUD callbacks
219 | func (c *Callback) reorder() {
220 | var creates, updates, deletes, queries, rowQueries []*CallbackProcessor
221 |
222 | for _, processor := range c.processors {
223 | if processor.name != "" {
224 | switch processor.kind {
225 | case "create":
226 | creates = append(creates, processor)
227 | case "update":
228 | updates = append(updates, processor)
229 | case "delete":
230 | deletes = append(deletes, processor)
231 | case "query":
232 | queries = append(queries, processor)
233 | case "row_query":
234 | rowQueries = append(rowQueries, processor)
235 | }
236 | }
237 | }
238 |
239 | c.creates = sortProcessors(creates)
240 | c.updates = sortProcessors(updates)
241 | c.deletes = sortProcessors(deletes)
242 | c.queries = sortProcessors(queries)
243 | c.rowQueries = sortProcessors(rowQueries)
244 | }
245 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/callback_query_preload.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "reflect"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | // preloadCallback used to preload associations
12 | func preloadCallback(scope *Scope) {
13 |
14 | if _, ok := scope.Get("gorm:auto_preload"); ok {
15 | autoPreload(scope)
16 | }
17 |
18 | if scope.Search.preload == nil || scope.HasError() {
19 | return
20 | }
21 |
22 | var (
23 | preloadedMap = map[string]bool{}
24 | fields = scope.Fields()
25 | )
26 |
27 | for _, preload := range scope.Search.preload {
28 | var (
29 | preloadFields = strings.Split(preload.schema, ".")
30 | currentScope = scope
31 | currentFields = fields
32 | )
33 |
34 | for idx, preloadField := range preloadFields {
35 | var currentPreloadConditions []interface{}
36 |
37 | if currentScope == nil {
38 | continue
39 | }
40 |
41 | // if not preloaded
42 | if preloadKey := strings.Join(preloadFields[:idx+1], "."); !preloadedMap[preloadKey] {
43 |
44 | // assign search conditions to last preload
45 | if idx == len(preloadFields)-1 {
46 | currentPreloadConditions = preload.conditions
47 | }
48 |
49 | for _, field := range currentFields {
50 | if field.Name != preloadField || field.Relationship == nil {
51 | continue
52 | }
53 |
54 | switch field.Relationship.Kind {
55 | case "has_one":
56 | currentScope.handleHasOnePreload(field, currentPreloadConditions)
57 | case "has_many":
58 | currentScope.handleHasManyPreload(field, currentPreloadConditions)
59 | case "belongs_to":
60 | currentScope.handleBelongsToPreload(field, currentPreloadConditions)
61 | case "many_to_many":
62 | currentScope.handleManyToManyPreload(field, currentPreloadConditions)
63 | default:
64 | scope.Err(errors.New("unsupported relation"))
65 | }
66 |
67 | preloadedMap[preloadKey] = true
68 | break
69 | }
70 |
71 | if !preloadedMap[preloadKey] {
72 | scope.Err(fmt.Errorf("can't preload field %s for %s", preloadField, currentScope.GetModelStruct().ModelType))
73 | return
74 | }
75 | }
76 |
77 | // preload next level
78 | if idx < len(preloadFields)-1 {
79 | currentScope = currentScope.getColumnAsScope(preloadField)
80 | if currentScope != nil {
81 | currentFields = currentScope.Fields()
82 | }
83 | }
84 | }
85 | }
86 | }
87 |
88 | func autoPreload(scope *Scope) {
89 | for _, field := range scope.Fields() {
90 | if field.Relationship == nil {
91 | continue
92 | }
93 |
94 | if val, ok := field.TagSettings["PRELOAD"]; ok {
95 | if preload, err := strconv.ParseBool(val); err != nil {
96 | scope.Err(errors.New("invalid preload option"))
97 | return
98 | } else if !preload {
99 | continue
100 | }
101 | }
102 |
103 | scope.Search.Preload(field.Name)
104 | }
105 | }
106 |
107 | func (scope *Scope) generatePreloadDBWithConditions(conditions []interface{}) (*DB, []interface{}) {
108 | var (
109 | preloadDB = scope.NewDB()
110 | preloadConditions []interface{}
111 | )
112 |
113 | for _, condition := range conditions {
114 | if scopes, ok := condition.(func(*DB) *DB); ok {
115 | preloadDB = scopes(preloadDB)
116 | } else {
117 | preloadConditions = append(preloadConditions, condition)
118 | }
119 | }
120 |
121 | return preloadDB, preloadConditions
122 | }
123 |
124 | // handleHasOnePreload used to preload has one associations
125 | func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{}) {
126 | relation := field.Relationship
127 |
128 | // get relations's primary keys
129 | primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
130 | if len(primaryKeys) == 0 {
131 | return
132 | }
133 |
134 | // preload conditions
135 | preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
136 |
137 | // find relations
138 | query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys))
139 | values := toQueryValues(primaryKeys)
140 | if relation.PolymorphicType != "" {
141 | query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
142 | values = append(values, relation.PolymorphicValue)
143 | }
144 |
145 | results := makeSlice(field.Struct.Type)
146 | scope.Err(preloadDB.Where(query, values...).Find(results, preloadConditions...).Error)
147 |
148 | // assign find results
149 | var (
150 | resultsValue = indirect(reflect.ValueOf(results))
151 | indirectScopeValue = scope.IndirectValue()
152 | )
153 |
154 | if indirectScopeValue.Kind() == reflect.Slice {
155 | for j := 0; j < indirectScopeValue.Len(); j++ {
156 | for i := 0; i < resultsValue.Len(); i++ {
157 | result := resultsValue.Index(i)
158 | foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
159 | if indirectValue := indirect(indirectScopeValue.Index(j)); equalAsString(getValueFromFields(indirectValue, relation.AssociationForeignFieldNames), foreignValues) {
160 | indirectValue.FieldByName(field.Name).Set(result)
161 | break
162 | }
163 | }
164 | }
165 | } else {
166 | for i := 0; i < resultsValue.Len(); i++ {
167 | result := resultsValue.Index(i)
168 | scope.Err(field.Set(result))
169 | }
170 | }
171 | }
172 |
173 | // handleHasManyPreload used to preload has many associations
174 | func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{}) {
175 | relation := field.Relationship
176 |
177 | // get relations's primary keys
178 | primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
179 | if len(primaryKeys) == 0 {
180 | return
181 | }
182 |
183 | // preload conditions
184 | preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
185 |
186 | // find relations
187 | query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys))
188 | values := toQueryValues(primaryKeys)
189 | if relation.PolymorphicType != "" {
190 | query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
191 | values = append(values, relation.PolymorphicValue)
192 | }
193 |
194 | results := makeSlice(field.Struct.Type)
195 | scope.Err(preloadDB.Where(query, values...).Find(results, preloadConditions...).Error)
196 |
197 | // assign find results
198 | var (
199 | resultsValue = indirect(reflect.ValueOf(results))
200 | indirectScopeValue = scope.IndirectValue()
201 | )
202 |
203 | if indirectScopeValue.Kind() == reflect.Slice {
204 | preloadMap := make(map[string][]reflect.Value)
205 | for i := 0; i < resultsValue.Len(); i++ {
206 | result := resultsValue.Index(i)
207 | foreignValues := getValueFromFields(result, relation.ForeignFieldNames)
208 | preloadMap[toString(foreignValues)] = append(preloadMap[toString(foreignValues)], result)
209 | }
210 |
211 | for j := 0; j < indirectScopeValue.Len(); j++ {
212 | object := indirect(indirectScopeValue.Index(j))
213 | objectRealValue := getValueFromFields(object, relation.AssociationForeignFieldNames)
214 | f := object.FieldByName(field.Name)
215 | if results, ok := preloadMap[toString(objectRealValue)]; ok {
216 | f.Set(reflect.Append(f, results...))
217 | } else {
218 | f.Set(reflect.MakeSlice(f.Type(), 0, 0))
219 | }
220 | }
221 | } else {
222 | scope.Err(field.Set(resultsValue))
223 | }
224 | }
225 |
226 | // handleBelongsToPreload used to preload belongs to associations
227 | func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{}) {
228 | relation := field.Relationship
229 |
230 | // preload conditions
231 | preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
232 |
233 | // get relations's primary keys
234 | primaryKeys := scope.getColumnAsArray(relation.ForeignFieldNames, scope.Value)
235 | if len(primaryKeys) == 0 {
236 | return
237 | }
238 |
239 | // find relations
240 | results := makeSlice(field.Struct.Type)
241 | scope.Err(preloadDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, preloadConditions...).Error)
242 |
243 | // assign find results
244 | var (
245 | resultsValue = indirect(reflect.ValueOf(results))
246 | indirectScopeValue = scope.IndirectValue()
247 | )
248 |
249 | for i := 0; i < resultsValue.Len(); i++ {
250 | result := resultsValue.Index(i)
251 | if indirectScopeValue.Kind() == reflect.Slice {
252 | value := getValueFromFields(result, relation.AssociationForeignFieldNames)
253 | for j := 0; j < indirectScopeValue.Len(); j++ {
254 | object := indirect(indirectScopeValue.Index(j))
255 | if equalAsString(getValueFromFields(object, relation.ForeignFieldNames), value) {
256 | object.FieldByName(field.Name).Set(result)
257 | }
258 | }
259 | } else {
260 | scope.Err(field.Set(result))
261 | }
262 | }
263 | }
264 |
265 | // handleManyToManyPreload used to preload many to many associations
266 | func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface{}) {
267 | var (
268 | relation = field.Relationship
269 | joinTableHandler = relation.JoinTableHandler
270 | fieldType = field.Struct.Type.Elem()
271 | foreignKeyValue interface{}
272 | foreignKeyType = reflect.ValueOf(&foreignKeyValue).Type()
273 | linkHash = map[string][]reflect.Value{}
274 | isPtr bool
275 | )
276 |
277 | if fieldType.Kind() == reflect.Ptr {
278 | isPtr = true
279 | fieldType = fieldType.Elem()
280 | }
281 |
282 | var sourceKeys = []string{}
283 | for _, key := range joinTableHandler.SourceForeignKeys() {
284 | sourceKeys = append(sourceKeys, key.DBName)
285 | }
286 |
287 | // preload conditions
288 | preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
289 |
290 | // generate query with join table
291 | newScope := scope.New(reflect.New(fieldType).Interface())
292 | preloadDB = preloadDB.Table(newScope.TableName()).Model(newScope.Value).Select("*")
293 | preloadDB = joinTableHandler.JoinWith(joinTableHandler, preloadDB, scope.Value)
294 |
295 | // preload inline conditions
296 | if len(preloadConditions) > 0 {
297 | preloadDB = preloadDB.Where(preloadConditions[0], preloadConditions[1:]...)
298 | }
299 |
300 | rows, err := preloadDB.Rows()
301 |
302 | if scope.Err(err) != nil {
303 | return
304 | }
305 | defer rows.Close()
306 |
307 | columns, _ := rows.Columns()
308 | for rows.Next() {
309 | var (
310 | elem = reflect.New(fieldType).Elem()
311 | fields = scope.New(elem.Addr().Interface()).Fields()
312 | )
313 |
314 | // register foreign keys in join tables
315 | var joinTableFields []*Field
316 | for _, sourceKey := range sourceKeys {
317 | joinTableFields = append(joinTableFields, &Field{StructField: &StructField{DBName: sourceKey, IsNormal: true}, Field: reflect.New(foreignKeyType).Elem()})
318 | }
319 |
320 | scope.scan(rows, columns, append(fields, joinTableFields...))
321 |
322 | var foreignKeys = make([]interface{}, len(sourceKeys))
323 | // generate hashed forkey keys in join table
324 | for idx, joinTableField := range joinTableFields {
325 | if !joinTableField.Field.IsNil() {
326 | foreignKeys[idx] = joinTableField.Field.Elem().Interface()
327 | }
328 | }
329 | hashedSourceKeys := toString(foreignKeys)
330 |
331 | if isPtr {
332 | linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem.Addr())
333 | } else {
334 | linkHash[hashedSourceKeys] = append(linkHash[hashedSourceKeys], elem)
335 | }
336 | }
337 |
338 | if err := rows.Err(); err != nil {
339 | scope.Err(err)
340 | }
341 |
342 | // assign find results
343 | var (
344 | indirectScopeValue = scope.IndirectValue()
345 | fieldsSourceMap = map[string][]reflect.Value{}
346 | foreignFieldNames = []string{}
347 | )
348 |
349 | for _, dbName := range relation.ForeignFieldNames {
350 | if field, ok := scope.FieldByName(dbName); ok {
351 | foreignFieldNames = append(foreignFieldNames, field.Name)
352 | }
353 | }
354 |
355 | if indirectScopeValue.Kind() == reflect.Slice {
356 | for j := 0; j < indirectScopeValue.Len(); j++ {
357 | object := indirect(indirectScopeValue.Index(j))
358 | key := toString(getValueFromFields(object, foreignFieldNames))
359 | fieldsSourceMap[key] = append(fieldsSourceMap[key], object.FieldByName(field.Name))
360 | }
361 | } else if indirectScopeValue.IsValid() {
362 | key := toString(getValueFromFields(indirectScopeValue, foreignFieldNames))
363 | fieldsSourceMap[key] = append(fieldsSourceMap[key], indirectScopeValue.FieldByName(field.Name))
364 | }
365 | for source, link := range linkHash {
366 | for i, field := range fieldsSourceMap[source] {
367 | //If not 0 this means Value is a pointer and we already added preloaded models to it
368 | if fieldsSourceMap[source][i].Len() != 0 {
369 | continue
370 | }
371 | field.Set(reflect.Append(fieldsSourceMap[source][i], link...))
372 | }
373 |
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/vendor/github.com/jinzhu/gorm/association.go:
--------------------------------------------------------------------------------
1 | package gorm
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "reflect"
7 | )
8 |
9 | // Association Mode contains some helper methods to handle relationship things easily.
10 | type Association struct {
11 | Error error
12 | scope *Scope
13 | column string
14 | field *Field
15 | }
16 |
17 | // Find find out all related associations
18 | func (association *Association) Find(value interface{}) *Association {
19 | association.scope.related(value, association.column)
20 | return association.setErr(association.scope.db.Error)
21 | }
22 |
23 | // Append append new associations for many2many, has_many, replace current association for has_one, belongs_to
24 | func (association *Association) Append(values ...interface{}) *Association {
25 | if association.Error != nil {
26 | return association
27 | }
28 |
29 | if relationship := association.field.Relationship; relationship.Kind == "has_one" {
30 | return association.Replace(values...)
31 | }
32 | return association.saveAssociations(values...)
33 | }
34 |
35 | // Replace replace current associations with new one
36 | func (association *Association) Replace(values ...interface{}) *Association {
37 | if association.Error != nil {
38 | return association
39 | }
40 |
41 | var (
42 | relationship = association.field.Relationship
43 | scope = association.scope
44 | field = association.field.Field
45 | newDB = scope.NewDB()
46 | )
47 |
48 | // Append new values
49 | association.field.Set(reflect.Zero(association.field.Field.Type()))
50 | association.saveAssociations(values...)
51 |
52 | // Belongs To
53 | if relationship.Kind == "belongs_to" {
54 | // Set foreign key to be null when clearing value (length equals 0)
55 | if len(values) == 0 {
56 | // Set foreign key to be nil
57 | var foreignKeyMap = map[string]interface{}{}
58 | for _, foreignKey := range relationship.ForeignDBNames {
59 | foreignKeyMap[foreignKey] = nil
60 | }
61 | association.setErr(newDB.Model(scope.Value).UpdateColumn(foreignKeyMap).Error)
62 | }
63 | } else {
64 | // Polymorphic Relations
65 | if relationship.PolymorphicDBName != "" {
66 | newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(relationship.PolymorphicDBName)), relationship.PolymorphicValue)
67 | }
68 |
69 | // Delete Relations except new created
70 | if len(values) > 0 {
71 | var associationForeignFieldNames, associationForeignDBNames []string
72 | if relationship.Kind == "many_to_many" {
73 | // if many to many relations, get association fields name from association foreign keys
74 | associationScope := scope.New(reflect.New(field.Type()).Interface())
75 | for idx, dbName := range relationship.AssociationForeignFieldNames {
76 | if field, ok := associationScope.FieldByName(dbName); ok {
77 | associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
78 | associationForeignDBNames = append(associationForeignDBNames, relationship.AssociationForeignDBNames[idx])
79 | }
80 | }
81 | } else {
82 | // If has one/many relations, use primary keys
83 | for _, field := range scope.New(reflect.New(field.Type()).Interface()).PrimaryFields() {
84 | associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
85 | associationForeignDBNames = append(associationForeignDBNames, field.DBName)
86 | }
87 | }
88 |
89 | newPrimaryKeys := scope.getColumnAsArray(associationForeignFieldNames, field.Interface())
90 |
91 | if len(newPrimaryKeys) > 0 {
92 | sql := fmt.Sprintf("%v NOT IN (%v)", toQueryCondition(scope, associationForeignDBNames), toQueryMarks(newPrimaryKeys))
93 | newDB = newDB.Where(sql, toQueryValues(newPrimaryKeys)...)
94 | }
95 | }
96 |
97 | if relationship.Kind == "many_to_many" {
98 | // if many to many relations, delete related relations from join table
99 | var sourceForeignFieldNames []string
100 |
101 | for _, dbName := range relationship.ForeignFieldNames {
102 | if field, ok := scope.FieldByName(dbName); ok {
103 | sourceForeignFieldNames = append(sourceForeignFieldNames, field.Name)
104 | }
105 | }
106 |
107 | if sourcePrimaryKeys := scope.getColumnAsArray(sourceForeignFieldNames, scope.Value); len(sourcePrimaryKeys) > 0 {
108 | newDB = newDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(sourcePrimaryKeys)), toQueryValues(sourcePrimaryKeys)...)
109 |
110 | association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship))
111 | }
112 | } else if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
113 | // has_one or has_many relations, set foreign key to be nil (TODO or delete them?)
114 | var foreignKeyMap = map[string]interface{}{}
115 | for idx, foreignKey := range relationship.ForeignDBNames {
116 | foreignKeyMap[foreignKey] = nil
117 | if field, ok := scope.FieldByName(relationship.AssociationForeignFieldNames[idx]); ok {
118 | newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface())
119 | }
120 | }
121 |
122 | fieldValue := reflect.New(association.field.Field.Type()).Interface()
123 | association.setErr(newDB.Model(fieldValue).UpdateColumn(foreignKeyMap).Error)
124 | }
125 | }
126 | return association
127 | }
128 |
129 | // Delete remove relationship between source & passed arguments, but won't delete those arguments
130 | func (association *Association) Delete(values ...interface{}) *Association {
131 | if association.Error != nil {
132 | return association
133 | }
134 |
135 | var (
136 | relationship = association.field.Relationship
137 | scope = association.scope
138 | field = association.field.Field
139 | newDB = scope.NewDB()
140 | )
141 |
142 | if len(values) == 0 {
143 | return association
144 | }
145 |
146 | var deletingResourcePrimaryFieldNames, deletingResourcePrimaryDBNames []string
147 | for _, field := range scope.New(reflect.New(field.Type()).Interface()).PrimaryFields() {
148 | deletingResourcePrimaryFieldNames = append(deletingResourcePrimaryFieldNames, field.Name)
149 | deletingResourcePrimaryDBNames = append(deletingResourcePrimaryDBNames, field.DBName)
150 | }
151 |
152 | deletingPrimaryKeys := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, values...)
153 |
154 | if relationship.Kind == "many_to_many" {
155 | // source value's foreign keys
156 | for idx, foreignKey := range relationship.ForeignDBNames {
157 | if field, ok := scope.FieldByName(relationship.ForeignFieldNames[idx]); ok {
158 | newDB = newDB.Where(fmt.Sprintf("%v = ?", scope.Quote(foreignKey)), field.Field.Interface())
159 | }
160 | }
161 |
162 | // get association's foreign fields name
163 | var associationScope = scope.New(reflect.New(field.Type()).Interface())
164 | var associationForeignFieldNames []string
165 | for _, associationDBName := range relationship.AssociationForeignFieldNames {
166 | if field, ok := associationScope.FieldByName(associationDBName); ok {
167 | associationForeignFieldNames = append(associationForeignFieldNames, field.Name)
168 | }
169 | }
170 |
171 | // association value's foreign keys
172 | deletingPrimaryKeys := scope.getColumnAsArray(associationForeignFieldNames, values...)
173 | sql := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(deletingPrimaryKeys))
174 | newDB = newDB.Where(sql, toQueryValues(deletingPrimaryKeys)...)
175 |
176 | association.setErr(relationship.JoinTableHandler.Delete(relationship.JoinTableHandler, newDB, relationship))
177 | } else {
178 | var foreignKeyMap = map[string]interface{}{}
179 | for _, foreignKey := range relationship.ForeignDBNames {
180 | foreignKeyMap[foreignKey] = nil
181 | }
182 |
183 | if relationship.Kind == "belongs_to" {
184 | // find with deleting relation's foreign keys
185 | primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, values...)
186 | newDB = newDB.Where(
187 | fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
188 | toQueryValues(primaryKeys)...,
189 | )
190 |
191 | // set foreign key to be null if there are some records affected
192 | modelValue := reflect.New(scope.GetModelStruct().ModelType).Interface()
193 | if results := newDB.Model(modelValue).UpdateColumn(foreignKeyMap); results.Error == nil {
194 | if results.RowsAffected > 0 {
195 | scope.updatedAttrsWithValues(foreignKeyMap)
196 | }
197 | } else {
198 | association.setErr(results.Error)
199 | }
200 | } else if relationship.Kind == "has_one" || relationship.Kind == "has_many" {
201 | // find all relations
202 | primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
203 | newDB = newDB.Where(
204 | fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
205 | toQueryValues(primaryKeys)...,
206 | )
207 |
208 | // only include those deleting relations
209 | newDB = newDB.Where(
210 | fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, deletingResourcePrimaryDBNames), toQueryMarks(deletingPrimaryKeys)),
211 | toQueryValues(deletingPrimaryKeys)...,
212 | )
213 |
214 | // set matched relation's foreign key to be null
215 | fieldValue := reflect.New(association.field.Field.Type()).Interface()
216 | association.setErr(newDB.Model(fieldValue).UpdateColumn(foreignKeyMap).Error)
217 | }
218 | }
219 |
220 | // Remove deleted records from source's field
221 | if association.Error == nil {
222 | if field.Kind() == reflect.Slice {
223 | leftValues := reflect.Zero(field.Type())
224 |
225 | for i := 0; i < field.Len(); i++ {
226 | reflectValue := field.Index(i)
227 | primaryKey := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, reflectValue.Interface())[0]
228 | var isDeleted = false
229 | for _, pk := range deletingPrimaryKeys {
230 | if equalAsString(primaryKey, pk) {
231 | isDeleted = true
232 | break
233 | }
234 | }
235 | if !isDeleted {
236 | leftValues = reflect.Append(leftValues, reflectValue)
237 | }
238 | }
239 |
240 | association.field.Set(leftValues)
241 | } else if field.Kind() == reflect.Struct {
242 | primaryKey := scope.getColumnAsArray(deletingResourcePrimaryFieldNames, field.Interface())[0]
243 | for _, pk := range deletingPrimaryKeys {
244 | if equalAsString(primaryKey, pk) {
245 | association.field.Set(reflect.Zero(field.Type()))
246 | break
247 | }
248 | }
249 | }
250 | }
251 |
252 | return association
253 | }
254 |
255 | // Clear remove relationship between source & current associations, won't delete those associations
256 | func (association *Association) Clear() *Association {
257 | return association.Replace()
258 | }
259 |
260 | // Count return the count of current associations
261 | func (association *Association) Count() int {
262 | var (
263 | count = 0
264 | relationship = association.field.Relationship
265 | scope = association.scope
266 | fieldValue = association.field.Field.Interface()
267 | query = scope.DB()
268 | )
269 |
270 | if relationship.Kind == "many_to_many" {
271 | query = relationship.JoinTableHandler.JoinWith(relationship.JoinTableHandler, query, scope.Value)
272 | } else if relationship.Kind == "has_many" || relationship.Kind == "has_one" {
273 | primaryKeys := scope.getColumnAsArray(relationship.AssociationForeignFieldNames, scope.Value)
274 | query = query.Where(
275 | fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.ForeignDBNames), toQueryMarks(primaryKeys)),
276 | toQueryValues(primaryKeys)...,
277 | )
278 | } else if relationship.Kind == "belongs_to" {
279 | primaryKeys := scope.getColumnAsArray(relationship.ForeignFieldNames, scope.Value)
280 | query = query.Where(
281 | fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relationship.AssociationForeignDBNames), toQueryMarks(primaryKeys)),
282 | toQueryValues(primaryKeys)...,
283 | )
284 | }
285 |
286 | if relationship.PolymorphicType != "" {
287 | query = query.Where(
288 | fmt.Sprintf("%v.%v = ?", scope.New(fieldValue).QuotedTableName(), scope.Quote(relationship.PolymorphicDBName)),
289 | relationship.PolymorphicValue,
290 | )
291 | }
292 |
293 | if err := query.Model(fieldValue).Count(&count).Error; err != nil {
294 | association.Error = err
295 | }
296 | return count
297 | }
298 |
299 | // saveAssociations save passed values as associations
300 | func (association *Association) saveAssociations(values ...interface{}) *Association {
301 | var (
302 | scope = association.scope
303 | field = association.field
304 | relationship = field.Relationship
305 | )
306 |
307 | saveAssociation := func(reflectValue reflect.Value) {
308 | // value has to been pointer
309 | if reflectValue.Kind() != reflect.Ptr {
310 | reflectPtr := reflect.New(reflectValue.Type())
311 | reflectPtr.Elem().Set(reflectValue)
312 | reflectValue = reflectPtr
313 | }
314 |
315 | // value has to been saved for many2many
316 | if relationship.Kind == "many_to_many" {
317 | if scope.New(reflectValue.Interface()).PrimaryKeyZero() {
318 | association.setErr(scope.NewDB().Save(reflectValue.Interface()).Error)
319 | }
320 | }
321 |
322 | // Assign Fields
323 | var fieldType = field.Field.Type()
324 | var setFieldBackToValue, setSliceFieldBackToValue bool
325 | if reflectValue.Type().AssignableTo(fieldType) {
326 | field.Set(reflectValue)
327 | } else if reflectValue.Type().Elem().AssignableTo(fieldType) {
328 | // if field's type is struct, then need to set value back to argument after save
329 | setFieldBackToValue = true
330 | field.Set(reflectValue.Elem())
331 | } else if fieldType.Kind() == reflect.Slice {
332 | if reflectValue.Type().AssignableTo(fieldType.Elem()) {
333 | field.Set(reflect.Append(field.Field, reflectValue))
334 | } else if reflectValue.Type().Elem().AssignableTo(fieldType.Elem()) {
335 | // if field's type is slice of struct, then need to set value back to argument after save
336 | setSliceFieldBackToValue = true
337 | field.Set(reflect.Append(field.Field, reflectValue.Elem()))
338 | }
339 | }
340 |
341 | if relationship.Kind == "many_to_many" {
342 | association.setErr(relationship.JoinTableHandler.Add(relationship.JoinTableHandler, scope.NewDB(), scope.Value, reflectValue.Interface()))
343 | } else {
344 | association.setErr(scope.NewDB().Select(field.Name).Save(scope.Value).Error)
345 |
346 | if setFieldBackToValue {
347 | reflectValue.Elem().Set(field.Field)
348 | } else if setSliceFieldBackToValue {
349 | reflectValue.Elem().Set(field.Field.Index(field.Field.Len() - 1))
350 | }
351 | }
352 | }
353 |
354 | for _, value := range values {
355 | reflectValue := reflect.ValueOf(value)
356 | indirectReflectValue := reflect.Indirect(reflectValue)
357 | if indirectReflectValue.Kind() == reflect.Struct {
358 | saveAssociation(reflectValue)
359 | } else if indirectReflectValue.Kind() == reflect.Slice {
360 | for i := 0; i < indirectReflectValue.Len(); i++ {
361 | saveAssociation(indirectReflectValue.Index(i))
362 | }
363 | } else {
364 | association.setErr(errors.New("invalid value type"))
365 | }
366 | }
367 | return association
368 | }
369 |
370 | func (association *Association) setErr(err error) *Association {
371 | if err != nil {
372 | association.Error = err
373 | }
374 | return association
375 | }
376 |
--------------------------------------------------------------------------------
/app/master/admin/static/vendor/bootstrap/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */
--------------------------------------------------------------------------------