From 3ce9dfb10f4ad98799e287928f837f083044f07e Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 May 2025 10:57:15 +0800 Subject: [PATCH 01/34] fix: optimization hr's style --- ui/src/index.scss | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ui/src/index.scss b/ui/src/index.scss index 97aa7b1fd..b299a993e 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -208,6 +208,39 @@ img[src=''] { .fmt { width: 100%; + hr { + height: 1.5rem; + border: none; + position: relative; + overflow: visible; + text-align: center; + opacity: 1; + flex: none; + &::before { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(calc(-50% - 16px), -50%); + width: 3px; + height: 3px; + background-color: var(--bs-border-color); + border-radius: 50%; + } + + &::after { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 3px; + height: 3px; + background-color: var(--bs-border-color); + border-radius: 50%; + box-shadow: 16px 0 0 var(--bs-border-color); + } + } h1 { @extend .fs-3; margin-top: 2rem; From 07217e25ab6cbb7a5bc525430ddc52e2889dce33 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 May 2025 11:08:58 +0800 Subject: [PATCH 02/34] fix: optimization hr's style --- ui/src/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/index.scss b/ui/src/index.scss index b299a993e..59de8d9ab 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -209,7 +209,7 @@ img[src=''] { .fmt { width: 100%; hr { - height: 1.5rem; + height: 3px; border: none; position: relative; overflow: visible; From a963f5a88122213b0a6e1b4588880c9cafaab95e Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 May 2025 11:22:00 +0800 Subject: [PATCH 03/34] fix: hr tag's margin use 1.5rem --- ui/src/index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/index.scss b/ui/src/index.scss index 59de8d9ab..fb1e06a79 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -216,6 +216,7 @@ img[src=''] { text-align: center; opacity: 1; flex: none; + margin: 1.5rem 0; &::before { content: ''; position: absolute; From b6a23d69d6415e35e186b56cf3d85ca8b6bcbb0c Mon Sep 17 00:00:00 2001 From: Luffy <52o@qq52o.cn> Date: Tue, 3 Jun 2025 18:27:41 +0800 Subject: [PATCH 04/34] feat: add loading state for counts display in search results --- i18n/en_US.yaml | 1 + i18n/zh_CN.yaml | 1 + ui/src/components/Header/index.tsx | 1 - ui/src/pages/Search/components/SearchHead/index.tsx | 6 +++++- ui/src/pages/Search/index.tsx | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index cb05e5e13..1791d85c5 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1534,6 +1534,7 @@ ui: follow: Follow following: Following counts: "{{count}} Results" + counts_loading: "... Results" more: More sort_btns: relevance: Relevance diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 9852ba605..6b153e0a5 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1501,6 +1501,7 @@ ui: follow: 关注 following: 已关注 counts: "{{count}} 个结果" + counts_loading: "... 个结果" more: 更多 sort_btns: relevance: 相关性 diff --git a/ui/src/components/Header/index.tsx b/ui/src/components/Header/index.tsx index 0b015ba68..dbc7565d0 100644 --- a/ui/src/components/Header/index.tsx +++ b/ui/src/components/Header/index.tsx @@ -87,7 +87,6 @@ const Header: FC = () => { if (theme_config?.[theme]?.navbar_style) { // const color = theme_config[theme].navbar_style.startsWith('#') themeMode = isLight(theme_config[theme].navbar_style) ? 'light' : 'dark'; - console.log('isLightTheme', themeMode); navbarStyle = `theme-${themeMode}`; } diff --git a/ui/src/pages/Search/components/SearchHead/index.tsx b/ui/src/pages/Search/components/SearchHead/index.tsx index a175d381b..1bbb6b019 100644 --- a/ui/src/pages/Search/components/SearchHead/index.tsx +++ b/ui/src/pages/Search/components/SearchHead/index.tsx @@ -35,7 +35,11 @@ const Index: FC = ({ sort, count = 0 }) => { return (
-
{t('counts', { count, keyPrefix: 'search' })}
+
+ {count === -1 + ? t('counts_loading', { keyPrefix: 'search' }) + : t('counts', { count, keyPrefix: 'search' })} +
{ - + {isSkeletonShow ? ( From ef8c1ebb8862d573915a699ab1a98c690549be96 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Fri, 13 Jun 2025 16:01:05 +0800 Subject: [PATCH 05/34] fix(notification): handle potential nil pointer dereference #1356 --- internal/service/notification_common/notification.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/service/notification_common/notification.go b/internal/service/notification_common/notification.go index 5edb6652b..2bceb6298 100644 --- a/internal/service/notification_common/notification.go +++ b/internal/service/notification_common/notification.go @@ -132,6 +132,7 @@ func (ns *NotificationCommon) AddNotification(ctx context.Context, msg *schema.N objInfo, err = ns.objectInfoService.GetInfo(ctx, req.ObjectInfo.ObjectID) if err != nil { log.Error(err) + return err } else { req.ObjectInfo.Title = objInfo.Title questionID = objInfo.QuestionID @@ -354,6 +355,9 @@ func (ns *NotificationCommon) SendNotificationToAllFollower(ctx context.Context, func (ns *NotificationCommon) syncNotificationToPlugin(ctx context.Context, objInfo *schema.SimpleObjectInfo, msg *schema.NotificationMsg) { + if objInfo == nil { + return + } siteInfo, err := ns.siteInfoService.GetSiteGeneral(ctx) if err != nil { log.Errorf("get site general info failed: %v", err) From ee6e67243ddebf876d485dfc50db6b9968db9b92 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Fri, 13 Jun 2025 16:02:11 +0800 Subject: [PATCH 06/34] test(internal): add mock for IsBrandingFileUsed method --- internal/service/mock/siteinfo_repo_mock.go | 29 +++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/internal/service/mock/siteinfo_repo_mock.go b/internal/service/mock/siteinfo_repo_mock.go index 1b8412780..a98ceb68c 100644 --- a/internal/service/mock/siteinfo_repo_mock.go +++ b/internal/service/mock/siteinfo_repo_mock.go @@ -77,6 +77,21 @@ func (mr *MockSiteInfoRepoMockRecorder) GetByType(ctx, siteType any) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByType", reflect.TypeOf((*MockSiteInfoRepo)(nil).GetByType), ctx, siteType) } +// IsBrandingFileUsed mocks base method. +func (m *MockSiteInfoRepo) IsBrandingFileUsed(ctx context.Context, filePath string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsBrandingFileUsed", ctx, filePath) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsBrandingFileUsed indicates an expected call of IsBrandingFileUsed. +func (mr *MockSiteInfoRepoMockRecorder) IsBrandingFileUsed(ctx, filePath any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsBrandingFileUsed", reflect.TypeOf((*MockSiteInfoRepo)(nil).IsBrandingFileUsed), ctx, filePath) +} + // SaveByType mocks base method. func (m *MockSiteInfoRepo) SaveByType(ctx context.Context, siteType string, data *entity.SiteInfo) error { m.ctrl.T.Helper() @@ -306,3 +321,17 @@ func (mr *MockSiteInfoCommonServiceMockRecorder) GetSiteWrite(ctx any) *gomock.C mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSiteWrite", reflect.TypeOf((*MockSiteInfoCommonService)(nil).GetSiteWrite), ctx) } + +// IsBrandingFileUsed mocks base method. +func (m *MockSiteInfoCommonService) IsBrandingFileUsed(ctx context.Context, filePath string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsBrandingFileUsed", ctx, filePath) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsBrandingFileUsed indicates an expected call of IsBrandingFileUsed. +func (mr *MockSiteInfoCommonServiceMockRecorder) IsBrandingFileUsed(ctx, filePath any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsBrandingFileUsed", reflect.TypeOf((*MockSiteInfoCommonService)(nil).IsBrandingFileUsed), ctx, filePath) +} From a62c975c7e81d15d8aeece7b84a0f652d18ff051 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 10:42:32 +0800 Subject: [PATCH 07/34] chore(deps): update Go version and dependencies to v1.23.0 and latest compatible versions --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 89c6876b5..6578d5cef 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ module github.com/apache/answer -go 1.22.0 +go 1.23.0 require ( github.com/Machiel/slugify v1.0.1 @@ -57,10 +57,10 @@ require ( github.com/tidwall/gjson v1.17.3 github.com/yuin/goldmark v1.7.4 go.uber.org/mock v0.5.0 - golang.org/x/crypto v0.27.0 + golang.org/x/crypto v0.36.0 golang.org/x/image v0.20.0 - golang.org/x/net v0.29.0 - golang.org/x/text v0.18.0 + golang.org/x/net v0.38.0 + golang.org/x/text v0.23.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.33.0 @@ -160,7 +160,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.10.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.31.0 // indirect golang.org/x/tools v0.25.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 446401f58..afe9b1ea9 100644 --- a/go.sum +++ b/go.sum @@ -685,8 +685,8 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= @@ -731,8 +731,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -743,8 +743,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -779,8 +779,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 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= @@ -793,8 +793,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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From dc98f61d69e28023d1113fee276227f3f2390896 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 11:05:11 +0800 Subject: [PATCH 08/34] refactor(api): update API documentation and method names for clarity --- internal/controller/answer_controller.go | 70 ++++++++++++------------ internal/router/answer_api_router.go | 4 +- internal/schema/answer_schema.go | 5 ++ 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index 157e1b253..a22c597ff 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -69,7 +69,7 @@ func NewAnswerController( // RemoveAnswer delete answer // @Summary delete answer // @Description delete answer -// @Tags api-answer +// @Tags Answer // @Accept json // @Produce json // @Security ApiKeyAuth @@ -119,7 +119,7 @@ func (ac *AnswerController) RemoveAnswer(ctx *gin.Context) { // RecoverAnswer recover answer // @Summary recover answer -// @Description recover deleted answer +// @Description recover the deleted answer // @Tags Answer // @Accept json // @Produce json @@ -151,16 +151,16 @@ func (ac *AnswerController) RecoverAnswer(ctx *gin.Context) { handler.HandleResponse(ctx, err, nil) } -// Get godoc -// @Summary Get Answer -// @Description Get Answer -// @Tags api-answer -// @Accept json -// @Produce json -// @Param id query string true "Answer TagID" default(1) -// @Router /answer/api/v1/answer/info [get] -// @Success 200 {string} string "" -func (ac *AnswerController) Get(ctx *gin.Context) { +// GetAnswerInfo get answer info +// @Summary Get Answer Detail +// @Description Get Answer Detail +// @Tags Answer +// @Accept json +// @Produce json +// @Param id query string true "id" +// @Success 200 {object} handler.RespBody{data=schema.GetAnswerInfoResp} +// @Router /answer/api/v1/answer/info [get] +func (ac *AnswerController) GetAnswerInfo(ctx *gin.Context) { id := ctx.Query("id") id = uid.DeShortID(id) userID := middleware.GetLoginUserIDFromContext(ctx) @@ -174,23 +174,23 @@ func (ac *AnswerController) Get(ctx *gin.Context) { handler.HandleResponse(ctx, fmt.Errorf(""), gin.H{}) return } - handler.HandleResponse(ctx, err, gin.H{ - "info": info, - "question": questionInfo, + handler.HandleResponse(ctx, err, &schema.GetAnswerInfoResp{ + Info: info, + Question: questionInfo, }) } -// Add godoc -// @Summary Insert Answer -// @Description Insert Answer -// @Tags api-answer -// @Accept json -// @Produce json +// AddAnswer add answer +// @Summary Add Answer +// @Description add answer +// @Tags Answer +// @Accept json +// @Produce json // @Security ApiKeyAuth -// @Param data body schema.AnswerAddReq true "AnswerAddReq" -// @Success 200 {string} string "" +// @Param data body schema.AnswerAddReq true "add answer request" +// @Success 200 {object} handler.RespBody{} // @Router /answer/api/v1/answer [post] -func (ac *AnswerController) Add(ctx *gin.Context) { +func (ac *AnswerController) AddAnswer(ctx *gin.Context) { req := &schema.AnswerAddReq{} if handler.BindAndCheck(ctx, req) { return @@ -295,11 +295,11 @@ func (ac *AnswerController) Add(ctx *gin.Context) { // Update godoc // @Summary Update Answer // @Description Update Answer -// @Tags api-answer -// @Accept json -// @Produce json +// @Tags Answer +// @Accept json +// @Produce json // @Security ApiKeyAuth -// @Param data body schema.AnswerUpdateReq true "AnswerUpdateReq" +// @Param data body schema.AnswerUpdateReq true "AnswerUpdateReq" // @Success 200 {string} string "" // @Router /answer/api/v1/answer [put] func (ac *AnswerController) Update(ctx *gin.Context) { @@ -360,9 +360,9 @@ func (ac *AnswerController) Update(ctx *gin.Context) { // AnswerList godoc // @Summary AnswerList // @Description AnswerList
order (default or updated) -// @Tags api-answer -// @Accept json -// @Produce json +// @Tags Answer +// @Accept json +// @Produce json // @Param question_id query string true "question_id" // @Param order query string true "order" // @Param page query string true "page" @@ -405,11 +405,11 @@ func (ac *AnswerController) AnswerList(ctx *gin.Context) { // Accepted godoc // @Summary Accepted // @Description Accepted -// @Tags api-answer -// @Accept json -// @Produce json +// @Tags Answer +// @Accept json +// @Produce json // @Security ApiKeyAuth -// @Param data body schema.AcceptAnswerReq true "AcceptAnswerReq" +// @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) { diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index e01bd9a95..cba237072 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -157,7 +157,7 @@ func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) { r.GET("/user/staff", a.userController.UserStaff) // answer - r.GET("/answer/info", a.answerController.Get) + r.GET("/answer/info", a.answerController.GetAnswerInfo) r.GET("/answer/page", a.answerController.AnswerList) r.GET("/personal/answer/page", a.questionController.PersonalAnswerPage) @@ -265,7 +265,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { r.POST("/question/recover", a.questionController.QuestionRecover) // answer - r.POST("/answer", a.answerController.Add) + r.POST("/answer", a.answerController.AddAnswer) r.PUT("/answer", a.answerController.Update) r.POST("/answer/acceptance", a.answerController.Accepted) r.DELETE("/answer", a.answerController.RemoveAnswer) diff --git a/internal/schema/answer_schema.go b/internal/schema/answer_schema.go index 3a404ed1a..015e26ac5 100644 --- a/internal/schema/answer_schema.go +++ b/internal/schema/answer_schema.go @@ -71,6 +71,11 @@ func (req *AnswerAddReq) Check() (errFields []*validator.FormErrorField, err err return nil, nil } +type GetAnswerInfoResp struct { + Info *AnswerInfo `json:"info"` + Question *QuestionInfoResp `json:"question"` +} + type AnswerUpdateReq struct { ID string `json:"id"` QuestionID string `json:"question_id"` From d02d09bb9a80c74962cc6543320762d44a48253c Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 11:06:14 +0800 Subject: [PATCH 09/34] refactor(api): improve API documentation for Answer endpoints --- docs/docs.go | 254 +++++++++++++++++++++++++++++++++++++++++++--- docs/swagger.json | 254 +++++++++++++++++++++++++++++++++++++++++++--- docs/swagger.yaml | 178 +++++++++++++++++++++++++++++--- 3 files changed, 638 insertions(+), 48 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 82ab3c779..31f361465 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2134,7 +2134,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "Update Answer", "parameters": [ @@ -2163,7 +2163,7 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "Insert Answer", + "description": "add answer", "consumes": [ "application/json" ], @@ -2171,12 +2171,12 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], - "summary": "Insert Answer", + "summary": "Add Answer", "parameters": [ { - "description": "AnswerAddReq", + "description": "add answer request", "name": "data", "in": "body", "required": true, @@ -2189,7 +2189,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } @@ -2208,7 +2208,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "delete answer", "parameters": [ @@ -2247,7 +2247,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "Accepted", "parameters": [ @@ -2273,7 +2273,7 @@ const docTemplate = `{ }, "/answer/api/v1/answer/info": { "get": { - "description": "Get Answer", + "description": "Get Answer Detail", "consumes": [ "application/json" ], @@ -2281,14 +2281,13 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], - "summary": "Get Answer", + "summary": "Get Answer Detail", "parameters": [ { "type": "string", - "default": "1", - "description": "Answer TagID", + "description": "id", "name": "id", "in": "query", "required": true @@ -2298,7 +2297,19 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetAnswerInfoResp" + } + } + } + ] } } } @@ -2314,7 +2325,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "AnswerList", "parameters": [ @@ -2364,7 +2375,7 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "recover deleted answer", + "description": "recover the deleted answer", "consumes": [ "application/json" ], @@ -7992,6 +8003,60 @@ const docTemplate = `{ } } }, + "schema.AnswerInfo": { + "type": "object", + "properties": { + "accepted": { + "type": "integer" + }, + "collected": { + "type": "boolean" + }, + "content": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "html": { + "type": "string" + }, + "id": { + "type": "string" + }, + "member_actions": { + "description": "MemberActions", + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "question_id": { + "type": "string" + }, + "question_info": { + "$ref": "#/definitions/schema.QuestionInfoResp" + }, + "status": { + "type": "integer" + }, + "update_time": { + "type": "integer" + }, + "update_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "vote_count": { + "type": "integer" + }, + "vote_status": { + "type": "string" + } + } + }, "schema.AnswerUpdateReq": { "type": "object", "required": [ @@ -8352,6 +8417,17 @@ const docTemplate = `{ } } }, + "schema.GetAnswerInfoResp": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/schema.AnswerInfo" + }, + "question": { + "$ref": "#/definitions/schema.QuestionInfoResp" + } + } + }, "schema.GetBadgeInfoResp": { "type": "object", "properties": { @@ -9585,6 +9661,41 @@ const docTemplate = `{ } } }, + "schema.Operation": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "level": { + "$ref": "#/definitions/schema.OperationLevel" + }, + "msg": { + "type": "string" + }, + "time": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "schema.OperationLevel": { + "type": "string", + "enum": [ + "info", + "danger", + "warning", + "secondary" + ], + "x-enum-varnames": [ + "OperationLevelInfo", + "OperationLevelDanger", + "OperationLevelWarning", + "OperationLevelSecondary" + ] + }, "schema.OperationQuestionReq": { "type": "object", "required": [ @@ -9738,6 +9849,117 @@ const docTemplate = `{ } } }, + "schema.QuestionInfoResp": { + "type": "object", + "properties": { + "accepted_answer_id": { + "type": "string" + }, + "answer_count": { + "type": "integer" + }, + "answered": { + "type": "boolean" + }, + "collected": { + "type": "boolean" + }, + "collection_count": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "edit_time": { + "type": "integer" + }, + "extends_actions": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "first_answer_id": { + "type": "string" + }, + "follow_count": { + "type": "integer" + }, + "html": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_followed": { + "type": "boolean" + }, + "last_answer_id": { + "type": "string" + }, + "last_answered_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "member_actions": { + "description": "MemberActions", + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "operation": { + "$ref": "#/definitions/schema.Operation" + }, + "pin": { + "type": "integer" + }, + "show": { + "type": "integer" + }, + "status": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.TagResp" + } + }, + "title": { + "type": "string" + }, + "unique_view_count": { + "type": "integer" + }, + "update_time": { + "type": "integer" + }, + "update_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "url_title": { + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "view_count": { + "type": "integer" + }, + "vote_count": { + "type": "integer" + }, + "vote_status": { + "type": "string" + } + } + }, "schema.QuestionPageReq": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 689255a42..c2318a7c4 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2107,7 +2107,7 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "Update Answer", "parameters": [ @@ -2136,7 +2136,7 @@ "ApiKeyAuth": [] } ], - "description": "Insert Answer", + "description": "add answer", "consumes": [ "application/json" ], @@ -2144,12 +2144,12 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], - "summary": "Insert Answer", + "summary": "Add Answer", "parameters": [ { - "description": "AnswerAddReq", + "description": "add answer request", "name": "data", "in": "body", "required": true, @@ -2162,7 +2162,7 @@ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } @@ -2181,7 +2181,7 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "delete answer", "parameters": [ @@ -2220,7 +2220,7 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "Accepted", "parameters": [ @@ -2246,7 +2246,7 @@ }, "/answer/api/v1/answer/info": { "get": { - "description": "Get Answer", + "description": "Get Answer Detail", "consumes": [ "application/json" ], @@ -2254,14 +2254,13 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], - "summary": "Get Answer", + "summary": "Get Answer Detail", "parameters": [ { "type": "string", - "default": "1", - "description": "Answer TagID", + "description": "id", "name": "id", "in": "query", "required": true @@ -2271,7 +2270,19 @@ "200": { "description": "OK", "schema": { - "type": "string" + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.GetAnswerInfoResp" + } + } + } + ] } } } @@ -2287,7 +2298,7 @@ "application/json" ], "tags": [ - "api-answer" + "Answer" ], "summary": "AnswerList", "parameters": [ @@ -2337,7 +2348,7 @@ "ApiKeyAuth": [] } ], - "description": "recover deleted answer", + "description": "recover the deleted answer", "consumes": [ "application/json" ], @@ -7965,6 +7976,60 @@ } } }, + "schema.AnswerInfo": { + "type": "object", + "properties": { + "accepted": { + "type": "integer" + }, + "collected": { + "type": "boolean" + }, + "content": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "html": { + "type": "string" + }, + "id": { + "type": "string" + }, + "member_actions": { + "description": "MemberActions", + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "question_id": { + "type": "string" + }, + "question_info": { + "$ref": "#/definitions/schema.QuestionInfoResp" + }, + "status": { + "type": "integer" + }, + "update_time": { + "type": "integer" + }, + "update_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "vote_count": { + "type": "integer" + }, + "vote_status": { + "type": "string" + } + } + }, "schema.AnswerUpdateReq": { "type": "object", "required": [ @@ -8325,6 +8390,17 @@ } } }, + "schema.GetAnswerInfoResp": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/schema.AnswerInfo" + }, + "question": { + "$ref": "#/definitions/schema.QuestionInfoResp" + } + } + }, "schema.GetBadgeInfoResp": { "type": "object", "properties": { @@ -9558,6 +9634,41 @@ } } }, + "schema.Operation": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "level": { + "$ref": "#/definitions/schema.OperationLevel" + }, + "msg": { + "type": "string" + }, + "time": { + "type": "integer" + }, + "type": { + "type": "string" + } + } + }, + "schema.OperationLevel": { + "type": "string", + "enum": [ + "info", + "danger", + "warning", + "secondary" + ], + "x-enum-varnames": [ + "OperationLevelInfo", + "OperationLevelDanger", + "OperationLevelWarning", + "OperationLevelSecondary" + ] + }, "schema.OperationQuestionReq": { "type": "object", "required": [ @@ -9711,6 +9822,117 @@ } } }, + "schema.QuestionInfoResp": { + "type": "object", + "properties": { + "accepted_answer_id": { + "type": "string" + }, + "answer_count": { + "type": "integer" + }, + "answered": { + "type": "boolean" + }, + "collected": { + "type": "boolean" + }, + "collection_count": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "create_time": { + "type": "integer" + }, + "description": { + "type": "string" + }, + "edit_time": { + "type": "integer" + }, + "extends_actions": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "first_answer_id": { + "type": "string" + }, + "follow_count": { + "type": "integer" + }, + "html": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_followed": { + "type": "boolean" + }, + "last_answer_id": { + "type": "string" + }, + "last_answered_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "member_actions": { + "description": "MemberActions", + "type": "array", + "items": { + "$ref": "#/definitions/schema.PermissionMemberAction" + } + }, + "operation": { + "$ref": "#/definitions/schema.Operation" + }, + "pin": { + "type": "integer" + }, + "show": { + "type": "integer" + }, + "status": { + "type": "integer" + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/definitions/schema.TagResp" + } + }, + "title": { + "type": "string" + }, + "unique_view_count": { + "type": "integer" + }, + "update_time": { + "type": "integer" + }, + "update_user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "url_title": { + "type": "string" + }, + "user_info": { + "$ref": "#/definitions/schema.UserBasicInfo" + }, + "view_count": { + "type": "integer" + }, + "vote_count": { + "type": "integer" + }, + "vote_status": { + "type": "string" + } + } + }, "schema.QuestionPageReq": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index a399b3bfd..f89ea946a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -349,6 +349,42 @@ definitions: required: - content type: object + schema.AnswerInfo: + properties: + accepted: + type: integer + collected: + type: boolean + content: + type: string + create_time: + type: integer + html: + type: string + id: + type: string + member_actions: + description: MemberActions + items: + $ref: '#/definitions/schema.PermissionMemberAction' + type: array + question_id: + type: string + question_info: + $ref: '#/definitions/schema.QuestionInfoResp' + status: + type: integer + update_time: + type: integer + update_user_info: + $ref: '#/definitions/schema.UserBasicInfo' + user_info: + $ref: '#/definitions/schema.UserBasicInfo' + vote_count: + type: integer + vote_status: + type: string + type: object schema.AnswerUpdateReq: properties: captcha_code: @@ -595,6 +631,13 @@ definitions: description: if user is followed object will be true,otherwise false type: boolean type: object + schema.GetAnswerInfoResp: + properties: + info: + $ref: '#/definitions/schema.AnswerInfo' + question: + $ref: '#/definitions/schema.QuestionInfoResp' + type: object schema.GetBadgeInfoResp: properties: award_count: @@ -1455,6 +1498,31 @@ definitions: toast_return_message: type: boolean type: object + schema.Operation: + properties: + description: + type: string + level: + $ref: '#/definitions/schema.OperationLevel' + msg: + type: string + time: + type: integer + type: + type: string + type: object + schema.OperationLevel: + enum: + - info + - danger + - warning + - secondary + type: string + x-enum-varnames: + - OperationLevelInfo + - OperationLevelDanger + - OperationLevelWarning + - OperationLevelSecondary schema.OperationQuestionReq: properties: id: @@ -1565,6 +1633,80 @@ definitions: - tags - title type: object + schema.QuestionInfoResp: + properties: + accepted_answer_id: + type: string + answer_count: + type: integer + answered: + type: boolean + collected: + type: boolean + collection_count: + type: integer + content: + type: string + create_time: + type: integer + description: + type: string + edit_time: + type: integer + extends_actions: + items: + $ref: '#/definitions/schema.PermissionMemberAction' + type: array + first_answer_id: + type: string + follow_count: + type: integer + html: + type: string + id: + type: string + is_followed: + type: boolean + last_answer_id: + type: string + last_answered_user_info: + $ref: '#/definitions/schema.UserBasicInfo' + member_actions: + description: MemberActions + items: + $ref: '#/definitions/schema.PermissionMemberAction' + type: array + operation: + $ref: '#/definitions/schema.Operation' + pin: + type: integer + show: + type: integer + status: + type: integer + tags: + items: + $ref: '#/definitions/schema.TagResp' + type: array + title: + type: string + unique_view_count: + type: integer + update_time: + type: integer + update_user_info: + $ref: '#/definitions/schema.UserBasicInfo' + url_title: + type: string + user_info: + $ref: '#/definitions/schema.UserBasicInfo' + view_count: + type: integer + vote_count: + type: integer + vote_status: + type: string + type: object schema.QuestionPageReq: properties: in_days: @@ -4263,13 +4405,13 @@ paths: - ApiKeyAuth: [] summary: delete answer tags: - - api-answer + - Answer post: consumes: - application/json - description: Insert Answer + description: add answer parameters: - - description: AnswerAddReq + - description: add answer request in: body name: data required: true @@ -4281,12 +4423,12 @@ paths: "200": description: OK schema: - type: string + $ref: '#/definitions/handler.RespBody' security: - ApiKeyAuth: [] - summary: Insert Answer + summary: Add Answer tags: - - api-answer + - Answer put: consumes: - application/json @@ -4309,7 +4451,7 @@ paths: - ApiKeyAuth: [] summary: Update Answer tags: - - api-answer + - Answer /answer/api/v1/answer/acceptance: post: consumes: @@ -4333,15 +4475,14 @@ paths: - ApiKeyAuth: [] summary: Accepted tags: - - api-answer + - Answer /answer/api/v1/answer/info: get: consumes: - application/json - description: Get Answer + description: Get Answer Detail parameters: - - default: "1" - description: Answer TagID + - description: id in: query name: id required: true @@ -4352,10 +4493,15 @@ paths: "200": description: OK schema: - type: string - summary: Get Answer + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.GetAnswerInfoResp' + type: object + summary: Get Answer Detail tags: - - api-answer + - Answer /answer/api/v1/answer/page: get: consumes: @@ -4391,12 +4537,12 @@ paths: type: string summary: AnswerList tags: - - api-answer + - Answer /answer/api/v1/answer/recover: post: consumes: - application/json - description: recover deleted answer + description: recover the deleted answer parameters: - description: answer in: body From 03ec0ddfbb3492f9c28737488e3b1feeea16720a Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 11:16:29 +0800 Subject: [PATCH 10/34] chore(deps): update Go version to 1.23 in configuration files --- .github/workflows/build-binary-for-release.yml | 2 +- Dockerfile | 2 +- README.md | 2 +- cmd/main.go | 2 +- internal/cli/build.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-binary-for-release.yml b/.github/workflows/build-binary-for-release.yml index ec97f042d..49c53051a 100644 --- a/.github/workflows/build-binary-for-release.yml +++ b/.github/workflows/build-binary-for-release.yml @@ -44,7 +44,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: 1.22 + go-version: 1.23 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v4 with: diff --git a/Dockerfile b/Dockerfile index 929f321da..2e90a4301 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -FROM golang:1.22-alpine AS golang-builder +FROM golang:1.23-alpine AS golang-builder LABEL maintainer="linkinstar@apache.org" ARG GOPROXY diff --git a/README.md b/README.md index 8f5ae243b..8fbb325b7 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ You can also check out the [plugins here](https://answer.apache.org/plugins). ### Prerequisites -- Golang >= 1.22 +- Golang >= 1.23 - Node.js >= 20 - pnpm >= 9 - [mockgen](https://github.com/uber-go/mock?tab=readme-ov-file#installation) >= 1.6.0 diff --git a/cmd/main.go b/cmd/main.go index 5bc26572b..35256c073 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -49,7 +49,7 @@ var ( // Time is the build time of the project Time = "" // GoVersion is the go version of the project - GoVersion = "1.22" + GoVersion = "1.23" // log level logLevel = os.Getenv("LOG_LEVEL") // log path diff --git a/internal/cli/build.go b/internal/cli/build.go index 2f43a9116..43a7aa9fe 100644 --- a/internal/cli/build.go +++ b/internal/cli/build.go @@ -62,7 +62,7 @@ func main() { ` goModTpl = `module answer -go 1.22 +go 1.23 ` ) From d82c75d7ccdd42a87a31496fbbfb47270f4187a1 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 11:19:33 +0800 Subject: [PATCH 11/34] refactor(api): rename methods and update documentation for clarity --- internal/controller/answer_controller.go | 16 ++++++++-------- internal/router/answer_api_router.go | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index a22c597ff..7c5aca1db 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -292,7 +292,7 @@ func (ac *AnswerController) AddAnswer(ctx *gin.Context) { }) } -// Update godoc +// UpdateAnswer update answer // @Summary Update Answer // @Description Update Answer // @Tags Answer @@ -300,9 +300,9 @@ func (ac *AnswerController) AddAnswer(ctx *gin.Context) { // @Produce json // @Security ApiKeyAuth // @Param data body schema.AnswerUpdateReq true "AnswerUpdateReq" -// @Success 200 {string} string "" +// @Success 200 {object} handler.RespBody{} // @Router /answer/api/v1/answer [put] -func (ac *AnswerController) Update(ctx *gin.Context) { +func (ac *AnswerController) UpdateAnswer(ctx *gin.Context) { req := &schema.AnswerUpdateReq{} if handler.BindAndCheck(ctx, req) { return @@ -402,17 +402,17 @@ func (ac *AnswerController) AnswerList(ctx *gin.Context) { }) } -// Accepted godoc -// @Summary Accepted -// @Description Accepted +// AcceptAnswer accept answer +// @Summary Accept Answer +// @Description Accept Answer // @Tags Answer // @Accept json // @Produce json // @Security ApiKeyAuth // @Param data body schema.AcceptAnswerReq true "AcceptAnswerReq" -// @Success 200 {string} string "" +// @Success 200 {object} handler.RespBody{} // @Router /answer/api/v1/answer/acceptance [post] -func (ac *AnswerController) Accepted(ctx *gin.Context) { +func (ac *AnswerController) AcceptAnswer(ctx *gin.Context) { req := &schema.AcceptAnswerReq{} if handler.BindAndCheck(ctx, req) { return diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index cba237072..1191492b9 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -266,8 +266,8 @@ func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) { // answer r.POST("/answer", a.answerController.AddAnswer) - r.PUT("/answer", a.answerController.Update) - r.POST("/answer/acceptance", a.answerController.Accepted) + r.PUT("/answer", a.answerController.UpdateAnswer) + r.POST("/answer/acceptance", a.answerController.AcceptAnswer) r.DELETE("/answer", a.answerController.RemoveAnswer) r.POST("/answer/recover", a.answerController.RecoverAnswer) From 67b9c0b462845ce64cc9a48fd6115dde69dc66dc Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Tue, 24 Jun 2025 11:19:53 +0800 Subject: [PATCH 12/34] refactor(api): update API documentation for Accept Answer endpoint --- docs/docs.go | 8 ++++---- docs/swagger.json | 8 ++++---- docs/swagger.yaml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 31f361465..76750aee9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -2152,7 +2152,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } @@ -2239,7 +2239,7 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "Accepted", + "description": "Accept Answer", "consumes": [ "application/json" ], @@ -2249,7 +2249,7 @@ const docTemplate = `{ "tags": [ "Answer" ], - "summary": "Accepted", + "summary": "Accept Answer", "parameters": [ { "description": "AcceptAnswerReq", @@ -2265,7 +2265,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } diff --git a/docs/swagger.json b/docs/swagger.json index c2318a7c4..4682f67c8 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2125,7 +2125,7 @@ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } @@ -2212,7 +2212,7 @@ "ApiKeyAuth": [] } ], - "description": "Accepted", + "description": "Accept Answer", "consumes": [ "application/json" ], @@ -2222,7 +2222,7 @@ "tags": [ "Answer" ], - "summary": "Accepted", + "summary": "Accept Answer", "parameters": [ { "description": "AcceptAnswerReq", @@ -2238,7 +2238,7 @@ "200": { "description": "OK", "schema": { - "type": "string" + "$ref": "#/definitions/handler.RespBody" } } } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f89ea946a..3d68f8a65 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -4446,7 +4446,7 @@ paths: "200": description: OK schema: - type: string + $ref: '#/definitions/handler.RespBody' security: - ApiKeyAuth: [] summary: Update Answer @@ -4456,7 +4456,7 @@ paths: post: consumes: - application/json - description: Accepted + description: Accept Answer parameters: - description: AcceptAnswerReq in: body @@ -4470,10 +4470,10 @@ paths: "200": description: OK schema: - type: string + $ref: '#/definitions/handler.RespBody' security: - ApiKeyAuth: [] - summary: Accepted + summary: Accept Answer tags: - Answer /answer/api/v1/answer/info: From 246f307980e59ddfc444a6d0a2ad17fc1e0ad205 Mon Sep 17 00:00:00 2001 From: shuai Date: Wed, 4 Jun 2025 10:39:49 +0800 Subject: [PATCH 13/34] feat: update .asf.yaml content --- .asf.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.asf.yaml b/.asf.yaml index 7505cd7f9..9832b8387 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -43,6 +43,6 @@ github: notifications: commits: commits@answer.apache.org - issues: commits@answer.apache.org - pullrequests: commits@answer.apache.org - discussions: commits@answer.apache.org + issues: issues@answer.apache.org + pullrequests: issues@answer.apache.org + discussions: issues@answer.apache.org From 157291c2d5b9c3985bc6259f1a4f83ee39cf3f04 Mon Sep 17 00:00:00 2001 From: fen Date: Fri, 6 Jun 2025 18:34:08 +0800 Subject: [PATCH 14/34] fix: screenshot --- docs/img/screenshot.png | Bin 88558 -> 98459 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/img/screenshot.png b/docs/img/screenshot.png index 555de5002a737074367d1a69e6e9e9b14cb083dd..8a823a8750d50829c49180c7c76f8a4a1d1ce0af 100644 GIT binary patch literal 98459 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

Ji6dN;4!2bb(ZzvPT`n)16Y_msE5*2P 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 From b863ad6b78586ed0632fdbc2afca0f10e3b7987a Mon Sep 17 00:00:00 2001 From: Xing Ji Date: Tue, 17 Jun 2025 12:26:27 +0800 Subject: [PATCH 15/34] chore(configs/config.yaml): Change `api_url` to `api_base_url` --- configs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/config.yaml b/configs/config.yaml index 69c4e3034..08eb0f80f 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -38,6 +38,6 @@ service_config: purge_deleted_files_period_days: 30 ui: public_url: '/' - api_url: '/' base_url: '' + api_base_url: '' From e6f94c924d531aa46fa1037f3b3dba777ecafd55 Mon Sep 17 00:00:00 2001 From: Xing Ji Date: Tue, 24 Jun 2025 14:00:35 +0800 Subject: [PATCH 16/34] chore: Keep the `api_url` in configs/config.yaml --- configs/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/config.yaml b/configs/config.yaml index 08eb0f80f..d14072785 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -38,6 +38,7 @@ service_config: purge_deleted_files_period_days: 30 ui: public_url: '/' + api_url: '/' base_url: '' api_base_url: '' From 550a92684e1936200350e9db57ebf1106087a5f7 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 26 Jun 2025 11:20:23 +0800 Subject: [PATCH 17/34] feat(migrations): move user config to interface --- internal/migrations/init.go | 6 +- internal/migrations/migrations.go | 1 + internal/migrations/v27.go | 71 +++++++++++++++++++ internal/schema/siteinfo_schema.go | 6 +- internal/service/content/user_service.go | 30 +++----- .../siteinfo_common/siteinfo_service.go | 2 +- 6 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 internal/migrations/v27.go diff --git a/internal/migrations/init.go b/internal/migrations/init.go index 610e24d8b..fde27cdde 100644 --- a/internal/migrations/init.go +++ b/internal/migrations/init.go @@ -180,8 +180,10 @@ func (m *Mentor) initSiteInfoInterface() { } interfaceData := map[string]string{ - "language": m.userData.Language, - "time_zone": localTimezone, + "language": m.userData.Language, + "time_zone": localTimezone, + "default_avatar": "gravatar", + "gravatar_base_url": "https://www.gravatar.com/avatar/", } interfaceDataBytes, _ := json.Marshal(interfaceData) _, m.err = m.engine.Context(m.ctx).Insert(&entity.SiteInfo{ diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index 212bf14bb..4b5335b06 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -102,6 +102,7 @@ var migrations = []Migration{ NewMigration("v1.4.2", "add the number of question links", addQuestionLinkedCount, true), NewMigration("v1.4.5", "add file record", addFileRecord, true), NewMigration("v1.5.1", "add plugin kv storage", addPluginKVStorage, true), + NewMigration("v1.6.0", "move user config to interface", moveUserConfigToInterface, true), } func GetMigrations() []Migration { diff --git a/internal/migrations/v27.go b/internal/migrations/v27.go new file mode 100644 index 000000000..7f089a874 --- /dev/null +++ b/internal/migrations/v27.go @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package migrations + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/apache/answer/internal/base/constant" + "github.com/apache/answer/internal/entity" + "github.com/apache/answer/internal/schema" + "xorm.io/xorm" +) + +func moveUserConfigToInterface(ctx context.Context, x *xorm.Engine) error { + // Get old interface config + interfaceSiteInfo := &entity.SiteInfo{Type: constant.SiteTypeInterface} + exist, err := x.Context(ctx).Get(interfaceSiteInfo) + if err != nil { + return fmt.Errorf("get config failed: %w", err) + } + if !exist { + return fmt.Errorf("interface site info not found") + } + + interfaceConfig := &schema.SiteInterfaceReq{} + _ = json.Unmarshal([]byte(interfaceSiteInfo.Content), interfaceSiteInfo) + + // Get old user config + usersConfig := &entity.SiteInfo{Type: constant.SiteTypeUsers} + exist, err = x.Context(ctx).Get(usersConfig) + if err != nil { + return fmt.Errorf("get config failed: %w", err) + } + if !exist { + return fmt.Errorf("users site info not found") + } + + siteUsers := &schema.SiteUsersReq{} + _ = json.Unmarshal([]byte(usersConfig.Content), siteUsers) + + interfaceConfig.DefaultAvatar = siteUsers.DefaultAvatar + interfaceConfig.GravatarBaseURL = siteUsers.GravatarBaseURL + + interfaceConfigByte, _ := json.Marshal(interfaceConfig) + interfaceSiteInfo.Content = string(interfaceConfigByte) + + _, err = x.Context(ctx).ID(interfaceSiteInfo.ID).Update(interfaceSiteInfo) + if err != nil { + return fmt.Errorf("insert site info failed: %w", err) + } + return nil +} diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index 7e0c408fd..19f01ffdb 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -59,8 +59,10 @@ func (r *SiteGeneralReq) FormatSiteUrl() { // SiteInterfaceReq site interface request type SiteInterfaceReq struct { - Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"` - TimeZone string `validate:"required,gt=1,lte=128" form:"time_zone" json:"time_zone"` + Language string `validate:"required,gt=1,lte=128" form:"language" json:"language"` + TimeZone string `validate:"required,gt=1,lte=128" form:"time_zone" json:"time_zone"` + DefaultAvatar string `validate:"required,oneof=system gravatar" json:"default_avatar"` + GravatarBaseURL string `validate:"omitempty" json:"gravatar_base_url"` } // SiteBrandingReq site branding request diff --git a/internal/service/content/user_service.go b/internal/service/content/user_service.go index ece3a86de..1c94d22d8 100644 --- a/internal/service/content/user_service.go +++ b/internal/service/content/user_service.go @@ -314,12 +314,8 @@ func (us *UserService) UserModifyPassword(ctx context.Context, req *schema.UserM // UpdateInfo update user info func (us *UserService) UpdateInfo(ctx context.Context, req *schema.UpdateInfoRequest) ( errFields []*validator.FormErrorField, err error) { - siteUsers, err := us.siteInfoService.GetSiteUsers(ctx) - if err != nil { - return nil, err - } - if siteUsers.AllowUpdateUsername && len(req.Username) > 0 { + if len(req.Username) > 0 { if checker.IsInvalidUsername(req.Username) { return append(errFields, &validator.FormErrorField{ ErrorField: "username", @@ -359,7 +355,7 @@ func (us *UserService) UpdateInfo(ctx context.Context, req *schema.UpdateInfoReq return nil, errors.BadRequest(reason.UserNotFound) } - cond := us.formatUserInfoForUpdateInfo(oldUserInfo, req, siteUsers) + cond := us.formatUserInfoForUpdateInfo(oldUserInfo, req) us.cleanUpRemovedAvatar(ctx, oldUserInfo.Avatar, cond.Avatar) @@ -407,7 +403,7 @@ func (us *UserService) cleanUpRemovedAvatar( } func (us *UserService) formatUserInfoForUpdateInfo( - oldUserInfo *entity.User, req *schema.UpdateInfoRequest, siteUsersConf *schema.SiteUsersResp) *entity.User { + oldUserInfo *entity.User, req *schema.UpdateInfoRequest) *entity.User { avatar, _ := json.Marshal(req.Avatar) userInfo := &entity.User{} @@ -420,25 +416,19 @@ func (us *UserService) formatUserInfoForUpdateInfo( userInfo.Location = oldUserInfo.Location userInfo.ID = req.UserID - if len(req.DisplayName) > 0 && siteUsersConf.AllowUpdateDisplayName { + if len(req.DisplayName) > 0 { userInfo.DisplayName = req.DisplayName } - if len(req.Username) > 0 && siteUsersConf.AllowUpdateUsername { + if len(req.Username) > 0 { userInfo.Username = req.Username } - if len(avatar) > 0 && siteUsersConf.AllowUpdateAvatar { + if len(avatar) > 0 { userInfo.Avatar = string(avatar) } - if siteUsersConf.AllowUpdateBio { - userInfo.Bio = req.Bio - userInfo.BioHTML = req.BioHTML - } - if siteUsersConf.AllowUpdateWebsite { - userInfo.Website = req.Website - } - if siteUsersConf.AllowUpdateLocation { - userInfo.Location = req.Location - } + userInfo.Bio = req.Bio + userInfo.BioHTML = req.BioHTML + userInfo.Website = req.Website + userInfo.Location = req.Location return userInfo } diff --git a/internal/service/siteinfo_common/siteinfo_service.go b/internal/service/siteinfo_common/siteinfo_service.go index 0c896c2b0..ef4869cc4 100644 --- a/internal/service/siteinfo_common/siteinfo_service.go +++ b/internal/service/siteinfo_common/siteinfo_service.go @@ -123,7 +123,7 @@ func (s *siteInfoCommonService) FormatListAvatar(ctx context.Context, userList [ func (s *siteInfoCommonService) getAvatarDefaultConfig(ctx context.Context) (string, string) { gravatarBaseURL, defaultAvatar := constant.DefaultGravatarBaseURL, constant.DefaultAvatar - usersConfig, err := s.GetSiteUsers(ctx) + usersConfig, err := s.GetSiteInterface(ctx) if err != nil { log.Error(err) } From 6d02706d4db67e735d0b69554c21b4417f1c7c67 Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 26 Jun 2025 14:41:32 +0800 Subject: [PATCH 18/34] feat(user): add user suspension functionality --- cmd/wire_gen.go | 2 +- docs/docs.go | 58 ++++++++++++++++++++ docs/swagger.json | 58 ++++++++++++++++++++ docs/swagger.yaml | 45 +++++++++++++++ internal/base/cron/cron.go | 17 ++++++ internal/entity/user_entity.go | 4 ++ internal/migrations/v27.go | 14 ++++- internal/repo/user/user_backyard_repo.go | 25 ++++++++- internal/schema/backyard_user_schema.go | 43 ++++++++++++++- internal/schema/user_schema.go | 32 ++++++++--- internal/service/user_admin/user_backyard.go | 41 +++++++++++++- internal/service/user_common/user.go | 3 + 12 files changed, 326 insertions(+), 16 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index f98deb424..947b6605d 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -291,7 +291,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, renderController := controller.NewRenderController() pluginAPIRouter := router.NewPluginAPIRouter(connectorController, userCenterController, captchaController, embedController, renderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, shortIDMiddleware, templateRouter, pluginAPIRouter, uiConf) - scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService, fileRecordService, serviceConf) + scheduledTaskManager := cron.NewScheduledTaskManager(siteInfoCommonService, questionService, fileRecordService, userAdminService, serviceConf) application := newApplication(serverConf, ginEngine, scheduledTaskManager) return application, func() { cleanup2() diff --git a/docs/docs.go b/docs/docs.go index 76750aee9..48c8b1b40 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -8755,6 +8755,10 @@ const docTemplate = `{ "description": "user status", "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" @@ -8871,6 +8875,10 @@ const docTemplate = `{ "status_msg": { "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" @@ -9520,6 +9528,10 @@ const docTemplate = `{ "description": "suspended time", "type": "integer" }, + "suspended_until": { + "description": "suspended until time", + "type": "integer" + }, "user_id": { "description": "user id", "type": "string" @@ -10696,10 +10708,21 @@ const docTemplate = `{ "schema.SiteInterfaceReq": { "type": "object", "required": [ + "default_avatar", "language", "time_zone" ], "properties": { + "default_avatar": { + "type": "string", + "enum": [ + "system", + "gravatar" + ] + }, + "gravatar_base_url": { + "type": "string" + }, "language": { "type": "string", "maxLength": 128 @@ -10713,10 +10736,21 @@ const docTemplate = `{ "schema.SiteInterfaceResp": { "type": "object", "required": [ + "default_avatar", "language", "time_zone" ], "properties": { + "default_avatar": { + "type": "string", + "enum": [ + "system", + "gravatar" + ] + }, + "gravatar_base_url": { + "type": "string" + }, "language": { "type": "string", "maxLength": 128 @@ -11621,6 +11655,22 @@ const docTemplate = `{ "inactive" ] }, + "suspend_duration": { + "type": "string", + "enum": [ + "24h", + "48h", + "72h", + "7d", + "14d", + "1m", + "2m", + "3m", + "6m", + "1y", + "forever" + ] + }, "user_id": { "type": "string" } @@ -11650,6 +11700,10 @@ const docTemplate = `{ "status": { "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "type": "string" }, @@ -11812,6 +11866,10 @@ const docTemplate = `{ "description": "user status", "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" diff --git a/docs/swagger.json b/docs/swagger.json index 4682f67c8..768869c3e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -8728,6 +8728,10 @@ "description": "user status", "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" @@ -8844,6 +8848,10 @@ "status_msg": { "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" @@ -9493,6 +9501,10 @@ "description": "suspended time", "type": "integer" }, + "suspended_until": { + "description": "suspended until time", + "type": "integer" + }, "user_id": { "description": "user id", "type": "string" @@ -10669,10 +10681,21 @@ "schema.SiteInterfaceReq": { "type": "object", "required": [ + "default_avatar", "language", "time_zone" ], "properties": { + "default_avatar": { + "type": "string", + "enum": [ + "system", + "gravatar" + ] + }, + "gravatar_base_url": { + "type": "string" + }, "language": { "type": "string", "maxLength": 128 @@ -10686,10 +10709,21 @@ "schema.SiteInterfaceResp": { "type": "object", "required": [ + "default_avatar", "language", "time_zone" ], "properties": { + "default_avatar": { + "type": "string", + "enum": [ + "system", + "gravatar" + ] + }, + "gravatar_base_url": { + "type": "string" + }, "language": { "type": "string", "maxLength": 128 @@ -11594,6 +11628,22 @@ "inactive" ] }, + "suspend_duration": { + "type": "string", + "enum": [ + "24h", + "48h", + "72h", + "7d", + "14d", + "1m", + "2m", + "3m", + "6m", + "1y", + "forever" + ] + }, "user_id": { "type": "string" } @@ -11623,6 +11673,10 @@ "status": { "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "type": "string" }, @@ -11785,6 +11839,10 @@ "description": "user status", "type": "string" }, + "suspended_until": { + "description": "suspended until timestamp", + "type": "integer" + }, "username": { "description": "username", "type": "string" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3d68f8a65..96042f5f8 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -873,6 +873,9 @@ definitions: status: description: user status type: string + suspended_until: + description: suspended until timestamp + type: integer username: description: username type: string @@ -959,6 +962,9 @@ definitions: type: string status_msg: type: string + suspended_until: + description: suspended until timestamp + type: integer username: description: username type: string @@ -1401,6 +1407,9 @@ definitions: suspended_at: description: suspended time type: integer + suspended_until: + description: suspended until time + type: integer user_id: description: user id type: string @@ -2214,6 +2223,13 @@ definitions: type: object schema.SiteInterfaceReq: properties: + default_avatar: + enum: + - system + - gravatar + type: string + gravatar_base_url: + type: string language: maxLength: 128 type: string @@ -2221,11 +2237,19 @@ definitions: maxLength: 128 type: string required: + - default_avatar - language - time_zone type: object schema.SiteInterfaceResp: properties: + default_avatar: + enum: + - system + - gravatar + type: string + gravatar_base_url: + type: string language: maxLength: 128 type: string @@ -2233,6 +2257,7 @@ definitions: maxLength: 128 type: string required: + - default_avatar - language - time_zone type: object @@ -2845,6 +2870,20 @@ definitions: - deleted - inactive type: string + suspend_duration: + enum: + - 24h + - 48h + - 72h + - 7d + - 14d + - 1m + - 2m + - 3m + - 6m + - 1y + - forever + type: string user_id: type: string required: @@ -2867,6 +2906,9 @@ definitions: type: integer status: type: string + suspended_until: + description: suspended until timestamp + type: integer username: type: string website: @@ -2984,6 +3026,9 @@ definitions: status: description: user status type: string + suspended_until: + description: suspended until timestamp + type: integer username: description: username type: string diff --git a/internal/base/cron/cron.go b/internal/base/cron/cron.go index 98e59decf..1d8008ad6 100644 --- a/internal/base/cron/cron.go +++ b/internal/base/cron/cron.go @@ -27,6 +27,7 @@ import ( "github.com/apache/answer/internal/service/file_record" "github.com/apache/answer/internal/service/service_config" "github.com/apache/answer/internal/service/siteinfo_common" + "github.com/apache/answer/internal/service/user_admin" "github.com/robfig/cron/v3" "github.com/segmentfault/pacman/log" ) @@ -36,6 +37,7 @@ type ScheduledTaskManager struct { siteInfoService siteinfo_common.SiteInfoCommonService questionService *content.QuestionService fileRecordService *file_record.FileRecordService + userAdminService *user_admin.UserAdminService serviceConfig *service_config.ServiceConfig } @@ -44,12 +46,14 @@ func NewScheduledTaskManager( siteInfoService siteinfo_common.SiteInfoCommonService, questionService *content.QuestionService, fileRecordService *file_record.FileRecordService, + userAdminService *user_admin.UserAdminService, serviceConfig *service_config.ServiceConfig, ) *ScheduledTaskManager { manager := &ScheduledTaskManager{ siteInfoService: siteInfoService, questionService: questionService, fileRecordService: fileRecordService, + userAdminService: userAdminService, serviceConfig: serviceConfig, } return manager @@ -78,6 +82,19 @@ func (s *ScheduledTaskManager) Run() { log.Error(err) } + // Check for expired user suspensions every 10 minutes + _, err = c.AddFunc("*/10 * * * *", func() { + ctx := context.Background() + log.Infof("checking expired user suspensions") + err := s.userAdminService.CheckAndUnsuspendExpiredUsers(ctx) + if err != nil { + log.Errorf("failed to check expired user suspensions: %v", err) + } + }) + if err != nil { + log.Error(err) + } + if s.serviceConfig.CleanUpUploads { log.Infof("clean up uploads cron enabled") diff --git a/internal/entity/user_entity.go b/internal/entity/user_entity.go index cfc2b6c29..66d612926 100644 --- a/internal/entity/user_entity.go +++ b/internal/entity/user_entity.go @@ -36,12 +36,16 @@ const ( UserAdminFlag = 1 ) +// PermanentSuspensionTime is a fixed time representing permanent suspension (2099-12-31 23:59:59) +var PermanentSuspensionTime = time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC) + // User user type User struct { ID string `xorm:"not null pk autoincr BIGINT(20) id"` CreatedAt time.Time `xorm:"created TIMESTAMP created_at"` UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"` SuspendedAt time.Time `xorm:"TIMESTAMP suspended_at"` + SuspendedUntil time.Time `xorm:"DATETIME suspended_until"` DeletedAt time.Time `xorm:"TIMESTAMP deleted_at"` LastLoginDate time.Time `xorm:"TIMESTAMP last_login_date"` Username string `xorm:"not null default '' VARCHAR(50) UNIQUE username"` diff --git a/internal/migrations/v27.go b/internal/migrations/v27.go index 7f089a874..a0c4ef8a1 100644 --- a/internal/migrations/v27.go +++ b/internal/migrations/v27.go @@ -23,6 +23,7 @@ import ( "context" "encoding/json" "fmt" + "time" "github.com/apache/answer/internal/base/constant" "github.com/apache/answer/internal/entity" @@ -30,7 +31,18 @@ import ( "xorm.io/xorm" ) +func addSuspendedUntilToUser(ctx context.Context, x *xorm.Engine) error { + type User struct { + SuspendedUntil *time.Time `xorm:"DATETIME suspended_until"` + } + return x.Context(ctx).Sync(new(User)) +} + func moveUserConfigToInterface(ctx context.Context, x *xorm.Engine) error { + if err := addSuspendedUntilToUser(ctx, x); err != nil { + return fmt.Errorf("add suspended_until to user failed: %w", err) + } + // Get old interface config interfaceSiteInfo := &entity.SiteInfo{Type: constant.SiteTypeInterface} exist, err := x.Context(ctx).Get(interfaceSiteInfo) @@ -42,7 +54,7 @@ func moveUserConfigToInterface(ctx context.Context, x *xorm.Engine) error { } interfaceConfig := &schema.SiteInterfaceReq{} - _ = json.Unmarshal([]byte(interfaceSiteInfo.Content), interfaceSiteInfo) + _ = json.Unmarshal([]byte(interfaceSiteInfo.Content), interfaceConfig) // Get old user config usersConfig := &entity.SiteInfo{Type: constant.SiteTypeUsers} diff --git a/internal/repo/user/user_backyard_repo.go b/internal/repo/user/user_backyard_repo.go index f5987df9e..c93845e97 100644 --- a/internal/repo/user/user_backyard_repo.go +++ b/internal/repo/user/user_backyard_repo.go @@ -52,16 +52,20 @@ func NewUserAdminRepo(data *data.Data, authRepo auth.AuthRepo) user_admin.UserAd // UpdateUserStatus update user status func (ur *userAdminRepo) UpdateUserStatus(ctx context.Context, userID string, userStatus, mailStatus int, - email string, + email string, suspendedUntil time.Time, ) (err error) { cond := &entity.User{Status: userStatus, MailStatus: mailStatus, EMail: email} switch userStatus { case entity.UserStatusSuspended: cond.SuspendedAt = time.Now() + cond.SuspendedUntil = suspendedUntil case entity.UserStatusDeleted: cond.DeletedAt = time.Now() + case entity.UserStatusAvailable: + // When restoring user status, clear suspended until time to zero + cond.SuspendedUntil = time.Time{} } - _, err = ur.data.DB.Context(ctx).ID(userID).Update(cond) + _, err = ur.data.DB.Context(ctx).ID(userID).MustCols("status", "mail_status", "e_mail", "suspended_at", "suspended_until", "deleted_at").Update(cond) if err != nil { return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } @@ -184,3 +188,20 @@ func (ur *userAdminRepo) DeletePermanentlyUsers(ctx context.Context) (err error) } return } + +// GetExpiredSuspendedUsers gets all suspended users whose suspension has expired +func (ur *userAdminRepo) GetExpiredSuspendedUsers(ctx context.Context) (users []*entity.User, err error) { + users = make([]*entity.User, 0) + now := time.Now() + + err = ur.data.DB.Context(ctx). + Where("status = ?", entity.UserStatusSuspended). + Where("suspended_until < ?", now). + Find(&users) + + if err != nil { + return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + + return users, nil +} diff --git a/internal/schema/backyard_user_schema.go b/internal/schema/backyard_user_schema.go index 923a20ed9..6ec848721 100644 --- a/internal/schema/backyard_user_schema.go +++ b/internal/schema/backyard_user_schema.go @@ -21,19 +21,23 @@ package schema import ( "context" + "strings" + "time" + "github.com/apache/answer/internal/base/constant" "github.com/apache/answer/internal/base/handler" "github.com/apache/answer/internal/base/reason" "github.com/apache/answer/internal/base/translator" "github.com/apache/answer/internal/base/validator" + "github.com/apache/answer/internal/entity" "github.com/segmentfault/pacman/errors" - "strings" ) // UpdateUserStatusReq update user request type UpdateUserStatusReq struct { UserID string `validate:"required" json:"user_id"` Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"` + SuspendDuration string `validate:"omitempty,oneof=24h 48h 72h 7d 14d 1m 2m 3m 6m 1y forever" json:"suspend_duration"` RemoveAllContent bool `validate:"omitempty" json:"remove_all_content"` LoginUserID string `json:"-"` } @@ -43,6 +47,39 @@ func (r *UpdateUserStatusReq) IsSuspended() bool { return r.Status == constant.U func (r *UpdateUserStatusReq) IsDeleted() bool { return r.Status == constant.UserDeleted } func (r *UpdateUserStatusReq) IsInactive() bool { return r.Status == constant.UserInactive } +// GetSuspendedUntil calculates the suspended until time based on duration +func (r *UpdateUserStatusReq) GetSuspendedUntil() time.Time { + if !r.IsSuspended() || r.SuspendDuration == "" || r.SuspendDuration == "forever" { + return entity.PermanentSuspensionTime // permanent suspension + } + + now := time.Now() + switch r.SuspendDuration { + case "24h": + return now.Add(24 * time.Hour) + case "48h": + return now.Add(48 * time.Hour) + case "72h": + return now.Add(72 * time.Hour) + case "7d": + return now.Add(7 * 24 * time.Hour) + case "14d": + return now.Add(14 * 24 * time.Hour) + case "1m": + return now.AddDate(0, 1, 0) + case "2m": + return now.AddDate(0, 2, 0) + case "3m": + return now.AddDate(0, 3, 0) + case "6m": + return now.AddDate(0, 6, 0) + case "1y": + return now.AddDate(1, 0, 0) + default: + return entity.PermanentSuspensionTime // fallback to permanent + } +} + // GetUserPageReq get user list page request type GetUserPageReq struct { // page @@ -71,6 +108,8 @@ type GetUserPageResp struct { DeletedAt int64 `json:"deleted_at"` // suspended time SuspendedAt int64 `json:"suspended_at"` + // suspended until time + SuspendedUntil int64 `json:"suspended_until"` // username Username string `json:"username"` // email @@ -96,6 +135,8 @@ type GetUserInfoReq struct { // GetUserInfoResp get user response type GetUserInfoResp struct { + // suspended until + SuspendedUntil time.Time `json:"suspended_until"` } // UpdateUserRoleReq update user role request diff --git a/internal/schema/user_schema.go b/internal/schema/user_schema.go index 7c0af1dc4..dd65cba90 100644 --- a/internal/schema/user_schema.go +++ b/internal/schema/user_schema.go @@ -96,6 +96,8 @@ type UserLoginResp struct { HavePassword bool `json:"have_password"` // visit token VisitToken string `json:"visit_token"` + // suspended until timestamp + SuspendedUntil int64 `json:"suspended_until"` } func (r *UserLoginResp) ConvertFromUserEntity(userInfo *entity.User) { @@ -104,6 +106,9 @@ func (r *UserLoginResp) ConvertFromUserEntity(userInfo *entity.User) { r.LastLoginDate = userInfo.LastLoginDate.Unix() r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) r.HavePassword = len(userInfo.Pass) > 0 + if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + r.SuspendedUntil = userInfo.SuspendedUntil.Unix() + } } type GetCurrentLoginUserInfoResp struct { @@ -119,6 +124,9 @@ func (r *GetCurrentLoginUserInfoResp) ConvertFromUserEntity(userInfo *entity.Use if len(r.ColorScheme) == 0 { r.ColorScheme = constant.ColorSchemeDefault } + if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + r.SuspendedUntil = userInfo.SuspendedUntil.Unix() + } } // GetOtherUserInfoByUsernameResp get user response @@ -156,6 +164,8 @@ type GetOtherUserInfoByUsernameResp struct { Location string `json:"location"` Status string `json:"status"` StatusMsg string `json:"status_msg,omitempty"` + // suspended until timestamp + SuspendedUntil int64 `json:"suspended_until"` } func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity.User) { @@ -163,6 +173,9 @@ func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity. r.CreatedAt = userInfo.CreatedAt.Unix() r.LastLoginDate = userInfo.LastLoginDate.Unix() r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) + if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + r.SuspendedUntil = userInfo.SuspendedUntil.Unix() + } if userInfo.MailStatus == entity.EmailStatusToBeVerified { statusMsgShow, ok := UserStatusShowMsg[11] if ok { @@ -348,15 +361,16 @@ type ActionRecordResp struct { } type UserBasicInfo struct { - ID string `json:"id"` - Username string `json:"username"` - Rank int `json:"rank"` - DisplayName string `json:"display_name"` - Avatar string `json:"avatar"` - Website string `json:"website"` - Location string `json:"location"` - Language string `json:"language"` - Status string `json:"status"` + ID string `json:"id"` + Username string `json:"username"` + Rank int `json:"rank"` + DisplayName string `json:"display_name"` + Avatar string `json:"avatar"` + Website string `json:"website"` + Location string `json:"location"` + Language string `json:"language"` + Status string `json:"status"` + SuspendedUntil int64 `json:"suspended_until"` } type GetOtherUserInfoByUsernameReq struct { diff --git a/internal/service/user_admin/user_backyard.go b/internal/service/user_admin/user_backyard.go index f7dc0eae6..9992f8f8b 100644 --- a/internal/service/user_admin/user_backyard.go +++ b/internal/service/user_admin/user_backyard.go @@ -59,7 +59,7 @@ import ( // UserAdminRepo user repository type UserAdminRepo interface { - UpdateUserStatus(ctx context.Context, userID string, userStatus, mailStatus int, email string) (err error) + UpdateUserStatus(ctx context.Context, userID string, userStatus, mailStatus int, email string, suspendedUntil time.Time) (err error) GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error) GetUserInfoByEmail(ctx context.Context, email string) (user *entity.User, exist bool, err error) GetUserPage(ctx context.Context, page, pageSize int, user *entity.User, @@ -68,6 +68,7 @@ type UserAdminRepo interface { AddUsers(ctx context.Context, users []*entity.User) (err error) UpdateUserPassword(ctx context.Context, userID string, password string) (err error) DeletePermanentlyUsers(ctx context.Context) (err error) + GetExpiredSuspendedUsers(ctx context.Context) (users []*entity.User, err error) } // UserAdminService user service @@ -156,7 +157,8 @@ func (us *UserAdminService) UpdateUserStatus(ctx context.Context, req *schema.Up userInfo.MailStatus = entity.EmailStatusAvailable } - err = us.userRepo.UpdateUserStatus(ctx, userInfo.ID, userInfo.Status, userInfo.MailStatus, userInfo.EMail) + suspendedUntil := req.GetSuspendedUntil() + err = us.userRepo.UpdateUserStatus(ctx, userInfo.ID, userInfo.Status, userInfo.MailStatus, userInfo.EMail, suspendedUntil) if err != nil { return err } @@ -525,6 +527,9 @@ func (us *UserAdminService) GetUserPage(ctx context.Context, req *schema.GetUser } else if u.Status == entity.UserStatusSuspended { t.Status = constant.UserSuspended t.SuspendedAt = u.SuspendedAt.Unix() + if !u.SuspendedUntil.IsZero() && u.SuspendedUntil != entity.PermanentSuspensionTime { + t.SuspendedUntil = u.SuspendedUntil.Unix() + } } else if u.MailStatus == entity.EmailStatusToBeVerified { t.Status = constant.UserInactive } else { @@ -625,3 +630,35 @@ func (us *UserAdminService) DeletePermanently(ctx context.Context, req *schema.D return errors.BadRequest(reason.RequestFormatError) } + +// CheckAndUnsuspendExpiredUsers checks for users whose suspension has expired and restores them to normal status +func (us *UserAdminService) CheckAndUnsuspendExpiredUsers(ctx context.Context) error { + // Find all suspended users whose suspension time has expired + expiredUsers, err := us.userRepo.GetExpiredSuspendedUsers(ctx) + if err != nil { + return err + } + + now := time.Now() + for _, user := range expiredUsers { + // Check if suspension has expired (not permanent and time has passed) + if user.Status == entity.UserStatusSuspended && + !user.SuspendedUntil.IsZero() && + user.SuspendedUntil.Before(now) { + + log.Infof("Unsuspending user %s (ID: %s) - suspension expired at %v", + user.Username, user.ID, user.SuspendedUntil) + + // Update user status to normal + err = us.userRepo.UpdateUserStatus(ctx, user.ID, entity.UserStatusAvailable, + entity.EmailStatusAvailable, user.EMail, time.Time{}) + if err != nil { + log.Errorf("Failed to unsuspend user %s (ID: %s): %v", + user.Username, user.ID, err) + continue + } + } + } + + return nil +} diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index 3df99261d..166bf9556 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -180,6 +180,9 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity. userBasicInfo.Location = userInfo.Location userBasicInfo.Language = userInfo.Language userBasicInfo.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) + if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + userBasicInfo.SuspendedUntil = userInfo.SuspendedUntil.Unix() + } if userBasicInfo.Status == constant.UserDeleted { userBasicInfo.Avatar = "" userBasicInfo.DisplayName = "user" + converter.DeleteUserDisplay(userInfo.ID) From 19eca49d2a8e1a6b6fa67268061567421a2fcd04 Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 26 Jun 2025 14:55:35 +0800 Subject: [PATCH 19/34] fix: move the settings -> users section in admin to Interface #1360 --- ui/src/common/constants.ts | 44 +++- ui/src/common/interface.ts | 4 +- ui/src/pages/Admin/Interface/index.tsx | 51 +++- ui/src/pages/Admin/SettingsUsers/index.tsx | 218 ------------------ ui/src/pages/Admin/Users/index.tsx | 45 +++- ui/src/pages/Users/Settings/Profile/index.tsx | 24 +- ui/src/router/routes.ts | 4 - ui/src/services/admin/settings.ts | 2 - ui/src/stores/interface.ts | 1 + ui/src/stores/siteInfo.ts | 2 - 10 files changed, 141 insertions(+), 254 deletions(-) delete mode 100644 ui/src/pages/Admin/SettingsUsers/index.tsx diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index 163d4989a..7ca57400e 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -126,7 +126,6 @@ export const ADMIN_NAV_MENUS = [ { name: 'write' }, { name: 'seo' }, { name: 'login' }, - { name: 'users', path: 'settings-users' }, { name: 'privileges' }, ], }, @@ -660,3 +659,46 @@ export const SYSTEM_AVATAR_OPTIONS = [ export const TAG_SLUG_NAME_MAX_LENGTH = 35; export const DEFAULT_THEME_COLOR = '#0033ff'; + +export const SUSPENSE_USER_TIME = [ + { + label: 'hours', + value: '24', + }, + { + label: 'hours', + value: '48', + }, + { + label: 'hours', + value: '72', + }, + { + label: 'days', + value: '7', + }, + { + label: 'days', + value: '14', + }, + { + label: 'months', + value: '1', + }, + { + label: 'months', + value: '2', + }, + { + label: 'months', + value: '3', + }, + { + label: 'months', + value: '6', + }, + { + label: 'year', + value: '1', + }, +]; diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 3935c4391..114b0e4ce 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -382,6 +382,8 @@ export interface HelmetUpdate extends Omit { export interface AdminSettingsInterface { language: string; time_zone?: string; + default_avatar: string; + gravatar_base_url: string; } export interface AdminSettingsSmtp { @@ -403,8 +405,6 @@ export interface AdminSettingsUsers { allow_update_location: boolean; allow_update_username: boolean; allow_update_website: boolean; - default_avatar: string; - gravatar_base_url: string; } export interface SiteSettings { diff --git a/ui/src/pages/Admin/Interface/index.tsx b/ui/src/pages/Admin/Interface/index.tsx index 9c6220489..865029e9c 100644 --- a/ui/src/pages/Admin/Interface/index.tsx +++ b/ui/src/pages/Admin/Interface/index.tsx @@ -28,7 +28,7 @@ import { } from '@/common/interface'; import { interfaceStore, loggedUserInfoStore } from '@/stores'; import { JSONSchema, SchemaForm, UISchema } from '@/components'; -import { DEFAULT_TIMEZONE } from '@/common/constants'; +import { DEFAULT_TIMEZONE, SYSTEM_AVATAR_OPTIONS } from '@/common/constants'; import { updateInterfaceSetting, useInterfaceSetting, @@ -59,7 +59,8 @@ const Interface: FC = () => { description: t('language.text'), enum: langs?.map((lang) => lang.value), enumNames: langs?.map((lang) => lang.label), - default: setting?.language || storeInterface.language, + default: + setting?.language || storeInterface.language || langs?.[0]?.value, }, time_zone: { type: 'string', @@ -67,12 +68,26 @@ const Interface: FC = () => { description: t('time_zone.text'), default: setting?.time_zone || DEFAULT_TIMEZONE, }, + default_avatar: { + type: 'string', + title: t('avatar.label'), + description: t('avatar.text'), + enum: SYSTEM_AVATAR_OPTIONS?.map((v) => v.value), + enumNames: SYSTEM_AVATAR_OPTIONS?.map((v) => v.label), + default: setting?.default_avatar || 'system', + }, + gravatar_base_url: { + type: 'string', + title: t('gravatar_base_url.label'), + description: t('gravatar_base_url.text'), + default: setting?.gravatar_base_url || '', + }, }, }; const [formData, setFormData] = useState({ language: { - value: setting?.language || storeInterface.language, + value: setting?.language || storeInterface.language || langs?.[0]?.value, isInvalid: false, errorMsg: '', }, @@ -81,6 +96,16 @@ const Interface: FC = () => { isInvalid: false, errorMsg: '', }, + default_avatar: { + value: setting?.default_avatar || 'system', + isInvalid: false, + errorMsg: '', + }, + gravatar_base_url: { + value: setting?.gravatar_base_url || '', + isInvalid: false, + errorMsg: '', + }, }); const uiSchema: UISchema = { @@ -90,6 +115,15 @@ const Interface: FC = () => { time_zone: { 'ui:widget': 'timezone', }, + default_avatar: { + 'ui:widget': 'select', + }, + gravatar_base_url: { + 'ui:widget': 'input', + 'ui:options': { + placeholder: 'https://www.gravatar.com/avatar/', + }, + }, }; const getLangs = async () => { const res: LangsType[] = await loadLanguageOptions(true); @@ -122,6 +156,8 @@ const Interface: FC = () => { const reqParams: AdminSettingsInterface = { language: formData.language.value, time_zone: formData.time_zone.value, + default_avatar: formData.default_avatar.value, + gravatar_base_url: formData.gravatar_base_url.value, }; updateInterfaceSetting(reqParams) @@ -151,7 +187,14 @@ const Interface: FC = () => { if (setting) { const formMeta = {}; Object.keys(setting).forEach((k) => { - formMeta[k] = { ...formData[k], value: setting[k] }; + let v = setting[k]; + if (k === 'default_avatar' && !v) { + v = 'system'; + } + if (k === 'gravatar_base_url' && !v) { + v = ''; + } + formMeta[k] = { ...formData[k], value: v }; }); setFormData({ ...formData, ...formMeta }); } diff --git a/ui/src/pages/Admin/SettingsUsers/index.tsx b/ui/src/pages/Admin/SettingsUsers/index.tsx deleted file mode 100644 index e09809806..000000000 --- a/ui/src/pages/Admin/SettingsUsers/index.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { FC, FormEvent, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useToast } from '@/hooks'; -import { FormDataType } from '@/common/interface'; -import { JSONSchema, SchemaForm, UISchema, initFormData } from '@/components'; -import { SYSTEM_AVATAR_OPTIONS } from '@/common/constants'; -import { - getUsersSetting, - putUsersSetting, - AdminSettingsUsers, -} from '@/services'; -import { handleFormError, scrollToElementTop } from '@/utils'; -import * as Type from '@/common/interface'; -import { siteInfoStore } from '@/stores'; - -const Index: FC = () => { - const { t } = useTranslation('translation', { - keyPrefix: 'admin.settings_users', - }); - const Toast = useToast(); - const { updateUsers: updateUsersStore } = siteInfoStore(); - const schema: JSONSchema = { - title: t('title'), - properties: { - default_avatar: { - type: 'string', - title: t('avatar.label'), - description: t('avatar.text'), - enum: SYSTEM_AVATAR_OPTIONS?.map((v) => v.value), - enumNames: SYSTEM_AVATAR_OPTIONS?.map((v) => v.label), - default: 'system', - }, - gravatar_base_url: { - type: 'string', - title: t('gravatar_base_url.label'), - description: t('gravatar_base_url.text'), - }, - profile_editable: { - type: 'string', - title: t('profile_editable.title'), - }, - allow_update_display_name: { - type: 'boolean', - title: 'allow_update_display_name', - }, - allow_update_username: { - type: 'boolean', - title: 'allow_update_username', - }, - allow_update_avatar: { - type: 'boolean', - title: 'allow_update_avatar', - }, - allow_update_bio: { - type: 'boolean', - title: 'allow_update_bio', - }, - allow_update_website: { - type: 'boolean', - title: 'allow_update_website', - }, - allow_update_location: { - type: 'boolean', - title: 'allow_update_location', - }, - }, - }; - - const [formData, setFormData] = useState(initFormData(schema)); - - const uiSchema: UISchema = { - default_avatar: { - 'ui:widget': 'select', - }, - gravatar_base_url: { - 'ui:widget': 'input', - 'ui:options': { - placeholder: 'https://www.gravatar.com/avatar/', - }, - }, - profile_editable: { - 'ui:widget': 'legend', - }, - allow_update_display_name: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_display_name.label'), - simplify: true, - }, - }, - allow_update_username: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_username.label'), - simplify: true, - }, - }, - allow_update_avatar: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_avatar.label'), - simplify: true, - }, - }, - allow_update_bio: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_bio.label'), - simplify: true, - }, - }, - allow_update_website: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_website.label'), - simplify: true, - }, - }, - allow_update_location: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_location.label'), - field_class_name: 'mb-3', - simplify: true, - }, - }, - }; - - const onSubmit = (evt: FormEvent) => { - evt.preventDefault(); - evt.stopPropagation(); - const reqParams: AdminSettingsUsers = { - allow_update_avatar: formData.allow_update_avatar.value, - allow_update_bio: formData.allow_update_bio.value, - allow_update_display_name: formData.allow_update_display_name.value, - allow_update_location: formData.allow_update_location.value, - allow_update_username: formData.allow_update_username.value, - allow_update_website: formData.allow_update_website.value, - default_avatar: formData.default_avatar.value, - gravatar_base_url: formData.gravatar_base_url.value, - }; - putUsersSetting(reqParams) - .then(() => { - updateUsersStore(reqParams); - Toast.onShow({ - msg: t('update', { keyPrefix: 'toast' }), - variant: 'success', - }); - }) - .catch((err) => { - if (err.isError) { - const data = handleFormError(err, formData); - setFormData({ ...data }); - const ele = document.getElementById(err.list[0].error_field); - scrollToElementTop(ele); - } - }); - }; - - useEffect(() => { - getUsersSetting().then((resp) => { - if (!resp) { - return; - } - const formMeta: Type.FormDataType = {}; - Object.keys(formData).forEach((k) => { - let v = resp[k]; - if (k === 'default_avatar' && !v) { - v = 'system'; - } - if (k === 'gravatar_base_url' && !v) { - v = ''; - } - formMeta[k] = { ...formData[k], value: v }; - }); - setFormData({ ...formData, ...formMeta }); - }); - }, []); - - const handleOnChange = (data) => { - setFormData(data); - }; - - return ( - <> -

{t('title')}

- - - ); -}; - -export default Index; diff --git a/ui/src/pages/Admin/Users/index.tsx b/ui/src/pages/Admin/Users/index.tsx index 2402a839c..295cb075f 100644 --- a/ui/src/pages/Admin/Users/index.tsx +++ b/ui/src/pages/Admin/Users/index.tsx @@ -47,6 +47,7 @@ import { formatCount } from '@/utils'; import DeleteUserModal from './components/DeleteUserModal'; import Action from './components/Action'; +import SuspenseUserModal from './components/SuspenseUserModal'; const UserFilterKeys: Type.UserFilterBy[] = [ 'normal', @@ -70,6 +71,10 @@ const Users: FC = () => { show: false, userId: '', }); + const [suspenseUserModalState, setSuspenseUserModalState] = useState({ + show: false, + userId: '', + }); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); const curFilter = urlSearchParams.get('filter') || UserFilterKeys[0]; const curPage = Number(urlSearchParams.get('page') || '1'); @@ -172,6 +177,13 @@ const Users: FC = () => { }); }; + const handleSuspenseUserModalState = (modalData: { + show: boolean; + userId: string; + }) => { + setSuspenseUserModalState(modalData); + }; + const showAddUser = !ucAgent?.enabled || (ucAgent?.enabled && adminUcAgent?.allow_create_user); const showActionPassword = @@ -231,17 +243,22 @@ const Users: FC = () => { {t('name')} {t('reputation')} - + {t('email')} - + {t('created_at')} {(curFilter === 'deleted' || curFilter === 'suspended') && ( - + {curFilter === 'deleted' ? t('delete_at') : t('suspend_at')} )} + {curFilter === 'suspended' && ( + + {t('suspend_until')} + + )} {t('status')} {curFilter !== 'suspended' && curFilter !== 'deleted' && ( @@ -275,9 +292,14 @@ const Users: FC = () => { {curFilter === 'suspended' && ( - - - + <> + + + + + + + )} {curFilter === 'deleted' && ( @@ -306,6 +328,7 @@ const Users: FC = () => { currentUser={currentUser} refreshUsers={refreshUsers} showDeleteModal={changeDeleteUserModalState} + showSuspenseModal={handleSuspenseUserModalState} /> ) : null} @@ -332,6 +355,16 @@ const Users: FC = () => { }} onDelete={(val) => handleDelete(val)} /> + { + handleSuspenseUserModalState({ + show: false, + userId: '', + }); + }} + onDelete={(val) => handleDelete(val)} + /> ); }; diff --git a/ui/src/pages/Users/Settings/Profile/index.tsx b/ui/src/pages/Users/Settings/Profile/index.tsx index 9af28ce6e..7fb8247c1 100644 --- a/ui/src/pages/Users/Settings/Profile/index.tsx +++ b/ui/src/pages/Users/Settings/Profile/index.tsx @@ -25,7 +25,7 @@ import { sha256 } from 'js-sha256'; import type { FormDataType } from '@/common/interface'; import { UploadImg, Avatar, Icon, ImgViewer } from '@/components'; -import { loggedUserInfoStore, userCenterStore, siteInfoStore } from '@/stores'; +import { loggedUserInfoStore, userCenterStore, interfaceStore } from '@/stores'; import { useToast } from '@/hooks'; import { modifyUserInfo, @@ -42,7 +42,7 @@ const Index: React.FC = () => { const toast = useToast(); const { user, update } = loggedUserInfoStore(); const { agent: ucAgent } = userCenterStore(); - const { users: usersSetting } = siteInfoStore(); + const { interface: interfaceSetting } = interfaceStore(); const [mailHash, setMailHash] = useState(''); const [count] = useState(0); const [profileAgent, setProfileAgent] = useState(); @@ -309,7 +309,6 @@ const Index: React.FC = () => { @@ -332,7 +331,6 @@ const Index: React.FC = () => { @@ -356,7 +354,6 @@ const Index: React.FC = () => {
- {usersSetting.gravatar_base_url.includes('gravatar.cn') + {interfaceSetting.gravatar_base_url.includes( + 'gravatar.cn', + ) ? 'gravatar.cn' : 'gravatar.com'} @@ -413,15 +414,11 @@ const Index: React.FC = () => { alt={formData.display_name.value} /> - + @@ -463,7 +460,6 @@ const Index: React.FC = () => { required as="textarea" rows={5} - disabled={!usersSetting.allow_update_bio} value={formData.bio.value} isInvalid={formData.bio.isInvalid} onChange={(e) => @@ -489,7 +485,6 @@ const Index: React.FC = () => { required type="url" placeholder={t('website.placeholder')} - disabled={!usersSetting.allow_update_website} value={formData.website.value} isInvalid={formData.website.isInvalid} onChange={(e) => @@ -515,7 +510,6 @@ const Index: React.FC = () => { required type="text" placeholder={t('location.placeholder')} - disabled={!usersSetting.allow_update_location} value={formData.location.value} isInvalid={formData.location.isInvalid} onChange={(e) => diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index d9d20a44a..59907b418 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -398,10 +398,6 @@ const routes: RouteNode[] = [ path: 'login', page: 'pages/Admin/Login', }, - { - path: 'settings-users', - page: 'pages/Admin/SettingsUsers', - }, { path: 'privileges', page: 'pages/Admin/Privileges', diff --git a/ui/src/services/admin/settings.ts b/ui/src/services/admin/settings.ts index 7ce57ade1..f2b9e598c 100644 --- a/ui/src/services/admin/settings.ts +++ b/ui/src/services/admin/settings.ts @@ -29,8 +29,6 @@ export interface AdminSettingsUsers { allow_update_location: boolean; allow_update_username: boolean; allow_update_website: boolean; - default_avatar: string; - gravatar_base_url: string; } interface PrivilegeLevel { diff --git a/ui/src/stores/interface.ts b/ui/src/stores/interface.ts index 7b514eb66..cceb425cd 100644 --- a/ui/src/stores/interface.ts +++ b/ui/src/stores/interface.ts @@ -32,6 +32,7 @@ const interfaceSetting = create((set) => ({ language: DEFAULT_LANG, time_zone: '', default_avatar: 'system', + gravatar_base_url: '', }, update: (params) => set(() => { diff --git a/ui/src/stores/siteInfo.ts b/ui/src/stores/siteInfo.ts index 5208b9b4b..725546d64 100644 --- a/ui/src/stores/siteInfo.ts +++ b/ui/src/stores/siteInfo.ts @@ -39,8 +39,6 @@ const defaultUsersConf: AdminSettingsUsers = { allow_update_location: false, allow_update_username: false, allow_update_website: false, - default_avatar: 'system', - gravatar_base_url: 'https://www.gravatar.com/avatar/', }; const siteInfo = create((set) => ({ From 3b8b5871a6191163901287383bcc7b764944bf6f Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 26 Jun 2025 16:10:09 +0800 Subject: [PATCH 20/34] feat(user): add user status messages and support localization --- i18n/en_US.yaml | 8 ++++ i18n/zh_CN.yaml | 8 ++++ internal/base/reason/reason.go | 4 ++ internal/schema/user_schema.go | 50 ++++++++++++++---------- internal/service/content/user_service.go | 2 +- 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 1791d85c5..67d3e3a92 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -307,6 +307,14 @@ backend: other: "Error {{.Field}} format near '{{.Content}}' at line {{.Line}}. {{.ExtraMessage}}" add_bulk_users_amount_error: other: "The number of users you add at once should be in the range of 1-{{.MaxAmount}}." + status_suspended_forever: + other: "This user was suspended forever. This user doesn't meet a community guideline." + status_suspended_until: + other: "This user was suspended until {{.SuspendedUntil}}. This user doesn't meet a community guideline." + status_deleted: + other: "This user was deleted." + status_inactive: + other: "This user is inactive." config: read_config_failed: other: Read config failed diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 6b153e0a5..7a94840ef 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -306,6 +306,14 @@ backend: other: "发生错误,{{.Field}} 格式错误,在 '{{.Content}}' 行数 {{.Line}}. {{.ExtraMessage}}" add_bulk_users_amount_error: other: "一次性添加的用户数量应在 1-{{.MaxAmount}} 之间。" + status_suspended_forever: + other: "该用户已被永久封禁。该用户不符合社区准则。" + status_suspended_until: + other: "该用户已被封禁至 {{.SuspendedUntil}}。该用户不符合社区准则。" + status_deleted: + other: "该用户已被删除。" + status_inactive: + other: "该用户未激活。" config: read_config_failed: other: 读取配置失败 diff --git a/internal/base/reason/reason.go b/internal/base/reason/reason.go index f318e3359..953c316ed 100644 --- a/internal/base/reason/reason.go +++ b/internal/base/reason/reason.go @@ -111,6 +111,10 @@ const ( MetaObjectNotFound = "error.meta.object_not_found" BadgeObjectNotFound = "error.badge.object_not_found" StatusInvalid = "error.common.status_invalid" + UserStatusInactive = "error.user.status_inactive" + UserStatusSuspendedForever = "error.user.status_suspended_forever" + UserStatusSuspendedUntil = "error.user.status_suspended_until" + UserStatusDeleted = "error.user.status_deleted" ) // user external login reasons diff --git a/internal/schema/user_schema.go b/internal/schema/user_schema.go index dd65cba90..a320ca9b8 100644 --- a/internal/schema/user_schema.go +++ b/internal/schema/user_schema.go @@ -20,10 +20,13 @@ package schema import ( + "context" "encoding/json" + "github.com/apache/answer/internal/base/handler" "github.com/apache/answer/internal/base/reason" "github.com/apache/answer/internal/base/translator" + "github.com/apache/answer/pkg/day" "github.com/segmentfault/pacman/errors" "github.com/apache/answer/internal/base/constant" @@ -176,29 +179,36 @@ func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity. if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { r.SuspendedUntil = userInfo.SuspendedUntil.Unix() } - if userInfo.MailStatus == entity.EmailStatusToBeVerified { - statusMsgShow, ok := UserStatusShowMsg[11] - if ok { - r.StatusMsg = statusMsgShow - } - } else { - statusMsgShow, ok := UserStatusShowMsg[userInfo.Status] - if ok { - r.StatusMsg = statusMsgShow - } - } + r.StatusMsg = "" } -const ( - NoticeStatusOn = 1 - NoticeStatusOff = 2 -) +func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntityWithLang(ctx context.Context, userInfo *entity.User) { + _ = copier.Copy(r, userInfo) + r.CreatedAt = userInfo.CreatedAt.Unix() + r.LastLoginDate = userInfo.LastLoginDate.Unix() + r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) + if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + r.SuspendedUntil = userInfo.SuspendedUntil.Unix() + } -var UserStatusShowMsg = map[int]string{ - 1: "", - 9: "This user was suspended forever. This user doesn't meet a community guideline.", - 10: "This user was deleted.", - 11: "This user is inactive.", + lang := handler.GetLangByCtx(ctx) + if userInfo.MailStatus == entity.EmailStatusToBeVerified { + r.StatusMsg = translator.Tr(lang, reason.UserStatusInactive) + } + switch userInfo.Status { + case entity.UserStatusSuspended: + if userInfo.SuspendedUntil.IsZero() || userInfo.SuspendedUntil == entity.PermanentSuspensionTime { + r.StatusMsg = translator.Tr(lang, reason.UserStatusSuspendedForever) + } else { + trans := translator.GlobalTrans.Tr(lang, "ui.dates.long_date_with_year") + suspendedUntilFormatted := day.Format(userInfo.SuspendedUntil.Unix(), trans, "UTC") + r.StatusMsg = translator.TrWithData(lang, reason.UserStatusSuspendedUntil, map[string]interface{}{ + "SuspendedUntil": suspendedUntilFormatted, + }) + } + case entity.UserStatusDeleted: + r.StatusMsg = translator.Tr(lang, reason.UserStatusDeleted) + } } // UserEmailLoginReq user email login request diff --git a/internal/service/content/user_service.go b/internal/service/content/user_service.go index 1c94d22d8..81f7ac824 100644 --- a/internal/service/content/user_service.go +++ b/internal/service/content/user_service.go @@ -141,7 +141,7 @@ func (us *UserService) GetOtherUserInfoByUsername(ctx context.Context, req *sche return nil, errors.NotFound(reason.UserNotFound) } resp = &schema.GetOtherUserInfoByUsernameResp{} - resp.ConvertFromUserEntity(userInfo) + resp.ConvertFromUserEntityWithLang(ctx, userInfo) resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL() // Only the user himself and the administrator can see the hidden questions From b78be61ee84463afa6e8bfb79fbe17926ec15f8c Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 26 Jun 2025 16:42:24 +0800 Subject: [PATCH 21/34] refactor(user): simplify suspended until logic --- internal/schema/user_schema.go | 14 ++++++-------- internal/service/user_admin/user_backyard.go | 2 +- internal/service/user_common/user.go | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/internal/schema/user_schema.go b/internal/schema/user_schema.go index a320ca9b8..7ba8817a8 100644 --- a/internal/schema/user_schema.go +++ b/internal/schema/user_schema.go @@ -109,7 +109,7 @@ func (r *UserLoginResp) ConvertFromUserEntity(userInfo *entity.User) { r.LastLoginDate = userInfo.LastLoginDate.Unix() r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) r.HavePassword = len(userInfo.Pass) > 0 - if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + if !userInfo.SuspendedUntil.IsZero() { r.SuspendedUntil = userInfo.SuspendedUntil.Unix() } } @@ -127,7 +127,7 @@ func (r *GetCurrentLoginUserInfoResp) ConvertFromUserEntity(userInfo *entity.Use if len(r.ColorScheme) == 0 { r.ColorScheme = constant.ColorSchemeDefault } - if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + if !userInfo.SuspendedUntil.IsZero() { r.SuspendedUntil = userInfo.SuspendedUntil.Unix() } } @@ -176,7 +176,7 @@ func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntity(userInfo *entity. r.CreatedAt = userInfo.CreatedAt.Unix() r.LastLoginDate = userInfo.LastLoginDate.Unix() r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) - if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + if !userInfo.SuspendedUntil.IsZero() { r.SuspendedUntil = userInfo.SuspendedUntil.Unix() } r.StatusMsg = "" @@ -187,9 +187,6 @@ func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntityWithLang(ctx conte r.CreatedAt = userInfo.CreatedAt.Unix() r.LastLoginDate = userInfo.LastLoginDate.Unix() r.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) - if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { - r.SuspendedUntil = userInfo.SuspendedUntil.Unix() - } lang := handler.GetLangByCtx(ctx) if userInfo.MailStatus == entity.EmailStatusToBeVerified { @@ -197,10 +194,11 @@ func (r *GetOtherUserInfoByUsernameResp) ConvertFromUserEntityWithLang(ctx conte } switch userInfo.Status { case entity.UserStatusSuspended: - if userInfo.SuspendedUntil.IsZero() || userInfo.SuspendedUntil == entity.PermanentSuspensionTime { + if userInfo.SuspendedUntil.IsZero() || userInfo.SuspendedUntil.Year() >= 2099 { r.StatusMsg = translator.Tr(lang, reason.UserStatusSuspendedForever) } else { - trans := translator.GlobalTrans.Tr(lang, "ui.dates.long_date_with_year") + r.SuspendedUntil = userInfo.SuspendedUntil.Unix() + trans := translator.GlobalTrans.Tr(lang, "ui.dates.long_date_with_time") suspendedUntilFormatted := day.Format(userInfo.SuspendedUntil.Unix(), trans, "UTC") r.StatusMsg = translator.TrWithData(lang, reason.UserStatusSuspendedUntil, map[string]interface{}{ "SuspendedUntil": suspendedUntilFormatted, diff --git a/internal/service/user_admin/user_backyard.go b/internal/service/user_admin/user_backyard.go index 9992f8f8b..0f6ec81ed 100644 --- a/internal/service/user_admin/user_backyard.go +++ b/internal/service/user_admin/user_backyard.go @@ -527,7 +527,7 @@ func (us *UserAdminService) GetUserPage(ctx context.Context, req *schema.GetUser } else if u.Status == entity.UserStatusSuspended { t.Status = constant.UserSuspended t.SuspendedAt = u.SuspendedAt.Unix() - if !u.SuspendedUntil.IsZero() && u.SuspendedUntil != entity.PermanentSuspensionTime { + if !u.SuspendedUntil.IsZero() { t.SuspendedUntil = u.SuspendedUntil.Unix() } } else if u.MailStatus == entity.EmailStatusToBeVerified { diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index 166bf9556..124972a16 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -180,7 +180,7 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity. userBasicInfo.Location = userInfo.Location userBasicInfo.Language = userInfo.Language userBasicInfo.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus) - if !userInfo.SuspendedUntil.IsZero() && userInfo.SuspendedUntil != entity.PermanentSuspensionTime { + if !userInfo.SuspendedUntil.IsZero() { userBasicInfo.SuspendedUntil = userInfo.SuspendedUntil.Unix() } if userBasicInfo.Status == constant.UserDeleted { From 17e1b301752cd136e98cf83e81bae89cd03a3007 Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 26 Jun 2025 16:44:42 +0800 Subject: [PATCH 22/34] feat: Blocked users can choose the duration of the block #1361 --- i18n/en_US.yaml | 12 +++ i18n/zh_CN.yaml | 12 +++ ui/src/common/constants.ts | 30 ++++--- ui/src/components/Header/index.tsx | 1 - .../Admin/Users/components/Action/index.tsx | 16 ++-- .../components/SuspenseUserModal/index.tsx | 90 +++++++++++++++++++ ui/src/pages/Admin/Users/index.tsx | 13 ++- 7 files changed, 150 insertions(+), 24 deletions(-) create mode 100644 ui/src/pages/Admin/Users/components/SuspenseUserModal/index.tsx diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index cb05e5e13..3889a12a9 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1070,6 +1070,9 @@ ui: day: day hours: hours days: days + month: month + months: months + year: year reaction: heart: heart smile: smile @@ -1930,6 +1933,7 @@ ui: created_at: Created time delete_at: Deleted time suspend_at: Suspended time + suspend_until: Suspend until status: Status role: Role action: Action @@ -1964,6 +1968,8 @@ ui: suspend_user: title: Suspend this user content: A suspended user can't log in. + label: How long will the user be suspended for? + forever: Forever questions: page_title: Questions unlisted: Unlisted @@ -2024,6 +2030,12 @@ ui: label: Timezone msg: Timezone cannot be empty. text: Choose a city in the same timezone as you. + avatar: + label: Default avatar + text: For users without a custom avatar of their own. + gravatar_base_url: + label: Gravatar base URL + text: URL of the Gravatar provider's API base. Ignored when empty. smtp: page_title: SMTP from_email: diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 9852ba605..ce5fbb072 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -1056,6 +1056,9 @@ ui: day: 天 hours: 小时 days: 日 + month: 月 + months: 月 + year: 年 reaction: heart: 爱心 smile: 微笑 @@ -1890,6 +1893,7 @@ ui: created_at: 创建时间 delete_at: 删除时间 suspend_at: 封禁时间 + suspend_until: 封禁到期 status: 状态 role: 角色 action: 操作 @@ -1924,6 +1928,8 @@ ui: suspend_user: title: 挂起此用户 content: 被封禁的用户将无法登录。 + label: 用户将被封禁多长时间? + forever: 永久 questions: page_title: 问题 unlisted: 已隐藏 @@ -1984,6 +1990,12 @@ ui: label: 时区 msg: 时区不能为空。 text: 选择一个与您相同时区的城市。 + avatar: + label: 默认头像 + text: 没有自定义头像的用户。 + gravatar_base_url: + label: Gravatar 根路径 URL + text: Gravatar 提供商的 API 基础的 URL。当为空时忽略。 smtp: page_title: SMTP from_email: diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index 7ca57400e..18f251145 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -663,42 +663,52 @@ export const DEFAULT_THEME_COLOR = '#0033ff'; export const SUSPENSE_USER_TIME = [ { label: 'hours', - value: '24', + time: '24', + value: '24h', }, { label: 'hours', - value: '48', + time: '48', + value: '48h', }, { label: 'hours', - value: '72', + time: '72', + value: '72h', }, { label: 'days', - value: '7', + time: '7', + value: '7d', }, { label: 'days', - value: '14', + time: '14', + value: '14d', }, { label: 'months', - value: '1', + time: '1', + value: '1m', }, { label: 'months', - value: '2', + time: '2', + value: '2m', }, { label: 'months', - value: '3', + time: '3', + value: '3m', }, { label: 'months', - value: '6', + time: '6', + value: '6m', }, { label: 'year', - value: '1', + time: '1', + value: '1y', }, ]; diff --git a/ui/src/components/Header/index.tsx b/ui/src/components/Header/index.tsx index 0b015ba68..dbc7565d0 100644 --- a/ui/src/components/Header/index.tsx +++ b/ui/src/components/Header/index.tsx @@ -87,7 +87,6 @@ const Header: FC = () => { if (theme_config?.[theme]?.navbar_style) { // const color = theme_config[theme].navbar_style.startsWith('#') themeMode = isLight(theme_config[theme].navbar_style) ? 'light' : 'dark'; - console.log('isLightTheme', themeMode); navbarStyle = `theme-${themeMode}`; } diff --git a/ui/src/pages/Admin/Users/components/Action/index.tsx b/ui/src/pages/Admin/Users/components/Action/index.tsx index 9f0847a21..b55971f49 100644 --- a/ui/src/pages/Admin/Users/components/Action/index.tsx +++ b/ui/src/pages/Admin/Users/components/Action/index.tsx @@ -42,6 +42,7 @@ interface Props { currentUser; refreshUsers: () => void; showDeleteModal: (val) => void; + showSuspenseModal: (val) => void; userData; } @@ -53,6 +54,7 @@ const UserOperation = ({ refreshUsers, showDeleteModal, userData, + showSuspenseModal, }: Props) => { const { t } = useTranslation('translation', { keyPrefix: 'admin.users' }); const Toast = useToast(); @@ -170,17 +172,9 @@ const UserOperation = ({ if (type === 'suspend') { // cons - Modal.confirm({ - title: t('suspend_user.title'), - content: t('suspend_user.content'), - cancelBtnVariant: 'link', - cancelText: t('cancel', { keyPrefix: 'btns' }), - confirmBtnVariant: 'danger', - confirmText: t('suspend', { keyPrefix: 'btns' }), - onConfirm: () => { - // active -> suspended - postUserStatus('suspended'); - }, + showSuspenseModal({ + show: true, + userId: userData.user_id, }); } diff --git a/ui/src/pages/Admin/Users/components/SuspenseUserModal/index.tsx b/ui/src/pages/Admin/Users/components/SuspenseUserModal/index.tsx new file mode 100644 index 000000000..dc59ff345 --- /dev/null +++ b/ui/src/pages/Admin/Users/components/SuspenseUserModal/index.tsx @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useState } from 'react'; +import { Modal, Button, Form } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; + +import { changeUserStatus } from '@/services'; +import { SUSPENSE_USER_TIME } from '@/common/constants'; +import { toastStore } from '@/stores'; + +const SuspenseUserModal = ({ show, userId, onClose, refreshUsers }) => { + const { t } = useTranslation('translation', { keyPrefix: 'admin.users' }); + const [checkVal, setCheckVal] = useState('forever'); + + const handleClose = () => { + onClose(); + setCheckVal('forever'); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + changeUserStatus({ + user_id: userId, + status: 'suspended', + suspend_duration: checkVal, + }).then(() => { + toastStore.getState().show({ + msg: t('user_suspended', { keyPrefix: 'messages' }), + variant: 'success', + }); + refreshUsers?.(); + handleClose(); + }); + }; + + return ( + + + {t('suspend_user.title')} + + +

{t('suspend_user.content')}

+
+ + {t('suspend_user.label')} + setCheckVal(e.target.value)}> + + {SUSPENSE_USER_TIME.map((item) => { + return ( + + ); + })} + + +
+
+ + + + +
+ ); +}; + +export default SuspenseUserModal; diff --git a/ui/src/pages/Admin/Users/index.tsx b/ui/src/pages/Admin/Users/index.tsx index 295cb075f..7e8e4fd61 100644 --- a/ui/src/pages/Admin/Users/index.tsx +++ b/ui/src/pages/Admin/Users/index.tsx @@ -23,6 +23,7 @@ import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; +import dayjs from 'dayjs'; import { Pagination, @@ -297,7 +298,14 @@ const Users: FC = () => { - + {user.suspended_until <= 0 || + Number( + dayjs(user.suspended_until * 1000).format('YYYY'), + ) > 2099 + ? t('suspend_user.forever') + : dayjs(user.suspended_until * 1000).format( + t('long_date_with_time', { keyPrefix: 'dates' }), + )} )} @@ -357,13 +365,14 @@ const Users: FC = () => { /> { handleSuspenseUserModalState({ show: false, userId: '', }); }} - onDelete={(val) => handleDelete(val)} + refreshUsers={refreshUsers} /> ); From 2bc3167dbd2d0d26a9bad74c084f3b80f9b72e26 Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 26 Jun 2025 17:01:14 +0800 Subject: [PATCH 23/34] fix: Navigation dropdown menu theme color should follow the theme color of the application #1355 --- ui/src/components/Header/components/NavItems/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/components/Header/components/NavItems/index.tsx b/ui/src/components/Header/components/NavItems/index.tsx index 3dbed4e4b..ce373867e 100644 --- a/ui/src/components/Header/components/NavItems/index.tsx +++ b/ui/src/components/Header/components/NavItems/index.tsx @@ -24,7 +24,7 @@ import { NavLink, useNavigate } from 'react-router-dom'; import type * as Type from '@/common/interface'; import { Avatar, Icon } from '@/components'; -import { floppyNavigation } from '@/utils'; +import { floppyNavigation, isDarkTheme } from '@/utils'; import { userCenterStore } from '@/stores'; import { REACT_BASE_PATH } from '@/router/alias'; @@ -80,7 +80,7 @@ const Index: FC = ({ redDot, userInfo, logOut }) => { - + Date: Fri, 30 May 2025 10:57:15 +0800 Subject: [PATCH 24/34] fix: optimization hr's style --- ui/src/index.scss | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/ui/src/index.scss b/ui/src/index.scss index 97aa7b1fd..b299a993e 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -208,6 +208,39 @@ img[src=''] { .fmt { width: 100%; + hr { + height: 1.5rem; + border: none; + position: relative; + overflow: visible; + text-align: center; + opacity: 1; + flex: none; + &::before { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(calc(-50% - 16px), -50%); + width: 3px; + height: 3px; + background-color: var(--bs-border-color); + border-radius: 50%; + } + + &::after { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + width: 3px; + height: 3px; + background-color: var(--bs-border-color); + border-radius: 50%; + box-shadow: 16px 0 0 var(--bs-border-color); + } + } h1 { @extend .fs-3; margin-top: 2rem; From 60f87fd294e169e7ce471d593c529d5319e7f766 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 May 2025 11:08:58 +0800 Subject: [PATCH 25/34] fix: optimization hr's style --- ui/src/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/index.scss b/ui/src/index.scss index b299a993e..59de8d9ab 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -209,7 +209,7 @@ img[src=''] { .fmt { width: 100%; hr { - height: 1.5rem; + height: 3px; border: none; position: relative; overflow: visible; From d2768b1f11463553d032168bd9fccaa88cb37155 Mon Sep 17 00:00:00 2001 From: shuai Date: Fri, 30 May 2025 11:22:00 +0800 Subject: [PATCH 26/34] fix: hr tag's margin use 1.5rem --- ui/src/index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/index.scss b/ui/src/index.scss index 59de8d9ab..fb1e06a79 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -216,6 +216,7 @@ img[src=''] { text-align: center; opacity: 1; flex: none; + margin: 1.5rem 0; &::before { content: ''; position: absolute; From 8560c89e062b5d6e167f393952d5af5d3e7635c0 Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 26 Jun 2025 14:55:35 +0800 Subject: [PATCH 27/34] fix: move the settings -> users section in admin to Interface #1360 --- ui/src/common/constants.ts | 44 +++- ui/src/common/interface.ts | 4 +- ui/src/pages/Admin/Interface/index.tsx | 51 +++- ui/src/pages/Admin/SettingsUsers/index.tsx | 218 ------------------ ui/src/pages/Admin/Users/index.tsx | 45 +++- ui/src/pages/Users/Settings/Profile/index.tsx | 24 +- ui/src/router/routes.ts | 4 - ui/src/services/admin/settings.ts | 2 - ui/src/stores/interface.ts | 1 + ui/src/stores/siteInfo.ts | 2 - 10 files changed, 141 insertions(+), 254 deletions(-) delete mode 100644 ui/src/pages/Admin/SettingsUsers/index.tsx diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index 163d4989a..7ca57400e 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -126,7 +126,6 @@ export const ADMIN_NAV_MENUS = [ { name: 'write' }, { name: 'seo' }, { name: 'login' }, - { name: 'users', path: 'settings-users' }, { name: 'privileges' }, ], }, @@ -660,3 +659,46 @@ export const SYSTEM_AVATAR_OPTIONS = [ export const TAG_SLUG_NAME_MAX_LENGTH = 35; export const DEFAULT_THEME_COLOR = '#0033ff'; + +export const SUSPENSE_USER_TIME = [ + { + label: 'hours', + value: '24', + }, + { + label: 'hours', + value: '48', + }, + { + label: 'hours', + value: '72', + }, + { + label: 'days', + value: '7', + }, + { + label: 'days', + value: '14', + }, + { + label: 'months', + value: '1', + }, + { + label: 'months', + value: '2', + }, + { + label: 'months', + value: '3', + }, + { + label: 'months', + value: '6', + }, + { + label: 'year', + value: '1', + }, +]; diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 3935c4391..114b0e4ce 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -382,6 +382,8 @@ export interface HelmetUpdate extends Omit { export interface AdminSettingsInterface { language: string; time_zone?: string; + default_avatar: string; + gravatar_base_url: string; } export interface AdminSettingsSmtp { @@ -403,8 +405,6 @@ export interface AdminSettingsUsers { allow_update_location: boolean; allow_update_username: boolean; allow_update_website: boolean; - default_avatar: string; - gravatar_base_url: string; } export interface SiteSettings { diff --git a/ui/src/pages/Admin/Interface/index.tsx b/ui/src/pages/Admin/Interface/index.tsx index 9c6220489..865029e9c 100644 --- a/ui/src/pages/Admin/Interface/index.tsx +++ b/ui/src/pages/Admin/Interface/index.tsx @@ -28,7 +28,7 @@ import { } from '@/common/interface'; import { interfaceStore, loggedUserInfoStore } from '@/stores'; import { JSONSchema, SchemaForm, UISchema } from '@/components'; -import { DEFAULT_TIMEZONE } from '@/common/constants'; +import { DEFAULT_TIMEZONE, SYSTEM_AVATAR_OPTIONS } from '@/common/constants'; import { updateInterfaceSetting, useInterfaceSetting, @@ -59,7 +59,8 @@ const Interface: FC = () => { description: t('language.text'), enum: langs?.map((lang) => lang.value), enumNames: langs?.map((lang) => lang.label), - default: setting?.language || storeInterface.language, + default: + setting?.language || storeInterface.language || langs?.[0]?.value, }, time_zone: { type: 'string', @@ -67,12 +68,26 @@ const Interface: FC = () => { description: t('time_zone.text'), default: setting?.time_zone || DEFAULT_TIMEZONE, }, + default_avatar: { + type: 'string', + title: t('avatar.label'), + description: t('avatar.text'), + enum: SYSTEM_AVATAR_OPTIONS?.map((v) => v.value), + enumNames: SYSTEM_AVATAR_OPTIONS?.map((v) => v.label), + default: setting?.default_avatar || 'system', + }, + gravatar_base_url: { + type: 'string', + title: t('gravatar_base_url.label'), + description: t('gravatar_base_url.text'), + default: setting?.gravatar_base_url || '', + }, }, }; const [formData, setFormData] = useState({ language: { - value: setting?.language || storeInterface.language, + value: setting?.language || storeInterface.language || langs?.[0]?.value, isInvalid: false, errorMsg: '', }, @@ -81,6 +96,16 @@ const Interface: FC = () => { isInvalid: false, errorMsg: '', }, + default_avatar: { + value: setting?.default_avatar || 'system', + isInvalid: false, + errorMsg: '', + }, + gravatar_base_url: { + value: setting?.gravatar_base_url || '', + isInvalid: false, + errorMsg: '', + }, }); const uiSchema: UISchema = { @@ -90,6 +115,15 @@ const Interface: FC = () => { time_zone: { 'ui:widget': 'timezone', }, + default_avatar: { + 'ui:widget': 'select', + }, + gravatar_base_url: { + 'ui:widget': 'input', + 'ui:options': { + placeholder: 'https://www.gravatar.com/avatar/', + }, + }, }; const getLangs = async () => { const res: LangsType[] = await loadLanguageOptions(true); @@ -122,6 +156,8 @@ const Interface: FC = () => { const reqParams: AdminSettingsInterface = { language: formData.language.value, time_zone: formData.time_zone.value, + default_avatar: formData.default_avatar.value, + gravatar_base_url: formData.gravatar_base_url.value, }; updateInterfaceSetting(reqParams) @@ -151,7 +187,14 @@ const Interface: FC = () => { if (setting) { const formMeta = {}; Object.keys(setting).forEach((k) => { - formMeta[k] = { ...formData[k], value: setting[k] }; + let v = setting[k]; + if (k === 'default_avatar' && !v) { + v = 'system'; + } + if (k === 'gravatar_base_url' && !v) { + v = ''; + } + formMeta[k] = { ...formData[k], value: v }; }); setFormData({ ...formData, ...formMeta }); } diff --git a/ui/src/pages/Admin/SettingsUsers/index.tsx b/ui/src/pages/Admin/SettingsUsers/index.tsx deleted file mode 100644 index e09809806..000000000 --- a/ui/src/pages/Admin/SettingsUsers/index.tsx +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { FC, FormEvent, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useToast } from '@/hooks'; -import { FormDataType } from '@/common/interface'; -import { JSONSchema, SchemaForm, UISchema, initFormData } from '@/components'; -import { SYSTEM_AVATAR_OPTIONS } from '@/common/constants'; -import { - getUsersSetting, - putUsersSetting, - AdminSettingsUsers, -} from '@/services'; -import { handleFormError, scrollToElementTop } from '@/utils'; -import * as Type from '@/common/interface'; -import { siteInfoStore } from '@/stores'; - -const Index: FC = () => { - const { t } = useTranslation('translation', { - keyPrefix: 'admin.settings_users', - }); - const Toast = useToast(); - const { updateUsers: updateUsersStore } = siteInfoStore(); - const schema: JSONSchema = { - title: t('title'), - properties: { - default_avatar: { - type: 'string', - title: t('avatar.label'), - description: t('avatar.text'), - enum: SYSTEM_AVATAR_OPTIONS?.map((v) => v.value), - enumNames: SYSTEM_AVATAR_OPTIONS?.map((v) => v.label), - default: 'system', - }, - gravatar_base_url: { - type: 'string', - title: t('gravatar_base_url.label'), - description: t('gravatar_base_url.text'), - }, - profile_editable: { - type: 'string', - title: t('profile_editable.title'), - }, - allow_update_display_name: { - type: 'boolean', - title: 'allow_update_display_name', - }, - allow_update_username: { - type: 'boolean', - title: 'allow_update_username', - }, - allow_update_avatar: { - type: 'boolean', - title: 'allow_update_avatar', - }, - allow_update_bio: { - type: 'boolean', - title: 'allow_update_bio', - }, - allow_update_website: { - type: 'boolean', - title: 'allow_update_website', - }, - allow_update_location: { - type: 'boolean', - title: 'allow_update_location', - }, - }, - }; - - const [formData, setFormData] = useState(initFormData(schema)); - - const uiSchema: UISchema = { - default_avatar: { - 'ui:widget': 'select', - }, - gravatar_base_url: { - 'ui:widget': 'input', - 'ui:options': { - placeholder: 'https://www.gravatar.com/avatar/', - }, - }, - profile_editable: { - 'ui:widget': 'legend', - }, - allow_update_display_name: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_display_name.label'), - simplify: true, - }, - }, - allow_update_username: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_username.label'), - simplify: true, - }, - }, - allow_update_avatar: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_avatar.label'), - simplify: true, - }, - }, - allow_update_bio: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_bio.label'), - simplify: true, - }, - }, - allow_update_website: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_website.label'), - simplify: true, - }, - }, - allow_update_location: { - 'ui:widget': 'switch', - 'ui:options': { - label: t('allow_update_location.label'), - field_class_name: 'mb-3', - simplify: true, - }, - }, - }; - - const onSubmit = (evt: FormEvent) => { - evt.preventDefault(); - evt.stopPropagation(); - const reqParams: AdminSettingsUsers = { - allow_update_avatar: formData.allow_update_avatar.value, - allow_update_bio: formData.allow_update_bio.value, - allow_update_display_name: formData.allow_update_display_name.value, - allow_update_location: formData.allow_update_location.value, - allow_update_username: formData.allow_update_username.value, - allow_update_website: formData.allow_update_website.value, - default_avatar: formData.default_avatar.value, - gravatar_base_url: formData.gravatar_base_url.value, - }; - putUsersSetting(reqParams) - .then(() => { - updateUsersStore(reqParams); - Toast.onShow({ - msg: t('update', { keyPrefix: 'toast' }), - variant: 'success', - }); - }) - .catch((err) => { - if (err.isError) { - const data = handleFormError(err, formData); - setFormData({ ...data }); - const ele = document.getElementById(err.list[0].error_field); - scrollToElementTop(ele); - } - }); - }; - - useEffect(() => { - getUsersSetting().then((resp) => { - if (!resp) { - return; - } - const formMeta: Type.FormDataType = {}; - Object.keys(formData).forEach((k) => { - let v = resp[k]; - if (k === 'default_avatar' && !v) { - v = 'system'; - } - if (k === 'gravatar_base_url' && !v) { - v = ''; - } - formMeta[k] = { ...formData[k], value: v }; - }); - setFormData({ ...formData, ...formMeta }); - }); - }, []); - - const handleOnChange = (data) => { - setFormData(data); - }; - - return ( - <> -

{t('title')}

- - - ); -}; - -export default Index; diff --git a/ui/src/pages/Admin/Users/index.tsx b/ui/src/pages/Admin/Users/index.tsx index 2402a839c..295cb075f 100644 --- a/ui/src/pages/Admin/Users/index.tsx +++ b/ui/src/pages/Admin/Users/index.tsx @@ -47,6 +47,7 @@ import { formatCount } from '@/utils'; import DeleteUserModal from './components/DeleteUserModal'; import Action from './components/Action'; +import SuspenseUserModal from './components/SuspenseUserModal'; const UserFilterKeys: Type.UserFilterBy[] = [ 'normal', @@ -70,6 +71,10 @@ const Users: FC = () => { show: false, userId: '', }); + const [suspenseUserModalState, setSuspenseUserModalState] = useState({ + show: false, + userId: '', + }); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); const curFilter = urlSearchParams.get('filter') || UserFilterKeys[0]; const curPage = Number(urlSearchParams.get('page') || '1'); @@ -172,6 +177,13 @@ const Users: FC = () => { }); }; + const handleSuspenseUserModalState = (modalData: { + show: boolean; + userId: string; + }) => { + setSuspenseUserModalState(modalData); + }; + const showAddUser = !ucAgent?.enabled || (ucAgent?.enabled && adminUcAgent?.allow_create_user); const showActionPassword = @@ -231,17 +243,22 @@ const Users: FC = () => { {t('name')} {t('reputation')} - + {t('email')} - + {t('created_at')} {(curFilter === 'deleted' || curFilter === 'suspended') && ( - + {curFilter === 'deleted' ? t('delete_at') : t('suspend_at')} )} + {curFilter === 'suspended' && ( + + {t('suspend_until')} + + )} {t('status')} {curFilter !== 'suspended' && curFilter !== 'deleted' && ( @@ -275,9 +292,14 @@ const Users: FC = () => { {curFilter === 'suspended' && ( - - - + <> + + + + + + + )} {curFilter === 'deleted' && ( @@ -306,6 +328,7 @@ const Users: FC = () => { currentUser={currentUser} refreshUsers={refreshUsers} showDeleteModal={changeDeleteUserModalState} + showSuspenseModal={handleSuspenseUserModalState} /> ) : null} @@ -332,6 +355,16 @@ const Users: FC = () => { }} onDelete={(val) => handleDelete(val)} /> + { + handleSuspenseUserModalState({ + show: false, + userId: '', + }); + }} + onDelete={(val) => handleDelete(val)} + /> ); }; diff --git a/ui/src/pages/Users/Settings/Profile/index.tsx b/ui/src/pages/Users/Settings/Profile/index.tsx index 9af28ce6e..7fb8247c1 100644 --- a/ui/src/pages/Users/Settings/Profile/index.tsx +++ b/ui/src/pages/Users/Settings/Profile/index.tsx @@ -25,7 +25,7 @@ import { sha256 } from 'js-sha256'; import type { FormDataType } from '@/common/interface'; import { UploadImg, Avatar, Icon, ImgViewer } from '@/components'; -import { loggedUserInfoStore, userCenterStore, siteInfoStore } from '@/stores'; +import { loggedUserInfoStore, userCenterStore, interfaceStore } from '@/stores'; import { useToast } from '@/hooks'; import { modifyUserInfo, @@ -42,7 +42,7 @@ const Index: React.FC = () => { const toast = useToast(); const { user, update } = loggedUserInfoStore(); const { agent: ucAgent } = userCenterStore(); - const { users: usersSetting } = siteInfoStore(); + const { interface: interfaceSetting } = interfaceStore(); const [mailHash, setMailHash] = useState(''); const [count] = useState(0); const [profileAgent, setProfileAgent] = useState(); @@ -309,7 +309,6 @@ const Index: React.FC = () => { @@ -332,7 +331,6 @@ const Index: React.FC = () => { @@ -356,7 +354,6 @@ const Index: React.FC = () => {
diff --git a/ui/src/pages/Search/components/SearchItem/index.tsx b/ui/src/pages/Search/components/SearchItem/index.tsx index 2b0f002b0..e74caa4bf 100644 --- a/ui/src/pages/Search/components/SearchItem/index.tsx +++ b/ui/src/pages/Search/components/SearchItem/index.tsx @@ -81,11 +81,7 @@ const Index: FC = ({ data }) => { - + = ({ visible, data }) => { - const { t } = useTranslation('translation', { keyPrefix: 'personal' }); if (!visible || !data?.length) { return null; } @@ -53,11 +51,7 @@ const Index: FC = ({ visible, data }) => {
- + = ({ visible, tabName, data }) => { tabName === 'bookmarks' ? item.create_time : item.created_at } className="me-3" - preFix={t('asked')} /> Date: Wed, 2 Jul 2025 17:01:33 +0800 Subject: [PATCH 31/34] fix: adjustment of the position of plug-in inserting the editor toolbar --- ui/src/components/PluginRender/index.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/ui/src/components/PluginRender/index.tsx b/ui/src/components/PluginRender/index.tsx index 002d5a585..057f49521 100644 --- a/ui/src/components/PluginRender/index.tsx +++ b/ui/src/components/PluginRender/index.tsx @@ -20,7 +20,6 @@ import React, { FC, ReactNode } from 'react'; import PluginKit, { Plugin, PluginType } from '@/utils/pluginKit'; -import { writeSettingStore } from '@/stores'; /** * Note:Please set at least either of the `slug_name` and `type` attributes, otherwise no plugins will be rendered. * @@ -48,9 +47,6 @@ const Index: FC = ({ }) => { const pluginSlice: Plugin[] = []; const plugins = PluginKit.getPlugins().filter((plugin) => plugin.activated); - const { authorized_attachment_extensions = [] } = writeSettingStore( - (state) => state.write, - ); plugins.forEach((plugin) => { if (type && slug_name) { @@ -80,10 +76,9 @@ const Index: FC = ({ } if (type === 'editor') { - const showAttachFile = authorized_attachment_extensions?.length > 0; - const pendIndex = showAttachFile ? 16 : 15; + // index 16 is the position of the toolbar in the editor for plugins const nodes = React.Children.map(children, (child, index) => { - if (index === pendIndex) { + if (index === 16) { return ( <> {child} From 1871e2e580c6f3aa53e7f1746519e1eea155db0a Mon Sep 17 00:00:00 2001 From: shuai Date: Thu, 3 Jul 2025 11:13:59 +0800 Subject: [PATCH 32/34] fix: optimize the copy --- i18n/en_US.yaml | 19 ++++++++++--------- i18n/zh_CN.yaml | 19 ++++++++++--------- ui/src/components/QuestionList/index.tsx | 4 +++- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 9587de24d..da34fcb16 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -828,7 +828,7 @@ ui: tag_wiki: tag wiki create_tag: Create Tag edit_tag: Edit Tag - ask_a_question: Add Question + ask_a_question: Create Question edit_question: Edit Question edit_answer: Edit Answer search: Search @@ -1139,10 +1139,10 @@ ui: more: More wiki: Wiki ask: - title: Add Question + title: Create Question edit_title: Edit Question default_reason: Edit question - default_first_reason: Add question + default_first_reason: Create question similar_questions: Similar questions form: fields: @@ -1150,7 +1150,7 @@ ui: label: Revision title: label: Title - placeholder: Be specific and imagine you're asking a question to another person + placeholder: What's your topic? Be specific. msg: empty: Title cannot be empty. range: Title up to 150 characters @@ -1179,7 +1179,7 @@ ui: add_btn: Add tag create_btn: Create new tag search_tag: Search tag - hint: "Describe what your question is about, at least one tag is required." + hint: "Describe what your content is about, at least one tag is required." no_result: No tags matched tag_required_text: Required tag (at least one) header: @@ -1393,12 +1393,12 @@ ui: review: Your revision will show after review. sent_success: Sent successfully related_question: - title: Related Questions + title: Related answers: answers linked_question: - title: Linked Questions - description: Questions linked to - no_linked_question: No questions linked from this question. + title: Linked + description: Posts linked to + no_linked_question: No contents linked from this content. invite_to_answer: title: Invite People desc: Invite people you think can answer. @@ -1598,6 +1598,7 @@ ui: all_questions: All Questions x_questions: "{{ count }} Questions" x_answers: "{{ count }} answers" + x_posts: "{{ count }} Posts" questions: Questions answers: Answers newest: Newest diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index fdc0958f3..f1365b53d 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -817,7 +817,7 @@ ui: tag_wiki: 标签维基 create_tag: 创建标签 edit_tag: 编辑标签 - ask_a_question: 提问题 + ask_a_question: 创建问题 edit_question: 编辑问题 edit_answer: 编辑回答 search: 搜索 @@ -1122,10 +1122,10 @@ ui: more: 更多 wiki: 维基 ask: - title: 新增问题 + title: 创建问题 edit_title: 编辑问题 default_reason: 编辑问题 - default_first_reason: 新增问题 + default_first_reason: 创建问题 similar_questions: 相似问题 form: fields: @@ -1133,7 +1133,7 @@ ui: label: 修订版本 title: label: 标题 - placeholder: 请详细描述你的问题,想象你在问一个人 + placeholder: 你的主题是什么?请具体说明。 msg: empty: 标题不能为空。 range: 标题最多 150 个字符 @@ -1161,7 +1161,7 @@ ui: add_btn: 添加标签 create_btn: 创建新标签 search_tag: 搜索标签 - hint: "描述您的问题是关于什么,至少需要一个标签。" + hint: "描述您的内容是关于什么,至少需要一个标签。" no_result: 没有匹配的标签 tag_required_text: 必选标签(至少一个) header: @@ -1367,12 +1367,12 @@ ui: review: 您的修订将在审阅通过后显示。 sent_success: 发送成功 related_question: - title: 相关问题 + title: 相似 answers: 个回答 linked_question: - title: 关联的问题 - description: 问题关联到 - no_linked_question: 这个问题没有相关联的问题。 + title: 关联 + description: 帖子关联到 + no_linked_question: 没有与之关联的贴子。 invite_to_answer: title: 受邀人 desc: 邀请你认为可能知道答案的人。 @@ -1564,6 +1564,7 @@ ui: all_questions: 全部问题 x_questions: "{{ count }} 个问题" x_answers: "{{ count }} 个回答" + x_posts: "{{ count }} 个帖子" questions: 问题 answers: 回答 newest: 最新 diff --git a/ui/src/components/QuestionList/index.tsx b/ui/src/components/QuestionList/index.tsx index 06e8fe9e2..3d770dbe5 100644 --- a/ui/src/components/QuestionList/index.tsx +++ b/ui/src/components/QuestionList/index.tsx @@ -103,7 +103,9 @@ const QuestionList: FC = ({
{source === 'questions' ? t('all_questions') - : t('x_questions', { count })} + : source === 'linked' + ? t('x_posts', { count }) + : t('x_questions', { count })}
Date: Thu, 17 Jul 2025 15:52:25 +0800 Subject: [PATCH 33/34] docs: remove description for suspended_until field --- docs/docs.go | 1 - docs/swagger.json | 1 - docs/swagger.yaml | 1 - 3 files changed, 3 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 48c8b1b40..263e6775e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -11701,7 +11701,6 @@ const docTemplate = `{ "type": "string" }, "suspended_until": { - "description": "suspended until timestamp", "type": "integer" }, "username": { diff --git a/docs/swagger.json b/docs/swagger.json index 768869c3e..8cc85e263 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -11674,7 +11674,6 @@ "type": "string" }, "suspended_until": { - "description": "suspended until timestamp", "type": "integer" }, "username": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 96042f5f8..9cdf248e1 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -2907,7 +2907,6 @@ definitions: status: type: string suspended_until: - description: suspended until timestamp type: integer username: type: string From fa7ff6a9c696d6480c276472659a1064a8105b2f Mon Sep 17 00:00:00 2001 From: LinkinStars Date: Thu, 17 Jul 2025 15:54:28 +0800 Subject: [PATCH 34/34] docs(Makefile): upgrade version to 1.6.0 --- Makefile | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d88d5085b..0319a0198 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build clean ui -VERSION=1.5.1 +VERSION=1.6.0 BIN=answer DIR_SRC=./cmd/answer DOCKER_CMD=docker diff --git a/README.md b/README.md index 8fbb325b7..a05b68f81 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ To learn more about the project, visit [answer.apache.org](https://answer.apache ### Running with docker ```bash -docker run -d -p 9080:80 -v answer-data:/data --name answer apache/answer:1.5.1 +docker run -d -p 9080:80 -v answer-data:/data --name answer apache/answer:1.6.0 ``` For more information, see [Installation](https://answer.apache.org/docs/installation).