feat 后端文件基础框架

This commit is contained in:
bing 2022-10-04 21:09:31 +08:00
parent 3e7aa47e49
commit 8c7a1f39fd
19 changed files with 692 additions and 0 deletions

11
apiserver/apiserver.go Normal file
View File

@ -0,0 +1,11 @@
package apiserver
import (
"biblio.bing89.com/auth"
"biblio.bing89.com/conf"
)
type APIServer struct {
Conf *conf.Configuration
authManager auth.AuthManager
}

1
apiserver/handle_auth.go Normal file
View File

@ -0,0 +1 @@
package apiserver

1
apiserver/handle_book.go Normal file
View File

@ -0,0 +1 @@
package apiserver

View File

@ -0,0 +1 @@
package apiserver

48
auth/auth.go Normal file
View File

@ -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)
}

53
auth/jwt/cliam.go Normal file
View File

@ -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
}

185
auth/jwt/manager.go Normal file
View File

@ -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)
}

74
auth/jwt/store/store.go Normal file
View File

@ -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
}

16
code/code.go Normal file
View File

@ -0,0 +1,16 @@
package code
const (
CodeArgsInvalid = iota + 400000
)
const (
CodeDatabase = iota + 400100
)
const (
CodeNoAuth = iota + 403000
CodeAuthFailed
CodePasswordInvalid
CodeUserNotExist
)

29
conf/configuration.go Normal file
View File

@ -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
}

9
conf/mysql.go Normal file
View File

@ -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"`
}

8
conf/redis.go Normal file
View File

@ -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"`
}

35
go.mod Normal file
View File

@ -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
)

109
go.sum Normal file
View File

@ -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=

1
main.go Normal file
View File

@ -0,0 +1 @@
package main

25
types/book.go Normal file
View File

@ -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)"`
}

27
types/category.go Normal file
View File

@ -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)"`
}

27
types/user.go Normal file
View File

@ -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)"`
}

32
utils/ginhelper/gin.go Normal file
View File

@ -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)
}