Compare commits

...

No commits in common. "04d2b39b429177420123ddfe15858147c49b6724" and "8c7a1f39fd3a891d650daf1bfbb3e39f04c8c01c" have entirely different histories.

21 changed files with 800 additions and 132 deletions

239
.gitignore vendored
View File

@ -1,162 +1,137 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# ---> Go
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Test binary, built with `go test -c`
*.test
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Dependency directories (remove the comment below to include it)
# vendor/
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
# ---> Node
# Logs
logs
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Flask stuff:
instance/
.webassets-cache
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Scrapy stuff:
.scrapy
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Sphinx documentation
docs/_build/
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# PyBuilder
.pybuilder/
target/
# Coverage directory used by tools like istanbul
coverage
*.lcov
# Jupyter Notebook
.ipynb_checkpoints
# nyc test coverage
.nyc_output
# IPython
profile_default/
ipython_config.py
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# Bower dependency directory (https://bower.io/)
bower_components
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# node-waf configuration
.lock-wscript
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# Dependency directories
node_modules/
jspm_packages/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# TypeScript cache
*.tsbuildinfo
# SageMath parsed files
*.sage.py
# Optional npm cache directory
.npm
# Environments
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.env.test
.env.production
# Spyder project settings
.spyderproject
.spyproject
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Rope project settings
.ropeproject
# Next.js build output
.next
out
# mkdocs documentation
/site
# Nuxt.js build / generate output
.nuxt
dist
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# Pyre type checker
.pyre/
# vuepress build output
.vuepress/dist
# pytype static type analyzer
.pytype/
# Serverless directories
.serverless/
# Cython debug symbols
cython_debug/
# FuseBox cache
.fusebox/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

View File

@ -1,2 +1,3 @@
# biblio
书库

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