├── i18n └── i18n.go ├── README.md ├── middlewares ├── guest.go └── auth.go ├── controllers ├── user │ ├── index.go │ ├── node │ │ └── index.go │ └── shop │ │ └── index.go └── auth │ └── login.go ├── utils ├── md5.go ├── crypto.go ├── mysql.go ├── jwt.go └── config.go ├── main.go ├── .gitignore ├── models ├── ss_node_online_log.go ├── shop.go ├── ss_node.go └── user.go ├── LICENSE ├── router └── router.go └── vendor └── vendor.json /i18n/i18n.go: -------------------------------------------------------------------------------- 1 | package i18n 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sspanel-go 2 | Reconstructed SSPanel using Golang & Gin & Gorm 3 | -------------------------------------------------------------------------------- /middlewares/guest.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | func GuestMiddleWare() gin.HandlerFunc { 8 | return func(c *gin.Context) { 9 | c.Next() 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /controllers/user/index.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // GetInfo handles user info request 10 | func GetInfo(c *gin.Context) { 11 | user, _ := c.Get("user") 12 | 13 | c.JSON(http.StatusOK, user) 14 | } 15 | -------------------------------------------------------------------------------- /utils/md5.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | // EncodeMD5 implements md5 encryption 9 | func EncodeMD5(value string) string { 10 | m := md5.New() 11 | m.Write([]byte(value)) 12 | 13 | return hex.EncodeToString(m.Sum(nil)) 14 | } 15 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "github.com/go-sql-driver/mysql" 5 | "github.com/tonyzzzzzz/sspanel-go/router" 6 | "github.com/tonyzzzzzz/sspanel-go/utils" 7 | ) 8 | 9 | func main() { 10 | 11 | r := router.GetRouter() 12 | 13 | r.Run(utils.GetConfig().GetString("server.address")) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | vendor/* 16 | !vendor/vendor.json 17 | 18 | .vscode/ 19 | 20 | config.yaml 21 | -------------------------------------------------------------------------------- /models/ss_node_online_log.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // SsNodeOnlineLog ORM Model 4 | // Written By Indexyz @ Cloudhammer/Seeds 5 | type SsNodeOnlineLog struct { 6 | ID int `gorm:"column:id;NOT NULL;PRIMARY KEY;"` 7 | NodeID int `gorm:"column:node_id;NOT NULL;"` 8 | OnlineUser int `gorm:"column:online_user;NOT NULL;"` 9 | LogTime int `gorm:"column:log_time;NOT NULL;"` 10 | } 11 | 12 | func (SsNodeOnlineLog) TableName() string { 13 | return "ss_node_online_log" 14 | } 15 | -------------------------------------------------------------------------------- /utils/crypto.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto" 5 | "encoding/hex" 6 | "hash" 7 | ) 8 | 9 | func getHasher() hash.Hash { 10 | encryptMethod := GetConfig().Get("security.encryption") 11 | switch encryptMethod { 12 | case "md5": 13 | return crypto.MD5.New() 14 | case "sha256": 15 | return crypto.SHA256.New() 16 | } 17 | return nil 18 | } 19 | 20 | // EncryptString encrypts strings such as password 21 | func EncryptString(toEncrypt string) string { 22 | hasher := getHasher() 23 | hasher.Write([]byte(toEncrypt)) 24 | hash := hex.EncodeToString(hasher.Sum(nil)) 25 | return hash 26 | } 27 | -------------------------------------------------------------------------------- /controllers/user/node/index.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/tonyzzzzzz/sspanel-go/models" 7 | "github.com/tonyzzzzzz/sspanel-go/utils" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | // GetInfo returns the info of all nodes 13 | func GetInfo(c *gin.Context) { 14 | db := utils.GetMySQLInstance().Database 15 | var rawNodes []models.SsNode 16 | user, _ := c.Get("user") 17 | db.Where("type = 1").Where("node_class <= ?", user.(*models.User).Class).Where("sort != 9").Order("name").Find(&rawNodes) 18 | 19 | var nodes []models.Node 20 | for _, rawNode := range rawNodes { 21 | nodes = append(nodes, rawNode.GetNode()) 22 | } 23 | c.JSON(http.StatusOK, nodes) 24 | } 25 | -------------------------------------------------------------------------------- /controllers/user/shop/index.go: -------------------------------------------------------------------------------- 1 | package shop 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/tonyzzzzzz/sspanel-go/models" 8 | "github.com/tonyzzzzzz/sspanel-go/utils" 9 | ) 10 | 11 | // GetInfo fetches the shop list and returns to client 12 | func GetInfo(c *gin.Context) { 13 | var shops []models.Shop 14 | db := utils.GetMySQLInstance().Database 15 | db.Where("status = 1").Find(&shops) 16 | 17 | var respShops []map[string]interface{} 18 | for _, shop := range shops { 19 | respShops = append(respShops, map[string]interface{}{ 20 | "Id": shop.Id, 21 | "Name": shop.Name, 22 | "Price": shop.Price, 23 | "Content": shop.GetContent(), 24 | }) 25 | } 26 | c.JSON(http.StatusOK, respShops) 27 | } 28 | -------------------------------------------------------------------------------- /utils/mysql.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | 7 | "github.com/jinzhu/gorm" 8 | ) 9 | 10 | // SingletonMySQL constructs the Database Connector 11 | type SingletonMySQL struct { 12 | Database *gorm.DB 13 | } 14 | 15 | var instance *SingletonMySQL 16 | var lock sync.Once 17 | 18 | // GetMySQLInstance returns a MySQL Instance 19 | // Written By Indexyz @ Cloudhammer/Seeds 20 | func GetMySQLInstance() *SingletonMySQL { 21 | lock.Do(func() { 22 | // Init MySQL Instance 23 | db, err := gorm.Open("mysql", GetDataURL()) 24 | if err != nil { 25 | log.Fatal(err) 26 | panic(err) 27 | } 28 | 29 | db.LogMode(GetConfig().GetBool("server.debug")) 30 | //db.AutoMigrate(&models.SsNode{}) 31 | //db.AutoMigrate(&models.User{}) 32 | 33 | instance = &SingletonMySQL{ 34 | Database: db, 35 | } 36 | }) 37 | return instance 38 | } 39 | -------------------------------------------------------------------------------- /middlewares/auth.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/tonyzzzzzz/sspanel-go/models" 9 | "github.com/tonyzzzzzz/sspanel-go/utils" 10 | ) 11 | 12 | // AuthMiddleWare identifies the user and proceed to authorized zones 13 | func AuthMiddleWare() gin.HandlerFunc { 14 | return func(c *gin.Context) { 15 | token := c.Request.Header.Get("Authorization") 16 | token = strings.Replace(token, "Bearer ", "", 1) 17 | claims, err := utils.ParseToken(token) 18 | 19 | if token == "" || err != nil || claims == nil { 20 | c.Request.Header.Set("Authorization", "") 21 | c.JSON(http.StatusForbidden, gin.H{"msg": "Token Invalid"}) 22 | c.Abort() 23 | return 24 | } 25 | 26 | var user models.User 27 | db := utils.GetMySQLInstance().Database 28 | db.First(&user, claims.UserID) 29 | 30 | c.Set("user", &user) 31 | c.Next() 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /models/shop.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // Shop ORM Model 8 | type Shop struct { 9 | Id int `gorm:"column:id;NOT NULL;PRIMARY KEY;"` 10 | Name string `gorm:"column:name;NOT NULL"` 11 | Price float32 `gorm:"column:price;NOT NULL"` 12 | Content string `gorm:"column:content;NOT NULL"` 13 | } 14 | 15 | // ShopContent Model 16 | type ShopContent struct { 17 | Bandwidth string 18 | Expire string 19 | Class string 20 | Class_expire string 21 | Reset string 22 | Reset_exp string 23 | Speedlimit string 24 | Connector string 25 | } 26 | 27 | // GetContent converts the content field from string to an obj 28 | func (shop *Shop) GetContent() ShopContent { 29 | var content ShopContent 30 | json.Unmarshal([]byte(shop.Content), &content) 31 | return content 32 | } 33 | 34 | // TableName returns the table name 35 | func (Shop) TableName() string { 36 | return "shop" 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tony Zou 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 | -------------------------------------------------------------------------------- /utils/jwt.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/dgrijalva/jwt-go" 7 | ) 8 | 9 | var jwtSecret []byte 10 | 11 | // Claims stores basic encrypted user information 12 | type Claims struct { 13 | Username string `json:"username"` 14 | Password string `json:"password"` 15 | UserID int `json:"uid"` 16 | jwt.StandardClaims 17 | } 18 | 19 | // GenerateToken generate tokens used for auth 20 | func GenerateToken(username, password string, uid int) (string, error) { 21 | nowTime := time.Now() 22 | expireTime := nowTime.Add(3 * time.Hour) 23 | 24 | claims := Claims{ 25 | EncodeMD5(username), 26 | EncodeMD5(password), 27 | uid, 28 | jwt.StandardClaims{ 29 | ExpiresAt: expireTime.Unix(), 30 | Issuer: "sspanel-go", 31 | }, 32 | } 33 | 34 | tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 35 | token, err := tokenClaims.SignedString(jwtSecret) 36 | 37 | return token, err 38 | } 39 | 40 | // ParseToken parsing token 41 | func ParseToken(token string) (*Claims, error) { 42 | tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) { 43 | return jwtSecret, nil 44 | }) 45 | 46 | if tokenClaims != nil { 47 | if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { 48 | return claims, nil 49 | } 50 | } 51 | 52 | return nil, err 53 | } 54 | -------------------------------------------------------------------------------- /controllers/auth/login.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/tonyzzzzzz/sspanel-go/models" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/tonyzzzzzz/sspanel-go/utils" 11 | ) 12 | 13 | // Credentials as JSON binding 14 | type Credentials struct { 15 | UserName string `form:"username" json:"username" xml:"username" binding:"required"` 16 | Password string `form:"password" json:"password" xml:"password" binding:"required"` 17 | RememberMe bool `form:"remember" json:"remember" xml:"remember"` 18 | } 19 | 20 | // Login handles user login request 21 | func Login(c *gin.Context) { 22 | var credentials Credentials 23 | if err := c.ShouldBind(&credentials); err != nil { 24 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 25 | return 26 | } 27 | 28 | db := utils.GetMySQLInstance() 29 | var user models.User 30 | db.Database.Where("user_name = ? OR email = ?", credentials.UserName, credentials.UserName).First(&user) 31 | fmt.Println(user) 32 | if hashed := utils.EncryptString(credentials.Password); hashed != user.Pass { 33 | c.JSON(http.StatusForbidden, gin.H{"error": "Wrong Username or Password"}) 34 | return 35 | } 36 | 37 | token, err := utils.GenerateToken(credentials.UserName, credentials.Password, user.Id) 38 | if err != nil { 39 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) 40 | return 41 | } 42 | 43 | c.JSON(http.StatusOK, gin.H{"token": token}) 44 | } 45 | -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | // GetConfig returns the global config 12 | func GetConfig() *viper.Viper { 13 | c := viper.New() 14 | c.SetConfigType("yaml") 15 | c.SetConfigName("config") 16 | c.AddConfigPath(".") 17 | c.AutomaticEnv() 18 | 19 | c.SetDefault("security.encryption", "sha256") // Now supports sha256 & md5, will support more 20 | 21 | c.SetDefault("database.host", "localhost") 22 | c.SetDefault("database.port", 3306) 23 | c.SetDefault("database.name", "sspanel") 24 | c.SetDefault("database.user", "sspanel") 25 | c.SetDefault("database.pass", "sspanel") 26 | 27 | c.SetDefault("redis.host", "localhost") 28 | c.SetDefault("redis.port", 4468) 29 | c.SetDefault("redis.enableAuth", true) 30 | c.SetDefault("redis.password", "password") 31 | 32 | c.SetDefault("server.address", ":8080") 33 | c.SetDefault("server.debug", false) 34 | 35 | c.SetDefault("cacheTTL", 60) 36 | c.SetDefault("verifyKey", "Hello") 37 | c.SetDefault("modURL", true) 38 | 39 | replacer := strings.NewReplacer(".", "_") 40 | c.SetEnvKeyReplacer(replacer) 41 | c.ReadInConfig() 42 | return c 43 | } 44 | 45 | // GetDataURL returns the database url for gorm connection 46 | func GetDataURL() string { 47 | config := GetConfig() 48 | return fmt.Sprintf("%s:%s@(%s:%s)/%s?parseTime=true", 49 | config.Get("database.user"), config.Get("database.pass"), config.Get("database.host"), 50 | strconv.Itoa(config.Get("database.port").(int)), config.Get("database.name")) 51 | } 52 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/tonyzzzzzz/sspanel-go/utils" 7 | 8 | "github.com/dvwright/xss-mw" 9 | "github.com/gin-gonic/gin" 10 | "github.com/tonyzzzzzz/sspanel-go/controllers/auth" 11 | "github.com/tonyzzzzzz/sspanel-go/controllers/user" 12 | "github.com/tonyzzzzzz/sspanel-go/controllers/user/node" 13 | "github.com/tonyzzzzzz/sspanel-go/controllers/user/shop" 14 | "github.com/tonyzzzzzz/sspanel-go/middlewares" 15 | ) 16 | 17 | // GetRouter generates the router of the app 18 | func GetRouter() *gin.Engine { 19 | if !utils.GetConfig().GetBool("server.debug") { 20 | gin.SetMode(gin.ReleaseMode) 21 | } 22 | r := gin.New() 23 | r.Use(gin.Logger()) 24 | r.Use(gin.Recovery()) 25 | var xssMdlwr xss.XssMw 26 | r.Use(xssMdlwr.RemoveXss()) 27 | 28 | r.GET("/", func(c *gin.Context) { 29 | c.JSON(http.StatusOK, gin.H{"msg": "Blinkload API V2"}) 30 | }) 31 | 32 | v2 := r.Group("/") 33 | 34 | Auth := v2.Group("/auth") 35 | Auth.Use(middlewares.GuestMiddleWare()) 36 | { 37 | Auth.POST("/login", auth.Login) 38 | Auth.POST("/register") 39 | Auth.GET("/email_verification") 40 | Auth.POST("/forget") 41 | 42 | } 43 | 44 | User := v2.Group("/user") 45 | User.Use(middlewares.AuthMiddleWare()) 46 | { 47 | // General Info Query 48 | User.GET("/info", user.GetInfo) 49 | User.GET("/shop/info", shop.GetInfo) 50 | 51 | User.GET("/node/info", node.GetInfo) 52 | 53 | User.POST("/shop/buy") 54 | 55 | // Billing Interface 56 | User.GET("/invoice/info") 57 | 58 | } 59 | 60 | Admin := v2.Group("/admin") 61 | Admin.Use() 62 | { 63 | Admin.GET("/info") 64 | Admin.GET("/incidents") 65 | 66 | } 67 | return r 68 | } 69 | -------------------------------------------------------------------------------- /models/ss_node.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "github.com/tonyzzzzzz/sspanel-go/utils" 5 | ) 6 | 7 | // SsNode ORM Model 8 | // Written By Indexyz @ Cloudhammer/Seeds 9 | type SsNode struct { 10 | ID int `gorm:"column:id;NOT NULL;PRIMARY KEY;"` 11 | Name string `gorm:"column:name;NOT NULL;"` 12 | Type int `gorm:"column:type;NOT NULL;"` 13 | Server string `gorm:"column:server;NOT NULL;"` 14 | Method string `gorm:"column:method;NOT NULL;"` 15 | Info string `gorm:"column:info;NOT NULL;"` 16 | Status string `gorm:"column:status;NOT NULL;"` 17 | Sort int `gorm:"column:sort;NOT NULL;"` 18 | CustomMethod int `gorm:"column:custom_method;NOT NULL;"` 19 | TrafficRate float64 `gorm:"column:traffic_rate;NOT NULL;"` 20 | NodeClass int `gorm:"column:node_class;NOT NULL;"` 21 | NodeSpeedlimit float64 `gorm:"column:node_speedlimit;NOT NULL;"` 22 | NodeConnector int `gorm:"column:node_connector;NOT NULL;"` 23 | NodeBandwidth int64 `gorm:"column:node_bandwidth;NOT NULL;"` 24 | NodeBandwidthLimit int64 `gorm:"column:node_bandwidth_limit;NOT NULL;"` 25 | BandwidthlimitResetday int `gorm:"column:bandwidthlimit_resetday;NOT NULL;"` 26 | NodeHeartbeat int64 `gorm:"column:node_heartbeat;NOT NULL;"` 27 | NodeIP string `gorm:"column:node_ip;type:text;"` 28 | NodeGroup int `gorm:"column:node_group;NOT NULL;"` 29 | CustomRss int `gorm:"column:custom_rss;NOT NULL;"` 30 | MuOnly int `gorm:"column:mu_only;"` 31 | } 32 | 33 | // Node Model for RESTful API 34 | type Node struct { 35 | SsNode 36 | Online int 37 | } 38 | 39 | // GetNode returns the node with online user count 40 | func (node SsNode) GetNode() Node { 41 | var onlineLog SsNodeOnlineLog 42 | db := utils.GetMySQLInstance().Database 43 | db.Where("node_id = ?", node.ID).Last(&onlineLog) 44 | return Node{ 45 | node, 46 | onlineLog.OnlineUser, 47 | } 48 | } 49 | 50 | func (SsNode) TableName() string { 51 | return "ss_node" 52 | } 53 | -------------------------------------------------------------------------------- /models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // User ORM Model 8 | // Written By Indexyz @ Cloudhammer/Seeds 9 | type User struct { 10 | Id int `gorm:"column:id;NOT NULL;PRIMARY KEY;" json:"-"` 11 | UserName string `gorm:"column:user_name;NOT NULL;"` 12 | Email string `gorm:"column:email;NOT NULL;"` 13 | Pass string `gorm:"column:pass; NOT NULL;" json:"-"` 14 | Passwd string `gorm:"column:passwd;NOT NULL;"` 15 | T int `gorm:"column:t;NOT NULL;"` 16 | U int64 `gorm:"column:u;NOT NULL;"` 17 | D int64 `gorm:"column:d;NOT NULL;"` 18 | Plan string `gorm:"column:plan;NOT NULL;"` 19 | TransferEnable int64 `gorm:"column:transfer_enable;NOT NULL;"` 20 | Port int `gorm:"column:port;NOT NULL;"` 21 | Switch int `gorm:"column:switch;NOT NULL;"` 22 | Enable int `gorm:"column:enable;NOT NULL;"` 23 | Type int `gorm:"column:type;NOT NULL;"` 24 | LastGetGiftTime int `gorm:"column:last_get_gift_time;NOT NULL;"` 25 | LastCheckInTime int `gorm:"column:last_check_in_time;NOT NULL;"` 26 | LastRestPassTime int `gorm:"column:last_rest_pass_time;NOT NULL;"` 27 | RegDate time.Time `gorm:"column:reg_date;NOT NULL;"` 28 | InviteNum int `gorm:"column:invite_num;NOT NULL;"` 29 | Money float64 `gorm:"column:money;NOT NULL;"` 30 | RefBy int `gorm:"column:ref_by;NOT NULL;"` 31 | ExpireTime int `gorm:"column:expire_time;NOT NULL;"` 32 | Method string `gorm:"column:method;NOT NULL;"` 33 | IsEmailVerify int `gorm:"column:is_email_verify;NOT NULL;"` 34 | RegIp string `gorm:"column:reg_ip;NOT NULL;"` 35 | NodeSpeedlimit float64 `gorm:"column:node_speedlimit;NOT NULL;"` 36 | NodeConnector int `gorm:"column:node_connector;NOT NULL;"` 37 | IsAdmin int `gorm:"column:is_admin;NOT NULL;"` 38 | ImType int `gorm:"column:im_type;"` 39 | ImValue string `gorm:"column:im_value;type:text;"` 40 | LastDayT int64 `gorm:"column:last_day_t;NOT NULL;"` 41 | SendDailyMail int `gorm:"column:sendDailyMail;NOT NULL;"` 42 | Class int `gorm:"column:class;NOT NULL;"` 43 | ClassExpire time.Time `gorm:"column:class_expire;NOT NULL;"` 44 | ExpireIn time.Time `gorm:"column:expire_in;NOT NULL;"` 45 | Theme string `gorm:"column:theme;NOT NULL;type:text;"` 46 | GaToken string `gorm:"column:ga_token;NOT NULL;type:text;"` 47 | GaEnable int `gorm:"column:ga_enable;NOT NULL;"` 48 | Pac string `gorm:"column:pac;type:longtext;"` 49 | Remark string `gorm:"column:remark;type:text;" json:"-"` 50 | NodeGroup int `gorm:"column:node_group;NOT NULL;"` 51 | AutoResetDay int `gorm:"column:auto_reset_day;NOT NULL;"` 52 | AutoResetBandwidth float64 `gorm:"column:auto_reset_bandwidth;NOT NULL;"` 53 | Protocol string `gorm:"column:protocol;"` 54 | ProtocolParam string `gorm:"column:protocol_param;"` 55 | Obfs string `gorm:"column:obfs;"` 56 | ObfsParam string `gorm:"column:obfs_param;"` 57 | ForbiddenIp string `gorm:"column:forbidden_ip;type:longtext;"` 58 | ForbiddenPort string `gorm:"column:forbidden_port;type:longtext;"` 59 | DisconnectIp string `gorm:"column:disconnect_ip;type:longtext;"` 60 | IsHide int `gorm:"column:is_hide;NOT NULL;"` 61 | IsMultiUser int `gorm:"column:is_multi_user;NOT NULL;"` 62 | TelegramId int64 `gorm:"column:telegram_id;"` 63 | } 64 | 65 | 66 | func (User) TableName() string { 67 | return "user" 68 | } 69 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "ulHAt7P7SYMC2g3ThlXNzVN9Z/k=", 7 | "path": "github.com/chris-ramon/douceur/css", 8 | "revision": "f3463056cd52886eda904655e1940157bf323bd7", 9 | "revisionTime": "2016-06-03T23:54:19Z" 10 | }, 11 | { 12 | "checksumSHA1": "jWIgyVGWXtHIKfRy40IUlMZyvUY=", 13 | "path": "github.com/chris-ramon/douceur/parser", 14 | "revision": "f3463056cd52886eda904655e1940157bf323bd7", 15 | "revisionTime": "2016-06-03T23:54:19Z" 16 | }, 17 | { 18 | "checksumSHA1": "P6u6fDWwSfRL2Bc6uhZ9kcoPsek=", 19 | "path": "github.com/dgrijalva/jwt-go", 20 | "revision": "5e25c22bd5d6de03265bbe5462dcd162f85046f6", 21 | "revisionTime": "2019-06-20T18:01:02Z" 22 | }, 23 | { 24 | "checksumSHA1": "LTZ609BIgxg9+twU/qxt6BEenX8=", 25 | "path": "github.com/dvwright/xss-mw", 26 | "revision": "a00cba88ec98902cf5238c857862949dc734fcc1", 27 | "revisionTime": "2019-03-11T03:28:52Z" 28 | }, 29 | { 30 | "checksumSHA1": "YImPVNZRCKOaaReWnFEH+AaP8NM=", 31 | "path": "github.com/fsnotify/fsnotify", 32 | "revision": "1485a34d5d5723fea214f5710708e19a831720e4", 33 | "revisionTime": "2019-03-12T18:14:46Z" 34 | }, 35 | { 36 | "checksumSHA1": "qlEzrgKgIkh7y0ePm9BNo1cNdXo=", 37 | "path": "github.com/gin-contrib/sse", 38 | "revision": "54d8467d122d380a14768b6b4e5cd7ca4755938f", 39 | "revisionTime": "2019-06-02T15:02:53Z" 40 | }, 41 | { 42 | "checksumSHA1": "90S9sFqcIo/uuwQzz1t/wYs7GEs=", 43 | "path": "github.com/gin-gonic/gin", 44 | "revision": "b869fe1415e4b9eb52f247441830d502aece2d4d", 45 | "revisionTime": "2018-08-14T08:58:52Z", 46 | "version": "v1.3", 47 | "versionExact": "v1.3.0" 48 | }, 49 | { 50 | "checksumSHA1": "BjShmaQTKI8+411rCb7LFqJkLA0=", 51 | "path": "github.com/gin-gonic/gin/binding", 52 | "revision": "b869fe1415e4b9eb52f247441830d502aece2d4d", 53 | "revisionTime": "2018-08-14T08:58:52Z", 54 | "version": "v1.3", 55 | "versionExact": "v1.3.0" 56 | }, 57 | { 58 | "checksumSHA1": "rST0S8cuVbM57VmJhWXhKNUkKTc=", 59 | "path": "github.com/gin-gonic/gin/internal/json", 60 | "revision": "9a820cf0054bcd769f785457b7dbd149a7b29fdd", 61 | "revisionTime": "2019-08-16T01:10:44Z" 62 | }, 63 | { 64 | "checksumSHA1": "woO1qIxIeQ1bcbPSiMfAKk3r4xg=", 65 | "path": "github.com/gin-gonic/gin/json", 66 | "revision": "b869fe1415e4b9eb52f247441830d502aece2d4d", 67 | "revisionTime": "2018-08-14T08:58:52Z", 68 | "version": "v1.3", 69 | "versionExact": "v1.3.0" 70 | }, 71 | { 72 | "checksumSHA1": "f1n8P2YAi3o+X0//dkoDJ822zW0=", 73 | "path": "github.com/gin-gonic/gin/render", 74 | "revision": "b869fe1415e4b9eb52f247441830d502aece2d4d", 75 | "revisionTime": "2018-08-14T08:58:52Z", 76 | "version": "v1.3", 77 | "versionExact": "v1.3.0" 78 | }, 79 | { 80 | "checksumSHA1": "zGYOyUB58GVhLkrwhG1ut66SPec=", 81 | "path": "github.com/go-sql-driver/mysql", 82 | "revision": "877a9775f06853f611fb2d4e817d92479242d1cd", 83 | "revisionTime": "2019-05-10T10:23:35Z" 84 | }, 85 | { 86 | "checksumSHA1": "C8nYObwbo2oyODQbIT83lY37ajM=", 87 | "path": "github.com/golang/protobuf/proto", 88 | "revision": "4c88cc3f1a34ffade77b79abc53335d1e511f25b", 89 | "revisionTime": "2019-08-05T18:00:45Z" 90 | }, 91 | { 92 | "checksumSHA1": "Ttr0ESMQ/w/LYWdvGUPErQtQgsY=", 93 | "path": "github.com/gorilla/css/scanner", 94 | "revision": "4940b8d7810384b58c15dbf1103a148fdf0761f2", 95 | "revisionTime": "2019-06-27T04:11:19Z" 96 | }, 97 | { 98 | "checksumSHA1": "vgGv8zuy7q8c5LBAFO1fnnQRRgE=", 99 | "path": "github.com/hashicorp/hcl", 100 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 101 | "revisionTime": "2019-06-11T12:32:18Z" 102 | }, 103 | { 104 | "checksumSHA1": "XQmjDva9JCGGkIecOgwtBEMCJhU=", 105 | "path": "github.com/hashicorp/hcl/hcl/ast", 106 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 107 | "revisionTime": "2019-06-11T12:32:18Z" 108 | }, 109 | { 110 | "checksumSHA1": "1GmX7G0Pgf5XprOh+T3zXMXX0dc=", 111 | "path": "github.com/hashicorp/hcl/hcl/parser", 112 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 113 | "revisionTime": "2019-06-11T12:32:18Z" 114 | }, 115 | { 116 | "checksumSHA1": "encY+ZtDf4nJaMvsVL2c+EJ2r3Q=", 117 | "path": "github.com/hashicorp/hcl/hcl/printer", 118 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 119 | "revisionTime": "2019-06-11T12:32:18Z" 120 | }, 121 | { 122 | "checksumSHA1": "+qJTCxhkwC7r+VZlPlZz8S74KmU=", 123 | "path": "github.com/hashicorp/hcl/hcl/scanner", 124 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 125 | "revisionTime": "2019-06-11T12:32:18Z" 126 | }, 127 | { 128 | "checksumSHA1": "oS3SCN9Wd6D8/LG0Yx1fu84a7gI=", 129 | "path": "github.com/hashicorp/hcl/hcl/strconv", 130 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 131 | "revisionTime": "2019-06-11T12:32:18Z" 132 | }, 133 | { 134 | "checksumSHA1": "c6yprzj06ASwCo18TtbbNNBHljA=", 135 | "path": "github.com/hashicorp/hcl/hcl/token", 136 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 137 | "revisionTime": "2019-06-11T12:32:18Z" 138 | }, 139 | { 140 | "checksumSHA1": "PwlfXt7mFS8UYzWxOK5DOq0yxS0=", 141 | "path": "github.com/hashicorp/hcl/json/parser", 142 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 143 | "revisionTime": "2019-06-11T12:32:18Z" 144 | }, 145 | { 146 | "checksumSHA1": "afrZ8VmAwfTdDAYVgNSXbxa4GsA=", 147 | "path": "github.com/hashicorp/hcl/json/scanner", 148 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 149 | "revisionTime": "2019-06-11T12:32:18Z" 150 | }, 151 | { 152 | "checksumSHA1": "fNlXQCQEnb+B3k5UDL/r15xtSJY=", 153 | "path": "github.com/hashicorp/hcl/json/token", 154 | "revision": "cf7d376da96d9cecec7c7483cec2735efe54a410", 155 | "revisionTime": "2019-06-11T12:32:18Z" 156 | }, 157 | { 158 | "checksumSHA1": "YzIVWHj5vDlCp3A8Zy0vhCn4zZ0=", 159 | "path": "github.com/jinzhu/gorm", 160 | "revision": "836fb2c19d84dac7b0272958dfb9af7cf0d0ade4", 161 | "revisionTime": "2019-06-30T07:50:19Z" 162 | }, 163 | { 164 | "checksumSHA1": "iqeSAF7imnVsW+iqLqf6kkYn1VU=", 165 | "path": "github.com/jinzhu/inflection", 166 | "revision": "f5c5f50e6090ae76a29240b61ae2a90dd810112e", 167 | "revisionTime": "2019-06-03T04:28:36Z" 168 | }, 169 | { 170 | "checksumSHA1": "4DgEcOv2o9NyZAHCbdS76mToqpU=", 171 | "path": "github.com/json-iterator/go", 172 | "revision": "27518f6661eba504be5a7a9a9f6d9460d892ade3", 173 | "revisionTime": "2019-06-21T16:12:01Z" 174 | }, 175 | { 176 | "checksumSHA1": "nsiDN1JmbYszL69i6eYUAyL9y8E=", 177 | "path": "github.com/magiconair/properties", 178 | "revision": "de8848e004dd33dc07a2947b3d76f618a7fc7ef1", 179 | "revisionTime": "2019-05-10T14:34:07Z" 180 | }, 181 | { 182 | "checksumSHA1": "C3Xbt5Nq5Wez3B5cyi8vQlVkP1g=", 183 | "path": "github.com/mattn/go-isatty", 184 | "revision": "bf9a1dea1961e1d831824fb135332bfb8c10e8b8", 185 | "revisionTime": "2019-08-18T12:36:53Z" 186 | }, 187 | { 188 | "checksumSHA1": "br2vKm1G4rY7y5GWDTJK/iJ/ITw=", 189 | "path": "github.com/microcosm-cc/bluemonday", 190 | "revision": "d9ddfeccb93d4506ae8257aa13026c695552686c", 191 | "revisionTime": "2019-08-10T10:21:58Z" 192 | }, 193 | { 194 | "checksumSHA1": "J+g0oZePWp2zSIISD2dZZKTxmgg=", 195 | "path": "github.com/mitchellh/mapstructure", 196 | "revision": "3536a929edddb9a5b34bd6861dc4a9647cb459fe", 197 | "revisionTime": "2018-10-05T04:51:35Z" 198 | }, 199 | { 200 | "checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=", 201 | "path": "github.com/modern-go/concurrent", 202 | "revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94", 203 | "revisionTime": "2018-03-06T01:26:44Z" 204 | }, 205 | { 206 | "checksumSHA1": "qvH48wzTIV3QKSDqI0dLFtVjaDI=", 207 | "path": "github.com/modern-go/reflect2", 208 | "revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8", 209 | "revisionTime": "2018-07-18T01:23:57Z" 210 | }, 211 | { 212 | "checksumSHA1": "spFbuQuhy/UkCDnCeABb8i2wEHc=", 213 | "path": "github.com/pelletier/go-toml", 214 | "revision": "781fbae71e40e8e598206a0ac02c680d64df2fd0", 215 | "revisionTime": "2019-08-19T19:53:00Z" 216 | }, 217 | { 218 | "checksumSHA1": "2d0rR/VhqllUdrcH6kwHHrhqJ2E=", 219 | "path": "github.com/spf13/afero", 220 | "revision": "588a75ec4f32903aa5e39a2619ba6a4631e28424", 221 | "revisionTime": "2019-03-02T02:18:57Z" 222 | }, 223 | { 224 | "checksumSHA1": "HcOjO9+cwl+xYnkiGuIeesqwBs8=", 225 | "path": "github.com/spf13/afero/mem", 226 | "revision": "588a75ec4f32903aa5e39a2619ba6a4631e28424", 227 | "revisionTime": "2019-03-02T02:18:57Z" 228 | }, 229 | { 230 | "checksumSHA1": "YodSIERfO54v528HWIgxW9em7Wc=", 231 | "path": "github.com/spf13/cast", 232 | "revision": "c01685bb8421cecb276fa517e91f757215f980b3", 233 | "revisionTime": "2019-05-31T09:26:28Z" 234 | }, 235 | { 236 | "checksumSHA1": "kmcWtDErtMPA3+o7+B4MDC3z+T0=", 237 | "path": "github.com/spf13/jwalterweatherman", 238 | "revision": "94f6ae3ed3bceceafa716478c5fbf8d29ca601a1", 239 | "revisionTime": "2018-10-28T14:53:47Z" 240 | }, 241 | { 242 | "checksumSHA1": "ca/xiPs0IJikRcMg/YGIra+dvu8=", 243 | "path": "github.com/spf13/pflag", 244 | "revision": "972238283c0625cf3e881de7699ba8f2524c340a", 245 | "revisionTime": "2019-08-14T00:10:55Z" 246 | }, 247 | { 248 | "checksumSHA1": "UclVds6snZal53sDrrExEWGwoCg=", 249 | "path": "github.com/spf13/viper", 250 | "revision": "e697d557b7f549ddf91098cef2ad6e92176dbcdf", 251 | "revisionTime": "2019-08-16T21:15:10Z" 252 | }, 253 | { 254 | "checksumSHA1": "XaFlfXk4sA7VsnTu8GYxGXX8FjU=", 255 | "path": "github.com/subosito/gotenv", 256 | "revision": "422ef8095f112cfaf9f40c368e0b9ae8c0714cfb", 257 | "revisionTime": "2019-08-13T02:47:33Z" 258 | }, 259 | { 260 | "checksumSHA1": "JWmqQjxnUg8PRe1QfYCmzAcWDvM=", 261 | "path": "github.com/ugorji/go/codec", 262 | "revision": "42bc974514ff101a54c6b72a0e4dee29d96c0b26", 263 | "revisionTime": "2019-08-12T10:43:08Z" 264 | }, 265 | { 266 | "checksumSHA1": "bONEZcbkYKiPyABrecOLzHomjPU=", 267 | "path": "golang.org/x/net/html", 268 | "revision": "74dc4d7220e7acc4e100824340f3e66577424772", 269 | "revisionTime": "2019-08-11T06:12:18Z" 270 | }, 271 | { 272 | "checksumSHA1": "XtSbs1gpyaEsIqf6VRhJsgOQe5U=", 273 | "path": "golang.org/x/net/html/atom", 274 | "revision": "74dc4d7220e7acc4e100824340f3e66577424772", 275 | "revisionTime": "2019-08-11T06:12:18Z" 276 | }, 277 | { 278 | "checksumSHA1": "2DA7yQXGLTxH4M5zugia3YpJAV0=", 279 | "path": "golang.org/x/sys/unix", 280 | "revision": "fde4db37ae7ad8191b03d30d27f258b5291ae4e3", 281 | "revisionTime": "2019-08-12T08:24:04Z" 282 | }, 283 | { 284 | "checksumSHA1": "R9iBDY+aPnT+8pyRcqGjXq5QixA=", 285 | "path": "golang.org/x/text/transform", 286 | "revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475", 287 | "revisionTime": "2018-12-15T17:52:45Z" 288 | }, 289 | { 290 | "checksumSHA1": "OE61CWKF0KBo5hZEMrfhCkQEKg0=", 291 | "path": "golang.org/x/text/unicode/norm", 292 | "revision": "342b2e1fbaa52c93f31447ad2c6abc048c63e475", 293 | "revisionTime": "2018-12-15T17:52:45Z" 294 | }, 295 | { 296 | "checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=", 297 | "path": "gopkg.in/go-playground/validator.v8", 298 | "revision": "5f1438d3fca68893a817e4a66806cea46a9e4ebf", 299 | "revisionTime": "2017-07-30T05:02:35Z" 300 | }, 301 | { 302 | "checksumSHA1": "QqDq2x8XOU7IoOR98Cx1eiV5QY8=", 303 | "path": "gopkg.in/yaml.v2", 304 | "revision": "51d6538a90f86fe93ac480b35f37b2be17fef232", 305 | "revisionTime": "2018-11-15T11:05:04Z" 306 | } 307 | ], 308 | "rootPath": "github.com/tonyzzzzzz/sspanel-go" 309 | } 310 | --------------------------------------------------------------------------------