Pg to bun (#148)
* start moving to bun * changing more stuff * more * and yet more * tests passing * seems stable now * more big changes * small fix * little fixes
This commit is contained in:
parent
071eca20ce
commit
2dc9fc1626
|
@ -129,10 +129,10 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
|
||||||
* [gin-contrib/static](https://github.com/gin-contrib/static); Gin static page middleware. [MIT License](https://spdx.org/licenses/MIT.html)
|
* [gin-contrib/static](https://github.com/gin-contrib/static); Gin static page middleware. [MIT License](https://spdx.org/licenses/MIT.html)
|
||||||
* [go-fed/activity](https://github.com/go-fed/activity); Golang ActivityPub/ActivityStreams library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
* [go-fed/activity](https://github.com/go-fed/activity); Golang ActivityPub/ActivityStreams library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
||||||
* [go-fed/httpsig](https://github.com/go-fed/httpsig); secure HTTP signature library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
* [go-fed/httpsig](https://github.com/go-fed/httpsig); secure HTTP signature library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
||||||
* [go-pg/pg](https://github.com/go-pg/pg); Postgres ORM library. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
|
|
||||||
* [google/uuid](https://github.com/google/uuid); UUID generation. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html)
|
* [google/uuid](https://github.com/google/uuid); UUID generation. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html)
|
||||||
* [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
|
* [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
|
||||||
* [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
|
* [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
* [jackc/pgx](https://github.com/jackc/pgx); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
* [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
* [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
||||||
* [mvdan/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
* [mvdan/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
||||||
* [nfnt/resize](https://github.com/nfnt/resize); convenient image resizing. [ISC License](https://spdx.org/licenses/ISC.html).
|
* [nfnt/resize](https://github.com/nfnt/resize); convenient image resizing. [ISC License](https://spdx.org/licenses/ISC.html).
|
||||||
|
@ -145,6 +145,7 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
|
||||||
* [superseriousbusiness/oauth2](https://github.com/superseriousbusiness/oauth2) forked from [go-oauth2/oauth2](https://github.com/go-oauth2/oauth2); oauth server framework and token handling. [MIT License](https://spdx.org/licenses/MIT.html).
|
* [superseriousbusiness/oauth2](https://github.com/superseriousbusiness/oauth2) forked from [go-oauth2/oauth2](https://github.com/go-oauth2/oauth2); oauth server framework and token handling. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
* [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger); Swagger OpenAPI spec generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
|
* [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger); Swagger OpenAPI spec generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
|
||||||
* [tdewolff/minify](https://github.com/tdewolff/minify); HTML minification. [MIT License](https://spdx.org/licenses/MIT.html).
|
* [tdewolff/minify](https://github.com/tdewolff/minify); HTML minification. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
* [uptrace/bun](https://github.com/uptrace/bun); database ORM. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
|
||||||
* [urfave/cli](https://github.com/urfave/cli); command-line interface framework. [MIT License](https://spdx.org/licenses/MIT.html).
|
* [urfave/cli](https://github.com/urfave/cli); command-line interface framework. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
* [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
|
* [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -21,15 +21,15 @@ require (
|
||||||
github.com/go-errors/errors v1.4.0 // indirect
|
github.com/go-errors/errors v1.4.0 // indirect
|
||||||
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
|
github.com/go-fed/activity v1.0.1-0.20210803212804-d866ba75dd0f
|
||||||
github.com/go-fed/httpsig v1.1.0
|
github.com/go-fed/httpsig v1.1.0
|
||||||
github.com/go-pg/pg/extra/pgdebug v0.2.0
|
|
||||||
github.com/go-pg/pg/v10 v10.10.3
|
|
||||||
github.com/go-playground/validator/v10 v10.7.0 // indirect
|
github.com/go-playground/validator/v10 v10.7.0 // indirect
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/sessions v1.2.1 // indirect
|
github.com/gorilla/sessions v1.2.1 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/h2non/filetype v1.1.1
|
github.com/h2non/filetype v1.1.1
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0
|
||||||
github.com/json-iterator/go v1.1.11 // indirect
|
github.com/json-iterator/go v1.1.11 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||||
|
@ -38,7 +38,6 @@ require (
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/onsi/gomega v1.14.0 // indirect
|
|
||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0
|
github.com/russross/blackfriday/v2 v2.1.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
|
@ -47,13 +46,15 @@ require (
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.0-SSB
|
github.com/superseriousbusiness/oauth2/v4 v4.3.0-SSB
|
||||||
github.com/tdewolff/minify/v2 v2.9.21
|
github.com/tdewolff/minify/v2 v2.9.21
|
||||||
github.com/tidwall/buntdb v1.2.4 // indirect
|
github.com/tidwall/buntdb v1.2.4 // indirect
|
||||||
|
github.com/uptrace/bun v0.4.3
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v0.4.3
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
|
|
||||||
github.com/wagslane/go-password-validator v0.3.0
|
github.com/wagslane/go-password-validator v0.3.0
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||||
golang.org/x/text v0.3.6
|
golang.org/x/text v0.3.6
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
|
167
go.sum
167
go.sum
|
@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
github.com/ReneKroon/ttlcache v1.7.0 h1:8BkjFfrzVFXyrqnMtezAaJ6AHPSsVV10m6w28N/Fgkk=
|
github.com/ReneKroon/ttlcache v1.7.0 h1:8BkjFfrzVFXyrqnMtezAaJ6AHPSsVV10m6w28N/Fgkk=
|
||||||
github.com/ReneKroon/ttlcache v1.7.0/go.mod h1:8BGGzdumrIjWxdRx8zpK6L3oGMWvIXdvB2GD1cfvd+I=
|
github.com/ReneKroon/ttlcache v1.7.0/go.mod h1:8BGGzdumrIjWxdRx8zpK6L3oGMWvIXdvB2GD1cfvd+I=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
|
@ -54,11 +56,16 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
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/go-oidc/v3 v3.0.0 h1:/mAA0XMgYJw2Uqm7WKGCsKnjitE/+A0FFbOmiRJm7LQ=
|
github.com/coreos/go-oidc/v3 v3.0.0 h1:/mAA0XMgYJw2Uqm7WKGCsKnjitE/+A0FFbOmiRJm7LQ=
|
||||||
github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
|
||||||
|
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/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
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-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
github.com/dave/jennifer v1.3.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
@ -104,7 +111,6 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
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/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 h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
||||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||||
|
@ -131,13 +137,8 @@ github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-pg/pg/extra/pgdebug v0.2.0 h1:t62UhMiV6KYAxSWojwIJiyX06TdepkzCeIzdeb00184=
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
github.com/go-pg/pg/extra/pgdebug v0.2.0/go.mod h1:KmW//PLshMAQunfInLv9mFIbYXuGplOY9bc6qo3CaY0=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-pg/pg/v10 v10.6.2/go.mod h1:BfgPoQnD2wXNd986RYEHzikqv9iE875PrFaZ9vXvtNM=
|
|
||||||
github.com/go-pg/pg/v10 v10.10.3 h1:WobSfk5I+v7XwD1h9x2B7n4slDzjdBIonJ5PID95Aag=
|
|
||||||
github.com/go-pg/pg/v10 v10.10.3/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E=
|
|
||||||
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
|
||||||
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
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/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||||
|
@ -149,13 +150,15 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
|
||||||
github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
github.com/go-playground/validator/v10 v10.6.1/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
||||||
github.com/go-playground/validator/v10 v10.7.0 h1:gLi5ajTBBheLNt0ctewgq7eolXoDALQd5/y90Hh9ZgM=
|
github.com/go-playground/validator/v10 v10.7.0 h1:gLi5ajTBBheLNt0ctewgq7eolXoDALQd5/y90Hh9ZgM=
|
||||||
github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
github.com/go-playground/validator/v10 v10.7.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
|
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
|
||||||
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||||
github.com/goccy/go-json v0.5.1 h1:R9UYTOUvo7eIY9aeDMZ4L6OVtHaSr1k2No9W6MKjXrA=
|
github.com/goccy/go-json v0.5.1 h1:R9UYTOUvo7eIY9aeDMZ4L6OVtHaSr1k2No9W6MKjXrA=
|
||||||
github.com/goccy/go-json v0.5.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.5.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||||
|
@ -186,7 +189,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
@ -200,7 +202,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/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 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
@ -243,6 +244,53 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
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/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||||
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
|
||||||
|
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
|
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
|
||||||
|
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
|
||||||
|
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
@ -260,18 +308,30 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
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 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
|
||||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
|
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.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
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-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||||
|
@ -289,24 +349,17 @@ 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/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
|
||||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
|
||||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
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/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
|
||||||
github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
|
|
||||||
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
|
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
@ -315,13 +368,22 @@ github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY
|
||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
|
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/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
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/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 v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
@ -330,6 +392,8 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
@ -382,6 +446,10 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||||
|
github.com/uptrace/bun v0.4.3 h1:x6bjDqwjxwM/9Q1eauhkznuvTrz/rLiCK2p4tT63sAE=
|
||||||
|
github.com/uptrace/bun v0.4.3/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v0.4.3 h1:lM2IUKpU99110chKkupw3oTfXiOKpB0hTJIe6frqQDo=
|
||||||
|
github.com/uptrace/bun/dialect/pgdialect v0.4.3/go.mod h1:BaNvWejl32oKUhwpFkw/eNcWldzIlVY4nfw/sNul0s8=
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
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 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
@ -389,16 +457,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
||||||
github.com/valyala/fasthttp v1.14.0 h1:67bfuW9azCMwW/Jlq/C+VeihNpAuJMWkYPBig1gdi3A=
|
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/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/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
|
||||||
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
|
||||||
github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.1/go.mod h1:xlngVLeyQ/Qi05oQxhQ+oTuqa03RjMwMfk/7/TCs+QI=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.1/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
|
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
|
||||||
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
|
||||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I=
|
github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I=
|
||||||
|
@ -419,25 +479,36 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
|
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.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
go.uber.org/goleak v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4=
|
go.uber.org/goleak v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4=
|
||||||
go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
|
go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
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.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180527072434-ab813273cd59/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -484,6 +555,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
@ -501,12 +573,8 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -524,14 +592,17 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/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-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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -561,17 +632,15 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -579,6 +648,7 @@ 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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -592,14 +662,18 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
@ -607,6 +681,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
|
||||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
@ -625,8 +700,9 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -713,16 +789,15 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
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/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
@ -743,8 +818,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w=
|
|
||||||
mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ=
|
|
||||||
mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
|
mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
|
||||||
mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=
|
mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
|
|
@ -101,7 +101,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
form.IP = signUpIP
|
form.IP = signUpIP
|
||||||
|
|
||||||
ti, err := m.processor.AccountCreate(authed, form)
|
ti, err := m.processor.AccountCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorf("internal server error while creating new account: %s", err)
|
l.Errorf("internal server error while creating new account: %s", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (m *Module) AccountGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctInfo, err := m.processor.AccountGet(authed, targetAcctID)
|
acctInfo, err := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
|
||||||
return
|
return
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctSensitive, err := m.processor.AccountUpdate(authed, form)
|
acctSensitive, err := m.processor.AccountUpdate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("could not update account: %s", err)
|
l.Debugf("could not update account: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -79,7 +79,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedToken, oauth.TokenToOauthToken(suite.testTokens["local_account_1"]))
|
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Request = httptest.NewRequest(http.MethodPatch, fmt.Sprintf("http://localhost:8080/%s", account.UpdateCredentialsPath), bytes.NewReader(requestBody.Bytes())) // the endpoint we're hitting
|
ctx.Request = httptest.NewRequest(http.MethodPatch, fmt.Sprintf("http://localhost:8080/%s", account.UpdateCredentialsPath), bytes.NewReader(requestBody.Bytes())) // the endpoint we're hitting
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctSensitive, err := m.processor.AccountGet(authed, authed.Account.ID)
|
acctSensitive, err := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error getting account from processor: %s", err)
|
l.Debugf("error getting account from processor: %s", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountBlockCreate(authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
form.ID = targetAcctID
|
form.ID = targetAcctID
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountFollowCreate(authed, form)
|
relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
followers, errWithCode := m.processor.AccountFollowersGet(authed, targetAcctID)
|
followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
following, errWithCode := m.processor.AccountFollowingGet(authed, targetAcctID)
|
following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
||||||
relationships := []model.Relationship{}
|
relationships := []model.Relationship{}
|
||||||
|
|
||||||
for _, targetAccountID := range targetAccountIDs {
|
for _, targetAccountID := range targetAccountIDs {
|
||||||
r, errWithCode := m.processor.AccountRelationshipGet(authed, targetAccountID)
|
r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -166,7 +166,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
mediaOnly = i
|
mediaOnly = i
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses, errWithCode := m.processor.AccountStatusesGet(authed, targetAcctID, limit, excludeReplies, maxID, pinnedOnly, mediaOnly)
|
statuses, errWithCode := m.processor.AccountStatusesGet(c.Request.Context(), authed, targetAcctID, limit, excludeReplies, maxID, pinnedOnly, mediaOnly)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error from processor account statuses get: %s", errWithCode)
|
l.Debugf("error from processor account statuses get: %s", errWithCode)
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -72,7 +72,7 @@ func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountBlockRemove(authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountFollowRemove(authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debug(errWithCode.Error())
|
l.Debug(errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -141,7 +141,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
if imp {
|
if imp {
|
||||||
// we're importing multiple blocks
|
// we're importing multiple blocks
|
||||||
domainBlocks, err := m.processor.AdminDomainBlocksImport(authed, form)
|
domainBlocks, err := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error importing domain blocks: %s", err)
|
l.Debugf("error importing domain blocks: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
@ -150,7 +150,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, domainBlocks)
|
c.JSON(http.StatusOK, domainBlocks)
|
||||||
} else {
|
} else {
|
||||||
// we're just creating one block
|
// we're just creating one block
|
||||||
domainBlock, err := m.processor.AdminDomainBlockCreate(authed, form)
|
domainBlock, err := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error creating domain block: %s", err)
|
l.Debugf("error creating domain block: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(authed, domainBlockID)
|
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error deleting domain block: %s", errWithCode.Error())
|
l.Debugf("error deleting domain block: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||||
export = i
|
export = i
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlock, err := m.processor.AdminDomainBlockGet(authed, domainBlockID, export)
|
domainBlock, err := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error getting domain block: %s", err)
|
l.Debugf("error getting domain block: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||||
export = i
|
export = i
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlocks, err := m.processor.AdminDomainBlocksGet(authed, export)
|
domainBlocks, err := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error getting domain blocks: %s", err)
|
l.Debugf("error getting domain blocks: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (m *Module) emojiCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoEmoji, err := m.processor.AdminEmojiCreate(authed, form)
|
mastoEmoji, err := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error creating emoji: %s", err)
|
l.Debugf("error creating emoji: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -101,7 +101,7 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoApp, err := m.processor.AppCreate(authed, form)
|
mastoApp, err := m.processor.AppCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/pg"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -104,7 +104,7 @@ func (suite *AuthTestSuite) SetupTest() {
|
||||||
|
|
||||||
log := logrus.New()
|
log := logrus.New()
|
||||||
log.SetLevel(logrus.TraceLevel)
|
log.SetLevel(logrus.TraceLevel)
|
||||||
db, err := pg.NewPostgresService(context.Background(), suite.config, log)
|
db, err := bundb.NewBunDBService(context.Background(), suite.config, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Panicf("error creating database connection: %s", err)
|
logrus.Panicf("error creating database connection: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -120,23 +120,23 @@ func (suite *AuthTestSuite) SetupTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range models {
|
for _, m := range models {
|
||||||
if err := suite.db.CreateTable(m); err != nil {
|
if err := suite.db.CreateTable(context.Background(), m); err != nil {
|
||||||
logrus.Panicf("db connection error: %s", err)
|
logrus.Panicf("db connection error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.oauthServer = oauth.New(suite.db, log)
|
suite.oauthServer = oauth.New(suite.db, log)
|
||||||
|
|
||||||
if err := suite.db.Put(suite.testAccount); err != nil {
|
if err := suite.db.Put(context.Background(), suite.testAccount); err != nil {
|
||||||
logrus.Panicf("could not insert test account into db: %s", err)
|
logrus.Panicf("could not insert test account into db: %s", err)
|
||||||
}
|
}
|
||||||
if err := suite.db.Put(suite.testUser); err != nil {
|
if err := suite.db.Put(context.Background(), suite.testUser); err != nil {
|
||||||
logrus.Panicf("could not insert test user into db: %s", err)
|
logrus.Panicf("could not insert test user into db: %s", err)
|
||||||
}
|
}
|
||||||
if err := suite.db.Put(suite.testClient); err != nil {
|
if err := suite.db.Put(context.Background(), suite.testClient); err != nil {
|
||||||
logrus.Panicf("could not insert test client into db: %s", err)
|
logrus.Panicf("could not insert test client into db: %s", err)
|
||||||
}
|
}
|
||||||
if err := suite.db.Put(suite.testApplication); err != nil {
|
if err := suite.db.Put(context.Background(), suite.testApplication); err != nil {
|
||||||
logrus.Panicf("could not insert test application into db: %s", err)
|
logrus.Panicf("could not insert test application into db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ func (suite *AuthTestSuite) TearDownTest() {
|
||||||
>smodel.Application{},
|
>smodel.Application{},
|
||||||
}
|
}
|
||||||
for _, m := range models {
|
for _, m := range models {
|
||||||
if err := suite.db.DropTable(m); err != nil {
|
if err := suite.db.DropTable(context.Background(), m); err != nil {
|
||||||
logrus.Panicf("error dropping table: %s", err)
|
logrus.Panicf("error dropping table: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,30 +70,23 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app := >smodel.Application{
|
app := >smodel.Application{}
|
||||||
ClientID: clientID,
|
if err := m.db.GetWhere(c.Request.Context(), []db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil {
|
||||||
}
|
|
||||||
if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil {
|
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can also use the userid of the user to fetch their username from the db to greet them nicely <3
|
// we can also use the userid of the user to fetch their username from the db to greet them nicely <3
|
||||||
user := >smodel.User{
|
user := >smodel.User{}
|
||||||
ID: userID,
|
if err := m.db.GetByID(c.Request.Context(), user.ID, user); err != nil {
|
||||||
}
|
|
||||||
if err := m.db.GetByID(user.ID, user); err != nil {
|
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acct := >smodel.Account{
|
acct, err := m.db.GetAccountByID(c.Request.Context(), user.AccountID)
|
||||||
ID: user.AccountID,
|
if err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.db.GetByID(acct.ID, acct); err != nil {
|
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -80,13 +81,13 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
app := >smodel.Application{
|
app := >smodel.Application{
|
||||||
ClientID: clientID,
|
ClientID: clientID,
|
||||||
}
|
}
|
||||||
if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil {
|
if err := m.db.GetWhere(c.Request.Context(), []db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := m.parseUserFromClaims(claims, net.IP(c.ClientIP()), app.ID)
|
user, err := m.parseUserFromClaims(c.Request.Context(), claims, net.IP(c.ClientIP()), app.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||||
|
@ -103,14 +104,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, error) {
|
func (m *Module) parseUserFromClaims(ctx context.Context, claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, error) {
|
||||||
if claims.Email == "" {
|
if claims.Email == "" {
|
||||||
return nil, errors.New("no email returned in claims")
|
return nil, errors.New("no email returned in claims")
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if we already have a user for this email address
|
// see if we already have a user for this email address
|
||||||
user := >smodel.User{}
|
user := >smodel.User{}
|
||||||
err := m.db.GetWhere([]db.Where{{Key: "email", Value: claims.Email}}, user)
|
err := m.db.GetWhere(ctx, []db.Where{{Key: "email", Value: claims.Email}}, user)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// we do! so we can just return it
|
// we do! so we can just return it
|
||||||
return user, nil
|
return user, nil
|
||||||
|
@ -122,7 +123,7 @@ func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe we have an unconfirmed user
|
// maybe we have an unconfirmed user
|
||||||
err = m.db.GetWhere([]db.Where{{Key: "unconfirmed_email", Value: claims.Email}}, user)
|
err = m.db.GetWhere(ctx, []db.Where{{Key: "unconfirmed_email", Value: claims.Email}}, user)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// user is unconfirmed so return an error
|
// user is unconfirmed so return an error
|
||||||
return nil, fmt.Errorf("user with email address %s is unconfirmed", claims.Email)
|
return nil, fmt.Errorf("user with email address %s is unconfirmed", claims.Email)
|
||||||
|
@ -137,9 +138,13 @@ func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID strin
|
||||||
// however, because we trust the OIDC provider, we should now create a user + account with the provided claims
|
// however, because we trust the OIDC provider, we should now create a user + account with the provided claims
|
||||||
|
|
||||||
// check if the email address is available for use; if it's not there's nothing we can so
|
// check if the email address is available for use; if it's not there's nothing we can so
|
||||||
if err := m.db.IsEmailAvailable(claims.Email); err != nil {
|
emailAvailable, err := m.db.IsEmailAvailable(ctx, claims.Email)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("email %s not available: %s", claims.Email, err)
|
return nil, fmt.Errorf("email %s not available: %s", claims.Email, err)
|
||||||
}
|
}
|
||||||
|
if !emailAvailable {
|
||||||
|
return nil, fmt.Errorf("email %s in use", claims.Email)
|
||||||
|
}
|
||||||
|
|
||||||
// now we need a username
|
// now we need a username
|
||||||
var username string
|
var username string
|
||||||
|
@ -180,12 +185,11 @@ func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID strin
|
||||||
// note that for the first iteration, iString is still "" when the check is made, so our first choice
|
// note that for the first iteration, iString is still "" when the check is made, so our first choice
|
||||||
// is still the raw username with no integer stuck on the end
|
// is still the raw username with no integer stuck on the end
|
||||||
for i := 1; !found; i = i + 1 {
|
for i := 1; !found; i = i + 1 {
|
||||||
if err := m.db.IsUsernameAvailable(username + iString); err != nil {
|
usernameAvailable, err := m.db.IsUsernameAvailable(ctx, username+iString)
|
||||||
if strings.Contains(err.Error(), "db error") {
|
if err != nil {
|
||||||
// if there's an actual db error we should return
|
return nil, err
|
||||||
return nil, fmt.Errorf("error checking username availability: %s", err)
|
}
|
||||||
}
|
if usernameAvailable {
|
||||||
} else {
|
|
||||||
// no error so we've found a username that works
|
// no error so we've found a username that works
|
||||||
found = true
|
found = true
|
||||||
username = username + iString
|
username = username + iString
|
||||||
|
@ -209,7 +213,7 @@ func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID strin
|
||||||
password := uuid.NewString() + uuid.NewString()
|
password := uuid.NewString() + uuid.NewString()
|
||||||
|
|
||||||
// create the user! this will also create an account and store it in the database so we don't need to do that here
|
// create the user! this will also create an account and store it in the database so we don't need to do that here
|
||||||
user, err = m.db.NewSignup(username, "", m.config.AccountsConfig.RequireApproval, claims.Email, password, ip, "", appID, claims.EmailVerified, admin)
|
user, err = m.db.NewSignup(ctx, username, "", m.config.AccountsConfig.RequireApproval, claims.Email, password, ip, "", appID, claims.EmailVerified, admin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating user: %s", err)
|
return nil, fmt.Errorf("error creating user: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,15 +49,15 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) {
|
||||||
|
|
||||||
// fetch user's and account for this user id
|
// fetch user's and account for this user id
|
||||||
user := >smodel.User{}
|
user := >smodel.User{}
|
||||||
if err := m.db.GetByID(uid, user); err != nil || user == nil {
|
if err := m.db.GetByID(c.Request.Context(), uid, user); err != nil || user == nil {
|
||||||
l.Warnf("no user found for validated uid %s", uid)
|
l.Warnf("no user found for validated uid %s", uid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Set(oauth.SessionAuthorizedUser, user)
|
c.Set(oauth.SessionAuthorizedUser, user)
|
||||||
l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedUser, user)
|
l.Tracef("set gin context %s to %+v", oauth.SessionAuthorizedUser, user)
|
||||||
|
|
||||||
acct := >smodel.Account{}
|
acct, err := m.db.GetAccountByID(c.Request.Context(), user.AccountID)
|
||||||
if err := m.db.GetByID(user.AccountID, acct); err != nil || acct == nil {
|
if err != nil || acct == nil {
|
||||||
l.Warnf("no account found for validated user %s", uid)
|
l.Warnf("no account found for validated user %s", uid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) {
|
||||||
if cid := ti.GetClientID(); cid != "" {
|
if cid := ti.GetClientID(); cid != "" {
|
||||||
l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope())
|
l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope())
|
||||||
app := >smodel.Application{}
|
app := >smodel.Application{}
|
||||||
if err := m.db.GetWhere([]db.Where{{Key: "client_id", Value: cid}}, app); err != nil {
|
if err := m.db.GetWhere(c.Request.Context(), []db.Where{{Key: "client_id", Value: cid}}, app); err != nil {
|
||||||
l.Tracef("no app found for client %s", cid)
|
l.Tracef("no app found for client %s", cid)
|
||||||
}
|
}
|
||||||
c.Set(oauth.SessionAuthorizedApplication, app)
|
c.Set(oauth.SessionAuthorizedApplication, app)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
l.Tracef("parsed form: %+v", form)
|
l.Tracef("parsed form: %+v", form)
|
||||||
|
|
||||||
userid, err := m.ValidatePassword(form.Email, form.Password)
|
userid, err := m.ValidatePassword(c.Request.Context(), form.Email, form.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(http.StatusForbidden, err.Error())
|
c.String(http.StatusForbidden, err.Error())
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
|
@ -96,7 +97,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
||||||
// The goal is to authenticate the password against the one for that email
|
// The goal is to authenticate the password against the one for that email
|
||||||
// address stored in the database. If OK, we return the userid (a ulid) for that user,
|
// address stored in the database. If OK, we return the userid (a ulid) for that user,
|
||||||
// so that it can be used in further Oauth flows to generate a token/retreieve an oauth client from the db.
|
// so that it can be used in further Oauth flows to generate a token/retreieve an oauth client from the db.
|
||||||
func (m *Module) ValidatePassword(email string, password string) (userid string, err error) {
|
func (m *Module) ValidatePassword(ctx context.Context, email string, password string) (userid string, err error) {
|
||||||
l := m.log.WithField("func", "ValidatePassword")
|
l := m.log.WithField("func", "ValidatePassword")
|
||||||
|
|
||||||
// make sure an email/password was provided and bail if not
|
// make sure an email/password was provided and bail if not
|
||||||
|
@ -108,7 +109,7 @@ func (m *Module) ValidatePassword(email string, password string) (userid string,
|
||||||
// first we select the user from the database based on email address, bail if no user found for that email
|
// first we select the user from the database based on email address, bail if no user found for that email
|
||||||
gtsUser := >smodel.User{}
|
gtsUser := >smodel.User{}
|
||||||
|
|
||||||
if err := m.db.GetWhere([]db.Where{{Key: "email", Value: email}}, gtsUser); err != nil {
|
if err := m.db.GetWhere(ctx, []db.Where{{Key: "email", Value: email}}, gtsUser); err != nil {
|
||||||
l.Debugf("user %s was not retrievable from db during oauth authorization attempt: %s", email, err)
|
l.Debugf("user %s was not retrievable from db during oauth authorization attempt: %s", email, err)
|
||||||
return incorrectPassword()
|
return incorrectPassword()
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
||||||
limit = int(i)
|
limit = int(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errWithCode := m.processor.BlocksGet(authed, maxID, sinceID, limit)
|
resp, errWithCode := m.processor.BlocksGet(c.Request.Context(), authed, maxID, sinceID, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error from processor BlocksGet: %s", errWithCode)
|
l.Debugf("error from processor BlocksGet: %s", errWithCode)
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
||||||
limit = int(i)
|
limit = int(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errWithCode := m.processor.FavedTimelineGet(authed, maxID, minID, limit)
|
resp, errWithCode := m.processor.FavedTimelineGet(c.Request.Context(), authed, maxID, minID, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error from processor FavedTimelineGet: %s", errWithCode)
|
l.Debugf("error from processor FavedTimelineGet: %s", errWithCode)
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -25,8 +25,6 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||||
)
|
)
|
||||||
|
@ -66,17 +64,3 @@ func (m *FileServer) Route(s router.Router) error {
|
||||||
s.AttachHandler(http.MethodGet, fmt.Sprintf("%s/:%s/:%s/:%s/:%s", m.storageBase, AccountIDKey, MediaTypeKey, MediaSizeKey, FileNameKey), m.ServeFile)
|
s.AttachHandler(http.MethodGet, fmt.Sprintf("%s/:%s/:%s/:%s/:%s", m.storageBase, AccountIDKey, MediaTypeKey, MediaSizeKey, FileNameKey), m.ServeFile)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTables populates necessary tables in the given DB
|
|
||||||
func (m *FileServer) CreateTables(db db.DB) error {
|
|
||||||
models := []interface{}{
|
|
||||||
>smodel.MediaAttachment{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range models {
|
|
||||||
if err := db.CreateTable(m); err != nil {
|
|
||||||
return fmt.Errorf("error creating table: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (m *FileServer) ServeFile(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := m.processor.FileGet(authed, &model.GetContentRequestForm{
|
content, err := m.processor.FileGet(c.Request.Context(), authed, &model.GetContentRequestForm{
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
MediaType: mediaType,
|
MediaType: mediaType,
|
||||||
MediaSize: mediaSize,
|
MediaSize: mediaSize,
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (m *Module) FollowRequestAcceptPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r, errWithCode := m.processor.FollowRequestAccept(authed, originAccountID)
|
r, errWithCode := m.processor.FollowRequestAccept(c.Request.Context(), authed, originAccountID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debug(errWithCode.Error())
|
l.Debug(errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accts, errWithCode := m.processor.FollowRequestsGet(authed)
|
accts, errWithCode := m.processor.FollowRequestsGet(c.Request.Context(), authed)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -31,7 +31,7 @@ import (
|
||||||
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
|
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
|
||||||
l := m.log.WithField("func", "InstanceInformationGETHandler")
|
l := m.log.WithField("func", "InstanceInformationGETHandler")
|
||||||
|
|
||||||
instance, err := m.processor.InstanceGet(m.config.Host)
|
instance, err := m.processor.InstanceGet(c.Request.Context(), m.config.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error getting instance from processor: %s", err)
|
l.Debugf("error getting instance from processor: %s", err)
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
|
||||||
|
|
|
@ -116,7 +116,7 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
i, errWithCode := m.processor.InstancePatch(form)
|
i, errWithCode := m.processor.InstancePatch(c.Request.Context(), form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error with instance patch request: %s", errWithCode.Error())
|
l.Debugf("error with instance patch request: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -19,14 +19,11 @@
|
||||||
package media
|
package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||||
)
|
)
|
||||||
|
@ -63,17 +60,3 @@ func (m *Module) Route(s router.Router) error {
|
||||||
s.AttachHandler(http.MethodPut, BasePathWithID, m.MediaPUTHandler)
|
s.AttachHandler(http.MethodPut, BasePathWithID, m.MediaPUTHandler)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTables populates necessary tables in the given DB
|
|
||||||
func (m *Module) CreateTables(db db.DB) error {
|
|
||||||
models := []interface{}{
|
|
||||||
>smodel.MediaAttachment{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range models {
|
|
||||||
if err := db.CreateTable(m); err != nil {
|
|
||||||
return fmt.Errorf("error creating table: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Debug("calling processor media create func")
|
l.Debug("calling processor media create func")
|
||||||
mastoAttachment, err := m.processor.MediaCreate(authed, form)
|
mastoAttachment, err := m.processor.MediaCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error creating attachment: %s", err)
|
l.Debugf("error creating attachment: %s", err)
|
||||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful()
|
||||||
|
|
||||||
// set up the context for the request
|
// set up the context for the request
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (m *Module) MediaGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
attachment, errWithCode := m.processor.MediaGet(authed, attachmentID)
|
attachment, errWithCode := m.processor.MediaGet(c.Request.Context(), authed, attachmentID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
attachment, errWithCode := m.processor.MediaUpdate(authed, attachmentID, &form)
|
attachment, errWithCode := m.processor.MediaUpdate(c.Request.Context(), authed, attachmentID, &form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
return
|
return
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) {
|
||||||
sinceID = sinceIDString
|
sinceID = sinceIDString
|
||||||
}
|
}
|
||||||
|
|
||||||
notifs, errWithCode := m.processor.NotificationsGet(authed, limit, maxID, sinceID)
|
notifs, errWithCode := m.processor.NotificationsGet(c.Request.Context(), authed, limit, maxID, sinceID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error processing notifications get: %s", errWithCode.Error())
|
l.Debugf("error processing notifications get: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -164,7 +164,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
|
||||||
Following: following,
|
Following: following,
|
||||||
}
|
}
|
||||||
|
|
||||||
results, errWithCode := m.processor.SearchGet(authed, searchQuery)
|
results, errWithCode := m.processor.SearchGet(c.Request.Context(), authed, searchQuery)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error searching: %s", errWithCode.Error())
|
l.Debugf("error searching: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, errWithCode := m.processor.StatusBoost(authed, targetStatusID)
|
mastoStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error processing status boost: %s", errWithCode.Error())
|
l.Debugf("error processing status boost: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (suite *StatusBoostTestSuite) TearDownTest() {
|
||||||
func (suite *StatusBoostTestSuite) TestPostBoost() {
|
func (suite *StatusBoostTestSuite) TestPostBoost() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["admin_account_status_1"]
|
targetStatus := suite.testStatuses["admin_account_status_1"]
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ func (suite *StatusBoostTestSuite) TestPostBoost() {
|
||||||
func (suite *StatusBoostTestSuite) TestPostUnboostable() {
|
func (suite *StatusBoostTestSuite) TestPostUnboostable() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["local_account_2_status_4"]
|
targetStatus := suite.testStatuses["local_account_2_status_4"]
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ func (suite *StatusBoostTestSuite) TestPostUnboostable() {
|
||||||
func (suite *StatusBoostTestSuite) TestPostNotVisible() {
|
func (suite *StatusBoostTestSuite) TestPostNotVisible() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_2"]
|
t := suite.testTokens["local_account_2"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["local_account_1_status_3"] // this is a mutual only status and these accounts aren't mutuals
|
targetStatus := suite.testStatuses["local_account_1_status_3"] // this is a mutual only status and these accounts aren't mutuals
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (m *Module) StatusBoostedByGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoAccounts, err := m.processor.StatusBoostedBy(authed, targetStatusID)
|
mastoAccounts, err := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status boosted by request: %s", err)
|
l.Debugf("error processing status boosted by request: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (m *Module) StatusContextGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statusContext, errWithCode := m.processor.StatusGetContext(authed, targetStatusID)
|
statusContext, errWithCode := m.processor.StatusGetContext(c.Request.Context(), authed, targetStatusID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error getting status context: %s", errWithCode.Error())
|
l.Debugf("error getting status context: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -101,7 +101,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, err := m.processor.StatusCreate(authed, form)
|
mastoStatus, err := m.processor.StatusCreate(c.Request.Context(), authed, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status create: %s", err)
|
l.Debugf("error processing status create: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package status_test
|
package status_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -82,7 +83,7 @@ https://docs.gotosocial.org/en/latest/user_guide/posts/#links
|
||||||
func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -128,7 +129,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
||||||
}, statusReply.Tags[0])
|
}, statusReply.Tags[0])
|
||||||
|
|
||||||
gtsTag := >smodel.Tag{}
|
gtsTag := >smodel.Tag{}
|
||||||
err = suite.db.GetWhere([]db.Where{{Key: "name", Value: "helloworld"}}, gtsTag)
|
err = suite.db.GetWhere(context.Background(), []db.Where{{Key: "name", Value: "helloworld"}}, gtsTag)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
assert.Equal(suite.T(), statusReply.Account.ID, gtsTag.FirstSeenFromAccountID)
|
assert.Equal(suite.T(), statusReply.Account.ID, gtsTag.FirstSeenFromAccountID)
|
||||||
}
|
}
|
||||||
|
@ -136,7 +137,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
||||||
func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
|
func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -171,7 +172,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
|
||||||
func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -212,7 +213,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
||||||
// Try to reply to a status that doesn't exist
|
// Try to reply to a status that doesn't exist
|
||||||
func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
|
func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -243,7 +244,7 @@ func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
|
||||||
// Post a reply to the status of a local user that allows replies.
|
// Post a reply to the status of a local user that allows replies.
|
||||||
func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
|
func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -283,7 +284,7 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
|
||||||
// Take a media file which is currently not associated with a status, and attach it to a new status.
|
// Take a media file which is currently not associated with a status, and attach it to a new status.
|
||||||
func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
attachment := suite.testAttachments["local_account_1_unattached_1"]
|
attachment := suite.testAttachments["local_account_1_unattached_1"]
|
||||||
|
|
||||||
|
@ -322,12 +323,11 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
||||||
assert.Len(suite.T(), statusResponse.MediaAttachments, 1)
|
assert.Len(suite.T(), statusResponse.MediaAttachments, 1)
|
||||||
|
|
||||||
// get the updated media attachment from the database
|
// get the updated media attachment from the database
|
||||||
gtsAttachment := >smodel.MediaAttachment{}
|
gtsAttachment, err := suite.db.GetAttachmentByID(context.Background(), statusResponse.MediaAttachments[0].ID)
|
||||||
err = suite.db.GetByID(statusResponse.MediaAttachments[0].ID, gtsAttachment)
|
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
// convert it to a masto attachment
|
// convert it to a masto attachment
|
||||||
gtsAttachmentAsMasto, err := suite.tc.AttachmentToMasto(gtsAttachment)
|
gtsAttachmentAsMasto, err := suite.tc.AttachmentToMasto(context.Background(), gtsAttachment)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
// compare it with what we have now
|
// compare it with what we have now
|
||||||
|
|
|
@ -86,7 +86,7 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, err := m.processor.StatusDelete(authed, targetStatusID)
|
mastoStatus, err := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status delete: %s", err)
|
l.Debugf("error processing status delete: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, err := m.processor.StatusFave(authed, targetStatusID)
|
mastoStatus, err := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status fave: %s", err)
|
l.Debugf("error processing status fave: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (suite *StatusFaveTestSuite) TearDownTest() {
|
||||||
func (suite *StatusFaveTestSuite) TestPostFave() {
|
func (suite *StatusFaveTestSuite) TestPostFave() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["admin_account_status_2"]
|
targetStatus := suite.testStatuses["admin_account_status_2"]
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ func (suite *StatusFaveTestSuite) TestPostFave() {
|
||||||
func (suite *StatusFaveTestSuite) TestPostUnfaveable() {
|
func (suite *StatusFaveTestSuite) TestPostUnfaveable() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["local_account_2_status_3"] // this one is unlikeable and unreplyable
|
targetStatus := suite.testStatuses["local_account_2_status_3"] // this one is unlikeable and unreplyable
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoAccounts, err := m.processor.StatusFavedBy(authed, targetStatusID)
|
mastoAccounts, err := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status faved by request: %s", err)
|
l.Debugf("error processing status faved by request: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -69,7 +69,7 @@ func (suite *StatusFavedByTestSuite) TearDownTest() {
|
||||||
|
|
||||||
func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
|
func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
|
||||||
t := suite.testTokens["local_account_2"]
|
t := suite.testTokens["local_account_2"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
targetStatus := suite.testStatuses["admin_account_status_1"] // this status is faved by local_account_1
|
targetStatus := suite.testStatuses["admin_account_status_1"] // this status is faved by local_account_1
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, err := m.processor.StatusGet(authed, targetStatusID)
|
mastoStatus, err := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status get: %s", err)
|
l.Debugf("error processing status get: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -84,7 +84,7 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, errWithCode := m.processor.StatusUnboost(authed, targetStatusID)
|
mastoStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error processing status unboost: %s", errWithCode.Error())
|
l.Debugf("error processing status unboost: %s", errWithCode.Error())
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoStatus, err := m.processor.StatusUnfave(authed, targetStatusID)
|
mastoStatus, err := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error processing status unfave: %s", err)
|
l.Debugf("error processing status unfave: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (suite *StatusUnfaveTestSuite) TearDownTest() {
|
||||||
func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
|
func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// this is the status we wanna unfave: in the testrig it's already faved by this account
|
// this is the status we wanna unfave: in the testrig it's already faved by this account
|
||||||
targetStatus := suite.testStatuses["admin_account_status_1"]
|
targetStatus := suite.testStatuses["admin_account_status_1"]
|
||||||
|
@ -120,7 +120,7 @@ func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
|
||||||
func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
|
func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
|
||||||
|
|
||||||
t := suite.testTokens["local_account_1"]
|
t := suite.testTokens["local_account_1"]
|
||||||
oauthToken := oauth.TokenToOauthToken(t)
|
oauthToken := oauth.DBTokenToToken(t)
|
||||||
|
|
||||||
// this is the status we wanna unfave: in the testrig it's not faved by this account
|
// this is the status we wanna unfave: in the testrig it's not faved by this account
|
||||||
targetStatus := suite.testStatuses["admin_account_status_2"]
|
targetStatus := suite.testStatuses["admin_account_status_2"]
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure a valid token has been provided and obtain the associated account
|
// make sure a valid token has been provided and obtain the associated account
|
||||||
account, err := m.processor.AuthorizeStreamingRequest(accessToken)
|
account, err := m.processor.AuthorizeStreamingRequest(c.Request.Context(), accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "could not authorize with given token"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "could not authorize with given token"})
|
||||||
return
|
return
|
||||||
|
@ -147,7 +147,7 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
|
||||||
defer conn.Close() // whatever happens, when we leave this function we want to close the websocket connection
|
defer conn.Close() // whatever happens, when we leave this function we want to close the websocket connection
|
||||||
|
|
||||||
// inform the processor that we have a new connection and want a stream for it
|
// inform the processor that we have a new connection and want a stream for it
|
||||||
stream, errWithCode := m.processor.OpenStreamForAccount(account, streamType)
|
stream, errWithCode := m.processor.OpenStreamForAccount(c.Request.Context(), account, streamType)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
c.JSON(errWithCode.Code(), errWithCode.Safe())
|
c.JSON(errWithCode.Code(), errWithCode.Safe())
|
||||||
return
|
return
|
||||||
|
|
|
@ -153,7 +153,7 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
|
||||||
local = i
|
local = i
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errWithCode := m.processor.HomeTimelineGet(authed, maxID, sinceID, minID, limit, local)
|
resp, errWithCode := m.processor.HomeTimelineGet(c.Request.Context(), authed, maxID, sinceID, minID, limit, local)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error from processor HomeTimelineGet: %s", errWithCode)
|
l.Debugf("error from processor HomeTimelineGet: %s", errWithCode)
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -153,7 +153,7 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
|
||||||
local = i
|
local = i
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, errWithCode := m.processor.PublicTimelineGet(authed, maxID, sinceID, minID, limit, local)
|
resp, errWithCode := m.processor.PublicTimelineGet(c.Request.Context(), authed, maxID, sinceID, minID, limit, local)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
l.Debugf("error from processor PublicTimelineGet: %s", errWithCode)
|
l.Debugf("error from processor PublicTimelineGet: %s", errWithCode)
|
||||||
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (m *Module) NodeInfoGETHandler(c *gin.Context) {
|
||||||
"user-agent": c.Request.UserAgent(),
|
"user-agent": c.Request.UserAgent(),
|
||||||
})
|
})
|
||||||
|
|
||||||
ni, err := m.processor.GetNodeInfo(c.Request)
|
ni, err := m.processor.GetNodeInfo(c.Request.Context(), c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error with get node info request: %s", err)
|
l.Debugf("error with get node info request: %s", err)
|
||||||
c.JSON(err.Code(), err.Safe())
|
c.JSON(err.Code(), err.Safe())
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (m *Module) NodeInfoWellKnownGETHandler(c *gin.Context) {
|
||||||
"user-agent": c.Request.UserAgent(),
|
"user-agent": c.Request.UserAgent(),
|
||||||
})
|
})
|
||||||
|
|
||||||
niRel, err := m.processor.GetNodeInfoRel(c.Request)
|
niRel, err := m.processor.GetNodeInfoRel(c.Request.Context(), c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error with get node info rel request: %s", err)
|
l.Debugf("error with get node info rel request: %s", err)
|
||||||
c.JSON(err.Code(), err.Safe())
|
c.JSON(err.Code(), err.Safe())
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
||||||
|
|
||||||
// convert person to account
|
// convert person to account
|
||||||
// since this account is already known, we should get a pretty full model of it from the conversion
|
// since this account is already known, we should get a pretty full model of it from the conversion
|
||||||
a, err := suite.tc.ASRepresentationToAccount(person, false)
|
a, err := suite.tc.ASRepresentationToAccount(context.Background(), person, false)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
assert.EqualValues(suite.T(), targetAccount.Username, a.Username)
|
assert.EqualValues(suite.T(), targetAccount.Username, a.Username)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ func (m *Module) SignatureCheck(c *gin.Context) {
|
||||||
// we managed to parse the url!
|
// we managed to parse the url!
|
||||||
|
|
||||||
// if the domain is blocked we want to bail as early as possible
|
// if the domain is blocked we want to bail as early as possible
|
||||||
blocked, err := m.db.IsURIBlocked(requestingPublicKeyID)
|
blocked, err := m.db.IsURIBlocked(c.Request.Context(), requestingPublicKeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err)
|
l.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err)
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
|
|
@ -37,7 +37,7 @@ type cache struct {
|
||||||
// New returns a new in-memory cache.
|
// New returns a new in-memory cache.
|
||||||
func New() Cache {
|
func New() Cache {
|
||||||
c := ttlcache.NewCache()
|
c := ttlcache.NewCache()
|
||||||
c.SetTTL(30 * time.Second)
|
c.SetTTL(5 * time.Minute)
|
||||||
cache := &cache{
|
cache := &cache{
|
||||||
c: c,
|
c: c,
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
|
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/pg"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -36,7 +36,7 @@ import (
|
||||||
|
|
||||||
// Create creates a new account in the database using the provided flags.
|
// Create creates a new account in the database using the provided flags.
|
||||||
var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, lo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = dbConn.NewSignup(username, "", false, email, password, nil, "", "", false, false)
|
_, err = dbConn.NewSignup(ctx, username, "", false, email, password, nil, "", "", false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, lo
|
||||||
|
|
||||||
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
|
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
|
||||||
var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -88,20 +88,20 @@ var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := dbConn.GetLocalAccountByUsername(username)
|
a, err := dbConn.GetLocalAccountByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := >smodel.User{}
|
u := >smodel.User{}
|
||||||
if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Approved = true
|
u.Approved = true
|
||||||
u.Email = u.UnconfirmedEmail
|
u.Email = u.UnconfirmedEmail
|
||||||
u.ConfirmedAt = time.Now()
|
u.ConfirmedAt = time.Now()
|
||||||
if err := dbConn.UpdateByID(u.ID, u); err != nil {
|
if err := dbConn.UpdateByID(ctx, u.ID, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ var Confirm cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
|
|
||||||
// Promote sets a user to admin.
|
// Promote sets a user to admin.
|
||||||
var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -123,17 +123,17 @@ var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := dbConn.GetLocalAccountByUsername(username)
|
a, err := dbConn.GetLocalAccountByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := >smodel.User{}
|
u := >smodel.User{}
|
||||||
if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Admin = true
|
u.Admin = true
|
||||||
if err := dbConn.UpdateByID(u.ID, u); err != nil {
|
if err := dbConn.UpdateByID(ctx, u.ID, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ var Promote cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
|
|
||||||
// Demote sets admin on a user to false.
|
// Demote sets admin on a user to false.
|
||||||
var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -155,17 +155,17 @@ var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config, lo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := dbConn.GetLocalAccountByUsername(username)
|
a, err := dbConn.GetLocalAccountByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := >smodel.User{}
|
u := >smodel.User{}
|
||||||
if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Admin = false
|
u.Admin = false
|
||||||
if err := dbConn.UpdateByID(u.ID, u); err != nil {
|
if err := dbConn.UpdateByID(ctx, u.ID, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ var Demote cliactions.GTSAction = func(ctx context.Context, c *config.Config, lo
|
||||||
|
|
||||||
// Disable sets Disabled to true on a user.
|
// Disable sets Disabled to true on a user.
|
||||||
var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -187,17 +187,17 @@ var Disable cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := dbConn.GetLocalAccountByUsername(username)
|
a, err := dbConn.GetLocalAccountByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := >smodel.User{}
|
u := >smodel.User{}
|
||||||
if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Disabled = true
|
u.Disabled = true
|
||||||
if err := dbConn.UpdateByID(u.ID, u); err != nil {
|
if err := dbConn.UpdateByID(ctx, u.ID, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ var Suspend cliactions.GTSAction = func(ctx context.Context, c *config.Config, l
|
||||||
|
|
||||||
// Password sets the password of target account.
|
// Password sets the password of target account.
|
||||||
var Password cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Password cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbConn, err := pg.NewPostgresService(ctx, c, log)
|
dbConn, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -233,13 +233,13 @@ var Password cliactions.GTSAction = func(ctx context.Context, c *config.Config,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := dbConn.GetLocalAccountByUsername(username)
|
a, err := dbConn.GetLocalAccountByUsername(ctx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := >smodel.User{}
|
u := >smodel.User{}
|
||||||
if err := dbConn.GetWhere([]db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
if err := dbConn.GetWhere(ctx, []db.Where{{Key: "account_id", Value: a.ID}}, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ var Password cliactions.GTSAction = func(ctx context.Context, c *config.Config,
|
||||||
|
|
||||||
u.EncryptedPassword = string(pw)
|
u.EncryptedPassword = string(pw)
|
||||||
|
|
||||||
if err := dbConn.UpdateByID(u.ID, u); err != nil {
|
if err := dbConn.UpdateByID(ctx, u.ID, u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/blob"
|
"github.com/superseriousbusiness/gotosocial/internal/blob"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
|
"github.com/superseriousbusiness/gotosocial/internal/cliactions"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/pg"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
||||||
|
@ -79,28 +79,28 @@ var models []interface{} = []interface{}{
|
||||||
|
|
||||||
// Start creates and starts a gotosocial server
|
// Start creates and starts a gotosocial server
|
||||||
var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log *logrus.Logger) error {
|
||||||
dbService, err := pg.NewPostgresService(ctx, c, log)
|
dbService, err := bundb.NewBunDBService(ctx, c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, m := range models {
|
for _, m := range models {
|
||||||
if err := dbService.CreateTable(m); err != nil {
|
if err := dbService.CreateTable(ctx, m); err != nil {
|
||||||
return fmt.Errorf("table creation error: %s", err)
|
return fmt.Errorf("table creation error: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dbService.CreateInstanceAccount(); err != nil {
|
if err := dbService.CreateInstanceAccount(ctx); err != nil {
|
||||||
return fmt.Errorf("error creating instance account: %s", err)
|
return fmt.Errorf("error creating instance account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dbService.CreateInstanceInstance(); err != nil {
|
if err := dbService.CreateInstanceInstance(ctx); err != nil {
|
||||||
return fmt.Errorf("error creating instance instance: %s", err)
|
return fmt.Errorf("error creating instance instance: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
federatingDB := federatingdb.New(dbService, c, log)
|
federatingDB := federatingdb.New(dbService, c, log)
|
||||||
|
|
||||||
router, err := router.New(c, dbService, log)
|
router, err := router.New(ctx, c, dbService, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating router: %s", err)
|
return fmt.Errorf("error creating router: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log
|
||||||
transportController := transport.NewController(c, dbService, &federation.Clock{}, http.DefaultClient, log)
|
transportController := transport.NewController(c, dbService, &federation.Clock{}, http.DefaultClient, log)
|
||||||
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter, mediaHandler)
|
federator := federation.NewFederator(dbService, federatingDB, transportController, c, log, typeConverter, mediaHandler)
|
||||||
processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, timelineManager, dbService, log)
|
processor := processing.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, timelineManager, dbService, log)
|
||||||
if err := processor.Start(); err != nil {
|
if err := processor.Start(ctx); err != nil {
|
||||||
return fmt.Errorf("error starting processor: %s", err)
|
return fmt.Errorf("error starting processor: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config, log
|
||||||
federator := testrig.NewTestFederator(dbService, transportController, storageBackend)
|
federator := testrig.NewTestFederator(dbService, transportController, storageBackend)
|
||||||
|
|
||||||
processor := testrig.NewTestProcessor(dbService, storageBackend, federator)
|
processor := testrig.NewTestProcessor(dbService, storageBackend, federator)
|
||||||
if err := processor.Start(); err != nil {
|
if err := processor.Start(ctx); err != nil {
|
||||||
return fmt.Errorf("error starting processor: %s", err)
|
return fmt.Errorf("error starting processor: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
@ -27,40 +28,43 @@ import (
|
||||||
// Account contains functions related to account getting/setting/creation.
|
// Account contains functions related to account getting/setting/creation.
|
||||||
type Account interface {
|
type Account interface {
|
||||||
// GetAccountByID returns one account with the given ID, or an error if something goes wrong.
|
// GetAccountByID returns one account with the given ID, or an error if something goes wrong.
|
||||||
GetAccountByID(id string) (*gtsmodel.Account, Error)
|
GetAccountByID(ctx context.Context, id string) (*gtsmodel.Account, Error)
|
||||||
|
|
||||||
// GetAccountByURI returns one account with the given URI, or an error if something goes wrong.
|
// GetAccountByURI returns one account with the given URI, or an error if something goes wrong.
|
||||||
GetAccountByURI(uri string) (*gtsmodel.Account, Error)
|
GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.Account, Error)
|
||||||
|
|
||||||
// GetAccountByURL returns one account with the given URL, or an error if something goes wrong.
|
// GetAccountByURL returns one account with the given URL, or an error if something goes wrong.
|
||||||
GetAccountByURL(uri string) (*gtsmodel.Account, Error)
|
GetAccountByURL(ctx context.Context, uri string) (*gtsmodel.Account, Error)
|
||||||
|
|
||||||
|
// UpdateAccount updates one account by ID.
|
||||||
|
UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, Error)
|
||||||
|
|
||||||
// GetLocalAccountByUsername returns an account on this instance by its username.
|
// GetLocalAccountByUsername returns an account on this instance by its username.
|
||||||
GetLocalAccountByUsername(username string) (*gtsmodel.Account, Error)
|
GetLocalAccountByUsername(ctx context.Context, username string) (*gtsmodel.Account, Error)
|
||||||
|
|
||||||
// GetAccountFaves fetches faves/likes created by the target accountID.
|
// GetAccountFaves fetches faves/likes created by the target accountID.
|
||||||
GetAccountFaves(accountID string) ([]*gtsmodel.StatusFave, Error)
|
GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, Error)
|
||||||
|
|
||||||
// GetAccountStatusesCount is a shortcut for the common action of counting statuses produced by accountID.
|
// GetAccountStatusesCount is a shortcut for the common action of counting statuses produced by accountID.
|
||||||
CountAccountStatuses(accountID string) (int, Error)
|
CountAccountStatuses(ctx context.Context, accountID string) (int, Error)
|
||||||
|
|
||||||
// GetAccountStatuses is a shortcut for getting the most recent statuses. accountID is optional, if not provided
|
// GetAccountStatuses is a shortcut for getting the most recent statuses. accountID is optional, if not provided
|
||||||
// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can
|
// then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can
|
||||||
// be very memory intensive so you probably shouldn't do this!
|
// be very memory intensive so you probably shouldn't do this!
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
// In case of no entries, a 'no entries' error will be returned
|
||||||
GetAccountStatuses(accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, Error)
|
GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, Error)
|
||||||
|
|
||||||
GetAccountBlocks(accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, Error)
|
GetAccountBlocks(ctx context.Context, accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, Error)
|
||||||
|
|
||||||
// GetAccountLastPosted simply gets the timestamp of the most recent post by the account.
|
// GetAccountLastPosted simply gets the timestamp of the most recent post by the account.
|
||||||
//
|
//
|
||||||
// The returned time will be zero if account has never posted anything.
|
// The returned time will be zero if account has never posted anything.
|
||||||
GetAccountLastPosted(accountID string) (time.Time, Error)
|
GetAccountLastPosted(ctx context.Context, accountID string) (time.Time, Error)
|
||||||
|
|
||||||
// SetAccountHeaderOrAvatar sets the header or avatar for the given accountID to the given media attachment.
|
// SetAccountHeaderOrAvatar sets the header or avatar for the given accountID to the given media attachment.
|
||||||
SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) Error
|
SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) Error
|
||||||
|
|
||||||
// GetInstanceAccount returns the instance account for the given domain.
|
// GetInstanceAccount returns the instance account for the given domain.
|
||||||
// If domain is empty, this instance account will be returned.
|
// If domain is empty, this instance account will be returned.
|
||||||
GetInstanceAccount(domain string) (*gtsmodel.Account, Error)
|
GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
@ -28,26 +29,26 @@ import (
|
||||||
type Admin interface {
|
type Admin interface {
|
||||||
// IsUsernameAvailable checks whether a given username is available on our domain.
|
// IsUsernameAvailable checks whether a given username is available on our domain.
|
||||||
// Returns an error if the username is already taken, or something went wrong in the db.
|
// Returns an error if the username is already taken, or something went wrong in the db.
|
||||||
IsUsernameAvailable(username string) Error
|
IsUsernameAvailable(ctx context.Context, username string) (bool, Error)
|
||||||
|
|
||||||
// IsEmailAvailable checks whether a given email address for a new account is available to be used on our domain.
|
// IsEmailAvailable checks whether a given email address for a new account is available to be used on our domain.
|
||||||
// Return an error if:
|
// Return an error if:
|
||||||
// A) the email is already associated with an account
|
// A) the email is already associated with an account
|
||||||
// B) we block signups from this email domain
|
// B) we block signups from this email domain
|
||||||
// C) something went wrong in the db
|
// C) something went wrong in the db
|
||||||
IsEmailAvailable(email string) Error
|
IsEmailAvailable(ctx context.Context, email string) (bool, Error)
|
||||||
|
|
||||||
// NewSignup creates a new user in the database with the given parameters.
|
// NewSignup creates a new user in the database with the given parameters.
|
||||||
// By the time this function is called, it should be assumed that all the parameters have passed validation!
|
// By the time this function is called, it should be assumed that all the parameters have passed validation!
|
||||||
NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, Error)
|
NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, Error)
|
||||||
|
|
||||||
// CreateInstanceAccount creates an account in the database with the same username as the instance host value.
|
// CreateInstanceAccount creates an account in the database with the same username as the instance host value.
|
||||||
// Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'.
|
// Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'.
|
||||||
// This is needed for things like serving files that belong to the instance and not an individual user/account.
|
// This is needed for things like serving files that belong to the instance and not an individual user/account.
|
||||||
CreateInstanceAccount() Error
|
CreateInstanceAccount(ctx context.Context) Error
|
||||||
|
|
||||||
// CreateInstanceInstance creates an instance in the database with the same domain as the instance host value.
|
// CreateInstanceInstance creates an instance in the database with the same domain as the instance host value.
|
||||||
// Ie., if the instance is hosted at 'example.org' the instance will have a domain of 'example.org'.
|
// Ie., if the instance is hosted at 'example.org' the instance will have a domain of 'example.org'.
|
||||||
// This is needed for things like serving instance information through /api/v1/instance
|
// This is needed for things like serving instance information through /api/v1/instance
|
||||||
CreateInstanceInstance() Error
|
CreateInstanceInstance(ctx context.Context) Error
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,11 @@ import "context"
|
||||||
type Basic interface {
|
type Basic interface {
|
||||||
// CreateTable creates a table for the given interface.
|
// CreateTable creates a table for the given interface.
|
||||||
// For implementations that don't use tables, this can just return nil.
|
// For implementations that don't use tables, this can just return nil.
|
||||||
CreateTable(i interface{}) Error
|
CreateTable(ctx context.Context, i interface{}) Error
|
||||||
|
|
||||||
// DropTable drops the table for the given interface.
|
// DropTable drops the table for the given interface.
|
||||||
// For implementations that don't use tables, this can just return nil.
|
// For implementations that don't use tables, this can just return nil.
|
||||||
DropTable(i interface{}) Error
|
DropTable(ctx context.Context, i interface{}) Error
|
||||||
|
|
||||||
// RegisterTable registers a table for use in many2many relations.
|
|
||||||
// For implementations that don't use tables, or many2many relations, this can just return nil.
|
|
||||||
RegisterTable(i interface{}) Error
|
|
||||||
|
|
||||||
// Stop should stop and close the database connection cleanly, returning an error if this is not possible.
|
// Stop should stop and close the database connection cleanly, returning an error if this is not possible.
|
||||||
// If the database implementation doesn't need to be stopped, this can just return nil.
|
// If the database implementation doesn't need to be stopped, this can just return nil.
|
||||||
|
@ -45,43 +41,38 @@ type Basic interface {
|
||||||
// for other implementations (for example, in-memory) it might just be the key of a map.
|
// for other implementations (for example, in-memory) it might just be the key of a map.
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
// In case of no entries, a 'no entries' error will be returned
|
||||||
GetByID(id string, i interface{}) Error
|
GetByID(ctx context.Context, id string, i interface{}) Error
|
||||||
|
|
||||||
// GetWhere gets one entry where key = value. This is similar to GetByID but allows the caller to specify the
|
// GetWhere gets one entry where key = value. This is similar to GetByID but allows the caller to specify the
|
||||||
// name of the key to select from.
|
// name of the key to select from.
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
// In case of no entries, a 'no entries' error will be returned
|
||||||
GetWhere(where []Where, i interface{}) Error
|
GetWhere(ctx context.Context, where []Where, i interface{}) Error
|
||||||
|
|
||||||
// GetAll will try to get all entries of type i.
|
// GetAll will try to get all entries of type i.
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
// In case of no entries, a 'no entries' error will be returned
|
||||||
GetAll(i interface{}) Error
|
GetAll(ctx context.Context, i interface{}) Error
|
||||||
|
|
||||||
// Put simply stores i. It is up to the implementation to figure out how to store it, and using what key.
|
// Put simply stores i. It is up to the implementation to figure out how to store it, and using what key.
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
Put(i interface{}) Error
|
Put(ctx context.Context, i interface{}) Error
|
||||||
|
|
||||||
// Upsert stores or updates i based on the given conflict column, as in https://www.postgresqltutorial.com/postgresql-upsert/
|
|
||||||
// It is up to the implementation to figure out how to store it, and using what key.
|
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
|
||||||
Upsert(i interface{}, conflictColumn string) Error
|
|
||||||
|
|
||||||
// UpdateByID updates i with id id.
|
// UpdateByID updates i with id id.
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
UpdateByID(id string, i interface{}) Error
|
UpdateByID(ctx context.Context, id string, i interface{}) Error
|
||||||
|
|
||||||
// UpdateOneByID updates interface i with database the given database id. It will update one field of key key and value value.
|
// UpdateOneByID updates interface i with database the given database id. It will update one field of key key and value value.
|
||||||
UpdateOneByID(id string, key string, value interface{}, i interface{}) Error
|
UpdateOneByID(ctx context.Context, id string, key string, value interface{}, i interface{}) Error
|
||||||
|
|
||||||
// UpdateWhere updates column key of interface i with the given value, where the given parameters apply.
|
// UpdateWhere updates column key of interface i with the given value, where the given parameters apply.
|
||||||
UpdateWhere(where []Where, key string, value interface{}, i interface{}) Error
|
UpdateWhere(ctx context.Context, where []Where, key string, value interface{}, i interface{}) Error
|
||||||
|
|
||||||
// DeleteByID removes i with id id.
|
// DeleteByID removes i with id id.
|
||||||
// If i didn't exist anyway, then no error should be returned.
|
// If i didn't exist anyway, then no error should be returned.
|
||||||
DeleteByID(id string, i interface{}) Error
|
DeleteByID(ctx context.Context, id string, i interface{}) Error
|
||||||
|
|
||||||
// DeleteWhere deletes i where key = value
|
// DeleteWhere deletes i where key = value
|
||||||
// If i didn't exist anyway, then no error should be returned.
|
// If i didn't exist anyway, then no error should be returned.
|
||||||
DeleteWhere(where []Where, i interface{}) Error
|
DeleteWhere(ctx context.Context, where []Where, i interface{}) Error
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,70 +16,90 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type accountDB struct {
|
type accountDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) newAccountQ(account *gtsmodel.Account) *orm.Query {
|
func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery {
|
||||||
return a.conn.Model(account).
|
return a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(account).
|
||||||
Relation("AvatarMediaAttachment").
|
Relation("AvatarMediaAttachment").
|
||||||
Relation("HeaderMediaAttachment")
|
Relation("HeaderMediaAttachment")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByID(id string) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) GetAccountByID(ctx context.Context, id string) (*gtsmodel.Account, db.Error) {
|
||||||
account := >smodel.Account{}
|
account := new(gtsmodel.Account)
|
||||||
|
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("account.id = ?", id)
|
Where("account.id = ?", id)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByURI(uri string) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) {
|
||||||
account := >smodel.Account{}
|
account := new(gtsmodel.Account)
|
||||||
|
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("account.uri = ?", uri)
|
Where("account.uri = ?", uri)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByURL(uri string) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) GetAccountByURL(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) {
|
||||||
account := >smodel.Account{}
|
account := new(gtsmodel.Account)
|
||||||
|
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("account.url = ?", uri)
|
Where("account.url = ?", uri)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetInstanceAccount(domain string) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) {
|
||||||
account := >smodel.Account{}
|
if strings.TrimSpace(account.ID) == "" {
|
||||||
|
return nil, errors.New("account had no ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
account.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
q := a.conn.
|
||||||
|
NewUpdate().
|
||||||
|
Model(account).
|
||||||
|
WherePK()
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
err = processErrorResponse(err)
|
||||||
|
|
||||||
|
return account, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, db.Error) {
|
||||||
|
account := new(gtsmodel.Account)
|
||||||
|
|
||||||
q := a.newAccountQ(account)
|
q := a.newAccountQ(account)
|
||||||
|
|
||||||
|
@ -90,29 +110,31 @@ func (a *accountDB) GetInstanceAccount(domain string) (*gtsmodel.Account, db.Err
|
||||||
} else {
|
} else {
|
||||||
q = q.
|
q = q.
|
||||||
Where("account.username = ?", domain).
|
Where("account.username = ?", domain).
|
||||||
Where("? IS NULL", pg.Ident("domain"))
|
Where("? IS NULL", bun.Ident("domain"))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountLastPosted(accountID string) (time.Time, db.Error) {
|
func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string) (time.Time, db.Error) {
|
||||||
status := >smodel.Status{}
|
status := new(gtsmodel.Status)
|
||||||
|
|
||||||
q := a.conn.Model(status).
|
q := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(status).
|
||||||
Order("id DESC").
|
Order("id DESC").
|
||||||
Limit(1).
|
Limit(1).
|
||||||
Where("account_id = ?", accountID).
|
Where("account_id = ?", accountID).
|
||||||
Column("created_at")
|
Column("created_at")
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return status.CreatedAt, err
|
return status.CreatedAt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error {
|
func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error {
|
||||||
if mediaAttachment.Avatar && mediaAttachment.Header {
|
if mediaAttachment.Avatar && mediaAttachment.Header {
|
||||||
return errors.New("one media attachment cannot be both header and avatar")
|
return errors.New("one media attachment cannot be both header and avatar")
|
||||||
}
|
}
|
||||||
|
@ -127,51 +149,66 @@ func (a *accountDB) SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAtta
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: there are probably more side effects here that need to be handled
|
// TODO: there are probably more side effects here that need to be handled
|
||||||
if _, err := a.conn.Model(mediaAttachment).OnConflict("(id) DO UPDATE").Insert(); err != nil {
|
if _, err := a.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(mediaAttachment).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := a.conn.Model(>smodel.Account{}).Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID).Where("id = ?", accountID).Update(); err != nil {
|
if _, err := a.conn.
|
||||||
|
NewUpdate().
|
||||||
|
Model(>smodel.Account{}).
|
||||||
|
Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID).
|
||||||
|
Where("id = ?", accountID).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetLocalAccountByUsername(username string) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) GetLocalAccountByUsername(ctx context.Context, username string) (*gtsmodel.Account, db.Error) {
|
||||||
account := >smodel.Account{}
|
account := new(gtsmodel.Account)
|
||||||
|
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("username = ?", username).
|
Where("username = ?", username).
|
||||||
Where("? IS NULL", pg.Ident("domain"))
|
Where("? IS NULL", bun.Ident("domain"))
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountFaves(accountID string) ([]*gtsmodel.StatusFave, db.Error) {
|
func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, db.Error) {
|
||||||
faves := []*gtsmodel.StatusFave{}
|
faves := new([]*gtsmodel.StatusFave)
|
||||||
|
|
||||||
if err := a.conn.Model(&faves).
|
if err := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(faves).
|
||||||
Where("account_id = ?", accountID).
|
Where("account_id = ?", accountID).
|
||||||
Select(); err != nil {
|
Scan(ctx); err != nil {
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return faves, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return faves, nil
|
return *faves, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) CountAccountStatuses(accountID string) (int, db.Error) {
|
func (a *accountDB) CountAccountStatuses(ctx context.Context, accountID string) (int, db.Error) {
|
||||||
return a.conn.Model(>smodel.Status{}).Where("account_id = ?", accountID).Count()
|
return a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Status{}).
|
||||||
|
Where("account_id = ?", accountID).
|
||||||
|
Count(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountStatuses(accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, db.Error) {
|
func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]*gtsmodel.Status, db.Error) {
|
||||||
a.log.Debugf("getting statuses for account %s", accountID)
|
a.log.Debugf("getting statuses for account %s", accountID)
|
||||||
statuses := []*gtsmodel.Status{}
|
statuses := []*gtsmodel.Status{}
|
||||||
|
|
||||||
q := a.conn.Model(&statuses).Order("id DESC")
|
q := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&statuses).
|
||||||
|
Order("id DESC")
|
||||||
|
|
||||||
if accountID != "" {
|
if accountID != "" {
|
||||||
q = q.Where("account_id = ?", accountID)
|
q = q.Where("account_id = ?", accountID)
|
||||||
}
|
}
|
||||||
|
@ -181,27 +218,26 @@ func (a *accountDB) GetAccountStatuses(accountID string, limit int, excludeRepli
|
||||||
}
|
}
|
||||||
|
|
||||||
if excludeReplies {
|
if excludeReplies {
|
||||||
q = q.Where("? IS NULL", pg.Ident("in_reply_to_id"))
|
q = q.Where("? IS NULL", bun.Ident("in_reply_to_id"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if pinnedOnly {
|
if pinnedOnly {
|
||||||
q = q.Where("pinned = ?", true)
|
q = q.Where("pinned = ?", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if mediaOnly {
|
|
||||||
q = q.WhereGroup(func(q *pg.Query) (*pg.Query, error) {
|
|
||||||
return q.Where("? IS NOT NULL", pg.Ident("attachments")).Where("attachments != '{}'"), nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxID != "" {
|
if maxID != "" {
|
||||||
q = q.Where("id < ?", maxID)
|
q = q.Where("id < ?", maxID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.Select(); err != nil {
|
if mediaOnly {
|
||||||
if err == pg.ErrNoRows {
|
q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
return nil, db.ErrNoEntries
|
return q.
|
||||||
}
|
WhereOr("? IS NOT NULL", bun.Ident("attachments")).
|
||||||
|
WhereOr("attachments != '{}'")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.Scan(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,10 +249,12 @@ func (a *accountDB) GetAccountStatuses(accountID string, limit int, excludeRepli
|
||||||
return statuses, nil
|
return statuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountBlocks(accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, db.Error) {
|
func (a *accountDB) GetAccountBlocks(ctx context.Context, accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, db.Error) {
|
||||||
blocks := []*gtsmodel.Block{}
|
blocks := []*gtsmodel.Block{}
|
||||||
|
|
||||||
fq := a.conn.Model(&blocks).
|
fq := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&blocks).
|
||||||
Where("block.account_id = ?", accountID).
|
Where("block.account_id = ?", accountID).
|
||||||
Relation("TargetAccount").
|
Relation("TargetAccount").
|
||||||
Order("block.id DESC")
|
Order("block.id DESC")
|
||||||
|
@ -233,11 +271,8 @@ func (a *accountDB) GetAccountBlocks(accountID string, maxID string, sinceID str
|
||||||
fq = fq.Limit(limit)
|
fq = fq.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := fq.Select()
|
err := fq.Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return nil, "", "", db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return nil, "", "", err
|
return nil, "", "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,19 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg_test
|
package bundb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccountTestSuite struct {
|
type AccountTestSuite struct {
|
||||||
PGStandardTestSuite
|
BunDBStandardTestSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountTestSuite) SetupSuite() {
|
func (suite *AccountTestSuite) SetupSuite() {
|
||||||
|
@ -54,7 +56,7 @@ func (suite *AccountTestSuite) TearDownTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountTestSuite) TestGetAccountByIDWithExtras() {
|
func (suite *AccountTestSuite) TestGetAccountByIDWithExtras() {
|
||||||
account, err := suite.db.GetAccountByID(suite.testAccounts["local_account_1"].ID)
|
account, err := suite.db.GetAccountByID(context.Background(), suite.testAccounts["local_account_1"].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -65,6 +67,20 @@ func (suite *AccountTestSuite) TestGetAccountByIDWithExtras() {
|
||||||
suite.NotEmpty(account.HeaderMediaAttachment.URL)
|
suite.NotEmpty(account.HeaderMediaAttachment.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AccountTestSuite) TestUpdateAccount() {
|
||||||
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
testAccount.DisplayName = "new display name!"
|
||||||
|
|
||||||
|
_, err := suite.db.UpdateAccount(context.Background(), testAccount)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
updated, err := suite.db.GetAccountByID(context.Background(), testAccount.ID)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal("new display name!", updated.DisplayName)
|
||||||
|
suite.WithinDuration(time.Now(), updated.UpdatedAt, 5*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccountTestSuite(t *testing.T) {
|
func TestAccountTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(AccountTestSuite))
|
suite.Run(t, new(AccountTestSuite))
|
||||||
}
|
}
|
|
@ -16,76 +16,76 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type adminDB struct {
|
type adminDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) IsUsernameAvailable(username string) db.Error {
|
func (a *adminDB) IsUsernameAvailable(ctx context.Context, username string) (bool, db.Error) {
|
||||||
// if no error we fail because it means we found something
|
q := a.conn.
|
||||||
// if error but it's not pg.ErrNoRows then we fail
|
NewSelect().
|
||||||
// if err is pg.ErrNoRows we're good, we found nothing so continue
|
Model(>smodel.Account{}).
|
||||||
if err := a.conn.Model(>smodel.Account{}).Where("username = ?", username).Where("domain = ?", nil).Select(); err == nil {
|
Where("username = ?", username).
|
||||||
return fmt.Errorf("username %s already in use", username)
|
Where("domain = ?", nil)
|
||||||
} else if err != pg.ErrNoRows {
|
|
||||||
return fmt.Errorf("db error: %s", err)
|
return notExists(ctx, q)
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) IsEmailAvailable(email string) db.Error {
|
func (a *adminDB) IsEmailAvailable(ctx context.Context, email string) (bool, db.Error) {
|
||||||
// parse the domain from the email
|
// parse the domain from the email
|
||||||
m, err := mail.ParseAddress(email)
|
m, err := mail.ParseAddress(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing email address %s: %s", email, err)
|
return false, fmt.Errorf("error parsing email address %s: %s", email, err)
|
||||||
}
|
}
|
||||||
domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @
|
domain := strings.Split(m.Address, "@")[1] // domain will always be the second part after @
|
||||||
|
|
||||||
// check if the email domain is blocked
|
// check if the email domain is blocked
|
||||||
if err := a.conn.Model(>smodel.EmailDomainBlock{}).Where("domain = ?", domain).Select(); err == nil {
|
if err := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.EmailDomainBlock{}).
|
||||||
|
Where("domain = ?", domain).
|
||||||
|
Scan(ctx); err == nil {
|
||||||
// fail because we found something
|
// fail because we found something
|
||||||
return fmt.Errorf("email domain %s is blocked", domain)
|
return false, fmt.Errorf("email domain %s is blocked", domain)
|
||||||
} else if err != pg.ErrNoRows {
|
} else if err != sql.ErrNoRows {
|
||||||
// fail because we got an unexpected error
|
return false, processErrorResponse(err)
|
||||||
return fmt.Errorf("db error: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this email is associated with a user already
|
// check if this email is associated with a user already
|
||||||
if err := a.conn.Model(>smodel.User{}).Where("email = ?", email).WhereOr("unconfirmed_email = ?", email).Select(); err == nil {
|
q := a.conn.
|
||||||
// fail because we found something
|
NewSelect().
|
||||||
return fmt.Errorf("email %s already in use", email)
|
Model(>smodel.User{}).
|
||||||
} else if err != pg.ErrNoRows {
|
Where("email = ?", email).
|
||||||
// fail because we got an unexpected error
|
WhereOr("unconfirmed_email = ?", email)
|
||||||
return fmt.Errorf("db error: %s", err)
|
|
||||||
}
|
return notExists(ctx, q)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, db.Error) {
|
func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, db.Error) {
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Errorf("error creating new rsa key: %s", err)
|
a.log.Errorf("error creating new rsa key: %s", err)
|
||||||
|
@ -94,13 +94,12 @@ func (a *adminDB) NewSignup(username string, reason string, requireApproval bool
|
||||||
|
|
||||||
// if something went wrong while creating a user, we might already have an account, so check here first...
|
// if something went wrong while creating a user, we might already have an account, so check here first...
|
||||||
acct := >smodel.Account{}
|
acct := >smodel.Account{}
|
||||||
err = a.conn.Model(acct).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select()
|
err = a.conn.NewSelect().
|
||||||
|
Model(acct).
|
||||||
|
Where("username = ?", username).
|
||||||
|
Where("? IS NULL", bun.Ident("domain")).
|
||||||
|
Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// there's been an actual error
|
|
||||||
if err != pg.ErrNoRows {
|
|
||||||
return nil, fmt.Errorf("db error checking existence of account: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we just don't have an account yet create one
|
// we just don't have an account yet create one
|
||||||
newAccountURIs := util.GenerateURIsForAccount(username, a.config.Protocol, a.config.Host)
|
newAccountURIs := util.GenerateURIsForAccount(username, a.config.Protocol, a.config.Host)
|
||||||
newAccountID, err := id.NewRandomULID()
|
newAccountID, err := id.NewRandomULID()
|
||||||
|
@ -125,7 +124,10 @@ func (a *adminDB) NewSignup(username string, reason string, requireApproval bool
|
||||||
FollowingURI: newAccountURIs.FollowingURI,
|
FollowingURI: newAccountURIs.FollowingURI,
|
||||||
FeaturedCollectionURI: newAccountURIs.CollectionURI,
|
FeaturedCollectionURI: newAccountURIs.CollectionURI,
|
||||||
}
|
}
|
||||||
if _, err = a.conn.Model(acct).Insert(); err != nil {
|
if _, err = a.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(acct).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,15 +163,33 @@ func (a *adminDB) NewSignup(username string, reason string, requireApproval bool
|
||||||
u.Moderator = true
|
u.Moderator = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = a.conn.Model(u).Insert(); err != nil {
|
if _, err = a.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(u).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) CreateInstanceAccount() db.Error {
|
func (a *adminDB) CreateInstanceAccount(ctx context.Context) db.Error {
|
||||||
username := a.config.Host
|
username := a.config.Host
|
||||||
|
|
||||||
|
// check if instance account already exists
|
||||||
|
existsQ := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Account{}).
|
||||||
|
Where("username = ?", username).
|
||||||
|
Where("? IS NULL", bun.Ident("domain"))
|
||||||
|
count, err := existsQ.Count(ctx)
|
||||||
|
if err != nil && count == 1 {
|
||||||
|
a.log.Infof("instance account %s already exists", username)
|
||||||
|
return nil
|
||||||
|
} else if err != sql.ErrNoRows {
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.log.Errorf("error creating new rsa key: %s", err)
|
a.log.Errorf("error creating new rsa key: %s", err)
|
||||||
|
@ -198,19 +218,36 @@ func (a *adminDB) CreateInstanceAccount() db.Error {
|
||||||
FollowingURI: newAccountURIs.FollowingURI,
|
FollowingURI: newAccountURIs.FollowingURI,
|
||||||
FeaturedCollectionURI: newAccountURIs.CollectionURI,
|
FeaturedCollectionURI: newAccountURIs.CollectionURI,
|
||||||
}
|
}
|
||||||
inserted, err := a.conn.Model(acct).Where("username = ?", username).SelectOrInsert()
|
|
||||||
if err != nil {
|
insertQ := a.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(acct)
|
||||||
|
|
||||||
|
if _, err := insertQ.Exec(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if inserted {
|
|
||||||
a.log.Infof("created instance account %s with id %s", username, acct.ID)
|
a.log.Infof("instance account %s CREATED with id %s", username, acct.ID)
|
||||||
} else {
|
|
||||||
a.log.Infof("instance account %s already exists with id %s", username, acct.ID)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adminDB) CreateInstanceInstance() db.Error {
|
func (a *adminDB) CreateInstanceInstance(ctx context.Context) db.Error {
|
||||||
|
domain := a.config.Host
|
||||||
|
|
||||||
|
// check if instance entry already exists
|
||||||
|
existsQ := a.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Instance{}).
|
||||||
|
Where("domain = ?", domain)
|
||||||
|
|
||||||
|
count, err := existsQ.Count(ctx)
|
||||||
|
if err != nil && count == 1 {
|
||||||
|
a.log.Infof("instance instance %s already exists", domain)
|
||||||
|
return nil
|
||||||
|
} else if err != sql.ErrNoRows {
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
iID, err := id.NewRandomULID()
|
iID, err := id.NewRandomULID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -218,18 +255,18 @@ func (a *adminDB) CreateInstanceInstance() db.Error {
|
||||||
|
|
||||||
i := >smodel.Instance{
|
i := >smodel.Instance{
|
||||||
ID: iID,
|
ID: iID,
|
||||||
Domain: a.config.Host,
|
Domain: domain,
|
||||||
Title: a.config.Host,
|
Title: domain,
|
||||||
URI: fmt.Sprintf("%s://%s", a.config.Protocol, a.config.Host),
|
URI: fmt.Sprintf("%s://%s", a.config.Protocol, a.config.Host),
|
||||||
}
|
}
|
||||||
inserted, err := a.conn.Model(i).Where("domain = ?", a.config.Host).SelectOrInsert()
|
|
||||||
if err != nil {
|
insertQ := a.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(i)
|
||||||
|
|
||||||
|
if _, err := insertQ.Exec(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if inserted {
|
a.log.Infof("created instance instance %s with id %s", domain, i.ID)
|
||||||
a.log.Infof("created instance instance %s with id %s", a.config.Host, i.ID)
|
|
||||||
} else {
|
|
||||||
a.log.Infof("instance instance %s already exists with id %s", a.config.Host, i.ID)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bundb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type basicDB struct {
|
||||||
|
config *config.Config
|
||||||
|
conn *bun.DB
|
||||||
|
log *logrus.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) Put(ctx context.Context, i interface{}) db.Error {
|
||||||
|
_, err := b.conn.NewInsert().Model(i).Exec(ctx)
|
||||||
|
if err != nil && strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
||||||
|
return db.ErrAlreadyExists
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) GetByID(ctx context.Context, id string, i interface{}) db.Error {
|
||||||
|
q := b.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(i).
|
||||||
|
Where("id = ?", id)
|
||||||
|
|
||||||
|
return processErrorResponse(q.Scan(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) GetWhere(ctx context.Context, where []db.Where, i interface{}) db.Error {
|
||||||
|
if len(where) == 0 {
|
||||||
|
return errors.New("no queries provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
q := b.conn.NewSelect().Model(i)
|
||||||
|
for _, w := range where {
|
||||||
|
|
||||||
|
if w.Value == nil {
|
||||||
|
q = q.Where("? IS NULL", bun.Ident(w.Key))
|
||||||
|
} else {
|
||||||
|
if w.CaseInsensitive {
|
||||||
|
q = q.Where("LOWER(?) = LOWER(?)", bun.Safe(w.Key), w.Value)
|
||||||
|
} else {
|
||||||
|
q = q.Where("? = ?", bun.Safe(w.Key), w.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processErrorResponse(q.Scan(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) GetAll(ctx context.Context, i interface{}) db.Error {
|
||||||
|
q := b.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(i)
|
||||||
|
|
||||||
|
return processErrorResponse(q.Scan(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) DeleteByID(ctx context.Context, id string, i interface{}) db.Error {
|
||||||
|
q := b.conn.
|
||||||
|
NewDelete().
|
||||||
|
Model(i).
|
||||||
|
Where("id = ?", id)
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) DeleteWhere(ctx context.Context, where []db.Where, i interface{}) db.Error {
|
||||||
|
if len(where) == 0 {
|
||||||
|
return errors.New("no queries provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
q := b.conn.
|
||||||
|
NewDelete().
|
||||||
|
Model(i)
|
||||||
|
|
||||||
|
for _, w := range where {
|
||||||
|
q = q.Where("? = ?", bun.Safe(w.Key), w.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) UpdateByID(ctx context.Context, id string, i interface{}) db.Error {
|
||||||
|
q := b.conn.
|
||||||
|
NewUpdate().
|
||||||
|
Model(i).
|
||||||
|
WherePK()
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) UpdateOneByID(ctx context.Context, id string, key string, value interface{}, i interface{}) db.Error {
|
||||||
|
q := b.conn.NewUpdate().
|
||||||
|
Model(i).
|
||||||
|
Set("? = ?", bun.Safe(key), value).
|
||||||
|
WherePK()
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string, value interface{}, i interface{}) db.Error {
|
||||||
|
q := b.conn.NewUpdate().Model(i)
|
||||||
|
|
||||||
|
for _, w := range where {
|
||||||
|
if w.Value == nil {
|
||||||
|
q = q.Where("? IS NULL", bun.Ident(w.Key))
|
||||||
|
} else {
|
||||||
|
if w.CaseInsensitive {
|
||||||
|
q = q.Where("LOWER(?) = LOWER(?)", bun.Safe(w.Key), w.Value)
|
||||||
|
} else {
|
||||||
|
q = q.Where("? = ?", bun.Safe(w.Key), w.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q = q.Set("? = ?", bun.Safe(key), value)
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) CreateTable(ctx context.Context, i interface{}) db.Error {
|
||||||
|
_, err := b.conn.NewCreateTable().Model(i).IfNotExists().Exec(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) DropTable(ctx context.Context, i interface{}) db.Error {
|
||||||
|
_, err := b.conn.NewDropTable().Model(i).IfExists().Exec(ctx)
|
||||||
|
return processErrorResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) IsHealthy(ctx context.Context) db.Error {
|
||||||
|
return b.conn.Ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicDB) Stop(ctx context.Context) db.Error {
|
||||||
|
b.log.Info("closing db connection")
|
||||||
|
if err := b.conn.Close(); err != nil {
|
||||||
|
// only cancel if there's a problem closing the db
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bundb_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BasicTestSuite struct {
|
||||||
|
BunDBStandardTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BasicTestSuite) SetupSuite() {
|
||||||
|
suite.testTokens = testrig.NewTestTokens()
|
||||||
|
suite.testClients = testrig.NewTestClients()
|
||||||
|
suite.testApplications = testrig.NewTestApplications()
|
||||||
|
suite.testUsers = testrig.NewTestUsers()
|
||||||
|
suite.testAccounts = testrig.NewTestAccounts()
|
||||||
|
suite.testAttachments = testrig.NewTestAttachments()
|
||||||
|
suite.testStatuses = testrig.NewTestStatuses()
|
||||||
|
suite.testTags = testrig.NewTestTags()
|
||||||
|
suite.testMentions = testrig.NewTestMentions()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BasicTestSuite) SetupTest() {
|
||||||
|
suite.config = testrig.NewTestConfig()
|
||||||
|
suite.db = testrig.NewTestDB()
|
||||||
|
suite.log = testrig.NewTestLog()
|
||||||
|
|
||||||
|
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BasicTestSuite) TearDownTest() {
|
||||||
|
testrig.StandardDBTeardown(suite.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *BasicTestSuite) TestGetAccountByID() {
|
||||||
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
a := >smodel.Account{}
|
||||||
|
err := suite.db.GetByID(context.Background(), testAccount.ID, a)
|
||||||
|
suite.NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasicTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(BasicTestSuite))
|
||||||
|
}
|
|
@ -16,12 +16,13 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"database/sql"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -29,14 +30,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/extra/pgdebug"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/jackc/pgx/v4/stdlib"
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect/pgdialect"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dbTypePostgres = "postgres"
|
||||||
|
dbTypeSqlite = "sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
var registerTables []interface{} = []interface{}{
|
var registerTables []interface{} = []interface{}{
|
||||||
|
@ -44,8 +51,8 @@ var registerTables []interface{} = []interface{}{
|
||||||
>smodel.StatusToTag{},
|
>smodel.StatusToTag{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// postgresService satisfies the DB interface
|
// bunDBService satisfies the DB interface
|
||||||
type postgresService struct {
|
type bunDBService struct {
|
||||||
db.Account
|
db.Account
|
||||||
db.Admin
|
db.Admin
|
||||||
db.Basic
|
db.Basic
|
||||||
|
@ -55,130 +62,115 @@ type postgresService struct {
|
||||||
db.Mention
|
db.Mention
|
||||||
db.Notification
|
db.Notification
|
||||||
db.Relationship
|
db.Relationship
|
||||||
|
db.Session
|
||||||
db.Status
|
db.Status
|
||||||
db.Timeline
|
db.Timeline
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
|
// NewBunDBService returns a bunDB derived from the provided config, which implements the go-fed DB interface.
|
||||||
// Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection.
|
// Under the hood, it uses https://github.com/uptrace/bun to create and maintain a database connection.
|
||||||
func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) {
|
func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) {
|
||||||
for _, t := range registerTables {
|
var sqldb *sql.DB
|
||||||
// https://pg.uptrace.dev/orm/many-to-many-relation/
|
var conn *bun.DB
|
||||||
orm.RegisterTable(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
opts, err := derivePGOptions(c)
|
// depending on the database type we're trying to create, we need to use a different driver...
|
||||||
if err != nil {
|
switch strings.ToLower(c.DBConfig.Type) {
|
||||||
return nil, fmt.Errorf("could not create postgres service: %s", err)
|
case dbTypePostgres:
|
||||||
}
|
// POSTGRES
|
||||||
log.Debugf("using pg options: %+v", opts)
|
opts, err := deriveBunDBPGOptions(c)
|
||||||
|
if err != nil {
|
||||||
// create a connection
|
return nil, fmt.Errorf("could not create bundb postgres options: %s", err)
|
||||||
pgCtx, cancel := context.WithCancel(ctx)
|
}
|
||||||
conn := pg.Connect(opts).WithContext(pgCtx)
|
sqldb = stdlib.OpenDB(*opts)
|
||||||
|
conn = bun.NewDB(sqldb, pgdialect.New())
|
||||||
// this will break the logfmt format we normally log in,
|
case dbTypeSqlite:
|
||||||
// since we can't choose where pg outputs to and it defaults to
|
// SQLITE
|
||||||
// stdout. So use this option with care!
|
// TODO: https://bun.uptrace.dev/guide/drivers.html#sqlite
|
||||||
if log.GetLevel() >= logrus.TraceLevel {
|
default:
|
||||||
conn.AddQueryHook(pgdebug.DebugHook{
|
return nil, fmt.Errorf("database type %s not supported for bundb", strings.ToLower(c.DBConfig.Type))
|
||||||
// Print all queries.
|
|
||||||
Verbose: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually *begin* the connection so that we can tell if the db is there and listening
|
// actually *begin* the connection so that we can tell if the db is there and listening
|
||||||
if err := conn.Ping(ctx); err != nil {
|
if err := conn.Ping(); err != nil {
|
||||||
cancel()
|
|
||||||
return nil, fmt.Errorf("db connection error: %s", err)
|
return nil, fmt.Errorf("db connection error: %s", err)
|
||||||
}
|
}
|
||||||
|
log.Info("connected to database")
|
||||||
|
|
||||||
// print out discovered postgres version
|
for _, t := range registerTables {
|
||||||
var version string
|
// https://bun.uptrace.dev/orm/many-to-many-relation/
|
||||||
if _, err = conn.QueryOneContext(ctx, pg.Scan(&version), "SELECT version()"); err != nil {
|
conn.RegisterModel(t)
|
||||||
cancel()
|
|
||||||
return nil, fmt.Errorf("db connection error: %s", err)
|
|
||||||
}
|
}
|
||||||
log.Infof("connected to postgres version: %s", version)
|
|
||||||
|
|
||||||
ps := &postgresService{
|
ps := &bunDBService{
|
||||||
Account: &accountDB{
|
Account: &accountDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Admin: &adminDB{
|
Admin: &adminDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Basic: &basicDB{
|
Basic: &basicDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Domain: &domainDB{
|
Domain: &domainDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Instance: &instanceDB{
|
Instance: &instanceDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Media: &mediaDB{
|
Media: &mediaDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Mention: &mentionDB{
|
Mention: &mentionDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Notification: ¬ificationDB{
|
Notification: ¬ificationDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Relationship: &relationshipDB{
|
Relationship: &relationshipDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
},
|
||||||
|
Session: &sessionDB{
|
||||||
|
config: c,
|
||||||
|
conn: conn,
|
||||||
|
log: log,
|
||||||
},
|
},
|
||||||
Status: &statusDB{
|
Status: &statusDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
Timeline: &timelineDB{
|
Timeline: &timelineDB{
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
},
|
},
|
||||||
config: c,
|
config: c,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
log: log,
|
log: log,
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can confidently return this useable postgres service now
|
// we can confidently return this useable service now
|
||||||
return ps, nil
|
return ps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,9 +178,9 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge
|
||||||
HANDY STUFF
|
HANDY STUFF
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// derivePGOptions takes an application config and returns either a ready-to-use *pg.Options
|
// deriveBunDBPGOptions takes an application config and returns either a ready-to-use set of options
|
||||||
// with sensible defaults, or an error if it's not satisfied by the provided config.
|
// with sensible defaults, or an error if it's not satisfied by the provided config.
|
||||||
func derivePGOptions(c *config.Config) (*pg.Options, error) {
|
func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) {
|
||||||
if strings.ToUpper(c.DBConfig.Type) != db.DBTypePostgres {
|
if strings.ToUpper(c.DBConfig.Type) != db.DBTypePostgres {
|
||||||
return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, c.DBConfig.Type)
|
return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, c.DBConfig.Type)
|
||||||
}
|
}
|
||||||
|
@ -266,18 +258,16 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {
|
||||||
tlsConfig.RootCAs = certPool
|
tlsConfig.RootCAs = certPool
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can rely on the pg library we're using to set
|
cfg, _ := pgx.ParseConfig("")
|
||||||
// sensible defaults for everything we don't set here.
|
cfg.Host = c.DBConfig.Address
|
||||||
options := &pg.Options{
|
cfg.Port = uint16(c.DBConfig.Port)
|
||||||
Addr: fmt.Sprintf("%s:%d", c.DBConfig.Address, c.DBConfig.Port),
|
cfg.User = c.DBConfig.User
|
||||||
User: c.DBConfig.User,
|
cfg.Password = c.DBConfig.Password
|
||||||
Password: c.DBConfig.Password,
|
cfg.TLSConfig = tlsConfig
|
||||||
Database: c.DBConfig.Database,
|
cfg.Database = c.DBConfig.Database
|
||||||
ApplicationName: c.ApplicationName,
|
cfg.PreferSimpleProtocol = true
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
return options, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -286,9 +276,9 @@ func derivePGOptions(c *config.Config) (*pg.Options, error) {
|
||||||
|
|
||||||
// TODO: move these to the type converter, it's bananas that they're here and not there
|
// TODO: move these to the type converter, it's bananas that they're here and not there
|
||||||
|
|
||||||
func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) {
|
func (ps *bunDBService) MentionStringsToMentions(ctx context.Context, targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) {
|
||||||
ogAccount := >smodel.Account{}
|
ogAccount := >smodel.Account{}
|
||||||
if err := ps.conn.Model(ogAccount).Where("id = ?", originAccountID).Select(); err != nil {
|
if err := ps.conn.NewSelect().Model(ogAccount).Where("id = ?", originAccountID).Scan(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,14 +323,14 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori
|
||||||
// match username + account, case insensitive
|
// match username + account, case insensitive
|
||||||
if local {
|
if local {
|
||||||
// local user -- should have a null domain
|
// local user -- should have a null domain
|
||||||
err = ps.conn.Model(mentionedAccount).Where("LOWER(?) = LOWER(?)", pg.Ident("username"), username).Where("? IS NULL", pg.Ident("domain")).Select()
|
err = ps.conn.NewSelect().Model(mentionedAccount).Where("LOWER(?) = LOWER(?)", bun.Ident("username"), username).Where("? IS NULL", bun.Ident("domain")).Scan(ctx)
|
||||||
} else {
|
} else {
|
||||||
// remote user -- should have domain defined
|
// remote user -- should have domain defined
|
||||||
err = ps.conn.Model(mentionedAccount).Where("LOWER(?) = LOWER(?)", pg.Ident("username"), username).Where("LOWER(?) = LOWER(?)", pg.Ident("domain"), domain).Select()
|
err = ps.conn.NewSelect().Model(mentionedAccount).Where("LOWER(?) = LOWER(?)", bun.Ident("username"), username).Where("LOWER(?) = LOWER(?)", bun.Ident("domain"), domain).Scan(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// no result found for this username/domain so just don't include it as a mencho and carry on about our business
|
// no result found for this username/domain so just don't include it as a mencho and carry on about our business
|
||||||
ps.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain)
|
ps.log.Debugf("no account found with username '%s' and domain '%s', skipping it", username, domain)
|
||||||
continue
|
continue
|
||||||
|
@ -364,14 +354,14 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori
|
||||||
return menchies, nil
|
return menchies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *postgresService) TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) {
|
func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) {
|
||||||
newTags := []*gtsmodel.Tag{}
|
newTags := []*gtsmodel.Tag{}
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
tag := >smodel.Tag{}
|
tag := >smodel.Tag{}
|
||||||
// we can use selectorinsert here to create the new tag if it doesn't exist already
|
// we can use selectorinsert here to create the new tag if it doesn't exist already
|
||||||
// inserted will be true if this is a new tag we just created
|
// inserted will be true if this is a new tag we just created
|
||||||
if err := ps.conn.Model(tag).Where("LOWER(?) = LOWER(?)", pg.Ident("name"), t).Select(); err != nil {
|
if err := ps.conn.NewSelect().Model(tag).Where("LOWER(?) = LOWER(?)", bun.Ident("name"), t).Scan(ctx); err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// tag doesn't exist yet so populate it
|
// tag doesn't exist yet so populate it
|
||||||
newID, err := id.NewRandomULID()
|
newID, err := id.NewRandomULID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -400,13 +390,13 @@ func (ps *postgresService) TagStringsToTags(tags []string, originAccountID strin
|
||||||
return newTags, nil
|
return newTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *postgresService) EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) {
|
func (ps *bunDBService) EmojiStringsToEmojis(ctx context.Context, emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) {
|
||||||
newEmojis := []*gtsmodel.Emoji{}
|
newEmojis := []*gtsmodel.Emoji{}
|
||||||
for _, e := range emojis {
|
for _, e := range emojis {
|
||||||
emoji := >smodel.Emoji{}
|
emoji := >smodel.Emoji{}
|
||||||
err := ps.conn.Model(emoji).Where("shortcode = ?", e).Where("visible_in_picker = true").Where("disabled = false").Select()
|
err := ps.conn.NewSelect().Model(emoji).Where("shortcode = ?", e).Where("visible_in_picker = true").Where("disabled = false").Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// no result found for this username/domain so just don't include it as an emoji and carry on about our business
|
// no result found for this username/domain so just don't include it as an emoji and carry on about our business
|
||||||
ps.log.Debugf("no emoji found with shortcode %s, skipping it", e)
|
ps.log.Debugf("no emoji found with shortcode %s, skipping it", e)
|
||||||
continue
|
continue
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg_test
|
package bundb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -27,7 +27,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PGStandardTestSuite struct {
|
type BunDBStandardTestSuite struct {
|
||||||
// standard suite interfaces
|
// standard suite interfaces
|
||||||
suite.Suite
|
suite.Suite
|
||||||
config *config.Config
|
config *config.Config
|
|
@ -16,48 +16,46 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type domainDB struct {
|
type domainDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainDB) IsDomainBlocked(domain string) (bool, db.Error) {
|
func (d *domainDB) IsDomainBlocked(ctx context.Context, domain string) (bool, db.Error) {
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
blocked, err := d.conn.
|
q := d.conn.
|
||||||
|
NewSelect().
|
||||||
Model(>smodel.DomainBlock{}).
|
Model(>smodel.DomainBlock{}).
|
||||||
Where("LOWER(domain) = LOWER(?)", domain).
|
Where("LOWER(domain) = LOWER(?)", domain).
|
||||||
Exists()
|
Limit(1)
|
||||||
|
|
||||||
err = processErrorResponse(err)
|
return exists(ctx, q)
|
||||||
|
|
||||||
return blocked, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainDB) AreDomainsBlocked(domains []string) (bool, db.Error) {
|
func (d *domainDB) AreDomainsBlocked(ctx context.Context, domains []string) (bool, db.Error) {
|
||||||
// filter out any doubles
|
// filter out any doubles
|
||||||
uniqueDomains := util.UniqueStrings(domains)
|
uniqueDomains := util.UniqueStrings(domains)
|
||||||
|
|
||||||
for _, domain := range uniqueDomains {
|
for _, domain := range uniqueDomains {
|
||||||
if blocked, err := d.IsDomainBlocked(domain); err != nil {
|
if blocked, err := d.IsDomainBlocked(ctx, domain); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if blocked {
|
} else if blocked {
|
||||||
return blocked, nil
|
return blocked, nil
|
||||||
|
@ -68,16 +66,16 @@ func (d *domainDB) AreDomainsBlocked(domains []string) (bool, db.Error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainDB) IsURIBlocked(uri *url.URL) (bool, db.Error) {
|
func (d *domainDB) IsURIBlocked(ctx context.Context, uri *url.URL) (bool, db.Error) {
|
||||||
domain := uri.Hostname()
|
domain := uri.Hostname()
|
||||||
return d.IsDomainBlocked(domain)
|
return d.IsDomainBlocked(ctx, domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainDB) AreURIsBlocked(uris []*url.URL) (bool, db.Error) {
|
func (d *domainDB) AreURIsBlocked(ctx context.Context, uris []*url.URL) (bool, db.Error) {
|
||||||
domains := []string{}
|
domains := []string{}
|
||||||
for _, uri := range uris {
|
for _, uri := range uris {
|
||||||
domains = append(domains, uri.Hostname())
|
domains = append(domains, uri.Hostname())
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.AreDomainsBlocked(domains)
|
return d.AreDomainsBlocked(ctx, domains)
|
||||||
}
|
}
|
|
@ -16,43 +16,50 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type instanceDB struct {
|
type instanceDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *instanceDB) CountInstanceUsers(domain string) (int, db.Error) {
|
func (i *instanceDB) CountInstanceUsers(ctx context.Context, domain string) (int, db.Error) {
|
||||||
q := i.conn.Model(&[]*gtsmodel.Account{})
|
q := i.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&[]*gtsmodel.Account{})
|
||||||
|
|
||||||
if domain == i.config.Host {
|
if domain == i.config.Host {
|
||||||
// if the domain is *this* domain, just count where the domain field is null
|
// if the domain is *this* domain, just count where the domain field is null
|
||||||
q = q.Where("? IS NULL", pg.Ident("domain"))
|
q = q.Where("? IS NULL", bun.Ident("domain"))
|
||||||
} else {
|
} else {
|
||||||
q = q.Where("domain = ?", domain)
|
q = q.Where("domain = ?", domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't count the instance account or suspended users
|
// don't count the instance account or suspended users
|
||||||
q = q.Where("username != ?", domain).Where("? IS NULL", pg.Ident("suspended_at"))
|
q = q.
|
||||||
|
Where("username != ?", domain).
|
||||||
|
Where("? IS NULL", bun.Ident("suspended_at"))
|
||||||
|
|
||||||
return q.Count()
|
count, err := q.Count(ctx)
|
||||||
|
|
||||||
|
return count, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *instanceDB) CountInstanceStatuses(domain string) (int, db.Error) {
|
func (i *instanceDB) CountInstanceStatuses(ctx context.Context, domain string) (int, db.Error) {
|
||||||
q := i.conn.Model(&[]*gtsmodel.Status{})
|
q := i.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&[]*gtsmodel.Status{})
|
||||||
|
|
||||||
if domain == i.config.Host {
|
if domain == i.config.Host {
|
||||||
// if the domain is *this* domain, just count where local is true
|
// if the domain is *this* domain, just count where local is true
|
||||||
|
@ -63,30 +70,39 @@ func (i *instanceDB) CountInstanceStatuses(domain string) (int, db.Error) {
|
||||||
Where("account.domain = ?", domain)
|
Where("account.domain = ?", domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Count()
|
count, err := q.Count(ctx)
|
||||||
|
|
||||||
|
return count, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *instanceDB) CountInstanceDomains(domain string) (int, db.Error) {
|
func (i *instanceDB) CountInstanceDomains(ctx context.Context, domain string) (int, db.Error) {
|
||||||
q := i.conn.Model(&[]*gtsmodel.Instance{})
|
q := i.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&[]*gtsmodel.Instance{})
|
||||||
|
|
||||||
if domain == i.config.Host {
|
if domain == i.config.Host {
|
||||||
// if the domain is *this* domain, just count other instances it knows about
|
// if the domain is *this* domain, just count other instances it knows about
|
||||||
// exclude domains that are blocked
|
// exclude domains that are blocked
|
||||||
q = q.Where("domain != ?", domain).Where("? IS NULL", pg.Ident("suspended_at"))
|
q = q.Where("domain != ?", domain).Where("? IS NULL", bun.Ident("suspended_at"))
|
||||||
} else {
|
} else {
|
||||||
// TODO: implement federated domain counting properly for remote domains
|
// TODO: implement federated domain counting properly for remote domains
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Count()
|
count, err := q.Count(ctx)
|
||||||
|
|
||||||
|
return count, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *instanceDB) GetInstanceAccounts(domain string, maxID string, limit int) ([]*gtsmodel.Account, db.Error) {
|
func (i *instanceDB) GetInstanceAccounts(ctx context.Context, domain string, maxID string, limit int) ([]*gtsmodel.Account, db.Error) {
|
||||||
i.log.Debug("GetAccountsForInstance")
|
i.log.Debug("GetAccountsForInstance")
|
||||||
|
|
||||||
accounts := []*gtsmodel.Account{}
|
accounts := []*gtsmodel.Account{}
|
||||||
|
|
||||||
q := i.conn.Model(&accounts).Where("domain = ?", domain).Order("id DESC")
|
q := i.conn.NewSelect().
|
||||||
|
Model(&accounts).
|
||||||
|
Where("domain = ?", domain).
|
||||||
|
Order("id DESC")
|
||||||
|
|
||||||
if maxID != "" {
|
if maxID != "" {
|
||||||
q = q.Where("id < ?", maxID)
|
q = q.Where("id < ?", maxID)
|
||||||
|
@ -96,17 +112,7 @@ func (i *instanceDB) GetInstanceAccounts(domain string, maxID string, limit int)
|
||||||
q = q.Limit(limit)
|
q = q.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := q.Select()
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
if err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(accounts) == 0 {
|
return accounts, err
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
return accounts, nil
|
|
||||||
}
|
}
|
|
@ -16,38 +16,38 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mediaDB struct {
|
type mediaDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mediaDB) newMediaQ(i interface{}) *orm.Query {
|
func (m *mediaDB) newMediaQ(i interface{}) *bun.SelectQuery {
|
||||||
return m.conn.Model(i).
|
return m.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(i).
|
||||||
Relation("Account")
|
Relation("Account")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mediaDB) GetAttachmentByID(id string) (*gtsmodel.MediaAttachment, db.Error) {
|
func (m *mediaDB) GetAttachmentByID(ctx context.Context, id string) (*gtsmodel.MediaAttachment, db.Error) {
|
||||||
attachment := >smodel.MediaAttachment{}
|
attachment := >smodel.MediaAttachment{}
|
||||||
|
|
||||||
q := m.newMediaQ(attachment).
|
q := m.newMediaQ(attachment).
|
||||||
Where("media_attachment.id = ?", id)
|
Where("media_attachment.id = ?", id)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return attachment, err
|
return attachment, err
|
||||||
}
|
}
|
|
@ -16,25 +16,23 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mentionDB struct {
|
type mentionDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
cache cache.Cache
|
cache cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,14 +65,16 @@ func (m *mentionDB) mentionCached(id string) (*gtsmodel.Mention, bool) {
|
||||||
return mention, true
|
return mention, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mentionDB) newMentionQ(i interface{}) *orm.Query {
|
func (m *mentionDB) newMentionQ(i interface{}) *bun.SelectQuery {
|
||||||
return m.conn.Model(i).
|
return m.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(i).
|
||||||
Relation("Status").
|
Relation("Status").
|
||||||
Relation("OriginAccount").
|
Relation("OriginAccount").
|
||||||
Relation("TargetAccount")
|
Relation("TargetAccount")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mentionDB) GetMention(id string) (*gtsmodel.Mention, db.Error) {
|
func (m *mentionDB) GetMention(ctx context.Context, id string) (*gtsmodel.Mention, db.Error) {
|
||||||
if mention, cached := m.mentionCached(id); cached {
|
if mention, cached := m.mentionCached(id); cached {
|
||||||
return mention, nil
|
return mention, nil
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func (m *mentionDB) GetMention(id string) (*gtsmodel.Mention, db.Error) {
|
||||||
q := m.newMentionQ(mention).
|
q := m.newMentionQ(mention).
|
||||||
Where("mention.id = ?", id)
|
Where("mention.id = ?", id)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
if err == nil && mention != nil {
|
if err == nil && mention != nil {
|
||||||
m.cacheMention(id, mention)
|
m.cacheMention(id, mention)
|
||||||
|
@ -93,11 +93,11 @@ func (m *mentionDB) GetMention(id string) (*gtsmodel.Mention, db.Error) {
|
||||||
return mention, err
|
return mention, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mentionDB) GetMentions(ids []string) ([]*gtsmodel.Mention, db.Error) {
|
func (m *mentionDB) GetMentions(ctx context.Context, ids []string) ([]*gtsmodel.Mention, db.Error) {
|
||||||
mentions := []*gtsmodel.Mention{}
|
mentions := []*gtsmodel.Mention{}
|
||||||
|
|
||||||
for _, i := range ids {
|
for _, i := range ids {
|
||||||
mention, err := m.GetMention(i)
|
mention, err := m.GetMention(ctx, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, processErrorResponse(err)
|
return nil, processErrorResponse(err)
|
||||||
}
|
}
|
|
@ -16,25 +16,23 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type notificationDB struct {
|
type notificationDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
cache cache.Cache
|
cache cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,14 +65,16 @@ func (n *notificationDB) notificationCached(id string) (*gtsmodel.Notification,
|
||||||
return notification, true
|
return notification, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationDB) newNotificationQ(i interface{}) *orm.Query {
|
func (n *notificationDB) newNotificationQ(i interface{}) *bun.SelectQuery {
|
||||||
return n.conn.Model(i).
|
return n.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(i).
|
||||||
Relation("OriginAccount").
|
Relation("OriginAccount").
|
||||||
Relation("TargetAccount").
|
Relation("TargetAccount").
|
||||||
Relation("Status")
|
Relation("Status")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationDB) GetNotification(id string) (*gtsmodel.Notification, db.Error) {
|
func (n *notificationDB) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, db.Error) {
|
||||||
if notification, cached := n.notificationCached(id); cached {
|
if notification, cached := n.notificationCached(id); cached {
|
||||||
return notification, nil
|
return notification, nil
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func (n *notificationDB) GetNotification(id string) (*gtsmodel.Notification, db.
|
||||||
q := n.newNotificationQ(notification).
|
q := n.newNotificationQ(notification).
|
||||||
Where("notification.id = ?", id)
|
Where("notification.id = ?", id)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
if err == nil && notification != nil {
|
if err == nil && notification != nil {
|
||||||
n.cacheNotification(id, notification)
|
n.cacheNotification(id, notification)
|
||||||
|
@ -93,10 +93,11 @@ func (n *notificationDB) GetNotification(id string) (*gtsmodel.Notification, db.
|
||||||
return notification, err
|
return notification, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notificationDB) GetNotifications(accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, db.Error) {
|
func (n *notificationDB) GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, db.Error) {
|
||||||
// begin by selecting just the IDs
|
// begin by selecting just the IDs
|
||||||
notifIDs := []*gtsmodel.Notification{}
|
notifIDs := []*gtsmodel.Notification{}
|
||||||
q := n.conn.
|
q := n.conn.
|
||||||
|
NewSelect().
|
||||||
Model(¬ifIDs).
|
Model(¬ifIDs).
|
||||||
Column("id").
|
Column("id").
|
||||||
Where("target_account_id = ?", accountID).
|
Where("target_account_id = ?", accountID).
|
||||||
|
@ -114,7 +115,7 @@ func (n *notificationDB) GetNotifications(accountID string, limit int, maxID str
|
||||||
q = q.Limit(limit)
|
q = q.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -123,7 +124,7 @@ func (n *notificationDB) GetNotifications(accountID string, limit int, maxID str
|
||||||
// reason for this is that for each notif, we can instead get it from our cache if it's cached
|
// reason for this is that for each notif, we can instead get it from our cache if it's cached
|
||||||
notifications := []*gtsmodel.Notification{}
|
notifications := []*gtsmodel.Notification{}
|
||||||
for _, notifID := range notifIDs {
|
for _, notifID := range notifIDs {
|
||||||
notif, err := n.GetNotification(notifID.ID)
|
notif, err := n.GetNotification(ctx, notifID.ID)
|
||||||
errP := processErrorResponse(err)
|
errP := processErrorResponse(err)
|
||||||
if errP != nil {
|
if errP != nil {
|
||||||
return nil, errP
|
return nil, errP
|
|
@ -16,44 +16,49 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type relationshipDB struct {
|
type relationshipDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *orm.Query {
|
func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *bun.SelectQuery {
|
||||||
return r.conn.Model(block).
|
return r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(block).
|
||||||
Relation("Account").
|
Relation("Account").
|
||||||
Relation("TargetAccount")
|
Relation("TargetAccount")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) newFollowQ(follow interface{}) *orm.Query {
|
func (r *relationshipDB) newFollowQ(follow interface{}) *bun.SelectQuery {
|
||||||
return r.conn.Model(follow).
|
return r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(follow).
|
||||||
Relation("Account").
|
Relation("Account").
|
||||||
Relation("TargetAccount")
|
Relation("TargetAccount")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) IsBlocked(account1 string, account2 string, eitherDirection bool) (bool, db.Error) {
|
func (r *relationshipDB) IsBlocked(ctx context.Context, account1 string, account2 string, eitherDirection bool) (bool, db.Error) {
|
||||||
q := r.conn.
|
q := r.conn.
|
||||||
|
NewSelect().
|
||||||
Model(>smodel.Block{}).
|
Model(>smodel.Block{}).
|
||||||
Where("account_id = ?", account1).
|
Where("account_id = ?", account1).
|
||||||
Where("target_account_id = ?", account2)
|
Where("target_account_id = ?", account2).
|
||||||
|
Limit(1)
|
||||||
|
|
||||||
if eitherDirection {
|
if eitherDirection {
|
||||||
q = q.
|
q = q.
|
||||||
|
@ -61,30 +66,36 @@ func (r *relationshipDB) IsBlocked(account1 string, account2 string, eitherDirec
|
||||||
Where("account_id = ?", account2)
|
Where("account_id = ?", account2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.Exists()
|
return exists(ctx, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) GetBlock(account1 string, account2 string) (*gtsmodel.Block, db.Error) {
|
func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2 string) (*gtsmodel.Block, db.Error) {
|
||||||
block := >smodel.Block{}
|
block := >smodel.Block{}
|
||||||
|
|
||||||
q := r.newBlockQ(block).
|
q := r.newBlockQ(block).
|
||||||
Where("block.account_id = ?", account1).
|
Where("block.account_id = ?", account1).
|
||||||
Where("block.target_account_id = ?", account2)
|
Where("block.target_account_id = ?", account2)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return block, err
|
return block, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.Error) {
|
func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.Error) {
|
||||||
rel := >smodel.Relationship{
|
rel := >smodel.Relationship{
|
||||||
ID: targetAccount,
|
ID: targetAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the requesting account follows the target account
|
// check if the requesting account follows the target account
|
||||||
follow := >smodel.Follow{}
|
follow := >smodel.Follow{}
|
||||||
if err := r.conn.Model(follow).Where("account_id = ?", requestingAccount).Where("target_account_id = ?", targetAccount).Select(); err != nil {
|
if err := r.conn.
|
||||||
if err != pg.ErrNoRows {
|
NewSelect().
|
||||||
|
Model(follow).
|
||||||
|
Where("account_id = ?", requestingAccount).
|
||||||
|
Where("target_account_id = ?", targetAccount).
|
||||||
|
Limit(1).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
// a proper error
|
// a proper error
|
||||||
return nil, fmt.Errorf("getrelationship: error checking follow existence: %s", err)
|
return nil, fmt.Errorf("getrelationship: error checking follow existence: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -100,75 +111,101 @@ func (r *relationshipDB) GetRelationship(requestingAccount string, targetAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the target account follows the requesting account
|
// check if the target account follows the requesting account
|
||||||
followedBy, err := r.conn.Model(>smodel.Follow{}).Where("account_id = ?", targetAccount).Where("target_account_id = ?", requestingAccount).Exists()
|
count, err := r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Follow{}).
|
||||||
|
Where("account_id = ?", targetAccount).
|
||||||
|
Where("target_account_id = ?", requestingAccount).
|
||||||
|
Limit(1).
|
||||||
|
Count(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getrelationship: error checking followed_by existence: %s", err)
|
return nil, fmt.Errorf("getrelationship: error checking followed_by existence: %s", err)
|
||||||
}
|
}
|
||||||
rel.FollowedBy = followedBy
|
rel.FollowedBy = count > 0
|
||||||
|
|
||||||
// check if the requesting account blocks the target account
|
// check if the requesting account blocks the target account
|
||||||
blocking, err := r.conn.Model(>smodel.Block{}).Where("account_id = ?", requestingAccount).Where("target_account_id = ?", targetAccount).Exists()
|
count, err = r.conn.NewSelect().
|
||||||
|
Model(>smodel.Block{}).
|
||||||
|
Where("account_id = ?", requestingAccount).
|
||||||
|
Where("target_account_id = ?", targetAccount).
|
||||||
|
Limit(1).
|
||||||
|
Count(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getrelationship: error checking blocking existence: %s", err)
|
return nil, fmt.Errorf("getrelationship: error checking blocking existence: %s", err)
|
||||||
}
|
}
|
||||||
rel.Blocking = blocking
|
rel.Blocking = count > 0
|
||||||
|
|
||||||
// check if the target account blocks the requesting account
|
// check if the target account blocks the requesting account
|
||||||
blockedBy, err := r.conn.Model(>smodel.Block{}).Where("account_id = ?", targetAccount).Where("target_account_id = ?", requestingAccount).Exists()
|
count, err = r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Block{}).
|
||||||
|
Where("account_id = ?", targetAccount).
|
||||||
|
Where("target_account_id = ?", requestingAccount).
|
||||||
|
Limit(1).
|
||||||
|
Count(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getrelationship: error checking blocked existence: %s", err)
|
return nil, fmt.Errorf("getrelationship: error checking blocked existence: %s", err)
|
||||||
}
|
}
|
||||||
rel.BlockedBy = blockedBy
|
rel.BlockedBy = count > 0
|
||||||
|
|
||||||
// check if there's a pending following request from requesting account to target account
|
// check if there's a pending following request from requesting account to target account
|
||||||
requested, err := r.conn.Model(>smodel.FollowRequest{}).Where("account_id = ?", requestingAccount).Where("target_account_id = ?", targetAccount).Exists()
|
count, err = r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.FollowRequest{}).
|
||||||
|
Where("account_id = ?", requestingAccount).
|
||||||
|
Where("target_account_id = ?", targetAccount).
|
||||||
|
Limit(1).
|
||||||
|
Count(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getrelationship: error checking blocked existence: %s", err)
|
return nil, fmt.Errorf("getrelationship: error checking blocked existence: %s", err)
|
||||||
}
|
}
|
||||||
rel.Requested = requested
|
rel.Requested = count > 0
|
||||||
|
|
||||||
return rel, nil
|
return rel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) IsFollowing(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) {
|
func (r *relationshipDB) IsFollowing(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) {
|
||||||
if sourceAccount == nil || targetAccount == nil {
|
if sourceAccount == nil || targetAccount == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
q := r.conn.
|
q := r.conn.
|
||||||
|
NewSelect().
|
||||||
Model(>smodel.Follow{}).
|
Model(>smodel.Follow{}).
|
||||||
Where("account_id = ?", sourceAccount.ID).
|
Where("account_id = ?", sourceAccount.ID).
|
||||||
Where("target_account_id = ?", targetAccount.ID)
|
Where("target_account_id = ?", targetAccount.ID).
|
||||||
|
Limit(1)
|
||||||
|
|
||||||
return q.Exists()
|
return exists(ctx, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) IsFollowRequested(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) {
|
func (r *relationshipDB) IsFollowRequested(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, db.Error) {
|
||||||
if sourceAccount == nil || targetAccount == nil {
|
if sourceAccount == nil || targetAccount == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
q := r.conn.
|
q := r.conn.
|
||||||
|
NewSelect().
|
||||||
Model(>smodel.FollowRequest{}).
|
Model(>smodel.FollowRequest{}).
|
||||||
Where("account_id = ?", sourceAccount.ID).
|
Where("account_id = ?", sourceAccount.ID).
|
||||||
Where("target_account_id = ?", targetAccount.ID)
|
Where("target_account_id = ?", targetAccount.ID)
|
||||||
|
|
||||||
return q.Exists()
|
return exists(ctx, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) IsMutualFollowing(account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, db.Error) {
|
func (r *relationshipDB) IsMutualFollowing(ctx context.Context, account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, db.Error) {
|
||||||
if account1 == nil || account2 == nil {
|
if account1 == nil || account2 == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure account 1 follows account 2
|
// make sure account 1 follows account 2
|
||||||
f1, err := r.IsFollowing(account1, account2)
|
f1, err := r.IsFollowing(ctx, account1, account2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, processErrorResponse(err)
|
return false, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure account 2 follows account 1
|
// make sure account 2 follows account 1
|
||||||
f2, err := r.IsFollowing(account2, account1)
|
f2, err := r.IsFollowing(ctx, account2, account1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, processErrorResponse(err)
|
return false, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
@ -176,14 +213,16 @@ func (r *relationshipDB) IsMutualFollowing(account1 *gtsmodel.Account, account2
|
||||||
return f1 && f2, nil
|
return f1 && f2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) AcceptFollowRequest(originAccountID string, targetAccountID string) (*gtsmodel.Follow, db.Error) {
|
func (r *relationshipDB) AcceptFollowRequest(ctx context.Context, originAccountID string, targetAccountID string) (*gtsmodel.Follow, db.Error) {
|
||||||
// make sure the original follow request exists
|
// make sure the original follow request exists
|
||||||
fr := >smodel.FollowRequest{}
|
fr := >smodel.FollowRequest{}
|
||||||
if err := r.conn.Model(fr).Where("account_id = ?", originAccountID).Where("target_account_id = ?", targetAccountID).Select(); err != nil {
|
if err := r.conn.
|
||||||
if err == pg.ErrMultiRows {
|
NewSelect().
|
||||||
return nil, db.ErrNoEntries
|
Model(fr).
|
||||||
}
|
Where("account_id = ?", originAccountID).
|
||||||
return nil, err
|
Where("target_account_id = ?", targetAccountID).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
|
return nil, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new follow to 'replace' the request with
|
// create a new follow to 'replace' the request with
|
||||||
|
@ -195,82 +234,95 @@ func (r *relationshipDB) AcceptFollowRequest(originAccountID string, targetAccou
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the follow already exists, just update the URI -- we don't need to do anything else
|
// if the follow already exists, just update the URI -- we don't need to do anything else
|
||||||
if _, err := r.conn.Model(follow).OnConflict("ON CONSTRAINT follows_account_id_target_account_id_key DO UPDATE set uri = ?", follow.URI).Insert(); err != nil {
|
if _, err := r.conn.
|
||||||
return nil, err
|
NewInsert().
|
||||||
|
Model(follow).
|
||||||
|
On("CONFLICT CONSTRAINT follows_account_id_target_account_id_key DO UPDATE set uri = ?", follow.URI).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return nil, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// now remove the follow request
|
// now remove the follow request
|
||||||
if _, err := r.conn.Model(>smodel.FollowRequest{}).Where("account_id = ?", originAccountID).Where("target_account_id = ?", targetAccountID).Delete(); err != nil {
|
if _, err := r.conn.
|
||||||
return nil, err
|
NewDelete().
|
||||||
|
Model(>smodel.FollowRequest{}).
|
||||||
|
Where("account_id = ?", originAccountID).
|
||||||
|
Where("target_account_id = ?", targetAccountID).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return nil, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return follow, nil
|
return follow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) GetAccountFollowRequests(accountID string) ([]*gtsmodel.FollowRequest, db.Error) {
|
func (r *relationshipDB) GetAccountFollowRequests(ctx context.Context, accountID string) ([]*gtsmodel.FollowRequest, db.Error) {
|
||||||
followRequests := []*gtsmodel.FollowRequest{}
|
followRequests := []*gtsmodel.FollowRequest{}
|
||||||
|
|
||||||
q := r.newFollowQ(&followRequests).
|
q := r.newFollowQ(&followRequests).
|
||||||
Where("target_account_id = ?", accountID)
|
Where("target_account_id = ?", accountID)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return followRequests, err
|
return followRequests, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) GetAccountFollows(accountID string) ([]*gtsmodel.Follow, db.Error) {
|
func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, db.Error) {
|
||||||
follows := []*gtsmodel.Follow{}
|
follows := []*gtsmodel.Follow{}
|
||||||
|
|
||||||
q := r.newFollowQ(&follows).
|
q := r.newFollowQ(&follows).
|
||||||
Where("account_id = ?", accountID)
|
Where("account_id = ?", accountID)
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
return follows, err
|
return follows, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) CountAccountFollows(accountID string, localOnly bool) (int, db.Error) {
|
func (r *relationshipDB) CountAccountFollows(ctx context.Context, accountID string, localOnly bool) (int, db.Error) {
|
||||||
return r.conn.
|
return r.conn.
|
||||||
|
NewSelect().
|
||||||
Model(&[]*gtsmodel.Follow{}).
|
Model(&[]*gtsmodel.Follow{}).
|
||||||
Where("account_id = ?", accountID).
|
Where("account_id = ?", accountID).
|
||||||
Count()
|
Count(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) GetAccountFollowedBy(accountID string, localOnly bool) ([]*gtsmodel.Follow, db.Error) {
|
func (r *relationshipDB) GetAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) ([]*gtsmodel.Follow, db.Error) {
|
||||||
|
|
||||||
follows := []*gtsmodel.Follow{}
|
follows := []*gtsmodel.Follow{}
|
||||||
|
|
||||||
q := r.conn.Model(&follows)
|
q := r.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&follows)
|
||||||
|
|
||||||
if localOnly {
|
if localOnly {
|
||||||
// for local accounts let's get where domain is null OR where domain is an empty string, just to be safe
|
// for local accounts let's get where domain is null OR where domain is an empty string, just to be safe
|
||||||
whereGroup := func(q *pg.Query) (*pg.Query, error) {
|
whereGroup := func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||||
q = q.
|
q = q.
|
||||||
WhereOr("? IS NULL", pg.Ident("a.domain")).
|
WhereOr("? IS NULL", bun.Ident("a.domain")).
|
||||||
WhereOr("a.domain = ?", "")
|
WhereOr("a.domain = ?", "")
|
||||||
return q, nil
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
q = q.ColumnExpr("follow.*").
|
q = q.ColumnExpr("follow.*").
|
||||||
Join("JOIN accounts AS a ON follow.account_id = TEXT(a.id)").
|
Join("JOIN accounts AS a ON follow.account_id = TEXT(a.id)").
|
||||||
Where("follow.target_account_id = ?", accountID).
|
Where("follow.target_account_id = ?", accountID).
|
||||||
WhereGroup(whereGroup)
|
WhereGroup(" AND ", whereGroup)
|
||||||
} else {
|
} else {
|
||||||
q = q.Where("target_account_id = ?", accountID)
|
q = q.Where("target_account_id = ?", accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := q.Select(); err != nil {
|
if err := q.Scan(ctx); err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return follows, nil
|
return follows, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, processErrorResponse(err)
|
||||||
}
|
}
|
||||||
return follows, nil
|
return follows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *relationshipDB) CountAccountFollowedBy(accountID string, localOnly bool) (int, db.Error) {
|
func (r *relationshipDB) CountAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) (int, db.Error) {
|
||||||
return r.conn.
|
return r.conn.
|
||||||
|
NewSelect().
|
||||||
Model(&[]*gtsmodel.Follow{}).
|
Model(&[]*gtsmodel.Follow{}).
|
||||||
Where("target_account_id = ?", accountID).
|
Where("target_account_id = ?", accountID).
|
||||||
Count()
|
Count(ctx)
|
||||||
}
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bundb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sessionDB struct {
|
||||||
|
config *config.Config
|
||||||
|
conn *bun.DB
|
||||||
|
log *logrus.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db.Error) {
|
||||||
|
rs := new(gtsmodel.RouterSession)
|
||||||
|
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(rs).
|
||||||
|
Limit(1)
|
||||||
|
|
||||||
|
_, err := q.Exec(ctx)
|
||||||
|
|
||||||
|
err = processErrorResponse(err)
|
||||||
|
|
||||||
|
return rs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionDB) CreateSession(ctx context.Context) (*gtsmodel.RouterSession, db.Error) {
|
||||||
|
auth := make([]byte, 32)
|
||||||
|
crypt := make([]byte, 32)
|
||||||
|
|
||||||
|
if _, err := rand.Read(auth); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := rand.Read(crypt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rid, err := id.NewULID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := >smodel.RouterSession{
|
||||||
|
ID: rid,
|
||||||
|
Auth: auth,
|
||||||
|
Crypt: crypt,
|
||||||
|
}
|
||||||
|
|
||||||
|
q := s.conn.
|
||||||
|
NewInsert().
|
||||||
|
Model(rs)
|
||||||
|
|
||||||
|
_, err = q.Exec(ctx)
|
||||||
|
|
||||||
|
err = processErrorResponse(err)
|
||||||
|
|
||||||
|
return rs, err
|
||||||
|
}
|
|
@ -0,0 +1,375 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bundb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statusDB struct {
|
||||||
|
config *config.Config
|
||||||
|
conn *bun.DB
|
||||||
|
log *logrus.Logger
|
||||||
|
cache cache.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) cacheStatus(id string, status *gtsmodel.Status) {
|
||||||
|
if s.cache == nil {
|
||||||
|
s.cache = cache.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.cache.Store(id, status); err != nil {
|
||||||
|
s.log.Panicf("statusDB: error storing in cache: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) statusCached(id string) (*gtsmodel.Status, bool) {
|
||||||
|
if s.cache == nil {
|
||||||
|
s.cache = cache.New()
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
sI, err := s.cache.Fetch(id)
|
||||||
|
if err != nil || sI == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
status, ok := sI.(*gtsmodel.Status)
|
||||||
|
if !ok {
|
||||||
|
s.log.Panicf("statusDB: cached interface with key %s was not a status", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return status, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) newStatusQ(status interface{}) *bun.SelectQuery {
|
||||||
|
return s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(status).
|
||||||
|
Relation("Attachments").
|
||||||
|
Relation("Tags").
|
||||||
|
Relation("Mentions").
|
||||||
|
Relation("Emojis").
|
||||||
|
Relation("Account").
|
||||||
|
Relation("InReplyToAccount").
|
||||||
|
Relation("BoostOfAccount").
|
||||||
|
Relation("CreatedWithApplication")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) getAttachedStatuses(ctx context.Context, status *gtsmodel.Status) *gtsmodel.Status {
|
||||||
|
if status.InReplyToID != "" && status.InReplyTo == nil {
|
||||||
|
if inReplyTo, cached := s.statusCached(status.InReplyToID); cached {
|
||||||
|
status.InReplyTo = inReplyTo
|
||||||
|
} else if inReplyTo, err := s.GetStatusByID(ctx, status.InReplyToID); err == nil {
|
||||||
|
status.InReplyTo = inReplyTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.BoostOfID != "" && status.BoostOf == nil {
|
||||||
|
if boostOf, cached := s.statusCached(status.BoostOfID); cached {
|
||||||
|
status.BoostOf = boostOf
|
||||||
|
} else if boostOf, err := s.GetStatusByID(ctx, status.BoostOfID); err == nil {
|
||||||
|
status.BoostOf = boostOf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) newFaveQ(faves interface{}) *bun.SelectQuery {
|
||||||
|
return s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(faves).
|
||||||
|
Relation("Account").
|
||||||
|
Relation("TargetAccount").
|
||||||
|
Relation("Status")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusByID(ctx context.Context, id string) (*gtsmodel.Status, db.Error) {
|
||||||
|
if status, cached := s.statusCached(id); cached {
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status := new(gtsmodel.Status)
|
||||||
|
|
||||||
|
q := s.newStatusQ(status).
|
||||||
|
Where("status.id = ?", id)
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != nil {
|
||||||
|
s.cacheStatus(id, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.getAttachedStatuses(ctx, status), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusByURI(ctx context.Context, uri string) (*gtsmodel.Status, db.Error) {
|
||||||
|
if status, cached := s.statusCached(uri); cached {
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status := >smodel.Status{}
|
||||||
|
|
||||||
|
q := s.newStatusQ(status).
|
||||||
|
Where("LOWER(status.uri) = LOWER(?)", uri)
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != nil {
|
||||||
|
s.cacheStatus(uri, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.getAttachedStatuses(ctx, status), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusByURL(ctx context.Context, uri string) (*gtsmodel.Status, db.Error) {
|
||||||
|
if status, cached := s.statusCached(uri); cached {
|
||||||
|
return status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
status := >smodel.Status{}
|
||||||
|
|
||||||
|
q := s.newStatusQ(status).
|
||||||
|
Where("LOWER(status.url) = LOWER(?)", uri)
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != nil {
|
||||||
|
s.cacheStatus(uri, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.getAttachedStatuses(ctx, status), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) db.Error {
|
||||||
|
transaction := func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
// create links between this status and any emojis it uses
|
||||||
|
for _, i := range status.EmojiIDs {
|
||||||
|
if _, err := tx.NewInsert().Model(>smodel.StatusToEmoji{
|
||||||
|
StatusID: status.ID,
|
||||||
|
EmojiID: i,
|
||||||
|
}).Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create links between this status and any tags it uses
|
||||||
|
for _, i := range status.TagIDs {
|
||||||
|
if _, err := tx.NewInsert().Model(>smodel.StatusToTag{
|
||||||
|
StatusID: status.ID,
|
||||||
|
TagID: i,
|
||||||
|
}).Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the status ID of the media attachments to the new status
|
||||||
|
for _, a := range status.Attachments {
|
||||||
|
a.StatusID = status.ID
|
||||||
|
a.UpdatedAt = time.Now()
|
||||||
|
if _, err := s.conn.NewUpdate().Model(a).
|
||||||
|
Where("id = ?", a.ID).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := tx.NewInsert().Model(status).Exec(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return processErrorResponse(s.conn.RunInTx(ctx, nil, transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusParents(ctx context.Context, status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.Error) {
|
||||||
|
parents := []*gtsmodel.Status{}
|
||||||
|
s.statusParent(ctx, status, &parents, onlyDirect)
|
||||||
|
|
||||||
|
return parents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) statusParent(ctx context.Context, status *gtsmodel.Status, foundStatuses *[]*gtsmodel.Status, onlyDirect bool) {
|
||||||
|
if status.InReplyToID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parentStatus, err := s.GetStatusByID(ctx, status.InReplyToID)
|
||||||
|
if err == nil {
|
||||||
|
*foundStatuses = append(*foundStatuses, parentStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onlyDirect {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.statusParent(ctx, parentStatus, foundStatuses, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusChildren(ctx context.Context, status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, db.Error) {
|
||||||
|
foundStatuses := &list.List{}
|
||||||
|
foundStatuses.PushFront(status)
|
||||||
|
s.statusChildren(ctx, status, foundStatuses, onlyDirect, minID)
|
||||||
|
|
||||||
|
children := []*gtsmodel.Status{}
|
||||||
|
for e := foundStatuses.Front(); e != nil; e = e.Next() {
|
||||||
|
entry, ok := e.Value.(*gtsmodel.Status)
|
||||||
|
if !ok {
|
||||||
|
panic(errors.New("entry in foundStatuses was not a *gtsmodel.Status"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// only append children, not the overall parent status
|
||||||
|
if entry.ID != status.ID {
|
||||||
|
children = append(children, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return children, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) statusChildren(ctx context.Context, status *gtsmodel.Status, foundStatuses *list.List, onlyDirect bool, minID string) {
|
||||||
|
immediateChildren := []*gtsmodel.Status{}
|
||||||
|
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&immediateChildren).
|
||||||
|
Where("in_reply_to_id = ?", status.ID)
|
||||||
|
if minID != "" {
|
||||||
|
q = q.Where("status.id > ?", minID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := q.Scan(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range immediateChildren {
|
||||||
|
insertLoop:
|
||||||
|
for e := foundStatuses.Front(); e != nil; e = e.Next() {
|
||||||
|
entry, ok := e.Value.(*gtsmodel.Status)
|
||||||
|
if !ok {
|
||||||
|
panic(errors.New("entry in foundStatuses was not a *gtsmodel.Status"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if child.InReplyToAccountID != "" && entry.ID == child.InReplyToID {
|
||||||
|
foundStatuses.InsertAfter(child, e)
|
||||||
|
break insertLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only do one loop if we only want direct children
|
||||||
|
if onlyDirect {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.statusChildren(ctx, child, foundStatuses, false, minID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) CountStatusReplies(ctx context.Context, status *gtsmodel.Status) (int, db.Error) {
|
||||||
|
return s.conn.NewSelect().Model(>smodel.Status{}).Where("in_reply_to_id = ?", status.ID).Count(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) CountStatusReblogs(ctx context.Context, status *gtsmodel.Status) (int, db.Error) {
|
||||||
|
return s.conn.NewSelect().Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Count(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) CountStatusFaves(ctx context.Context, status *gtsmodel.Status) (int, db.Error) {
|
||||||
|
return s.conn.NewSelect().Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Count(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) IsStatusFavedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.StatusFave{}).
|
||||||
|
Where("status_id = ?", status.ID).
|
||||||
|
Where("account_id = ?", accountID)
|
||||||
|
|
||||||
|
return exists(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.Status{}).
|
||||||
|
Where("boost_of_id = ?", status.ID).
|
||||||
|
Where("account_id = ?", accountID)
|
||||||
|
|
||||||
|
return exists(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.StatusMute{}).
|
||||||
|
Where("status_id = ?", status.ID).
|
||||||
|
Where("account_id = ?", accountID)
|
||||||
|
|
||||||
|
return exists(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
||||||
|
q := s.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(>smodel.StatusBookmark{}).
|
||||||
|
Where("status_id = ?", status.ID).
|
||||||
|
Where("account_id = ?", accountID)
|
||||||
|
|
||||||
|
return exists(ctx, q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusFaves(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.StatusFave, db.Error) {
|
||||||
|
faves := []*gtsmodel.StatusFave{}
|
||||||
|
|
||||||
|
q := s.newFaveQ(&faves).
|
||||||
|
Where("status_id = ?", status.ID)
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
return faves, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.Status, db.Error) {
|
||||||
|
reblogs := []*gtsmodel.Status{}
|
||||||
|
|
||||||
|
q := s.newStatusQ(&reblogs).
|
||||||
|
Where("boost_of_id = ?", status.ID)
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Scan(ctx))
|
||||||
|
return reblogs, err
|
||||||
|
}
|
|
@ -16,9 +16,10 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg_test
|
package bundb_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -28,7 +29,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type StatusTestSuite struct {
|
type StatusTestSuite struct {
|
||||||
PGStandardTestSuite
|
BunDBStandardTestSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatusTestSuite) SetupSuite() {
|
func (suite *StatusTestSuite) SetupSuite() {
|
||||||
|
@ -56,8 +57,9 @@ func (suite *StatusTestSuite) TearDownTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestGetStatusByID() {
|
func (suite *StatusTestSuite) TestGetStatusByID() {
|
||||||
status, err := suite.db.GetStatusByID(suite.testStatuses["local_account_1_status_1"].ID)
|
status, err := suite.db.GetStatusByID(context.Background(), suite.testStatuses["local_account_1_status_1"].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
suite.NotNil(status)
|
suite.NotNil(status)
|
||||||
|
@ -70,7 +72,7 @@ func (suite *StatusTestSuite) TestGetStatusByID() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestGetStatusByURI() {
|
func (suite *StatusTestSuite) TestGetStatusByURI() {
|
||||||
status, err := suite.db.GetStatusByURI(suite.testStatuses["local_account_1_status_1"].URI)
|
status, err := suite.db.GetStatusByURI(context.Background(), suite.testStatuses["local_account_1_status_1"].URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -84,7 +86,7 @@ func (suite *StatusTestSuite) TestGetStatusByURI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestGetStatusWithExtras() {
|
func (suite *StatusTestSuite) TestGetStatusWithExtras() {
|
||||||
status, err := suite.db.GetStatusByID(suite.testStatuses["admin_account_status_1"].ID)
|
status, err := suite.db.GetStatusByID(context.Background(), suite.testStatuses["admin_account_status_1"].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -97,7 +99,7 @@ func (suite *StatusTestSuite) TestGetStatusWithExtras() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestGetStatusWithMention() {
|
func (suite *StatusTestSuite) TestGetStatusWithMention() {
|
||||||
status, err := suite.db.GetStatusByID(suite.testStatuses["local_account_2_status_5"].ID)
|
status, err := suite.db.GetStatusByID(context.Background(), suite.testStatuses["local_account_2_status_5"].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -112,18 +114,18 @@ func (suite *StatusTestSuite) TestGetStatusWithMention() {
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestGetStatusTwice() {
|
func (suite *StatusTestSuite) TestGetStatusTwice() {
|
||||||
before1 := time.Now()
|
before1 := time.Now()
|
||||||
_, err := suite.db.GetStatusByURI(suite.testStatuses["local_account_1_status_1"].URI)
|
_, err := suite.db.GetStatusByURI(context.Background(), suite.testStatuses["local_account_1_status_1"].URI)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
after1 := time.Now()
|
after1 := time.Now()
|
||||||
duration1 := after1.Sub(before1)
|
duration1 := after1.Sub(before1)
|
||||||
fmt.Println(duration1.Nanoseconds())
|
fmt.Println(duration1.Milliseconds())
|
||||||
|
|
||||||
before2 := time.Now()
|
before2 := time.Now()
|
||||||
_, err = suite.db.GetStatusByURI(suite.testStatuses["local_account_1_status_1"].URI)
|
_, err = suite.db.GetStatusByURI(context.Background(), suite.testStatuses["local_account_1_status_1"].URI)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
after2 := time.Now()
|
after2 := time.Now()
|
||||||
duration2 := after2.Sub(before2)
|
duration2 := after2.Sub(before2)
|
||||||
fmt.Println(duration2.Nanoseconds())
|
fmt.Println(duration2.Milliseconds())
|
||||||
|
|
||||||
// second retrieval should be several orders faster since it will be cached now
|
// second retrieval should be several orders faster since it will be cached now
|
||||||
suite.Less(duration2, duration1)
|
suite.Less(duration2, duration1)
|
|
@ -16,43 +16,35 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package pg
|
package bundb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
type timelineDB struct {
|
type timelineDB struct {
|
||||||
config *config.Config
|
config *config.Config
|
||||||
conn *pg.DB
|
conn *bun.DB
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *timelineDB) GetHomeTimeline(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
|
func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
|
||||||
statuses := []*gtsmodel.Status{}
|
statuses := []*gtsmodel.Status{}
|
||||||
q := t.conn.Model(&statuses)
|
q := t.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&statuses)
|
||||||
|
|
||||||
q = q.ColumnExpr("status.*").
|
q = q.ColumnExpr("status.*").
|
||||||
// Find out who accountID follows.
|
// Find out who accountID follows.
|
||||||
Join("LEFT JOIN follows AS f ON f.target_account_id = status.account_id").
|
Join("LEFT JOIN follows AS f ON f.target_account_id = status.account_id").
|
||||||
// Use a WhereGroup here to specify that we want EITHER statuses posted by accounts that accountID follows,
|
|
||||||
// OR statuses posted by accountID itself (since a user should be able to see their own statuses).
|
|
||||||
//
|
|
||||||
// This is equivalent to something like WHERE ... AND (... OR ...)
|
|
||||||
// See: https://pg.uptrace.dev/queries/#select
|
|
||||||
WhereGroup(func(q *pg.Query) (*pg.Query, error) {
|
|
||||||
q = q.WhereOr("f.account_id = ?", accountID).
|
|
||||||
WhereOr("status.account_id = ?", accountID)
|
|
||||||
return q, nil
|
|
||||||
}).
|
|
||||||
// Sort by highest ID (newest) to lowest ID (oldest)
|
// Sort by highest ID (newest) to lowest ID (oldest)
|
||||||
Order("status.id DESC")
|
Order("status.id DESC")
|
||||||
|
|
||||||
|
@ -81,29 +73,32 @@ func (t *timelineDB) GetHomeTimeline(accountID string, maxID string, sinceID str
|
||||||
q = q.Limit(limit)
|
q = q.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := q.Select()
|
// Use a WhereGroup here to specify that we want EITHER statuses posted by accounts that accountID follows,
|
||||||
if err != nil {
|
// OR statuses posted by accountID itself (since a user should be able to see their own statuses).
|
||||||
if err == pg.ErrNoRows {
|
//
|
||||||
return nil, db.ErrNoEntries
|
// This is equivalent to something like WHERE ... AND (... OR ...)
|
||||||
}
|
// See: https://bun.uptrace.dev/guide/queries.html#select
|
||||||
return nil, err
|
whereGroup := func(*bun.SelectQuery) *bun.SelectQuery {
|
||||||
|
return q.
|
||||||
|
WhereOr("f.account_id = ?", accountID).
|
||||||
|
WhereOr("status.account_id = ?", accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(statuses) == 0 {
|
q = q.WhereGroup(" AND ", whereGroup)
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
return statuses, nil
|
return statuses, processErrorResponse(q.Scan(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *timelineDB) GetPublicTimeline(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
|
func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, db.Error) {
|
||||||
statuses := []*gtsmodel.Status{}
|
statuses := []*gtsmodel.Status{}
|
||||||
|
|
||||||
q := t.conn.Model(&statuses).
|
q := t.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&statuses).
|
||||||
Where("visibility = ?", gtsmodel.VisibilityPublic).
|
Where("visibility = ?", gtsmodel.VisibilityPublic).
|
||||||
Where("? IS NULL", pg.Ident("in_reply_to_id")).
|
Where("? IS NULL", bun.Ident("in_reply_to_id")).
|
||||||
Where("? IS NULL", pg.Ident("in_reply_to_uri")).
|
Where("? IS NULL", bun.Ident("in_reply_to_uri")).
|
||||||
Where("? IS NULL", pg.Ident("boost_of_id")).
|
Where("? IS NULL", bun.Ident("boost_of_id")).
|
||||||
Order("status.id DESC")
|
Order("status.id DESC")
|
||||||
|
|
||||||
if maxID != "" {
|
if maxID != "" {
|
||||||
|
@ -126,28 +121,18 @@ func (t *timelineDB) GetPublicTimeline(accountID string, maxID string, sinceID s
|
||||||
q = q.Limit(limit)
|
q = q.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := q.Select()
|
return statuses, processErrorResponse(q.Scan(ctx))
|
||||||
if err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(statuses) == 0 {
|
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
return statuses, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO optimize this query and the logic here, because it's slow as balls -- it takes like a literal second to return with a limit of 20!
|
// TODO optimize this query and the logic here, because it's slow as balls -- it takes like a literal second to return with a limit of 20!
|
||||||
// It might be worth serving it through a timeline instead of raw DB queries, like we do for Home feeds.
|
// It might be worth serving it through a timeline instead of raw DB queries, like we do for Home feeds.
|
||||||
func (t *timelineDB) GetFavedTimeline(accountID string, maxID string, minID string, limit int) ([]*gtsmodel.Status, string, string, db.Error) {
|
func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, maxID string, minID string, limit int) ([]*gtsmodel.Status, string, string, db.Error) {
|
||||||
|
|
||||||
faves := []*gtsmodel.StatusFave{}
|
faves := []*gtsmodel.StatusFave{}
|
||||||
|
|
||||||
fq := t.conn.Model(&faves).
|
fq := t.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&faves).
|
||||||
Where("account_id = ?", accountID).
|
Where("account_id = ?", accountID).
|
||||||
Order("id DESC")
|
Order("id DESC")
|
||||||
|
|
||||||
|
@ -163,9 +148,9 @@ func (t *timelineDB) GetFavedTimeline(accountID string, maxID string, minID stri
|
||||||
fq = fq.Limit(limit)
|
fq = fq.Limit(limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := fq.Select()
|
err := fq.Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, "", "", db.ErrNoEntries
|
return nil, "", "", db.ErrNoEntries
|
||||||
}
|
}
|
||||||
return nil, "", "", err
|
return nil, "", "", err
|
||||||
|
@ -185,9 +170,13 @@ func (t *timelineDB) GetFavedTimeline(accountID string, maxID string, minID stri
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses := []*gtsmodel.Status{}
|
statuses := []*gtsmodel.Status{}
|
||||||
err = t.conn.Model(&statuses).Where("id IN (?)", pg.In(in)).Select()
|
err = t.conn.
|
||||||
|
NewSelect().
|
||||||
|
Model(&statuses).
|
||||||
|
Where("id IN (?)", bun.In(in)).
|
||||||
|
Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, "", "", db.ErrNoEntries
|
return nil, "", "", db.ErrNoEntries
|
||||||
}
|
}
|
||||||
return nil, "", "", err
|
return nil, "", "", err
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
GoToSocial
|
||||||
|
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bundb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
// processErrorResponse parses the given error and returns an appropriate DBError.
|
||||||
|
func processErrorResponse(err error) db.Error {
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case sql.ErrNoRows:
|
||||||
|
return db.ErrNoEntries
|
||||||
|
default:
|
||||||
|
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
||||||
|
return db.ErrAlreadyExists
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exists(ctx context.Context, q *bun.SelectQuery) (bool, db.Error) {
|
||||||
|
count, err := q.Count(ctx)
|
||||||
|
|
||||||
|
exists := count != 0
|
||||||
|
|
||||||
|
err = processErrorResponse(err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == db.ErrNoEntries {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return exists, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func notExists(ctx context.Context, q *bun.SelectQuery) (bool, db.Error) {
|
||||||
|
count, err := q.Count(ctx)
|
||||||
|
|
||||||
|
notExists := count == 0
|
||||||
|
|
||||||
|
err = processErrorResponse(err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err == db.ErrNoEntries {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return notExists, nil
|
||||||
|
}
|
|
@ -19,6 +19,8 @@
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +40,7 @@ type DB interface {
|
||||||
Mention
|
Mention
|
||||||
Notification
|
Notification
|
||||||
Relationship
|
Relationship
|
||||||
|
Session
|
||||||
Status
|
Status
|
||||||
Timeline
|
Timeline
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ type DB interface {
|
||||||
//
|
//
|
||||||
// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking
|
// Note: this func doesn't/shouldn't do any manipulation of the accounts in the DB, it's just for checking
|
||||||
// if they exist in the db and conveniently returning them if they do.
|
// if they exist in the db and conveniently returning them if they do.
|
||||||
MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error)
|
MentionStringsToMentions(ctx context.Context, targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error)
|
||||||
|
|
||||||
// TagStringsToTags takes a slice of deduplicated, lowercase tags in the form "somehashtag", which have been
|
// TagStringsToTags takes a slice of deduplicated, lowercase tags in the form "somehashtag", which have been
|
||||||
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||||
|
@ -61,7 +64,7 @@ type DB interface {
|
||||||
//
|
//
|
||||||
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking
|
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking
|
||||||
// if they exist in the db already, and conveniently returning them, or creating new tag structs.
|
// if they exist in the db already, and conveniently returning them, or creating new tag structs.
|
||||||
TagStringsToTags(tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error)
|
TagStringsToTags(ctx context.Context, tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error)
|
||||||
|
|
||||||
// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been
|
// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been
|
||||||
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||||
|
@ -69,5 +72,5 @@ type DB interface {
|
||||||
//
|
//
|
||||||
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking
|
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking
|
||||||
// if they exist in the db and conveniently returning them if they do.
|
// if they exist in the db and conveniently returning them if they do.
|
||||||
EmojiStringsToEmojis(emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error)
|
EmojiStringsToEmojis(ctx context.Context, emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,22 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "net/url"
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
// Domain contains DB functions related to domains and domain blocks.
|
// Domain contains DB functions related to domains and domain blocks.
|
||||||
type Domain interface {
|
type Domain interface {
|
||||||
// IsDomainBlocked checks if an instance-level domain block exists for the given domain string (eg., `example.org`).
|
// IsDomainBlocked checks if an instance-level domain block exists for the given domain string (eg., `example.org`).
|
||||||
IsDomainBlocked(domain string) (bool, Error)
|
IsDomainBlocked(ctx context.Context, domain string) (bool, Error)
|
||||||
|
|
||||||
// AreDomainsBlocked checks if an instance-level domain block exists for any of the given domains strings, and returns true if even one is found.
|
// AreDomainsBlocked checks if an instance-level domain block exists for any of the given domains strings, and returns true if even one is found.
|
||||||
AreDomainsBlocked(domains []string) (bool, Error)
|
AreDomainsBlocked(ctx context.Context, domains []string) (bool, Error)
|
||||||
|
|
||||||
// IsURIBlocked checks if an instance-level domain block exists for the `host` in the given URI (eg., `https://example.org/users/whatever`).
|
// IsURIBlocked checks if an instance-level domain block exists for the `host` in the given URI (eg., `https://example.org/users/whatever`).
|
||||||
IsURIBlocked(uri *url.URL) (bool, Error)
|
IsURIBlocked(ctx context.Context, uri *url.URL) (bool, Error)
|
||||||
|
|
||||||
// AreURIsBlocked checks if an instance-level domain block exists for any `host` in the given URI slice, and returns true if even one is found.
|
// AreURIsBlocked checks if an instance-level domain block exists for any `host` in the given URI slice, and returns true if even one is found.
|
||||||
AreURIsBlocked(uris []*url.URL) (bool, Error)
|
AreURIsBlocked(ctx context.Context, uris []*url.URL) (bool, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,23 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// Instance contains functions for instance-level actions (counting instance users etc.).
|
// Instance contains functions for instance-level actions (counting instance users etc.).
|
||||||
type Instance interface {
|
type Instance interface {
|
||||||
// CountInstanceUsers returns the number of known accounts registered with the given domain.
|
// CountInstanceUsers returns the number of known accounts registered with the given domain.
|
||||||
CountInstanceUsers(domain string) (int, Error)
|
CountInstanceUsers(ctx context.Context, domain string) (int, Error)
|
||||||
|
|
||||||
// CountInstanceStatuses returns the number of known statuses posted from the given domain.
|
// CountInstanceStatuses returns the number of known statuses posted from the given domain.
|
||||||
CountInstanceStatuses(domain string) (int, Error)
|
CountInstanceStatuses(ctx context.Context, domain string) (int, Error)
|
||||||
|
|
||||||
// CountInstanceDomains returns the number of known instances known that the given domain federates with.
|
// CountInstanceDomains returns the number of known instances known that the given domain federates with.
|
||||||
CountInstanceDomains(domain string) (int, Error)
|
CountInstanceDomains(ctx context.Context, domain string) (int, Error)
|
||||||
|
|
||||||
// GetInstanceAccounts returns a slice of accounts from the given instance, arranged by ID.
|
// GetInstanceAccounts returns a slice of accounts from the given instance, arranged by ID.
|
||||||
GetInstanceAccounts(domain string, maxID string, limit int) ([]*gtsmodel.Account, Error)
|
GetInstanceAccounts(ctx context.Context, domain string, maxID string, limit int) ([]*gtsmodel.Account, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,14 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// Media contains functions related to creating/getting/removing media attachments.
|
// Media contains functions related to creating/getting/removing media attachments.
|
||||||
type Media interface {
|
type Media interface {
|
||||||
// GetAttachmentByID gets a single attachment by its ID
|
// GetAttachmentByID gets a single attachment by its ID
|
||||||
GetAttachmentByID(id string) (*gtsmodel.MediaAttachment, Error)
|
GetAttachmentByID(ctx context.Context, id string) (*gtsmodel.MediaAttachment, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,17 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// Mention contains functions for getting/creating mentions in the database.
|
// Mention contains functions for getting/creating mentions in the database.
|
||||||
type Mention interface {
|
type Mention interface {
|
||||||
// GetMention gets a single mention by ID
|
// GetMention gets a single mention by ID
|
||||||
GetMention(id string) (*gtsmodel.Mention, Error)
|
GetMention(ctx context.Context, id string) (*gtsmodel.Mention, Error)
|
||||||
|
|
||||||
// GetMentions gets multiple mentions.
|
// GetMentions gets multiple mentions.
|
||||||
GetMentions(ids []string) ([]*gtsmodel.Mention, Error)
|
GetMentions(ctx context.Context, ids []string) ([]*gtsmodel.Mention, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,14 +18,18 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// Notification contains functions for creating and getting notifications.
|
// Notification contains functions for creating and getting notifications.
|
||||||
type Notification interface {
|
type Notification interface {
|
||||||
// GetNotifications returns a slice of notifications that pertain to the given accountID.
|
// GetNotifications returns a slice of notifications that pertain to the given accountID.
|
||||||
//
|
//
|
||||||
// Returned notifications will be ordered ID descending (ie., highest/newest to lowest/oldest).
|
// Returned notifications will be ordered ID descending (ie., highest/newest to lowest/oldest).
|
||||||
GetNotifications(accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, Error)
|
GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, Error)
|
||||||
// GetNotification returns one notification according to its id.
|
// GetNotification returns one notification according to its id.
|
||||||
GetNotification(id string) (*gtsmodel.Notification, Error)
|
GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,205 +0,0 @@
|
||||||
/*
|
|
||||||
GoToSocial
|
|
||||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
type basicDB struct {
|
|
||||||
config *config.Config
|
|
||||||
conn *pg.DB
|
|
||||||
log *logrus.Logger
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) Put(i interface{}) db.Error {
|
|
||||||
_, err := b.conn.Model(i).Insert(i)
|
|
||||||
if err != nil && strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
|
||||||
return db.ErrAlreadyExists
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) GetByID(id string, i interface{}) db.Error {
|
|
||||||
if err := b.conn.Model(i).Where("id = ?", id).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) GetWhere(where []db.Where, i interface{}) db.Error {
|
|
||||||
if len(where) == 0 {
|
|
||||||
return errors.New("no queries provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
q := b.conn.Model(i)
|
|
||||||
for _, w := range where {
|
|
||||||
|
|
||||||
if w.Value == nil {
|
|
||||||
q = q.Where("? IS NULL", pg.Ident(w.Key))
|
|
||||||
} else {
|
|
||||||
if w.CaseInsensitive {
|
|
||||||
q = q.Where("LOWER(?) = LOWER(?)", pg.Safe(w.Key), w.Value)
|
|
||||||
} else {
|
|
||||||
q = q.Where("? = ?", pg.Safe(w.Key), w.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := q.Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) GetAll(i interface{}) db.Error {
|
|
||||||
if err := b.conn.Model(i).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) DeleteByID(id string, i interface{}) db.Error {
|
|
||||||
if _, err := b.conn.Model(i).Where("id = ?", id).Delete(); err != nil {
|
|
||||||
// if there are no rows *anyway* then that's fine
|
|
||||||
// just return err if there's an actual error
|
|
||||||
if err != pg.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) DeleteWhere(where []db.Where, i interface{}) db.Error {
|
|
||||||
if len(where) == 0 {
|
|
||||||
return errors.New("no queries provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
q := b.conn.Model(i)
|
|
||||||
for _, w := range where {
|
|
||||||
q = q.Where("? = ?", pg.Safe(w.Key), w.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := q.Delete(); err != nil {
|
|
||||||
// if there are no rows *anyway* then that's fine
|
|
||||||
// just return err if there's an actual error
|
|
||||||
if err != pg.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) Upsert(i interface{}, conflictColumn string) db.Error {
|
|
||||||
if _, err := b.conn.Model(i).OnConflict(fmt.Sprintf("(%s) DO UPDATE", conflictColumn)).Insert(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) UpdateByID(id string, i interface{}) db.Error {
|
|
||||||
if _, err := b.conn.Model(i).Where("id = ?", id).OnConflict("(id) DO UPDATE").Insert(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) UpdateOneByID(id string, key string, value interface{}, i interface{}) db.Error {
|
|
||||||
_, err := b.conn.Model(i).Set("? = ?", pg.Safe(key), value).Where("id = ?", id).Update()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) UpdateWhere(where []db.Where, key string, value interface{}, i interface{}) db.Error {
|
|
||||||
q := b.conn.Model(i)
|
|
||||||
|
|
||||||
for _, w := range where {
|
|
||||||
if w.Value == nil {
|
|
||||||
q = q.Where("? IS NULL", pg.Ident(w.Key))
|
|
||||||
} else {
|
|
||||||
if w.CaseInsensitive {
|
|
||||||
q = q.Where("LOWER(?) = LOWER(?)", pg.Safe(w.Key), w.Value)
|
|
||||||
} else {
|
|
||||||
q = q.Where("? = ?", pg.Safe(w.Key), w.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
q = q.Set("? = ?", pg.Safe(key), value)
|
|
||||||
|
|
||||||
_, err := q.Update()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) CreateTable(i interface{}) db.Error {
|
|
||||||
return b.conn.Model(i).CreateTable(&orm.CreateTableOptions{
|
|
||||||
IfNotExists: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) DropTable(i interface{}) db.Error {
|
|
||||||
return b.conn.Model(i).DropTable(&orm.DropTableOptions{
|
|
||||||
IfExists: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) RegisterTable(i interface{}) db.Error {
|
|
||||||
orm.RegisterTable(i)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) IsHealthy(ctx context.Context) db.Error {
|
|
||||||
return b.conn.Ping(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) Stop(ctx context.Context) db.Error {
|
|
||||||
b.log.Info("closing db connection")
|
|
||||||
if err := b.conn.Close(); err != nil {
|
|
||||||
// only cancel if there's a problem closing the db
|
|
||||||
b.cancel()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,318 +0,0 @@
|
||||||
/*
|
|
||||||
GoToSocial
|
|
||||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package pg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/go-pg/pg/v10/orm"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
)
|
|
||||||
|
|
||||||
type statusDB struct {
|
|
||||||
config *config.Config
|
|
||||||
conn *pg.DB
|
|
||||||
log *logrus.Logger
|
|
||||||
cancel context.CancelFunc
|
|
||||||
cache cache.Cache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) cacheStatus(id string, status *gtsmodel.Status) {
|
|
||||||
if s.cache == nil {
|
|
||||||
s.cache = cache.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.cache.Store(id, status); err != nil {
|
|
||||||
s.log.Panicf("statusDB: error storing in cache: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) statusCached(id string) (*gtsmodel.Status, bool) {
|
|
||||||
if s.cache == nil {
|
|
||||||
s.cache = cache.New()
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
sI, err := s.cache.Fetch(id)
|
|
||||||
if err != nil || sI == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
status, ok := sI.(*gtsmodel.Status)
|
|
||||||
if !ok {
|
|
||||||
s.log.Panicf("statusDB: cached interface with key %s was not a status", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) newStatusQ(status interface{}) *orm.Query {
|
|
||||||
return s.conn.Model(status).
|
|
||||||
Relation("Attachments").
|
|
||||||
Relation("Tags").
|
|
||||||
Relation("Mentions").
|
|
||||||
Relation("Emojis").
|
|
||||||
Relation("Account").
|
|
||||||
Relation("InReplyTo").
|
|
||||||
Relation("InReplyToAccount").
|
|
||||||
Relation("BoostOf").
|
|
||||||
Relation("BoostOfAccount").
|
|
||||||
Relation("CreatedWithApplication")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) newFaveQ(faves interface{}) *orm.Query {
|
|
||||||
return s.conn.Model(faves).
|
|
||||||
Relation("Account").
|
|
||||||
Relation("TargetAccount").
|
|
||||||
Relation("Status")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusByID(id string) (*gtsmodel.Status, db.Error) {
|
|
||||||
if status, cached := s.statusCached(id); cached {
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
status := >smodel.Status{}
|
|
||||||
|
|
||||||
q := s.newStatusQ(status).
|
|
||||||
Where("status.id = ?", id)
|
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
|
||||||
|
|
||||||
if err == nil && status != nil {
|
|
||||||
s.cacheStatus(id, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.Error) {
|
|
||||||
if status, cached := s.statusCached(uri); cached {
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
status := >smodel.Status{}
|
|
||||||
|
|
||||||
q := s.newStatusQ(status).
|
|
||||||
Where("LOWER(status.uri) = LOWER(?)", uri)
|
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
|
||||||
|
|
||||||
if err == nil && status != nil {
|
|
||||||
s.cacheStatus(uri, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusByURL(uri string) (*gtsmodel.Status, db.Error) {
|
|
||||||
if status, cached := s.statusCached(uri); cached {
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
status := >smodel.Status{}
|
|
||||||
|
|
||||||
q := s.newStatusQ(status).
|
|
||||||
Where("LOWER(status.url) = LOWER(?)", uri)
|
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
|
||||||
|
|
||||||
if err == nil && status != nil {
|
|
||||||
s.cacheStatus(uri, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return status, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) PutStatus(status *gtsmodel.Status) db.Error {
|
|
||||||
transaction := func(tx *pg.Tx) error {
|
|
||||||
// create links between this status and any emojis it uses
|
|
||||||
for _, i := range status.EmojiIDs {
|
|
||||||
if _, err := tx.Model(>smodel.StatusToEmoji{
|
|
||||||
StatusID: status.ID,
|
|
||||||
EmojiID: i,
|
|
||||||
}).Insert(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create links between this status and any tags it uses
|
|
||||||
for _, i := range status.TagIDs {
|
|
||||||
if _, err := tx.Model(>smodel.StatusToTag{
|
|
||||||
StatusID: status.ID,
|
|
||||||
TagID: i,
|
|
||||||
}).Insert(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// change the status ID of the media attachments to the new status
|
|
||||||
for _, a := range status.Attachments {
|
|
||||||
a.StatusID = status.ID
|
|
||||||
a.UpdatedAt = time.Now()
|
|
||||||
if _, err := s.conn.Model(a).
|
|
||||||
Where("id = ?", a.ID).
|
|
||||||
Update(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := tx.Model(status).Insert()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return processErrorResponse(s.conn.RunInTransaction(context.Background(), transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.Error) {
|
|
||||||
parents := []*gtsmodel.Status{}
|
|
||||||
s.statusParent(status, &parents, onlyDirect)
|
|
||||||
|
|
||||||
return parents, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) statusParent(status *gtsmodel.Status, foundStatuses *[]*gtsmodel.Status, onlyDirect bool) {
|
|
||||||
if status.InReplyToID == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parentStatus, err := s.GetStatusByID(status.InReplyToID)
|
|
||||||
if err == nil {
|
|
||||||
*foundStatuses = append(*foundStatuses, parentStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
if onlyDirect {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.statusParent(parentStatus, foundStatuses, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusChildren(status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, db.Error) {
|
|
||||||
foundStatuses := &list.List{}
|
|
||||||
foundStatuses.PushFront(status)
|
|
||||||
s.statusChildren(status, foundStatuses, onlyDirect, minID)
|
|
||||||
|
|
||||||
children := []*gtsmodel.Status{}
|
|
||||||
for e := foundStatuses.Front(); e != nil; e = e.Next() {
|
|
||||||
entry, ok := e.Value.(*gtsmodel.Status)
|
|
||||||
if !ok {
|
|
||||||
panic(errors.New("entry in foundStatuses was not a *gtsmodel.Status"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// only append children, not the overall parent status
|
|
||||||
if entry.ID != status.ID {
|
|
||||||
children = append(children, entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return children, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) statusChildren(status *gtsmodel.Status, foundStatuses *list.List, onlyDirect bool, minID string) {
|
|
||||||
immediateChildren := []*gtsmodel.Status{}
|
|
||||||
|
|
||||||
q := s.conn.Model(&immediateChildren).Where("in_reply_to_id = ?", status.ID)
|
|
||||||
if minID != "" {
|
|
||||||
q = q.Where("status.id > ?", minID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := q.Select(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range immediateChildren {
|
|
||||||
insertLoop:
|
|
||||||
for e := foundStatuses.Front(); e != nil; e = e.Next() {
|
|
||||||
entry, ok := e.Value.(*gtsmodel.Status)
|
|
||||||
if !ok {
|
|
||||||
panic(errors.New("entry in foundStatuses was not a *gtsmodel.Status"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if child.InReplyToAccountID != "" && entry.ID == child.InReplyToID {
|
|
||||||
foundStatuses.InsertAfter(child, e)
|
|
||||||
break insertLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only do one loop if we only want direct children
|
|
||||||
if onlyDirect {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.statusChildren(child, foundStatuses, false, minID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) CountStatusReplies(status *gtsmodel.Status) (int, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.Status{}).Where("in_reply_to_id = ?", status.ID).Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) CountStatusReblogs(status *gtsmodel.Status) (int, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) CountStatusFaves(status *gtsmodel.Status) (int, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) IsStatusFavedBy(status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) IsStatusRebloggedBy(status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) IsStatusMutedBy(status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.StatusMute{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) IsStatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, db.Error) {
|
|
||||||
return s.conn.Model(>smodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusFaves(status *gtsmodel.Status) ([]*gtsmodel.StatusFave, db.Error) {
|
|
||||||
faves := []*gtsmodel.StatusFave{}
|
|
||||||
|
|
||||||
q := s.newFaveQ(&faves).
|
|
||||||
Where("status_id = ?", status.ID)
|
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
|
||||||
|
|
||||||
return faves, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *statusDB) GetStatusReblogs(status *gtsmodel.Status) ([]*gtsmodel.Status, db.Error) {
|
|
||||||
reblogs := []*gtsmodel.Status{}
|
|
||||||
|
|
||||||
q := s.newStatusQ(&reblogs).
|
|
||||||
Where("boost_of_id = ?", status.ID)
|
|
||||||
|
|
||||||
err := processErrorResponse(q.Select())
|
|
||||||
|
|
||||||
return reblogs, err
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package pg
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
// processErrorResponse parses the given error and returns an appropriate DBError.
|
|
||||||
func processErrorResponse(err error) db.Error {
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return nil
|
|
||||||
case pg.ErrNoRows:
|
|
||||||
return db.ErrNoEntries
|
|
||||||
case pg.ErrMultiRows:
|
|
||||||
return db.ErrMultipleEntries
|
|
||||||
default:
|
|
||||||
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
|
||||||
return db.ErrAlreadyExists
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,54 +18,58 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
// Relationship contains functions for getting or modifying the relationship between two accounts.
|
// Relationship contains functions for getting or modifying the relationship between two accounts.
|
||||||
type Relationship interface {
|
type Relationship interface {
|
||||||
// IsBlocked checks whether account 1 has a block in place against block2.
|
// IsBlocked checks whether account 1 has a block in place against block2.
|
||||||
// If eitherDirection is true, then the function returns true if account1 blocks account2, OR if account2 blocks account1.
|
// If eitherDirection is true, then the function returns true if account1 blocks account2, OR if account2 blocks account1.
|
||||||
IsBlocked(account1 string, account2 string, eitherDirection bool) (bool, Error)
|
IsBlocked(ctx context.Context, account1 string, account2 string, eitherDirection bool) (bool, Error)
|
||||||
|
|
||||||
// GetBlock returns the block from account1 targeting account2, if it exists, or an error if it doesn't.
|
// GetBlock returns the block from account1 targeting account2, if it exists, or an error if it doesn't.
|
||||||
//
|
//
|
||||||
// Because this is slower than Blocked, only use it if you need the actual Block struct for some reason,
|
// Because this is slower than Blocked, only use it if you need the actual Block struct for some reason,
|
||||||
// not if you're just checking for the existence of a block.
|
// not if you're just checking for the existence of a block.
|
||||||
GetBlock(account1 string, account2 string) (*gtsmodel.Block, Error)
|
GetBlock(ctx context.Context, account1 string, account2 string) (*gtsmodel.Block, Error)
|
||||||
|
|
||||||
// GetRelationship retrieves the relationship of the targetAccount to the requestingAccount.
|
// GetRelationship retrieves the relationship of the targetAccount to the requestingAccount.
|
||||||
GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, Error)
|
GetRelationship(ctx context.Context, requestingAccount string, targetAccount string) (*gtsmodel.Relationship, Error)
|
||||||
|
|
||||||
// IsFollowing returns true if sourceAccount follows target account, or an error if something goes wrong while finding out.
|
// IsFollowing returns true if sourceAccount follows target account, or an error if something goes wrong while finding out.
|
||||||
IsFollowing(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, Error)
|
IsFollowing(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, Error)
|
||||||
|
|
||||||
// IsFollowRequested returns true if sourceAccount has requested to follow target account, or an error if something goes wrong while finding out.
|
// IsFollowRequested returns true if sourceAccount has requested to follow target account, or an error if something goes wrong while finding out.
|
||||||
IsFollowRequested(sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, Error)
|
IsFollowRequested(ctx context.Context, sourceAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (bool, Error)
|
||||||
|
|
||||||
// IsMutualFollowing returns true if account1 and account2 both follow each other, or an error if something goes wrong while finding out.
|
// IsMutualFollowing returns true if account1 and account2 both follow each other, or an error if something goes wrong while finding out.
|
||||||
IsMutualFollowing(account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, Error)
|
IsMutualFollowing(ctx context.Context, account1 *gtsmodel.Account, account2 *gtsmodel.Account) (bool, Error)
|
||||||
|
|
||||||
// AcceptFollowRequest moves a follow request in the database from the follow_requests table to the follows table.
|
// AcceptFollowRequest moves a follow request in the database from the follow_requests table to the follows table.
|
||||||
// In other words, it should create the follow, and delete the existing follow request.
|
// In other words, it should create the follow, and delete the existing follow request.
|
||||||
//
|
//
|
||||||
// It will return the newly created follow for further processing.
|
// It will return the newly created follow for further processing.
|
||||||
AcceptFollowRequest(originAccountID string, targetAccountID string) (*gtsmodel.Follow, Error)
|
AcceptFollowRequest(ctx context.Context, originAccountID string, targetAccountID string) (*gtsmodel.Follow, Error)
|
||||||
|
|
||||||
// GetAccountFollowRequests returns all follow requests targeting the given account.
|
// GetAccountFollowRequests returns all follow requests targeting the given account.
|
||||||
GetAccountFollowRequests(accountID string) ([]*gtsmodel.FollowRequest, Error)
|
GetAccountFollowRequests(ctx context.Context, accountID string) ([]*gtsmodel.FollowRequest, Error)
|
||||||
|
|
||||||
// GetAccountFollows returns a slice of follows owned by the given accountID.
|
// GetAccountFollows returns a slice of follows owned by the given accountID.
|
||||||
GetAccountFollows(accountID string) ([]*gtsmodel.Follow, Error)
|
GetAccountFollows(ctx context.Context, accountID string) ([]*gtsmodel.Follow, Error)
|
||||||
|
|
||||||
// CountAccountFollows returns the amount of accounts that the given accountID is following.
|
// CountAccountFollows returns the amount of accounts that the given accountID is following.
|
||||||
//
|
//
|
||||||
// If localOnly is set to true, then only follows from *this instance* will be returned.
|
// If localOnly is set to true, then only follows from *this instance* will be returned.
|
||||||
CountAccountFollows(accountID string, localOnly bool) (int, Error)
|
CountAccountFollows(ctx context.Context, accountID string, localOnly bool) (int, Error)
|
||||||
|
|
||||||
// GetAccountFollowedBy fetches follows that target given accountID.
|
// GetAccountFollowedBy fetches follows that target given accountID.
|
||||||
//
|
//
|
||||||
// If localOnly is set to true, then only follows from *this instance* will be returned.
|
// If localOnly is set to true, then only follows from *this instance* will be returned.
|
||||||
GetAccountFollowedBy(accountID string, localOnly bool) ([]*gtsmodel.Follow, Error)
|
GetAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) ([]*gtsmodel.Follow, Error)
|
||||||
|
|
||||||
// CountAccountFollowedBy returns the amounts that the given ID is followed by.
|
// CountAccountFollowedBy returns the amounts that the given ID is followed by.
|
||||||
CountAccountFollowedBy(accountID string, localOnly bool) (int, Error)
|
CountAccountFollowedBy(ctx context.Context, accountID string, localOnly bool) (int, Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,16 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dereferencing
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"context"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *deref) blockedDomain(host string) (bool, error) {
|
// Session handles getting/creation of router sessions.
|
||||||
b := >smodel.DomainBlock{}
|
type Session interface {
|
||||||
err := d.db.GetWhere([]db.Where{{Key: "domain", Value: host, CaseInsensitive: true}}, b)
|
GetSession(ctx context.Context) (*gtsmodel.RouterSession, Error)
|
||||||
if err == nil {
|
CreateSession(ctx context.Context) (*gtsmodel.RouterSession, Error)
|
||||||
// block exists
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == db.ErrNoEntries {
|
|
||||||
// there are no entries so there's no block
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// there's an actual error
|
|
||||||
return false, err
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue