diff --git a/Makefile b/Makefile index e4b1278..de53b33 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,9 @@ generate: ## generate the static assets build: mod ## Build the binary file go build -v -o build/bin/$(PROJECT) $(MAINFILE) +swag: + swag init -g internal/app/api/router.go --exclude client --parseDependency --parseDepth 1 --output internal/docs + install: # 复制二进制文件 # 复制默认配置文件 diff --git a/go.mod b/go.mod index 20dc187..7feff38 100644 --- a/go.mod +++ b/go.mod @@ -6,20 +6,25 @@ go 1.16 //replace github.com/saltbo/gopkg => /opt/works/gopkg require ( - github.com/NetEase-Object-Storage/nos-golang-sdk v0.0.0-20191125093154-335c2b73bf6b + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible github.com/aws/aws-sdk-go v1.34.14 + github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-gonic/gin v1.6.3 + github.com/go-oauth2/oauth2/v4 v4.3.0 + github.com/google/uuid v1.1.1 github.com/saltbo/gopkg v0.0.0-20200905151036-32195ea0b27b github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.0.0 github.com/spf13/viper v1.7.1 github.com/storyicon/grbac v0.0.0-20200224041032-a0461737df7e github.com/stretchr/testify v1.6.1 + github.com/swaggo/swag v1.7.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.18 github.com/upyun/go-sdk/v3 v3.0.2 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b gorm.io/driver/mysql v1.0.3 diff --git a/go.sum b/go.sum index 06fe434..3b33318 100644 --- a/go.sum +++ b/go.sum @@ -4,105 +4,85 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NetEase-Object-Storage/nos-golang-sdk v0.0.0-20191125093154-335c2b73bf6b h1:jZIv0SILYL0wRLzAB8Ha19VDm7JnYn8t9NYbz2s2AVc= -github.com/NetEase-Object-Storage/nos-golang-sdk v0.0.0-20191125093154-335c2b73bf6b/go.mod h1:0N5CbwYI/8V1T6YOEwkgMvLmiGDNn661vLutBZQrC2c= -github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409 h1:DTQ/38ao/CfXsrK0cSAL+h4R/u0VVvfWLZEOlLwEROI= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/aliyun-oss-go-sdk v1.9.8 h1:BOflvK0Zs/zGmoabyFIzTg5c3kguktWTXEwewwbuba0= github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible h1:Ft+KeWIJxFP76LqgJbvtOA1qBIoC8vGkTV3QeCOeJC4= github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= +github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= +github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.34.14 h1:G0jUdSDSp63P0oo/N3c/ldo7s8mYW3Kh/GPIJ+oESVQ= github.com/aws/aws-sdk-go v1.34.14/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bxcodec/faker/v3 v3.1.0 h1:VCCPusvvk1My6RjWFnqVbh6EdHDqjWmrHJCHduUksV0= github.com/bxcodec/faker/v3 v3.1.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg= github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -114,22 +94,26 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6 github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0= +github.com/go-oauth2/oauth2/v4 v4.3.0 h1:vp4goUmrq1YaPzpm34FDLlZiAkIqK3LsuNTTRyTnPbo= +github.com/go-oauth2/oauth2/v4 v4.3.0/go.mod h1:+rsyi0o/ZbSfhL/3Xr/sAtL4brS+IdGj86PHVlPjE+4= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk= github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4= +github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= +github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880= +github.com/go-openapi/spec v0.19.14 h1:r4fbYFo6N4ZelmSX8G6p+cv/hZRXzcuqQIADGT1iNKM= +github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= +github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= 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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -140,102 +124,88 @@ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So= github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU= +github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= @@ -289,9 +259,7 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= 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= @@ -300,32 +268,28 @@ github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I= +github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -333,7 +297,6 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= @@ -341,13 +304,15 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -356,26 +321,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= @@ -386,13 +341,18 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -402,51 +362,36 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/qiniu/api.v7/v7 v7.8.1 h1:2+kFmiyhwiOYWkyKN8euuHe0DP3YQ3z+dAD48Tvt9gE= -github.com/qiniu/api.v7/v7 v7.8.1/go.mod h1:UykCPhMtPScoW1HOaBiOxi3FG4Ydyw+2TBP4sviG+e8= -github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= -github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY= github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/saltbo/gopkg v0.0.0-20200905151036-32195ea0b27b h1:r9EtqZBFtvhwewA2NAFsa22HHMvY5aMzT3s8H6eQ4ro= github.com/saltbo/gopkg v0.0.0-20200905151036-32195ea0b27b/go.mod h1:XlRGt9E8qVHi0yMCOoIQBmK6YTJ03BazzIo7DTWgBhU= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -456,9 +401,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -477,7 +420,6 @@ github.com/storyicon/grbac v0.0.0-20200224041032-a0461737df7e h1:v/SEKCJ/DsYAl51 github.com/storyicon/grbac v0.0.0-20200224041032-a0461737df7e/go.mod h1:bkwjmFAuumy2DsLyhHzgPWC6hEFjevSmI7iabVzBJ7A= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -489,11 +431,29 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0= github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI= -github.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM= github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= +github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E= +github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo= github.com/tencentyun/cos-go-sdk-v5 v0.7.18 h1:hiHAC24LWVIeE5JriNUmCqe2BVLtN0RRsCjyE2glpEg= github.com/tencentyun/cos-go-sdk-v5 v0.7.18/go.mod h1:wQBO5HdAkLjj2q6XQiIfDSP8DXDNrppDRw2Kp/1BODA= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E= +github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= +github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo= +github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI= +github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= +github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= +github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7XByQEJVT8+9v0W/tIY0Oo4OwrE= +github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M= +github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= +github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= +github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo= +github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao= +github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE= +github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= @@ -503,34 +463,43 @@ github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/upyun/go-sdk v1.1.1 h1:lM6mNqS2pIrXsG0a5c9JYZB93vIRARYMb7QMqav9Ztk= github.com/upyun/go-sdk/v3 v3.0.2 h1:Ke+iOipK5CT0xzMwsgJsi7faJV7ID4lAs+wrH1RH0dA= github.com/upyun/go-sdk/v3 v3.0.2/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E= -github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.14.0 h1:67bfuW9azCMwW/Jlq/C+VeihNpAuJMWkYPBig1gdi3A= +github.com/valyala/fasthttp v1.14.0/go.mod h1:ol1PCaL0dX20wC0htZ7sYCsvCYmrouYra0zHzaclZhE= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -541,7 +510,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= @@ -550,10 +519,8 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -561,17 +528,17 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -588,24 +555,29 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -624,16 +596,22 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -659,24 +637,27 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e h1:t96dS3DO8DGjawSLJL/HIdz8CycAd2v07XxqB3UPTi0= +golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -685,49 +666,50 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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/datatypes v1.0.1-0.20210119093004-1b87de7b27bd h1:L+cRnuk7hQxzkhr+8UUfYfsd4yQuHC0aL4FyaxjkMSQ= -gorm.io/datatypes v1.0.1-0.20210119093004-1b87de7b27bd/go.mod h1:EwDx5wYfLyMS6mliQ9CqB4OUhzh9INZX7MSvIJfUOUI= gorm.io/driver/mysql v1.0.3 h1:+JKBYPfn1tygR1/of/Fh2T8iwuVwzt+PEJmKaXzMQXg= gorm.io/driver/mysql v1.0.3/go.mod h1:twGxftLBlFgNVNakL7F+P/x9oYqoymG3YYT8cAfI9oI= gorm.io/driver/postgres v1.0.6 h1:9sqNcNC9PCkZ6tMzWF1cEE2PARlCONgSqRobszSTffw= @@ -745,7 +727,5 @@ gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/app/api/matter.go b/internal/app/api/matter.go index 743b101..eb23c99 100644 --- a/internal/app/api/matter.go +++ b/internal/app/api/matter.go @@ -47,6 +47,18 @@ func (rs *FileResource) findAll(c *gin.Context) { ginutil.JSONList(c, list, total) } +// create godoc +// @Tags Matters +// @Summary 创建文件 +// @Description 创建文件 +// @Accept json +// @Produce json +// @Security OAuth2Application[matter, admin] +// @Param body body bind.BodyMatter true "参数" +// @Success 200 {object} httputil.JSONResponse{data=model.User} +// @Failure 400 {object} httputil.JSONResponse +// @Failure 500 {object} httputil.JSONResponse +// @Router /matters [post] func (rs *FileResource) create(c *gin.Context) { p := new(bind.BodyMatter) if err := c.ShouldBindJSON(p); err != nil { diff --git a/internal/app/api/router.go b/internal/app/api/router.go index 382e7d5..db124c5 100644 --- a/internal/app/api/router.go +++ b/internal/app/api/router.go @@ -3,13 +3,36 @@ package api import ( "github.com/gin-gonic/gin" "github.com/saltbo/gopkg/ginutil" + + _ "github.com/saltbo/zpan/internal/docs" ) +// @title zpan +// @description zpan apis +// @version 1.0.0 + +// @BasePath /api/ +// @securitydefinitions.oauth2.application OAuth2Application +// @scope.matter Grants matter access and write +// @scope.admin Grants read and write access to administrative information +// @tokenUrl /api/tokens +// @name Authorization + +// @contact.name API Support +// @contact.url http://zpan.space +// @contact.email saltbo@foxmail.com + +// @license.name GPL 3.0 +// @license.url https://github.com/saltbo/zpan/blob/master/LICENSE + func SetupRoutes(ge *gin.Engine) { + ginutil.SetupSwagger(ge) + apiRouter := ge.Group("/api") ginutil.SetupResource(apiRouter, NewOptionResource(), NewUserResource(), + NewUserKeyResource(), NewTokenResource(), NewStorageResource(), NewFileResource(), diff --git a/internal/app/api/token.go b/internal/app/api/token.go index 7eb275c..e31ae2f 100644 --- a/internal/app/api/token.go +++ b/internal/app/api/token.go @@ -1,7 +1,13 @@ package api import ( + "log" + "github.com/gin-gonic/gin" + "github.com/go-oauth2/oauth2/v4/errors" + "github.com/go-oauth2/oauth2/v4/manage" + "github.com/go-oauth2/oauth2/v4/server" + "github.com/go-oauth2/oauth2/v4/store" "github.com/saltbo/gopkg/ginutil" _ "github.com/saltbo/gopkg/httputil" @@ -12,10 +18,28 @@ import ( type TokenResource struct { sUser *service.User + + srv *server.Server } func NewTokenResource() *TokenResource { + uk := service.NewUserKey() + uk.LoadExistClient() + manager := manage.NewManager() + manager.MapAccessGenerate(uk) + manager.MapClientStorage(uk.ClientStore()) + manager.MustTokenStorage(store.NewMemoryTokenStore()) + + srv := server.NewDefaultServer(manager) + srv.SetAllowGetAccessRequest(true) + srv.SetClientInfoHandler(server.ClientBasicHandler) + srv.SetInternalErrorHandler(func(err error) (re *errors.Response) { + log.Println("Internal Error:", err.Error()) + return + }) + return &TokenResource{ + srv: srv, sUser: service.NewUser(), } } @@ -26,7 +50,7 @@ func (rs *TokenResource) Register(router *gin.RouterGroup) { } // create godoc -// @Tags v1/Tokens +// @Tags Tokens // @Summary 登录/密码重置 // @Description 用于账户登录和申请密码重置 // @Accept json @@ -35,8 +59,14 @@ func (rs *TokenResource) Register(router *gin.RouterGroup) { // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/tokens [post] +// @Router /tokens [post] func (rs *TokenResource) create(c *gin.Context) { + // support gen oauth2 access_token + if _, _, ok := c.Request.BasicAuth(); ok { + rs.srv.HandleTokenRequest(c.Writer, c.Request) + return + } + p := new(bind.BodyToken) if err := c.ShouldBindJSON(p); err != nil { ginutil.JSONBadRequest(c, err) @@ -67,7 +97,7 @@ func (rs *TokenResource) create(c *gin.Context) { } // delete godoc -// @Tags v1/Tokens +// @Tags Tokens // @Summary 退出登录 // @Description 用户状态登出 // @Accept json @@ -75,7 +105,7 @@ func (rs *TokenResource) create(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/tokens [delete] +// @Router /tokens [delete] func (rs *TokenResource) delete(c *gin.Context) { authed.TokenCookieSet(c, "", 1) authed.RoleCookieSet(c, "", 1) diff --git a/internal/app/api/user.go b/internal/app/api/user.go index 65af98b..8c0cea2 100644 --- a/internal/app/api/user.go +++ b/internal/app/api/user.go @@ -44,16 +44,16 @@ func (rs *UserResource) Register(router *gin.RouterGroup) { } // create godoc -// @Tags v1/Users +// @Tags Users // @Summary 用户注册 // @Description 注册一个用户 // @Accept json // @Produce json -// @Param body body bind.BodyUser true "参数" +// @Param body body bind.BodyUserCreation true "参数" // @Success 200 {object} httputil.JSONResponse{data=model.User} // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users [post] +// @Router /users [post] func (rs *UserResource) create(c *gin.Context) { p := new(bind.BodyUserCreation) if err := c.ShouldBindJSON(p); err != nil { @@ -84,7 +84,7 @@ func (rs *UserResource) create(c *gin.Context) { } // patch godoc -// @Tags v1/Users +// @Tags Users // @Summary 更新一项用户信息 // @Description 用于账户激活和密码重置 // @Accept json @@ -94,7 +94,7 @@ func (rs *UserResource) create(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{email} [patch] +// @Router /users/{email} [patch] func (rs *UserResource) patch(c *gin.Context) { p := new(bind.BodyUserPatch) if err := c.ShouldBindJSON(p); err != nil { @@ -122,16 +122,17 @@ func (rs *UserResource) patch(c *gin.Context) { } // findAll godoc -// @Tags v1/Users +// @Tags Users // @Summary 用户列表 // @Description 获取用户列表信息 // @Accept json // @Produce json +// @Security OAuth2Application[admin] // @Param query query bind.QueryUser true "参数" -// @Success 200 {object} httputil.JSONResponse{data=gin.H{list=[]model.UserFormats,total=int64}} +// @Success 200 {object} httputil.JSONResponse{data=gin.H{list=[]model.User,total=int64}} // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users [get] +// @Router /users [get] func (rs *UserResource) findAll(c *gin.Context) { p := new(bind.QueryUser) if err := c.BindQuery(p); err != nil { @@ -155,7 +156,7 @@ func (rs *UserResource) findAll(c *gin.Context) { } // find godoc -// @Tags v1/Users +// @Tags Users // @Summary 用户查询 // @Description 获取一个用户的公开信息 // @Accept json @@ -164,7 +165,7 @@ func (rs *UserResource) findAll(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse{data=model.UserProfile} // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{username} [get] +// @Router /users/{username} [get] func (rs *UserResource) find(c *gin.Context) { user, exist := rs.dUser.UsernameExist(c.Param("username")) if !exist { @@ -176,17 +177,18 @@ func (rs *UserResource) find(c *gin.Context) { } // updateStorage godoc -// @Tags v1/Users +// @Tags Users // @Summary 修改某一个用户的存储空间 // @Description 修改某一个用户的存储空间 // @Accept json // @Produce json +// @Security OAuth2Application[admin] // @Param username path string true "用户名" // @Param body body bind.BodyUserPassword true "参数" // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{username}/storage [put] +// @Router /users/{username}/storage [put] func (rs *UserResource) updateStorage(c *gin.Context) { p := new(bind.BodyUserStorage) if err := c.ShouldBindJSON(p); err != nil { @@ -209,17 +211,18 @@ func (rs *UserResource) updateStorage(c *gin.Context) { } // updateStatus godoc -// @Tags v1/Users +// @Tags Users // @Summary 修改某一个用户的状态 // @Description 修改某一个用户的状态 // @Accept json // @Produce json +// @Security OAuth2Application[admin] // @Param username path string true "用户名" // @Param body body bind.BodyUserStatus true "参数" // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{username}/storage [put] +// @Router /users/{username}/status [put] func (rs *UserResource) updateStatus(c *gin.Context) { p := new(bind.BodyUserStatus) if err := c.ShouldBindJSON(p); err != nil { @@ -242,17 +245,18 @@ func (rs *UserResource) updateStatus(c *gin.Context) { } // resetPassword godoc -// @Tags v1/Users +// @Tags Users // @Summary 重置某一个用户的密码 // @Description 重置某一个用户的密码 // @Accept json // @Produce json +// @Security OAuth2Application[admin] // @Param username path string true "用户名" // @Param body body bind.BodyUserStatus true "参数" // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{username}/password [put] +// @Router /users/{username}/password [put] func (rs *UserResource) resetPassword(c *gin.Context) { p := new(bind.BodyUserPasswordReset) if err := c.ShouldBindJSON(p); err != nil { @@ -276,16 +280,17 @@ func (rs *UserResource) resetPassword(c *gin.Context) { } // remove godoc -// @Tags v1/Users +// @Tags Users // @Summary 删除某一个用户 // @Description 删除某一个用户 // @Accept json // @Produce json +// @Security OAuth2Application[admin] // @Param username path string true "用户名" // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/users/{username} [delete] +// @Router /users/{username} [delete] func (rs *UserResource) remove(c *gin.Context) { user, err := rs.dUser.FindByUsername(c.Param("username")) if err != nil { @@ -302,7 +307,7 @@ func (rs *UserResource) remove(c *gin.Context) { } // profile godoc -// @Tags v1/Users +// @Tags Users // @Summary 当前登录用户信息 // @Description 获取已登录用户的详细信息 // @Accept json @@ -310,7 +315,7 @@ func (rs *UserResource) remove(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse{data=gin.H{user=model.User,profile=model.UserProfile}} // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/user [get] +// @Router /user [get] func (rs *UserResource) userMe(c *gin.Context) { user, err := rs.dUser.Find(authed.UidGet(c)) if err != nil { @@ -322,7 +327,7 @@ func (rs *UserResource) userMe(c *gin.Context) { } // updatePassword godoc -// @Tags v1/Users +// @Tags Users // @Summary 修改登录用户密码 // @Description 修改登录用户密码 // @Accept json @@ -331,7 +336,7 @@ func (rs *UserResource) userMe(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/user/password [put] +// @Router /user/password [put] func (rs *UserResource) updatePassword(c *gin.Context) { p := new(bind.BodyUserPassword) if err := c.ShouldBindJSON(p); err != nil { @@ -349,7 +354,7 @@ func (rs *UserResource) updatePassword(c *gin.Context) { } // updateProfile godoc -// @Tags v1/Users +// @Tags Users // @Summary 修改个人信息 // @Description 更新用户的个人信息 // @Accept json @@ -358,7 +363,7 @@ func (rs *UserResource) updatePassword(c *gin.Context) { // @Success 200 {object} httputil.JSONResponse // @Failure 400 {object} httputil.JSONResponse // @Failure 500 {object} httputil.JSONResponse -// @Router /v1/user/profile [put] +// @Router /user/profile [put] func (rs *UserResource) updateProfile(c *gin.Context) { p := new(bind.BodyUserProfile) if err := c.ShouldBindJSON(p); err != nil { diff --git a/internal/app/api/user_key.go b/internal/app/api/user_key.go new file mode 100644 index 0000000..52ddfe1 --- /dev/null +++ b/internal/app/api/user_key.go @@ -0,0 +1,130 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "github.com/saltbo/gopkg/ginutil" + "github.com/saltbo/zpan/internal/app/dao" + "github.com/saltbo/zpan/internal/app/model" + "github.com/saltbo/zpan/internal/app/service" + "github.com/saltbo/zpan/internal/pkg/authed" + "github.com/saltbo/zpan/internal/pkg/bind" +) + +type UserKeyResource struct { + dUserKey *dao.UserKey + sUserKey *service.UserKey +} + +func NewUserKeyResource() *UserKeyResource { + return &UserKeyResource{ + dUserKey: dao.NewUserKey(), + sUserKey: service.NewUserKey(), + } +} + +func (rs *UserKeyResource) Register(router *gin.RouterGroup) { + router.POST("/user/keys", rs.create) // 创建一个KEY + router.GET("/user/keys/:name", rs.find) // 获取一个KEY + router.PATCH("/user/keys/:name/secret", rs.reset) // 重置KEY的secret + router.DELETE("/user/keys/:name", rs.remove) // 重置KEY的secret +} + +// create godoc +// @Tags UserKeys +// @Summary 创建秘钥 +// @Description 创建秘钥 +// @Accept json +// @Produce json +// @Param body body bind.BodyUserKeyCreation true "参数" +// @Success 200 {object} httputil.JSONResponse +// @Failure 400 {object} httputil.JSONResponse +// @Failure 500 {object} httputil.JSONResponse +// @Router /user/keys [post] +func (rs *UserKeyResource) create(c *gin.Context) { + p := new(bind.BodyUserKeyCreation) + if err := c.ShouldBind(p); err != nil { + ginutil.JSONBadRequest(c, err) + return + } + + uk := model.NewUserKey(authed.UidGet(c), p.Name) + if err := rs.sUserKey.Create(uk); err != nil { + ginutil.JSONServerError(c, err) + return + } + + ginutil.JSONData(c, uk) +} + +// find godoc +// @Tags UserKeys +// @Summary 查询秘钥 +// @Description 查询秘钥 +// @Accept json +// @Produce json +// @Param name path string true "秘钥名称" +// @Success 200 {object} httputil.JSONResponse +// @Failure 400 {object} httputil.JSONResponse +// @Failure 500 {object} httputil.JSONResponse +// @Router /user/keys/{name} [get] +func (rs *UserKeyResource) find(c *gin.Context) { + uk, err := rs.dUserKey.Find(c.Param("name")) + if err != nil { + ginutil.JSONBadRequest(c, err) + return + } + + ginutil.JSONData(c, uk) +} + +// reset godoc +// @Tags UserKeys +// @Summary 重置秘钥 +// @Description 重置秘钥 +// @Accept json +// @Produce json +// @Param name path string true "秘钥名称" +// @Success 200 {object} httputil.JSONResponse +// @Failure 400 {object} httputil.JSONResponse +// @Failure 500 {object} httputil.JSONResponse +// @Router /user/keys/{name}/secret [patch] +func (rs *UserKeyResource) reset(c *gin.Context) { + uk, err := rs.dUserKey.Find(c.Param("name")) + if err != nil { + ginutil.JSONBadRequest(c, err) + return + } + + if err := rs.sUserKey.ResetSecret(uk); err != nil { + ginutil.JSONServerError(c, err) + return + } + + ginutil.JSONData(c, uk) +} + +// remove godoc +// @Tags UserKeys +// @Summary 删除秘钥 +// @Description 删除秘钥 +// @Accept json +// @Produce json +// @Param name path string true "秘钥名称" +// @Success 200 {object} httputil.JSONResponse +// @Failure 400 {object} httputil.JSONResponse +// @Failure 500 {object} httputil.JSONResponse +// @Router /user/keys/{name} [delete] +func (rs *UserKeyResource) remove(c *gin.Context) { + uk, err := rs.dUserKey.Find(c.Param("name")) + if err != nil { + ginutil.JSONBadRequest(c, err) + return + } + + if err := rs.dUserKey.Delete(uk); err != nil { + ginutil.JSONServerError(c, err) + return + } + + ginutil.JSON(c) +} diff --git a/internal/app/dao/user_key.go b/internal/app/dao/user_key.go new file mode 100644 index 0000000..e68ef00 --- /dev/null +++ b/internal/app/dao/user_key.go @@ -0,0 +1,66 @@ +package dao + +import ( + "errors" + "fmt" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + + "github.com/saltbo/zpan/internal/app/model" +) + +type UserKey struct { +} + +func NewUserKey() *UserKey { + return &UserKey{} +} + +func (u *UserKey) Find(name string) (*model.UserKey, error) { + uk := new(model.UserKey) + if err := gdb.Where("name=?", name).First(uk).Error; errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("userKey not exist") + } + + return uk, nil +} + +func (u *UserKey) FindByClientID(clientID string) (*model.UserKey, error) { + uk := new(model.UserKey) + if err := gdb.Where("access_key=?", clientID).First(uk).Error; errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("userKey not exist") + } + + return uk, nil +} + +func (u *UserKey) FindAll(query *Query) (list []*model.UserKey, total int64, err error) { + sn := gdb.Model(&model.UserKey{}) + if len(query.Params) > 0 { + sn = sn.Where(query.SQL(), query.Params...) + } + sn.Count(&total) + err = sn.Offset(query.Offset).Limit(query.Limit).Preload(clause.Associations).Find(&list).Error + return +} + +func (u *UserKey) Create(uk *model.UserKey) (*model.UserKey, error) { + if _, err := u.Find(uk.Name); err == nil { + return nil, fmt.Errorf("userKey already exist: %s", uk.Name) + } + + if err := gdb.Create(uk).Error; err != nil { + return nil, err + } + + return uk, nil +} + +func (u *UserKey) Update(user *model.UserKey) error { + return gdb.Save(user).Error +} + +func (u *UserKey) Delete(user *model.UserKey) error { + return gdb.Delete(user).Error +} diff --git a/internal/app/model/base.go b/internal/app/model/base.go index 00f76f4..b0bb6f0 100644 --- a/internal/app/model/base.go +++ b/internal/app/model/base.go @@ -4,6 +4,7 @@ func Tables() []interface{} { return []interface{}{ new(Option), new(User), + new(UserKey), new(UserProfile), new(UserStorage), new(Storage), diff --git a/internal/app/model/user_key.go b/internal/app/model/user_key.go new file mode 100644 index 0000000..bbef785 --- /dev/null +++ b/internal/app/model/user_key.go @@ -0,0 +1,44 @@ +package model + +import ( + "fmt" + "strings" + "time" + + "github.com/saltbo/gopkg/strutil" + "gorm.io/gorm" +) + +const () + +type UserKey struct { + Id int64 `json:"id"` + Uid int64 `json:"uid" gorm:"not null"` + Name string `json:"name" gorm:"not null"` + AccessKey string `json:"access_key" gorm:"size:32;not null"` + SecretKey string `json:"secret_key" gorm:"size:64;not null"` + Created time.Time `json:"created" gorm:"autoCreateTime;not null"` + Updated time.Time `json:"updated" gorm:"autoUpdateTime;not null"` + Deleted gorm.DeletedAt `json:"-"` +} + +func NewUserKey(uid int64, name string) *UserKey { + uk := &UserKey{ + Uid: uid, + Name: name, + AccessKey: strutil.Md5Hex(fmt.Sprintf("%d:%d:%s", uid, time.Now().Unix(), strutil.RandomText(5))), + } + uk.ResetSecret() + return uk +} + +func (UserKey) TableName() string { + return "zp_user_key" +} + +func (uk *UserKey) ResetSecret() { + l := strutil.Md5HexShort(strutil.RandomText(8)) + r := strutil.Md5HexShort(strutil.RandomText(8)) + m := strutil.Md5HexShort(l + uk.AccessKey + r) + uk.SecretKey = strings.ToLower(l + m + r) +} diff --git a/internal/app/service/user_key.go b/internal/app/service/user_key.go new file mode 100644 index 0000000..6b7df01 --- /dev/null +++ b/internal/app/service/user_key.go @@ -0,0 +1,92 @@ +package service + +import ( + "context" + "encoding/base64" + "log" + "strings" + + "github.com/go-oauth2/oauth2/v4" + "github.com/go-oauth2/oauth2/v4/models" + "github.com/go-oauth2/oauth2/v4/store" + "github.com/google/uuid" + "github.com/saltbo/zpan/internal/app/dao" + "github.com/saltbo/zpan/internal/app/model" +) + +var cs = store.NewClientStore() + +type UserKey struct { + dUserKey *dao.UserKey + + sToken *Token +} + +func NewUserKey() *UserKey { + return &UserKey{ + dUserKey: dao.NewUserKey(), + + sToken: NewToken(), + } +} + +func (uk *UserKey) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (access, refresh string, err error) { + muk, err := uk.dUserKey.FindByClientID(data.Client.GetID()) + if err != nil { + return "", "", err + } + + user, err := dao.NewUser().Find(muk.Uid) + if err != nil { + return "", "", err + } + + ttl := data.TokenInfo.GetAccessCreateAt().Add(data.TokenInfo.GetAccessExpiresIn()).Unix() + access, err = uk.sToken.Create(user.IDString(), int(ttl), user.Roles) + if err != nil { + return + } + + if isGenRefresh { + t := uuid.NewSHA1(uuid.Must(uuid.NewRandom()), []byte(access)).String() + refresh = base64.URLEncoding.EncodeToString([]byte(t)) + refresh = strings.ToUpper(strings.TrimRight(refresh, "=")) + } + return +} + +func (uk *UserKey) ClientStore() *store.ClientStore { + return cs +} + +func (uk *UserKey) Create(muk *model.UserKey) error { + if _, err := uk.dUserKey.Create(muk); err != nil { + return err + } + + return uk.ClientStore().Set(muk.AccessKey, &models.Client{ID: muk.AccessKey, Secret: muk.SecretKey}) +} + +func (uk *UserKey) ResetSecret(muk *model.UserKey) error { + muk.ResetSecret() + if err := uk.dUserKey.Update(muk); err != nil { + return err + } + + return uk.ClientStore().Set(muk.AccessKey, &models.Client{ID: muk.AccessKey, Secret: muk.SecretKey}) +} + +func (uk *UserKey) LoadExistClient() { + list, _, err := uk.dUserKey.FindAll(dao.NewQuery()) + if err != nil { + log.Println(err) + return + } + + for _, muk := range list { + cli := &models.Client{ID: muk.AccessKey, Secret: muk.SecretKey} + if err := uk.ClientStore().Set(muk.AccessKey, cli); err != nil { + log.Println(err) + } + } +} diff --git a/internal/docs/docs.go b/internal/docs/docs.go new file mode 100644 index 0000000..60eb1c5 --- /dev/null +++ b/internal/docs/docs.go @@ -0,0 +1,1317 @@ +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag + +package docs + +import ( + "bytes" + "encoding/json" + "strings" + + "github.com/alecthomas/template" + "github.com/swaggo/swag" +) + +var doc = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{.Description}}", + "title": "{{.Title}}", + "contact": { + "name": "API Support", + "url": "http://zpan.space", + "email": "saltbo@foxmail.com" + }, + "license": { + "name": "GPL 3.0", + "url": "https://github.com/saltbo/zpan/blob/master/LICENSE" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/matters": { + "post": { + "security": [ + { + "OAuth2Application": [ + "matter", + "admin" + ] + } + ], + "description": "创建文件", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Matters" + ], + "summary": "创建文件", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyMatter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/tokens": { + "post": { + "description": "用于账户登录和申请密码重置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Tokens" + ], + "summary": "登录/密码重置", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "description": "用户状态登出", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Tokens" + ], + "summary": "退出登录", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user": { + "get": { + "description": "获取已登录用户的详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "当前登录用户信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/gin.H" + }, + { + "type": "object", + "properties": { + "profile": { + "$ref": "#/definitions/model.UserProfile" + }, + "user": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys": { + "post": { + "description": "创建秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "创建秘钥", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserKeyCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys/{name}": { + "get": { + "description": "查询秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "查询秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "description": "删除秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "删除秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys/{name}/secret": { + "patch": { + "description": "重置秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "重置秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/password": { + "put": { + "description": "修改登录用户密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改登录用户密码", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/profile": { + "put": { + "description": "更新用户的个人信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改个人信息", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserProfile" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users": { + "get": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "获取用户列表信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户列表", + "parameters": [ + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "integer", + "name": "pageNo", + "in": "query" + }, + { + "type": "integer", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/gin.H" + }, + { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/model.User" + } + }, + "total": { + "type": "integer" + } + } + } + ] + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "post": { + "description": "注册一个用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户注册", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{email}": { + "patch": { + "description": "用于账户激活和密码重置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "更新一项用户信息", + "parameters": [ + { + "type": "string", + "description": "邮箱", + "name": "email", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}": { + "get": { + "description": "获取一个用户的公开信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户查询", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserProfile" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "删除某一个用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "删除某一个用户", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/password": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "重置某一个用户的密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "重置某一个用户的密码", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/status": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "修改某一个用户的状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改某一个用户的状态", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/storage": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "修改某一个用户的存储空间", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改某一个用户的存储空间", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + } + }, + "definitions": { + "bind.BodyMatter": { + "type": "object", + "required": [ + "name", + "sid" + ], + "properties": { + "dir": { + "type": "string" + }, + "is_dir": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "sid": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "bind.BodyToken": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "captcha": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "bind.BodyUserCreation": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "roles": { + "type": "string" + }, + "storage_max": { + "type": "integer" + }, + "ticket": { + "type": "string" + } + } + }, + "bind.BodyUserKeyCreation": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "bind.BodyUserPassword": { + "type": "object", + "required": [ + "new_password", + "old_password" + ], + "properties": { + "new_password": { + "type": "string" + }, + "old_password": { + "type": "string" + } + } + }, + "bind.BodyUserPatch": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "activated": { + "type": "boolean" + }, + "password": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "bind.BodyUserProfile": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "company": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "location": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "bind.BodyUserStatus": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "integer" + } + } + }, + "gin.H": { + "type": "object", + "additionalProperties": true + }, + "httputil.JSONResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + }, + "model.User": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "profile": { + "$ref": "#/definitions/model.UserProfile" + }, + "role": { + "type": "string" + }, + "status": { + "type": "string" + }, + "storage": { + "$ref": "#/definitions/model.UserStorage" + }, + "ticket": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "model.UserProfile": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "company": { + "type": "string" + }, + "created": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "locale": { + "type": "string" + }, + "location": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "uid": { + "type": "integer" + }, + "updated": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "model.UserStorage": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max": { + "type": "integer" + }, + "uid": { + "type": "integer" + }, + "updated": { + "type": "string" + }, + "used": { + "type": "integer" + } + } + } + }, + "securityDefinitions": { + "OAuth2Application": { + "type": "oauth2", + "flow": "application", + "authorizationUrl": "", + "tokenUrl": "/api/tokens", + "scopes": { + "admin": " Grants read and write access to administrative information", + "matter": " Grants matter access and write" + } + } + } +}` + +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = swaggerInfo{ + Version: "1.0.0", + Host: "", + BasePath: "/api/", + Schemes: []string{}, + Title: "zpan", + Description: "zpan apis", +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() +} + +func init() { + swag.Register(swag.Name, &s{}) +} diff --git a/internal/docs/swagger.json b/internal/docs/swagger.json new file mode 100644 index 0000000..62ef7b1 --- /dev/null +++ b/internal/docs/swagger.json @@ -0,0 +1,1254 @@ +{ + "swagger": "2.0", + "info": { + "description": "zpan apis", + "title": "zpan", + "contact": { + "name": "API Support", + "url": "http://zpan.space", + "email": "saltbo@foxmail.com" + }, + "license": { + "name": "GPL 3.0", + "url": "https://github.com/saltbo/zpan/blob/master/LICENSE" + }, + "version": "1.0.0" + }, + "basePath": "/api/", + "paths": { + "/matters": { + "post": { + "security": [ + { + "OAuth2Application": [ + "matter", + "admin" + ] + } + ], + "description": "创建文件", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Matters" + ], + "summary": "创建文件", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyMatter" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/tokens": { + "post": { + "description": "用于账户登录和申请密码重置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Tokens" + ], + "summary": "登录/密码重置", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "description": "用户状态登出", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Tokens" + ], + "summary": "退出登录", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user": { + "get": { + "description": "获取已登录用户的详细信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "当前登录用户信息", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/gin.H" + }, + { + "type": "object", + "properties": { + "profile": { + "$ref": "#/definitions/model.UserProfile" + }, + "user": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys": { + "post": { + "description": "创建秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "创建秘钥", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserKeyCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys/{name}": { + "get": { + "description": "查询秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "查询秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "description": "删除秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "删除秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/keys/{name}/secret": { + "patch": { + "description": "重置秘钥", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "UserKeys" + ], + "summary": "重置秘钥", + "parameters": [ + { + "type": "string", + "description": "秘钥名称", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/password": { + "put": { + "description": "修改登录用户密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改登录用户密码", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/user/profile": { + "put": { + "description": "更新用户的个人信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改个人信息", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserProfile" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users": { + "get": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "获取用户列表信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户列表", + "parameters": [ + { + "type": "string", + "name": "email", + "in": "query" + }, + { + "type": "integer", + "name": "pageNo", + "in": "query" + }, + { + "type": "integer", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "allOf": [ + { + "$ref": "#/definitions/gin.H" + }, + { + "type": "object", + "properties": { + "list": { + "type": "array", + "items": { + "$ref": "#/definitions/model.User" + } + }, + "total": { + "type": "integer" + } + } + } + ] + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "post": { + "description": "注册一个用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户注册", + "parameters": [ + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserCreation" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.User" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{email}": { + "patch": { + "description": "用于账户激活和密码重置", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "更新一项用户信息", + "parameters": [ + { + "type": "string", + "description": "邮箱", + "name": "email", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPatch" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}": { + "get": { + "description": "获取一个用户的公开信息", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "用户查询", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/httputil.JSONResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/model.UserProfile" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + }, + "delete": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "删除某一个用户", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "删除某一个用户", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/password": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "重置某一个用户的密码", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "重置某一个用户的密码", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/status": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "修改某一个用户的状态", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改某一个用户的状态", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserStatus" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + }, + "/users/{username}/storage": { + "put": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "description": "修改某一个用户的存储空间", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "修改某一个用户的存储空间", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "path", + "required": true + }, + { + "description": "参数", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/bind.BodyUserPassword" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/httputil.JSONResponse" + } + } + } + } + } + }, + "definitions": { + "bind.BodyMatter": { + "type": "object", + "required": [ + "name", + "sid" + ], + "properties": { + "dir": { + "type": "string" + }, + "is_dir": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "sid": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "bind.BodyToken": { + "type": "object", + "required": [ + "email" + ], + "properties": { + "captcha": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "bind.BodyUserCreation": { + "type": "object", + "required": [ + "email", + "password" + ], + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "roles": { + "type": "string" + }, + "storage_max": { + "type": "integer" + }, + "ticket": { + "type": "string" + } + } + }, + "bind.BodyUserKeyCreation": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "bind.BodyUserPassword": { + "type": "object", + "required": [ + "new_password", + "old_password" + ], + "properties": { + "new_password": { + "type": "string" + }, + "old_password": { + "type": "string" + } + } + }, + "bind.BodyUserPatch": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "activated": { + "type": "boolean" + }, + "password": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "bind.BodyUserProfile": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "company": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "location": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "bind.BodyUserStatus": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "integer" + } + } + }, + "gin.H": { + "type": "object", + "additionalProperties": true + }, + "httputil.JSONResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "type": "object" + }, + "msg": { + "type": "string" + } + } + }, + "model.User": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "email": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "profile": { + "$ref": "#/definitions/model.UserProfile" + }, + "role": { + "type": "string" + }, + "status": { + "type": "string" + }, + "storage": { + "$ref": "#/definitions/model.UserStorage" + }, + "ticket": { + "type": "string" + }, + "updated": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "model.UserProfile": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "company": { + "type": "string" + }, + "created": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "locale": { + "type": "string" + }, + "location": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "uid": { + "type": "integer" + }, + "updated": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "model.UserStorage": { + "type": "object", + "properties": { + "created": { + "type": "string" + }, + "id": { + "type": "integer" + }, + "max": { + "type": "integer" + }, + "uid": { + "type": "integer" + }, + "updated": { + "type": "string" + }, + "used": { + "type": "integer" + } + } + } + }, + "securityDefinitions": { + "OAuth2Application": { + "type": "oauth2", + "flow": "application", + "authorizationUrl": "", + "tokenUrl": "/api/tokens", + "scopes": { + "admin": " Grants read and write access to administrative information", + "matter": " Grants matter access and write" + } + } + } +} \ No newline at end of file diff --git a/internal/docs/swagger.yaml b/internal/docs/swagger.yaml new file mode 100644 index 0000000..dc6c006 --- /dev/null +++ b/internal/docs/swagger.yaml @@ -0,0 +1,796 @@ +basePath: /api/ +definitions: + bind.BodyMatter: + properties: + dir: + type: string + is_dir: + type: boolean + name: + type: string + sid: + type: integer + size: + type: integer + type: + type: string + required: + - name + - sid + type: object + bind.BodyToken: + properties: + captcha: + type: string + email: + type: string + password: + type: string + required: + - email + type: object + bind.BodyUserCreation: + properties: + email: + type: string + password: + type: string + roles: + type: string + storage_max: + type: integer + ticket: + type: string + required: + - email + - password + type: object + bind.BodyUserKeyCreation: + properties: + name: + type: string + required: + - name + type: object + bind.BodyUserPassword: + properties: + new_password: + type: string + old_password: + type: string + required: + - new_password + - old_password + type: object + bind.BodyUserPatch: + properties: + activated: + type: boolean + password: + type: string + token: + type: string + required: + - token + type: object + bind.BodyUserProfile: + properties: + avatar: + type: string + bio: + type: string + company: + type: string + locale: + type: string + location: + type: string + nickname: + type: string + url: + type: string + type: object + bind.BodyUserStatus: + properties: + status: + type: integer + required: + - status + type: object + gin.H: + additionalProperties: true + type: object + httputil.JSONResponse: + properties: + code: + type: integer + data: + type: object + msg: + type: string + type: object + model.User: + properties: + created: + type: string + email: + type: string + id: + type: integer + profile: + $ref: '#/definitions/model.UserProfile' + role: + type: string + status: + type: string + storage: + $ref: '#/definitions/model.UserStorage' + ticket: + type: string + updated: + type: string + username: + type: string + type: object + model.UserProfile: + properties: + avatar: + type: string + bio: + type: string + company: + type: string + created: + type: string + id: + type: integer + locale: + type: string + location: + type: string + nickname: + type: string + uid: + type: integer + updated: + type: string + url: + type: string + type: object + model.UserStorage: + properties: + created: + type: string + id: + type: integer + max: + type: integer + uid: + type: integer + updated: + type: string + used: + type: integer + type: object +info: + contact: + email: saltbo@foxmail.com + name: API Support + url: http://zpan.space + description: zpan apis + license: + name: GPL 3.0 + url: https://github.com/saltbo/zpan/blob/master/LICENSE + title: zpan + version: 1.0.0 +paths: + /matters: + post: + consumes: + - application/json + description: 创建文件 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyMatter' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/httputil.JSONResponse' + - properties: + data: + $ref: '#/definitions/model.User' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - matter + - admin + summary: 创建文件 + tags: + - Matters + /tokens: + delete: + consumes: + - application/json + description: 用户状态登出 + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 退出登录 + tags: + - Tokens + post: + consumes: + - application/json + description: 用于账户登录和申请密码重置 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyToken' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 登录/密码重置 + tags: + - Tokens + /user: + get: + consumes: + - application/json + description: 获取已登录用户的详细信息 + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/httputil.JSONResponse' + - properties: + data: + allOf: + - $ref: '#/definitions/gin.H' + - properties: + profile: + $ref: '#/definitions/model.UserProfile' + user: + $ref: '#/definitions/model.User' + type: object + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 当前登录用户信息 + tags: + - Users + /user/keys: + post: + consumes: + - application/json + description: 创建秘钥 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserKeyCreation' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 创建秘钥 + tags: + - UserKeys + /user/keys/{name}: + delete: + consumes: + - application/json + description: 删除秘钥 + parameters: + - description: 秘钥名称 + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 删除秘钥 + tags: + - UserKeys + get: + consumes: + - application/json + description: 查询秘钥 + parameters: + - description: 秘钥名称 + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 查询秘钥 + tags: + - UserKeys + /user/keys/{name}/secret: + patch: + consumes: + - application/json + description: 重置秘钥 + parameters: + - description: 秘钥名称 + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 重置秘钥 + tags: + - UserKeys + /user/password: + put: + consumes: + - application/json + description: 修改登录用户密码 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserPassword' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 修改登录用户密码 + tags: + - Users + /user/profile: + put: + consumes: + - application/json + description: 更新用户的个人信息 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserProfile' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 修改个人信息 + tags: + - Users + /users: + get: + consumes: + - application/json + description: 获取用户列表信息 + parameters: + - in: query + name: email + type: string + - in: query + name: pageNo + type: integer + - in: query + name: pageSize + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/httputil.JSONResponse' + - properties: + data: + allOf: + - $ref: '#/definitions/gin.H' + - properties: + list: + items: + $ref: '#/definitions/model.User' + type: array + total: + type: integer + type: object + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - admin + summary: 用户列表 + tags: + - Users + post: + consumes: + - application/json + description: 注册一个用户 + parameters: + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserCreation' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/httputil.JSONResponse' + - properties: + data: + $ref: '#/definitions/model.User' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 用户注册 + tags: + - Users + /users/{email}: + patch: + consumes: + - application/json + description: 用于账户激活和密码重置 + parameters: + - description: 邮箱 + in: path + name: email + required: true + type: string + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserPatch' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 更新一项用户信息 + tags: + - Users + /users/{username}: + delete: + consumes: + - application/json + description: 删除某一个用户 + parameters: + - description: 用户名 + in: path + name: username + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - admin + summary: 删除某一个用户 + tags: + - Users + get: + consumes: + - application/json + description: 获取一个用户的公开信息 + parameters: + - description: 用户名 + in: path + name: username + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/httputil.JSONResponse' + - properties: + data: + $ref: '#/definitions/model.UserProfile' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + summary: 用户查询 + tags: + - Users + /users/{username}/password: + put: + consumes: + - application/json + description: 重置某一个用户的密码 + parameters: + - description: 用户名 + in: path + name: username + required: true + type: string + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserStatus' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - admin + summary: 重置某一个用户的密码 + tags: + - Users + /users/{username}/status: + put: + consumes: + - application/json + description: 修改某一个用户的状态 + parameters: + - description: 用户名 + in: path + name: username + required: true + type: string + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserStatus' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - admin + summary: 修改某一个用户的状态 + tags: + - Users + /users/{username}/storage: + put: + consumes: + - application/json + description: 修改某一个用户的存储空间 + parameters: + - description: 用户名 + in: path + name: username + required: true + type: string + - description: 参数 + in: body + name: body + required: true + schema: + $ref: '#/definitions/bind.BodyUserPassword' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/httputil.JSONResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/httputil.JSONResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/httputil.JSONResponse' + security: + - OAuth2Application: + - admin + summary: 修改某一个用户的存储空间 + tags: + - Users +securityDefinitions: + OAuth2Application: + authorizationUrl: "" + flow: application + scopes: + admin: ' Grants read and write access to administrative information' + matter: ' Grants matter access and write' + tokenUrl: /api/tokens + type: oauth2 +swagger: "2.0" diff --git a/internal/pkg/bind/user.go b/internal/pkg/bind/user.go index 37bb5ab..68a9d0c 100644 --- a/internal/pkg/bind/user.go +++ b/internal/pkg/bind/user.go @@ -45,3 +45,7 @@ type BodyUserStatus struct { type BodyUserPasswordReset struct { Password string `json:"password" binding:"required"` } + +type BodyUserKeyCreation struct { + Name string `json:"name" binding:"required"` +} diff --git a/internal/pkg/fakefs/file_test.go b/internal/pkg/fakefs/file_test.go index 8c5d0b6..668eeb2 100644 --- a/internal/pkg/fakefs/file_test.go +++ b/internal/pkg/fakefs/file_test.go @@ -34,6 +34,10 @@ func init() { var fs = New() +func init() { + fs.Start() +} + func TestPreSignPutURL(t *testing.T) { bf := &bind.BodyMatter{ Name: "test.txt", diff --git a/internal/pkg/middleware/auth.go b/internal/pkg/middleware/auth.go index 25dc182..bf917cb 100644 --- a/internal/pkg/middleware/auth.go +++ b/internal/pkg/middleware/auth.go @@ -4,6 +4,7 @@ import ( _ "embed" "fmt" "log" + "strings" "github.com/gin-gonic/gin" "github.com/saltbo/gopkg/ginutil" @@ -34,7 +35,7 @@ func LoginAuthWithRoles() gin.HandlerFunc { } return func(c *gin.Context) { - rc, err := token2Roles(authed.TokenCookieGet(c)) + rc, err := token2Roles(c) if err != nil { ginutil.JSONUnauthorized(c, err) return @@ -61,10 +62,19 @@ func LoginAuthWithRoles() gin.HandlerFunc { } } -func token2Roles(token string) (*service.RoleClaims, error) { - if token == "" { +func token2Roles(c *gin.Context) (*service.RoleClaims, error) { + const basicPrefix = "Basic " + const BearerPrefix = "Bearer " + cookieAuth := authed.TokenCookieGet(c) + headerAuth := c.GetHeader("Authorization") + if (cookieAuth == "" && headerAuth == "") || strings.HasPrefix(headerAuth, basicPrefix) { return service.NewRoleClaims("anonymous", 3600, []string{"guest"}), nil } - return service.NewToken().Verify(token) + authToken := strings.TrimPrefix(headerAuth, BearerPrefix) + if authToken == "" { + authToken = cookieAuth + } + + return service.NewToken().Verify(authToken) } diff --git a/internal/pkg/provider/provider_nos.go b/internal/pkg/provider/provider_nos.go index 44a0dfe..6bd020f 100644 --- a/internal/pkg/provider/provider_nos.go +++ b/internal/pkg/provider/provider_nos.go @@ -1,8 +1,8 @@ package provider import ( - "github.com/NetEase-Object-Storage/nos-golang-sdk/config" - "github.com/NetEase-Object-Storage/nos-golang-sdk/nosclient" + "github.com/saltbo/zpan/pkg/nos/config" + "github.com/saltbo/zpan/pkg/nos/nosclient" ) // NOSProvider 网易云 diff --git a/pkg/nos/auth/nosauth.go b/pkg/nos/auth/nosauth.go new file mode 100644 index 0000000..d273982 --- /dev/null +++ b/pkg/nos/auth/nosauth.go @@ -0,0 +1,89 @@ +package auth + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "net/http" + "sort" + "strings" +) + +var subResources map[string]bool = map[string]bool{ + "acl": true, + "location": true, + "versioning": true, + "versions": true, + "versionId": true, + "uploadId": true, + "uploads": true, + "partNumber": true, + "delete": true, + "deduplication": true, +} + +func SignRequest(request *http.Request, publicKey string, secretKey string, + bucket string, encodedObject string) string { + + stringToSign := "" + stringToSign += (request.Method + "\n") + stringToSign += (request.Header.Get("Content-MD5") + "\n") + stringToSign += (request.Header.Get("Content-Type") + "\n") + stringToSign += (request.Header.Get("Date") + "\n") + + var headerKeys sort.StringSlice + for origKey, _ := range request.Header { + key := strings.ToLower(origKey) + if strings.HasPrefix(key, "x-nos-") { + headerKeys = append(headerKeys, origKey) + } + } + + headerKeys.Sort() + + for i := 0; i < headerKeys.Len(); i++ { + key := strings.ToLower(headerKeys[i]) + stringToSign += (key + ":" + request.Header.Get(headerKeys[i]) + "\n") + } + + stringToSign += (getResource(bucket, encodedObject)) + + request.ParseForm() + + var keys sort.StringSlice + for key := range request.Form { + if _, ok := subResources[key]; ok { + keys = append(keys, key) + } + } + keys.Sort() + + for i := 0; i < keys.Len(); i++ { + if i == 0 { + stringToSign += "?" + } + stringToSign += keys[i] + if val := request.Form[keys[i]]; val[0] != "" { + stringToSign += ("=" + val[0]) + } + + if i < keys.Len()-1 { + stringToSign += "&" + } + } + key := []byte(secretKey) + h := hmac.New(sha256.New, key) + h.Write([]byte(stringToSign)) + return "NOS " + publicKey + ":" + base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func getResource(bucket string, encodedObject string) string { + resource := "/" + if bucket != "" { + resource += bucket + "/" + } + if encodedObject != "" { + resource += encodedObject + } + return resource +} diff --git a/pkg/nos/config/config.go b/pkg/nos/config/config.go new file mode 100644 index 0000000..9c79ddf --- /dev/null +++ b/pkg/nos/config/config.go @@ -0,0 +1,76 @@ +package config + +import ( + "github.com/saltbo/zpan/pkg/nos/logger" + "github.com/saltbo/zpan/pkg/nos/noserror" + "github.com/saltbo/zpan/pkg/nos/utils" +) + +type Config struct { + Endpoint string + AccessKey string + SecretKey string + + NosServiceConnectTimeout int + NosServiceReadWriteTimeout int + NosServiceMaxIdleConnection int + + LogLevel *logger.LogLevelType + + Logger logger.Logger + + IsSubDomain *bool +} + +func (conf *Config) SetIsSubDomain(isSubDomain bool) error { + conf.IsSubDomain = &isSubDomain + return nil +} + +func (conf *Config) GetIsSubDomain() bool { + if conf.IsSubDomain == nil { + return true + } else { + return *conf.IsSubDomain + } +} + +func (conf *Config) Check() error { + if conf.Endpoint == "" { + return utils.ProcessClientError(noserror.ERROR_CODE_CFG_ENDPOINT, "", "", "") + } + + if conf.NosServiceConnectTimeout < 0 { + return utils.ProcessClientError(noserror.ERROR_CODE_CFG_CONNECT_TIMEOUT, "", "", "") + } + + if conf.NosServiceReadWriteTimeout < 0 { + return utils.ProcessClientError(noserror.ERROR_CODE_CFG_READWRITE_TIMEOUT, "", "", "") + } + + if conf.NosServiceMaxIdleConnection < 0 { + return utils.ProcessClientError(noserror.ERROR_CODE_CFG_MAXIDLECONNECT, "", "", "") + } + + if conf.NosServiceConnectTimeout == 0 { + conf.NosServiceConnectTimeout = 30 + } + + if conf.NosServiceReadWriteTimeout == 0 { + conf.NosServiceReadWriteTimeout = 60 + } + + if conf.NosServiceMaxIdleConnection == 0 { + conf.NosServiceMaxIdleConnection = 60 + } + + if conf.Logger == nil { + conf.Logger = logger.NewDefaultLogger() + } + + if conf.LogLevel == nil { + conf.LogLevel = logger.LogLevel(logger.DEBUG) + } + + return nil +} diff --git a/pkg/nos/httpclient/httpclient.go b/pkg/nos/httpclient/httpclient.go new file mode 100644 index 0000000..89e018b --- /dev/null +++ b/pkg/nos/httpclient/httpclient.go @@ -0,0 +1,237 @@ +/* +Provides an HTTP Transport that implements the `RoundTripper` interface and +can be used as a built in replacement for the standard library's, providing: + + * connection timeouts + * request timeouts + +This is a thin wrapper around `http.Transport` that sets dial timeouts and uses +Go's internal timer scheduler to call the Go 1.1+ `CancelRequest()` API. +*/ +package httpclient + +import ( + "crypto/tls" + "errors" + "io" + "net" + "net/http" + "net/url" + "sync" + "time" +) + +// returns the current version of the package +func Version() string { + return "0.4.1" +} + +// Transport implements the RoundTripper interface and can be used as a replacement +// for Go's built in http.Transport implementing end-to-end request timeouts. +// +// transport := &httpclient.Transport{ +// ConnectTimeout: 1*time.Second, +// ResponseHeaderTimeout: 5*time.Second, +// RequestTimeout: 10*time.Second, +// } +// defer transport.Close() +// +// client := &http.Client{Transport: transport} +// req, _ := http.NewRequest("GET", "http://127.0.0.1/test", nil) +// resp, err := client.Do(req) +// if err != nil { +// return err +// } +// defer resp.Body.Close() +// +type Transport struct { + // Proxy specifies a function to return a proxy for a given + // *http.Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *url.URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // Dial specifies the dial function for creating TCP + // connections. This will override the Transport's ConnectTimeout and + // ReadWriteTimeout settings. + // If Dial is nil, a dialer is generated on demand matching the Transport's + // options. + Dial func(network, addr string) (net.Conn, error) + + // TLSClientConfig specifies the TLS configuration to use with + // tls.Client. If nil, the default configuration is used. + TLSClientConfig *tls.Config + + // DisableKeepAlives, if true, prevents re-use of TCP connections + // between different HTTP requests. + DisableKeepAlives bool + + // DisableCompression, if true, prevents the Transport from + // requesting compression with an "Accept-Encoding: gzip" + // request header when the Request contains no existing + // Accept-Encoding value. If the Transport requests gzip on + // its own and gets a gzipped response, it's transparently + // decoded in the Response.Body. However, if the user + // explicitly requested gzip it is not automatically + // uncompressed. + DisableCompression bool + + // MaxIdleConnsPerHost, if non-zero, controls the maximum idle + // (keep-alive) to keep per-host. If zero, + // http.DefaultMaxIdleConnsPerHost is used. + MaxIdleConnsPerHost int + + // ConnectTimeout, if non-zero, is the maximum amount of time a dial will wait for + // a connect to complete. + ConnectTimeout time.Duration + + // ResponseHeaderTimeout, if non-zero, specifies the amount of + // time to wait for a server's response headers after fully + // writing the request (including its body, if any). This + // time does not include the time to read the response body. + ResponseHeaderTimeout time.Duration + + // RequestTimeout, if non-zero, specifies the amount of time for the entire + // request to complete (including all of the above timeouts + entire response body). + // This should never be less than the sum total of the above two timeouts. + RequestTimeout time.Duration + + // ReadWriteTimeout, if non-zero, will set a deadline for every Read and + // Write operation on the request connection. + ReadWriteTimeout time.Duration + + // TCPWriteBufferSize, the size of the operating system's write + // buffer associated with the connection. + TCPWriteBufferSize int + + // TCPReadBuffserSize, the size of the operating system's read + // buffer associated with the connection. + TCPReadBufferSize int + + starter sync.Once + transport *http.Transport +} + +// Close cleans up the Transport, currently a no-op +func (t *Transport) Close() error { + return nil +} + +func (t *Transport) lazyStart() { + if t.Dial == nil { + t.Dial = func(netw, addr string) (net.Conn, error) { + c, err := net.DialTimeout(netw, addr, t.ConnectTimeout) + if err != nil { + return nil, err + } + + if t.TCPReadBufferSize != 0 || t.TCPWriteBufferSize != 0 { + if tcpCon, ok := c.(*net.TCPConn); ok { + if t.TCPWriteBufferSize != 0 { + if err = tcpCon.SetWriteBuffer(t.TCPWriteBufferSize); err != nil { + return nil, err + } + } + if t.TCPReadBufferSize != 0 { + if err = tcpCon.SetReadBuffer(t.TCPReadBufferSize); err != nil { + return nil, err + } + } + } else { + err = errors.New("Not Tcp Connection") + return nil, err + } + } + + if t.ReadWriteTimeout > 0 { + timeoutConn := &rwTimeoutConn{ + TCPConn: c.(*net.TCPConn), + rwTimeout: t.ReadWriteTimeout, + } + return timeoutConn, nil + } + return c, nil + } + } + + t.transport = &http.Transport{ + Dial: t.Dial, + Proxy: t.Proxy, + TLSClientConfig: t.TLSClientConfig, + DisableKeepAlives: t.DisableKeepAlives, + DisableCompression: t.DisableCompression, + MaxIdleConnsPerHost: t.MaxIdleConnsPerHost, + ResponseHeaderTimeout: t.ResponseHeaderTimeout, + } +} + +func (t *Transport) CancelRequest(req *http.Request) { + t.starter.Do(t.lazyStart) + + t.transport.CancelRequest(req) +} + +func (t *Transport) CloseIdleConnections() { + t.starter.Do(t.lazyStart) + + t.transport.CloseIdleConnections() +} + +func (t *Transport) RegisterProtocol(scheme string, rt http.RoundTripper) { + t.starter.Do(t.lazyStart) + + t.transport.RegisterProtocol(scheme, rt) +} + +func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + t.starter.Do(t.lazyStart) + + if t.RequestTimeout > 0 { + timer := time.AfterFunc(t.RequestTimeout, func() { + t.transport.CancelRequest(req) + }) + + resp, err = t.transport.RoundTrip(req) + if err != nil { + timer.Stop() + } else { + resp.Body = &bodyCloseInterceptor{ReadCloser: resp.Body, timer: timer} + } + } else { + resp, err = t.transport.RoundTrip(req) + } + + return +} + +type bodyCloseInterceptor struct { + io.ReadCloser + timer *time.Timer +} + +func (bci *bodyCloseInterceptor) Close() error { + bci.timer.Stop() + return bci.ReadCloser.Close() +} + +// A net.Conn that sets a deadline for every Read or Write operation +type rwTimeoutConn struct { + *net.TCPConn + rwTimeout time.Duration +} + +func (c *rwTimeoutConn) Read(b []byte) (int, error) { + err := c.TCPConn.SetDeadline(time.Now().Add(c.rwTimeout)) + if err != nil { + return 0, err + } + return c.TCPConn.Read(b) +} + +func (c *rwTimeoutConn) Write(b []byte) (int, error) { + err := c.TCPConn.SetDeadline(time.Now().Add(c.rwTimeout)) + if err != nil { + return 0, err + } + return c.TCPConn.Write(b) +} diff --git a/pkg/nos/logger/logger.go b/pkg/nos/logger/logger.go new file mode 100644 index 0000000..c8ccb50 --- /dev/null +++ b/pkg/nos/logger/logger.go @@ -0,0 +1,235 @@ +package logger + +import ( + "log" + "os" +) + +// These are the integer logging levels used by the logger +type Level int + +const ( + LOGOFF LogLevelType = iota + FINE + DEBUG + TRACE + INFO + WARNING + ERROR + CRITICAL +) + +const ( + // LogOff states that no logging should be performed by the SDK. This is the + // default state of the SDK, and should be use to disable all logging. + LogOff LogLevelType = iota * 0x1000 + + // LogDebug state that debug output should be logged by the SDK. This should + // be used to inspect request made and responses received. + LogDebug +) + +// Debug Logging Sub Levels +const ( + // LogDebugWithSigning states that the SDK should log request signing and + // presigning events. This should be used to log the signing details of + // requests for debugging. Will also enable LogDebug. + LogDebugWithSigning LogLevelType = LogDebug | (1 << iota) + + // LogDebugWithHTTPBody states the SDK should log HTTP request and response + // HTTP bodys in addition to the headers and path. This should be used to + // see the body content of requests and responses made while using the SDK + // Will also enable LogDebug. + LogDebugWithHTTPBody + + // LogDebugWithRequestRetries states the SDK should log when service requests will + // be retried. This should be used to log when you want to log when service + // requests are being retried. Will also enable LogDebug. + LogDebugWithRequestRetries + + // LogDebugWithRequestErrors states the SDK should log when service requests fail + // to build, send, validate, or unmarshal. + LogDebugWithRequestErrors +) + +// Logging level strings +var ( + levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} +) + +func (l Level) String() string { + if l < 0 || int(l) > len(levelStrings) { + return "UNKNOWN" + } + return levelStrings[int(l)] +} + +/****** Variables ******/ +var ( + // LogBufferLength specifies how many log messages a particular log4go + // logger can buffer at a time before writing them. + LogBufferLength = 32 +) + +// A LogLevelType defines the level logging should be performed at. Used to instruct +// the SDK which statements should be logged. +type LogLevelType uint + +// LogLevel returns the pointer to a LogLevel. Should be used to workaround +// not being able to take the address of a non-composite literal. +func LogLevel(l LogLevelType) *LogLevelType { + return &l +} + +// Value returns the LogLevel value or the default value LogOff if the LogLevel +// is nil. Safe to use on nil value LogLevelTypes. +func (l *LogLevelType) Value() LogLevelType { + if l != nil { + return *l + } + return LogOff +} + +// Matches returns true if the v LogLevel is enabled by this LogLevel. Should be +// used with logging sub levels. Is safe to use on nil value LogLevelTypes. If +// LogLevel is nill, will default to LogOff comparison. +func (l *LogLevelType) Matches(v LogLevelType) bool { + c := l.Value() + return c&v == v +} + +// AtLeast returns true if this LogLevel is at least high enough to satisfies v. +// Is safe to use on nil value LogLevelTypes. If LogLevel is nill, will default +// to LogOff comparison. +func (l *LogLevelType) AtLeast(v LogLevelType) bool { + c := l.Value() + return c >= v +} + +// to LogOff comparison. +func (l *LogLevelType) logOff(v LogLevelType) bool { + c := l.Value() + + if c == LOGOFF { + return true + } + + return c > v +} + +// A Logger is a minimalistic interface for the SDK to log messages to. Should +// be used to provide custom logging writers for the SDK to use. +type Logger interface { + Log(...interface{}) +} + +// A LoggerFunc is a convenience type to convert a function taking a variadic +// list of arguments and wrap it so the Logger interface can be used. +// +// Example: +// s3.New(sess, &aws.Config{Logger: aws.LoggerFunc(func(args ...interface{}) { +// fmt.Fprintln(os.Stdout, args...) +// })}) +type LoggerFunc func(...interface{}) + +// Log calls the wrapped function with the arguments provided +func (f LoggerFunc) Log(args ...interface{}) { + f(args...) +} + +// NewDefaultLogger returns a Logger which will write log messages to stdout, and +// use same formatting runes as the stdlib log.Logger +func NewDefaultLogger() Logger { + return &defaultLogger{ + logger: log.New(os.Stdout, "", log.LstdFlags), + } +} + +// A defaultLogger provides a minimalistic logger satisfying the Logger interface. +type defaultLogger struct { + logger *log.Logger +} + +// Log logs the parameters to the stdlib logger. See log.Println. +func (l defaultLogger) Log(args ...interface{}) { + l.logger.Println(args...) +} + +type NosLog struct { + LogLevel *LogLevelType + // The logger writer interface to write logging messages to. Defaults to + // standard out. + Logger Logger +} + +func (nosLog NosLog) Debug(args ...interface{}) { + if nosLog.LogLevel.logOff(DEBUG) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} + +func (nosLog NosLog) Trace(args ...interface{}) { + if nosLog.LogLevel.logOff(TRACE) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} + +func (nosLog NosLog) Info(args ...interface{}) { + if nosLog.LogLevel.logOff(INFO) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} + +func (nosLog NosLog) Warn(args ...interface{}) { + if nosLog.LogLevel.logOff(WARNING) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} + +func (nosLog NosLog) Error(args ...interface{}) { + if nosLog.LogLevel.logOff(ERROR) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} + +func (nosLog NosLog) Critical(args ...interface{}) { + if nosLog.LogLevel.logOff(CRITICAL) { + return + } + + if nosLog.Logger == nil { + return + } + + nosLog.Logger.Log(args...) +} diff --git a/pkg/nos/model/nosrequest.go b/pkg/nos/model/nosrequest.go new file mode 100644 index 0000000..cc802d7 --- /dev/null +++ b/pkg/nos/model/nosrequest.go @@ -0,0 +1,142 @@ +package model + +import ( + "encoding/xml" + "io" +) + +// Create Bucket + +type CreateBucketRequest struct { + XMLName xml.Name `xml:"CreateBucketConfiguration"` + Location string `xml:"LocationConstraint"` +} + +// CompleteMultiUpload +type UploadPart struct { + XMLName xml.Name `xml:"Part"` + PartNumber int `xml:"PartNumber"` + Etag string `xml:"ETag"` +} + +type UploadParts struct { + XMLName xml.Name `xml:"CompleteMultipartUpload"` + Parts []UploadPart `xml:"Part"` +} + +func (uploadParts *UploadParts) Append(part UploadPart) { + uploadParts.Parts = append(uploadParts.Parts, part) +} + +// DeleteMultiObjects +type DeleteObject struct { + XMLName xml.Name `xml:"Object"` + Key string `xml:"Key"` +} + +type DeleteMultiObjects struct { + XMLName xml.Name `xml:"Delete"` + Quiet bool `xml:"Quiet"` + Objects []DeleteObject `xml:"Object"` +} + +func (deleteMulti *DeleteMultiObjects) Append(object DeleteObject) { + deleteMulti.Objects = append(deleteMulti.Objects, object) +} + +type ObjectMetadata struct { + ContentLength int64 + Metadata map[string]string +} + +type PutObjectRequest struct { + Bucket string + Object string + Body io.ReadSeeker + FilePath string + Metadata *ObjectMetadata +} + +type CopyObjectRequest struct { + SrcBucket string + SrcObject string + DestBucket string + DestObject string +} + +type MoveObjectRequest struct { + SrcBucket string + SrcObject string + DestBucket string + DestObject string +} + +type DeleteMultiObjectsRequest struct { + Bucket string + DelectObjects *DeleteMultiObjects +} + +type GetObjectRequest struct { + Bucket string + Object string + ObjRange string + IfModifiedSince string +} + +type ObjectRequest struct { + Bucket string + Object string +} + +type ListObjectsRequest struct { + Bucket string + Prefix string + Delimiter string + Marker string + MaxKeys int +} + +type InitMultiUploadRequest struct { + Bucket string + Object string + Metadata *ObjectMetadata +} + +type UploadPartRequest struct { + Bucket string + Object string + UploadId string + PartNumber int + Content []byte + PartSize int64 + ContentMd5 string +} + +type CompleteMultiUploadRequest struct { + Bucket string + Object string + UploadId string + Parts []UploadPart + ContentMd5 string + ObjectMd5 string +} + +type AbortMultiUploadRequest struct { + Bucket string + Object string + UploadId string +} + +type ListUploadPartsRequest struct { + Bucket string + Object string + UploadId string + MaxParts int + PartNumberMarker int +} + +type ListMultiUploadsRequest struct { + Bucket string + KeyMarker string + MaxUploads int +} diff --git a/pkg/nos/model/nosresponse.go b/pkg/nos/model/nosresponse.go new file mode 100644 index 0000000..dbcb17c --- /dev/null +++ b/pkg/nos/model/nosresponse.go @@ -0,0 +1,120 @@ +package model + +import ( + "encoding/xml" + "io" +) + +type ObjectResult struct { + Etag string + RequestId string +} + +type NOSObject struct { + Key string + BucketName string + ObjectMetadata *ObjectMetadata + Body io.ReadCloser `type:"blob"` +} + +type DeleteError struct { + XMLName xml.Name `xml:"Error"` + Key string `xml:"Key"` + Code string `xml:"Code"` + Message string `xml:"Message"` +} + +type DeleteKey struct { + XMLName xml.Name `xml:"Deleted"` + Key string `xml:"Key"` +} + +type DeleteObjectsResult struct { + XMLName xml.Name `xml:"DeleteResult"` + Deleted []DeleteKey `xml:"Deleted"` + Error []DeleteError `xml:"Error"` +} + +type ListObjectsResult struct { + XMLName xml.Name `xml:"ListBucketResult"` + Bucket string `xml:"Name"` + Prefix string `xml:"Prefix"` + CommonPrefixes []CommonPrefix `xml:"CommonPrefixes"` + MaxKeys string `xml:"MaxKeys"` + NextMarker string `xml:"NextMarker"` + IsTruncated bool `xml:"IsTruncated"` + Contents []Contents `xml:"Contents"` +} + +type InitMultiUploadResult struct { + XMLName xml.Name `xml:"InitiateMultipartUploadResult"` + Bucket string `xml:"Bucket"` + Object string `xml:"Key"` + UploadId string `xml:"UploadId"` +} + +type CompleteMultiUploadResult struct { + XMLName xml.Name `xml:"CompleteMultipartUploadResult"` + Location string `xml:"Location"` + Bucket string `xml:"Bucket"` + Key string `xml:"Key"` + Etag string `xml:"ETag"` +} + +type Owner struct { + XMLName xml.Name `xml:"Owner"` + Id string `xml:"ID"` + DisplayName string `xml:"DisplayName"` +} + +type UploadPartRet struct { + XMLName xml.Name `xml:"Part"` + PartNumber int `xml:"PartNumber"` + LastModified string `xml:"LastModified"` + Etag string `xml:"ETag"` + Size int `xml:"Size"` +} + +type ListPartsResult struct { + XMLName xml.Name `xml:"ListPartsResult"` + Bucket string `xml:"Bucket"` + Key string `xml:"Key"` + UploadId string `xml:"UploadId"` + Owner Owner `xml:"Owner"` + StorageClass string `xml:"StorageClass"` + PartNumberMarker int `xml:"PartNumberMarker"` + NextPartNumberMarker int `xml:"NextPartNumberMarker"` + MaxPart int `xml:"MaxParts"` + IsTruncated bool `xml:"IsTruncated"` + Parts []UploadPartRet `xml:"Part"` +} + +type Contents struct { + XMLName xml.Name `xml:"Contents"` + Key string `xml:"Key"` + LastModified string `xml:"LastModified"` + Etag string `xml:"Etag"` + Size int64 `xml:"Size"` +} + +type CommonPrefix struct { + XMLName xml.Name `xml:"CommonPrefixes"` + Prefix string `xml:"Prefix"` +} + +type MultipartUpload struct { + XMLName xml.Name `xml:"Upload"` + Key string `xml:"Key"` + UploadId string `xml:"UploadId"` + StorageClass string `xml:"StorageClass"` + Owner Owner `xml:"Owner"` + Initiated string `xml:"Initiated"` +} + +type ListMultiUploadsResult struct { + XMLName xml.Name `xml:"ListMultipartUploadsResult"` + Bucket string `xml:"Bucket"` + NextKeyMarker string `xml:"NextKeyMarker"` + IsTruncated bool `xml:"IsTruncated"` + Uploads []MultipartUpload `xml:"Upload"` +} diff --git a/pkg/nos/nosclient/nosclient.go b/pkg/nos/nosclient/nosclient.go new file mode 100644 index 0000000..db74b09 --- /dev/null +++ b/pkg/nos/nosclient/nosclient.go @@ -0,0 +1,923 @@ +package nosclient + +import ( + "bytes" + "crypto/md5" + "encoding/hex" + "encoding/xml" + "errors" + "io" + "net/http" + "net/url" + "os" + "strconv" + "time" + + "github.com/saltbo/zpan/pkg/nos/auth" + "github.com/saltbo/zpan/pkg/nos/config" + "github.com/saltbo/zpan/pkg/nos/httpclient" + "github.com/saltbo/zpan/pkg/nos/logger" + "github.com/saltbo/zpan/pkg/nos/model" + "github.com/saltbo/zpan/pkg/nos/nosconst" + "github.com/saltbo/zpan/pkg/nos/noserror" + "github.com/saltbo/zpan/pkg/nos/utils" +) + +type NosClient struct { + endPoint string + accessKey string + secretKey string + + httpClient *http.Client + Log logger.NosLog + isSubDomain bool +} + +func NewHttpClient(connectTimeout, requestTimeout, readWriteTimeout, + maxIdleConnection int) *http.Client { + + tr := &httpclient.Transport{ + ConnectTimeout: time.Duration(connectTimeout) * time.Second, + RequestTimeout: time.Duration(requestTimeout) * time.Second, + ReadWriteTimeout: time.Duration(readWriteTimeout) * time.Second, + DisableKeepAlives: false, + MaxIdleConnsPerHost: maxIdleConnection, + } + + return &http.Client{Transport: tr} +} + +// New constructs a new Driver with the given NOS credentials, bucket, chunksize flag +func New(conf *config.Config) (*NosClient, error) { + noserror.Init() + + err := conf.Check() + if err != nil { + return nil, err + } + + client := &NosClient{ + endPoint: conf.Endpoint, + accessKey: conf.AccessKey, + secretKey: conf.SecretKey, + + httpClient: NewHttpClient( + conf.NosServiceConnectTimeout, + conf.NosServiceReadWriteTimeout, + conf.NosServiceReadWriteTimeout, + conf.NosServiceMaxIdleConnection), + + Log: logger.NosLog{ + LogLevel: conf.LogLevel, + Logger: conf.Logger, + }, + + isSubDomain: conf.GetIsSubDomain(), + } + + return client, nil +} + +func (client *NosClient) getNosRequest(method, bucket, object string, metadata *model.ObjectMetadata, + body io.Reader, params map[string]string, bodyStyle string) (*http.Request, error) { + + var opaque string + var urlStr string + if client.isSubDomain { + urlStr = "http://" + bucket + "." + client.endPoint + "/" + } else { + urlStr = "http://" + client.endPoint + "/" + bucket + "/" + } + + encodedObject := utils.NosUrlEncode(object) + urlStr += encodedObject + opaque = urlStr + + v := url.Values{} + for key, val := range params { + v.Add(key, val) + } + + if len(v) > 0 { + urlStr += "?" + v.Encode() + } + + request, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + request.URL.Opaque = opaque + //add http header + //request.Header.Set(nosconst.DATE, (time.Now().Format(nosconst.RFC1123_GMT))) + request.Header.Set(nosconst.DATE, (time.Now().UTC().Format(nosconst.RFC1123_GMT))) + request.Header.Set(nosconst.NOS_ENTITY_TYPE, bodyStyle) + request.Header.Set(nosconst.USER_AGENT, utils.InitUserAgent()) + + if metadata != nil { + if metadata.Metadata != nil { + for key, value := range metadata.Metadata { + if value != "" { + request.Header.Set(key, value) + } + } + } + } + + if client.accessKey != "" && client.secretKey != "" { + request.Header.Set(nosconst.AUTHORIZATION, + auth.SignRequest(request, client.accessKey, client.secretKey, bucket, encodedObject)) + } + + return request, nil +} + +func (client *NosClient) CreateBucket(bucketName string, location nosconst.Location, + acl nosconst.Acl) error { + var locationConstraint string + switch location { + case nosconst.HZ: + locationConstraint = "HZ" + default: + return errors.New("unsupported Location") + } + + var aclString string + + switch acl { + case nosconst.PUBLICREAD: + aclString = "public-read" + case nosconst.PRIVATE: + aclString = "private" + } + + request := &model.CreateBucketRequest{ + Location: locationConstraint, + } + body, err := xml.Marshal(request) + if err != nil { + return err + } + + //Metadata + metadata := &model.ObjectMetadata{ + Metadata: map[string]string{ + nosconst.X_NOS_ACL: aclString, + }, + } + + req, err := client.getNosRequest("PUT", bucketName, "", + metadata, bytes.NewReader(body), nil, nosconst.XML_TYPE) + + resp, err := client.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } else { + err := utils.ProcessServerError(resp, bucketName, "") + return err + } +} + +func (client *NosClient) PutObjectByStream(putObjectRequest *model.PutObjectRequest) (*model.ObjectResult, error) { + if putObjectRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + var contentLength int64 + if putObjectRequest.Metadata != nil { + contentLength = putObjectRequest.Metadata.ContentLength + } + + err := utils.VerifyParamsWithLength(putObjectRequest.Bucket, putObjectRequest.Object, contentLength) + if err != nil { + return nil, err + } + + request, err := client.getNosRequest("PUT", putObjectRequest.Bucket, putObjectRequest.Object, + putObjectRequest.Metadata, putObjectRequest.Body, nil, nosconst.JSON_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode = ", resp.StatusCode) + if resp.StatusCode == http.StatusOK { + requestid, etag := utils.PopulateResponseHeader(resp) + objectResult := &model.ObjectResult{ + Etag: etag, + RequestId: requestid, + } + + return objectResult, nil + } else { + err := utils.ProcessServerError(resp, putObjectRequest.Bucket, putObjectRequest.Object) + return nil, err + } +} + +func (client *NosClient) PutObjectByFile(putObjectRequest *model.PutObjectRequest) (*model.ObjectResult, error) { + if putObjectRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + file, err := os.Open(putObjectRequest.FilePath) + if err != nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_FILE_INVALID, "", "", err.Error()) + } + defer file.Close() + + if putObjectRequest.Metadata == nil { + putObjectRequest.Metadata = &model.ObjectMetadata{} + } + + if putObjectRequest.Metadata.ContentLength == 0 { + fi, err := file.Stat() + if err == nil { + putObjectRequest.Metadata.ContentLength = fi.Size() + } else { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_FILE_INVALID, "", "", err.Error()) + } + } + + putObjectRequest.Body = file + + return client.PutObjectByStream(putObjectRequest) +} + +func (client *NosClient) CopyObject(copyObjectRequest *model.CopyObjectRequest) error { + + if copyObjectRequest == nil { + return utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + srcBucket := copyObjectRequest.SrcBucket + srcObject := copyObjectRequest.SrcObject + destBucket := copyObjectRequest.DestBucket + destObject := copyObjectRequest.DestObject + + err := utils.VerifyParamsWithObject(destBucket, destObject) + if err != nil { + return err + } + + err = utils.VerifyParamsWithObject(srcBucket, srcObject) + if err != nil { + return utils.ProcessClientError(noserror.ERROR_CODE_SRCBUCKETANDOBJECT_ERROR, destBucket, destObject, "") + } + + copySource := "/" + utils.NosUrlEncode(srcBucket) + "/" + utils.NosUrlEncode(srcObject) + metadata := &model.ObjectMetadata{ + Metadata: map[string]string{ + nosconst.X_NOS_COPY_SOURCE: copySource, + }, + } + + request, err := client.getNosRequest("PUT", destBucket, destObject, metadata, nil, nil, nosconst.JSON_TYPE) + if err != nil { + return err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } else { + err := utils.ProcessServerError(resp, destBucket, destObject) + return err + } +} + +func (client *NosClient) MoveObject(moveObjectRequest *model.MoveObjectRequest) error { + + if moveObjectRequest == nil { + return utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + srcBucket := moveObjectRequest.SrcBucket + srcObject := moveObjectRequest.SrcObject + destBucket := moveObjectRequest.DestBucket + destObject := moveObjectRequest.DestObject + + err := utils.VerifyParamsWithObject(destBucket, destObject) + if err != nil { + return err + } + + err = utils.VerifyParamsWithObject(srcBucket, srcObject) + if err != nil { + return utils.ProcessClientError(noserror.ERROR_CODE_SRCBUCKETANDOBJECT_ERROR, destBucket, destObject, "") + } + + moveSource := "/" + srcBucket + "/" + srcObject + metadata := &model.ObjectMetadata{ + Metadata: map[string]string{ + nosconst.X_NOS_MOVE_SOURCE: utils.NosUrlEncode(moveSource), + }, + } + + request, err := client.getNosRequest("PUT", destBucket, destObject, metadata, nil, nil, nosconst.JSON_TYPE) + if err != nil { + return err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } else { + err := utils.ProcessServerError(resp, destBucket, destObject) + return err + } +} + +func (client *NosClient) DeleteObject(deleteObjectRequest *model.ObjectRequest) error { + + if deleteObjectRequest == nil { + return utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + err := utils.VerifyParamsWithObject(deleteObjectRequest.Bucket, deleteObjectRequest.Object) + if err != nil { + return err + } + + request, err := client.getNosRequest("DELETE", deleteObjectRequest.Bucket, deleteObjectRequest.Object, + nil, nil, nil, nosconst.JSON_TYPE) + if err != nil { + return err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } else { + err := utils.ProcessServerError(resp, deleteObjectRequest.Bucket, deleteObjectRequest.Object) + return err + } +} + +func (client *NosClient) DeleteMultiObjects(deleteRequest *model.DeleteMultiObjectsRequest) (*model.DeleteObjectsResult, + error) { + + if deleteRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + err := utils.VerifyParams(deleteRequest.Bucket) + if err != nil { + return nil, err + } + delectObjects := deleteRequest.DelectObjects + if delectObjects == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_DELETEMULTIOBJECTS_ERROR, "", "", "") + } + if len(delectObjects.Objects) > nosconst.MAX_FILENUMBER { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_OBJECTSBIGGER_ERROR, "", "", "") + } + + body, err := xml.Marshal(delectObjects) + if err != nil { + return nil, err + } + + contentLength := int64(len(body)) + if contentLength > nosconst.MAX_DELETEBODY { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_OBJECTSBIGGER_ERROR, "", "", "") + } + + md5Ctx := md5.New() + md5Ctx.Write(body) + cipherStr := md5Ctx.Sum(nil) + metadata := &model.ObjectMetadata{ + ContentLength: contentLength, + Metadata: map[string]string{ + "Content-MD5": hex.EncodeToString(cipherStr), + }, + } + params := map[string]string{ + "delete": "", + } + request, err := client.getNosRequest("POST", deleteRequest.Bucket, "", metadata, + bytes.NewReader(body), params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.DeleteObjectsResult{} + + err := utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + return result, nil + } else { + err := utils.ProcessServerError(resp, deleteRequest.Bucket, "") + return nil, err + } +} + +func (client *NosClient) GetObject(getObjectRequest *model.GetObjectRequest) (*model.NOSObject, error) { + + if getObjectRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + err := utils.VerifyParamsWithObject(getObjectRequest.Bucket, getObjectRequest.Object) + if err != nil { + return nil, err + } + + metadata := &model.ObjectMetadata{ + Metadata: map[string]string{ + nosconst.IfMODIFYSINCE: getObjectRequest.IfModifiedSince, + nosconst.RANGE: getObjectRequest.ObjRange, + }, + } + + request, err := client.getNosRequest("GET", getObjectRequest.Bucket, getObjectRequest.Object, metadata, + nil, nil, nosconst.JSON_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusPartialContent { + nosObject := &model.NOSObject{ + Key: getObjectRequest.Object, + BucketName: getObjectRequest.Bucket, + ObjectMetadata: utils.PopulateAllHeader(resp), + Body: resp.Body, + } + return nosObject, nil + } else if resp.StatusCode == http.StatusNotModified { + return nil, nil + } else { + err := utils.ProcessServerError(resp, getObjectRequest.Bucket, getObjectRequest.Object) + resp.Body.Close() + return nil, err + } +} + +func (client *NosClient) DoesObjectExist(objectRequest *model.ObjectRequest) (bool, error) { + + if objectRequest == nil { + return false, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + err := utils.VerifyParamsWithObject(objectRequest.Bucket, objectRequest.Object) + if err != nil { + return false, err + } + + request, err := client.getNosRequest("HEAD", objectRequest.Bucket, objectRequest.Object, nil, nil, + nil, nosconst.JSON_TYPE) + if err != nil { + return false, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return false, err + } + + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return true, nil + } else if resp.StatusCode == http.StatusNotFound { + return false, nil + } else { + err := utils.ProcessServerError(resp, objectRequest.Bucket, objectRequest.Object) + return false, err + } +} + +func (client *NosClient) GetObjectMetaData(objectRequest *model.ObjectRequest) (*model.ObjectMetadata, error) { + + if objectRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + err := utils.VerifyParamsWithObject(objectRequest.Bucket, objectRequest.Object) + if err != nil { + return nil, err + } + + request, err := client.getNosRequest("HEAD", objectRequest.Bucket, objectRequest.Object, nil, + nil, nil, nosconst.JSON_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return utils.PopulateAllHeader(resp), nil + } else { + err := utils.ProcessServerError(resp, objectRequest.Bucket, objectRequest.Object) + return nil, err + } +} + +func (client *NosClient) ListObjects(listObjectsRequest *model.ListObjectsRequest) (*model.ListObjectsResult, error) { + + if listObjectsRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := listObjectsRequest.Bucket + prefix := listObjectsRequest.Prefix + delimiter := listObjectsRequest.Delimiter + marker := listObjectsRequest.Marker + maxKeys := listObjectsRequest.MaxKeys + if maxKeys <= 0 { + maxKeys = 100 + } + + err := utils.VerifyParams(bucket) + if err != nil { + return nil, err + } + + params := map[string]string{ + nosconst.LIST_PREFIX: prefix, + nosconst.LIST_DELIMITER: delimiter, + nosconst.LIST_MARKER: marker, + nosconst.LIST_MAXKEYS: strconv.Itoa(maxKeys), + } + + request, err := client.getNosRequest("GET", bucket, "", nil, nil, params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.ListObjectsResult{} + err = utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + return result, nil + } else { + err := utils.ProcessServerError(resp, bucket, "") + return nil, err + } +} + +// multipart upload api +func (client *NosClient) InitMultiUpload(initMultiUploadRequest *model.InitMultiUploadRequest) (*model.InitMultiUploadResult, error) { + + if initMultiUploadRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := initMultiUploadRequest.Bucket + object := initMultiUploadRequest.Object + metadata := initMultiUploadRequest.Metadata + + err := utils.VerifyParamsWithObject(bucket, object) + if err != nil { + return nil, err + } + + params := map[string]string{ + "uploads": "", + } + + request, err := client.getNosRequest("POST", bucket, object, metadata, nil, params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.InitMultiUploadResult{} + err = utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + return result, nil + } else { + err := utils.ProcessServerError(resp, bucket, object) + return nil, err + } +} + +func (client *NosClient) UploadPart(uploadPartRequest *model.UploadPartRequest) (*model.ObjectResult, error) { + + if uploadPartRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := uploadPartRequest.Bucket + object := uploadPartRequest.Object + uploadId := uploadPartRequest.UploadId + partNumber := uploadPartRequest.PartNumber + content := uploadPartRequest.Content + partSize := uploadPartRequest.PartSize + contentMd5 := uploadPartRequest.ContentMd5 + + err := utils.VerifyParamsWithObject(bucket, object) + if err != nil { + return nil, err + } + metadata := &model.ObjectMetadata{} + metadata.Metadata = make(map[string]string) + if contentMd5 != "" { + metadata.Metadata[nosconst.CONTENT_MD5] = contentMd5 + } + + params := map[string]string{ + nosconst.UPLOADID: uploadId, + nosconst.PARTNUMBER: strconv.FormatInt(int64(partNumber), 10), + } + limitReader := &io.LimitedReader{ + R: bytes.NewReader(content), + N: partSize, + } + request, err := client.getNosRequest("PUT", bucket, object, metadata, limitReader, + params, nosconst.JSON_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + requestid, etag := utils.PopulateResponseHeader(resp) + objectResult := &model.ObjectResult{ + Etag: etag, + RequestId: requestid, + } + return objectResult, nil + } else { + err := utils.ProcessServerError(resp, bucket, object) + return nil, err + } +} + +func (client *NosClient) CompleteMultiUpload(completeMultiUploadRequest *model.CompleteMultiUploadRequest) ( + *model.CompleteMultiUploadResult, error) { + + if completeMultiUploadRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := completeMultiUploadRequest.Bucket + object := completeMultiUploadRequest.Object + uploadId := completeMultiUploadRequest.UploadId + parts := completeMultiUploadRequest.Parts + contentMd5 := completeMultiUploadRequest.ContentMd5 + objectMd5 := completeMultiUploadRequest.ObjectMd5 + + err := utils.VerifyParamsWithObject(bucket, object) + if err != nil { + return nil, err + } + + params := map[string]string{ + nosconst.UPLOADID: uploadId, + } + + metadata := &model.ObjectMetadata{} + metadata.Metadata = make(map[string]string) + if contentMd5 != "" { + metadata.Metadata[nosconst.CONTENT_MD5] = contentMd5 + } + if objectMd5 != "" { + metadata.Metadata[nosconst.X_NOS_OBJECT_MD5] = objectMd5 + } + + uploadParts := model.UploadParts{Parts: parts} + body, err := xml.Marshal(uploadParts) + if err != nil { + return nil, err + } + + request, err := client.getNosRequest("POST", bucket, object, metadata, bytes.NewReader(body), + params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.CompleteMultiUploadResult{} + err = utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + result.Etag = utils.RemoveQuotes(result.Etag) + return result, nil + } else { + err := utils.ProcessServerError(resp, bucket, object) + return nil, err + } +} + +func (client *NosClient) AbortMultiUpload(abortMultiUploadRequest *model.AbortMultiUploadRequest) error { + + if abortMultiUploadRequest == nil { + return utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := abortMultiUploadRequest.Bucket + object := abortMultiUploadRequest.Object + uploadId := abortMultiUploadRequest.UploadId + + err := utils.VerifyParamsWithObject(bucket, object) + if err != nil { + return err + } + + params := map[string]string{ + nosconst.UPLOADID: uploadId, + } + + request, err := client.getNosRequest("DELETE", bucket, object, nil, nil, params, nosconst.JSON_TYPE) + if err != nil { + return err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + return nil + } else { + err := utils.ProcessServerError(resp, bucket, object) + return err + } +} + +func (client *NosClient) ListUploadParts(listUploadPartsRequest *model.ListUploadPartsRequest) (*model.ListPartsResult, error) { + + if listUploadPartsRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := listUploadPartsRequest.Bucket + object := listUploadPartsRequest.Object + uploadId := listUploadPartsRequest.UploadId + maxParts := listUploadPartsRequest.MaxParts + partNumberMarker := listUploadPartsRequest.PartNumberMarker + + err := utils.VerifyParamsWithObject(bucket, object) + if err != nil { + return nil, err + } + + params := map[string]string{ + nosconst.UPLOADID: uploadId, + nosconst.MAX_PARTS: strconv.Itoa(maxParts), + nosconst.PART_NUMBER_MARKER: strconv.Itoa(partNumberMarker), + } + + request, err := client.getNosRequest("GET", bucket, object, nil, nil, params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.ListPartsResult{} + err = utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + + return result, nil + } else { + err := utils.ProcessServerError(resp, bucket, object) + return nil, err + } +} + +// This operation lists in-progress multipart uploads. +func (client *NosClient) ListMultiUploads(listMultiUploadsRequest *model.ListMultiUploadsRequest) ( + *model.ListMultiUploadsResult, error) { + + if listMultiUploadsRequest == nil { + return nil, utils.ProcessClientError(noserror.ERROR_CODE_REQUEST_ERROR, "", "", "") + } + + bucket := listMultiUploadsRequest.Bucket + err := utils.VerifyParams(bucket) + if err != nil { + return nil, err + } + + if listMultiUploadsRequest.MaxUploads == 0 { + listMultiUploadsRequest.MaxUploads = nosconst.DEFAULTVALUE + } + + params := map[string]string{ + nosconst.UPLOADS: "", + nosconst.LIST_KEY_MARKER: listMultiUploadsRequest.KeyMarker, + nosconst.LIST_MAX_UPLOADS: strconv.Itoa(listMultiUploadsRequest.MaxUploads), + } + + request, err := client.getNosRequest("GET", bucket, "", nil, nil, params, nosconst.XML_TYPE) + if err != nil { + return nil, err + } + + resp, err := client.httpClient.Do(request) + if err != nil { + return nil, err + } + defer resp.Body.Close() + client.Log.Debug("resp.StatusCode=", resp.StatusCode) + + if resp.StatusCode == http.StatusOK { + result := &model.ListMultiUploadsResult{} + err = utils.ParseXmlBody(resp.Body, result) + if err != nil { + return nil, err + } + + return result, nil + } else { + err := utils.ProcessServerError(resp, bucket, "") + return nil, err + } + +} diff --git a/pkg/nos/nosconst/nosconst.go b/pkg/nos/nosconst/nosconst.go new file mode 100644 index 0000000..1c953f4 --- /dev/null +++ b/pkg/nos/nosconst/nosconst.go @@ -0,0 +1,71 @@ +package nosconst + +const ( + HZ = iota +) + +type Location int +type Acl int + +const ( + PRIVATE = iota + PUBLICREAD +) + +const ( + DEFAULT_MAXBUFFERSIZE = 1024 * 1024 + MAX_FILESIZE = 100 * 1024 * 1024 + MIN_FILESIZE = 16 * 1024 + MAX_FILENUMBER = 1000 + DEFAULTVALUE = 1000 + MAX_DELETEBODY = 2 * 1024 * 1024 + + RFC1123_NOS = "Mon, 02 Jan 2006 15:04:05 Asia/Shanghai" + RFC1123_GMT = "Mon, 02 Jan 2006 15:04:05 GMT" + CONTENT_LENGTH = "Content-Length" + CONTENT_TYPE = "Content-Type" + CONTENT_MD5 = "Content-Md5" + LAST_MODIFIED = "Last-Modified" + USER_AGENT = "User-Agent" + DATE = "Date" + AUTHORIZATION = "Authorization" + RANGE = "Range" + IfMODIFYSINCE = "If-Modified-Since" + LIST_PREFIX = "prefix" + LIST_DELIMITER = "delimiter" + LIST_MARKER = "marker" + LIST_MAXKEYS = "max-keys" + UPLOADID = "uploadId" + MAX_PARTS = "max-parts" + PARTNUMBER = "partNumber" + UPLOADS = "uploads" + PART_NUMBER_MARKER = "part-number-marker" + LIST_KEY_MARKER = "key-marker" + LIST_MAX_UPLOADS = "max-uploads" + LIST_UPLOADID_MARKER = "upload-id-marker" + + ETAG = "Etag" + NOS_USER_METADATA_PREFIX = "X-Nos-Meta-" + NOS_ENTITY_TYPE = "X-Nos-Entity-Type" + NOS_VERSION_ID = "X-Nos-Version-Id" + X_NOS_OBJECT_NAME = "X-Nos-Object-Name" + X_NOS_REQUEST_ID = "X-Nos-Request-Id" + X_NOS_OBJECT_MD5 = "X-Nos-Object-Md5" + X_NOS_COPY_SOURCE = "x-nos-copy-source" + X_NOS_MOVE_SOURCE = "x-nos-move-source" + X_NOS_ACL = "x-nos-acl" + + ORIG_CONTENT_MD5 = "Content-MD5" + ORIG_ETAG = "ETag" + ORIG_NOS_USER_METADATA_PREFIX = "x-nos-meta-" + ORIG_NOS_VERSION_ID = "x-nos-version-id" + ORIG_X_NOS_OBJECT_NAME = "x-nos-object-name" + ORIG_X_NOS_REQUEST_ID = "x-nos-request-id" + ORIG_X_NOS_OBJECT_MD5 = "x-nos-Object-md5" + + SDKNAME = "nos-golang-sdk" + VERSION = "1.0.0" + + JSON_TYPE = "json" + XML_TYPE = "xml" +) diff --git a/pkg/nos/noserror/noserror.go b/pkg/nos/noserror/noserror.go new file mode 100644 index 0000000..2c634a5 --- /dev/null +++ b/pkg/nos/noserror/noserror.go @@ -0,0 +1,142 @@ +package noserror + +import ( + "encoding/xml" + "strconv" +) + +const ( + BASE_ERROR_CODE = 400 + + /*client error codes*/ + //4xx errors + ERROR_CODE_CFG_ENDPOINT = BASE_ERROR_CODE + 20 + ERROR_CODE_CFG_CONNECT_TIMEOUT = BASE_ERROR_CODE + 21 + ERROR_CODE_CFG_READWRITE_TIMEOUT = BASE_ERROR_CODE + 22 + ERROR_CODE_CFG_MAXIDLECONNECT = BASE_ERROR_CODE + 23 + ERROR_CODE_BUCKET_INVALID = BASE_ERROR_CODE + 30 + ERROR_CODE_OBJECT_INVALID = BASE_ERROR_CODE + 31 + ERROR_CODE_FILELENGTH_INVALID = BASE_ERROR_CODE + 32 + ERROR_CODE_FILE_INVALID = BASE_ERROR_CODE + 33 + ERROR_CODE_REQUEST_ERROR = BASE_ERROR_CODE + 34 + ERROR_CODE_READCONTENT_ERROR = BASE_ERROR_CODE + 35 + ERROR_CODE_PARSEJSON_ERROR = BASE_ERROR_CODE + 36 + ERROR_CODE_PARSEXML_ERROR = BASE_ERROR_CODE + 37 + ERROR_CODE_SRCBUCKETANDOBJECT_ERROR = BASE_ERROR_CODE + 38 + ERROR_CODE_DELETEMULTIOBJECTS_ERROR = BASE_ERROR_CODE + 39 + ERROR_CODE_OBJECTSBIGGER_ERROR = BASE_ERROR_CODE + 40 + ERROR_CODE_PARTLENGTH_ERROR = BASE_ERROR_CODE + 41 + + /*short message code*/ + ERROR_MSG_CFG_ENDPOINT = "Config: InvalidEndpoint" + ERROR_MSG_CFG_CONNECT_TIMEOUT = "Config: InvalidConnectionTimeout" + ERROR_MSG_CFG_READWRITE_TIMEOUT = "Config: InvalidReadWriteTimeout" + ERROR_MSG_CFG_MAXIDLECONNECT = "Config: InvalidMaxIdleConnect" + ERROR_MSG_BUCKET_INVALID = "InvalidBucketName" + ERROR_MSG_OBJECT_INVALID = "InvalidObjectName" + ERROR_MSG_FILELENGTH_INVALID = "InvalidFileSize" + ERROR_MSG_FILE_INVALID = "Failed to open file. " + ERROR_MSG_REQUEST_ERROR = "Request is nil" + ERROR_MSG_READCONTENT_ERROR = "ReadContentError" + ERROR_MSG_PARSEJSON_ERROR = "InvalidJSONContent" + ERROR_MSG_PARSEXML_ERROR = "InvalidXmlContent" + ERROR_MSG_SRCBUCKETANDOBJECT_ERROR = "SrcBucket or SrcObject is invalid" + ERROR_MSG_DELETEMULTIOBJECTS_ERROR = "InvalidDeleteMultiObjects" + ERROR_MSG_OBJECTSBIGGER_ERROR = "InvalidObjects: the number is < 1000 and size of body is < 2M" + ERROR_MSG_PARTLENGTH_ERROR = "InvalidPartLength: the length should be between 16k and 100M" +) + +// mErrHttpCodeMap is map of Http Code +var mErrMsgMap map[int]string + +func Init() { + mErrMsgMap = make(map[int]string) + + //init err msg map + mErrMsgMap[ERROR_CODE_CFG_ENDPOINT] = ERROR_MSG_CFG_ENDPOINT + mErrMsgMap[ERROR_CODE_CFG_CONNECT_TIMEOUT] = ERROR_MSG_CFG_CONNECT_TIMEOUT + mErrMsgMap[ERROR_CODE_CFG_READWRITE_TIMEOUT] = ERROR_MSG_CFG_READWRITE_TIMEOUT + mErrMsgMap[ERROR_CODE_CFG_MAXIDLECONNECT] = ERROR_MSG_CFG_MAXIDLECONNECT + mErrMsgMap[ERROR_CODE_BUCKET_INVALID] = ERROR_MSG_BUCKET_INVALID + mErrMsgMap[ERROR_CODE_OBJECT_INVALID] = ERROR_MSG_OBJECT_INVALID + mErrMsgMap[ERROR_CODE_FILELENGTH_INVALID] = ERROR_MSG_FILELENGTH_INVALID + mErrMsgMap[ERROR_CODE_FILE_INVALID] = ERROR_MSG_FILE_INVALID + mErrMsgMap[ERROR_CODE_REQUEST_ERROR] = ERROR_MSG_REQUEST_ERROR + mErrMsgMap[ERROR_CODE_READCONTENT_ERROR] = ERROR_MSG_READCONTENT_ERROR + mErrMsgMap[ERROR_CODE_PARSEJSON_ERROR] = ERROR_MSG_PARSEJSON_ERROR + mErrMsgMap[ERROR_CODE_PARSEXML_ERROR] = ERROR_MSG_PARSEXML_ERROR + mErrMsgMap[ERROR_CODE_SRCBUCKETANDOBJECT_ERROR] = ERROR_MSG_SRCBUCKETANDOBJECT_ERROR + mErrMsgMap[ERROR_CODE_DELETEMULTIOBJECTS_ERROR] = ERROR_MSG_DELETEMULTIOBJECTS_ERROR + mErrMsgMap[ERROR_CODE_OBJECTSBIGGER_ERROR] = ERROR_MSG_OBJECTSBIGGER_ERROR + mErrMsgMap[ERROR_CODE_PARTLENGTH_ERROR] = ERROR_MSG_PARTLENGTH_ERROR +} + +type NosError struct { + XMLName xml.Name `xml:"Error" json:"-"` + Code string `xml:"Code" json:"Code"` + Message string `xml:"Message" json:"Message"` + Resource string `xml:"Resource" json:"Resource"` + NosRequestId string `xml:"RequestId" json:"RequestId"` +} + +func NewNosError(code string, message string, resource string, requestid string) *NosError { + nosError := &(NosError{ + Code: code, + Message: message, + Resource: resource, + NosRequestId: requestid, + }) + return nosError +} + +func (nosError *NosError) Error() string { + return "Code = " + nosError.Code + + ", Message = " + nosError.Message + + ", Resource = " + nosError.Resource + + ", NosRequestId = " + nosError.NosRequestId +} + +type ServerError struct { + StatusCode int + RequestId string + NosErr *NosError `json:"Error"` +} + +func NewServerError(errCode int, requestid string, nosErr *NosError) error { + serverError := &(ServerError{ + StatusCode: errCode, + RequestId: requestid, + NosErr: nosErr, + }) + return serverError +} + +func (serverError *ServerError) Error() string { + return "StatusCode = " + strconv.Itoa(serverError.StatusCode) + + ", RequestId = " + serverError.RequestId + + ", NosError: " + serverError.NosErr.Error() +} + +type ClientError struct { + StatusCode int + Resource string + Message string +} + +func NewClientError(errCode int, resource string, msg string) error { + clientError := &(ClientError{ + StatusCode: errCode, + Resource: resource, + Message: mErrMsgMap[errCode], + }) + if msg != "" { + clientError.Message += ": " + msg + } + return clientError +} + +func (clientError *ClientError) Error() string { + return "StatusCode = " + strconv.Itoa(clientError.StatusCode) + + ", Resource = " + clientError.Resource + + ", Message = " + clientError.Message +} diff --git a/pkg/nos/sample.go b/pkg/nos/sample.go new file mode 100644 index 0000000..348e746 --- /dev/null +++ b/pkg/nos/sample.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + + "github.com/saltbo/zpan/pkg/nos/config" + "github.com/saltbo/zpan/pkg/nos/logger" + "github.com/saltbo/zpan/pkg/nos/model" + "github.com/saltbo/zpan/pkg/nos/nosclient" +) + +func sample_init(endpoint, accessKey, secretKey string) *nosclient.NosClient { + conf := &config.Config{ + Endpoint: endpoint, + AccessKey: accessKey, + SecretKey: secretKey, + + NosServiceConnectTimeout: 3, + NosServiceReadWriteTimeout: 60, + NosServiceMaxIdleConnection: 100, + + LogLevel: logger.LogLevel(logger.DEBUG), + Logger: logger.NewDefaultLogger(), + } + + nosClient, _ := nosclient.New(conf) + return nosClient +} + +func main() { + path := "" + endpoint := "" + accessKey := "" + secretKey := "" + + nosClient := sample_init(endpoint, accessKey, secretKey) + + putObjectRequest := &model.PutObjectRequest{ + Bucket: "", + Object: "", + FilePath: path, + } + _, err := nosClient.PutObjectByFile(putObjectRequest) + if err != nil { + fmt.Println(err.Error()) + } + + getObjectRequest := &model.GetObjectRequest{ + Bucket: "", + Object: "", + } + objectResult, err := nosClient.GetObject(getObjectRequest) + if err != nil { + fmt.Println(err.Error()) + } else { + objectResult.Body.Close() + } + + objectRequest := &model.ObjectRequest{ + Bucket: "", + Object: "", + } + err = nosClient.DeleteObject(objectRequest) + if err != nil { + fmt.Println(err.Error()) + } + + fmt.Println("Simple samples completed") +} diff --git a/pkg/nos/test/fortest b/pkg/nos/test/fortest new file mode 100644 index 0000000..78704a0 Binary files /dev/null and b/pkg/nos/test/fortest differ diff --git a/pkg/nos/test/testserver b/pkg/nos/test/testserver new file mode 100644 index 0000000..a08e73d Binary files /dev/null and b/pkg/nos/test/testserver differ diff --git a/pkg/nos/test/testserver2 b/pkg/nos/test/testserver2 new file mode 100644 index 0000000..a08e73d Binary files /dev/null and b/pkg/nos/test/testserver2 differ diff --git a/pkg/nos/tools/cover.sh b/pkg/nos/tools/cover.sh new file mode 100644 index 0000000..5b74ecd --- /dev/null +++ b/pkg/nos/tools/cover.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# used in converage html generation for golang project +# put this shell in project's dir +curPath=`pwd` +# export GOPATH=$curPath/../../.. +ROOT_DIR=`pwd`/.. +COVERAGE_FILE=`pwd`/coverage.html # filepath to put converage + +##package need to ignore +declare -a ignorePackage=("github.com" "gopkg.in" "logger" "tools" ,"test") + +subdirs=`ls $ROOT_DIR` +prefix='{"Packages":[' +suffix=']}' +empty_result='{"Packages":null}' +left=${#prefix} ##left postion of json result for a package + +# +# check and cd package if package legall +# +check_and_cd_package() +{ + pkg=$1 + curDir=$ROOT_DIR/$pkg + #echo $pkg + #echo $ROOT_DIR + echo "curDir:"$curDir + for ipkg in ${ignorePackage[@]}; + do +# echo "echo $pkg | grep $ipkg" + info=`echo $pkg| grep $ipkg` + if [ "$info" != "" ]; then +# echo "ignorePackage $pkg" + return 1 + fi + done + isDir=`test -d $curDir` + if [ "$?" != "0" ];then + return 1 + fi + cd $curDir + return $? +} + +# +# get current package's converge +# +get_package_coverage(){ + + result=`gocov test` + + if [ "$?" != "0" ] || [ "$result" == "" ] ; then + return 1 + fi + if [ "$result" == "$empty_result" ]; then + return 1 + fi + + right=$((${#result}-${#suffix}-$left)) + cur_result=${result:$left:$right} + echo $cur_result + return 0 +} + + +json_result="" +for pkg in $subdirs; +do + check_and_cd_package $pkg + + if [ "$?" != "0" ] ; then + continue + fi + #cd $curDir + cur_result=$(get_package_coverage) + if [ "$?" != "0" ] ; then + continue + fi + + echo "Get package $pkg's coverage success" + + if [ "$json_result" != "" ]; then + json_result="$json_result," + fi + + json_result=$json_result$cur_result + #echo "*************current result***************" + #echo $cur_result + #echo "***************current result end*********" + #echo "PSW:"$ROOT_DIR +done + +#echo "-----------------------" + +total_result=$prefix$json_result$suffix +echo $total_result | gocov-html > $COVERAGE_FILE +#echo $total_result diff --git a/pkg/nos/tools/run_test.sh b/pkg/nos/tools/run_test.sh new file mode 100644 index 0000000..3f6aa04 --- /dev/null +++ b/pkg/nos/tools/run_test.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cur=`pwd` +# export GOPATH=$cur/../../../ + +curPath=`pwd`/.. +for dir in $curPath/* +do + echo $dir + test -d $dir && cd $dir && go test +done diff --git a/pkg/nos/utils/utils.go b/pkg/nos/utils/utils.go new file mode 100644 index 0000000..1889a97 --- /dev/null +++ b/pkg/nos/utils/utils.go @@ -0,0 +1,226 @@ +package utils + +import ( + "encoding/json" + "encoding/xml" + "io" + "io/ioutil" + "net/http" + "net/url" + "runtime" + "strconv" + "strings" + "unicode" + + "github.com/saltbo/zpan/pkg/nos/model" + "github.com/saltbo/zpan/pkg/nos/nosconst" + "github.com/saltbo/zpan/pkg/nos/noserror" +) + +// VerifyObjectName check if the BucketName is legal +func VerifyBucketName(bucketName string) bool { + if bucketName == "" { + return false + } + + length := len(bucketName) + if length < 3 || length > 63 { + return false + } + + if bucketName != strings.ToLower(bucketName) { + return false + } + + if strings.Contains(bucketName, ".") { + return false + } + + specialCharactersCursor := false + for i := 0; i != length; i++ { + ch := bucketName[i] + if !unicode.IsLetter(rune(ch)) && !unicode.IsDigit(rune(ch)) { + //no start or end with special characters + if i == 0 || i == (length-1) { + return false + } + //no Continuous two speical characters + if specialCharactersCursor { + return false + } + specialCharactersCursor = true + if ch != '-' { + return false + } + } else { + specialCharactersCursor = false + } + } + + return true +} + +// VerifyObjectName check if the object name is legal +func VerifyObjectName(object string) bool { + if object == "" { + return false + } + if len(object) > 1000 { + return false + } + return true +} + +func VerifyParams(bucket string) error { + if !VerifyBucketName(bucket) { + return ProcessClientError(noserror.ERROR_CODE_BUCKET_INVALID, bucket, "", "") + } + + return nil +} + +func VerifyParamsWithObject(bucket string, object string) error { + + err := VerifyParams(bucket) + if err != nil { + return err + } + + if !VerifyObjectName(object) { + return ProcessClientError(noserror.ERROR_CODE_OBJECT_INVALID, bucket, object, "") + } + + return nil +} + +func VerifyParamsWithLength(bucket string, object string, length int64) error { + + err := VerifyParamsWithObject(bucket, object) + if err != nil { + return err + } + + if length > nosconst.MAX_FILESIZE { + return ProcessClientError(noserror.ERROR_CODE_FILELENGTH_INVALID, bucket, object, "") + } + + return nil +} + +func ParseXmlBody(body io.Reader, value interface{}) error { + content, err := ioutil.ReadAll(body) + if err != nil { + return err + } + err = xml.Unmarshal(content, value) + if err != nil { + return err + } + + return nil +} + +func RemoveQuotes(orig string) string { + s := strings.TrimSpace(orig) + + if strings.HasPrefix(s, "\"") { + s = s[1:len(s)] + } + + if strings.HasSuffix(s, "\"") { + s = s[0 : len(s)-1] + } + + return s +} + +func PopulateResponseHeader(response *http.Response) (requestid, etag string) { + hdr := response.Header + + etag = RemoveQuotes(hdr.Get(nosconst.ETAG)) + requestid = hdr.Get(nosconst.X_NOS_REQUEST_ID) + + return requestid, etag +} + +func PopulateAllHeader(response *http.Response) *model.ObjectMetadata { + hdr := response.Header + result := &model.ObjectMetadata{ + Metadata: map[string]string{}, + } + + for key, value := range hdr { + if value != nil { + if strings.EqualFold(key, nosconst.CONTENT_LENGTH) { + result.ContentLength, _ = strconv.ParseInt(value[0], 10, 64) + } else if strings.EqualFold(key, nosconst.ETAG) { + result.Metadata[nosconst.ETAG] = RemoveQuotes(value[0]) + } else { + result.Metadata[key] = value[0] + } + } + } + + return result +} + +func ProcessClientError(statCode int, bucket, object string, msg string) error { + var resource string + if bucket != "" { + resource += "/" + bucket + } + if object != "" { + resource += "/" + object + } + clientError := noserror.NewClientError(statCode, resource, msg) + + return clientError +} + +func ProcessServerError(response *http.Response, bucketName, objectName string) error { + var nosErr *noserror.NosError + + resource := bucketName + "/" + objectName + requestId := response.Header.Get(nosconst.X_NOS_REQUEST_ID) + contenttype := response.Header.Get(nosconst.CONTENT_TYPE) + + serverError := &noserror.ServerError{ + StatusCode: response.StatusCode, + RequestId: requestId, + } + + content, err := ioutil.ReadAll(response.Body) + if err != nil { + nosErr = noserror.NewNosError("", noserror.ERROR_MSG_READCONTENT_ERROR, resource, requestId) + serverError.NosErr = nosErr + } else { + if strings.Contains(contenttype, nosconst.JSON_TYPE) { + if err = json.Unmarshal(content, &serverError); err != nil { + nosErr = noserror.NewNosError("", noserror.ERROR_MSG_PARSEJSON_ERROR, resource, requestId) + serverError.NosErr = nosErr + } + } else { + if err = xml.Unmarshal(content, &nosErr); err != nil { + nosErr = noserror.NewNosError("", noserror.ERROR_MSG_PARSEXML_ERROR, resource, requestId) + } + serverError.NosErr = nosErr + } + } + + return serverError +} + +func NosUrlEncode(origin string) string { + str := strings.Replace(url.QueryEscape(origin), "+", "%20", -1) + str = strings.Replace(str, "~", "%7E", -1) + str = strings.Replace(str, "%2A", "*", -1) + return str +} + +func InitUserAgent() string { + str := nosconst.SDKNAME + "/" + nosconst.VERSION + " " + str += runtime.GOOS + "/" + runtime.GOARCH + "/" + str += "golang version:" + runtime.Version() + + return str +}