diff --git a/Makefile b/Makefile index 32be4b3a..2bfb0d05 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build clean ui -VERSION=1.1.2 +VERSION=1.1.3 BIN=answer DIR_SRC=./cmd/answer DOCKER_CMD=docker diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 643ad928..d63b765f 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -130,10 +130,10 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, userExternalLoginRepo := user_external_login.NewUserExternalLoginRepo(dataData) userExternalLoginService := user_external_login2.NewUserExternalLoginService(userRepo, userCommon, userExternalLoginRepo, emailService, siteInfoCommonService, userActiveActivityRepo) userNotificationConfigRepo := user_notification_config.NewUserNotificationConfigRepo(dataData) - userService := service.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo) + userNotificationConfigService := user_notification_config2.NewUserNotificationConfigService(userRepo, userNotificationConfigRepo) + userService := service.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo, userNotificationConfigService) captchaRepo := captcha.NewCaptchaRepo(dataData) captchaService := action.NewCaptchaService(captchaRepo) - userNotificationConfigService := user_notification_config2.NewUserNotificationConfigService(userRepo, userNotificationConfigRepo) userController := controller.NewUserController(authService, userService, captchaService, emailService, siteInfoCommonService, userNotificationConfigService) commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo) commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo) @@ -184,7 +184,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService, captchaService) answerController := controller.NewAnswerController(answerService, rankService, captchaService) searchParser := search_parser.NewSearchParser(tagCommonService, userCommon) - searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon) + searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon, tagCommonService) searchService := service.NewSearchService(searchParser, searchRepo) searchController := controller.NewSearchController(searchService, captchaService) serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService, objService, questionRepo, answerRepo, tagRepo, tagCommonService, notificationQueueService, activityQueueService) diff --git a/go.mod b/go.mod index e3ed74e9..37c3e547 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/lib/pq v1.10.7 github.com/microcosm-cc/bluemonday v1.0.21 github.com/mojocn/base64Captcha v1.3.5 - github.com/ory/dockertest/v3 v3.9.1 + github.com/ory/dockertest/v3 v3.10.0 github.com/robfig/cron/v3 v3.0.1 github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405 github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f @@ -41,8 +41,8 @@ require ( github.com/swaggo/swag v1.16.1 github.com/tidwall/gjson v1.14.4 github.com/yuin/goldmark v1.4.13 - golang.org/x/crypto v0.11.0 - golang.org/x/net v0.12.0 + golang.org/x/crypto v0.13.0 + golang.org/x/net v0.15.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.24.0 @@ -51,22 +51,22 @@ require ( ) require ( - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect - github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bytedance/sonic v1.9.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/containerd/continuity v0.3.0 // indirect + github.com/containerd/continuity v0.4.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/cli v20.10.14+incompatible // indirect - github.com/docker/docker v20.10.7+incompatible // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f // indirect github.com/dsoprea/go-jpeg-image-structure v0.0.0-20190422055009-d6f9ba25cf48 // indirect github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696 // indirect @@ -87,7 +87,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -100,19 +100,19 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.1.2 // indirect + github.com/opencontainers/runc v1.1.9 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.9.2 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -124,7 +124,7 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect go.uber.org/atomic v1.10.0 // indirect @@ -133,9 +133,9 @@ require ( golang.org/x/arch v0.3.0 // indirect golang.org/x/image v0.1.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect - golang.org/x/tools v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.13.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index ed09da26..3b97bb54 100644 --- a/go.sum +++ b/go.sum @@ -39,8 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -56,8 +56,8 @@ github.com/Machiel/slugify v1.0.1 h1:EfWSlRWstMadsgzmiV7d0yVd2IFlagWH68Q+DcYCm4E github.com/Machiel/slugify v1.0.1/go.mod h1:fTFGn5uWEynW4CUMG7sWkYXOf1UgDxyTM3DbR6Qfg3k= 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/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= -github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -91,7 +91,6 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -103,18 +102,16 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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= @@ -123,22 +120,18 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= -github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 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/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -146,14 +139,14 @@ github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= -github.com/docker/cli v20.10.14+incompatible h1:dSBKJOVesDgHo7rbxlYjYsXe7gPzrTT+/cKQgpDAazg= -github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ= -github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f h1:vqfYiZ+xF0xJvl9SZ1kovmMgKjaGZIz/Hn8JDQdyd9A= github.com/dsoprea/go-exif v0.0.0-20190901173045-3ce78807c90f/go.mod h1:DmMpU91/Ax6BAwoRkjgRCr2rmgEgS4tsmatfV7M+U+c= github.com/dsoprea/go-jpeg-image-structure v0.0.0-20190422055009-d6f9ba25cf48 h1:9zARagUAxQJjibcDy+0+koUMR6sbX38L49Bk2Vni628= @@ -180,7 +173,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -246,8 +238,6 @@ github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -375,8 +365,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -525,9 +515,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -537,7 +526,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0= github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -560,10 +548,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= -github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= +github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -572,8 +558,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/ory/dockertest/v3 v3.9.1 h1:v4dkG+dlu76goxMiTT2j8zV7s4oPPEppKT8K8p2f1kY= -github.com/ory/dockertest/v3 v3.9.1/go.mod h1:42Ir9hmvaAPm0Mgibk6mBPi7SFvTXxEcnztDYOJ//uM= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= @@ -640,13 +626,8 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405 h1:2ieGkj4z/YPXVyQ2ayZUg3GwE1pYWd5f1RB6DzAOXKM= github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405/go.mod h1:rIxVzVLKlBwLxO+lC+k/I4HJfRQcemg/f/76Xmmzsec= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/segmentfault/pacman v1.0.5-0.20230822075009-309985fb8700 h1:VqxiuNGQg86GEKxnzmJegZR2Ufr7EVXo68mdKaf+/MQ= -github.com/segmentfault/pacman v1.0.5-0.20230822075009-309985fb8700/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs= github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f h1:9f2Bjf6bdMvNyUop32wAGJCdp+Jdm/d6nKBYvFvkRo0= github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs= -github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822075009-309985fb8700 h1:VZpexPTcr7sOxxYUGa/9cneyCESfisAhCNbaEuTpcwI= -github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822075009-309985fb8700/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs= github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822083413-c0075a2d401f h1:1KHe0uN6p798E7XJZPhZkgm/hXk5CTjisCvFMqaZSKI= github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822083413-c0075a2d401f/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs= github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 h1:BlqTgc3/MYKG6vMI2MI+6o+7P4Gy5PXlawu185wPXAk= @@ -664,9 +645,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -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.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -681,7 +661,6 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= @@ -717,7 +696,6 @@ github.com/swaggo/gin-swagger v1.5.3/go.mod h1:3XJKSfHjDMB5dBo/0rrTXidPmgLeqsX89 github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ= github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= @@ -738,10 +716,9 @@ github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= @@ -777,7 +754,6 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 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= @@ -812,8 +788,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -901,8 +877,8 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -942,14 +918,12 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -967,7 +941,6 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -975,7 +948,6 @@ golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -983,24 +955,23 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w 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-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/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/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/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1016,8 +987,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1038,7 +1009,6 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn 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-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/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-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1085,8 +1055,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= -golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= @@ -1190,7 +1160,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= @@ -1230,8 +1199,7 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/i18n/af_ZA.yaml b/i18n/af_ZA.yaml index cd2edf89..a5a528c5 100644 --- a/i18n/af_ZA.yaml +++ b/i18n/af_ZA.yaml @@ -1292,16 +1292,16 @@ ui: page_title: CSS and HTML custom_css: label: Custom CSS - text: This will insert as + text: This will insert as <link> head: label: Head - text: This will insert before + text: This will insert before </head> header: label: Header - text: This will insert after + text: This will insert after <body> footer: label: Footer - text: This will insert before . + text: This will insert before </body>. login: page_title: Login membership: diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 37ed2be2..8ba8392c 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1263,12 +1263,14 @@ ui: site_name: label: Site name msg: Site name cannot be empty. + msg_max_length: Site name must be at maximum 30 characters in length. site_url: label: Site URL text: The address of your site. msg: empty: Site URL cannot be empty. incorrect: Site URL incorrect format. + max_length: Site URL must be at maximum 512 characters in length. contact_email: label: Contact email text: Email address of key contact responsible for this site. @@ -1283,12 +1285,15 @@ ui: label: Name msg: Name cannot be empty. character: 'Must use the character set "a-z", "0-9", " - . _"' + msg_max_length: Name must be at maximum 30 characters in length. admin_password: label: Password text: >- You will need this password to log in. Please store it in a secure location. msg: Password cannot be empty. + msg_min_length: Password must be at least 8 characters in length. + msg_max_length: Password must be at maximum 32 characters in length. admin_email: label: Email text: You will need this email to log in. @@ -1652,16 +1657,16 @@ ui: page_title: CSS and HTML custom_css: label: Custom CSS - text: This will insert as + text: This will insert as <link> head: label: Head - text: This will insert before + text: This will insert before </head> header: label: Header - text: This will insert after + text: This will insert after <body> footer: label: Footer - text: This will insert before . + text: This will insert before </body>. sidebar: label: Sidebar text: This will insert in sidebar. diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 36523e8a..b14600a9 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1607,16 +1607,16 @@ ui: page_title: CSS 与 HTML custom_css: label: 自定义 CSS - text: 这将以 方式插入 + text: 这将以 <link> 方式插入 head: label: 头部 - text: 这将在 之前插入 + text: 这将在 </head> 之前插入 header: label: 页眉 - text: 这将在 之后插入 + text: 这将在 <body> 之后插入 footer: label: 页脚 - text: 这将在 之前插入. + text: 这将在 </body> 之前插入. sidebar: label: 侧边栏 text: 这将插入侧边栏中。 diff --git a/internal/base/constant/cache_key.go b/internal/base/constant/cache_key.go index c04e56e4..31b6b752 100644 --- a/internal/base/constant/cache_key.go +++ b/internal/base/constant/cache_key.go @@ -14,6 +14,7 @@ const ( SiteInfoCacheTime = 1 * time.Hour ConfigID2KEYCacheKeyPrefix = "answer:config:id:" ConfigKEY2ContentCacheKeyPrefix = "answer:config:key:" + ConfigCacheTime = 1 * time.Hour ConnectorUserExternalInfoCacheKey = "answer:connector:" ConnectorUserExternalInfoCacheTime = 10 * time.Minute SiteMapQuestionCacheKeyPrefix = "answer:sitemap:question:%d" diff --git a/internal/base/handler/handler.go b/internal/base/handler/handler.go index 22784a81..2e919d12 100644 --- a/internal/base/handler/handler.go +++ b/internal/base/handler/handler.go @@ -2,15 +2,13 @@ package handler import ( "errors" - "net/http" - "strings" - "github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/validator" "github.com/gin-gonic/gin" myErrors "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" + "net/http" ) // HandleResponse Handle response body @@ -74,10 +72,3 @@ func BindAndCheckReturnErr(ctx *gin.Context, data interface{}) (errFields []*val errFields, _ = validator.GetValidatorByLang(lang).Check(data) return errFields } - -func MsgWithParameter(msg string, list map[string]string) string { - for k, v := range list { - msg = strings.Replace(msg, "{{ "+k+" }}", v, -1) - } - return msg -} diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index 81344b85..60120e4c 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -319,11 +319,11 @@ func (ac *AnswerController) AnswerList(ctx *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param data body schema.AnswerAcceptedReq true "AnswerAcceptedReq" +// @Param data body schema.AcceptAnswerReq true "AcceptAnswerReq" // @Success 200 {string} string "" // @Router /answer/api/v1/answer/acceptance [post] func (ac *AnswerController) Accepted(ctx *gin.Context) { - req := &schema.AnswerAcceptedReq{} + req := &schema.AcceptAnswerReq{} if handler.BindAndCheck(ctx, req) { return } @@ -341,7 +341,7 @@ func (ac *AnswerController) Accepted(ctx *gin.Context) { return } - err = ac.answerService.UpdateAccepted(ctx, req) + err = ac.answerService.AcceptAnswer(ctx, req) handler.HandleResponse(ctx, err, nil) } diff --git a/internal/controller/comment_controller.go b/internal/controller/comment_controller.go index 17e65d49..20f623bf 100644 --- a/internal/controller/comment_controller.go +++ b/internal/controller/comment_controller.go @@ -157,20 +157,23 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) { } req.UserID = middleware.GetLoginUserIDFromContext(ctx) + req.IsAdmin = middleware.GetIsAdminFromContext(ctx) canList, err := cc.rankService.CheckOperationPermissions(ctx, req.UserID, []string{ - permission.CommentAdd, permission.CommentEdit, - permission.CommentDelete, permission.LinkUrlLimit, }) if err != nil { handler.HandleResponse(ctx, err, nil) return } - linkUrlLimitUser := canList[3] - req.IsAdmin = middleware.GetIsAdminFromContext(ctx) - isAdmin := middleware.GetUserIsAdminModerator(ctx) - if !isAdmin || !linkUrlLimitUser { + req.CanEdit = canList[0] || cc.rankService.CheckOperationObjectOwner(ctx, req.UserID, req.CommentID) + linkUrlLimitUser := canList[1] + if !req.CanEdit { + handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) + return + } + + if !req.IsAdmin || !linkUrlLimitUser { captchaPass := cc.actionService.ActionRecordVerifyCaptcha(ctx, entity.CaptchaActionEdit, req.UserID, req.CaptchaID, req.CaptchaCode) if !captchaPass { errFields := append([]*validator.FormErrorField{}, &validator.FormErrorField{ @@ -182,21 +185,8 @@ func (cc *CommentController) UpdateComment(ctx *gin.Context) { } } - req.CanAdd = canList[0] - req.CanEdit = canList[1] - req.CanDelete = canList[2] - can, err := cc.rankService.CheckOperationPermission(ctx, req.UserID, permission.CommentEdit, req.CommentID) - if err != nil { - handler.HandleResponse(ctx, err, nil) - return - } - if !can { - handler.HandleResponse(ctx, errors.Forbidden(reason.RankFailToMeetTheCondition), nil) - return - } - resp, err := cc.commentService.UpdateComment(ctx, req) - if !isAdmin || !linkUrlLimitUser { + if !req.IsAdmin || !linkUrlLimitUser { cc.actionService.ActionRecordAdd(ctx, entity.CaptchaActionEdit, req.UserID) } handler.HandleResponse(ctx, err, resp) diff --git a/internal/controller/question_controller.go b/internal/controller/question_controller.go index 71fc24ec..80b56f45 100644 --- a/internal/controller/question_controller.go +++ b/internal/controller/question_controller.go @@ -686,9 +686,9 @@ func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) { handler.HandleResponse(ctx, nil, nil) } -// SearchByTitleLike add question title like -// @Summary add question title like -// @Description add question title like +// GetSimilarQuestions fuzzy query similar questions based on title +// @Summary fuzzy query similar questions based on title +// @Description fuzzy query similar questions based on title // @Tags Question // @Accept json // @Produce json @@ -696,10 +696,9 @@ func (qc *QuestionController) UpdateQuestionInviteUser(ctx *gin.Context) { // @Param title query string true "title" default(string) // @Success 200 {object} handler.RespBody // @Router /answer/api/v1/question/similar [get] -func (qc *QuestionController) SearchByTitleLike(ctx *gin.Context) { +func (qc *QuestionController) GetSimilarQuestions(ctx *gin.Context) { title := ctx.Query("title") - userID := middleware.GetLoginUserIDFromContext(ctx) - resp, err := qc.questionService.SearchByTitleLike(ctx, title, userID) + resp, err := qc.questionService.GetQuestionsByTitle(ctx, title) handler.HandleResponse(ctx, err, resp) } diff --git a/internal/controller/tag_controller.go b/internal/controller/tag_controller.go index 55edc8ec..331b2a18 100644 --- a/internal/controller/tag_controller.go +++ b/internal/controller/tag_controller.go @@ -45,7 +45,6 @@ func (tc *TagController) SearchTagLike(ctx *gin.Context) { if handler.BindAndCheck(ctx, req) { return } - req.IsAdmin = middleware.GetIsAdminFromContext(ctx) resp, err := tc.tagCommonService.SearchTagLike(ctx, req) handler.HandleResponse(ctx, err, resp) } diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index bc5a753f..71da96d4 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -161,35 +161,41 @@ func (tc *TemplateController) QuestionList(ctx *gin.Context) { } func (tc *TemplateController) QuestionInfoeRdirect(ctx *gin.Context, siteInfo *schema.TemplateSiteInfoResp, correctTitle bool) (jump bool, url string) { - id := ctx.Param("id") + questionID := ctx.Param("id") title := ctx.Param("title") + answerID := uid.DeShortID(title) titleIsAnswerID := false needChangeShortID := false + objectType, err := obj.GetObjectTypeStrByObjectID(answerID) + if err == nil && objectType == constant.AnswerObjectType { + titleIsAnswerID = true + } + siteSeo, err := tc.siteInfoService.GetSiteSeo(ctx) if err != nil { return false, "" } - isShortID := uid.IsShortID(id) + isShortID := uid.IsShortID(questionID) if siteSeo.IsShortLink() { if !isShortID { - id = uid.EnShortID(id) + questionID = uid.EnShortID(questionID) needChangeShortID = true } + if titleIsAnswerID { + answerID = uid.EnShortID(answerID) + } } else { if isShortID { needChangeShortID = true - id = uid.DeShortID(id) + questionID = uid.DeShortID(questionID) + } + if titleIsAnswerID { + answerID = uid.DeShortID(answerID) } } - objectType, objectTypeerr := obj.GetObjectTypeStrByObjectID(uid.DeShortID(title)) - if objectTypeerr == nil { - if objectType == constant.AnswerObjectType { - titleIsAnswerID = true - } - } - url = fmt.Sprintf("%s/questions/%s", siteInfo.General.SiteUrl, id) + url = fmt.Sprintf("%s/questions/%s", siteInfo.General.SiteUrl, questionID) if siteInfo.SiteSeo.PermaLink == constant.PermaLinkQuestionID || siteInfo.SiteSeo.PermaLink == constant.PermaLinkQuestionIDByShortID { if len(ctx.Request.URL.Query()) > 0 { url = fmt.Sprintf("%s?%s", url, ctx.Request.URL.RawQuery) @@ -205,14 +211,14 @@ func (tc *TemplateController) QuestionInfoeRdirect(ctx *gin.Context, siteInfo *s return true, url } else { - detail, err := tc.templateRenderController.QuestionDetail(ctx, id) + detail, err := tc.templateRenderController.QuestionDetail(ctx, questionID) if err != nil { tc.Page404(ctx) return } url = fmt.Sprintf("%s/%s", url, htmltext.UrlTitle(detail.Title)) if titleIsAnswerID { - url = fmt.Sprintf("%s/%s", url, title) + url = fmt.Sprintf("%s/%s", url, answerID) } if len(ctx.Request.URL.Query()) > 0 { diff --git a/internal/entity/config_entity.go b/internal/entity/config_entity.go index 908903a1..c4275e51 100644 --- a/internal/entity/config_entity.go +++ b/internal/entity/config_entity.go @@ -2,6 +2,7 @@ package entity import ( "encoding/json" + "github.com/segmentfault/pacman/log" "github.com/answerdev/answer/pkg/converter" ) @@ -33,6 +34,9 @@ func (c *Config) JsonString() string { // GetIntValue get int value func (c *Config) GetIntValue() int { + if len(c.Value) == 0 { + log.Warnf("config value is empty, key: %s, value: %s", c.Key, c.Value) + } return converter.StringToInt(c.Value) } diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index c77cbd88..a4ebeba8 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -72,6 +72,7 @@ var migrations = []Migration{ NewMigration("v1.1.0", "add gravatar base url", updateCount, true), NewMigration("v1.1.1", "update the length of revision content", updateTheLengthOfRevisionContent, false), NewMigration("v1.1.2", "add notification config", addNoticeConfig, true), + NewMigration("v1.1.3", "set default user notification config", setDefaultUserNotificationConfig, false), } func GetMigrations() []Migration { diff --git a/internal/migrations/v16.go b/internal/migrations/v16.go new file mode 100644 index 00000000..75965ef4 --- /dev/null +++ b/internal/migrations/v16.go @@ -0,0 +1,38 @@ +package migrations + +import ( + "context" + "github.com/answerdev/answer/internal/base/constant" + "github.com/answerdev/answer/internal/entity" + "github.com/segmentfault/pacman/log" + "xorm.io/xorm" +) + +func setDefaultUserNotificationConfig(ctx context.Context, x *xorm.Engine) error { + userIDs := make([]string, 0) + err := x.Context(ctx).Table("user").Select("id").Find(&userIDs) + if err != nil { + return err + } + + for _, id := range userIDs { + bean := entity.UserNotificationConfig{UserID: id, Source: string(constant.InboxSource)} + exist, err := x.Context(ctx).Get(&bean) + if err != nil { + log.Error(err) + } + if exist { + continue + } + _, err = x.Context(ctx).Insert(&entity.UserNotificationConfig{ + UserID: id, + Source: string(constant.InboxSource), + Channels: `[{"key":"email","enable":true}]`, + Enabled: true, + }) + if err != nil { + log.Error(err) + } + } + return nil +} diff --git a/internal/repo/activity/answer_repo.go b/internal/repo/activity/answer_repo.go index 02ae66f0..23fb6432 100644 --- a/internal/repo/activity/answer_repo.go +++ b/internal/repo/activity/answer_repo.go @@ -46,15 +46,6 @@ func NewAnswerActivityRepo( func (ar *AnswerActivityRepo) SaveAcceptAnswerActivity(ctx context.Context, op *schema.AcceptAnswerOperationInfo) ( err error) { - // pre check - noNeedToDo, err := ar.activityPreCheck(ctx, op) - if err != nil { - return err - } - if noNeedToDo { - return nil - } - // save activity _, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) { session = session.Context(ctx) @@ -131,21 +122,6 @@ func (ar *AnswerActivityRepo) SaveCancelAcceptAnswerActivity(ctx context.Context return nil } -func (ar *AnswerActivityRepo) activityPreCheck(ctx context.Context, op *schema.AcceptAnswerOperationInfo) ( - noNeedToDo bool, err error) { - activities, err := ar.getExistActivity(ctx, op) - if err != nil { - return false, err - } - done := 0 - for _, act := range activities { - if act.Cancelled == entity.ActivityAvailable { - done++ - } - } - return done == len(op.Activities), nil -} - func (ar *AnswerActivityRepo) acquireUserInfo(session *xorm.Session, userIDs []string) (map[string]*entity.User, error) { us := make([]*entity.User, 0) err := session.In("id", userIDs).ForUpdate().Find(&us) @@ -286,18 +262,17 @@ func (ar *AnswerActivityRepo) rollbackUserRank(ctx context.Context, session *xor func (ar *AnswerActivityRepo) getExistActivity(ctx context.Context, op *schema.AcceptAnswerOperationInfo) ([]*entity.Activity, error) { var activities []*entity.Activity for _, action := range op.Activities { - t := &entity.Activity{} - exist, err := ar.data.DB.Context(ctx). + var t []*entity.Activity + err := ar.data.DB.Context(ctx). Where(builder.Eq{"user_id": action.ActivityUserID}). - And(builder.Eq{"trigger_user_id": action.TriggerUserID}). And(builder.Eq{"activity_type": action.ActivityType}). And(builder.Eq{"object_id": op.AnswerObjectID}). - Get(t) + Find(&t) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } - if exist { - activities = append(activities, t) + if len(t) > 0 { + activities = append(activities, t...) } } return activities, nil diff --git a/internal/repo/answer/answer_repo.go b/internal/repo/answer/answer_repo.go index 377a5c28..a2fe119e 100644 --- a/internal/repo/answer/answer_repo.go +++ b/internal/repo/answer/answer_repo.go @@ -166,30 +166,29 @@ func (ar *answerRepo) GetAnswerPage(ctx context.Context, page, pageSize int, ans return } -// UpdateAccepted -// If no answer is selected, the answer id can be 0 -func (ar *answerRepo) UpdateAccepted(ctx context.Context, id string, questionID string) error { - if questionID == "" { - return nil - } - id = uid.DeShortID(id) +// UpdateAcceptedStatus update all accepted status of this question's answers +func (ar *answerRepo) UpdateAcceptedStatus(ctx context.Context, acceptedAnswerID string, questionID string) error { + acceptedAnswerID = uid.DeShortID(acceptedAnswerID) questionID = uid.DeShortID(questionID) - var data entity.Answer - data.ID = id - data.Accepted = schema.AnswerAcceptedFailed - _, err := ar.data.DB.Context(ctx).Where("question_id =?", questionID).Cols("adopted").Update(&data) + // update all this question's answer accepted status to false + _, err := ar.data.DB.Context(ctx).Where("question_id = ?", questionID).Cols("adopted").Update(&entity.Answer{ + Accepted: schema.AnswerAcceptedFailed, + }) if err != nil { - return err + return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } - if id != "0" { - data.Accepted = schema.AnswerAcceptedEnable - _, err = ar.data.DB.Context(ctx).Where("id = ?", id).Cols("adopted").Update(&data) + + // if acceptedAnswerID is not empty, update accepted status to true + if len(acceptedAnswerID) > 0 && acceptedAnswerID != "0" { + _, err = ar.data.DB.Context(ctx).Where("id = ?", acceptedAnswerID).Cols("adopted").Update(&entity.Answer{ + Accepted: schema.AnswerAcceptedEnable, + }) if err != nil { return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } } - _ = ar.updateSearch(ctx, id) + _ = ar.updateSearch(ctx, acceptedAnswerID) return nil } @@ -259,7 +258,7 @@ func (ar *answerRepo) SearchList(ctx context.Context, search *entity.AnswerSearc search.PageSize = constant.DefaultPageSize } offset := search.Page * search.PageSize - session := ar.data.DB.Context(ctx).Where("") + session := ar.data.DB.Context(ctx) if search.QuestionID != "" { session = session.And("question_id = ?", search.QuestionID) diff --git a/internal/repo/collection/collection_repo.go b/internal/repo/collection/collection_repo.go index f21a323a..e0726886 100644 --- a/internal/repo/collection/collection_repo.go +++ b/internal/repo/collection/collection_repo.go @@ -2,9 +2,9 @@ package collection import ( "context" - "github.com/answerdev/answer/internal/base/constant" "github.com/answerdev/answer/internal/base/data" + "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/pager" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" @@ -142,25 +142,32 @@ func (cr *collectionRepo) GetCollectionPage(ctx context.Context, page, pageSize session = session.OrderBy("update_time desc") total, err = pager.Help(page, pageSize, collectionList, collection, session) - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } return } // SearchObjectCollected check object is collected or not func (cr *collectionRepo) SearchObjectCollected(ctx context.Context, userID string, objectIds []string) (map[string]bool, error) { - collectedMap := make(map[string]bool) - for k, object_id := range objectIds { - objectIds[k] = uid.DeShortID(object_id) + for i := 0; i < len(objectIds); i++ { + objectIds[i] = uid.DeShortID(objectIds[i]) } + list, err := cr.SearchByObjectIDsAndUser(ctx, userID, objectIds) if err != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - return collectedMap, err + return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + + collectedMap := make(map[string]bool) + short := handler.GetEnableShortID(ctx) for _, item := range list { + if short { + item.ObjectID = uid.EnShortID(item.ObjectID) + } collectedMap[item.ObjectID] = true } - return collectedMap, err + return collectedMap, nil } // SearchList diff --git a/internal/repo/comment/comment_repo.go b/internal/repo/comment/comment_repo.go index d3b3d8ac..f1863fb3 100644 --- a/internal/repo/comment/comment_repo.go +++ b/internal/repo/comment/comment_repo.go @@ -58,9 +58,13 @@ func (cr *commentRepo) RemoveComment(ctx context.Context, commentID string) (err return } -// UpdateComment update comment -func (cr *commentRepo) UpdateComment(ctx context.Context, comment *entity.Comment) (err error) { - _, err = cr.data.DB.Context(ctx).ID(comment.ID).Where("user_id = ?", comment.UserID).Update(comment) +// UpdateCommentContent update comment +func (cr *commentRepo) UpdateCommentContent( + ctx context.Context, commentID string, originalText string, parsedText string) (err error) { + _, err = cr.data.DB.Context(ctx).ID(commentID).Update(&entity.Comment{ + OriginalText: originalText, + ParsedText: parsedText, + }) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } @@ -69,8 +73,7 @@ func (cr *commentRepo) UpdateComment(ctx context.Context, comment *entity.Commen // GetComment get comment one func (cr *commentRepo) GetComment(ctx context.Context, commentID string) ( - comment *entity.Comment, exist bool, err error, -) { + comment *entity.Comment, exist bool, err error) { comment = &entity.Comment{} exist, err = cr.data.DB.Context(ctx).ID(commentID).Get(comment) if err != nil { diff --git a/internal/repo/config/config_repo.go b/internal/repo/config/config_repo.go index 07f4ce7f..44128afc 100644 --- a/internal/repo/config/config_repo.go +++ b/internal/repo/config/config_repo.go @@ -28,7 +28,8 @@ func NewConfigRepo(data *data.Data) config.ConfigRepo { func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Config, err error) { cacheKey := fmt.Sprintf("%s%d", constant.ConfigID2KEYCacheKeyPrefix, id) - if cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && exist { + cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey) + if err == nil && exist && len(cacheData) > 0 { c = &entity.Config{} c.BuildByJSON([]byte(cacheData)) if c.ID > 0 { @@ -37,7 +38,7 @@ func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Confi } c = &entity.Config{} - exist, err := cr.data.DB.Context(ctx).ID(id).Get(c) + exist, err = cr.data.DB.Context(ctx).ID(id).Get(c) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } @@ -46,7 +47,7 @@ func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Confi } // update cache - if err := cr.data.Cache.SetString(ctx, cacheKey, c.JsonString(), -1); err != nil { + if err := cr.data.Cache.SetString(ctx, cacheKey, c.JsonString(), constant.ConfigCacheTime); err != nil { log.Error(err) } return c, nil @@ -54,7 +55,8 @@ func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Confi func (cr configRepo) GetConfigByKey(ctx context.Context, key string) (c *entity.Config, err error) { cacheKey := constant.ConfigKEY2ContentCacheKeyPrefix + key - if cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && exist { + cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey) + if err == nil && exist && len(cacheData) > 0 { c = &entity.Config{} c.BuildByJSON([]byte(cacheData)) if c.ID > 0 { @@ -63,7 +65,7 @@ func (cr configRepo) GetConfigByKey(ctx context.Context, key string) (c *entity. } c = &entity.Config{Key: key} - exist, err := cr.data.DB.Context(ctx).Get(c) + exist, err = cr.data.DB.Context(ctx).Get(c) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } @@ -72,7 +74,7 @@ func (cr configRepo) GetConfigByKey(ctx context.Context, key string) (c *entity. } // update cache - if err := cr.data.Cache.SetString(ctx, cacheKey, c.JsonString(), -1); err != nil { + if err := cr.data.Cache.SetString(ctx, cacheKey, c.JsonString(), constant.ConfigCacheTime); err != nil { log.Error(err) } return c, nil @@ -99,11 +101,11 @@ func (cr configRepo) UpdateConfig(ctx context.Context, key string, value string) cacheVal := oldConfig.JsonString() // update cache if err := cr.data.Cache.SetString(ctx, - constant.ConfigKEY2ContentCacheKeyPrefix+key, cacheVal, -1); err != nil { + constant.ConfigKEY2ContentCacheKeyPrefix+key, cacheVal, constant.ConfigCacheTime); err != nil { log.Error(err) } if err := cr.data.Cache.SetString(ctx, - fmt.Sprintf("%s%d", constant.ConfigID2KEYCacheKeyPrefix, oldConfig.ID), cacheVal, -1); err != nil { + fmt.Sprintf("%s%d", constant.ConfigID2KEYCacheKeyPrefix, oldConfig.ID), cacheVal, constant.ConfigCacheTime); err != nil { log.Error(err) } return diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go index 299b8451..129af992 100644 --- a/internal/repo/question/question_repo.go +++ b/internal/repo/question/question_repo.go @@ -186,10 +186,15 @@ func (qr *questionRepo) GetQuestion(ctx context.Context, id string) ( return } -// GetTagBySlugName get tag by slug name -func (qr *questionRepo) SearchByTitleLike(ctx context.Context, title string) (questionList []*entity.Question, err error) { +// GetQuestionsByTitle get question list by title +func (qr *questionRepo) GetQuestionsByTitle(ctx context.Context, title string, pageSize int) ( + questionList []*entity.Question, err error) { questionList = make([]*entity.Question, 0) - err = qr.data.DB.Context(ctx).Table("question").Where("title like ?", "%"+title+"%").Limit(10, 0).Find(&questionList) + session := qr.data.DB.Context(ctx) + session.Where("status != ?", entity.QuestionStatusDeleted) + session.Where("title like ?", "%"+title+"%") + session.Limit(pageSize) + err = session.Find(&questionList) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } diff --git a/internal/repo/repo_test/comment_repo_test.go b/internal/repo/repo_test/comment_repo_test.go index b482cbf1..8885de7c 100644 --- a/internal/repo/repo_test/comment_repo_test.go +++ b/internal/repo/repo_test/comment_repo_test.go @@ -65,7 +65,7 @@ func Test_commentRepo_UpdateComment(t *testing.T) { assert.NoError(t, err) testCommentEntity.ParsedText = "test" - err = commentRepo.UpdateComment(context.TODO(), testCommentEntity) + err = commentRepo.UpdateCommentContent(context.TODO(), testCommentEntity, "", "") assert.NoError(t, err) newComment, exist, err := commonCommentRepo.GetComment(context.TODO(), testCommentEntity.ID) diff --git a/internal/repo/search_common/search_repo.go b/internal/repo/search_common/search_repo.go index fcc0d74c..78985df1 100644 --- a/internal/repo/search_common/search_repo.go +++ b/internal/repo/search_common/search_repo.go @@ -3,6 +3,7 @@ package search_common import ( "context" "fmt" + tagcommon "github.com/answerdev/answer/internal/service/tag_common" "github.com/answerdev/answer/plugin" "strconv" "strings" @@ -19,7 +20,6 @@ import ( usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/answerdev/answer/pkg/converter" "github.com/answerdev/answer/pkg/obj" - "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "xorm.io/builder" ) @@ -58,19 +58,26 @@ type searchRepo struct { data *data.Data userCommon *usercommon.UserCommon uniqueIDRepo unique.UniqueIDRepo + tagCommon *tagcommon.TagCommonService } // NewSearchRepo new repository -func NewSearchRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo, userCommon *usercommon.UserCommon) search_common.SearchRepo { +func NewSearchRepo( + data *data.Data, + uniqueIDRepo unique.UniqueIDRepo, + userCommon *usercommon.UserCommon, + tagCommon *tagcommon.TagCommonService, +) search_common.SearchRepo { return &searchRepo{ data: data, uniqueIDRepo: uniqueIDRepo, userCommon: userCommon, + tagCommon: tagCommon, } } // SearchContents search question and answer data -func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { +func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) { words = filterWords(words) var ( @@ -210,7 +217,7 @@ func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs } // SearchQuestions search question data -func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { +func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) { words = filterWords(words) var ( qfs = qFields @@ -319,7 +326,7 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagID } // SearchAnswers search answer data -func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { +func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) { words = filterWords(words) var ( @@ -428,7 +435,7 @@ func (sr *searchRepo) parseOrder(ctx context.Context, order string) (res string) } // ParseSearchPluginResult parse search plugin result -func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []schema.SearchResult, err error) { +func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []*schema.SearchResult, err error) { var ( qres []map[string][]byte res = make([]map[string][]byte, 0) @@ -455,82 +462,79 @@ func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin } // parseResult parse search result, return the data structure -func (sr *searchRepo) parseResult(ctx context.Context, res []map[string][]byte) (resp []schema.SearchResult, err error) { +func (sr *searchRepo) parseResult(ctx context.Context, res []map[string][]byte) (resp []*schema.SearchResult, err error) { + questionIDs := make([]string, 0) + userIDs := make([]string, 0) + resultList := make([]*schema.SearchResult, 0) for _, r := range res { - var ( - objectKey, - status string + questionIDs = append(questionIDs, string(r["question_id"])) + userIDs = append(userIDs, string(r["user_id"])) + tp, _ := time.ParseInLocation("2006-01-02 15:04:05", string(r["created_at"]), time.Local) + object := &schema.SearchObject{ + ID: string(r["id"]), + QuestionID: string(r["question_id"]), + Title: string(r["title"]), + Excerpt: htmltext.FetchExcerpt(string(r["parsed_text"]), "...", 240), + CreatedAtParsed: tp.Unix(), + UserInfo: &schema.SearchObjectUser{ + ID: string(r["user_id"]), + }, + Tags: make([]*schema.TagResp, 0), + VoteCount: converter.StringToInt(string(r["vote_count"])), + Accepted: string(r["accepted"]) == "2", + AnswerCount: converter.StringToInt(string(r["answer_count"])), + } - tags []schema.TagResp - tagsEntity []entity.Tag - object schema.SearchObject - ) - objectKey, err = obj.GetObjectTypeStrByObjectID(string(r["id"])) + objectKey, err := obj.GetObjectTypeStrByObjectID(string(r["id"])) if err != nil { continue } - tp, _ := time.ParseInLocation("2006-01-02 15:04:05", string(r["created_at"]), time.Local) - - // get user info - userInfo, _, e := sr.userCommon.GetUserBasicInfoByID(ctx, string(r["user_id"])) - if e != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(e).WithStack() - return - } - - // get tags - err = sr.data.DB.Context(ctx). - Select("`display_name`,`slug_name`,`main_tag_slug_name`,`recommend`,`reserved`"). - Table("tag"). - Join("INNER", "tag_rel", "tag.id = tag_rel.tag_id"). - Where(builder.Eq{"tag_rel.object_id": r["question_id"]}). - And(builder.Eq{"tag_rel.status": entity.TagRelStatusAvailable}). - UseBool("recommend", "reserved"). - OrderBy("tag.recommend DESC, tag.reserved DESC, tag.id DESC"). - Find(&tagsEntity) - - if err != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - return - } - _ = copier.Copy(&tags, tagsEntity) switch objectKey { case "question": for k, v := range entity.AdminQuestionSearchStatus { if v == converter.StringToInt(string(r["status"])) { - status = k + object.StatusStr = k break } } case "answer": for k, v := range entity.AdminAnswerSearchStatus { if v == converter.StringToInt(string(r["status"])) { - status = k + object.StatusStr = k break } } } - object = schema.SearchObject{ - ID: string(r["id"]), - QuestionID: string(r["question_id"]), - Title: string(r["title"]), - Excerpt: htmltext.FetchExcerpt(string(r["parsed_text"]), "...", 240), - CreatedAtParsed: tp.Unix(), - UserInfo: userInfo, - Tags: tags, - VoteCount: converter.StringToInt(string(r["vote_count"])), - Accepted: string(r["accepted"]) == "2", - AnswerCount: converter.StringToInt(string(r["answer_count"])), - StatusStr: status, - } - resp = append(resp, schema.SearchResult{ + resultList = append(resultList, &schema.SearchResult{ ObjectType: objectKey, Object: object, }) } - return + + tagsMap, err := sr.tagCommon.BatchGetObjectTag(ctx, questionIDs) + if err != nil { + return nil, err + } + userInfoMap, err := sr.userCommon.BatchUserBasicInfoByID(ctx, userIDs) + if err != nil { + return nil, err + } + + for _, item := range resultList { + tags, ok := tagsMap[item.Object.QuestionID] + if ok { + item.Object.Tags = tags + } + if userInfo := userInfoMap[item.Object.UserInfo.ID]; userInfo != nil { + item.Object.UserInfo.Username = userInfo.Username + item.Object.UserInfo.DisplayName = userInfo.DisplayName + item.Object.UserInfo.Rank = userInfo.Rank + item.Object.UserInfo.Status = userInfo.Status + } + } + return resultList, nil } func addRelevanceField(searchFields, words, fields []string) (res []string, args []interface{}) { diff --git a/internal/repo/tag_common/tag_common_repo.go b/internal/repo/tag_common/tag_common_repo.go index a7380028..9218ba47 100644 --- a/internal/repo/tag_common/tag_common_repo.go +++ b/internal/repo/tag_common/tag_common_repo.go @@ -3,6 +3,7 @@ package tag_common import ( "context" "fmt" + "strings" "github.com/answerdev/answer/internal/base/data" "github.com/answerdev/answer/internal/base/pager" @@ -56,19 +57,28 @@ func (tr *tagCommonRepo) GetTagBySlugName(ctx context.Context, slugName string) } // GetTagListByName get tag list all like name -func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error) { - tagList = make([]*entity.Tag, 0) +func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, recommend, reserved bool) (tagList []*entity.Tag, err error) { cond := &entity.Tag{} - session := tr.data.DB.Context(ctx).Where("") - if name != "" { - session.Where("slug_name LIKE LOWER(?) or display_name LIKE ?", name+"%", name+"%") - } else { - session.UseBool("recommend") + session := tr.data.DB.Context(ctx) + if len(name) > 0 { + session.Where("slug_name LIKE ? OR display_name LIKE ?", strings.ToLower(name)+"%", name+"%") + } + var columns []string + if recommend { + columns = append(columns, "recommend") cond.Recommend = true } + if reserved { + columns = append(columns, "reserved") + cond.Reserved = true + } + if len(columns) > 0 { + session.UseBool(columns...) + } session.Where(builder.Eq{"status": entity.TagStatusAvailable}) - session.Asc("slug_name") - err = session.OrderBy("recommend desc,reserved desc,id desc").Find(&tagList, cond) + + tagList = make([]*entity.Tag, 0) + err = session.OrderBy("recommend DESC,reserved DESC,slug_name ASC").Find(&tagList, cond) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } diff --git a/internal/repo/user/user_backyard_repo.go b/internal/repo/user/user_backyard_repo.go index 1b06f2dc..bb2b1034 100644 --- a/internal/repo/user/user_backyard_repo.go +++ b/internal/repo/user/user_backyard_repo.go @@ -131,21 +131,21 @@ func (ur *userAdminRepo) GetUserPage(ctx context.Context, page, pageSize int, us session := ur.data.DB.Context(ctx) switch user.Status { case entity.UserStatusDeleted: - session.Desc("user.deleted_at") + session.Desc("`user`.deleted_at") case entity.UserStatusSuspended: - session.Desc("user.suspended_at") + session.Desc("`user`.suspended_at") default: - session.Desc("user.created_at") + session.Desc("`user`.created_at") } if len(usernameOrDisplayName) > 0 { session.And(builder.Or( - builder.Like{"user.username", usernameOrDisplayName}, - builder.Like{"user.display_name", usernameOrDisplayName}, + builder.Like{"`user`.username", usernameOrDisplayName}, + builder.Like{"`user`.display_name", usernameOrDisplayName}, )) } if isStaff { - session.Join("INNER", "user_role_rel", "user.id = user_role_rel.user_id AND user_role_rel.role_id > 1") + session.Join("INNER", "user_role_rel", "`user`.id = `user_role_rel`.user_id AND `user_role_rel`.role_id > 1") } total, err = pager.Help(page, pageSize, &users, user, session) diff --git a/internal/repo/user_notification_config/user_notification_config_repo.go b/internal/repo/user_notification_config/user_notification_config_repo.go index 15dba36f..5a1f8348 100644 --- a/internal/repo/user_notification_config/user_notification_config_repo.go +++ b/internal/repo/user_notification_config/user_notification_config_repo.go @@ -22,6 +22,24 @@ func NewUserNotificationConfigRepo(data *data.Data) user_notification_config.Use } } +// Add add notification config +func (ur *userNotificationConfigRepo) Add(ctx context.Context, userIDs []string, source, channels string) (err error) { + var configs []*entity.UserNotificationConfig + for _, userID := range userIDs { + configs = append(configs, &entity.UserNotificationConfig{ + UserID: userID, + Source: source, + Channels: channels, + Enabled: true, + }) + } + _, err = ur.data.DB.Context(ctx).Insert(configs) + if err != nil { + return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + return nil +} + // Save save notification config, if existed, update, if not exist, insert func (ur *userNotificationConfigRepo) Save(ctx context.Context, uc *entity.UserNotificationConfig) (err error) { old := &entity.UserNotificationConfig{UserID: uc.UserID, Source: uc.Source} diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 8124648e..4f196042 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -203,7 +203,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { r.PUT("/question/status", a.questionController.CloseQuestion) r.PUT("/question/operation", a.questionController.OperationQuestion) r.PUT("/question/reopen", a.questionController.ReopenQuestion) - r.GET("/question/similar", a.questionController.SearchByTitleLike) + r.GET("/question/similar", a.questionController.GetSimilarQuestions) // answer r.POST("/answer", a.answerController.Add) diff --git a/internal/schema/answer_schema.go b/internal/schema/answer_schema.go index 4777e770..2c09b043 100644 --- a/internal/schema/answer_schema.go +++ b/internal/schema/answer_schema.go @@ -108,12 +108,19 @@ type AdminAnswerInfo struct { } `json:"question_info"` } -type AnswerAcceptedReq struct { - QuestionID string `json:"question_id"` - AnswerID string `json:"answer_id"` +type AcceptAnswerReq struct { + QuestionID string `validate:"required,gt=0,lte=30" json:"question_id"` + AnswerID string `validate:"omitempty" json:"answer_id"` UserID string `json:"-"` } +func (req *AcceptAnswerReq) Check() (errFields []*validator.FormErrorField, err error) { + if len(req.AnswerID) == 0 { + req.AnswerID = "0" + } + return nil, nil +} + type AdminSetAnswerStatusRequest struct { StatusStr string `json:"status"` AnswerID string `json:"answer_id"` diff --git a/internal/schema/comment_schema.go b/internal/schema/comment_schema.go index caba2cea..8865795d 100644 --- a/internal/schema/comment_schema.go +++ b/internal/schema/comment_schema.go @@ -58,11 +58,10 @@ type UpdateCommentReq struct { UserID string `json:"-"` IsAdmin bool `json:"-"` - CanAdd bool `json:"-"` // whether user can edit it CanEdit bool `json:"-"` + // whether user can delete it - CanDelete bool `json:"-"` CaptchaID string `json:"captcha_id"` // captcha_id CaptchaCode string `json:"captcha_code"` } @@ -72,6 +71,15 @@ func (req *UpdateCommentReq) Check() (errFields []*validator.FormErrorField, err return nil, nil } +type UpdateCommentResp struct { + // comment id + CommentID string `json:"comment_id"` + // original comment content + OriginalText string `json:"original_text"` + // parsed comment content + ParsedText string `json:"parsed_text"` +} + // GetCommentListReq get comment list all request type GetCommentListReq struct { // user id diff --git a/internal/schema/search_schema.go b/internal/schema/search_schema.go index 58585176..aaf0c8ae 100644 --- a/internal/schema/search_schema.go +++ b/internal/schema/search_schema.go @@ -90,13 +90,21 @@ type SearchObject struct { Accepted bool `json:"accepted"` AnswerCount int `json:"answer_count"` // user info - UserInfo *UserBasicInfo `json:"user_info"` + UserInfo *SearchObjectUser `json:"user_info"` // tags - Tags []TagResp `json:"tags"` + Tags []*TagResp `json:"tags"` // Status StatusStr string `json:"status"` } +type SearchObjectUser struct { + ID string `json:"id"` + Username string `json:"username"` + DisplayName string `json:"display_name"` + Rank int `json:"rank"` + Status string `json:"status"` +} + type TagResp struct { ID string `json:"-"` SlugName string `json:"slug_name"` @@ -111,13 +119,13 @@ type SearchResult struct { // object_type ObjectType string `json:"object_type"` // this object - Object SearchObject `json:"object"` + Object *SearchObject `json:"object"` } type SearchResp struct { Total int64 `json:"count"` // search response - SearchResults []SearchResult `json:"list"` + SearchResults []*SearchResult `json:"list"` } type SearchDescResp struct { diff --git a/internal/service/activity/answer_activity_service.go b/internal/service/activity/answer_activity_service.go index daab2bf7..9785061a 100644 --- a/internal/service/activity/answer_activity_service.go +++ b/internal/service/activity/answer_activity_service.go @@ -34,6 +34,9 @@ func NewAnswerActivityService( // AcceptAnswer accept answer change activity func (as *AnswerActivityService) AcceptAnswer(ctx context.Context, loginUserID, answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error) { + log.Debugf("user %s want to accept answer %s[%s] for question %s[%s]", loginUserID, + answerObjID, answerUserID, + questionObjID, questionUserID) operationInfo := as.createAcceptAnswerOperationInfo(ctx, loginUserID, answerObjID, questionObjID, questionUserID, answerUserID, isSelf) return as.answerActivityRepo.SaveAcceptAnswerActivity(ctx, operationInfo) diff --git a/internal/service/answer_common/answer.go b/internal/service/answer_common/answer.go index 2373a6d0..4da92c07 100644 --- a/internal/service/answer_common/answer.go +++ b/internal/service/answer_common/answer.go @@ -17,7 +17,7 @@ type AnswerRepo interface { GetAnswer(ctx context.Context, id string) (answer *entity.Answer, exist bool, err error) GetAnswerList(ctx context.Context, answer *entity.Answer) (answerList []*entity.Answer, err error) GetAnswerPage(ctx context.Context, page, pageSize int, answer *entity.Answer) (answerList []*entity.Answer, total int64, err error) - UpdateAccepted(ctx context.Context, id string, questionID string) error + UpdateAcceptedStatus(ctx context.Context, acceptedAnswerID string, questionID string) error GetByID(ctx context.Context, id string) (*entity.Answer, bool, error) GetCountByQuestionID(ctx context.Context, questionID string) (int64, error) GetCountByUserID(ctx context.Context, userID string) (int64, error) @@ -83,6 +83,7 @@ func (as *AnswerCommon) ShowFormat(ctx context.Context, data *entity.Answer) *sc info.UserID = data.UserID info.UpdateUserID = data.LastEditUserID info.Status = data.Status + info.MemberActions = make([]*schema.PermissionMemberAction, 0) return &info } diff --git a/internal/service/answer_service.go b/internal/service/answer_service.go index 4c0e57b6..f1e76bf1 100644 --- a/internal/service/answer_service.go +++ b/internal/service/answer_service.go @@ -230,15 +230,13 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) ( } func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq) (string, error) { - //req.NoNeedReview //true 不需要审核 var canUpdate bool _, existUnreviewed, err := as.revisionService.ExistUnreviewedByObjectID(ctx, req.ID) if err != nil { return "", err } if existUnreviewed { - err = errors.BadRequest(reason.AnswerCannotUpdate) - return "", err + return "", errors.BadRequest(reason.AnswerCannotUpdate) } questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID) @@ -254,12 +252,11 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq return "", err } if !exist { - return "", nil + return "", errors.BadRequest(reason.AnswerNotFound) } if answerInfo.Status == entity.AnswerStatusDeleted { - err = errors.BadRequest(reason.AnswerCannotUpdate) - return "", err + return "", errors.BadRequest(reason.AnswerCannotUpdate) } //If the content is the same, ignore it @@ -267,15 +264,13 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq return "", nil } - now := time.Now() - insertData := new(entity.Answer) + insertData := &entity.Answer{} insertData.ID = req.ID insertData.UserID = answerInfo.UserID insertData.QuestionID = req.QuestionID insertData.OriginalText = req.Content insertData.ParsedText = req.HTML - insertData.UpdatedAt = now - + insertData.UpdatedAt = time.Now() insertData.LastEditUserID = "0" if answerInfo.UserID != req.UserID { insertData.LastEditUserID = req.UserID @@ -284,7 +279,6 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq revisionDTO := &schema.AddRevisionDTO{ UserID: req.UserID, ObjectID: req.ID, - Title: "", Log: req.EditSummary, } @@ -314,7 +308,7 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq } if canUpdate { as.activityQueueService.Send(ctx, &schema.ActivityMsg{ - UserID: insertData.UserID, + UserID: req.UserID, ObjectID: insertData.ID, OriginalObjectID: insertData.ID, ActivityTypeKey: constant.ActAnswerEdited, @@ -325,47 +319,47 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq return insertData.ID, nil } -// UpdateAccepted -func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerAcceptedReq) error { - if req.AnswerID == "" { - req.AnswerID = "0" - } - if req.UserID == "" { - return nil - } - - newAnswerInfo := &entity.Answer{} - newAnswerInfoexist := false - var err error - - if req.AnswerID != "0" { - newAnswerInfo, newAnswerInfoexist, err = as.answerRepo.GetByID(ctx, req.AnswerID) - if err != nil { - return err - } - newAnswerInfo.ID = uid.DeShortID(newAnswerInfo.ID) - if !newAnswerInfoexist { - return errors.BadRequest(reason.AnswerNotFound) - } - } - +// AcceptAnswer accept answer +func (as *AnswerService) AcceptAnswer(ctx context.Context, req *schema.AcceptAnswerReq) (err error) { + // find question questionInfo, exist, err := as.questionRepo.GetQuestion(ctx, req.QuestionID) if err != nil { return err } - questionInfo.ID = uid.DeShortID(questionInfo.ID) if !exist { return errors.BadRequest(reason.QuestionNotFound) } - // if questionInfo.UserID != req.UserID { - // return fmt.Errorf("no permission to set answer") - // } + questionInfo.ID = uid.DeShortID(questionInfo.ID) if questionInfo.AcceptedAnswerID == req.AnswerID { return nil } + // find answer + var acceptedAnswerInfo *entity.Answer + if len(req.AnswerID) > 1 { + acceptedAnswerInfo, exist, err = as.answerRepo.GetByID(ctx, req.AnswerID) + if err != nil { + return err + } + if !exist { + return errors.BadRequest(reason.AnswerNotFound) + } + acceptedAnswerInfo.ID = uid.DeShortID(acceptedAnswerInfo.ID) + } + + // update answers status + if err = as.answerRepo.UpdateAcceptedStatus(ctx, req.AnswerID, req.QuestionID); err != nil { + return err + } + + // update question status + err = as.questionCommon.UpdateAccepted(ctx, req.QuestionID, req.AnswerID) + if err != nil { + log.Error("UpdateLastAnswer error", err.Error()) + } + var oldAnswerInfo *entity.Answer - if len(questionInfo.AcceptedAnswerID) > 0 && questionInfo.AcceptedAnswerID != "0" { + if len(questionInfo.AcceptedAnswerID) > 1 { oldAnswerInfo, _, err = as.answerRepo.GetByID(ctx, questionInfo.AcceptedAnswerID) if err != nil { return err @@ -373,17 +367,7 @@ func (as *AnswerService) UpdateAccepted(ctx context.Context, req *schema.AnswerA oldAnswerInfo.ID = uid.DeShortID(oldAnswerInfo.ID) } - err = as.answerRepo.UpdateAccepted(ctx, req.AnswerID, req.QuestionID) - if err != nil { - return err - } - - err = as.questionCommon.UpdateAccepted(ctx, req.QuestionID, req.AnswerID) - if err != nil { - log.Error("UpdateLastAnswer error", err.Error()) - } - - as.updateAnswerRank(ctx, req.UserID, questionInfo, newAnswerInfo, oldAnswerInfo) + as.updateAnswerRank(ctx, req.UserID, questionInfo, acceptedAnswerInfo, oldAnswerInfo) return nil } @@ -398,9 +382,9 @@ func (as *AnswerService) updateAnswerRank(ctx context.Context, userID string, log.Error(err) } } - if newAnswerInfo.ID != "" { + if newAnswerInfo != nil { err := as.answerActivityService.AcceptAnswer(ctx, userID, newAnswerInfo.ID, - questionInfo.ID, questionInfo.UserID, newAnswerInfo.UserID, newAnswerInfo.UserID == userID) + questionInfo.ID, questionInfo.UserID, newAnswerInfo.UserID, newAnswerInfo.UserID == questionInfo.UserID) if err != nil { log.Error(err) } @@ -443,12 +427,11 @@ func (as *AnswerService) Get(ctx context.Context, answerID, loginUserID string) info.VoteStatus = as.voteRepo.GetVoteStatus(ctx, answerID, loginUserID) - CollectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{answerInfo.ID}) + collectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{answerInfo.ID}) if err != nil { - log.Error("CollectionFunc.SearchObjectCollected error", err) + return nil, nil, has, err } - _, ok = CollectedMap[answerInfo.ID] - if ok { + if len(collectedMap) > 0 { info.Collected = true } @@ -529,46 +512,28 @@ func (as *AnswerService) SearchFormatInfo(ctx context.Context, answers []*entity item := as.ShowFormat(ctx, info) list = append(list, item) objectIDs = append(objectIDs, info.ID) - userIDs = append(userIDs, info.UserID) - userIDs = append(userIDs, info.LastEditUserID) - if req.UserID != "" { - item.ID = uid.DeShortID(item.ID) - item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, req.UserID) - } + userIDs = append(userIDs, info.UserID, info.LastEditUserID) } + userInfoMap, err := as.userCommon.BatchUserBasicInfoByID(ctx, userIDs) if err != nil { return list, err } for _, item := range list { - _, ok := userInfoMap[item.UserID] - if ok { - item.UserInfo = userInfoMap[item.UserID] - } - _, ok = userInfoMap[item.UpdateUserID] - if ok { - item.UpdateUserInfo = userInfoMap[item.UpdateUserID] - } + item.UserInfo = userInfoMap[item.UserID] + item.UpdateUserInfo = userInfoMap[item.UpdateUserID] } - - if req.UserID == "" { + if len(req.UserID) == 0 { return list, nil } - searchObjectCollected, err := as.collectionCommon.SearchObjectCollected(ctx, req.UserID, objectIDs) + collectedMap, err := as.collectionCommon.SearchObjectCollected(ctx, req.UserID, objectIDs) if err != nil { return nil, err } - for _, item := range list { - _, ok := searchObjectCollected[item.ID] - if ok { - item.Collected = true - } - } - - for _, item := range list { - item.ID = uid.EnShortID(item.ID) + item.VoteStatus = as.voteRepo.GetVoteStatus(ctx, item.ID, req.UserID) + item.Collected = collectedMap[item.ID] item.MemberActions = permission.GetAnswerPermission(ctx, req.UserID, item.UserID, req.CanEdit, req.CanDelete) } return list, nil diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go index 295572f1..b59cd958 100644 --- a/internal/service/comment/comment_service.go +++ b/internal/service/comment/comment_service.go @@ -29,7 +29,7 @@ import ( type CommentRepo interface { AddComment(ctx context.Context, comment *entity.Comment) (err error) RemoveComment(ctx context.Context, commentID string) (err error) - UpdateComment(ctx context.Context, comment *entity.Comment) (err error) + UpdateCommentContent(ctx context.Context, commentID string, original string, parsedText string) (err error) GetComment(ctx context.Context, commentID string) (comment *entity.Comment, exist bool, err error) GetCommentPage(ctx context.Context, commentQuery *CommentQuery) ( comments []*entity.Comment, total int64, err error) @@ -224,39 +224,34 @@ func (cs *CommentService) RemoveComment(ctx context.Context, req *schema.RemoveC // UpdateComment update comment func (cs *CommentService) UpdateComment(ctx context.Context, req *schema.UpdateCommentReq) ( - resp *schema.GetCommentResp, err error) { - resp = &schema.GetCommentResp{} - + resp *schema.UpdateCommentResp, err error) { old, exist, err := cs.commentCommonRepo.GetComment(ctx, req.CommentID) - if err != nil { - return - } - if !exist { - return resp, errors.BadRequest(reason.CommentNotFound) - } - - // user can edit the comment that was posted by himself before deadline. - if !req.IsAdmin && (time.Now().After(old.CreatedAt.Add(constant.CommentEditDeadline))) { - return resp, errors.BadRequest(reason.CommentCannotEditAfterDeadline) - } - - comment := &entity.Comment{} - _ = copier.Copy(comment, req) - comment.ID = req.CommentID - resp.SetFromComment(comment) - resp.MemberActions = permission.GetCommentPermission(ctx, req.UserID, resp.UserID, - time.Now(), req.CanEdit, req.CanDelete) - userInfo, exist, err := cs.userCommon.GetUserBasicInfoByID(ctx, resp.UserID) if err != nil { return nil, err } - if exist { - resp.Username = userInfo.Username - resp.UserDisplayName = userInfo.DisplayName - resp.UserAvatar = userInfo.Avatar - resp.UserStatus = userInfo.Status + if !exist { + return nil, errors.BadRequest(reason.CommentNotFound) } - return resp, cs.commentRepo.UpdateComment(ctx, comment) + // user can't edit the comment that was posted by others except admin + if !req.IsAdmin && req.UserID != old.UserID { + return nil, errors.BadRequest(reason.CommentNotFound) + } + + // user can edit the comment that was posted by himself before deadline. + // admin can edit it at any time + if !req.IsAdmin && (time.Now().After(old.CreatedAt.Add(constant.CommentEditDeadline))) { + return nil, errors.BadRequest(reason.CommentCannotEditAfterDeadline) + } + + if err = cs.commentRepo.UpdateCommentContent(ctx, old.ID, req.OriginalText, req.ParsedText); err != nil { + return nil, err + } + resp = &schema.UpdateCommentResp{ + CommentID: old.ID, + OriginalText: req.OriginalText, + ParsedText: req.ParsedText, + } + return resp, nil } // GetComment get comment one diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go index 84a6c880..8aebb1e9 100644 --- a/internal/service/question_common/question.go +++ b/internal/service/question_common/question.go @@ -40,7 +40,7 @@ type QuestionRepo interface { UpdateQuestionStatus(ctx context.Context, question *entity.Question) (err error) UpdateQuestionStatusWithOutUpdateTime(ctx context.Context, question *entity.Question) (err error) UpdateQuestionOperation(ctx context.Context, question *entity.Question) (err error) - SearchByTitleLike(ctx context.Context, title string) (questionList []*entity.Question, err error) + GetQuestionsByTitle(ctx context.Context, title string, pageSize int) (questionList []*entity.Question, err error) UpdatePvCount(ctx context.Context, questionID string) (err error) UpdateAnswerCount(ctx context.Context, questionID string, num int) (err error) UpdateCollectionCount(ctx context.Context, questionID string, num int) (err error) @@ -282,17 +282,13 @@ func (qs *QuestionCommon) Info(ctx context.Context, questionID string, loginUser } showinfo.Answered = has - // login user Collected information - - CollectedMap, err := qs.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{dbinfo.ID}) + collectedMap, err := qs.collectionCommon.SearchObjectCollected(ctx, loginUserID, []string{dbinfo.ID}) if err != nil { - log.Error("CollectionFunc.SearchObjectCollected", err) + return nil, err } - _, ok = CollectedMap[dbinfo.ID] - if ok { + if len(collectedMap) > 0 { showinfo.Collected = true } - return showinfo, nil } @@ -406,9 +402,7 @@ func (qs *QuestionCommon) FormatQuestions(ctx context.Context, questionList []*e item := qs.ShowFormat(ctx, questionInfo) list = append(list, item) objectIds = append(objectIds, item.ID) - userIds = append(userIds, item.UserID) - userIds = append(userIds, item.LastEditUserID) - userIds = append(userIds, item.LastAnsweredUserID) + userIds = append(userIds, item.UserID, item.LastEditUserID, item.LastAnsweredUserID) } tagsMap, err := qs.tagCommon.BatchGetObjectTag(ctx, objectIds) if err != nil { @@ -421,38 +415,21 @@ func (qs *QuestionCommon) FormatQuestions(ctx context.Context, questionList []*e } for _, item := range list { - _, ok := tagsMap[item.ID] - if ok { - item.Tags = tagsMap[item.ID] - } - _, ok = userInfoMap[item.UserID] - if ok { - item.UserInfo = userInfoMap[item.UserID] - } - _, ok = userInfoMap[item.LastEditUserID] - if ok { - item.UpdateUserInfo = userInfoMap[item.LastEditUserID] - } - _, ok = userInfoMap[item.LastAnsweredUserID] - if ok { - item.LastAnsweredUserInfo = userInfoMap[item.LastAnsweredUserID] - } + item.Tags = tagsMap[item.ID] + item.UserInfo = userInfoMap[item.UserID] + item.UpdateUserInfo = userInfoMap[item.LastEditUserID] + item.LastAnsweredUserInfo = userInfoMap[item.LastAnsweredUserID] } - if loginUserID == "" { return list, nil } - // //login user Collected information - CollectedMap, err := qs.collectionCommon.SearchObjectCollected(ctx, loginUserID, objectIds) - if err != nil { - log.Error("CollectionFunc.SearchObjectCollected", err) - } + collectedMap, err := qs.collectionCommon.SearchObjectCollected(ctx, loginUserID, objectIds) + if err != nil { + return nil, err + } for _, item := range list { - _, ok := CollectedMap[item.ID] - if ok { - item.Collected = true - } + item.Collected = collectedMap[item.ID] } return list, nil } diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 48dbffcd..00bdf299 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -1103,14 +1103,18 @@ func (qs *QuestionService) SearchUserTopList(ctx context.Context, userName strin return userQuestionlist, userAnswerlist, nil } -// SearchByTitleLike -func (qs *QuestionService) SearchByTitleLike(ctx context.Context, title string, loginUserID string) ([]*schema.QuestionBaseInfo, error) { - list := make([]*schema.QuestionBaseInfo, 0) - dblist, err := qs.questionRepo.SearchByTitleLike(ctx, title) - if err != nil { - return list, err +// GetQuestionsByTitle get questions by title +func (qs *QuestionService) GetQuestionsByTitle(ctx context.Context, title string) ( + resp []*schema.QuestionBaseInfo, err error) { + resp = make([]*schema.QuestionBaseInfo, 0) + if len(title) == 0 { + return resp, nil } - for _, question := range dblist { + questions, err := qs.questionRepo.GetQuestionsByTitle(ctx, title, 10) + if err != nil { + return resp, err + } + for _, question := range questions { item := &schema.QuestionBaseInfo{} item.ID = question.ID item.Title = question.Title @@ -1125,10 +1129,9 @@ func (qs *QuestionService) SearchByTitleLike(ctx context.Context, title string, if question.AcceptedAnswerID != "0" { item.AcceptedAnswer = true } - list = append(list, item) + resp = append(resp, item) } - - return list, nil + return resp, nil } // SimilarQuestion diff --git a/internal/service/search_common/search.go b/internal/service/search_common/search.go index 8d35ee1b..6459d02f 100644 --- a/internal/service/search_common/search.go +++ b/internal/service/search_common/search.go @@ -7,8 +7,8 @@ import ( ) type SearchRepo interface { - SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes, page, size int, order string) (resp []schema.SearchResult, total int64, err error) - SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) - SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []schema.SearchResult, total int64, err error) - ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []schema.SearchResult, err error) + SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) + SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) + SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) + ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []*schema.SearchResult, err error) } diff --git a/internal/service/search_parser/search_parser.go b/internal/service/search_parser/search_parser.go index 1427fb44..1b9c93f9 100644 --- a/internal/service/search_parser/search_parser.go +++ b/internal/service/search_parser/search_parser.go @@ -2,6 +2,7 @@ package search_parser import ( "context" + "fmt" "github.com/answerdev/answer/internal/base/constant" "regexp" "strings" @@ -105,7 +106,11 @@ func (sp *SearchParser) parseTags(ctx context.Context, query *string) (tags []st if err != nil || !exists { continue } - tags = append(tags, tag.ID) + if tag.MainTagID > 0 { + tags = append(tags, fmt.Sprintf("%d", tag.MainTagID)) + } else { + tags = append(tags, tag.ID) + } } // limit maximum 5 tags diff --git a/internal/service/tag_common/tag_common.go b/internal/service/tag_common/tag_common.go index b57eb240..7979719a 100644 --- a/internal/service/tag_common/tag_common.go +++ b/internal/service/tag_common/tag_common.go @@ -24,7 +24,7 @@ type TagCommonRepo interface { AddTagList(ctx context.Context, tagList []*entity.Tag) (err error) GetTagListByIDs(ctx context.Context, ids []string) (tagList []*entity.Tag, err error) GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error) - GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error) + GetTagListByName(ctx context.Context, name string, recommend, reserved bool) (tagList []*entity.Tag, err error) GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error) GetTagByID(ctx context.Context, tagID string, includeDeleted bool) (tag *entity.Tag, exist bool, err error) GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (tagList []*entity.Tag, total int64, err error) @@ -86,7 +86,7 @@ func NewTagCommonService( // SearchTagLike get tag list all func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []schema.SearchTagLikeResp, err error) { - tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, req.IsAdmin) + tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, len(req.Tag) == 0, false) if err != nil { return } @@ -97,35 +97,39 @@ func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.Searc mainTagId = append(mainTagId, converter.IntToString(tag.MainTagID)) } } - mainTagList, err := ts.tagCommonRepo.GetTagListByIDs(ctx, mainTagId) - if err != nil { - return - } mainTagMap := make(map[string]*entity.Tag) - for _, tag := range mainTagList { - mainTagMap[tag.ID] = tag - } - for _, tag := range tags { - if tag.MainTagID != 0 { - _, ok := mainTagMap[converter.IntToString(tag.MainTagID)] - if ok { - tag.SlugName = mainTagMap[converter.IntToString(tag.MainTagID)].SlugName - tag.DisplayName = mainTagMap[converter.IntToString(tag.MainTagID)].DisplayName - tag.Reserved = mainTagMap[converter.IntToString(tag.MainTagID)].Reserved - tag.Recommend = mainTagMap[converter.IntToString(tag.MainTagID)].Recommend - } + if len(mainTagId) > 0 { + mainTagList, err := ts.tagCommonRepo.GetTagListByIDs(ctx, mainTagId) + if err != nil { + return nil, err + } + for _, tag := range mainTagList { + mainTagMap[tag.ID] = tag } } - RepetitiveTag := make(map[string]bool) for _, tag := range tags { - if _, ok := RepetitiveTag[tag.SlugName]; !ok { + if tag.MainTagID == 0 { + continue + } + mainTagID := converter.IntToString(tag.MainTagID) + if _, ok := mainTagMap[mainTagID]; ok { + tag.SlugName = mainTagMap[mainTagID].SlugName + tag.DisplayName = mainTagMap[mainTagID].DisplayName + tag.Reserved = mainTagMap[mainTagID].Reserved + tag.Recommend = mainTagMap[mainTagID].Recommend + } + } + resp = make([]schema.SearchTagLikeResp, 0) + repetitiveTag := make(map[string]bool) + for _, tag := range tags { + if _, ok := repetitiveTag[tag.SlugName]; !ok { item := schema.SearchTagLikeResp{} item.SlugName = tag.SlugName item.DisplayName = tag.DisplayName item.Recommend = tag.Recommend item.Reserved = tag.Reserved resp = append(resp, item) - RepetitiveTag[tag.SlugName] = true + repetitiveTag[tag.SlugName] = true } } return resp, nil @@ -432,6 +436,9 @@ func (ts *TagCommonService) tagFormatRecommendAndReserved(ctx context.Context, t // BatchGetObjectTag batch get object tag func (ts *TagCommonService) BatchGetObjectTag(ctx context.Context, objectIds []string) (map[string][]*schema.TagResp, error) { objectIDTagMap := make(map[string][]*schema.TagResp) + if len(objectIds) == 0 { + return objectIDTagMap, nil + } objectTagRelList, err := ts.tagRelRepo.BatchGetObjectTagRelList(ctx, objectIds) if err != nil { return objectIDTagMap, err diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index c1886041..34ab7a75 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -106,9 +106,12 @@ func (us *UserCommon) UpdateQuestionCount(ctx context.Context, userID string, nu return us.userRepo.UpdateQuestionCount(ctx, userID, num) } -func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, IDs []string) (map[string]*schema.UserBasicInfo, error) { +func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, userIDs []string) (map[string]*schema.UserBasicInfo, error) { userMap := make(map[string]*schema.UserBasicInfo) - userList, err := us.userRepo.BatchGetByID(ctx, IDs) + if len(userIDs) == 0 { + return userMap, nil + } + userList, err := us.userRepo.BatchGetByID(ctx, userIDs) if err != nil { return userMap, err } diff --git a/internal/service/user_notification_config/user_notification_config_service.go b/internal/service/user_notification_config/user_notification_config_service.go index f41e22cc..e681ea26 100644 --- a/internal/service/user_notification_config/user_notification_config_service.go +++ b/internal/service/user_notification_config/user_notification_config_service.go @@ -9,6 +9,7 @@ import ( ) type UserNotificationConfigRepo interface { + Add(ctx context.Context, userIDs []string, source, channels string) (err error) Save(ctx context.Context, uc *entity.UserNotificationConfig) (err error) GetByUserID(ctx context.Context, userID string) ([]*entity.UserNotificationConfig, error) GetBySource(ctx context.Context, source constant.NotificationSource) ([]*entity.UserNotificationConfig, error) @@ -68,6 +69,13 @@ func (us *UserNotificationConfigService) UpdateUserNotificationConfig( return nil } +// SetDefaultUserNotificationConfig set default user notification config for user register +func (us *UserNotificationConfigService) SetDefaultUserNotificationConfig(ctx context.Context, userIDs []string) ( + err error) { + return us.userNotificationConfigRepo.Add(ctx, userIDs, + string(constant.InboxSource), `[{"key":"email","enable":true}]`) +} + func (us *UserNotificationConfigService) convertToEntity(ctx context.Context, userID string, source constant.NotificationSource, channels schema.NotificationChannels) (c *entity.UserNotificationConfig) { c = &entity.UserNotificationConfig{ diff --git a/internal/service/user_service.go b/internal/service/user_service.go index 7d42a3d4..25db3422 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -32,16 +32,17 @@ import ( // UserService user service type UserService struct { - userCommonService *usercommon.UserCommon - userRepo usercommon.UserRepo - userActivity activity.UserActiveActivityRepo - activityRepo activity_common.ActivityRepo - emailService *export.EmailService - authService *auth.AuthService - siteInfoService siteinfo_common.SiteInfoCommonService - userRoleService *role.UserRoleRelService - userExternalLoginService *user_external_login.UserExternalLoginService - userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo + userCommonService *usercommon.UserCommon + userRepo usercommon.UserRepo + userActivity activity.UserActiveActivityRepo + activityRepo activity_common.ActivityRepo + emailService *export.EmailService + authService *auth.AuthService + siteInfoService siteinfo_common.SiteInfoCommonService + userRoleService *role.UserRoleRelService + userExternalLoginService *user_external_login.UserExternalLoginService + userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo + userNotificationConfigService *user_notification_config.UserNotificationConfigService } func NewUserService(userRepo usercommon.UserRepo, @@ -54,18 +55,20 @@ func NewUserService(userRepo usercommon.UserRepo, userCommonService *usercommon.UserCommon, userExternalLoginService *user_external_login.UserExternalLoginService, userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo, + userNotificationConfigService *user_notification_config.UserNotificationConfigService, ) *UserService { return &UserService{ - userCommonService: userCommonService, - userRepo: userRepo, - userActivity: userActivity, - activityRepo: activityRepo, - emailService: emailService, - authService: authService, - siteInfoService: siteInfoService, - userRoleService: userRoleService, - userExternalLoginService: userExternalLoginService, - userNotificationConfigRepo: userNotificationConfigRepo, + userCommonService: userCommonService, + userRepo: userRepo, + userActivity: userActivity, + activityRepo: activityRepo, + emailService: emailService, + authService: authService, + siteInfoService: siteInfoService, + userRoleService: userRoleService, + userExternalLoginService: userExternalLoginService, + userNotificationConfigRepo: userNotificationConfigRepo, + userNotificationConfigService: userNotificationConfigService, } } @@ -398,6 +401,9 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo if err != nil { return nil, nil, err } + if err := us.userNotificationConfigService.SetDefaultUserNotificationConfig(ctx, []string{userInfo.ID}); err != nil { + log.Errorf("set default user notification config failed, err: %v", err) + } // send email data := &schema.EmailCodeContent{ diff --git a/ui/src/components/BaseUserCard/index.tsx b/ui/src/components/BaseUserCard/index.tsx index 826f3319..12d9ed88 100644 --- a/ui/src/components/BaseUserCard/index.tsx +++ b/ui/src/components/BaseUserCard/index.tsx @@ -12,6 +12,7 @@ interface Props { avatarSearchStr?: string; className?: string; avatarClass?: string; + nameMaxWidth?: string; } const Index: FC = ({ @@ -22,11 +23,14 @@ const Index: FC = ({ className = 'small', avatarSearchStr = 's=48', showReputation = true, + nameMaxWidth = '300px', }) => { return (
{data?.status !== 'deleted' ? ( - + {showAvatar && ( = ({ alt={data?.display_name} /> )} - + {data?.display_name} @@ -51,9 +57,7 @@ const Index: FC = ({ alt={data?.display_name} /> )} - - {data?.display_name} - + {data?.display_name} )} diff --git a/ui/src/components/Editor/toolItem.tsx b/ui/src/components/Editor/toolItem.tsx index cba3a622..2af40682 100644 --- a/ui/src/components/Editor/toolItem.tsx +++ b/ui/src/components/Editor/toolItem.tsx @@ -1,5 +1,5 @@ import { FC, useContext, useEffect } from 'react'; -import { Dropdown, OverlayTrigger, Tooltip, Button } from 'react-bootstrap'; +import { Dropdown, Button } from 'react-bootstrap'; import { EditorContext } from './EditorContext'; @@ -49,28 +49,27 @@ const ToolItem: FC = (props) => { }, []); const btnRender = () => ( - {tip}}> -