From 8c7a1f39fd3a891d650daf1bfbb3e39f04c8c01c Mon Sep 17 00:00:00 2001 From: bing Date: Tue, 4 Oct 2022 21:09:31 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E5=90=8E=E7=AB=AF=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apiserver/apiserver.go | 11 +++ apiserver/handle_auth.go | 1 + apiserver/handle_book.go | 1 + apiserver/handle_category.go | 1 + auth/auth.go | 48 +++++++++ auth/jwt/cliam.go | 53 ++++++++++ auth/jwt/manager.go | 185 +++++++++++++++++++++++++++++++++++ auth/jwt/store/store.go | 74 ++++++++++++++ code/code.go | 16 +++ conf/configuration.go | 29 ++++++ conf/mysql.go | 9 ++ conf/redis.go | 8 ++ go.mod | 35 +++++++ go.sum | 109 +++++++++++++++++++++ main.go | 1 + types/book.go | 25 +++++ types/category.go | 27 +++++ types/user.go | 27 +++++ utils/ginhelper/gin.go | 32 ++++++ 19 files changed, 692 insertions(+) create mode 100644 apiserver/apiserver.go create mode 100644 apiserver/handle_auth.go create mode 100644 apiserver/handle_book.go create mode 100644 apiserver/handle_category.go create mode 100644 auth/auth.go create mode 100644 auth/jwt/cliam.go create mode 100644 auth/jwt/manager.go create mode 100644 auth/jwt/store/store.go create mode 100644 code/code.go create mode 100644 conf/configuration.go create mode 100644 conf/mysql.go create mode 100644 conf/redis.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 types/book.go create mode 100644 types/category.go create mode 100644 types/user.go create mode 100644 utils/ginhelper/gin.go diff --git a/apiserver/apiserver.go b/apiserver/apiserver.go new file mode 100644 index 0000000..b4a955b --- /dev/null +++ b/apiserver/apiserver.go @@ -0,0 +1,11 @@ +package apiserver + +import ( + "biblio.bing89.com/auth" + "biblio.bing89.com/conf" +) + +type APIServer struct { + Conf *conf.Configuration + authManager auth.AuthManager +} \ No newline at end of file diff --git a/apiserver/handle_auth.go b/apiserver/handle_auth.go new file mode 100644 index 0000000..5431069 --- /dev/null +++ b/apiserver/handle_auth.go @@ -0,0 +1 @@ +package apiserver \ No newline at end of file diff --git a/apiserver/handle_book.go b/apiserver/handle_book.go new file mode 100644 index 0000000..5431069 --- /dev/null +++ b/apiserver/handle_book.go @@ -0,0 +1 @@ +package apiserver \ No newline at end of file diff --git a/apiserver/handle_category.go b/apiserver/handle_category.go new file mode 100644 index 0000000..5431069 --- /dev/null +++ b/apiserver/handle_category.go @@ -0,0 +1 @@ +package apiserver \ No newline at end of file diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..f821a99 --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,48 @@ +package auth + +import ( + "time" + + "github.com/gin-gonic/gin" +) + +type AuthenticationRequest struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Token string `json:"token,omitempty"` + Captcha string `json:"captcha,omitempty"` + Phone string `json:"phone,omitempty"` + IP string `json:"ip,omitempty"` +} + +type AuthenticationResponse struct { + Token string `json:"token,omitempty"` + UID uint64 `json:"uid,omitempty"` + Username string `json:"username,omitempty"` + Nickname string `json:"nickname,omitempty"` + Avatar string `json:"avatar,omitempty"` + Phone string `json:"phone,omitempty"` + Email string `json:"email,omitempty"` + CreatedAt uint `json:"created_at,omitempty"` + UpdatedAt uint `json:"updated_at,omitempty"` + Code int `json:"-"` +} + +// type IdentifyResult struct { +// KubernetesToken string +// Cert string +// } + +// 验证账号密码的方法,这里想把这一块单独剥离出去,这样不同的auth就更加抽象化 +type IdentifyFunc func(*AuthenticationRequest) (*AuthenticationResponse, error) + +type AuthManager interface { + Login(IdentifyFunc, *AuthenticationRequest) (*AuthenticationResponse, error) + Logout(*AuthenticationRequest) error + Refresh(*AuthenticationRequest) (string, error) + MiddleWare() gin.HandlerFunc + // InitAuthRoute(IdentifyFunc, *gin.RouterGroup) + ExpireAt(uint64, time.Time) error + Expire(uint64, time.Duration) error + TTL(uint64) (time.Duration, error) +} diff --git a/auth/jwt/cliam.go b/auth/jwt/cliam.go new file mode 100644 index 0000000..c99f790 --- /dev/null +++ b/auth/jwt/cliam.go @@ -0,0 +1,53 @@ +package jwt + +import ( + "fmt" + "time" + + gojwt "github.com/golang-jwt/jwt" +) + +type TokenClaim struct { + User string `json:"-"` + UserID uint64 `json:"userId"` + gojwt.StandardClaims + Expiration time.Duration `json:"-"` +} + +func ParseTokenClaim(token string, secret string) (*TokenClaim, error) { + tokenClaims, err := gojwt.ParseWithClaims(token, &TokenClaim{}, func(token *gojwt.Token) (interface{}, error) { + return []byte(secret), nil + }) + + if tokenClaims != nil { + if claims, ok := tokenClaims.Claims.(*TokenClaim); ok { + return claims, nil + } + } + // + return nil, err +} + +func NewTokenClaim(user string, expiration time.Duration, uid uint64) (*TokenClaim, error) { + if int64(expiration) < 0 { + return nil, fmt.Errorf("expired at invalid") + } + claim := TokenClaim{ + User: user, + StandardClaims: gojwt.StandardClaims{ + // ExpiresAt: time.Now().Add(expiration).Unix(), + }, + UserID: uid, + } + return &claim, nil +} + +func (t *TokenClaim) InValid() bool { + return t.Valid() != nil +} + +func (t *TokenClaim) String(secret string) (string, error) { + tokenClaims := gojwt.NewWithClaims(gojwt.SigningMethodHS256, t) + token, err := tokenClaims.SignedString([]byte(secret)) + return token, err +} diff --git a/auth/jwt/manager.go b/auth/jwt/manager.go new file mode 100644 index 0000000..4a6b100 --- /dev/null +++ b/auth/jwt/manager.go @@ -0,0 +1,185 @@ +package jwt + +import ( + "errors" + "fmt" + "strconv" + "time" + + "biblio.bing89.com/auth" + "biblio.bing89.com/auth/jwt/store" + "biblio.bing89.com/code" + "biblio.bing89.com/utils/ginhelper" + "github.com/gin-gonic/gin" +) + +type manager struct { + store store.ClaimStore + term time.Duration + signedSecret string +} + +func NewManager(store store.ClaimStore, term time.Duration, secret string) auth.AuthManager { + return &manager{ + store: store, + term: term, + signedSecret: secret, + } +} + +func (m *manager) Login(identifyFunc auth.IdentifyFunc, data *auth.AuthenticationRequest) (*auth.AuthenticationResponse, error) { + // if _, err := m.IsLogined(data.Username); err == nil { + // return nil, fmt.Errorf("user is already logined") + // } + resp, err := identifyFunc(data) + if err != nil { + return nil, err + } + tokenClaim, err := m.IsLogined(resp.UID) + if err == nil && tokenClaim != nil { //已登录 + //刷新过期时间 + err = m.Expire(tokenClaim.UserID, m.term) + tokenString, err1 := tokenClaim.String(m.signedSecret) + if err != nil || tokenString == "" { + return nil, fmt.Errorf("get token string failed:%v", err1) + } + resp.Token = tokenString + if err != nil { + return resp, fmt.Errorf("update token expiration failed:%v", err) + } + return resp, nil + } + //未登录 + tokenClaim, err = NewTokenClaim(data.Username, m.term, resp.UID) + if err != nil { + return nil, fmt.Errorf("generate token failed:%v", err) + } + tokenString, err := tokenClaim.String(m.signedSecret) + if err != nil { + return nil, fmt.Errorf("get token string failed:%v", err) + } + //存储登录状态 + err = m.StoreLoginState(resp.UID, tokenClaim) + if err != nil { + return nil, fmt.Errorf("StoreLoginState failed:%v", err) + } + resp.Token = tokenString + return resp, nil +} + +func (m *manager) Logout(data *auth.AuthenticationRequest) error { + //解析token,确认有logout权限 + claim, err := ParseTokenClaim(data.Token, m.signedSecret) + if err != nil { + return err + + } + if c, err := m.IsLogined(claim.UserID); err == nil { + err = m.DeleteToken(c) + return err + } + return fmt.Errorf("user not login or token invalid") +} + +func (m *manager) Refresh(data *auth.AuthenticationRequest) (string, error) { + cliam, err := ParseTokenClaim(data.Token, m.signedSecret) + if err != nil { + return "", err + } + //已过期 + if cliam.Valid() != nil { + return "", fmt.Errorf("cannot use an invalid token to refresh: %v", err) + + } + //找不到登陆状态 + if _, err := m.IsLogined(cliam.UserID); err != nil { + return "", fmt.Errorf("token invalid, user maybe not login") + + } + //重置有效期 + cliam.ExpiresAt = time.Now().Add(m.term).Unix() + err = m.StoreLoginState(cliam.UserID, cliam) + if err != nil { + return "", err + } + tokenStr, err := cliam.String(m.signedSecret) + return tokenStr, err + +} + +//中间件 +//ERR_NOT_AUTH +func (m *manager) MiddleWare() gin.HandlerFunc { + return func(c *gin.Context) { + tokenString := c.GetHeader("Token") + if tokenString == "" { + ginhelper.Error(c, code.CodeNoAuth, errors.New("token is empty")) + return + } + tokenClaim, err := ParseTokenClaim(tokenString, m.signedSecret) + if err != nil { + ginhelper.Error(c, code.CodeNoAuth,err) + return + } + if err := m.TokenValid(tokenClaim.UserID, tokenString); err != nil { + ginhelper.Error(c, code.CodeNoAuth, err) + return + } + c.Set("uid", strconv.FormatUint(tokenClaim.UserID, 10)) + c.Next() + } +} + +//存储登录状态 +func (m *manager) StoreLoginState(user uint64, claim *TokenClaim) error { + //储存登录状态 + str, err := claim.String(m.signedSecret) + if err != nil { + return err + } + return m.store.Set(user, str, m.term) +} + +//获取token +//是否已登录 +func (m *manager) IsLogined(user uint64) (*TokenClaim, error) { + token, err := m.store.Get(user) + if err != nil { + return nil, err + } + return ParseTokenClaim(token, m.signedSecret) +} + +//删除token +func (m *manager) DeleteToken(claim *TokenClaim) error { + return m.store.Delete(claim.UserID) +} + +func (m *manager) Close() error { + return m.store.Close() +} + +func (m *manager) TokenValid(uid uint64, token string) error { + t, err := m.store.Get(uid) + if err != nil { + return fmt.Errorf("get token failed: %v", err) + } + if t != token { + return fmt.Errorf("token is invalid") + } + return nil +} + +func (m *manager) ExpireAt(uid uint64, t time.Time) error { + err := m.store.ExpireAt(uid, t) + return err +} + +func (m *manager) Expire(uid uint64, t time.Duration) error { + err := m.store.Expire(uid, t) + return err +} + +func (m *manager) TTL(uid uint64) (time.Duration, error) { + return m.store.TTL(uid) +} diff --git a/auth/jwt/store/store.go b/auth/jwt/store/store.go new file mode 100644 index 0000000..c92a137 --- /dev/null +++ b/auth/jwt/store/store.go @@ -0,0 +1,74 @@ +package store + +import ( + "context" + "strconv" + "time" + + "github.com/go-redis/redis/v8" +) + +type ClaimStore interface { + Get(uint64) (string, error) + Set(uint64, string, time.Duration) error + Delete(uint64) error + Close() error + ExpireAt(uint64, time.Time) error + Expire(uint64, time.Duration) error + TTL(uint64) (time.Duration, error) +} + +type redisStore struct { + host string + password string + db int + client *redis.Client +} + +func NewRedis(host string, password string, db int) (ClaimStore, error) { + r := redisStore{ + host: host, + password: password, + db: db, + } + r.client = redis.NewClient(&redis.Options{Addr: host, Password: password, DB: db}) + return &r, nil +} + +func (r *redisStore) Get(key uint64) (string, error) { + val, err := r.client.Get(context.Background(), r.key(key)).Result() + return val, err +} + +func (r *redisStore) Set(key uint64, val string, expiration time.Duration) error { + err := r.client.SetEX(context.Background(), r.key(key), val, expiration).Err() + return err +} + +func (r *redisStore) Delete(key uint64) error { + err := r.client.Del(context.Background(), r.key(key)).Err() + return err +} + +func (r *redisStore) Close() error { + return r.client.Close() +} + +func (r *redisStore) key(key uint64) string { + return "auth_token:" + strconv.FormatUint(key, 10) +} + +func (r *redisStore) ExpireAt(key uint64, t time.Time) error { + err := r.client.ExpireAt(context.Background(), r.key(key), t).Err() + return err +} + +func (r *redisStore) Expire(key uint64, term time.Duration) error { + err := r.client.Expire(context.Background(), r.key(key), term).Err() + return err +} + +func (r *redisStore) TTL(key uint64) (time.Duration, error) { + expire, err := r.client.TTL(context.Background(), r.key(key)).Result() + return expire, err +} diff --git a/code/code.go b/code/code.go new file mode 100644 index 0000000..5d2130d --- /dev/null +++ b/code/code.go @@ -0,0 +1,16 @@ +package code + +const ( + CodeArgsInvalid = iota + 400000 +) + +const ( + CodeDatabase = iota + 400100 +) + +const ( + CodeNoAuth = iota + 403000 + CodeAuthFailed + CodePasswordInvalid + CodeUserNotExist +) \ No newline at end of file diff --git a/conf/configuration.go b/conf/configuration.go new file mode 100644 index 0000000..3b5c019 --- /dev/null +++ b/conf/configuration.go @@ -0,0 +1,29 @@ +package conf + +import ( + "errors" + "os" + + "gopkg.in/yaml.v2" +) + +type Configuration struct { + ApiVersion string `json:"apiVersion" yaml:"apiVersion"` + Port int `json:"port" yaml:"port"` + MySQL MySQL `json:"mysql" yaml:"mysql"` + Redis Redis `json:"redis" yaml:"redis"` +} + + +func NewConfiguration(file string)(*Configuration, error){ + if file == "" { + return nil ,errors.New("file not exist") + } + data, err := os.ReadFile(file) + if err != nil { + return nil, err + } + c := &Configuration{} + err = yaml.Unmarshal(data, c) + return c, err +} \ No newline at end of file diff --git a/conf/mysql.go b/conf/mysql.go new file mode 100644 index 0000000..60f0f33 --- /dev/null +++ b/conf/mysql.go @@ -0,0 +1,9 @@ +package conf + +type MySQL struct { + Host string `json:"host" yaml:"host"` + Port int `json:"port" yaml:"port"` + Database string `json:"database" yaml:"database"` + Username string `json:"username" yaml:"username"` + Password string `json:"password" yaml:"password"` +} diff --git a/conf/redis.go b/conf/redis.go new file mode 100644 index 0000000..f427b96 --- /dev/null +++ b/conf/redis.go @@ -0,0 +1,8 @@ +package conf + +type Redis struct { + Host string `json:"host" yaml:"host"` + Port int `json:"port" yaml:"port"` + Database int `json:"database" yaml:"database"` + Password string `json:"password" yaml:"password"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7e75da2 --- /dev/null +++ b/go.mod @@ -0,0 +1,35 @@ +module biblio.bing89.com + +go 1.19 + +require ( + github.com/gin-gonic/gin v1.8.1 + github.com/go-redis/redis/v8 v8.11.5 + github.com/golang-jwt/jwt v3.2.2+incompatible + gorm.io/gorm v1.23.10 +) + +require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a88ce78 --- /dev/null +++ b/go.sum @@ -0,0 +1,109 @@ +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.23.10 h1:4Ne9ZbzID9GUxRkllxN4WjJKpsHx8YbKvekVdgyWh24= +gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..85f0393 --- /dev/null +++ b/main.go @@ -0,0 +1 @@ +package main \ No newline at end of file diff --git a/types/book.go b/types/book.go new file mode 100644 index 0000000..de714c6 --- /dev/null +++ b/types/book.go @@ -0,0 +1,25 @@ +package types + +import "gorm.io/gorm" + +/* +drop table if exists books; +create table if not exists books ( + id int unsigned auto_increment primary key, + name varchar(32) not null comment '', + category int unsigned default 0 comment '', + avatar varchar(128) default '' comment '', + filename varchar(128) not null comment '', + created_at bigint default 0 comment '', + updated_at bigint default 0 comment '', + deleted_at bigint default 0 comment '' +); +*/ + +type Book struct { + gorm.Model + Name string `json:"name" gorm:"type:varchar(32)"` + Category uint `json:"category"` + Avatar string `json:"avatar" gorm:"type:varchar(128)"` + Filename string `json:"filename" gorm:"type:varchar(128)"` +} diff --git a/types/category.go b/types/category.go new file mode 100644 index 0000000..85e3883 --- /dev/null +++ b/types/category.go @@ -0,0 +1,27 @@ +package types + +import "gorm.io/gorm" + +/* +drop table if exists catecories; +create table if not exists categories ( + id int unsigned auto_increment primary key, + name varchar(32) not null comment '', + description varchar(256) default '' comment '', + parent int unsigned default 0 comment '', + avatar varchar(128) default '' comment '', + created_at bigint default 0 comment '', + updated_at bigint default 0 comment '', + deleted_at bigint default 0 comment '', + unique(name,parent) +); + +*/ + +type Category struct { + gorm.Model + Name string `json:"name" gorm:"type:varchar(32)"` + Parent uint `json:"category"` + Avatar string `json:"avatar" gorm:"type:varchar(128)"` + Description string `json:"description" gorm:"type:varchar(256)"` +} \ No newline at end of file diff --git a/types/user.go b/types/user.go new file mode 100644 index 0000000..cdd31d2 --- /dev/null +++ b/types/user.go @@ -0,0 +1,27 @@ +package types + +import "gorm.io/gorm" + +/* +drop table if exists users; +create table if not exists users( + id int unsigned auto_increment primary key, + username varchar(32) not null comment '', + passwd varchar(128) not null comment '', + nikename varchar(32) default '' comment '', + phone char(11) default '' comment '', + email varchar(64) default '' comment '', + created_at bigint default 0 comment '', + updated_at bigint default 0 comment '', + deleted_at bigint default 0 comment '', + unique(username) +); +*/ +type User struct { + gorm.Model + Name string `json:"name" gorm:"type:varchar(32);unique;not null"` + Password string `json:"-" gorm:"type:varchar(128);not null"` + Nickname string `json:"nickname" gorm:"type:varchar(32)"` + Phone string `json:"phone" gorm:"type:char(11)"` + Email string `json:"email" gorm:"type:varchar(64)"` +} \ No newline at end of file diff --git a/utils/ginhelper/gin.go b/utils/ginhelper/gin.go new file mode 100644 index 0000000..5c482f2 --- /dev/null +++ b/utils/ginhelper/gin.go @@ -0,0 +1,32 @@ +package ginhelper + +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" +) + +const MessageSuccess="success" + +type Response struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + +func Success(c *gin.Context, data interface{}){ + c.JSON(http.StatusOK, &Response{Code: 0, + Message: MessageSuccess, + Data: data, + }) +} + +func Error(c *gin.Context, code int, err error){ + c.JSON(http.StatusOK, &Response{Code: code, Message: err.Error(), Data: nil}) +} + +func ErrorWithLog(c *gin.Context, code int, err error){ + log.Println(err) + Error(c, code, err) +} \ No newline at end of file