From 436829c2d1d1c285ea35b2bef56f2527a48cebbe Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Fri, 25 Nov 2022 11:40:54 +0800 Subject: [PATCH 001/272] update tag show --- internal/repo/tag_common/tag_common_repo.go | 15 +++++++++------ internal/service/tag_common/tag_common.go | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/repo/tag_common/tag_common_repo.go b/internal/repo/tag_common/tag_common_repo.go index 50f8517f..3d372618 100644 --- a/internal/repo/tag_common/tag_common_repo.go +++ b/internal/repo/tag_common/tag_common_repo.go @@ -65,13 +65,16 @@ func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, limi cond.Recommend = true } session.Where(builder.Eq{"status": entity.TagStatusAvailable}) + // if limit == 0 { + // session.Asc("slug_name") + // } session.Limit(limit).Asc("slug_name") - if !hasReserved { - cond.Reserved = false - session.UseBool("recommend", "reserved") - } else { - session.UseBool("recommend") - } + // if !hasReserved { + // cond.Reserved = false + // session.UseBool("recommend", "reserved") + // } else { + session.UseBool("recommend") + // } err = session.OrderBy("recommend desc,reserved desc,id desc").Find(&tagList, cond) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() diff --git a/internal/service/tag_common/tag_common.go b/internal/service/tag_common/tag_common.go index 42a156a7..35721c1a 100644 --- a/internal/service/tag_common/tag_common.go +++ b/internal/service/tag_common/tag_common.go @@ -64,7 +64,7 @@ func NewTagCommonService(tagCommonRepo TagCommonRepo, tagRelRepo TagRelRepo, // SearchTagLike get tag list all func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []schema.SearchTagLikeResp, err error) { - tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, 5, req.IsAdmin) + tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, 0, req.IsAdmin) if err != nil { return } From 6e8cfb4ac54092d941fb033fd124f0df195fc67e Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Fri, 25 Nov 2022 18:00:50 +0800 Subject: [PATCH 002/272] fix: update the initial reputation value --- internal/migrations/init.go | 48 +++++++++++++++++++++---------------- internal/migrations/v3.go | 2 +- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/internal/migrations/init.go b/internal/migrations/init.go index 9c4bb522..d3a518d7 100644 --- a/internal/migrations/init.go +++ b/internal/migrations/init.go @@ -191,26 +191,26 @@ func initConfigTable(engine *xorm.Engine) error { {ID: 32, Key: "question.follow", Value: `0`}, {ID: 33, Key: "email.config", Value: `{"from_name":"","from_email":"","smtp_host":"","smtp_port":465,"smtp_password":"","smtp_username":"","smtp_authentication":true,"encryption":"","register_title":"[{{.SiteName}}] Confirm your new account","register_body":"Welcome to {{.SiteName}}

\n\nClick the following link to confirm and activate your new account:
\n{{.RegisterUrl}}

\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n","pass_reset_title":"[{{.SiteName }}] Password reset","pass_reset_body":"Somebody asked to reset your password on [{{.SiteName}}].

\n\nIf it was not you, you can safely ignore this email.

\n\nClick the following link to choose a new password:
\n{{.PassResetUrl}}\n","change_title":"[{{.SiteName}}] Confirm your new email address","change_body":"Confirm your new email address for {{.SiteName}} by clicking on the following link:

\n\n{{.ChangeEmailUrl}}

\n\nIf you did not request this change, please ignore this email.\n","test_title":"[{{.SiteName}}] Test Email","test_body":"This is a test email."}`}, {ID: 35, Key: "tag.follow", Value: `0`}, - {ID: 36, Key: "rank.question.add", Value: `0`}, - {ID: 37, Key: "rank.question.edit", Value: `0`}, - {ID: 38, Key: "rank.question.delete", Value: `0`}, - {ID: 39, Key: "rank.question.vote_up", Value: `0`}, - {ID: 40, Key: "rank.question.vote_down", Value: `0`}, - {ID: 41, Key: "rank.answer.add", Value: `0`}, - {ID: 42, Key: "rank.answer.edit", Value: `0`}, - {ID: 43, Key: "rank.answer.delete", Value: `0`}, - {ID: 44, Key: "rank.answer.accept", Value: `0`}, - {ID: 45, Key: "rank.answer.vote_up", Value: `0`}, - {ID: 46, Key: "rank.answer.vote_down", Value: `0`}, - {ID: 47, Key: "rank.comment.add", Value: `0`}, - {ID: 48, Key: "rank.comment.edit", Value: `0`}, - {ID: 49, Key: "rank.comment.delete", Value: `0`}, - {ID: 50, Key: "rank.report.add", Value: `0`}, - {ID: 51, Key: "rank.tag.add", Value: `0`}, - {ID: 52, Key: "rank.tag.edit", Value: `0`}, - {ID: 53, Key: "rank.tag.delete", Value: `0`}, - {ID: 54, Key: "rank.tag.synonym", Value: `0`}, - {ID: 55, Key: "rank.link.url_limit", Value: `0`}, + {ID: 36, Key: "rank.question.add", Value: `1`}, + {ID: 37, Key: "rank.question.edit", Value: `200`}, + {ID: 38, Key: "rank.question.delete", Value: `-1`}, + {ID: 39, Key: "rank.question.vote_up", Value: `15`}, + {ID: 40, Key: "rank.question.vote_down", Value: `125`}, + {ID: 41, Key: "rank.answer.add", Value: `1`}, + {ID: 42, Key: "rank.answer.edit", Value: `200`}, + {ID: 43, Key: "rank.answer.delete", Value: `-1`}, + {ID: 44, Key: "rank.answer.accept", Value: `1`}, + {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, + {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, + {ID: 47, Key: "rank.comment.add", Value: `1`}, + {ID: 48, Key: "rank.comment.edit", Value: `-1`}, + {ID: 49, Key: "rank.comment.delete", Value: `-1`}, + {ID: 50, Key: "rank.report.add", Value: `1`}, + {ID: 51, Key: "rank.tag.add", Value: `1`}, + {ID: 52, Key: "rank.tag.edit", Value: `100`}, + {ID: 53, Key: "rank.tag.delete", Value: `-1`}, + {ID: 54, Key: "rank.tag.synonym", Value: `20000`}, + {ID: 55, Key: "rank.link.url_limit", Value: `10`}, {ID: 56, Key: "rank.vote.detail", Value: `0`}, {ID: 57, Key: "reason.spam", Value: `{"name":"spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic."}`}, {ID: 58, Key: "reason.rude_or_abusive", Value: `{"name":"rude or abusive","description":"A reasonable person would find this content inappropriate for respectful discourse."}`}, @@ -262,6 +262,14 @@ func initConfigTable(engine *xorm.Engine) error { {ID: 104, Key: "tag.rollback", Value: `0`}, {ID: 105, Key: "tag.deleted", Value: `0`}, {ID: 106, Key: "tag.undeleted", Value: `0`}, + {ID: 107, Key: "rank.comment.vote_up", Value: `1`}, + {ID: 108, Key: "rank.comment.vote_down", Value: `1`}, + {ID: 109, Key: "rank.question.edit_without_review", Value: `2000`}, + {ID: 110, Key: "rank.answer.edit_without_review", Value: `2000`}, + {ID: 111, Key: "rank.tag.edit_without_review", Value: `20000`}, + {ID: 112, Key: "rank.answer.audit", Value: `2000`}, + {ID: 113, Key: "rank.question.audit", Value: `2000`}, + {ID: 114, Key: "rank.tag.audit", Value: `20000`}, } _, err := engine.Insert(defaultConfigTable) return err diff --git a/internal/migrations/v3.go b/internal/migrations/v3.go index 0f955e8d..11d613ef 100644 --- a/internal/migrations/v3.go +++ b/internal/migrations/v3.go @@ -29,7 +29,7 @@ func addActivityTimeline(x *xorm.Engine) error { {ID: 45, Key: "rank.answer.vote_up", Value: `15`}, {ID: 46, Key: "rank.answer.vote_down", Value: `125`}, {ID: 47, Key: "rank.comment.add", Value: `1`}, - {ID: 48, Key: "rank.comment.edit", Value: `1`}, + {ID: 48, Key: "rank.comment.edit", Value: `-1`}, {ID: 49, Key: "rank.comment.delete", Value: `-1`}, {ID: 50, Key: "rank.report.add", Value: `1`}, {ID: 51, Key: "rank.tag.add", Value: `1`}, From bf41dec058544f8464714c164104e2a9e198840f Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 15:48:26 +0800 Subject: [PATCH 003/272] add template --- internal/controller/controller.go | 1 + internal/controller/template_controller.go | 11 +++++++++++ internal/router/provider.go | 2 +- internal/router/template_router.go | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 internal/controller/template_controller.go create mode 100644 internal/router/template_router.go diff --git a/internal/controller/controller.go b/internal/controller/controller.go index e23230b1..48dcabd4 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -22,4 +22,5 @@ var ProviderSetController = wire.NewSet( NewSiteinfoController, NewDashboardController, NewUploadController, + NewTemplateController, ) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go new file mode 100644 index 00000000..625bfa3e --- /dev/null +++ b/internal/controller/template_controller.go @@ -0,0 +1,11 @@ +package controller + +type TemplateController struct { +} + +// NewTemplateController new controller +func NewTemplateController() *TemplateController { + return &TemplateController{} +} + +// func (tc *TemplateController) SearchTagLike(ctx *gin.Context) { diff --git a/internal/router/provider.go b/internal/router/provider.go index 08df9ce5..f705c973 100644 --- a/internal/router/provider.go +++ b/internal/router/provider.go @@ -3,4 +3,4 @@ package router import "github.com/google/wire" // ProviderSetRouter is providers. -var ProviderSetRouter = wire.NewSet(NewAnswerAPIRouter, NewSwaggerRouter, NewStaticRouter, NewUIRouter) +var ProviderSetRouter = wire.NewSet(NewAnswerAPIRouter, NewSwaggerRouter, NewStaticRouter, NewUIRouter, NewTemplateRouter) diff --git a/internal/router/template_router.go b/internal/router/template_router.go new file mode 100644 index 00000000..d764690a --- /dev/null +++ b/internal/router/template_router.go @@ -0,0 +1,16 @@ +package router + +import ( + "github.com/gin-gonic/gin" +) + +type TemplateRouter struct { +} + +func NewTemplateRouter() *TemplateRouter { + return &TemplateRouter{} +} + +// TemplateRouter template router +func (a *TemplateRouter) TemplateRouter(r *gin.RouterGroup) { +} From 9839272d9548b5794dcb0f3e8c81d61eca7f2479 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:20:28 +0800 Subject: [PATCH 004/272] update template --- cmd/answer/wire_gen.go | 4 +- internal/base/server/http.go | 2 + internal/controller/template_controller.go | 81 +++++++++++++++++++++- internal/router/template_router.go | 21 +++++- ui/build/index.html | 2 +- 5 files changed, 103 insertions(+), 7 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index acee8475..c80d1460 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -189,7 +189,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, uiRouter := router.NewUIRouter() authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) - ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware) + templateController := controller.NewTemplateController() + templateRouter := router.NewTemplateRouter(templateController) + ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) return application, func() { cleanup2() diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 71220162..82eeb293 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -15,6 +15,7 @@ func NewHTTPServer(debug bool, viewRouter *router.UIRouter, authUserMiddleware *middleware.AuthUserMiddleware, avatarMiddleware *middleware.AvatarMiddleware, + templateRouter *router.TemplateRouter, ) *gin.Engine { if debug { @@ -30,6 +31,7 @@ func NewHTTPServer(debug bool, rootGroup := r.Group("") swaggerRouter.Register(rootGroup) + templateRouter.RegisterTemplateRouter(rootGroup) static := r.Group("") static.Use(avatarMiddleware.AvatarThumb()) staticRouter.RegisterStaticRouter(static) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 625bfa3e..84810397 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -1,11 +1,88 @@ package controller +import ( + "net/http" + "regexp" + + "github.com/answerdev/answer/ui" + "github.com/gin-gonic/gin" +) + type TemplateController struct { + scriptPath string + cssPath string } // NewTemplateController new controller func NewTemplateController() *TemplateController { - return &TemplateController{} + script, css := GetStyle() + return &TemplateController{ + scriptPath: script, + cssPath: css, + } +} +func GetStyle() (script, css string) { + file, err := ui.Build.ReadFile("build/index.html") + if err != nil { + return + } + scriptRegexp := regexp.MustCompile(``) + scriptData := scriptRegexp.FindStringSubmatch(string(file)) + cssRegexp := regexp.MustCompile(``) + cssListData := cssRegexp.FindStringSubmatch(string(file)) + if len(scriptData) == 2 { + script = scriptData[1] + } + if len(cssListData) == 2 { + css = cssListData[1] + } + return } -// func (tc *TemplateController) SearchTagLike(ctx *gin.Context) { +// Index question list +func (tc *TemplateController) Index(ctx *gin.Context) { + ctx.JSON(http.StatusOK, gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} + +// QuestionInfo question and answers info +func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { + id := ctx.Param("id") + answerid := ctx.Param("answerid") + ctx.JSON(http.StatusOK, gin.H{ + "id": id, + "answerid": answerid, + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} + +// TagList tags list +func (tc *TemplateController) TagList(ctx *gin.Context) { + ctx.JSON(http.StatusOK, gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} + +// TagInfo taginfo +func (tc *TemplateController) TagInfo(ctx *gin.Context) { + tag := ctx.Param("tag") + ctx.JSON(http.StatusOK, gin.H{ + "tag": tag, + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} + +// UserInfo user info +func (tc *TemplateController) UserInfo(ctx *gin.Context) { + username := ctx.Param("username") + ctx.JSON(http.StatusOK, gin.H{ + "username": username, + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} diff --git a/internal/router/template_router.go b/internal/router/template_router.go index d764690a..4ccedb43 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -1,16 +1,31 @@ package router import ( + "github.com/answerdev/answer/internal/controller" "github.com/gin-gonic/gin" ) type TemplateRouter struct { + templateController *controller.TemplateController } -func NewTemplateRouter() *TemplateRouter { - return &TemplateRouter{} +func NewTemplateRouter( + templateController *controller.TemplateController, +) *TemplateRouter { + return &TemplateRouter{ + templateController: templateController, + } } // TemplateRouter template router -func (a *TemplateRouter) TemplateRouter(r *gin.RouterGroup) { +func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { + r.GET("/", a.templateController.Index) + r.GET("/index", a.templateController.Index) + r.GET("/questions", a.templateController.Index) + r.GET("/questions/:id/", a.templateController.QuestionInfo) + r.GET("/questions/:id/:title/", a.templateController.QuestionInfo) + r.GET("/questions/:id/:title/:answerid", a.templateController.QuestionInfo) + r.GET("/tags", a.templateController.TagList) + r.GET("/tags/:tag", a.templateController.TagInfo) + r.GET("/users/:username", a.templateController.UserInfo) } diff --git a/ui/build/index.html b/ui/build/index.html index 1eec7ed2..33e11053 100644 --- a/ui/build/index.html +++ b/ui/build/index.html @@ -1 +1 @@ -Answer
\ No newline at end of file +Answer
From bb52bf50ed9dc7560d0933d5699d07231b6aa615 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 17:26:18 +0800 Subject: [PATCH 005/272] add template --- internal/controller/template_render/index.go | 1 + ui/template/homepage.html | 107 ++++ ui/template/question-detail.html | 552 +++++++++++++++++++ ui/template/question.html | 130 +++++ ui/template/tag-detail.html | 194 +++++++ ui/template/tags.html | 115 ++++ 6 files changed, 1099 insertions(+) create mode 100644 internal/controller/template_render/index.go create mode 100644 ui/template/homepage.html create mode 100644 ui/template/question-detail.html create mode 100644 ui/template/question.html create mode 100644 ui/template/tag-detail.html create mode 100644 ui/template/tags.html diff --git a/internal/controller/template_render/index.go b/internal/controller/template_render/index.go new file mode 100644 index 00000000..362739ec --- /dev/null +++ b/internal/controller/template_render/index.go @@ -0,0 +1 @@ +package templaterender diff --git a/ui/template/homepage.html b/ui/template/homepage.html new file mode 100644 index 00000000..bcc3f051 --- /dev/null +++ b/ui/template/homepage.html @@ -0,0 +1,107 @@ + + + + + + + + + + + +
+ +
+
+
+
+
+ +
+
+ robin +
+ Mod +
+
+
@bmm11uodayryy
+
+
+ 89 reputation +
+
+ 1 answers +
+
+ 1 questions +
+
+
+
+
+
+
+ +
+
+
+ +
+
+
About Me
+
// Hello, World !
+
+
+
+ +
+
+
+
+
+
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+
+ + + \ No newline at end of file diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html new file mode 100644 index 00000000..1b708a49 --- /dev/null +++ b/ui/template/question-detail.html @@ -0,0 +1,552 @@ + + + + + + + + + + + +
+ +
+
+
+
+
+

+ how to make links and how to + make errors ? +

+
+ +
Viewed 68
+ +
+ +
+

如下面的例子

+
const getStrLength = (target: string | number): number => {
+  if (typeof target === "string") {
+    return target.length; // 这里的`target`被识别为`string`,不用写`(target as string)`
+  } else {
+    return target.toString().length;
+  }
+};
+
+

+ 那要是不能用typeof进行识别的类型,如何写这个if可以在后续内容中省略断言呢? +

+
function getSomething(target: Event): number | string {
+  if (??? MouseEvnet ???) { // 这里怎么写可以省略下面的`(target as MouseEvent)`
+    console.log(target.clientX);
+    console.log(target.clientX);
+    console.log(target.clientX);
+    .... // 多行代码总不能每次都写`(target as MouseEvent)`吧
+    return target.clientX;
+  } else if (??? KeyboardEvent ???) {
+    return target.key;
+  } else {
+    ...
+  }
+}
+
+
+
+
+ +
+ +
+
+
+
+ + Edit +
+
+ +
+
+ +
+
+ shuai150 +
+ +
+
+
+
+
+
+
+
+

this is a good question

+
+
+
+ +
+ +
+ +
+
+
+
+
+

+ this is a something about ts? you can looak at ... +

+
+
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+
3 Answers
+ +
+
+
+
    +
  1. /questions/10010000000000887
  2. +
  3. + /questions/10010000000000887/questions/10010000000000887 +
  4. +
  5. /questions/10010000000000887
  6. +
  7. /questions/10010000000000887
  8. +
+
+
+
+
+ +
+
+ +
+
+
+
+ + Edit +
+
+ +
+
+ +
+
+ 34256434 +
+ +
+
+
+
+
+
+ +
+
+
+
+
+

you can do this TypeScript 是 Microsoft 公司注册商标。

+

+ TypeScript 具有类型系统,且是 + JavaScript 的超集。 + 它可以编译成普通的JavaScript代码。 + TypeScript + 支持任意浏览器,任意环境,并且是开源的。 +

+

+ TypeScript + 目前还在积极的开发完善之中,不断地会有新的特性加入进来。 + 因此本手册也会紧随官方的每个commit,不断地更新新的章节以及修改措词不妥之处。 +

+
+
+
+
+ +
+
+
+
+
+
+ + Edit +
+
+ +
+
+ +
+
+ shuai150 +
+ +
+
+
+
+
+
+
+
+

oh my god, good done boy!

+
+
+
+ +
+ +
+ +
+
+
+ +
+
+
+
+
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa qui officia + deserunt mollit anim id est laborum. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit + esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa qui officia + deserunt mollit anim id est laborum. +

+
+
+
+
+ +
+
+
+
+
+
+ + Edit +
+
+ +
+
+ +
+
+ shuai150 +
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+
+ + + \ No newline at end of file diff --git a/ui/template/question.html b/ui/template/question.html new file mode 100644 index 00000000..f2e1135a --- /dev/null +++ b/ui/template/question.html @@ -0,0 +1,130 @@ + + + + + + + + + + + +
+ +
+
+
+
+
+
+
All Questions
+
+
+
+
+ 创建的时候报错了吗? +
+
+
+ + • +
+
+ 002 +
+
+
+ bugnotag +
+
+ + +
+
+
    +
  • + 1(current) +
  • +
  • + 2 +
  • +
  • + 3 +
  • +
  • + 4 +
  • +
  • + Next +
  • +
+
+
+
+
+ +
+
+
+
+
+
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+
+ + + \ No newline at end of file diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html new file mode 100644 index 00000000..979f7579 --- /dev/null +++ b/ui/template/tag-detail.html @@ -0,0 +1,194 @@ + + + + + + + + + + + + +
+ +
+
+
+
+
+

+ Apple +

+

+ 对外只有3001一个端口,群集内有负载策略会分发给不同实例1111[More] +

+
+ +
+
+
+
+
3 Questions
+
+
+
+
+ 测试标签处理 +
+
+
+
+ str234ing1,000 +
+ • +
+
+ 0056 +
+
+ +
+
+
+ Examples and usage + guidelines for form control + styles +
+
+
+
+ fen162 +
+ • +
+
+ 14225 +
+
+ +
+
+
+ test date [closed] +
+
+
+
+ robin89 +
+ • +
+
+ 11691 +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+
+ + + \ No newline at end of file diff --git a/ui/template/tags.html b/ui/template/tags.html new file mode 100644 index 00000000..4e04be87 --- /dev/null +++ b/ui/template/tags.html @@ -0,0 +1,115 @@ + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+

Tags

+
+
+
+ +
+
+
+ 算法 +

212121212121221 +

+
+ 0 + questions +
+
+
+
+ + +
+
+ +
+
+
+
+
+
+
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+
+ + + \ No newline at end of file From fadba21723296dc6a77526fda456ae4089cf410f Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 29 Nov 2022 17:50:31 +0800 Subject: [PATCH 006/272] feat: template render init wire --- cmd/answer/wire.go | 2 ++ internal/controller/template_render/controller.go | 8 ++++++++ .../template_render/question_controller.go | 14 ++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 internal/controller/template_render/controller.go create mode 100644 internal/controller/template_render/question_controller.go diff --git a/cmd/answer/wire.go b/cmd/answer/wire.go index 067314f7..d809bfd5 100644 --- a/cmd/answer/wire.go +++ b/cmd/answer/wire.go @@ -12,6 +12,7 @@ import ( "github.com/answerdev/answer/internal/base/server" "github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/controller" + "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/controller_backyard" "github.com/answerdev/answer/internal/repo" "github.com/answerdev/answer/internal/router" @@ -37,6 +38,7 @@ func initApplication( router.ProviderSetRouter, controller.ProviderSetController, controller_backyard.ProviderSetController, + templaterender.ProviderSetTemplateRenderController, service.ProviderSetService, repo.ProviderSetRepo, translator.ProviderSet, diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go new file mode 100644 index 00000000..77603889 --- /dev/null +++ b/internal/controller/template_render/controller.go @@ -0,0 +1,8 @@ +package templaterender + +import "github.com/google/wire" + +// ProviderSetTemplateRenderController is template render controller providers. +var ProviderSetTemplateRenderController = wire.NewSet( + NewQuestionController, +) diff --git a/internal/controller/template_render/question_controller.go b/internal/controller/template_render/question_controller.go new file mode 100644 index 00000000..3081c321 --- /dev/null +++ b/internal/controller/template_render/question_controller.go @@ -0,0 +1,14 @@ +package templaterender + +type QuestionController struct { +} + +func NewQuestionController() *QuestionController { + return &QuestionController{} +} + +func (q *QuestionController) Index() { + +} + +func (q *QuestionController) Detail() {} From 8e240b677eda62925f6b740866a747eafa13037e Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:02:42 +0800 Subject: [PATCH 007/272] add template --- internal/base/server/http.go | 16 +++++++++++++++- internal/controller/template_controller.go | 10 +++++----- internal/router/template_router.go | 1 + ui/build/index.html | 2 +- ui/static.go | 3 +++ ui/template/homepage.html | 6 ++++-- ui/template/question.html | 7 +++++-- 7 files changed, 34 insertions(+), 11 deletions(-) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 82eeb293..aeb11c5c 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -1,9 +1,14 @@ package server import ( + "html/template" + "io/fs" + "os" + brotli "github.com/anargu/gin-brotli" "github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/router" + "github.com/answerdev/answer/ui" "github.com/gin-gonic/gin" ) @@ -31,7 +36,6 @@ func NewHTTPServer(debug bool, rootGroup := r.Group("") swaggerRouter.Register(rootGroup) - templateRouter.RegisterTemplateRouter(rootGroup) static := r.Group("") static.Use(avatarMiddleware.AvatarThumb()) staticRouter.RegisterStaticRouter(static) @@ -50,5 +54,15 @@ func NewHTTPServer(debug bool, cmsauthV1.Use(authUserMiddleware.CmsAuth()) answerRouter.RegisterAnswerCmsAPIRouter(cmsauthV1) + dev := os.Getenv("DEVCODE") + if dev != "" { + r.LoadHTMLGlob("../../ui/template/*") + } else { + html, _ := fs.Sub(ui.Template, "template") + htmlTemplate := template.Must(template.New("").ParseFS(html, "*.html")) + r.SetHTMLTemplate(htmlTemplate) + } + + templateRouter.RegisterTemplateRouter(rootGroup) return r } diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 84810397..7361f0d2 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -41,7 +41,7 @@ func GetStyle() (script, css string) { // Index question list func (tc *TemplateController) Index(ctx *gin.Context) { - ctx.JSON(http.StatusOK, gin.H{ + ctx.HTML(http.StatusOK, "question.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) @@ -51,7 +51,7 @@ func (tc *TemplateController) Index(ctx *gin.Context) { func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { id := ctx.Param("id") answerid := ctx.Param("answerid") - ctx.JSON(http.StatusOK, gin.H{ + ctx.HTML(http.StatusOK, "question-detail.html", gin.H{ "id": id, "answerid": answerid, "scriptPath": tc.scriptPath, @@ -61,7 +61,7 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { // TagList tags list func (tc *TemplateController) TagList(ctx *gin.Context) { - ctx.JSON(http.StatusOK, gin.H{ + ctx.HTML(http.StatusOK, "tags.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) @@ -70,7 +70,7 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { // TagInfo taginfo func (tc *TemplateController) TagInfo(ctx *gin.Context) { tag := ctx.Param("tag") - ctx.JSON(http.StatusOK, gin.H{ + ctx.HTML(http.StatusOK, "tag-detail.html", gin.H{ "tag": tag, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, @@ -80,7 +80,7 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { // UserInfo user info func (tc *TemplateController) UserInfo(ctx *gin.Context) { username := ctx.Param("username") - ctx.JSON(http.StatusOK, gin.H{ + ctx.HTML(http.StatusOK, "homepage.html", gin.H{ "username": username, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, diff --git a/internal/router/template_router.go b/internal/router/template_router.go index 4ccedb43..6ee28746 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -19,6 +19,7 @@ func NewTemplateRouter( // TemplateRouter template router func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { + r.GET("/", a.templateController.Index) r.GET("/index", a.templateController.Index) r.GET("/questions", a.templateController.Index) diff --git a/ui/build/index.html b/ui/build/index.html index 33e11053..d07af796 100644 --- a/ui/build/index.html +++ b/ui/build/index.html @@ -1 +1 @@ -Answer
+Answer
diff --git a/ui/static.go b/ui/static.go index 74002b6b..a78eef7e 100644 --- a/ui/static.go +++ b/ui/static.go @@ -7,3 +7,6 @@ import ( //go:embed build var Build embed.FS + +//go:embed template +var Template embed.FS diff --git a/ui/template/homepage.html b/ui/template/homepage.html index bcc3f051..a49ec341 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -4,7 +4,9 @@ - + + + @@ -104,4 +106,4 @@ - \ No newline at end of file + diff --git a/ui/template/question.html b/ui/template/question.html index f2e1135a..757753a9 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -4,10 +4,13 @@ - + + + + 111
- \ No newline at end of file + From 8172d4a953607077d9747d7e21f7a06ca8e31c59 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:04:46 +0800 Subject: [PATCH 008/272] update template --- ui/template/homepage.html | 2 +- ui/template/question-detail.html | 6 ++++-- ui/template/tag-detail.html | 6 ++++-- ui/template/tags.html | 6 ++++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ui/template/homepage.html b/ui/template/homepage.html index a49ec341..29607966 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -6,7 +6,7 @@ - + diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html index 1b708a49..0700fa79 100644 --- a/ui/template/question-detail.html +++ b/ui/template/question-detail.html @@ -4,7 +4,9 @@ - + + + @@ -549,4 +551,4 @@ - \ No newline at end of file + diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html index 979f7579..8b49bbfc 100644 --- a/ui/template/tag-detail.html +++ b/ui/template/tag-detail.html @@ -5,7 +5,9 @@ - + + + @@ -191,4 +193,4 @@ - \ No newline at end of file + diff --git a/ui/template/tags.html b/ui/template/tags.html index 4e04be87..72d4dfb6 100644 --- a/ui/template/tags.html +++ b/ui/template/tags.html @@ -5,7 +5,9 @@ - + + + @@ -112,4 +114,4 @@ - \ No newline at end of file + From 056fea83be8430887e879a4b3d3103ee3edfe299 Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 29 Nov 2022 18:14:26 +0800 Subject: [PATCH 009/272] feat: template render controller changed --- cmd/answer/wire_gen.go | 4 +++- .../controller/template_render/controller.go | 19 +++++++++++++++++-- .../controller/template_render/question.go | 11 +++++++++++ .../template_render/question_controller.go | 14 -------------- internal/router/template_router.go | 18 ++++++++++++------ 5 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 internal/controller/template_render/question.go delete mode 100644 internal/controller/template_render/question_controller.go diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index c80d1460..492a074a 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -13,6 +13,7 @@ import ( "github.com/answerdev/answer/internal/base/server" "github.com/answerdev/answer/internal/base/translator" "github.com/answerdev/answer/internal/controller" + "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/controller_backyard" "github.com/answerdev/answer/internal/repo/activity" "github.com/answerdev/answer/internal/repo/activity_common" @@ -190,7 +191,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) templateController := controller.NewTemplateController() - templateRouter := router.NewTemplateRouter(templateController) + templateRenderController := templaterender.NewTemplateRenderController(questionService) + templateRouter := router.NewTemplateRouter(templateController, templateRenderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) return application, func() { diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index 77603889..773b7ba0 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -1,8 +1,23 @@ package templaterender -import "github.com/google/wire" +import ( + "github.com/answerdev/answer/internal/service" + "github.com/google/wire" +) // ProviderSetTemplateRenderController is template render controller providers. var ProviderSetTemplateRenderController = wire.NewSet( - NewQuestionController, + NewTemplateRenderController, ) + +type TemplateRenderController struct { + questionService *service.QuestionService +} + +func NewTemplateRenderController( + questionService *service.QuestionService, +) *TemplateRenderController { + return &TemplateRenderController{ + questionService: questionService, + } +} diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go new file mode 100644 index 00000000..5a9cb49b --- /dev/null +++ b/internal/controller/template_render/question.go @@ -0,0 +1,11 @@ +package templaterender + +import "github.com/gin-gonic/gin" + +func (q *TemplateRenderController) Index(ctx *gin.Context) { + +} + +func (q *TemplateRenderController) QuestionDetail(ctx *gin.Context) {} + +func (q *TemplateRenderController) AnswerDetail(ctx *gin.Context) {} diff --git a/internal/controller/template_render/question_controller.go b/internal/controller/template_render/question_controller.go deleted file mode 100644 index 3081c321..00000000 --- a/internal/controller/template_render/question_controller.go +++ /dev/null @@ -1,14 +0,0 @@ -package templaterender - -type QuestionController struct { -} - -func NewQuestionController() *QuestionController { - return &QuestionController{} -} - -func (q *QuestionController) Index() { - -} - -func (q *QuestionController) Detail() {} diff --git a/internal/router/template_router.go b/internal/router/template_router.go index 6ee28746..de5bf6fc 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -2,18 +2,22 @@ package router import ( "github.com/answerdev/answer/internal/controller" + templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/gin-gonic/gin" ) type TemplateRouter struct { - templateController *controller.TemplateController + templateController *controller.TemplateController + templateRenderController *templaterender.TemplateRenderController } func NewTemplateRouter( templateController *controller.TemplateController, + templateRenderController *templaterender.TemplateRenderController, ) *TemplateRouter { return &TemplateRouter{ - templateController: templateController, + templateController: templateController, + templateRenderController: templateRenderController, } } @@ -22,10 +26,12 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { r.GET("/", a.templateController.Index) r.GET("/index", a.templateController.Index) - r.GET("/questions", a.templateController.Index) - r.GET("/questions/:id/", a.templateController.QuestionInfo) - r.GET("/questions/:id/:title/", a.templateController.QuestionInfo) - r.GET("/questions/:id/:title/:answerid", a.templateController.QuestionInfo) + + r.GET("/questions", a.templateRenderController.Index) + r.GET("/questions/:id/", a.templateRenderController.QuestionDetail) + r.GET("/questions/:id/:title/", a.templateRenderController.QuestionDetail) + r.GET("/questions/:id/:title/:answerid", a.templateRenderController.AnswerDetail) + r.GET("/tags", a.templateController.TagList) r.GET("/tags/:tag", a.templateController.TagInfo) r.GET("/users/:username", a.templateController.UserInfo) From 77a6d2d16de7dc7e0fc7b017d01e450291caeec3 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:25:25 +0800 Subject: [PATCH 010/272] add 404 --- ui/template/404.html | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 ui/template/404.html diff --git a/ui/template/404.html b/ui/template/404.html new file mode 100644 index 00000000..99cda5ca --- /dev/null +++ b/ui/template/404.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + +
+ +
+
+
(=‘x‘=)
+
Unfortunately, this page doesn't exist.
+ +
+
+
+
+
+

Built on + Answer - the open-source software that power Q&A communities
Made with love © 2022 + Answer

+
+
+
+ + + From b09fc6b15f95fccf772c4184196bfb61b6eb9946 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:32:49 +0800 Subject: [PATCH 011/272] fix question router --- internal/router/template_router.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/router/template_router.go b/internal/router/template_router.go index de5bf6fc..87c91d20 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -27,10 +27,10 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { r.GET("/", a.templateController.Index) r.GET("/index", a.templateController.Index) - r.GET("/questions", a.templateRenderController.Index) - r.GET("/questions/:id/", a.templateRenderController.QuestionDetail) - r.GET("/questions/:id/:title/", a.templateRenderController.QuestionDetail) - r.GET("/questions/:id/:title/:answerid", a.templateRenderController.AnswerDetail) + r.GET("/questions", a.templateController.Index) + r.GET("/questions/:id/", a.templateController.QuestionInfo) + r.GET("/questions/:id/:title/", a.templateController.QuestionInfo) + r.GET("/questions/:id/:title/:answerid", a.templateController.QuestionInfo) r.GET("/tags", a.templateController.TagList) r.GET("/tags/:tag", a.templateController.TagInfo) From d99f3f31f10d9f9e485bcf850abe335962eea48e Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 11:23:04 +0800 Subject: [PATCH 012/272] split template --- internal/controller/template_controller.go | 7 ++ internal/router/template_router.go | 1 + ui/template/404.html | 54 +------------- ui/template/footer.html | 17 +++++ ui/template/header.html | 42 +++++++++++ ui/template/homepage.html | 62 +--------------- ui/template/question-detail.html | 63 +--------------- ui/template/question.html | 65 +---------------- ui/template/tag-detail.html | 65 +---------------- ui/template/tags.html | 84 ++++++---------------- 10 files changed, 100 insertions(+), 360 deletions(-) create mode 100644 ui/template/footer.html create mode 100644 ui/template/header.html diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 7361f0d2..a01307ff 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -86,3 +86,10 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { "cssPath": tc.cssPath, }) } + +func (tc *TemplateController) Page404(ctx *gin.Context) { + ctx.HTML(http.StatusOK, "404.html", gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + }) +} diff --git a/internal/router/template_router.go b/internal/router/template_router.go index 87c91d20..cd7a6f6b 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -35,4 +35,5 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { r.GET("/tags", a.templateController.TagList) r.GET("/tags/:tag", a.templateController.TagInfo) r.GET("/users/:username", a.templateController.UserInfo) + r.GET("/404", a.templateController.Page404) } diff --git a/ui/template/404.html b/ui/template/404.html index 99cda5ca..b16e4028 100644 --- a/ui/template/404.html +++ b/ui/template/404.html @@ -1,59 +1,9 @@ - - - - - - - - - - - - -
- -
+{{template "header" . }}
(=‘x‘=)
Unfortunately, this page doesn't exist.
-
-
-
-
-

Built on - Answer - the open-source software that power Q&A communities
Made with love © 2022 - Answer

-
-
-
- - +{{template "footer" .}} diff --git a/ui/template/footer.html b/ui/template/footer.html new file mode 100644 index 00000000..724e282d --- /dev/null +++ b/ui/template/footer.html @@ -0,0 +1,17 @@ +{{define "footer"}} + + +
+
+

+ Built on Answer - + the open-source software that power Q&A communities
Made + with love © 2022 Answer +

+
+
+ + + + +{{end}} diff --git a/ui/template/header.html b/ui/template/header.html new file mode 100644 index 00000000..1fa2f045 --- /dev/null +++ b/ui/template/header.html @@ -0,0 +1,42 @@ +{{define "header"}} + + + + + + + + + + + + + +
+ +
+{{end}} diff --git a/ui/template/homepage.html b/ui/template/homepage.html index 29607966..9da911d0 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -1,43 +1,6 @@ - - +{{template "header" . }} - - - - - - - - - -
- -
@@ -85,25 +48,6 @@
-
-
-
-
-

- Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer -

-
-
-
- - + +{{template "footer" .}} diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html index 0700fa79..3e30df35 100644 --- a/ui/template/question-detail.html +++ b/ui/template/question-detail.html @@ -1,43 +1,4 @@ - - - - - - - - - - - - - -
- -
+{{template "header" . }}
@@ -530,25 +491,5 @@ ">
-
-
-
-
-

- Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer -

-
-
-
- +{{template "footer" .}} - diff --git a/ui/template/question.html b/ui/template/question.html index 757753a9..b29200b1 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -1,44 +1,4 @@ - - - - - - - - - - - - - 111 - -
- -
+{{template "header" . }}
@@ -109,25 +69,4 @@
-
-
-
-
-

- Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer -

-
-
-
- - - +{{template "footer" .}} diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html index 8b49bbfc..65b473b0 100644 --- a/ui/template/tag-detail.html +++ b/ui/template/tag-detail.html @@ -1,44 +1,4 @@ - - - - - - - - - - - - - - -
- -
+{{template "header" . }}
@@ -172,25 +132,4 @@
-
-
-
-
-

- Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer -

-
-
-
- - - +{{template "footer" .}} diff --git a/ui/template/tags.html b/ui/template/tags.html index 72d4dfb6..a1d2c3c6 100644 --- a/ui/template/tags.html +++ b/ui/template/tags.html @@ -1,44 +1,4 @@ - - - - - - - - - - - - - - -
- -
+{{template "header" . }}
+
+
+ 算法 +

212121212121221 +

+
+ 0 + questions +
+
+
+
+ +
    @@ -93,25 +70,8 @@
-
-
-
-
-

- Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer -

-
-
-
- - + + +{{template "footer" .}} + From 5884aea2a721a0c647ae2dd0a7824fc06558289c Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:01:41 +0800 Subject: [PATCH 013/272] update template --- ui/template/question-detail.html | 292 +------------------------------ ui/template/tag-detail.html | 7 +- ui/template/tags.html | 6 +- 3 files changed, 8 insertions(+), 297 deletions(-) diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html index 3e30df35..1e58ce20 100644 --- a/ui/template/question-detail.html +++ b/ui/template/question-detail.html @@ -13,9 +13,7 @@ datetime="2022-11-28T09:48:16.000Z" title="Nov 28, 2022 at 17:48">Modified 23h ago
Viewed 68
- +
-
-
-
-

- this is a something about ts? you can looak at ... -

-
-
-
- -
- -
- -
-
-
- -
@@ -228,18 +159,7 @@
- - Edit +
@@ -269,212 +189,10 @@
-
-
- -
-
- -
-
-

you can do this TypeScript 是 Microsoft 公司注册商标。

-

- TypeScript 具有类型系统,且是 - JavaScript 的超集。 - 它可以编译成普通的JavaScript代码。 - TypeScript - 支持任意浏览器,任意环境,并且是开源的。 -

-

- TypeScript - 目前还在积极的开发完善之中,不断地会有新的特性加入进来。 - 因此本手册也会紧随官方的每个commit,不断地更新新的章节以及修改措词不妥之处。 -

-
-
-
-
- -
-
-
-
-
-
- - Edit -
-
- -
-
- -
-
- shuai150 -
- -
-
-
-
-
-
-
-
-

oh my god, good done boy!

-
-
-
- -
- -
- -
-
-
- -
-
-
-
-
-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed - do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation - ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit - esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - occaecat cupidatat non proident, sunt in culpa qui officia - deserunt mollit anim id est laborum. -

-

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed - do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation - ullamco laboris nisi ut aliquip ex ea commodo consequat. - Duis aute irure dolor in reprehenderit in voluptate velit - esse cillum dolore eu fugiat nulla pariatur. Excepteur sint - occaecat cupidatat non proident, sunt in culpa qui officia - deserunt mollit anim id est laborum. -

-
-
-
-
- -
-
-
-
-
-
- - Edit -
-
- -
-
- -
-
- shuai150 -
- -
-
-
-
+
diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html index 65b473b0..2fbf32ed 100644 --- a/ui/template/tag-detail.html +++ b/ui/template/tag-detail.html @@ -7,13 +7,8 @@ Apple

- 对外只有3001一个端口,群集内有负载策略会分发给不同实例1111[More] + 对外只有3001一个端口,群集内有负载策略会分发给不同实例1111

-
- -
diff --git a/ui/template/tags.html b/ui/template/tags.html index a1d2c3c6..f73fc943 100644 --- a/ui/template/tags.html +++ b/ui/template/tags.html @@ -25,8 +25,7 @@

212121212121221

- 0 + 0 questions
@@ -42,8 +41,7 @@

212121212121221

- 0 + 0 questions
From 1972872105f28d0a7e15f74c62f978af6d18571a Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:13:26 +0800 Subject: [PATCH 014/272] add userinfo --- cmd/answer/wire_gen.go | 4 ++-- internal/controller/template_controller.go | 23 +++++++++++++++---- .../controller/template_render/controller.go | 4 ++++ .../controller/template_render/userinfo.go | 13 +++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 internal/controller/template_render/userinfo.go diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 492a074a..fa0ba1ba 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -190,8 +190,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, uiRouter := router.NewUIRouter() authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) - templateController := controller.NewTemplateController() - templateRenderController := templaterender.NewTemplateRenderController(questionService) + templateRenderController := templaterender.NewTemplateRenderController(questionService, userService) + templateController := controller.NewTemplateController(templateRenderController) templateRouter := router.NewTemplateRouter(templateController, templateRenderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index a01307ff..05ab4748 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -4,21 +4,28 @@ import ( "net/http" "regexp" + "github.com/answerdev/answer/internal/base/handler" + templaterender "github.com/answerdev/answer/internal/controller/template_render" + "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/ui" "github.com/gin-gonic/gin" ) type TemplateController struct { - scriptPath string - cssPath string + scriptPath string + cssPath string + templateRenderController *templaterender.TemplateRenderController } // NewTemplateController new controller -func NewTemplateController() *TemplateController { +func NewTemplateController( + templateRenderController *templaterender.TemplateRenderController, +) *TemplateController { script, css := GetStyle() return &TemplateController{ - scriptPath: script, - cssPath: css, + scriptPath: script, + cssPath: css, + templateRenderController: templateRenderController, } } func GetStyle() (script, css string) { @@ -79,6 +86,12 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { // UserInfo user info func (tc *TemplateController) UserInfo(ctx *gin.Context) { + req := &schema.GetOtherUserInfoByUsernameReq{} + if handler.BindAndCheck(ctx, req) { + return + } + tc.templateRenderController.UserInfo(ctx, req) + username := ctx.Param("username") ctx.HTML(http.StatusOK, "homepage.html", gin.H{ "username": username, diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index 773b7ba0..a8fead33 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -12,12 +12,16 @@ var ProviderSetTemplateRenderController = wire.NewSet( type TemplateRenderController struct { questionService *service.QuestionService + userService *service.UserService } func NewTemplateRenderController( questionService *service.QuestionService, + userService *service.UserService, + ) *TemplateRenderController { return &TemplateRenderController{ questionService: questionService, + userService: userService, } } diff --git a/internal/controller/template_render/userinfo.go b/internal/controller/template_render/userinfo.go new file mode 100644 index 00000000..e5b17a79 --- /dev/null +++ b/internal/controller/template_render/userinfo.go @@ -0,0 +1,13 @@ +package templaterender + +import ( + "github.com/answerdev/answer/internal/schema" + "github.com/davecgh/go-spew/spew" + "golang.org/x/net/context" +) + +func (q *TemplateRenderController) UserInfo(ctx context.Context, req *schema.GetOtherUserInfoByUsernameReq) { + + resp, err := q.userService.GetOtherUserInfoByUsername(ctx, req.Username) + spew.Dump(resp, err) +} From fac34f66c949cb27fd2cc228817254d5c47f351b Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:25:03 +0800 Subject: [PATCH 015/272] update userinfo --- internal/controller/template_controller.go | 18 ++++++++++++------ .../controller/template_render/userinfo.go | 7 ++----- ui/template/homepage.html | 17 ++++++++--------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 05ab4748..f38aab1a 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -4,10 +4,10 @@ import ( "net/http" "regexp" - "github.com/answerdev/answer/internal/base/handler" templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/ui" + "github.com/davecgh/go-spew/spew" "github.com/gin-gonic/gin" ) @@ -86,15 +86,21 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { // UserInfo user info func (tc *TemplateController) UserInfo(ctx *gin.Context) { + username := ctx.Param("username") req := &schema.GetOtherUserInfoByUsernameReq{} - if handler.BindAndCheck(ctx, req) { + req.Username = username + userinfo, err := tc.templateRenderController.UserInfo(ctx, req) + if err != nil { + ctx.HTML(http.StatusOK, "404.html", gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + "err": err.Error(), + }) return } - tc.templateRenderController.UserInfo(ctx, req) - - username := ctx.Param("username") + spew.Dump(userinfo) ctx.HTML(http.StatusOK, "homepage.html", gin.H{ - "username": username, + "userinfo": userinfo, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) diff --git a/internal/controller/template_render/userinfo.go b/internal/controller/template_render/userinfo.go index e5b17a79..e10e2dfa 100644 --- a/internal/controller/template_render/userinfo.go +++ b/internal/controller/template_render/userinfo.go @@ -2,12 +2,9 @@ package templaterender import ( "github.com/answerdev/answer/internal/schema" - "github.com/davecgh/go-spew/spew" "golang.org/x/net/context" ) -func (q *TemplateRenderController) UserInfo(ctx context.Context, req *schema.GetOtherUserInfoByUsernameReq) { - - resp, err := q.userService.GetOtherUserInfoByUsername(ctx, req.Username) - spew.Dump(resp, err) +func (q *TemplateRenderController) UserInfo(ctx context.Context, req *schema.GetOtherUserInfoByUsernameReq) (resp *schema.GetOtherUserInfoResp, err error) { + return q.userService.GetOtherUserInfoByUsername(ctx, req.Username) } diff --git a/ui/template/homepage.html b/ui/template/homepage.html index 9da911d0..a6794b86 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -10,23 +10,22 @@ width="160px" height="160px" class="rounded" alt="" />
-
@bmm11uodayryy
+
@{{.userinfo.Info.Username}}
- 89 reputation + {{.userinfo.Info.Rank}} reputation
- 1 answers + {{.userinfo.Info.AnswerCount}} answers
- 1 questions + {{.userinfo.Info.QuestionCount}} questions
+
@@ -40,7 +39,7 @@
About Me
-
// Hello, World !
+
{{.userinfo.Info.BioHTML}}
From 25908ab3c64e151a5f6e5e95a218a9b5bd09350f Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 14:45:23 +0800 Subject: [PATCH 016/272] update userinfo --- internal/controller/template_controller.go | 4 ++-- ui/template/homepage.html | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index f38aab1a..b566f2ba 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -1,13 +1,13 @@ package controller import ( + "html/template" "net/http" "regexp" templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/ui" - "github.com/davecgh/go-spew/spew" "github.com/gin-gonic/gin" ) @@ -98,11 +98,11 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { }) return } - spew.Dump(userinfo) ctx.HTML(http.StatusOK, "homepage.html", gin.H{ "userinfo": userinfo, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, + "bio": template.HTML(userinfo.Info.BioHTML), }) } diff --git a/ui/template/homepage.html b/ui/template/homepage.html index a6794b86..7cdab7e4 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -5,12 +5,12 @@
-
@{{.userinfo.Info.Username}}
@@ -39,7 +39,7 @@
About Me
-
{{.userinfo.Info.BioHTML}}
+
{{.bio}}
From c3479b70834d91e3694e0ca2d2f848144fb07723 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 15:11:06 +0800 Subject: [PATCH 017/272] add paginator --- .../controller/template_render/controller.go | 56 +++++++++++++++++++ .../template_render/paginator_test.go | 12 ++++ 2 files changed, 68 insertions(+) create mode 100644 internal/controller/template_render/paginator_test.go diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index a8fead33..fc71984a 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -1,6 +1,8 @@ package templaterender import ( + "math" + "github.com/answerdev/answer/internal/service" "github.com/google/wire" ) @@ -25,3 +27,57 @@ func NewTemplateRenderController( userService: userService, } } + +// Paginator page +// page : now page +// prepage : Number per page +// nums : Total +// Returns the contents of the page in the format of 1, 2, 3, 4, and 5. If the contents are less than 5 pages, the page number is returned +func Paginator(page, prepage int, nums int64) map[string]interface{} { + + var firstpage int //前一页地址 + var lastpage int //后一页地址 + //根据nums总数,和prepage每页数量 生成分页总数 + totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //page总数 + if page > totalpages { + page = totalpages + } + if page <= 0 { + page = 1 + } + var pages []int + switch { + case page >= totalpages-5 && totalpages > 5: //最后5页 + start := totalpages - 5 + 1 + firstpage = page - 1 + lastpage = int(math.Min(float64(totalpages), float64(page+1))) + pages = make([]int, 5) + for i, _ := range pages { + pages[i] = start + i + } + case page >= 3 && totalpages > 5: + start := page - 3 + 1 + pages = make([]int, 5) + firstpage = page - 3 + for i, _ := range pages { + pages[i] = start + i + } + firstpage = page - 1 + lastpage = page + 1 + default: + pages = make([]int, int(math.Min(5, float64(totalpages)))) + for i, _ := range pages { + pages[i] = i + 1 + } + firstpage = int(math.Max(float64(1), float64(page-1))) + lastpage = page + 1 + //fmt.Println(pages) + } + paginatorMap := make(map[string]interface{}) + paginatorMap["pages"] = pages + paginatorMap["totalpages"] = totalpages + paginatorMap["firstpage"] = firstpage + paginatorMap["lastpage"] = lastpage + paginatorMap["currpage"] = page + return paginatorMap +} diff --git a/internal/controller/template_render/paginator_test.go b/internal/controller/template_render/paginator_test.go new file mode 100644 index 00000000..a3ddf74f --- /dev/null +++ b/internal/controller/template_render/paginator_test.go @@ -0,0 +1,12 @@ +package templaterender + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" +) + +func TestPaginator(t *testing.T) { + list := Paginator(5, 20, 300) + spew.Dump(list) +} From 7cad11e725ec5b0e0c64bb1f22631208663795d4 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 15:15:39 +0800 Subject: [PATCH 018/272] update paginator --- internal/controller/template_render/controller.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index fc71984a..d60e8d97 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -35,10 +35,10 @@ func NewTemplateRenderController( // Returns the contents of the page in the format of 1, 2, 3, 4, and 5. If the contents are less than 5 pages, the page number is returned func Paginator(page, prepage int, nums int64) map[string]interface{} { - var firstpage int //前一页地址 - var lastpage int //后一页地址 - //根据nums总数,和prepage每页数量 生成分页总数 - totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //page总数 + var firstpage int //Previous page address + var lastpage int //Address on the last page + //Generate the total number of pages based on the total number of nums and the number of prepage pages + totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //Total number of Pages if page > totalpages { page = totalpages } @@ -47,7 +47,7 @@ func Paginator(page, prepage int, nums int64) map[string]interface{} { } var pages []int switch { - case page >= totalpages-5 && totalpages > 5: //最后5页 + case page >= totalpages-5 && totalpages > 5: //The last 5 pages start := totalpages - 5 + 1 firstpage = page - 1 lastpage = int(math.Min(float64(totalpages), float64(page+1))) @@ -71,7 +71,6 @@ func Paginator(page, prepage int, nums int64) map[string]interface{} { } firstpage = int(math.Max(float64(1), float64(page-1))) lastpage = page + 1 - //fmt.Println(pages) } paginatorMap := make(map[string]interface{}) paginatorMap["pages"] = pages From a20105321d4371f3c6eaf5d5f7a7f667ae353a17 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:18:23 +0800 Subject: [PATCH 019/272] update page --- cmd/answer/wire_gen.go | 2 +- internal/controller/template_controller.go | 19 ++++ .../controller/template_render/controller.go | 24 +++-- .../template_render/paginator_test.go | 12 --- internal/controller/template_render/tags.go | 12 +++ internal/schema/template_schema.go | 9 ++ ui/template/page.html | 33 +++++++ ui/template/tags.html | 87 +++++++------------ 8 files changed, 119 insertions(+), 79 deletions(-) delete mode 100644 internal/controller/template_render/paginator_test.go create mode 100644 internal/controller/template_render/tags.go create mode 100644 internal/schema/template_schema.go create mode 100644 ui/template/page.html diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index fa0ba1ba..a8fbba1d 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -190,7 +190,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, uiRouter := router.NewUIRouter() authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) - templateRenderController := templaterender.NewTemplateRenderController(questionService, userService) + templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService) templateController := controller.NewTemplateController(templateRenderController) templateRouter := router.NewTemplateRouter(templateController, templateRenderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index b566f2ba..a5aca76f 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -5,9 +5,11 @@ import ( "net/http" "regexp" + "github.com/answerdev/answer/internal/base/handler" templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/ui" + "github.com/davecgh/go-spew/spew" "github.com/gin-gonic/gin" ) @@ -68,9 +70,26 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { // TagList tags list func (tc *TemplateController) TagList(ctx *gin.Context) { + req := &schema.GetTagWithPageReq{} + if handler.BindAndCheck(ctx, req) { + return + } + data, err := tc.templateRenderController.TagList(ctx, req) + if err != nil { + ctx.HTML(http.StatusOK, "404.html", gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + "err": err.Error(), + }) + return + } + page := templaterender.Paginator(req.Page, req.PageSize, data.Count) + spew.Dump(page) ctx.HTML(http.StatusOK, "tags.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, + "page": page, + "data": data, }) } diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index d60e8d97..f9ea8186 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -3,7 +3,9 @@ package templaterender import ( "math" + "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service" + "github.com/answerdev/answer/internal/service/tag" "github.com/google/wire" ) @@ -15,16 +17,19 @@ var ProviderSetTemplateRenderController = wire.NewSet( type TemplateRenderController struct { questionService *service.QuestionService userService *service.UserService + tagService *tag.TagService } func NewTemplateRenderController( questionService *service.QuestionService, userService *service.UserService, + tagService *tag.TagService, ) *TemplateRenderController { return &TemplateRenderController{ questionService: questionService, userService: userService, + tagService: tagService, } } @@ -33,7 +38,10 @@ func NewTemplateRenderController( // prepage : Number per page // nums : Total // Returns the contents of the page in the format of 1, 2, 3, 4, and 5. If the contents are less than 5 pages, the page number is returned -func Paginator(page, prepage int, nums int64) map[string]interface{} { +func Paginator(page, prepage int, nums int64) *schema.Paginator { + if prepage == 0 { + prepage = 10 + } var firstpage int //Previous page address var lastpage int //Address on the last page @@ -72,11 +80,11 @@ func Paginator(page, prepage int, nums int64) map[string]interface{} { firstpage = int(math.Max(float64(1), float64(page-1))) lastpage = page + 1 } - paginatorMap := make(map[string]interface{}) - paginatorMap["pages"] = pages - paginatorMap["totalpages"] = totalpages - paginatorMap["firstpage"] = firstpage - paginatorMap["lastpage"] = lastpage - paginatorMap["currpage"] = page - return paginatorMap + paginator := &schema.Paginator{} + paginator.Pages = pages + paginator.Totalpages = totalpages + paginator.Firstpage = firstpage + paginator.Lastpage = lastpage + paginator.Currpage = page + return paginator } diff --git a/internal/controller/template_render/paginator_test.go b/internal/controller/template_render/paginator_test.go deleted file mode 100644 index a3ddf74f..00000000 --- a/internal/controller/template_render/paginator_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package templaterender - -import ( - "testing" - - "github.com/davecgh/go-spew/spew" -) - -func TestPaginator(t *testing.T) { - list := Paginator(5, 20, 300) - spew.Dump(list) -} diff --git a/internal/controller/template_render/tags.go b/internal/controller/template_render/tags.go new file mode 100644 index 00000000..8ff868d1 --- /dev/null +++ b/internal/controller/template_render/tags.go @@ -0,0 +1,12 @@ +package templaterender + +import ( + "github.com/answerdev/answer/internal/base/pager" + "github.com/answerdev/answer/internal/schema" + "golang.org/x/net/context" +) + +func (q *TemplateRenderController) TagList(ctx context.Context, req *schema.GetTagWithPageReq) (resp *pager.PageModel, err error) { + resp, err = q.tagService.GetTagWithPage(ctx, req) + return resp, err +} diff --git a/internal/schema/template_schema.go b/internal/schema/template_schema.go new file mode 100644 index 00000000..19bb3826 --- /dev/null +++ b/internal/schema/template_schema.go @@ -0,0 +1,9 @@ +package schema + +type Paginator struct { + Pages []int + Totalpages int + Firstpage int + Lastpage int + Currpage int +} diff --git a/ui/template/page.html b/ui/template/page.html new file mode 100644 index 00000000..a520f873 --- /dev/null +++ b/ui/template/page.html @@ -0,0 +1,33 @@ +{{define "page"}} +
    + {{$currpage := .page.Currpage}} + {{$totalpages := .page.Totalpages}} + {{$firstpage := .page.Firstpage}} + {{$lastpage := .page.Lastpage}} + {{ if ne $currpage 1 }} +
  • + Prev +
  • + {{ end }} + {{ range $value := .page.Pages }} + {{ if eq $currpage $value }} +
  • + {{$value}} + (current) + +
  • + {{ else }} +
  • + {{$value}} +
  • + {{ end }} + {{ end }} + {{ if lt $currpage $totalpages }} +
  • + Next +
  • + {{ end }} +
+{{end}} diff --git a/ui/template/tags.html b/ui/template/tags.html index f73fc943..36e4c39e 100644 --- a/ui/template/tags.html +++ b/ui/template/tags.html @@ -1,5 +1,5 @@ {{template "header" . }} -
-
-
-
-
-
-

Tags

-
-
-
- -
-
-
- 算法 -

212121212121221 -

-
- 0 - questions -
-
-
+
+
+
+
+
+

Tags

+
+
+
+ {{ range .data.List }} +
+
+
+ {{.SlugName}} +

{{.ParsedText}} +

+
+ {{.QuestionCount}} + questions
- - -
-
-
- 算法 -

212121212121221 -

-
- 0 - questions -
-
-
-
- - -
-
-
+ {{ end }} +
+
+ {{template "page" .}} +
+
+
+
{{template "footer" .}} - From cae46c94cfabaa633e0437f2430bc05c703b306c Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:27:23 +0800 Subject: [PATCH 020/272] update page --- .../controller/template_render/controller.go | 32 +++++++++---------- internal/schema/template_schema.go | 4 +-- ui/template/page.html | 14 +++----- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/internal/controller/template_render/controller.go b/internal/controller/template_render/controller.go index f9ea8186..ecb0b542 100644 --- a/internal/controller/template_render/controller.go +++ b/internal/controller/template_render/controller.go @@ -35,18 +35,18 @@ func NewTemplateRenderController( // Paginator page // page : now page -// prepage : Number per page +// pageSize : Number per page // nums : Total // Returns the contents of the page in the format of 1, 2, 3, 4, and 5. If the contents are less than 5 pages, the page number is returned -func Paginator(page, prepage int, nums int64) *schema.Paginator { - if prepage == 0 { - prepage = 10 +func Paginator(page, pageSize int, nums int64) *schema.Paginator { + if pageSize == 0 { + pageSize = 10 } - var firstpage int //Previous page address - var lastpage int //Address on the last page + var prevpage int //Previous page address + var nextpage int //Address on the last page //Generate the total number of pages based on the total number of nums and the number of prepage pages - totalpages := int(math.Ceil(float64(nums) / float64(prepage))) //Total number of Pages + totalpages := int(math.Ceil(float64(nums) / float64(pageSize))) //Total number of Pages if page > totalpages { page = totalpages } @@ -57,8 +57,8 @@ func Paginator(page, prepage int, nums int64) *schema.Paginator { switch { case page >= totalpages-5 && totalpages > 5: //The last 5 pages start := totalpages - 5 + 1 - firstpage = page - 1 - lastpage = int(math.Min(float64(totalpages), float64(page+1))) + prevpage = page - 1 + nextpage = int(math.Min(float64(totalpages), float64(page+1))) pages = make([]int, 5) for i, _ := range pages { pages[i] = start + i @@ -66,25 +66,25 @@ func Paginator(page, prepage int, nums int64) *schema.Paginator { case page >= 3 && totalpages > 5: start := page - 3 + 1 pages = make([]int, 5) - firstpage = page - 3 + prevpage = page - 3 for i, _ := range pages { pages[i] = start + i } - firstpage = page - 1 - lastpage = page + 1 + prevpage = page - 1 + nextpage = page + 1 default: pages = make([]int, int(math.Min(5, float64(totalpages)))) for i, _ := range pages { pages[i] = i + 1 } - firstpage = int(math.Max(float64(1), float64(page-1))) - lastpage = page + 1 + prevpage = int(math.Max(float64(1), float64(page-1))) + nextpage = page + 1 } paginator := &schema.Paginator{} paginator.Pages = pages paginator.Totalpages = totalpages - paginator.Firstpage = firstpage - paginator.Lastpage = lastpage + paginator.Prevpage = prevpage + paginator.Nextpage = nextpage paginator.Currpage = page return paginator } diff --git a/internal/schema/template_schema.go b/internal/schema/template_schema.go index 19bb3826..6446565e 100644 --- a/internal/schema/template_schema.go +++ b/internal/schema/template_schema.go @@ -3,7 +3,7 @@ package schema type Paginator struct { Pages []int Totalpages int - Firstpage int - Lastpage int + Prevpage int + Nextpage int Currpage int } diff --git a/ui/template/page.html b/ui/template/page.html index a520f873..a93106eb 100644 --- a/ui/template/page.html +++ b/ui/template/page.html @@ -1,17 +1,13 @@ {{define "page"}}
    - {{$currpage := .page.Currpage}} - {{$totalpages := .page.Totalpages}} - {{$firstpage := .page.Firstpage}} - {{$lastpage := .page.Lastpage}} - {{ if ne $currpage 1 }} + {{ if ne $.page.Currpage 1 }}
  • - Prev
  • {{ end }} {{ range $value := .page.Pages }} - {{ if eq $currpage $value }} + {{ if eq $.page.Currpage $value }}
  • {{$value}} (current) @@ -23,9 +19,9 @@
  • {{ end }} {{ end }} - {{ if lt $currpage $totalpages }} + {{ if lt $.page.Currpage $.page.Totalpages }}
  • - Next
  • {{ end }} From 972d11b0e50c6d2a9c0b32bd875ec48cc2cea99e Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 17:39:19 +0800 Subject: [PATCH 021/272] update homepage --- internal/base/server/http.go | 6 ++++++ internal/controller/template_controller.go | 16 +++++++++++++--- internal/controller/template_render/tags.go | 5 +++++ ui/template/homepage.html | 9 ++++++++- ui/template/tag-detail.html | 6 +++--- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index aeb11c5c..79d5bb8c 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -54,6 +54,12 @@ func NewHTTPServer(debug bool, cmsauthV1.Use(authUserMiddleware.CmsAuth()) answerRouter.RegisterAnswerCmsAPIRouter(cmsauthV1) + r.SetFuncMap(template.FuncMap{ + "templateHTML": func(data string) template.HTML { + return template.HTML(data) + }, + }) + dev := os.Getenv("DEVCODE") if dev != "" { r.LoadHTMLGlob("../../ui/template/*") diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index a5aca76f..d1b33152 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -9,7 +9,6 @@ import ( templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/ui" - "github.com/davecgh/go-spew/spew" "github.com/gin-gonic/gin" ) @@ -84,7 +83,6 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { return } page := templaterender.Paginator(req.Page, req.PageSize, data.Count) - spew.Dump(page) ctx.HTML(http.StatusOK, "tags.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, @@ -96,8 +94,20 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { // TagInfo taginfo func (tc *TemplateController) TagInfo(ctx *gin.Context) { tag := ctx.Param("tag") + + req := &schema.GetTagInfoReq{} + req.Name = tag + taginifo, err := tc.templateRenderController.TagInfo(ctx, req) + if err != nil { + ctx.HTML(http.StatusOK, "404.html", gin.H{ + "scriptPath": tc.scriptPath, + "cssPath": tc.cssPath, + "err": err.Error(), + }) + return + } ctx.HTML(http.StatusOK, "tag-detail.html", gin.H{ - "tag": tag, + "tag": taginifo, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) diff --git a/internal/controller/template_render/tags.go b/internal/controller/template_render/tags.go index 8ff868d1..c7443be6 100644 --- a/internal/controller/template_render/tags.go +++ b/internal/controller/template_render/tags.go @@ -10,3 +10,8 @@ func (q *TemplateRenderController) TagList(ctx context.Context, req *schema.GetT resp, err = q.tagService.GetTagWithPage(ctx, req) return resp, err } + +func (q *TemplateRenderController) TagInfo(ctx context.Context, req *schema.GetTagInfoReq) (resp *schema.GetTagResp, err error) { + resp, err = q.tagService.GetTagInfo(ctx, req) + return resp, err +} diff --git a/ui/template/homepage.html b/ui/template/homepage.html index 7cdab7e4..c9720b61 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -25,7 +25,10 @@ {{.userinfo.Info.QuestionCount}} questions
+ {{if .userinfo.Info.Website }} + {{else}} + {{end}}
@@ -39,7 +42,11 @@
About Me
-
{{.bio}}
+ {{if .bio }} +
{{.bio}}
+ {{else}} +
// Hello, World !
+ {{end}}
diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html index 2fbf32ed..a6a2fd6d 100644 --- a/ui/template/tag-detail.html +++ b/ui/template/tag-detail.html @@ -4,15 +4,15 @@

- Apple + {{$.tag.SlugName}}

- 对外只有3001一个端口,群集内有负载策略会分发给不同实例1111 + {{templateHTML $.tag.ParsedText}}

-
3 Questions
+
{{.tag.QuestionCount}} Questions
From 9a2e1b67975825615eae78cf8a1593e904d4c264 Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 30 Nov 2022 17:47:40 +0800 Subject: [PATCH 022/272] feat: render question list --- internal/controller/template_controller.go | 16 ++- .../controller/template_render/question.go | 15 ++- ui/template/page.html | 3 +- ui/template/question.html | 124 ++++++++---------- 4 files changed, 82 insertions(+), 76 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d1b33152..ea2f0fdd 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -49,9 +49,21 @@ func GetStyle() (script, css string) { // Index question list func (tc *TemplateController) Index(ctx *gin.Context) { + req := &schema.QuestionSearch{} + if handler.BindAndCheck(ctx, req) { + return + } + data, count, err := tc.templateRenderController.Index(ctx, req) + if err != nil { + tc.Page404(ctx) + return + } + ctx.HTML(http.StatusOK, "question.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, + "data": data, + "page": templaterender.Paginator(req.Page, req.PageSize, count), }) } @@ -120,7 +132,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { req.Username = username userinfo, err := tc.templateRenderController.UserInfo(ctx, req) if err != nil { - ctx.HTML(http.StatusOK, "404.html", gin.H{ + ctx.HTML(http.StatusNotFound, "404.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "err": err.Error(), @@ -136,7 +148,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { } func (tc *TemplateController) Page404(ctx *gin.Context) { - ctx.HTML(http.StatusOK, "404.html", gin.H{ + ctx.HTML(http.StatusNotFound, "404.html", gin.H{ "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index 5a9cb49b..4be63bbd 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -1,11 +1,16 @@ package templaterender -import "github.com/gin-gonic/gin" +import ( + "github.com/answerdev/answer/internal/schema" + "github.com/gin-gonic/gin" +) -func (q *TemplateRenderController) Index(ctx *gin.Context) { +func (t *TemplateRenderController) Index(ctx *gin.Context, req *schema.QuestionSearch) ([]*schema.QuestionInfo, int64, error) { + return t.questionService.SearchList(ctx, req, req.UserID) +} + +func (t *TemplateRenderController) QuestionDetail(ctx *gin.Context) { } -func (q *TemplateRenderController) QuestionDetail(ctx *gin.Context) {} - -func (q *TemplateRenderController) AnswerDetail(ctx *gin.Context) {} +func (t *TemplateRenderController) AnswerDetail(ctx *gin.Context) {} diff --git a/ui/template/page.html b/ui/template/page.html index a93106eb..73e7bbc9 100644 --- a/ui/template/page.html +++ b/ui/template/page.html @@ -1,6 +1,6 @@ {{define "page"}}
    - {{ if ne $.page.Currpage 1 }} + {{ if ne .page.Currpage 1 }}
  • Prev @@ -27,3 +27,4 @@ {{ end }}
{{end}} +ㅤ diff --git a/ui/template/question.html b/ui/template/question.html index b29200b1..b960a6c6 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -1,72 +1,60 @@ {{template "header" . }} -
-
-
-
-
-
All Questions
-
-
-
-
- 创建的时候报错了吗? -
-
-
- - • -
-
- 002 -
-
-
- bugnotag -
-
- - -
-
-
    -
  • - 1(current) -
  • -
  • - 2 -
  • -
  • - 3 -
  • -
  • - 4 -
  • -
  • - Next -
  • -
-
-
-
-
- -
+
+
+
+
+
+
All Questions
+
+
+ {{range .data}} +
+
+ {{.Title}} +
+
+
+
+ {{.UserInfo.DisplayName}}{{.UserInfo.Rank}}
+ • + +
+
+ {{.VoteCount}} + {{.AnswerCount}} + {{.ViewCount}} +
+
+ {{range .Tags }} + + {{.SlugName}} + + {{end}} +
+
+ {{end}} +
+
+ {{template "page" .}} +
+
+
+
+ +
+
+
{{template "footer" .}} From 888024403a14b2be8496354eee385ffab16685b1 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 17:48:35 +0800 Subject: [PATCH 023/272] update siteinfo --- internal/controller/template_controller.go | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d1b33152..4f0628ee 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -8,25 +8,30 @@ import ( "github.com/answerdev/answer/internal/base/handler" templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/internal/service/siteinfo_common" "github.com/answerdev/answer/ui" "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/log" ) type TemplateController struct { scriptPath string cssPath string templateRenderController *templaterender.TemplateRenderController + siteInfoService *siteinfo_common.SiteInfoCommonService } // NewTemplateController new controller func NewTemplateController( templateRenderController *templaterender.TemplateRenderController, + siteInfoService *siteinfo_common.SiteInfoCommonService, ) *TemplateController { script, css := GetStyle() return &TemplateController{ scriptPath: script, cssPath: css, templateRenderController: templateRenderController, + siteInfoService: siteInfoService, } } func GetStyle() (script, css string) { @@ -46,10 +51,29 @@ func GetStyle() (script, css string) { } return } +func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.SiteInfoResp { + var err error + resp := &schema.SiteInfoResp{} + resp.General, err = tc.siteInfoService.GetSiteGeneral(ctx) + if err != nil { + log.Error(err) + } + resp.Interface, err = tc.siteInfoService.GetSiteInterface(ctx) + if err != nil { + log.Error(err) + } + + resp.Branding, err = tc.siteInfoService.GetSiteBranding(ctx) + if err != nil { + log.Error(err) + } + return resp +} // Index question list func (tc *TemplateController) Index(ctx *gin.Context) { ctx.HTML(http.StatusOK, "question.html", gin.H{ + "siteinfo": tc.SiteInfo(ctx), "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) From a99a7dd1ce8e0624c7b0cae013227fae22ad02fd Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 17:56:25 +0800 Subject: [PATCH 024/272] add siteinfo --- cmd/answer/wire_gen.go | 2 +- internal/controller/template_controller.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index a8fbba1d..acfe373f 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -191,7 +191,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService) - templateController := controller.NewTemplateController(templateRenderController) + templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService) templateRouter := router.NewTemplateRouter(templateController, templateRenderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 35ef34a2..d54ddeb8 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -81,7 +81,6 @@ func (tc *TemplateController) Index(ctx *gin.Context) { tc.Page404(ctx) return } - ctx.HTML(http.StatusOK, "question.html", gin.H{ "siteinfo": tc.SiteInfo(ctx), "scriptPath": tc.scriptPath, From ae964c537ea7c165770671b191c72ba2c1be3579 Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 30 Nov 2022 17:58:33 +0800 Subject: [PATCH 025/272] fix: render question list, pagination error --- cmd/answer/wire_gen.go | 2 +- internal/controller/template_controller.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index a8fbba1d..acfe373f 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -191,7 +191,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService) - templateController := controller.NewTemplateController(templateRenderController) + templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService) templateRouter := router.NewTemplateRouter(templateController, templateRenderController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 35ef34a2..27a763ac 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -76,6 +76,12 @@ func (tc *TemplateController) Index(ctx *gin.Context) { if handler.BindAndCheck(ctx, req) { return } + + var ( + page = req.Page + pageSize = req.PageSize + ) + data, count, err := tc.templateRenderController.Index(ctx, req) if err != nil { tc.Page404(ctx) @@ -87,7 +93,7 @@ func (tc *TemplateController) Index(ctx *gin.Context) { "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "data": data, - "page": templaterender.Paginator(req.Page, req.PageSize, count), + "page": templaterender.Paginator(page, pageSize, count), }) } From abc9d9ad96c2b29489f139e500c44c6802ea196d Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 30 Nov 2022 18:02:58 +0800 Subject: [PATCH 026/272] fix: render question list, pagination error --- internal/controller/template_controller.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 7c9d94cd..903359ec 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -77,10 +77,7 @@ func (tc *TemplateController) Index(ctx *gin.Context) { return } - var ( - page = req.Page - pageSize = req.PageSize - ) + var page = req.Page data, count, err := tc.templateRenderController.Index(ctx, req) if err != nil { @@ -92,7 +89,7 @@ func (tc *TemplateController) Index(ctx *gin.Context) { "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "data": data, - "page": templaterender.Paginator(page, pageSize, count), + "page": templaterender.Paginator(page, req.PageSize, count), }) } From c2b1c60c5b2a3f928d9f004ba015230acd8bbf88 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 30 Nov 2022 18:10:38 +0800 Subject: [PATCH 027/272] add year --- internal/controller/template_controller.go | 15 +++++++++++++-- internal/schema/siteinfo_schema.go | 6 ++++++ ui/template/footer.html | 4 ++-- ui/template/header.html | 8 +++----- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d54ddeb8..12c9a2a2 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -1,9 +1,11 @@ package controller import ( + "fmt" "html/template" "net/http" "regexp" + "time" "github.com/answerdev/answer/internal/base/handler" templaterender "github.com/answerdev/answer/internal/controller/template_render" @@ -51,9 +53,9 @@ func GetStyle() (script, css string) { } return } -func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.SiteInfoResp { +func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.TemplateSiteInfoResp { var err error - resp := &schema.SiteInfoResp{} + resp := &schema.TemplateSiteInfoResp{} resp.General, err = tc.siteInfoService.GetSiteGeneral(ctx) if err != nil { log.Error(err) @@ -67,6 +69,7 @@ func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.SiteInfoResp { if err != nil { log.Error(err) } + resp.Year = fmt.Sprintf("%d", time.Now().Year()) return resp } @@ -99,6 +102,7 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { "answerid": answerid, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, + "siteinfo": tc.SiteInfo(ctx), }) } @@ -114,6 +118,7 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "err": err.Error(), + "siteinfo": tc.SiteInfo(ctx), }) return } @@ -123,6 +128,7 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { "cssPath": tc.cssPath, "page": page, "data": data, + "siteinfo": tc.SiteInfo(ctx), }) } @@ -138,6 +144,7 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "err": err.Error(), + "siteinfo": tc.SiteInfo(ctx), }) return } @@ -145,6 +152,7 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { "tag": taginifo, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, + "siteinfo": tc.SiteInfo(ctx), }) } @@ -156,6 +164,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { userinfo, err := tc.templateRenderController.UserInfo(ctx, req) if err != nil { ctx.HTML(http.StatusNotFound, "404.html", gin.H{ + "siteinfo": tc.SiteInfo(ctx), "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, "err": err.Error(), @@ -163,6 +172,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { return } ctx.HTML(http.StatusOK, "homepage.html", gin.H{ + "siteinfo": tc.SiteInfo(ctx), "userinfo": userinfo, "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, @@ -172,6 +182,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { func (tc *TemplateController) Page404(ctx *gin.Context) { ctx.HTML(http.StatusNotFound, "404.html", gin.H{ + "siteinfo": tc.SiteInfo(ctx), "scriptPath": tc.scriptPath, "cssPath": tc.cssPath, }) diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index 88c767e9..97fcc2d9 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -95,6 +95,12 @@ type SiteInfoResp struct { Interface *SiteInterfaceResp `json:"interface"` Branding *SiteBrandingResp `json:"branding"` } +type TemplateSiteInfoResp struct { + General *SiteGeneralResp `json:"general"` + Interface *SiteInterfaceResp `json:"interface"` + Branding *SiteBrandingResp `json:"branding"` + Year string +} // UpdateSMTPConfigReq get smtp config request type UpdateSMTPConfigReq struct { diff --git a/ui/template/footer.html b/ui/template/footer.html index 724e282d..45839a21 100644 --- a/ui/template/footer.html +++ b/ui/template/footer.html @@ -5,8 +5,8 @@

Built on Answer - - the open-source software that power Q&A communities
Made - with love © 2022 Answer + the open-source software that power Q&A communities.
Made + with love © {{.siteinfo.Year}} {{.siteinfo.General.Name}}.

diff --git a/ui/template/header.html b/ui/template/header.html index 1fa2f045..db2db8cc 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -20,11 +20,9 @@
From 4983482b322da3845496258033e08a32f31c1f6d Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Mon, 5 Dec 2022 12:06:51 +0800 Subject: [PATCH 048/272] feat(i18n): parse all translation file --- internal/base/translator/provider.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/base/translator/provider.go b/internal/base/translator/provider.go index 527f9137..8e945068 100644 --- a/internal/base/translator/provider.go +++ b/internal/base/translator/provider.go @@ -52,19 +52,19 @@ func NewTranslator(c *I18n) (tr i18n.Translator, err error) { } // only parse the backend translation - translation := struct { - Content map[string]interface{} `yaml:"backend"` - }{} - if err = yaml.Unmarshal(buf, &translation); err != nil { - return nil, err - } - content, err := yaml.Marshal(translation.Content) - if err != nil { - return nil, fmt.Errorf("marshal translation content failed: %s %s", file.Name(), err) - } + //translation := struct { + // Content map[string]interface{} `yaml:"backend"` + //}{} + //if err = yaml.Unmarshal(buf, &translation); err != nil { + // return nil, err + //} + //content, err := yaml.Marshal(translation.Content) + //if err != nil { + // return nil, fmt.Errorf("marshal translation content failed: %s %s", file.Name(), err) + //} // add translator use backend translation - if err = myTran.AddTranslator(content, file.Name()); err != nil { + if err = myTran.AddTranslator(buf, file.Name()); err != nil { return nil, fmt.Errorf("add translator failed: %s %s", file.Name(), err) } } From aee0baba9226a6c50b548ba006468b7e66f30cda Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 5 Dec 2022 14:56:18 +0800 Subject: [PATCH 049/272] feat(template/timeformat): backend time render synchronous with frontend --- i18n/en_US.yaml | 18 ++++-------------- i18n/zh_CN.yaml | 16 ++++------------ internal/base/server/http.go | 21 +++++++++++---------- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 2ac840c7..f7beb0a9 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -187,16 +187,6 @@ backend: other: "Your answer has been deleted" your_comment_was_deleted: other: "Your comment has been deleted" - dates: - long_date: "Jan 2" - long_date_with_year: "Jan 2, 2006" - long_date_with_time: "Jan 2, 2006 at 15:04" - now: now - x_seconds_ago: "{{count}}s ago" - x_minutes_ago: "{{count}}m ago" - x_hours_ago: "{{count}}h ago" - hour: hour - day: day # The following fields are used for interface presentation(Front-end) ui: @@ -384,10 +374,10 @@ ui: slug_name: label: URL Slug description: 'Must use the character set "a-z", "0-9", "+ # - ."' - msg: - empty: URL slug cannot be empty. - range: URL slug up to 35 characters. - character: URL slug contains unallowed character set. + msg: "todo" +# empty: URL slug cannot be empty. +# range: URL slug up to 35 characters. +# character: URL slug contains unallowed character set. description: label: Description (optional) btn_cancel: Cancel diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 05f570f0..ddf16db4 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -171,14 +171,6 @@ backend: other: "你的答案已被删除" your_comment_was_deleted: other: "你的评论已被删除" - dates: - long_date: "01月02日" - long_date_with_year: "2006年01月02日" - long_date_with_time: "2006年01月02日 15:04" - now: 刚刚 - x_seconds_ago: '{{count}} 秒前' - x_minutes_ago: '{{count}} 分钟前' - x_hours_ago: '{{count}} 小时前' # The following fields are used for interface presentation(Front-end) ui: how_to_format: @@ -361,10 +353,10 @@ ui: slug_name: label: URL 固定链接 description: '必须由 "a-z", "0-9", "+ # - ." 组成' - msg: - empty: 不能为空 - range: 不能超过 35 个字符 - character: 包含非法字符 + msg: "todo" +# empty: 不能为空 +# range: 不能超过 35 个字符 +# character: 包含非法字符 description: label: 标签描述(可选) btn_cancel: 取消 diff --git a/internal/base/server/http.go b/internal/base/server/http.go index d6e7611f..2fdb3c9b 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -3,6 +3,7 @@ package server import ( "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/pkg/converter" + "github.com/answerdev/answer/pkg/day" "html/template" "io/fs" "math" @@ -74,8 +75,8 @@ func NewHTTPServer(debug bool, return time.Unix(timestamp, 0).Format("2006-01-02T15:04:05.000Z") }, "translatorTimeFormatLongDate": func(la i18n.Language, tz string, timestamp int64) string { - trans := translator.GlobalTrans.Tr(la, "dates.long_date_with_time") - return time.Unix(timestamp, 0).Format(trans) + trans := translator.GlobalTrans.Tr(la, "ui.dates.long_date_with_time") + return day.Format(timestamp, trans, tz) }, "translatorTimeFormat": func(la i18n.Language, tz string, timestamp int64) string { var ( @@ -89,35 +90,35 @@ func NewHTTPServer(debug bool, } if between <= 1 { - return translator.GlobalTrans.Tr(la, "dates.now") + return translator.GlobalTrans.Tr(la, "ui.dates.now") } if between > 1 && between < 60 { - trans = translator.GlobalTrans.Tr(la, "dates.x_seconds_ago") + trans = translator.GlobalTrans.Tr(la, "ui.dates.x_seconds_ago") return strings.ReplaceAll(trans, "{{count}}", converter.IntToString(between)) } if between >= 60 && between < 3600 { min := math.Floor(float64(between / 60)) - trans = translator.GlobalTrans.Tr(la, "dates.x_minutes_ago") + trans = translator.GlobalTrans.Tr(la, "ui.dates.x_minutes_ago") return strings.ReplaceAll(trans, "{{count}}", strconv.FormatFloat(min, 'f', 0, 64)) } if between >= 3600 && between < 3600*24 { h := math.Floor(float64(between / 60)) - trans = translator.GlobalTrans.Tr(la, "dates.x_hours_ago") + trans = translator.GlobalTrans.Tr(la, "ui.dates.x_hours_ago") return strings.ReplaceAll(trans, "{{count}}", strconv.FormatFloat(h, 'f', 0, 64)) } if between >= 3600*24 && between < 3600*24*366 && time.Unix(timestamp, 0).Format("2006") == time.Unix(now, 0).Format("2006") { - trans = translator.GlobalTrans.Tr(la, "dates.long_date") - return time.Unix(timestamp, 0).Format(trans) + trans = translator.GlobalTrans.Tr(la, "ui.dates.long_date") + return day.Format(timestamp, trans, tz) } - trans = translator.GlobalTrans.Tr(la, "dates.long_date_with_year") - return time.Unix(timestamp, 0).Format(trans) + trans = translator.GlobalTrans.Tr(la, "ui.dates.long_date_with_year") + return day.Format(timestamp, trans, tz) }, "wrapComments": func(comments []*schema.GetCommentResp, la i18n.Language, tz string) map[string]interface{} { return map[string]interface{}{ From 23bb488731afdfe4e2c28cc51a52248919be0a23 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 5 Dec 2022 15:06:41 +0800 Subject: [PATCH 050/272] feat(template/timeformat): backend time render synchronous with frontend --- pkg/day/day.go | 169 ++++++++++++++++++++++++++++++++++++++++++++ pkg/day/day_test.go | 16 +++++ 2 files changed, 185 insertions(+) create mode 100644 pkg/day/day.go create mode 100644 pkg/day/day_test.go diff --git a/pkg/day/day.go b/pkg/day/day.go new file mode 100644 index 00000000..59fd8bcf --- /dev/null +++ b/pkg/day/day.go @@ -0,0 +1,169 @@ +package day + +import ( + "time" +) + +var placeholder = map[string]string{ + "YY": "06", // 06 year + "YYYY": "2006", // 2006 full year + "M": "1", // 1-12 month + "MM": "01", // 01-12 month + "MMM": "Jan", // Jan-Dec month + "MMMM": "January", // January-December month + "D": "2", // 1-31 date + "DD": "02", // 01-31 date preset 0 + "H": "15", // 00-23 hour 24 + "HH": "15", // 00-23 hour 24 + "h": "3", // 1-12 hour 12 + "hh": "03", // 01-12 hour 12 + "m": "4", // 0-59 minute + "mm": "04", // 00-59 minute + "s": "5", // 0-59 second + "ss": "05", // 00-59 second + "A": "PM", // AM / PM + "a": "pm", // am / pm + "[at]": "at", // at string +} + +func Format(unix int64, format, tz string) (formatted string) { + /*l := len(placeholders) - 1 + for i := l; i >= 0; i-- { + format = strings.ReplaceAll(format, placeholders[i].old, placeholders[i].new) + }*/ + toFormat := "" + for len(format) > 0 { + to, suffix := nextStdChunk(format) + toFormat += to + format = suffix + } + + _, _ = time.LoadLocation(tz) + formatted = time.Unix(unix, 0).Format(toFormat) + return +} + +func nextStdChunk(from string) (to, suffix string) { + if len(from) == 0 { + to = "" + suffix = "" + return + } + + s := string(from[0]) + old := "" + switch s { + case "Y": + if len(from) >= 4 && from[:4] == "YYYY" { + old = "YYYY" + } else if len(from) >= 2 && from[:2] == "YY" { + old = "YY" + } + case "M": + for i := 4; i > 0; i-- { + if len(from) >= i { + switch from[:i] { + case "MMMM": + old = "MMMM" + case "MMM": + old = "MMM" + case "MM": + old = "MM" + case "M": + old = "M" + } + } + if old != "" { + break + } + } + case "D": + for i := 2; i >= 0; i-- { + if len(from) >= i { + switch from[:i] { + case "DD": + old = "DD" + case "D": + old = "D" + } + } + if old != "" { + break + } + } + case "H": + for i := 2; i >= 0; i-- { + if len(from) >= i { + switch from[:i] { + case "HH": + old = "HH" + case "H": + old = "H" + } + } + if old != "" { + break + } + } + case "h": + for i := 2; i >= 0; i-- { + if len(from) >= i { + switch from[:i] { + case "hh": + old = "hh" + case "h": + old = "h" + } + } + if old != "" { + break + } + } + case "m": + for i := 2; i >= 0; i-- { + if len(from) >= i { + switch from[:i] { + case "mm": + old = "mm" + case "m": + old = "m" + } + } + if old != "" { + break + } + } + case "s": + for i := 2; i >= 0; i-- { + if len(from) >= i { + switch from[:i] { + case "ss": + old = "ss" + case "s": + old = "s" + } + } + if old != "" { + break + } + } + case "A": + old = "A" + case "a": + old = "a" + case "[": + if len(from) >= 4 && from[:4] == "[at]" { + old = "[at]" + } + default: + old = s + } + + to, ok := placeholder[old] + if !ok { + to = old + } + + suffix = from[len(old):] + return +} diff --git a/pkg/day/day_test.go b/pkg/day/day_test.go new file mode 100644 index 00000000..91cfc815 --- /dev/null +++ b/pkg/day/day_test.go @@ -0,0 +1,16 @@ +package day + +import ( + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestFormat(t *testing.T) { + sec := time.Now().Unix() + tz := "Asia/Shanghai" + actual := Format(sec, "YYYY-MM-DD HH:mm:ss", tz) + _, _ = time.LoadLocation(tz) + expected := time.Unix(sec, 0).Format("2006-01-02 15:04:05") + assert.Equal(t, expected, actual) +} From 892d76928f047fb403da2dc539e6e6d40731300b Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 5 Dec 2022 15:20:51 +0800 Subject: [PATCH 051/272] feat(template/timeformat): backend time render synchronous with frontend --- pkg/day/day.go | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/pkg/day/day.go b/pkg/day/day.go index 59fd8bcf..c698e789 100644 --- a/pkg/day/day.go +++ b/pkg/day/day.go @@ -32,10 +32,11 @@ func Format(unix int64, format, tz string) (formatted string) { format = strings.ReplaceAll(format, placeholders[i].old, placeholders[i].new) }*/ toFormat := "" - for len(format) > 0 { - to, suffix := nextStdChunk(format) - toFormat += to - format = suffix + from := []rune(format) + for len(from) > 0 { + to, suffix := nextStdChunk(from) + toFormat += string(to) + from = suffix } _, _ = time.LoadLocation(tz) @@ -43,26 +44,27 @@ func Format(unix int64, format, tz string) (formatted string) { return } -func nextStdChunk(from string) (to, suffix string) { +func nextStdChunk(from []rune) (to, suffix []rune) { if len(from) == 0 { - to = "" - suffix = "" + to = []rune{} + suffix = []rune{} return } s := string(from[0]) old := "" + switch s { case "Y": - if len(from) >= 4 && from[:4] == "YYYY" { + if len(from) >= 4 && string(from[:4]) == "YYYY" { old = "YYYY" - } else if len(from) >= 2 && from[:2] == "YY" { + } else if len(from) >= 2 && string(from[:2]) == "YY" { old = "YY" } case "M": for i := 4; i > 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "MMMM": old = "MMMM" case "MMM": @@ -80,7 +82,7 @@ func nextStdChunk(from string) (to, suffix string) { case "D": for i := 2; i >= 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "DD": old = "DD" case "D": @@ -94,7 +96,7 @@ func nextStdChunk(from string) (to, suffix string) { case "H": for i := 2; i >= 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "HH": old = "HH" case "H": @@ -108,7 +110,7 @@ func nextStdChunk(from string) (to, suffix string) { case "h": for i := 2; i >= 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "hh": old = "hh" case "h": @@ -122,7 +124,7 @@ func nextStdChunk(from string) (to, suffix string) { case "m": for i := 2; i >= 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "mm": old = "mm" case "m": @@ -136,7 +138,7 @@ func nextStdChunk(from string) (to, suffix string) { case "s": for i := 2; i >= 0; i-- { if len(from) >= i { - switch from[:i] { + switch string(from[:i]) { case "ss": old = "ss" case "s": @@ -152,18 +154,20 @@ func nextStdChunk(from string) (to, suffix string) { case "a": old = "a" case "[": - if len(from) >= 4 && from[:4] == "[at]" { + if len(from) >= 4 && string(from[:4]) == "[at]" { old = "[at]" } default: old = s } - to, ok := placeholder[old] + tos, ok := placeholder[old] if !ok { - to = old + to = []rune(old) + } else { + to = []rune(tos) } - suffix = from[len(old):] + suffix = from[len([]rune(old)):] return } From 82f1e72483e1c378098579a71c3e72d24beef088 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 5 Dec 2022 15:24:52 +0800 Subject: [PATCH 052/272] feat(template/i18n): backend template render synchronous with frontend --- internal/controller/template_controller.go | 3 ++- ui/template/comment.html | 2 +- ui/template/question-detail.html | 18 ++++++++++-------- ui/template/question.html | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d78f6a73..c986ad4e 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,6 +3,7 @@ package controller import ( "encoding/json" "fmt" + "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -307,7 +308,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = handler.GetLang(ctx) + data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) diff --git a/ui/template/comment.html b/ui/template/comment.html index 5866104d..26936b2d 100644 --- a/ui/template/comment.html +++ b/ui/template/comment.html @@ -24,7 +24,7 @@
diff --git a/ui/template/question-detail.html b/ui/template/question-detail.html index f46627f4..6bdf52ec 100644 --- a/ui/template/question-detail.html +++ b/ui/template/question-detail.html @@ -10,13 +10,13 @@ class="d-flex flex-wrap align-items-center fs-14 mb-3 text-secondary"> -
Viewed {{.detail.ViewCount}}
+
{{translator $.language "ui.question_detail.Views"}} {{.detail.ViewCount}}
@@ -79,7 +80,7 @@
@@ -116,7 +117,7 @@ {{if eq 2 .Adopted}} {{end}}
@@ -131,7 +132,8 @@
@@ -157,7 +159,7 @@
diff --git a/ui/template/question.html b/ui/template/question.html index bfbcaf35..8724d28e 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -4,7 +4,7 @@
-
All Questions
+
{{translator $.language "ui.question.all_questions"}}
{{range .data}} @@ -23,7 +23,7 @@ •
From e78747c172f04d99c20ad3f490cdfeb45a3cc769 Mon Sep 17 00:00:00 2001 From: kumfo Date: Mon, 5 Dec 2022 15:25:29 +0800 Subject: [PATCH 053/272] feat(template/i18n): backend template render synchronous with frontend --- internal/controller/template_controller.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index c986ad4e..d78f6a73 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,7 +3,6 @@ package controller import ( "encoding/json" "fmt" - "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -308,7 +307,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) + data["language"] = handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) From ceb8f099bfdbb74d66d46bad067dcfdb5f6c7c5b Mon Sep 17 00:00:00 2001 From: "haitao(lj)" Date: Mon, 5 Dec 2022 16:42:08 +0800 Subject: [PATCH 054/272] feat(permalink): permalink and permalink general setting done --- ui/src/common/interface.ts | 7 +++- ui/src/components/SchemaForm/index.tsx | 10 +++--- ui/src/components/Share/index.tsx | 9 +++-- ui/src/components/Tag/index.tsx | 2 +- ui/src/pages/Admin/Answers/index.tsx | 7 +++- ui/src/pages/Admin/Flags/index.tsx | 6 +++- ui/src/pages/Admin/General/index.tsx | 15 ++++---- ui/src/pages/Admin/Questions/index.tsx | 3 +- .../Ask/components/SearchQuestion/index.tsx | 3 +- ui/src/pages/Questions/Ask/index.tsx | 7 ++-- .../Detail/components/Question/index.tsx | 7 +++- .../components/RelatedQuestions/index.tsx | 3 +- ui/src/pages/Questions/EditAnswer/index.tsx | 15 ++++++-- ui/src/pages/Review/index.tsx | 12 +++---- .../Search/components/SearchItem/index.tsx | 11 ++++-- ui/src/pages/Tags/Detail/index.tsx | 6 ++-- ui/src/pages/Tags/Info/index.tsx | 3 +- ui/src/pages/Timeline/index.tsx | 12 +++++-- .../Personal/components/Answers/index.tsx | 7 +++- .../Personal/components/Comments/index.tsx | 13 ++++--- .../Personal/components/DefaultList/index.tsx | 9 +++-- .../Personal/components/Reputation/index.tsx | 13 ++++--- .../Personal/components/TopList/index.tsx | 14 +++++--- .../Users/Personal/components/Votes/index.tsx | 14 +++++--- ui/src/router/pathFactory.ts | 34 ++++++++++++------- ui/src/router/routes.ts | 18 ++++++---- ui/src/stores/siteInfo.ts | 5 ++- 27 files changed, 184 insertions(+), 81 deletions(-) diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index c3cd2918..ce37c548 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -279,7 +279,12 @@ export interface AdminSettingsGeneral { description: string; site_url: string; contact_email: string; - permalink: boolean; + /** + * 0: not set + * 1:with title + * 2: no title + */ + permalink: number; } export interface HeadInfo { diff --git a/ui/src/components/SchemaForm/index.tsx b/ui/src/components/SchemaForm/index.tsx index 52ed5bcc..f71f4476 100644 --- a/ui/src/components/SchemaForm/index.tsx +++ b/ui/src/components/SchemaForm/index.tsx @@ -14,13 +14,13 @@ export interface JSONSchema { required?: string[]; properties: { [key: string]: { - type: 'string' | 'boolean'; + type: 'string' | 'boolean' | 'number'; title: string; label?: string; description?: string; - enum?: Array; + enum?: Array; enumNames?: string[]; - default?: string | boolean; + default?: string | boolean | number; }; }; } @@ -477,8 +477,10 @@ const SchemaForm: FC = ({ export const initFormData = (schema: JSONSchema): Type.FormDataType => { const formData: Type.FormDataType = {}; Object.keys(schema.properties).forEach((key) => { + const v = schema.properties[key]?.default; + // TODO: set default value by property type formData[key] = { - value: '', + value: typeof v !== 'undefined' ? v : '', isInvalid: false, errorMsg: '', }; diff --git a/ui/src/components/Share/index.tsx b/ui/src/components/Share/index.tsx index 652dcef1..9cb9e042 100644 --- a/ui/src/components/Share/index.tsx +++ b/ui/src/components/Share/index.tsx @@ -6,6 +6,7 @@ import { FacebookShareButton, TwitterShareButton } from 'next-share'; import copy from 'copy-to-clipboard'; import { loggedUserInfoStore } from '@/stores'; +import { pathFactory } from '@/router/pathFactory'; interface IProps { type: 'answer' | 'question'; @@ -23,8 +24,12 @@ const Index: FC = ({ type, qid, aid, title }) => { let baseUrl = type === 'question' - ? `${window.location.origin}/questions/${qid}` - : `${window.location.origin}/questions/${qid}/${aid}`; + ? `${window.location.origin}${pathFactory.questionLanding(qid, title)}` + : `${window.location.origin}${pathFactory.answerLanding({ + questionId: qid, + questionTitle: title, + answerId: aid, + })}`; if (user.id) { baseUrl = `${baseUrl}?shareUserId=${user.username}`; } diff --git a/ui/src/components/Tag/index.tsx b/ui/src/components/Tag/index.tsx index 64e9f25b..eb0c9d49 100644 --- a/ui/src/components/Tag/index.tsx +++ b/ui/src/components/Tag/index.tsx @@ -18,7 +18,7 @@ const Index: FC = ({ className = '', textClassName = '', }) => { - href ||= pathFactory.tagLanding(data); + href ||= pathFactory.tagLanding(data?.slug_name); return ( { diff --git a/ui/src/pages/Admin/Flags/index.tsx b/ui/src/pages/Admin/Flags/index.tsx index ff85ea1e..92c2dd24 100644 --- a/ui/src/pages/Admin/Flags/index.tsx +++ b/ui/src/pages/Admin/Flags/index.tsx @@ -14,6 +14,7 @@ import { useReportModal } from '@/hooks'; import * as Type from '@/common/interface'; import { useFlagSearch } from '@/services'; import { escapeRemove } from '@/utils'; +import { pathFactory } from '@/router/pathFactory'; import '../index.scss'; @@ -101,7 +102,10 @@ const Flags: FC = () => { diff --git a/ui/src/pages/Admin/General/index.tsx b/ui/src/pages/Admin/General/index.tsx index 7e7a1bc1..1ab1f8b7 100644 --- a/ui/src/pages/Admin/General/index.tsx +++ b/ui/src/pages/Admin/General/index.tsx @@ -49,11 +49,12 @@ const General: FC = () => { description: t('contact_email.text'), }, permalink: { - type: 'boolean', + type: 'number', title: t('permalink.label'), description: t('permalink.text'), - enum: [true, false], + enum: [1, 2], enumNames: ['/questions/123/post-title', '/questions/123'], + default: 1, }, }, }; @@ -100,7 +101,6 @@ const General: FC = () => { const [formData, setFormData] = useState( initFormData(schema), ); - const onSubmit = (evt) => { evt.preventDefault(); evt.stopPropagation(); @@ -110,7 +110,7 @@ const General: FC = () => { short_description: formData.short_description.value, site_url: formData.site_url.value, contact_email: formData.contact_email.value, - permalink: formData.permalink.value, + permalink: Number(formData.permalink.value), }; updateGeneralSetting(reqParams) @@ -133,10 +133,13 @@ const General: FC = () => { if (!setting) { return; } - const formMeta = {}; - Object.keys(setting).forEach((k) => { + const formMeta: Type.FormDataType = {}; + Object.keys(formData).forEach((k) => { formMeta[k] = { ...formData[k], value: setting[k] }; }); + if (formMeta.permalink.value !== 1 && formMeta.permalink.value !== 2) { + formMeta.permalink.value = 1; + } setFormData({ ...formData, ...formMeta }); }, [setting]); diff --git a/ui/src/pages/Admin/Questions/index.tsx b/ui/src/pages/Admin/Questions/index.tsx index 924e6dd9..7da830e5 100644 --- a/ui/src/pages/Admin/Questions/index.tsx +++ b/ui/src/pages/Admin/Questions/index.tsx @@ -18,6 +18,7 @@ import { ADMIN_LIST_STATUS } from '@/common/constants'; import { useEditStatusModal, useReportModal } from '@/hooks'; import * as Type from '@/common/interface'; import { useQuestionSearch, changeQuestionStatus } from '@/services'; +import { pathFactory } from '@/router/pathFactory'; import '../index.scss'; @@ -138,7 +139,7 @@ const Questions: FC = () => { diff --git a/ui/src/pages/Questions/Ask/components/SearchQuestion/index.tsx b/ui/src/pages/Questions/Ask/components/SearchQuestion/index.tsx index 99a8026a..eb382c94 100644 --- a/ui/src/pages/Questions/Ask/components/SearchQuestion/index.tsx +++ b/ui/src/pages/Questions/Ask/components/SearchQuestion/index.tsx @@ -3,6 +3,7 @@ import { Accordion, ListGroup } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Icon } from '@/components'; +import { pathFactory } from '@/router/pathFactory'; import './index.scss'; @@ -28,7 +29,7 @@ const SearchQuestion = ({ similarQuestions }) => { as="a" className="link-dark" key={item.id} - href={`/questions/${item.id}`} + href={pathFactory.questionLanding(item.id, item.title)} target="_blank"> {item.title} diff --git a/ui/src/pages/Questions/Ask/index.tsx b/ui/src/pages/Questions/Ask/index.tsx index 6bd338e5..a94fb6a2 100644 --- a/ui/src/pages/Questions/Ask/index.tsx +++ b/ui/src/pages/Questions/Ask/index.tsx @@ -17,6 +17,7 @@ import { useQueryQuestionByTitle, } from '@/services'; import { handleFormError } from '@/utils'; +import { pathFactory } from '@/router/pathFactory'; import SearchQuestion from './components/SearchQuestion'; @@ -235,7 +236,7 @@ const Ask = () => { edit_summary: formData.edit_summary.value, }) .then(() => { - navigate(`/questions/${qid}`); + navigate(pathFactory.questionLanding(qid, params.title)); }) .catch((err) => { if (err.isError) { @@ -260,7 +261,7 @@ const Ask = () => { html: editorRef2.current.getHtml(), }) .then(() => { - navigate(`/questions/${id}`); + navigate(pathFactory.questionLanding(id, params.title)); }) .catch((err) => { if (err.isError) { @@ -269,7 +270,7 @@ const Ask = () => { } }); } else { - navigate(`/questions/${id}`); + navigate(pathFactory.questionLanding(id, params.title)); } } } diff --git a/ui/src/pages/Questions/Detail/components/Question/index.tsx b/ui/src/pages/Questions/Detail/components/Question/index.tsx index 92bdd235..458726f3 100644 --- a/ui/src/pages/Questions/Detail/components/Question/index.tsx +++ b/ui/src/pages/Questions/Detail/components/Question/index.tsx @@ -14,6 +14,7 @@ import { } from '@/components'; import { formatCount } from '@/utils'; import { following } from '@/services'; +import { pathFactory } from '@/router/pathFactory'; interface Props { data: any; @@ -57,10 +58,14 @@ const Index: FC = ({ data, initPage, hasAnswer, isLogged }) => { if (!data?.id) { return null; } + return (

- + {data.title} {data.status === 2 ? ` [${t('closed', { keyPrefix: 'question' })}]` diff --git a/ui/src/pages/Questions/Detail/components/RelatedQuestions/index.tsx b/ui/src/pages/Questions/Detail/components/RelatedQuestions/index.tsx index 3dd91c7f..2e29913e 100644 --- a/ui/src/pages/Questions/Detail/components/RelatedQuestions/index.tsx +++ b/ui/src/pages/Questions/Detail/components/RelatedQuestions/index.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; import { Icon } from '@/components'; import { useSimilarQuestion } from '@/services'; import { loggedUserInfoStore } from '@/stores'; +import { pathFactory } from '@/router/pathFactory'; interface Props { id: string; @@ -31,7 +32,7 @@ const Index: FC = ({ id }) => { action key={item.id} as={Link} - to={`/questions/${item.id}`}> + to={pathFactory.questionLanding(item.id, item.title)}>
{item.title}
{item.answer_count > 0 && (
{ edit_summary: formData.description.value, }; modifyAnswer(params).then(() => { - navigate(`/questions/${qid}/${aid}`); + navigate( + pathFactory.answerLanding({ + questionId: qid, + questionTitle: data?.question?.title, + answerId: aid, + }), + ); }); }; - const handleSelectedRevision = (e) => { const index = e.target.value; const revision = revisions[index]; @@ -136,7 +142,10 @@ const Ask = () => { - +
{data?.question.title}
diff --git a/ui/src/pages/Review/index.tsx b/ui/src/pages/Review/index.tsx index faa88516..3cf973eb 100644 --- a/ui/src/pages/Review/index.tsx +++ b/ui/src/pages/Review/index.tsx @@ -105,18 +105,18 @@ const Index: FC = () => { editBadge = t('question_edit'); editSummary ||= t('edit_question'); } else if (type === 'answer') { - itemLink = pathFactory.answerLanding( + itemLink = pathFactory.answerLanding({ // @ts-ignore - unreviewed_info.content.question_id, - info.title, - unreviewed_info.object_id, - ); + questionId: unreviewed_info.content.question_id, + questionTitle: info?.title, + answerId: unreviewed_info.object_id, + }); itemTitle = info?.title; editBadge = t('answer_edit'); editSummary ||= t('edit_answer'); } else if (type === 'tag') { const tagInfo = unreviewed_info.content as Type.Tag; - itemLink = pathFactory.tagLanding(tagInfo); + itemLink = pathFactory.tagLanding(tagInfo.slug_name); itemTitle = tagInfo.display_name; editBadge = t('tag_edit'); editSummary ||= t('edit_tag'); diff --git a/ui/src/pages/Search/components/SearchItem/index.tsx b/ui/src/pages/Search/components/SearchItem/index.tsx index fbbb67b8..91e0d364 100644 --- a/ui/src/pages/Search/components/SearchItem/index.tsx +++ b/ui/src/pages/Search/components/SearchItem/index.tsx @@ -2,6 +2,7 @@ import { memo, FC } from 'react'; import { ListGroupItem } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; +import { pathFactory } from '@/router/pathFactory'; import { Icon, Tag, FormatTime, BaseUserCard } from '@/components'; import type { SearchResItem } from '@/common/interface'; import { escapeRemove } from '@/utils'; @@ -14,9 +15,13 @@ const Index: FC = ({ data }) => { if (!data?.object_type) { return null; } - let itemUrl = `/questions/${data.object.id}`; - if (data.object_type === 'answer') { - itemUrl = `/questions/${data.object.question_id}/${data.object.id}`; + let itemUrl = pathFactory.questionLanding(data.object.id, data.object.title); + if (data.object_type === 'answer' && data.object.question_id) { + itemUrl = pathFactory.answerLanding({ + questionId: data.object.question_id, + questionTitle: data.object.title, + answerId: data.object.id, + }); } return ( diff --git a/ui/src/pages/Tags/Detail/index.tsx b/ui/src/pages/Tags/Detail/index.tsx index 1c32621a..763341ef 100644 --- a/ui/src/pages/Tags/Detail/index.tsx +++ b/ui/src/pages/Tags/Detail/index.tsx @@ -31,7 +31,9 @@ const Questions: FC = () => { if (tagResp) { const info = { ...tagResp }; if (info.main_tag_slug_name) { - navigate(pathFactory.tagLanding(info), { replace: true }); + navigate(pathFactory.tagLanding(info.main_tag_slug_name), { + replace: true, + }); return; } if (followResp) { @@ -63,7 +65,7 @@ const Questions: FC = () => {

{tagInfo.display_name} diff --git a/ui/src/pages/Tags/Info/index.tsx b/ui/src/pages/Tags/Info/index.tsx index 8e4a765a..b3be3e66 100644 --- a/ui/src/pages/Tags/Info/index.tsx +++ b/ui/src/pages/Tags/Info/index.tsx @@ -91,6 +91,7 @@ const TagIntroduction = () => { keyPrefix: 'page_title', })}`; } + return ( <> @@ -99,7 +100,7 @@ const TagIntroduction = () => {

{tagInfo.display_name} diff --git a/ui/src/pages/Timeline/index.tsx b/ui/src/pages/Timeline/index.tsx index d9f5fdda..dfab48d5 100644 --- a/ui/src/pages/Timeline/index.tsx +++ b/ui/src/pages/Timeline/index.tsx @@ -3,6 +3,7 @@ import { Container, Row, Col, Form, Table } from 'react-bootstrap'; import { Link, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; +import { pathFactory } from '@/router/pathFactory'; import { loggedUserInfoStore } from '@/stores'; import { getTimelineData } from '@/services'; import { PageTitle, Empty } from '@/components'; @@ -44,12 +45,19 @@ const Index: FC = () => { let linkUrl = ''; let pageTitle = ''; if (timelineData?.object_info.object_type === 'question') { - linkUrl = `/questions/${timelineData?.object_info.question_id}`; + linkUrl = pathFactory.questionLanding( + timelineData?.object_info.question_id, + timelineData?.object_info.title, + ); pageTitle = `${t('title_for_question')} ${timelineData?.object_info.title}`; } if (timelineData?.object_info.object_type === 'answer') { - linkUrl = `/questions/${timelineData?.object_info.question_id}/${timelineData?.object_info.answer_id}`; + linkUrl = pathFactory.answerLanding({ + questionId: timelineData?.object_info.question_id, + questionTitle: timelineData?.object_info.title, + answerId: timelineData?.object_info.answer_id, + }); pageTitle = `${t('title_for_answer', { title: timelineData?.object_info.title, author: timelineData?.object_info.display_name, diff --git a/ui/src/pages/Users/Personal/components/Answers/index.tsx b/ui/src/pages/Users/Personal/components/Answers/index.tsx index 4d7b6f1d..2f7077b4 100644 --- a/ui/src/pages/Users/Personal/components/Answers/index.tsx +++ b/ui/src/pages/Users/Personal/components/Answers/index.tsx @@ -3,6 +3,7 @@ import { ListGroup, ListGroupItem } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Icon, FormatTime, Tag } from '@/components'; +import { pathFactory } from '@/router/pathFactory'; interface Props { visible: boolean; @@ -20,7 +21,11 @@ const Index: FC = ({ visible, data }) => {
{item.question_info?.title} diff --git a/ui/src/pages/Users/Personal/components/Comments/index.tsx b/ui/src/pages/Users/Personal/components/Comments/index.tsx index f53e468e..6115c8b4 100644 --- a/ui/src/pages/Users/Personal/components/Comments/index.tsx +++ b/ui/src/pages/Users/Personal/components/Comments/index.tsx @@ -1,6 +1,7 @@ import { FC, memo } from 'react'; import { ListGroup, ListGroupItem } from 'react-bootstrap'; +import { pathFactory } from '@/router/pathFactory'; import { FormatTime } from '@/components'; interface Props { @@ -19,11 +20,15 @@ const Index: FC = ({ visible, data }) => { + ? pathFactory.questionLanding(item.object_id, item.title) + : pathFactory.answerLanding({ + questionId: item.question_id, + questionTitle: item.title, + answerId: item.object_id, + }) + }> {item.title}
= ({ visible, tabName, data }) => { if (!visible) { return null; } + return ( {data.map((item) => { @@ -25,9 +27,10 @@ const Index: FC = ({ visible, tabName, data }) => {
+ href={pathFactory.questionLanding( + tabName === 'questions' ? item.question_id : item.id, + item.title, + )}> {item.title} {tabName === 'questions' && item.status === 'closed' ? ` [${t('closed', { keyPrefix: 'question' })}]` diff --git a/ui/src/pages/Users/Personal/components/Reputation/index.tsx b/ui/src/pages/Users/Personal/components/Reputation/index.tsx index 6458e921..c034713e 100644 --- a/ui/src/pages/Users/Personal/components/Reputation/index.tsx +++ b/ui/src/pages/Users/Personal/components/Reputation/index.tsx @@ -3,6 +3,7 @@ import { ListGroup, ListGroupItem } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { FormatTime } from '@/components'; +import { pathFactory } from '@/router/pathFactory'; interface Props { visible: boolean; @@ -30,11 +31,15 @@ const Index: FC = ({ visible, data }) => {
+ ? pathFactory.questionLanding(item.object_id, item.title) + : pathFactory.answerLanding({ + questionId: item.question_id, + questionTitle: item.title, + answerId: item.object_id, + }) + }> {item.title}
diff --git a/ui/src/pages/Users/Personal/components/TopList/index.tsx b/ui/src/pages/Users/Personal/components/TopList/index.tsx index 4af1da3c..f7655875 100644 --- a/ui/src/pages/Users/Personal/components/TopList/index.tsx +++ b/ui/src/pages/Users/Personal/components/TopList/index.tsx @@ -2,6 +2,7 @@ import { FC, memo } from 'react'; import { ListGroup, ListGroupItem } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; +import { pathFactory } from '@/router/pathFactory'; import { Icon } from '@/components'; interface Props { @@ -10,7 +11,6 @@ interface Props { } const Index: FC = ({ data, type }) => { const { t } = useTranslation('translation', { keyPrefix: 'personal' }); - return ( {data?.map((item) => { @@ -19,11 +19,15 @@ const Index: FC = ({ data, type }) => { className="p-0 border-0 mb-2" key={type === 'answer' ? item.answer_id : item.question_id}> + ? pathFactory.answerLanding({ + questionId: item.question_id, + questionTitle: item.question_info?.title, + answerId: item.answer_id, + }) + : pathFactory.questionLanding(item.question_id, item.title) + }> {type === 'answer' ? item.question_info.title : item.title}
diff --git a/ui/src/pages/Users/Personal/components/Votes/index.tsx b/ui/src/pages/Users/Personal/components/Votes/index.tsx index 86a37aba..71d3bcf3 100644 --- a/ui/src/pages/Users/Personal/components/Votes/index.tsx +++ b/ui/src/pages/Users/Personal/components/Votes/index.tsx @@ -1,6 +1,7 @@ import { FC, memo } from 'react'; import { ListGroup, ListGroupItem } from 'react-bootstrap'; +import { pathFactory } from '@/router/pathFactory'; import { FormatTime } from '@/components'; interface Props { @@ -12,6 +13,7 @@ const Index: FC = ({ visible, data }) => { if (!visible || !data?.length) { return null; } + return ( {data.map((item) => { @@ -25,11 +27,15 @@ const Index: FC = ({ visible, data }) => {
+ ? pathFactory.questionLanding(item.question_id, item.title) + : pathFactory.answerLanding({ + questionId: item.question_id, + questionTitle: item.title, + answerId: item.answer_id, + }) + }> {item.title}
diff --git a/ui/src/router/pathFactory.ts b/ui/src/router/pathFactory.ts index 8fc5386b..00b2038f 100644 --- a/ui/src/router/pathFactory.ts +++ b/ui/src/router/pathFactory.ts @@ -1,15 +1,19 @@ import urlcat from 'urlcat'; -import type * as Type from '@/common/interface'; import Pattern from '@/common/pattern'; import { siteInfoStore } from '@/stores'; -const tagLanding = (tag: Type.Tag) => { - let slugName = tag.main_tag_slug_name || tag.slug_name || ''; +const tagLanding = (slugName: string) => { + if (!slugName) { + return '/tags'; + } slugName = slugName.toLowerCase(); return urlcat('/tags/:slugName', { slugName }); }; const tagInfo = (slugName: string) => { + if (!slugName) { + return '/tags'; + } slugName = slugName.toLowerCase(); return urlcat('/tags/:slugName/info', { slugName }); }; @@ -18,25 +22,31 @@ const tagEdit = (tagId: string) => { }; const questionLanding = (questionId: string, title: string = '') => { const { siteInfo } = siteInfoStore.getState(); - if (siteInfo.permalink) { + if (siteInfo.permalink === 1) { title = title.toLowerCase(); title = title.trim().replace(/\s+/g, '-'); title = title.replace(Pattern.emoji, ''); if (title) { - return urlcat('/questions/:questionId/:title', { questionId, title }); + return urlcat('/questions/:questionId/:slugPermalink', { + questionId, + slugPermalink: title, + }); } } return urlcat('/questions/:questionId', { questionId }); }; -const answerLanding = ( - questionId: string, - questionTitle: string = '', - answerId: string, -) => { - const questionLandingUrl = questionLanding(questionId, questionTitle); +const answerLanding = (params: { + questionId: string; + questionTitle?: string; + answerId: string; +}) => { + const questionLandingUrl = questionLanding( + params.questionId, + params.questionTitle, + ); return urlcat(`${questionLandingUrl}/:answerId`, { - answerId, + answerId: params.answerId, }); }; diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 16cff2f5..56bf46de 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -33,6 +33,17 @@ const routes: RouteNode[] = [ path: 'questions', page: 'pages/Questions', }, + { + path: 'questions/ask', + page: 'pages/Questions/Ask', + guard: async () => { + return guard.activated(); + }, + }, + { + path: 'questions/:qid', + page: 'pages/Questions/Detail', + }, { path: 'questions/:qid/:slugPermalink', page: 'pages/Questions/Detail', @@ -41,13 +52,6 @@ const routes: RouteNode[] = [ path: 'questions/:qid/:slugPermalink/:aid', page: 'pages/Questions/Detail', }, - { - path: 'questions/ask', - page: 'pages/Questions/Ask', - guard: async () => { - return guard.activated(); - }, - }, { path: 'posts/:qid/edit', page: 'pages/Questions/Ask', diff --git a/ui/src/stores/siteInfo.ts b/ui/src/stores/siteInfo.ts index 7d5055ab..98e7ecb8 100644 --- a/ui/src/stores/siteInfo.ts +++ b/ui/src/stores/siteInfo.ts @@ -14,11 +14,14 @@ const siteInfo = create((set) => ({ short_description: '', site_url: '', contact_email: '', - permalink: true, + permalink: 1, }, update: (params) => set((_) => { const o = { ..._.siteInfo, ...params }; + if (o.permalink !== 1 && o.permalink !== 2) { + o.permalink = 1; + } return { siteInfo: o, }; From 6e000bb5f105221fcac914508931f787aff9cfc5 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Mon, 5 Dec 2022 17:18:56 +0800 Subject: [PATCH 055/272] update seo config --- cmd/answer/wire_gen.go | 2 +- docs/docs.go | 113 ++++++++++++++++++ docs/swagger.json | 113 ++++++++++++++++++ docs/swagger.yaml | 68 +++++++++++ internal/base/constant/constant.go | 2 +- internal/base/server/http.go | 7 +- .../siteinfo_controller.go | 49 ++++++++ internal/router/answer_api_router.go | 2 + internal/router/template_router.go | 7 ++ internal/schema/siteinfo_schema.go | 6 + internal/service/siteinfo/siteinfo_service.go | 30 +++++ 11 files changed, 394 insertions(+), 5 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 016d73eb..0f908a60 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -192,7 +192,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService) templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService) - templateRouter := router.NewTemplateRouter(templateController, templateRenderController) + templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware, templateRouter) application := newApplication(serverConf, ginEngine) return application, func() { diff --git a/docs/docs.go b/docs/docs.go index 389e0df0..a1fe4c82 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -787,6 +787,77 @@ const docTemplate = `{ } } }, + "/answer/admin/api/siteinfo/seo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "get site seo information", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.SiteSeoResp" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "update site seo information", + "parameters": [ + { + "description": "seo", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.SiteSeoReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/admin/api/siteinfo/write": { "get": { "security": [ @@ -4337,6 +4408,26 @@ const docTemplate = `{ } } } + }, + "/robots.txt": { + "get": { + "description": "get site robots information", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "get site robots information", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { @@ -6037,6 +6128,28 @@ const docTemplate = `{ } } }, + "schema.SiteSeoReq": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, + "schema.SiteSeoResp": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, "schema.SiteWriteReq": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index a91beb70..08b798fb 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -775,6 +775,77 @@ } } }, + "/answer/admin/api/siteinfo/seo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "get site seo information", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.SiteSeoResp" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "update site seo information", + "parameters": [ + { + "description": "seo", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.SiteSeoReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/admin/api/siteinfo/write": { "get": { "security": [ @@ -4325,6 +4396,26 @@ } } } + }, + "/robots.txt": { + "get": { + "description": "get site robots information", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "get site robots information", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { @@ -6025,6 +6116,28 @@ } } }, + "schema.SiteSeoReq": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, + "schema.SiteSeoResp": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, "schema.SiteWriteReq": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6ec43e18..a03f6364 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1223,6 +1223,20 @@ definitions: terms_of_service_parsed_text: type: string type: object + schema.SiteSeoReq: + properties: + robots: + type: string + required: + - robots + type: object + schema.SiteSeoResp: + properties: + robots: + type: string + required: + - robots + type: object schema.SiteWriteReq: properties: recommend_tags: @@ -2071,6 +2085,47 @@ paths: summary: update site legal info tags: - admin + /answer/admin/api/siteinfo/seo: + get: + description: get site seo information + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.SiteSeoResp' + type: object + security: + - ApiKeyAuth: [] + summary: get site seo information + tags: + - admin + put: + description: update site seo information + parameters: + - description: seo + in: body + name: data + required: true + schema: + $ref: '#/definitions/schema.SiteSeoReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.RespBody' + security: + - ApiKeyAuth: [] + summary: update site seo information + tags: + - admin /answer/admin/api/siteinfo/write: get: description: get site interface @@ -4230,6 +4285,19 @@ paths: summary: UserList tags: - api-question + /robots.txt: + get: + description: get site robots information + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: get site robots information + tags: + - site securityDefinitions: ApiKeyAuth: in: header diff --git a/internal/base/constant/constant.go b/internal/base/constant/constant.go index c36a0f7f..de3850e8 100644 --- a/internal/base/constant/constant.go +++ b/internal/base/constant/constant.go @@ -56,5 +56,5 @@ const ( SiteTypeBranding = "branding" SiteTypeWrite = "write" SiteTypeLegal = "legal" + SiteTypeSeo = "seo" ) - diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 2fdb3c9b..7e724607 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -1,9 +1,6 @@ package server import ( - "github.com/answerdev/answer/internal/schema" - "github.com/answerdev/answer/pkg/converter" - "github.com/answerdev/answer/pkg/day" "html/template" "io/fs" "math" @@ -12,6 +9,10 @@ import ( "strings" "time" + "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/pkg/converter" + "github.com/answerdev/answer/pkg/day" + brotli "github.com/anargu/gin-brotli" "github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/base/translator" diff --git a/internal/controller_backyard/siteinfo_controller.go b/internal/controller_backyard/siteinfo_controller.go index 0950c225..a8323504 100644 --- a/internal/controller_backyard/siteinfo_controller.go +++ b/internal/controller_backyard/siteinfo_controller.go @@ -1,6 +1,8 @@ package controller_backyard import ( + "net/http" + "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/middleware" "github.com/answerdev/answer/internal/schema" @@ -85,6 +87,53 @@ func (sc *SiteInfoController) GetSiteLegal(ctx *gin.Context) { handler.HandleResponse(ctx, err, resp) } +// GetSeo get site seo information +// @Summary get site seo information +// @Description get site seo information +// @Security ApiKeyAuth +// @Tags admin +// @Produce json +// @Success 200 {object} handler.RespBody{data=schema.SiteSeoResp} +// @Router /answer/admin/api/siteinfo/seo [get] +func (sc *SiteInfoController) GetSeo(ctx *gin.Context) { + resp, err := sc.siteInfoService.GetSeo(ctx) + handler.HandleResponse(ctx, err, resp) +} + +// GetRobots get site robots information +// @Summary get site robots information +// @Description get site robots information +// @Tags site +// @Produce json +// @Success 200 {string} txt "" +// @Router /robots.txt [get] +func (sc *SiteInfoController) GetRobots(ctx *gin.Context) { + resp, err := sc.siteInfoService.GetSeo(ctx) + if err != nil { + ctx.String(http.StatusOK, "") + return + } + ctx.String(http.StatusOK, resp.Robots) +} + +// UpdateSeo update site seo information +// @Summary update site seo information +// @Description update site seo information +// @Security ApiKeyAuth +// @Tags admin +// @Produce json +// @Param data body schema.SiteSeoReq true "seo" +// @Success 200 {object} handler.RespBody{} +// @Router /answer/admin/api/siteinfo/seo [put] +func (sc *SiteInfoController) UpdateSeo(ctx *gin.Context) { + req := schema.SiteSeoReq{} + if handler.BindAndCheck(ctx, &req) { + return + } + err := sc.siteInfoService.SaveSeo(ctx, req) + handler.HandleResponse(ctx, err, nil) +} + // UpdateGeneral update site general information // @Summary update site general information // @Description update site general information diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index 7640dc0f..fbf96d02 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -231,11 +231,13 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) { r.GET("/siteinfo/branding", a.siteInfoController.GetSiteBranding) r.GET("/siteinfo/write", a.siteInfoController.GetSiteWrite) r.GET("/siteinfo/legal", a.siteInfoController.GetSiteLegal) + r.GET("/siteinfo/seo", a.siteInfoController.GetSeo) r.PUT("/siteinfo/general", a.siteInfoController.UpdateGeneral) r.PUT("/siteinfo/interface", a.siteInfoController.UpdateInterface) r.PUT("/siteinfo/branding", a.siteInfoController.UpdateBranding) r.PUT("/siteinfo/write", a.siteInfoController.UpdateSiteWrite) r.PUT("/siteinfo/legal", a.siteInfoController.UpdateSiteLegal) + r.PUT("/siteinfo/seo", a.siteInfoController.UpdateSeo) r.GET("/setting/smtp", a.siteInfoController.GetSMTPConfig) r.PUT("/setting/smtp", a.siteInfoController.UpdateSMTPConfig) diff --git a/internal/router/template_router.go b/internal/router/template_router.go index b72a54d4..af108081 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -3,27 +3,34 @@ package router import ( "github.com/answerdev/answer/internal/controller" templaterender "github.com/answerdev/answer/internal/controller/template_render" + "github.com/answerdev/answer/internal/controller_backyard" "github.com/gin-gonic/gin" ) type TemplateRouter struct { templateController *controller.TemplateController templateRenderController *templaterender.TemplateRenderController + siteInfoController *controller_backyard.SiteInfoController } func NewTemplateRouter( templateController *controller.TemplateController, templateRenderController *templaterender.TemplateRenderController, + siteInfoController *controller_backyard.SiteInfoController, + ) *TemplateRouter { return &TemplateRouter{ templateController: templateController, templateRenderController: templateRenderController, + siteInfoController: siteInfoController, } } // TemplateRouter template router func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { + r.GET("/robots.txt", a.siteInfoController.GetRobots) + r.GET("/", a.templateController.Index) r.GET("/index", a.templateController.Index) diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index b92213f5..9e77a0f4 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -18,6 +18,10 @@ type SiteGeneralReq struct { PermaLink int `validate:"required,lte=3,gte=0" form:"permalink" json:"permalink"` } +type SiteSeoReq struct { + Robots string `validate:"required" form:"robots" json:"robots"` +} + func (r *SiteGeneralReq) FormatSiteUrl() { parsedUrl, err := url.Parse(r.SiteUrl) if err != nil { @@ -93,6 +97,8 @@ type SiteWriteResp SiteWriteReq // SiteLegalResp site write response type SiteLegalResp SiteLegalReq +type SiteSeoResp SiteSeoReq + // SiteInfoResp get site info response type SiteInfoResp struct { General *SiteGeneralResp `json:"general"` diff --git a/internal/service/siteinfo/siteinfo_service.go b/internal/service/siteinfo/siteinfo_service.go index 256906fe..e9460a4d 100644 --- a/internal/service/siteinfo/siteinfo_service.go +++ b/internal/service/siteinfo/siteinfo_service.go @@ -241,3 +241,33 @@ func (s *SiteInfoService) UpdateSMTPConfig(ctx context.Context, req *schema.Upda } return } + +func (s *SiteInfoService) GetSeo(ctx context.Context) (resp *schema.SiteSeoResp, err error) { + resp = &schema.SiteSeoResp{} + siteInfo, exist, err := s.siteInfoRepo.GetByType(ctx, constant.SiteTypeSeo) + if err != nil { + log.Error(err) + return resp, nil + } + if !exist { + return resp, nil + } + _ = json.Unmarshal([]byte(siteInfo.Content), resp) + return resp, nil +} + +func (s *SiteInfoService) SaveSeo(ctx context.Context, req schema.SiteSeoReq) (err error) { + var ( + siteType = constant.SiteTypeSeo + content []byte + ) + content, _ = json.Marshal(req) + + data := entity.SiteInfo{ + Type: siteType, + Content: string(content), + } + + err = s.siteInfoRepo.SaveByType(ctx, siteType, &data) + return +} From 5088c3a9efc319cfc0868a6c19f9ad016304e2ac Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Mon, 5 Dec 2022 17:20:30 +0800 Subject: [PATCH 056/272] feat(role): log out user when user was changed role --- cmd/answer/wire_gen.go | 2 +- internal/base/constant/constant.go | 2 +- internal/repo/auth/auth.go | 43 +++++++++++++++++++ internal/service/auth/auth.go | 12 ++++++ .../service/user_backyard/user_backyard.go | 13 +++++- 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index fcb4ca2a..e0747ba3 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -179,7 +179,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo) controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService) userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo) - userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService) + userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService) userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService) reasonRepo := reason.NewReasonRepo(configRepo) reasonService := reason2.NewReasonService(reasonRepo) diff --git a/internal/base/constant/constant.go b/internal/base/constant/constant.go index c36a0f7f..68f4316c 100644 --- a/internal/base/constant/constant.go +++ b/internal/base/constant/constant.go @@ -11,6 +11,7 @@ const ( AdminTokenCacheKey = "answer:admin:token:" AdminTokenCacheTime = 7 * 24 * time.Hour AcceptLanguageFlag = "Accept-Language" + UserTokenMappingCacheKey = "answer:user-token:mapping:" ) const ( @@ -57,4 +58,3 @@ const ( SiteTypeWrite = "write" SiteTypeLegal = "legal" ) - diff --git a/internal/repo/auth/auth.go b/internal/repo/auth/auth.go index 9f5a1813..6a5dc61e 100644 --- a/internal/repo/auth/auth.go +++ b/internal/repo/auth/auth.go @@ -10,6 +10,7 @@ import ( "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/service/auth" "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" ) // authRepo auth repository @@ -42,6 +43,9 @@ func (ar *authRepo) SetUserCacheInfo(ctx context.Context, accessToken string, us if err != nil { return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + if err := ar.AddUserTokenMapping(ctx, userInfo.UserID, accessToken); err != nil { + log.Error(err) + } return nil } @@ -130,6 +134,45 @@ func (ar *authRepo) RemoveBackyardUserCacheInfo(ctx context.Context, accessToken return nil } +// AddUserTokenMapping add user token mapping +func (ar *authRepo) AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error) { + key := constant.UserTokenMappingCacheKey + userID + resp, _ := ar.data.Cache.GetString(ctx, key) + mapping := make(map[string]bool, 0) + if len(resp) > 0 { + _ = json.Unmarshal([]byte(resp), &mapping) + } + mapping[accessToken] = true + content, _ := json.Marshal(mapping) + return ar.data.Cache.SetString(ctx, key, string(content), constant.UserTokenCacheTime) +} + +// RemoveAllUserTokens Log out all users under this user id +func (ar *authRepo) RemoveAllUserTokens(ctx context.Context, userID string) { + key := constant.UserTokenMappingCacheKey + userID + resp, _ := ar.data.Cache.GetString(ctx, key) + mapping := make(map[string]bool, 0) + if len(resp) > 0 { + _ = json.Unmarshal([]byte(resp), &mapping) + log.Debugf("find %d user tokens by user id %s", len(mapping), userID) + } + + for token := range mapping { + if err := ar.RemoveUserCacheInfo(ctx, token); err != nil { + log.Error(err) + } else { + log.Debugf("del user %s token success") + } + } + if err := ar.RemoveUserStatus(ctx, userID); err != nil { + log.Error(err) + } + if err := ar.data.Cache.Del(ctx, key); err != nil { + log.Error(err) + } + return +} + // NewAuthRepo new repository func NewAuthRepo(data *data.Data) auth.AuthRepo { return &authRepo{ diff --git a/internal/service/auth/auth.go b/internal/service/auth/auth.go index 1f77d174..9f3bbb75 100644 --- a/internal/service/auth/auth.go +++ b/internal/service/auth/auth.go @@ -19,6 +19,8 @@ type AuthRepo interface { GetBackyardUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) SetBackyardUserCacheInfo(ctx context.Context, accessToken string, userInfo *entity.UserCacheInfo) error RemoveBackyardUserCacheInfo(ctx context.Context, accessToken string) (err error) + AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error) + RemoveAllUserTokens(ctx context.Context, userID string) } // AuthService kit service @@ -78,6 +80,16 @@ func (as *AuthService) RemoveUserCacheInfo(ctx context.Context, accessToken stri return as.authRepo.RemoveUserCacheInfo(ctx, accessToken) } +// AddUserTokenMapping add user token mapping +func (as *AuthService) AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error) { + return as.authRepo.AddUserTokenMapping(ctx, userID, accessToken) +} + +// RemoveAllUserTokens Log out all users under this user id +func (as *AuthService) RemoveAllUserTokens(ctx context.Context, userID string) { + as.authRepo.RemoveAllUserTokens(ctx, userID) +} + //cms func (as *AuthService) GetCmsUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) { diff --git a/internal/service/user_backyard/user_backyard.go b/internal/service/user_backyard/user_backyard.go index f3689a56..8bf0e2e7 100644 --- a/internal/service/user_backyard/user_backyard.go +++ b/internal/service/user_backyard/user_backyard.go @@ -12,6 +12,7 @@ import ( "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/internal/service/auth" "github.com/answerdev/answer/internal/service/role" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" @@ -30,16 +31,19 @@ type UserBackyardRepo interface { type UserBackyardService struct { userRepo UserBackyardRepo userRoleRelService *role.UserRoleRelService + authService *auth.AuthService } // NewUserBackyardService new user backyard service func NewUserBackyardService( userRepo UserBackyardRepo, userRoleRelService *role.UserRoleRelService, + authService *auth.AuthService, ) *UserBackyardService { return &UserBackyardService{ userRepo: userRepo, userRoleRelService: userRoleRelService, + authService: authService, } } @@ -80,7 +84,14 @@ func (us *UserBackyardService) UpdateUserRole(ctx context.Context, req *schema.U if req.UserID == req.LoginUserID { return errors.BadRequest(reason.UserCannotUpdateYourRole) } - return us.userRoleRelService.SaveUserRole(ctx, req.UserID, req.RoleID) + + err = us.userRoleRelService.SaveUserRole(ctx, req.UserID, req.RoleID) + if err != nil { + return err + } + + us.authService.RemoveAllUserTokens(ctx, req.UserID) + return } // GetUserInfo get user one From 1812d59cd9e5a229025075a4bd7b969b34b9e007 Mon Sep 17 00:00:00 2001 From: "haitao(lj)" Date: Tue, 6 Dec 2022 10:35:06 +0800 Subject: [PATCH 057/272] feat(seo): add robots setting for seo --- i18n/en_US.yaml | 6 ++ ui/src/common/constants.ts | 1 + ui/src/common/interface.ts | 4 ++ ui/src/pages/Admin/Answers/index.tsx | 2 - ui/src/pages/Admin/Flags/index.tsx | 2 - ui/src/pages/Admin/General/index.tsx | 2 - ui/src/pages/Admin/Legal/index.tsx | 1 - ui/src/pages/Admin/Questions/index.tsx | 2 - ui/src/pages/Admin/Seo/index.tsx | 86 ++++++++++++++++++++++++++ ui/src/pages/Admin/Smtp/index.tsx | 3 +- ui/src/pages/Admin/Users/index.tsx | 2 - ui/src/pages/Admin/Write/index.tsx | 7 +-- ui/src/router/routes.ts | 4 ++ ui/src/services/admin/settings.ts | 8 +++ 14 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 ui/src/pages/Admin/Seo/index.tsx diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 5dd61252..0f5e5e8b 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -955,6 +955,7 @@ ui: write: Write tos: Terms of Service privacy: Privacy + seo: SEO admin: admin_header: title: Admin @@ -1204,6 +1205,11 @@ ui: reserved_tags: label: Reserved Tags text: "Reserved tags can only be added to a post by moderator." + seo: + page_title: SEO + robots: + label: robots.txt + text: This will permanently override any related site settings. form: empty: cannot be empty invalid: is invalid diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index ab06f5f9..90a07fd6 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -62,6 +62,7 @@ export const ADMIN_NAV_MENUS = [ { name: 'smtp' }, { name: 'legal' }, { name: 'write' }, + { name: 'seo' }, ], }, ]; diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index ce37c548..1e9a6ba5 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -338,6 +338,10 @@ export interface AdminSettingsWrite { reserved_tags: string[]; } +export interface AdminSettingsSeo { + robots: string; +} + /** * @description interface for Activity */ diff --git a/ui/src/pages/Admin/Answers/index.tsx b/ui/src/pages/Admin/Answers/index.tsx index 72a98990..f2a3f01a 100644 --- a/ui/src/pages/Admin/Answers/index.tsx +++ b/ui/src/pages/Admin/Answers/index.tsx @@ -21,8 +21,6 @@ import { useAnswerSearch, changeAnswerStatus } from '@/services'; import { escapeRemove } from '@/utils'; import { pathFactory } from '@/router/pathFactory'; -import '../index.scss'; - const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted']; const Answers: FC = () => { diff --git a/ui/src/pages/Admin/Flags/index.tsx b/ui/src/pages/Admin/Flags/index.tsx index 92c2dd24..79164715 100644 --- a/ui/src/pages/Admin/Flags/index.tsx +++ b/ui/src/pages/Admin/Flags/index.tsx @@ -16,8 +16,6 @@ import { useFlagSearch } from '@/services'; import { escapeRemove } from '@/utils'; import { pathFactory } from '@/router/pathFactory'; -import '../index.scss'; - const flagFilterKeys: Type.FlagStatus[] = ['pending', 'completed']; const flagTypeKeys: Type.FlagType[] = ['all', 'question', 'answer', 'comment']; diff --git a/ui/src/pages/Admin/General/index.tsx b/ui/src/pages/Admin/General/index.tsx index 1ab1f8b7..289ceb56 100644 --- a/ui/src/pages/Admin/General/index.tsx +++ b/ui/src/pages/Admin/General/index.tsx @@ -9,8 +9,6 @@ import { useGeneralSetting, updateGeneralSetting } from '@/services'; import Pattern from '@/common/pattern'; import { handleFormError } from '@/utils'; -import '../index.scss'; - const General: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.general', diff --git a/ui/src/pages/Admin/Legal/index.tsx b/ui/src/pages/Admin/Legal/index.tsx index 1e6e4dd3..1438b20a 100644 --- a/ui/src/pages/Admin/Legal/index.tsx +++ b/ui/src/pages/Admin/Legal/index.tsx @@ -8,7 +8,6 @@ import { SchemaForm, JSONSchema, initFormData, UISchema } from '@/components'; import { useToast } from '@/hooks'; import { getLegalSetting, putLegalSetting } from '@/services'; import { handleFormError } from '@/utils'; -import '../index.scss'; const Legal: FC = () => { const { t } = useTranslation('translation', { diff --git a/ui/src/pages/Admin/Questions/index.tsx b/ui/src/pages/Admin/Questions/index.tsx index 7da830e5..c1694ce4 100644 --- a/ui/src/pages/Admin/Questions/index.tsx +++ b/ui/src/pages/Admin/Questions/index.tsx @@ -20,8 +20,6 @@ import * as Type from '@/common/interface'; import { useQuestionSearch, changeQuestionStatus } from '@/services'; import { pathFactory } from '@/router/pathFactory'; -import '../index.scss'; - const questionFilterItems: Type.AdminContentsFilterBy[] = [ 'normal', 'closed', diff --git a/ui/src/pages/Admin/Seo/index.tsx b/ui/src/pages/Admin/Seo/index.tsx new file mode 100644 index 00000000..932d7d22 --- /dev/null +++ b/ui/src/pages/Admin/Seo/index.tsx @@ -0,0 +1,86 @@ +import { FC, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import type * as Type from '@/common/interface'; +import { getSeoSetting, putSeoSetting } from '@/services'; +import { SchemaForm, JSONSchema, initFormData, UISchema } from '@/components'; +import { useToast } from '@/hooks'; +import { handleFormError } from '@/utils'; + +const Index: FC = () => { + const { t } = useTranslation('translation', { + keyPrefix: 'admin.seo', + }); + const Toast = useToast(); + const schema: JSONSchema = { + title: t('page_title'), + properties: { + robots: { + type: 'string', + title: t('robots.label'), + description: t('robots.text'), + }, + }, + }; + const uiSchema: UISchema = { + robots: { + 'ui:widget': 'textarea', + 'ui:options': { + rows: 10, + }, + }, + }; + const [formData, setFormData] = useState(initFormData(schema)); + + const onSubmit = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + + const reqParams: Type.AdminSettingsSeo = { + robots: formData.robots.value, + }; + + putSeoSetting(reqParams) + .then(() => { + Toast.onShow({ + msg: t('update', { keyPrefix: 'toast' }), + variant: 'success', + }); + }) + .catch((err) => { + if (err.isError) { + const data = handleFormError(err, formData); + setFormData({ ...data }); + } + }); + }; + + useEffect(() => { + getSeoSetting().then((setting) => { + if (setting) { + const formMeta = { ...formData }; + formMeta.robots.value = setting.robots; + setFormData(formMeta); + } + }); + }, []); + + const handleOnChange = (data) => { + setFormData(data); + }; + + return ( + <> +

{t('page_title')}

+ + + ); +}; + +export default Index; diff --git a/ui/src/pages/Admin/Smtp/index.tsx b/ui/src/pages/Admin/Smtp/index.tsx index a60ed5c1..ba902aa4 100644 --- a/ui/src/pages/Admin/Smtp/index.tsx +++ b/ui/src/pages/Admin/Smtp/index.tsx @@ -5,8 +5,7 @@ import type * as Type from '@/common/interface'; import { useToast } from '@/hooks'; import { useSmtpSetting, updateSmtpSetting } from '@/services'; import pattern from '@/common/pattern'; -import { SchemaForm, JSONSchema, UISchema } from '@/components'; -import { initFormData } from '../../../components/SchemaForm/index'; +import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components'; import { handleFormError } from '@/utils'; const Smtp: FC = () => { diff --git a/ui/src/pages/Admin/Users/index.tsx b/ui/src/pages/Admin/Users/index.tsx index 89b92bd4..4ad54bc4 100644 --- a/ui/src/pages/Admin/Users/index.tsx +++ b/ui/src/pages/Admin/Users/index.tsx @@ -18,8 +18,6 @@ import { useChangeModal, useChangeUserRoleModal, useToast } from '@/hooks'; import { useQueryUsers } from '@/services'; import { loggedUserInfoStore } from '@/stores'; -import '../index.scss'; - const UserFilterKeys: Type.UserFilterBy[] = [ 'all', 'staff', diff --git a/ui/src/pages/Admin/Write/index.tsx b/ui/src/pages/Admin/Write/index.tsx index ce2c4335..1df9788f 100644 --- a/ui/src/pages/Admin/Write/index.tsx +++ b/ui/src/pages/Admin/Write/index.tsx @@ -10,14 +10,11 @@ import { } from '@/services'; import { handleFormError } from '@/utils'; -import '../index.scss'; - -const Legal: FC = () => { +const Index: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.write', }); const Toast = useToast(); - // const updateSiteInfo = siteInfoStore((state) => state.update); const schema: JSONSchema = { title: t('page_title'), @@ -125,4 +122,4 @@ const Legal: FC = () => { ); }; -export default Legal; +export default Index; diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 56bf46de..950d997b 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -276,6 +276,10 @@ const routes: RouteNode[] = [ path: 'write', page: 'pages/Admin/Write', }, + { + path: 'seo', + page: 'pages/Admin/Seo', + }, ], }, // for review diff --git a/ui/src/services/admin/settings.ts b/ui/src/services/admin/settings.ts index e24cf34f..6e0028c1 100644 --- a/ui/src/services/admin/settings.ts +++ b/ui/src/services/admin/settings.ts @@ -101,3 +101,11 @@ export const getLegalSetting = () => { export const putLegalSetting = (params: Type.AdminSettingsLegal) => { return request.put('/answer/admin/api/siteinfo/legal', params); }; + +export const getSeoSetting = () => { + return request.get('/answer/admin/api/siteinfo/seo'); +}; + +export const putSeoSetting = (params: Type.AdminSettingsSeo) => { + return request.put('/answer/admin/api/siteinfo/seo', params); +}; From 67d93e196f19c38324accc464e8a0e652e571fe3 Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 12:16:43 +0800 Subject: [PATCH 058/272] feat(template/i18n): backend template render synchronous with frontend --- internal/base/server/http.go | 13 +++- internal/controller/template_controller.go | 3 +- pkg/converter/str.go | 4 ++ ui/template/header.html | 71 ++++++++++++---------- ui/template/page.html | 8 +-- ui/template/tag-detail.html | 49 +++++++-------- ui/template/tags.html | 4 +- 7 files changed, 86 insertions(+), 66 deletions(-) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 7e724607..0897b8b4 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -68,8 +68,17 @@ func NewHTTPServer(debug bool, "templateHTML": func(data string) template.HTML { return template.HTML(data) }, - "translator": func(la i18n.Language, data string) string { - return translator.GlobalTrans.Tr(la, data) + "translator": func(la i18n.Language, data string, params ...interface{}) string { + /*if len(params) > 0 && len(params)%2 == 0 { + for i := 0; i < len(params); i += 2 { + k := converter.InterfaceToString(params[i]) + v := converter.InterfaceToString(params[i+1]) + data = strings.ReplaceAll(data, "{{ "+k+" }}", v) + } + }*/ + + trans := translator.GlobalTrans.Tr(la, data) + return trans }, "timeFormatISO": func(tz string, timestamp int64) string { _, _ = time.LoadLocation(tz) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d78f6a73..c986ad4e 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,6 +3,7 @@ package controller import ( "encoding/json" "fmt" + "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -307,7 +308,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = handler.GetLang(ctx) + data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) diff --git a/pkg/converter/str.go b/pkg/converter/str.go index 74d9b2ec..7d4374cc 100644 --- a/pkg/converter/str.go +++ b/pkg/converter/str.go @@ -24,3 +24,7 @@ func StringToInt(str string) int { func IntToString(data int64) string { return fmt.Sprintf("%d", data) } + +func InterfaceToString(data interface{}) string { + return fmt.Sprintf("%d", data) +} diff --git a/ui/template/header.html b/ui/template/header.html index baf712e5..360b75b4 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -3,43 +3,48 @@ - - + + - - - - - {{if $.siteinfo.JsonLD }}{{ .siteinfo.JsonLD | templateHTML}}{{end}} + + + + + {{if $.siteinfo.JsonLD }}{{ .siteinfo.JsonLD | templateHTML}}{{end}} - -
- +
+ {{end}} diff --git a/ui/template/page.html b/ui/template/page.html index 73e7bbc9..ef37bea5 100644 --- a/ui/template/page.html +++ b/ui/template/page.html @@ -2,8 +2,8 @@ diff --git a/ui/template/tag-detail.html b/ui/template/tag-detail.html index ee2268cb..eedc3a89 100644 --- a/ui/template/tag-detail.html +++ b/ui/template/tag-detail.html @@ -1,21 +1,22 @@ {{template "header" . }}
-
-
-
-

- {{$.tag.SlugName}} -

-

- {{templateHTML $.tag.ParsedText}} -

-
-
-
-
{{.questionCount}} Questions
-
-
- {{range .questionList}} +
+
+
+

+ {{$.tag.SlugName}} +

+

+ {{templateHTML $.tag.ParsedText}} +

+
+
+
+
{{ .questionCount }} + {{translator ($.language) ("ui.question.questions")}}
+
+
+ {{range .questionList}}
{{.Title}} @@ -30,8 +31,8 @@
@@ -54,13 +55,13 @@
{{end}} -
-
-
-
-
- {{template "page" .}}
+
+
+
+ {{template "page" .}} +
+
{{template "footer" .}} diff --git a/ui/template/tags.html b/ui/template/tags.html index 36e4c39e..a77e4fa7 100644 --- a/ui/template/tags.html +++ b/ui/template/tags.html @@ -12,7 +12,7 @@
-

Tags

+

{{translator $.language "ui.page_title.tags"}}

@@ -26,7 +26,7 @@

{{.QuestionCount}} - questions + {{translator $.language "ui.tags.tag_label"}}
From d8b50b482fc265d34d1f5ab4f882b6b6a4abdf5b Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 12:17:37 +0800 Subject: [PATCH 059/272] style(template/i18n): add todo --- internal/base/server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 0897b8b4..408c7d82 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -69,6 +69,7 @@ func NewHTTPServer(debug bool, return template.HTML(data) }, "translator": func(la i18n.Language, data string, params ...interface{}) string { + // todo /*if len(params) > 0 && len(params)%2 == 0 { for i := 0; i < len(params); i += 2 { k := converter.InterfaceToString(params[i]) From e59e95fe115d173299b687948feda69ddf0685f8 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 6 Dec 2022 12:17:45 +0800 Subject: [PATCH 060/272] add page meta --- internal/controller/template_controller.go | 17 +++++++++++++++++ internal/schema/siteinfo_schema.go | 14 ++++++++------ ui/template/header.html | 3 ++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d78f6a73..58ff3730 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -6,6 +6,7 @@ import ( "html/template" "net/http" "regexp" + "strings" "time" "github.com/answerdev/answer/internal/base/handler" @@ -190,6 +191,13 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { siteInfo.JsonLD = `` } + siteInfo.Description = htmltext.FetchExcerpt(detail.HTML, "...", 240) + tags := make([]string, 0) + for _, tag := range detail.Tags { + tags = append(tags, tag.DisplayName) + } + siteInfo.Keywords = strings.Replace(strings.Trim(fmt.Sprint(tags), "[]"), " ", ",", -1) + tc.html(ctx, http.StatusOK, "question-detail.html", siteInfo, gin.H{ "id": id, "answerid": answerid, @@ -254,6 +262,10 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { siteInfo := tc.SiteInfo(ctx) siteInfo.Canonical = fmt.Sprintf("%s/tags/%s", siteInfo.General.SiteUrl, tag) + + siteInfo.Description = htmltext.FetchExcerpt(taginifo.ParsedText, "...", 240) + siteInfo.Keywords = taginifo.DisplayName + tc.html(ctx, http.StatusOK, "tag-detail.html", siteInfo, gin.H{ "tag": taginifo, "questionList": questionList, @@ -307,6 +319,11 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath + data["keywords"] = siteInfo.Keywords + if siteInfo.Description == "" { + siteInfo.Description = siteInfo.General.Description + } + data["description"] = siteInfo.Description data["language"] = handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index 9e77a0f4..920d8807 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -106,12 +106,14 @@ type SiteInfoResp struct { Branding *SiteBrandingResp `json:"branding"` } type TemplateSiteInfoResp struct { - General *SiteGeneralResp `json:"general"` - Interface *SiteInterfaceResp `json:"interface"` - Branding *SiteBrandingResp `json:"branding"` - Year string - Canonical string - JsonLD string + General *SiteGeneralResp `json:"general"` + Interface *SiteInterfaceResp `json:"interface"` + Branding *SiteBrandingResp `json:"branding"` + Year string + Canonical string + JsonLD string + Keywords string + Description string } // UpdateSMTPConfigReq get smtp config request diff --git a/ui/template/header.html b/ui/template/header.html index baf712e5..b265fcf7 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -5,7 +5,8 @@ - + + {{if .keywords }}{{end}} From 6afd3d44b7e4941577d7c686aecc7ee768f56a6f Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 12:18:42 +0800 Subject: [PATCH 061/272] fix(template/i18n): i18n test chinese cancelled --- internal/controller/template_controller.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index c986ad4e..d78f6a73 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,7 +3,6 @@ package controller import ( "encoding/json" "fmt" - "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -308,7 +307,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) + data["language"] = handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) From 3cdd75983f817d3c9d5a324d80f804f662e22104 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 6 Dec 2022 12:20:07 +0800 Subject: [PATCH 062/272] i18n --- internal/controller/template_controller.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 89a00ff7..61cffcae 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -321,13 +321,13 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["keywords"] = siteInfo.Keywords if siteInfo.Description == "" { siteInfo.Description = siteInfo.General.Description } data["description"] = siteInfo.Description - data["language"] = handler.GetLang(ctx) + // data["language"] = handler.GetLang(ctx) + data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) From be753459f64b0a4b84379d3e01cbe92d3c9b1bbb Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 12:23:25 +0800 Subject: [PATCH 063/272] feat(template/i18n): homepage convert --- internal/controller/template_controller.go | 3 ++- ui/template/homepage.html | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index d78f6a73..c986ad4e 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,6 +3,7 @@ package controller import ( "encoding/json" "fmt" + "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -307,7 +308,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI data["siteinfo"] = siteInfo data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath - data["language"] = handler.GetLang(ctx) + data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) diff --git a/ui/template/homepage.html b/ui/template/homepage.html index 6b2e19f3..dac6b9ad 100644 --- a/ui/template/homepage.html +++ b/ui/template/homepage.html @@ -15,13 +15,13 @@
@{{.userinfo.Info.Username}}
- {{.userinfo.Info.Rank}} reputation + {{.userinfo.Info.Rank}} {{translator $.language "ui.personal.x_reputation"}}
- {{.userinfo.Info.AnswerCount}} answers + {{.userinfo.Info.AnswerCount}} {{translator $.language "ui.personal.x_answers"}}
- {{.userinfo.Info.QuestionCount}} questions + {{.userinfo.Info.QuestionCount}} {{translator $.language "ui.personal.x_questions"}}
{{if .userinfo.Info.Website }} @@ -40,11 +40,11 @@
-
About Me
+
{{translator $.language "ui.personal.about_me"}}
{{if .bio }}
{{.bio}}
{{else}} -
// Hello, World !
+
{{translator $.language "ui.personal.about_me_empty"}}
{{end}}
From 787948f94494e002522955208dffa3be96ccf5f7 Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 12:25:16 +0800 Subject: [PATCH 064/272] merge(template/i18n): merge --- internal/controller/template_controller.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 61cffcae..58ff3730 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -9,8 +9,6 @@ import ( "strings" "time" - "github.com/segmentfault/pacman/i18n" - "github.com/answerdev/answer/internal/base/handler" templaterender "github.com/answerdev/answer/internal/controller/template_render" "github.com/answerdev/answer/internal/schema" @@ -326,8 +324,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI siteInfo.Description = siteInfo.General.Description } data["description"] = siteInfo.Description - // data["language"] = handler.GetLang(ctx) - data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) + data["language"] = handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) From 995510595b6a64af2ca52b4ec4c5dc88c16538e9 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Sun, 27 Nov 2022 15:47:46 +0800 Subject: [PATCH 065/272] feat: support for manifest.json dynamic rendering --- cmd/answer/wire_gen.go | 2 +- internal/controller/siteinfo_controller.go | 26 ++++++++++++++++++++++ internal/router/ui.go | 10 ++++++--- internal/schema/siteinfo_schema.go | 11 +++++++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 852fd015..aa4e9c5b 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -193,7 +193,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, activityController := controller.NewActivityController(activityCommon, activityService) answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, controller_backyardReportController, userBackyardController, reasonController, themeController, siteInfoController, siteinfoController, notificationController, dashboardController, uploadController, activityController) swaggerRouter := router.NewSwaggerRouter(swaggerConf) - uiRouter := router.NewUIRouter() + uiRouter := router.NewUIRouter(siteinfoController) authUserMiddleware := middleware.NewAuthUserMiddleware(authService) avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService) ginEngine := server.NewHTTPServer(debug, staticRouter, answerAPIRouter, swaggerRouter, uiRouter, authUserMiddleware, avatarMiddleware) diff --git a/internal/controller/siteinfo_controller.go b/internal/controller/siteinfo_controller.go index 80d6faa7..6337b133 100644 --- a/internal/controller/siteinfo_controller.go +++ b/internal/controller/siteinfo_controller.go @@ -1,6 +1,8 @@ package controller import ( + "net/http" + "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/siteinfo_common" @@ -73,3 +75,27 @@ func (sc *SiteinfoController) GetSiteLegalInfo(ctx *gin.Context) { } handler.HandleResponse(ctx, nil, resp) } + +// GetManifestJson get manifest.json +func (sc *SiteinfoController) GetManifestJson(ctx *gin.Context) { + resp := &schema.GetManifestJsonResp{ + ShortName: "Answer", + Name: "Answer.dev", + Icons: map[string]string{ + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon", + }, + StartUrl: ".", + Display: "standalone", + ThemeColor: "#000000", + BackgroundColor: "#ffffff", + } + branding, err := sc.siteInfoService.GetSiteBranding(ctx) + if err != nil { + log.Error(err) + } else if len(branding.Favicon) > 0 { + resp.Icons["scr"] = branding.Favicon + } + ctx.JSON(http.StatusOK, resp) +} diff --git a/internal/router/ui.go b/internal/router/ui.go index 6f0e174f..1255d6ce 100644 --- a/internal/router/ui.go +++ b/internal/router/ui.go @@ -7,6 +7,7 @@ import ( "net/http" "os" + "github.com/answerdev/answer/internal/controller" "github.com/answerdev/answer/ui" "github.com/gin-gonic/gin" "github.com/segmentfault/pacman/log" @@ -18,11 +19,12 @@ const UIStaticPath = "build/static" // UIRouter is an interface that provides ui static file routers type UIRouter struct { + siteInfoController *controller.SiteinfoController } // NewUIRouter creates a new UIRouter instance with the embed resources -func NewUIRouter() *UIRouter { - return &UIRouter{} +func NewUIRouter(siteInfoController *controller.SiteinfoController) *UIRouter { + return &UIRouter{siteInfoController: siteInfoController} } // _resource is an interface that provides static file, it's a private interface @@ -75,7 +77,9 @@ func (a *UIRouter) Register(r *gin.Engine) { c.Header("content-type", "image/vnd.microsoft.icon") filePath = UIRootFilePath + urlPath case "/manifest.json": - filePath = UIRootFilePath + urlPath + // filePath = UIRootFilePath + urlPath + a.siteInfoController.GetManifestJson(c) + return case "/install": // if answer is running by run command user can not access install page. c.Redirect(http.StatusFound, "/") diff --git a/internal/schema/siteinfo_schema.go b/internal/schema/siteinfo_schema.go index d1292623..637b3bd6 100644 --- a/internal/schema/siteinfo_schema.go +++ b/internal/schema/siteinfo_schema.go @@ -120,3 +120,14 @@ type GetSMTPConfigResp struct { SMTPPassword string `json:"smtp_password"` SMTPAuthentication bool `json:"smtp_authentication"` } + +// GetManifestJsonResp get manifest json response +type GetManifestJsonResp struct { + ShortName string `json:"short_name"` + Name string `json:"name"` + Icons map[string]string `json:"icons"` + StartUrl string `json:"start_url"` + Display string `json:"display"` + ThemeColor string `json:"theme_color"` + BackgroundColor string `json:"background_color"` +} From d138c372a5162c6a919bbf1531555a13787ee63c Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Tue, 6 Dec 2022 17:01:42 +0800 Subject: [PATCH 066/272] fix(rename): rename function and request --- internal/controller/template_controller.go | 12 ++++++------ internal/controller/template_render/answer.go | 3 ++- internal/controller/template_render/question.go | 2 +- internal/service/rank/rank_service.go | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 58ff3730..5747fa9c 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -133,12 +133,12 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) { } // answers - answerReq := &schema.AnswerList{ - QuestionID: id, - Order: "", - Page: 1, - PageSize: 999, - LoginUserID: "", + answerReq := &schema.AnswerListReq{ + QuestionID: id, + Order: "", + Page: 1, + PageSize: 999, + UserID: "", } answers, answerCount, err := tc.templateRenderController.AnswerList(ctx, answerReq) if err != nil { diff --git a/internal/controller/template_render/answer.go b/internal/controller/template_render/answer.go index 1e87144e..7893c108 100644 --- a/internal/controller/template_render/answer.go +++ b/internal/controller/template_render/answer.go @@ -2,9 +2,10 @@ package templaterender import ( "context" + "github.com/answerdev/answer/internal/schema" ) -func (t *TemplateRenderController) AnswerList(ctx context.Context, req *schema.AnswerList) ([]*schema.AnswerInfo, int64, error) { +func (t *TemplateRenderController) AnswerList(ctx context.Context, req *schema.AnswerListReq) ([]*schema.AnswerInfo, int64, error) { return t.answerService.SearchList(ctx, req) } diff --git a/internal/controller/template_render/question.go b/internal/controller/template_render/question.go index d4fd30e9..e3fd66ba 100644 --- a/internal/controller/template_render/question.go +++ b/internal/controller/template_render/question.go @@ -10,5 +10,5 @@ func (t *TemplateRenderController) Index(ctx *gin.Context, req *schema.QuestionS } func (t *TemplateRenderController) QuestionDetail(ctx *gin.Context, id string) (resp *schema.QuestionInfo, err error) { - return t.questionService.GetQuestion(ctx, id, "", true) + return t.questionService.GetQuestion(ctx, id, "", schema.QuestionPermission{}) } diff --git a/internal/service/rank/rank_service.go b/internal/service/rank/rank_service.go index 871c62a7..99a7024b 100644 --- a/internal/service/rank/rank_service.go +++ b/internal/service/rank/rank_service.go @@ -150,10 +150,10 @@ func (rs *RankService) CheckVotePermission(ctx context.Context, userID, objectID if !exist { return can, nil } - // administrator have all permissions - if userInfo.IsAdmin { - return true, nil - } + // TODO administrator have all permissions + //if userInfo.IsAdmin { + // return true, nil + //} objectInfo, err := rs.objectInfoService.GetInfo(ctx, objectID) if err != nil { From 149751153c8f89349e77bb94c29f5c508ad5b6cc Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Tue, 6 Dec 2022 17:16:42 +0800 Subject: [PATCH 067/272] fix FuncMap --- internal/base/server/http.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 408c7d82..17e4eec3 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -64,7 +64,7 @@ func NewHTTPServer(debug bool, cmsauthV1.Use(authUserMiddleware.CmsAuth()) answerRouter.RegisterAnswerCmsAPIRouter(cmsauthV1) - r.SetFuncMap(template.FuncMap{ + funcMap := template.FuncMap{ "templateHTML": func(data string) template.HTML { return template.HTML(data) }, @@ -138,14 +138,15 @@ func NewHTTPServer(debug bool, "timezone": tz, } }, - }) + } + r.SetFuncMap(funcMap) dev := os.Getenv("DEVCODE") if dev != "" { r.LoadHTMLGlob("../../ui/template/*") } else { html, _ := fs.Sub(ui.Template, "template") - htmlTemplate := template.Must(template.New("").ParseFS(html, "*.html")) + htmlTemplate := template.Must(template.New("").Funcs(funcMap).ParseFS(html, "*.html")) r.SetHTMLTemplate(htmlTemplate) } From ee1804f7baf367bf5593b4beeee8d719fe6b1d72 Mon Sep 17 00:00:00 2001 From: robin Date: Tue, 6 Dec 2022 17:44:55 +0800 Subject: [PATCH 068/272] core: update .gitlab-ci.yml --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc4dd08e..c9933810 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,8 +13,6 @@ stages: "compile the html and other static files": image: node:16 stage: compile-html - tags: - - runner-nanjing before_script: - npm config set registry https://repo.huaweicloud.com/repository/npm/ - make install-ui-packages From c2e72d106e16e279987431aafd5af3b317cb588a Mon Sep 17 00:00:00 2001 From: "haitao(lj)" Date: Tue, 6 Dec 2022 18:12:10 +0800 Subject: [PATCH 069/272] refactor(i18n): Rewrite description as desc to be compatible with go-i18n --- ui/src/hooks/useChangeModal/index.tsx | 8 ++++---- ui/src/hooks/useEditStatusModal/index.tsx | 6 +++--- ui/src/hooks/useTagModal/index.tsx | 6 ++---- ui/src/pages/404/index.jsx | 2 +- ui/src/pages/50X/index.jsx | 2 +- ui/src/pages/Admin/General/index.tsx | 8 ++++---- ui/src/pages/Questions/Ask/index.tsx | 2 +- ui/src/pages/Tags/Detail/index.tsx | 2 +- ui/src/pages/Tags/Edit/index.tsx | 4 ++-- 9 files changed, 19 insertions(+), 21 deletions(-) diff --git a/ui/src/hooks/useChangeModal/index.tsx b/ui/src/hooks/useChangeModal/index.tsx index 767618b6..f597e6ab 100644 --- a/ui/src/hooks/useChangeModal/index.tsx +++ b/ui/src/hooks/useChangeModal/index.tsx @@ -35,22 +35,22 @@ const useChangeModal = ({ callback }: Props) => { { type: 'normal', name: t('normal_name'), - description: t('normal_description'), + description: t('normal_desc'), }, { type: 'suspended', name: t('suspended_name'), - description: t('suspended_description'), + description: t('suspended_desc'), }, { type: 'deleted', name: t('deleted_name'), - description: t('deleted_description'), + description: t('deleted_desc'), }, { type: 'inactive', name: t('inactive_name'), - description: t('inactive_description'), + description: t('inactive_desc'), }, ]); diff --git a/ui/src/hooks/useEditStatusModal/index.tsx b/ui/src/hooks/useEditStatusModal/index.tsx index 54a246ba..d2f4147e 100644 --- a/ui/src/hooks/useEditStatusModal/index.tsx +++ b/ui/src/hooks/useEditStatusModal/index.tsx @@ -27,17 +27,17 @@ const useEditStatusModal = ({ { type: 'normal', name: t('normal_name'), - description: t('normal_description'), + description: t('normal_desc'), }, { type: 'closed', name: t('closed_name'), - description: t('closed_description'), + description: t('closed_desc'), }, { type: 'deleted', name: t('deleted_name'), - description: t('deleted_description'), + description: t('deleted_desc'), }, ]); diff --git a/ui/src/hooks/useTagModal/index.tsx b/ui/src/hooks/useTagModal/index.tsx index d4fbef7e..99ea8b56 100644 --- a/ui/src/hooks/useTagModal/index.tsx +++ b/ui/src/hooks/useTagModal/index.tsx @@ -204,15 +204,13 @@ const useTagModal = (props: IProps = {}) => { isInvalid={formData.slugName.isInvalid} /> - - {t('form.fields.slug_name.description')} - + {t('form.fields.slug_name.desc')} {formData.slugName.errorMsg} - {t('form.fields.description.label')} + {t('form.fields.desc.label')} { style={{ fontSize: '120px', lineHeight: 1.2 }}> (=‘x‘=)
-
{t('description')}
+
{t('desc')}
-
{t('description')}
+
{t('desc')}

- {escapeRemove(tagInfo.excerpt) || t('no_description')} + {escapeRemove(tagInfo.excerpt) || t('no_desc')} [{t('more')}] diff --git a/ui/src/pages/Tags/Edit/index.tsx b/ui/src/pages/Tags/Edit/index.tsx index d7f508b3..01f91e6d 100644 --- a/ui/src/pages/Tags/Edit/index.tsx +++ b/ui/src/pages/Tags/Edit/index.tsx @@ -203,7 +203,7 @@ const Ask = () => { - {t('form.fields.description.label')} + {t('form.fields.desc.label')} { From 7d6acca9c97a176aa494addee5b0747980b0fa80 Mon Sep 17 00:00:00 2001 From: "haitao(lj)" Date: Tue, 6 Dec 2022 18:13:04 +0800 Subject: [PATCH 070/272] fix(i18n): Rewrite description as desc to be compatible with go-i18n --- i18n/en_US.yaml | 66 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 8a8181ba..71b8cb16 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -129,32 +129,32 @@ backend: spam: name: other: "spam" - description: + desc: other: "This post is an advertisement, or vandalism. It is not useful or relevant to the current topic." rude: name: other: "rude or abusive" - description: + desc: other: "A reasonable person would find this content inappropriate for respectful discourse." duplicate: name: other: "a duplicate" - description: + desc: other: "This question has been asked before and already has an answer." not_answer: name: other: "not an answer" - description: + desc: other: "This was posted as an answer, but it does not attempt to answer the question. It should possibly be an edit, a comment, another question, or deleted altogether." not_need: name: other: "no longer needed" - description: + desc: other: "This comment is outdated, conversational or not relevant to this post." other: name: other: "something else" - description: + desc: other: "This post requires staff attention for another reason not listed above." question: @@ -162,22 +162,22 @@ backend: duplicate: name: other: "spam" - description: + desc: other: "This question has been asked before and already has an answer." guideline: name: other: "a community-specific reason" - description: + desc: other: "This question doesn't meet a community guideline." multiple: name: other: "needs details or clarity" - description: + desc: other: "This question currently includes multiple questions in one. It should focus on one problem only." other: name: other: "something else" - description: + desc: other: "This post requires another reason not listed above." notification: @@ -210,7 +210,7 @@ backend: ui: how_to_format: title: How to Format - description: >- + desc: >-

  • to make links

    <https://url.com>

    [Title](https://url.com)
  • put returns between paragraphs

  • - + desc: >- You can create the <1>config.yaml file manually in the <1>/var/wwww/xxx/ directory and paste the following text into it. info: "After you’ve done that, click “Next” button." @@ -934,31 +934,31 @@ ui: empty: Email cannot be empty. incorrect: Email incorrect format. ready_title: Your Answer is Ready! - ready_description: >- + ready_desc: >- If you ever feel like changing more settings, visit <1>admin section; find it in the site menu. good_luck: "Have fun, and good luck!" warn_title: Warning - warn_description: >- + warn_desc: >- The file <1>config.yaml already exists. If you need to reset any of the configuration items in this file, please delete it first. install_now: You may try <1>installing now. installed: Already installed - installed_description: >- + installed_desc: >- You appear to have already installed. To reinstall please clear your old database tables first. db_failed: Database connection failed - db_failed_description: >- + db_failed_desc: >- This either means that the database information in your <1>config.yaml file is incorrect or that contact with the database server could not be established. This could mean your host’s database server is down. page_404: - description: "Unfortunately, this page doesn't exist." + desc: "Unfortunately, this page doesn't exist." back_home: Back to homepage page_50X: - description: The server encountered an error and could not complete your request. + desc: The server encountered an error and could not complete your request. back_home: Back to homepage page_maintenance: - description: "We are under maintenance, we’ll be back soon." + desc: "We are under maintenance, we’ll be back soon." nav_menus: dashboard: Dashboard contents: Contents @@ -1025,13 +1025,13 @@ ui: btn_cancel: Cancel btn_submit: Submit normal_name: normal - normal_description: A normal user can ask and answer questions. + normal_desc: A normal user can ask and answer questions. suspended_name: suspended - suspended_description: A suspended user can't log in. + suspended_desc: A suspended user can't log in. deleted_name: deleted - deleted_description: "Delete profile, authentication associations." + deleted_desc: "Delete profile, authentication associations." inactive_name: inactive - inactive_description: An inactive user must re-validate their email. + inactive_desc: An inactive user must re-validate their email. confirm_title: Delete this user confirm_content: Are you sure you want to delete this user? This is permanent! confirm_btn: Delete @@ -1040,11 +1040,11 @@ ui: status_modal: title: "Change {{ type }} status to..." normal_name: normal - normal_description: A normal post available to everyone. + normal_desc: A normal post available to everyone. closed_name: closed - closed_description: "A closed question can't answer, but still can edit, vote and comment." + closed_desc: "A closed question can't answer, but still can edit, vote and comment." deleted_name: deleted - deleted_description: All reputation gained and lost will be restored. + deleted_desc: All reputation gained and lost will be restored. btn_cancel: Cancel btn_submit: Submit btn_next: Next @@ -1116,11 +1116,11 @@ ui: msg: Site url cannot be empty. validate: Please enter a valid URL. text: The address of your site. - short_description: + short_desc: label: Short Site Description (optional) msg: Short site description cannot be empty. text: "Short description, as used in the title tag on homepage." - description: + desc: label: Site Description (optional) msg: Site description cannot be empty. text: "Describe this site in one sentence, as used in the meta description tag." From 44237c6f78efbdf5216306c098c6b333ea8e8713 Mon Sep 17 00:00:00 2001 From: kumfo Date: Tue, 6 Dec 2022 18:37:08 +0800 Subject: [PATCH 071/272] feat(zh_CN.yaml): sync with zh_US.yaml --- i18n/zh_CN.yaml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/i18n/zh_CN.yaml b/i18n/zh_CN.yaml index 4b9bf2b0..55b70b03 100644 --- a/i18n/zh_CN.yaml +++ b/i18n/zh_CN.yaml @@ -206,7 +206,7 @@ backend: ui: how_to_format: title: 如何设定文本格式 - description: >- + desc: >-

    • 添加链接:

      <https://url.com>

      [标题](https://url.com)
    • 段落之间使用空行分隔

    • _斜体_ 或者 @@ -311,7 +311,7 @@ ui: empty: 请选择图片文件。 only_image: 只能上传图片文件。 max_size: 图片文件大小不能超过 4 MB。 - description: + desc: label: 图片描述(可选) tab_url: 网络图片 form_url: @@ -383,12 +383,12 @@ ui: range: 不能超过 35 个字符 slug_name: label: URL 固定链接 - description: '必须由 "a-z", "0-9", "+ # - ." 组成' - msg: "todo" -# empty: 不能为空 -# range: 不能超过 35 个字符 -# character: 包含非法字符 - description: + desc: '必须由 "a-z", "0-9", "+ # - ." 组成' + msg: + empty: 不能为空 + range: 不能超过 35 个字符 + character: 包含非法字符 + desc: label: 标签描述(可选) btn_cancel: 取消 btn_submit: 提交 @@ -420,7 +420,7 @@ ui: slug_name: label: URL 固定链接 info: '必须由 "a-z", "0-9", "+ # - ." 组成' - description: + desc: label: 描述 edit_summary: label: 编辑概要 @@ -471,7 +471,7 @@ ui: button_following: 已关注 tag_label: 个问题 search_placeholder: 通过标签名过滤 - no_description: 此标签无描述。 + no_desc: 此标签无描述。 more: 更多 ask: title: 提交新的问题 @@ -785,10 +785,10 @@ ui: x_answers: 个回答 x_questions: 个问题 page_404: - description: 页面不存在 + desc: 页面不存在 back_home: 回到主页 page_50X: - description: 服务器遇到了一个错误,无法完成你的请求。 + desc: 服务器遇到了一个错误,无法完成你的请求。 back_home: 回到主页 nav_menus: dashboard: 后台管理 @@ -821,13 +821,13 @@ ui: btn_cancel: 取消 btn_submit: 提交 normal_name: 正常 - normal_description: 正常状态的用户可以提问和回答。 + normal_desc: 正常状态的用户可以提问和回答。 suspended_name: 封禁 - suspended_description: 被封禁的用户将无法登录。 + suspended_desc: 被封禁的用户将无法登录。 deleted_name: 删除 - deleted_description: 删除用户的个人信息,认证等等。 + deleted_desc: 删除用户的个人信息,认证等等。 inactive_name: 不活跃 - inactive_description: 不活跃的用户必须重新验证邮箱。 + inactive_desc: 不活跃的用户必须重新验证邮箱。 confirm_title: 删除此用户 confirm_content: 确定要删除此用户?此操作无法撤销! confirm_btn: 删除 @@ -836,11 +836,11 @@ ui: status_modal: title: '更改 {{ type }} 状态为...' normal_name: 正常 - normal_description: 所有用户都可以访问 + normal_desc: 所有用户都可以访问 closed_name: 关闭 - closed_description: 不能回答,但仍然可以编辑、投票和评论。 + closed_desc: 不能回答,但仍然可以编辑、投票和评论。 deleted_name: 删除 - deleted_description: 所有获得/损失的声望将会恢复。 + deleted_desc: 所有获得/损失的声望将会恢复。 btn_cancel: 取消 btn_submit: 提交 btn_next: 下一步 @@ -888,11 +888,11 @@ ui: label: 站点名称 msg: 不能为空 text: 站点的名称,作为站点的标题(HTML 的 title 标签)。 - short_description: + short_desc: label: 简短的站点标语 (可选) msg: 不能为空 text: 简短的标语,作为网站主页的标题(HTML 的 title 标签)。 - description: + desc: label: 网站描述 (可选) msg: 不能为空 text: 使用一句话描述本站,作为网站的描述(HTML 的 meta 标签)。 From 9489a4291ba47ed0034a4030a98bffd89b7f60ac Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 10:05:11 +0800 Subject: [PATCH 072/272] open js --- ui/template/header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/template/header.html b/ui/template/header.html index e07e31d1..a402f3ba 100644 --- a/ui/template/header.html +++ b/ui/template/header.html @@ -10,7 +10,7 @@ - + {{if $.siteinfo.JsonLD }}{{ .siteinfo.JsonLD | templateHTML}}{{end}} From 4f977549db61f49e094989c6fb328ea9d054871c Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 7 Dec 2022 10:19:20 +0800 Subject: [PATCH 073/272] refactor(ui): add rank field in table --- ui/src/pages/Admin/Users/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/src/pages/Admin/Users/index.tsx b/ui/src/pages/Admin/Users/index.tsx index 21c8865b..b425f49e 100644 --- a/ui/src/pages/Admin/Users/index.tsx +++ b/ui/src/pages/Admin/Users/index.tsx @@ -17,6 +17,7 @@ import * as Type from '@/common/interface'; import { useChangeModal, useChangeUserRoleModal, useToast } from '@/hooks'; import { useQueryUsers } from '@/services'; import { loggedUserInfoStore } from '@/stores'; +import { formatCount } from '@/utils'; const UserFilterKeys: Type.UserFilterBy[] = [ 'all', @@ -151,6 +152,7 @@ const Users: FC = () => { showReputation={false} /> + {formatCount(user.rank)} {user.e_mail} From 42756c07422c3bff39659d91fc8b18fd7d393dfb Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 7 Dec 2022 10:26:02 +0800 Subject: [PATCH 074/272] feat(user): backend add user and change user password --- cmd/answer/wire_gen.go | 6 +- .../user_backyard_controller.go | 44 +++++++++++ internal/repo/user/user_backyard_repo.go | 18 +++++ internal/router/answer_api_router.go | 2 + internal/schema/backyard_user_schema.go | 15 ++++ .../service/user_backyard/user_backyard.go | 58 ++++++++++++++ internal/service/user_common/user.go | 46 +++++++++++ internal/service/user_service.go | 77 +++++-------------- 8 files changed, 204 insertions(+), 62 deletions(-) diff --git a/cmd/answer/wire_gen.go b/cmd/answer/wire_gen.go index 56b71382..4b3e11c6 100644 --- a/cmd/answer/wire_gen.go +++ b/cmd/answer/wire_gen.go @@ -116,14 +116,14 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, roleRepo := role.NewRoleRepo(dataData) roleService := role2.NewRoleService(roleRepo) userRoleRelService := role2.NewUserRoleRelService(userRoleRelRepo, roleService) - userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService) + userCommon := usercommon.NewUserCommon(userRepo) + userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService, userCommon) captchaRepo := captcha.NewCaptchaRepo(dataData) captchaService := action.NewCaptchaService(captchaRepo) uploaderService := uploader.NewUploaderService(serviceConf, siteInfoCommonService) userController := controller.NewUserController(authService, userService, captchaService, emailService, uploaderService) commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo) commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo) - userCommon := usercommon.NewUserCommon(userRepo) answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo) questionRepo := question.NewQuestionRepo(dataData, uniqueIDRepo) tagCommonRepo := tag_common.NewTagCommonRepo(dataData, uniqueIDRepo) @@ -180,7 +180,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo) controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService) userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo) - userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService) + userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService, userCommon) userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService) reasonRepo := reason.NewReasonRepo(configRepo) reasonService := reason2.NewReasonService(reasonRepo) diff --git a/internal/controller_backyard/user_backyard_controller.go b/internal/controller_backyard/user_backyard_controller.go index 636bc039..1b35a2ec 100644 --- a/internal/controller_backyard/user_backyard_controller.go +++ b/internal/controller_backyard/user_backyard_controller.go @@ -60,6 +60,50 @@ func (uc *UserBackyardController) UpdateUserRole(ctx *gin.Context) { handler.HandleResponse(ctx, err, nil) } +// AddUser add user +// @Summary add user +// @Description add user +// @Security ApiKeyAuth +// @Tags admin +// @Accept json +// @Produce json +// @Param data body schema.AddUserReq true "user" +// @Success 200 {object} handler.RespBody +// @Router /answer/admin/api/user [post] +func (uc *UserBackyardController) AddUser(ctx *gin.Context) { + req := &schema.AddUserReq{} + if handler.BindAndCheck(ctx, req) { + return + } + + req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx) + + err := uc.userService.AddUser(ctx, req) + handler.HandleResponse(ctx, err, nil) +} + +// UpdateUserPassword update user password +// @Summary update user password +// @Description update user password +// @Security ApiKeyAuth +// @Tags admin +// @Accept json +// @Produce json +// @Param data body schema.UpdateUserPasswordReq true "user" +// @Success 200 {object} handler.RespBody +// @Router /answer/admin/api/user/password [put] +func (uc *UserBackyardController) UpdateUserPassword(ctx *gin.Context) { + req := &schema.UpdateUserPasswordReq{} + if handler.BindAndCheck(ctx, req) { + return + } + + req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx) + + err := uc.userService.UpdateUserPassword(ctx, req) + handler.HandleResponse(ctx, err, nil) +} + // GetUserPage get user page // @Summary get user page // @Description get user page diff --git a/internal/repo/user/user_backyard_repo.go b/internal/repo/user/user_backyard_repo.go index f54244d3..bffe18ac 100644 --- a/internal/repo/user/user_backyard_repo.go +++ b/internal/repo/user/user_backyard_repo.go @@ -61,6 +61,24 @@ func (ur *userBackyardRepo) UpdateUserStatus(ctx context.Context, userID string, return } +// AddUser add user +func (ur *userBackyardRepo) AddUser(ctx context.Context, user *entity.User) (err error) { + _, err = ur.data.DB.Insert(user) + if err != nil { + err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + return +} + +// UpdateUserPassword update user password +func (ur *userBackyardRepo) UpdateUserPassword(ctx context.Context, userID string, password string) (err error) { + _, err = ur.data.DB.ID(userID).Update(&entity.User{Pass: password}) + if err != nil { + return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + return +} + // GetUserInfo get user info func (ur *userBackyardRepo) GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error) { user = &entity.User{} diff --git a/internal/router/answer_api_router.go b/internal/router/answer_api_router.go index e81e706f..54580eee 100644 --- a/internal/router/answer_api_router.go +++ b/internal/router/answer_api_router.go @@ -234,6 +234,8 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) { r.GET("/users/page", a.backyardUserController.GetUserPage) r.PUT("/user/status", a.backyardUserController.UpdateUserStatus) r.PUT("/user/role", a.backyardUserController.UpdateUserRole) + r.POST("/user", a.backyardUserController.AddUser) + r.PUT("/user/password", a.backyardUserController.UpdateUserPassword) // reason r.GET("/reasons", a.reasonController.Reasons) diff --git a/internal/schema/backyard_user_schema.go b/internal/schema/backyard_user_schema.go index 44e9cb4a..57f3f183 100644 --- a/internal/schema/backyard_user_schema.go +++ b/internal/schema/backyard_user_schema.go @@ -84,3 +84,18 @@ type UpdateUserRoleReq struct { // login user id LoginUserID string `json:"-"` } + +// AddUserReq add user request +type AddUserReq struct { + DisplayName string `validate:"required,gt=4,lte=30" json:"display_name"` + Email string `validate:"required,email,gt=0,lte=500" json:"email"` + Password string `validate:"required,gte=8,lte=32" json:"password"` + LoginUserID string `json:"-"` +} + +// UpdateUserPasswordReq update user password request +type UpdateUserPasswordReq struct { + UserID string `validate:"required" json:"user_id"` + Password string `validate:"required,gte=8,lte=32" json:"password"` + LoginUserID string `json:"-"` +} diff --git a/internal/service/user_backyard/user_backyard.go b/internal/service/user_backyard/user_backyard.go index 8bf0e2e7..84907501 100644 --- a/internal/service/user_backyard/user_backyard.go +++ b/internal/service/user_backyard/user_backyard.go @@ -14,9 +14,11 @@ import ( "github.com/answerdev/answer/internal/schema" "github.com/answerdev/answer/internal/service/auth" "github.com/answerdev/answer/internal/service/role" + usercommon "github.com/answerdev/answer/internal/service/user_common" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" + "golang.org/x/crypto/bcrypt" ) // UserBackyardRepo user repository @@ -25,6 +27,8 @@ type UserBackyardRepo interface { GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error) GetUserPage(ctx context.Context, page, pageSize int, user *entity.User, usernameOrDisplayName string, isStaff bool) (users []*entity.User, total int64, err error) + AddUser(ctx context.Context, user *entity.User) (err error) + UpdateUserPassword(ctx context.Context, userID string, password string) (err error) } // UserBackyardService user service @@ -32,6 +36,7 @@ type UserBackyardService struct { userRepo UserBackyardRepo userRoleRelService *role.UserRoleRelService authService *auth.AuthService + userCommonService *usercommon.UserCommon } // NewUserBackyardService new user backyard service @@ -39,11 +44,13 @@ func NewUserBackyardService( userRepo UserBackyardRepo, userRoleRelService *role.UserRoleRelService, authService *auth.AuthService, + userCommonService *usercommon.UserCommon, ) *UserBackyardService { return &UserBackyardService{ userRepo: userRepo, userRoleRelService: userRoleRelService, authService: authService, + userCommonService: userCommonService, } } @@ -94,6 +101,57 @@ func (us *UserBackyardService) UpdateUserRole(ctx context.Context, req *schema.U return } +// AddUser add user +func (us *UserBackyardService) AddUser(ctx context.Context, req *schema.AddUserReq) (err error) { + hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return err + } + + userInfo := &entity.User{} + userInfo.EMail = req.Email + userInfo.DisplayName = req.DisplayName + userInfo.Pass = string(hashPwd) + + userInfo.Username, err = us.userCommonService.MakeUsername(ctx, userInfo.DisplayName) + if err != nil { + return err + } + userInfo.MailStatus = entity.EmailStatusAvailable + userInfo.Status = entity.UserStatusAvailable + userInfo.Rank = 1 + + err = us.userRepo.AddUser(ctx, userInfo) + if err != nil { + return err + } + return +} + +// UpdateUserPassword update user password +func (us *UserBackyardService) UpdateUserPassword(ctx context.Context, req *schema.UpdateUserPasswordReq) (err error) { + userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID) + if err != nil { + return err + } + if !exist { + return errors.BadRequest(reason.UserNotFound) + } + + hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return err + } + + err = us.userRepo.UpdateUserPassword(ctx, userInfo.ID, string(hashPwd)) + if err != nil { + return err + } + // logout this user + us.authService.RemoveAllUserTokens(ctx, req.UserID) + return +} + // GetUserInfo get user one func (us *UserBackyardService) GetUserInfo(ctx context.Context, userID string) (resp *schema.GetUserInfoResp, err error) { user, exist, err := us.userRepo.GetUserInfo(ctx, userID) diff --git a/internal/service/user_common/user.go b/internal/service/user_common/user.go index 1f28525c..bc33a1ba 100644 --- a/internal/service/user_common/user.go +++ b/internal/service/user_common/user.go @@ -2,9 +2,17 @@ package usercommon import ( "context" + "encoding/hex" + "math/rand" + "regexp" + "strings" + "github.com/Chain-Zhang/pinyin" + "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/entity" "github.com/answerdev/answer/internal/schema" + "github.com/answerdev/answer/pkg/checker" + "github.com/segmentfault/pacman/errors" ) type UserRepo interface { @@ -94,3 +102,41 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity. } return userBasicInfo } + +// MakeUsername +// Generate a unique Username based on the displayName +func (us *UserCommon) MakeUsername(ctx context.Context, displayName string) (username string, err error) { + // Chinese processing + if has := checker.IsChinese(displayName); has { + str, err := pinyin.New(displayName).Split("").Mode(pinyin.WithoutTone).Convert() + if err != nil { + return "", errors.BadRequest(reason.UsernameInvalid) + } else { + displayName = str + } + } + + username = strings.ReplaceAll(displayName, " ", "_") + username = strings.ToLower(username) + suffix := "" + + re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`) + match := re.MatchString(username) + if !match { + return "", errors.BadRequest(reason.UsernameInvalid) + } + + for { + _, has, err := us.userRepo.GetByUsername(ctx, username+suffix) + if err != nil { + return "", err + } + if !has { + break + } + bytes := make([]byte, 2) + _, _ = rand.Read(bytes) + suffix = hex.EncodeToString(bytes) + } + return username + suffix, nil +} diff --git a/internal/service/user_service.go b/internal/service/user_service.go index f2ba3b85..a23d9b4a 100644 --- a/internal/service/user_service.go +++ b/internal/service/user_service.go @@ -2,14 +2,9 @@ package service import ( "context" - "encoding/hex" "encoding/json" "fmt" - "math/rand" - "regexp" - "strings" - "github.com/Chain-Zhang/pinyin" "github.com/answerdev/answer/internal/base/handler" "github.com/answerdev/answer/internal/base/reason" "github.com/answerdev/answer/internal/base/translator" @@ -23,7 +18,6 @@ import ( "github.com/answerdev/answer/internal/service/service_config" "github.com/answerdev/answer/internal/service/siteinfo_common" usercommon "github.com/answerdev/answer/internal/service/user_common" - "github.com/answerdev/answer/pkg/checker" "github.com/google/uuid" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/log" @@ -34,13 +28,14 @@ import ( // UserService user service type UserService struct { - userRepo usercommon.UserRepo - userActivity activity.UserActiveActivityRepo - serviceConfig *service_config.ServiceConfig - emailService *export.EmailService - authService *auth.AuthService - siteInfoService *siteinfo_common.SiteInfoCommonService - userRoleService *role.UserRoleRelService + userCommonService *usercommon.UserCommon + userRepo usercommon.UserRepo + userActivity activity.UserActiveActivityRepo + serviceConfig *service_config.ServiceConfig + emailService *export.EmailService + authService *auth.AuthService + siteInfoService *siteinfo_common.SiteInfoCommonService + userRoleService *role.UserRoleRelService } func NewUserService(userRepo usercommon.UserRepo, @@ -50,15 +45,17 @@ func NewUserService(userRepo usercommon.UserRepo, serviceConfig *service_config.ServiceConfig, siteInfoService *siteinfo_common.SiteInfoCommonService, userRoleService *role.UserRoleRelService, + userCommonService *usercommon.UserCommon, ) *UserService { return &UserService{ - userRepo: userRepo, - userActivity: userActivity, - emailService: emailService, - serviceConfig: serviceConfig, - authService: authService, - siteInfoService: siteInfoService, - userRoleService: userRoleService, + userCommonService: userCommonService, + userRepo: userRepo, + userActivity: userActivity, + emailService: emailService, + serviceConfig: serviceConfig, + authService: authService, + siteInfoService: siteInfoService, + userRoleService: userRoleService, } } @@ -307,7 +304,7 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo if err != nil { return nil, err } - userInfo.Username, err = us.makeUsername(ctx, registerUserInfo.Name) + userInfo.Username, err = us.userCommonService.MakeUsername(ctx, registerUserInfo.Name) if err != nil { return nil, err } @@ -456,44 +453,6 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri return resp, nil } -// makeUsername -// Generate a unique Username based on the displayName -func (us *UserService) makeUsername(ctx context.Context, displayName string) (username string, err error) { - // Chinese processing - if has := checker.IsChinese(displayName); has { - str, err := pinyin.New(displayName).Split("").Mode(pinyin.WithoutTone).Convert() - if err != nil { - return "", err - } else { - displayName = str - } - } - - username = strings.ReplaceAll(displayName, " ", "_") - username = strings.ToLower(username) - suffix := "" - - re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`) - match := re.MatchString(username) - if !match { - return "", errors.BadRequest(reason.UsernameInvalid) - } - - for { - _, has, err := us.userRepo.GetByUsername(ctx, username+suffix) - if err != nil { - return "", err - } - if !has { - break - } - bytes := make([]byte, 2) - _, _ = rand.Read(bytes) - suffix = hex.EncodeToString(bytes) - } - return username + suffix, nil -} - // verifyPassword // Compare whether the password is correct func (us *UserService) verifyPassword(ctx context.Context, LoginPass, UserPass string) bool { From 78c0606e52373dfdcb7412998bb98e6ec8cd18d2 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:11:14 +0800 Subject: [PATCH 075/272] fix users --- internal/controller/template_controller.go | 19 +++++++++++++++++++ internal/router/template_router.go | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 5747fa9c..86f8c230 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -276,6 +276,14 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { // UserInfo user info func (tc *TemplateController) UserInfo(ctx *gin.Context) { + // urlPath := ctx.Request.URL.Path + // filePath := "" + // switch urlPath { + // case "/users/login": + // filePath = "build/index.html" + // case "/users/register": + // filePath = "build/index.html" + // default: username := ctx.Param("username") req := &schema.GetOtherUserInfoByUsernameReq{} req.Username = username @@ -305,6 +313,17 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { "userinfo": userinfo, "bio": template.HTML(userinfo.Info.BioHTML), }) + // } + + // file, err := ui.Build.ReadFile(filePath) + // if err != nil { + // log.Error(err) + // ctx.Status(http.StatusNotFound) + // return + // } + // ctx.Header("content-type", "text/html;charset=utf-8") + // ctx.String(http.StatusOK, string(file)) + } func (tc *TemplateController) Page404(ctx *gin.Context) { diff --git a/internal/router/template_router.go b/internal/router/template_router.go index af108081..fbc48cdd 100644 --- a/internal/router/template_router.go +++ b/internal/router/template_router.go @@ -41,6 +41,6 @@ func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup) { r.GET("/tags", a.templateController.TagList) r.GET("/tags/:tag", a.templateController.TagInfo) - r.GET("/users/:username", a.templateController.UserInfo) + // r.GET("/users/:username", a.templateController.UserInfo) r.GET("/404", a.templateController.Page404) } From 9dc7d9cf44527a3a6d29b5d04f5767c55ca38e16 Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 7 Dec 2022 11:14:05 +0800 Subject: [PATCH 076/272] feat(template/render): common render with 404 page --- internal/controller/template_controller.go | 41 ++++------------------ 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 5747fa9c..1766ab9d 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -215,12 +215,7 @@ func (tc *TemplateController) TagList(ctx *gin.Context) { } data, err := tc.templateRenderController.TagList(ctx, req) if err != nil { - ctx.HTML(http.StatusOK, "404.html", gin.H{ - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - "err": err.Error(), - "siteinfo": tc.SiteInfo(ctx), - }) + tc.Page404(ctx) return } page := templaterender.Paginator(req.Page, req.PageSize, data.Count) @@ -238,24 +233,14 @@ func (tc *TemplateController) TagInfo(ctx *gin.Context) { tag := ctx.Param("tag") req := &schema.GetTamplateTagInfoReq{} if handler.BindAndCheck(ctx, req) { - ctx.HTML(http.StatusOK, "404.html", gin.H{ - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - "err": "", - "siteinfo": tc.SiteInfo(ctx), - }) + tc.Page404(ctx) return } nowPage := req.Page req.Name = tag taginifo, questionList, questionCount, err := tc.templateRenderController.TagInfo(ctx, req) if err != nil { - ctx.HTML(http.StatusOK, "404.html", gin.H{ - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - "err": err.Error(), - "siteinfo": tc.SiteInfo(ctx), - }) + tc.Page404(ctx) return } page := templaterender.Paginator(nowPage, req.PageSize, questionCount) @@ -281,21 +266,11 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { req.Username = username userinfo, err := tc.templateRenderController.UserInfo(ctx, req) if !userinfo.Has { - ctx.HTML(http.StatusNotFound, "404.html", gin.H{ - "siteinfo": tc.SiteInfo(ctx), - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - "err": "", - }) + tc.Page404(ctx) return } if err != nil { - ctx.HTML(http.StatusNotFound, "404.html", gin.H{ - "siteinfo": tc.SiteInfo(ctx), - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - "err": err.Error(), - }) + tc.Page404(ctx) return } @@ -308,11 +283,7 @@ func (tc *TemplateController) UserInfo(ctx *gin.Context) { } func (tc *TemplateController) Page404(ctx *gin.Context) { - ctx.HTML(http.StatusNotFound, "404.html", gin.H{ - "siteinfo": tc.SiteInfo(ctx), - "scriptPath": tc.scriptPath, - "cssPath": tc.cssPath, - }) + tc.html(ctx, http.StatusNotFound, "404.html", tc.SiteInfo(ctx), gin.H{}) } func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteInfo *schema.TemplateSiteInfoResp, data gin.H) { From 00dd6e9f0c18ebd89a4b55ad2eba576a62935ce2 Mon Sep 17 00:00:00 2001 From: LinkinStar Date: Wed, 7 Dec 2022 11:45:41 +0800 Subject: [PATCH 077/272] feat(i18n): Try to return to the original text when translation is not possible --- go.mod | 5 +++-- go.sum | 6 ++++++ internal/base/translator/provider.go | 32 +++++++++++++++++----------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 13bd9f47..11d49010 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/segmentfault/pacman v1.0.1 github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 - github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221109042453-26158da67632 + github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221207032920-3662d1e32068 github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 github.com/spf13/cobra v1.6.1 @@ -45,6 +45,7 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/KyleBanks/depth v1.2.1 // indirect + github.com/LinkinStars/go-i18n/v2 v2.2.2 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/andybalholm/brotli v1.0.4 // indirect @@ -108,7 +109,7 @@ require ( golang.org/x/image v0.1.0 // indirect golang.org/x/mod v0.6.0 // indirect golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/text v0.5.0 // indirect golang.org/x/tools v0.2.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect diff --git a/go.sum b/go.sum index 8bfba639..eaecd6bd 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/Chain-Zhang/pinyin v0.1.3/go.mod h1:5iHpt9p4znrnaP59/hfPMnAojajkDxQaP github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/LinkinStars/go-i18n/v2 v2.2.2 h1:ZfjpzbW13dv6btv3RALKZkpN9A+7K1JA//2QcNeWaxU= +github.com/LinkinStars/go-i18n/v2 v2.2.2/go.mod h1:hLglSJ4/3M0Y7ZVcoEJI+OwqkglHCA32DdjuJJR2LbM= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= @@ -599,6 +601,8 @@ github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd143 github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY= github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221109042453-26158da67632 h1:so07u8RWXZQ0gz30KXJ9MKtQ5zjgcDlQ/UwFZrwm5b0= github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221109042453-26158da67632/go.mod h1:5Afm+OQdau/HQqSOp/ALlSUp0vZsMMMbv//kJhxuoi8= +github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221207032920-3662d1e32068 h1:ln/qgrC62e7/XHGPiikWFV4dyYgCaWeZYkmSGqrHZp4= +github.com/segmentfault/pacman/contrib/i18n v0.0.0-20221207032920-3662d1e32068/go.mod h1:7QcRmnV7OYq4hNOOCWXT5HXnN/u756JUsqIW0Bw8n9E= github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05 h1:jcGZU2juv0L3eFEkuZYV14ESLUlWfGMWnP0mjOfrSZc= github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05/go.mod h1:L4GqtXLoR73obTYqUQIzfkm8NG8pvZafxFb6KZFSSHk= github.com/segmentfault/pacman/contrib/server/http v0.0.0-20221018072427-a15dd1434e05 h1:91is1nKNbfTOl8CvMYiFgg4c5Vmol+5mVmMV/jDXD+A= @@ -932,6 +936,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 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/base/translator/provider.go b/internal/base/translator/provider.go index 8e945068..518132ea 100644 --- a/internal/base/translator/provider.go +++ b/internal/base/translator/provider.go @@ -51,20 +51,28 @@ func NewTranslator(c *I18n) (tr i18n.Translator, err error) { return nil, fmt.Errorf("read file failed: %s %s", file.Name(), err) } - // only parse the backend translation - //translation := struct { - // Content map[string]interface{} `yaml:"backend"` - //}{} - //if err = yaml.Unmarshal(buf, &translation); err != nil { - // return nil, err - //} - //content, err := yaml.Marshal(translation.Content) - //if err != nil { - // return nil, fmt.Errorf("marshal translation content failed: %s %s", file.Name(), err) - //} + // parse the backend translation + originalTr := struct { + Backend map[string]map[string]interface{} `yaml:"backend"` + UI map[string]interface{} `yaml:"ui"` + }{} + if err = yaml.Unmarshal(buf, &originalTr); err != nil { + return nil, err + } + translation := make(map[string]interface{}, 0) + for k, v := range originalTr.Backend { + translation[k] = v + } + translation["backend"] = originalTr.Backend + translation["ui"] = originalTr.UI + + content, err := yaml.Marshal(translation) + if err != nil { + return nil, fmt.Errorf("marshal translation content failed: %s %s", file.Name(), err) + } // add translator use backend translation - if err = myTran.AddTranslator(buf, file.Name()); err != nil { + if err = myTran.AddTranslator(content, file.Name()); err != nil { return nil, fmt.Errorf("add translator failed: %s %s", file.Name(), err) } } From 676a19500c2b588de18143d2da9bbee27ff3766b Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:30:42 +0800 Subject: [PATCH 078/272] userinfo show userid --- internal/schema/activity.go | 14 +++++++------- internal/schema/user_schema.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/schema/activity.go b/internal/schema/activity.go index 57c45ea1..6e775c88 100644 --- a/internal/schema/activity.go +++ b/internal/schema/activity.go @@ -39,17 +39,17 @@ type ActObjectTimeline struct { ObjectType string `json:"object_type"` Cancelled bool `json:"cancelled"` CancelledAt int64 `json:"cancelled_at"` - UserID string `json:"-"` + UserID string `json:"id"` } // ActObjectInfo act object info type ActObjectInfo struct { - Title string `json:"title"` - ObjectType string `json:"object_type"` - QuestionID string `json:"question_id"` - AnswerID string `json:"answer_id"` - Username string `json:"username"` - DisplayName string `json:"display_name"` + Title string `json:"title"` + ObjectType string `json:"object_type"` + QuestionID string `json:"question_id"` + AnswerID string `json:"answer_id"` + Username string `json:"username"` + DisplayName string `json:"display_name"` MainTagSlugName string `json:"main_tag_slug_name"` } diff --git a/internal/schema/user_schema.go b/internal/schema/user_schema.go index 2212dfe6..e625dedf 100644 --- a/internal/schema/user_schema.go +++ b/internal/schema/user_schema.go @@ -368,7 +368,7 @@ type ActionRecordResp struct { } type UserBasicInfo struct { - ID string `json:"-"` // user_id + ID string `json:"id"` // user_id Username string `json:"username" ` // name Rank int `json:"rank" ` // rank DisplayName string `json:"display_name"` // display_name From 46bff9e4f9aaaab3a6c46c92191f2e1cb784db1a Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 7 Dec 2022 15:44:41 +0800 Subject: [PATCH 079/272] fix(template/render): fix template footer / time --- internal/base/server/http.go | 34 ++++++++++++++++++---- internal/controller/template_controller.go | 13 ++++++--- pkg/converter/str.go | 26 ++++++++++++++++- ui/template/footer.html | 7 ++--- ui/template/question.html | 7 +++++ ui/template/tag-detail.html | 5 ++-- 6 files changed, 75 insertions(+), 17 deletions(-) diff --git a/internal/base/server/http.go b/internal/base/server/http.go index 17e4eec3..22729570 100644 --- a/internal/base/server/http.go +++ b/internal/base/server/http.go @@ -5,6 +5,7 @@ import ( "io/fs" "math" "os" + "regexp" "strconv" "strings" "time" @@ -65,20 +66,41 @@ func NewHTTPServer(debug bool, answerRouter.RegisterAnswerCmsAPIRouter(cmsauthV1) funcMap := template.FuncMap{ + "replaceHTMLTag": func(src string, tags ...string) string { + p := `(?U)<(\d+)>.+` + + re := regexp.MustCompile(p) + ms := re.FindAllStringSubmatch(src, -1) + for _, mi := range ms { + if mi[1] == mi[2] { + i, err := strconv.Atoi(mi[1]) + if err != nil || len(tags) < i { + break + } + + src = strings.ReplaceAll(src, mi[0], tags[i-1]) + } + } + + return src + }, + "join": func(sep string, elems ...string) string { + return strings.Join(elems, sep) + }, "templateHTML": func(data string) template.HTML { return template.HTML(data) }, "translator": func(la i18n.Language, data string, params ...interface{}) string { - // todo - /*if len(params) > 0 && len(params)%2 == 0 { + trans := translator.GlobalTrans.Tr(la, data) + + if len(params) > 0 && len(params)%2 == 0 { for i := 0; i < len(params); i += 2 { k := converter.InterfaceToString(params[i]) v := converter.InterfaceToString(params[i+1]) - data = strings.ReplaceAll(data, "{{ "+k+" }}", v) + trans = strings.ReplaceAll(trans, "{{ "+k+" }}", v) } - }*/ + } - trans := translator.GlobalTrans.Tr(la, data) return trans }, "timeFormatISO": func(tz string, timestamp int64) string { @@ -116,7 +138,7 @@ func NewHTTPServer(debug bool, } if between >= 3600 && between < 3600*24 { - h := math.Floor(float64(between / 60)) + h := math.Floor(float64(between / 3600)) trans = translator.GlobalTrans.Tr(la, "ui.dates.x_hours_ago") return strings.ReplaceAll(trans, "{{count}}", strconv.FormatFloat(h, 'f', 0, 64)) } diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 52ba1b16..b4a43d98 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,6 +3,7 @@ package controller import ( "encoding/json" "fmt" + "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -78,7 +79,9 @@ func (tc *TemplateController) SiteInfo(ctx *gin.Context) *schema.TemplateSiteInf // Index question list func (tc *TemplateController) Index(ctx *gin.Context) { - req := &schema.QuestionSearch{} + req := &schema.QuestionSearch{ + Order: "newest", + } if handler.BindAndCheck(ctx, req) { tc.Page404(ctx) return @@ -101,7 +104,9 @@ func (tc *TemplateController) Index(ctx *gin.Context) { } func (tc *TemplateController) QuestionList(ctx *gin.Context) { - req := &schema.QuestionSearch{} + req := &schema.QuestionSearch{ + Order: "newest", + } if handler.BindAndCheck(ctx, req) { tc.Page404(ctx) return @@ -307,14 +312,14 @@ func (tc *TemplateController) Page404(ctx *gin.Context) { func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteInfo *schema.TemplateSiteInfoResp, data gin.H) { data["siteinfo"] = siteInfo - data["scriptPath"] = tc.scriptPath + data["scriptPath"] = "" //tc.scriptPath data["cssPath"] = tc.cssPath data["keywords"] = siteInfo.Keywords if siteInfo.Description == "" { siteInfo.Description = siteInfo.General.Description } data["description"] = siteInfo.Description - data["language"] = handler.GetLang(ctx) + data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) diff --git a/pkg/converter/str.go b/pkg/converter/str.go index 7d4374cc..04415fe4 100644 --- a/pkg/converter/str.go +++ b/pkg/converter/str.go @@ -2,6 +2,7 @@ package converter import ( "fmt" + "github.com/segmentfault/pacman/log" "strconv" ) @@ -25,6 +26,29 @@ func IntToString(data int64) string { return fmt.Sprintf("%d", data) } +// InterfaceToString converts data to string +// It will be used in template render func InterfaceToString(data interface{}) string { - return fmt.Sprintf("%d", data) + switch t := data.(type) { + case int: + i := data.(int) + return strconv.Itoa(i) + case int8: + i := data.(int8) + return strconv.Itoa(int(i)) + case int16: + i := data.(int16) + return strconv.Itoa(int(i)) + case int32: + i := data.(int32) + return string(i) + case int64: + i := data.(int64) + return strconv.FormatInt(i, 10) + case string: + return data.(string) + default: + log.Warn("can't convert type:", t) + } + return "" } diff --git a/ui/template/footer.html b/ui/template/footer.html index 45839a21..f83ed4f5 100644 --- a/ui/template/footer.html +++ b/ui/template/footer.html @@ -1,12 +1,11 @@ {{define "footer"}}

-

- Built on Answer - - the open-source software that power Q&A communities.
Made - with love © {{.siteinfo.Year}} {{.siteinfo.General.Name}}. + {{$cc := (join " " .siteinfo.Year .siteinfo.General.Name) -}} + {{- $ft := translator $.language "ui.footer.build_on" "cc" $cc -}} + {{templateHTML (replaceHTMLTag $ft " Answer ")}}

diff --git a/ui/template/question.html b/ui/template/question.html index 8724d28e..8a32fc1d 100644 --- a/ui/template/question.html +++ b/ui/template/question.html @@ -21,10 +21,17 @@ class="fw-bold" title="Reputation">{{.UserInfo.Rank}}

• + {{if eq .CreateTime .UpdateTime}} + {{else if gt .UpdateTime 0}} + + {{end}}
-
{{ .questionCount }} - {{translator ($.language) ("ui.question.questions")}}
+
+ {{translator ($.language) "ui.question.x_questions" "count" .questionCount}} +
{{range .questionList}} From 5bf63452f72d811504154424d283aedb2412b2d9 Mon Sep 17 00:00:00 2001 From: kumfo Date: Wed, 7 Dec 2022 15:45:14 +0800 Subject: [PATCH 080/272] fix(template/render): fix template footer / time --- internal/controller/template_controller.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index b4a43d98..5bd19710 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -3,7 +3,6 @@ package controller import ( "encoding/json" "fmt" - "github.com/segmentfault/pacman/i18n" "html/template" "net/http" "regexp" @@ -319,7 +318,7 @@ func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteI siteInfo.Description = siteInfo.General.Description } data["description"] = siteInfo.Description - data["language"] = i18n.LanguageChinese //handler.GetLang(ctx) + data["language"] = handler.GetLang(ctx) data["timezone"] = siteInfo.Interface.TimeZone ctx.HTML(code, tpl, data) From 962b4b098c1860d6d61c1e450b0dd12189b6b744 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:50:46 +0800 Subject: [PATCH 081/272] fix search question tags --- internal/repo/tag_common/tag_common_repo.go | 7 ++----- internal/service/tag_common/tag_common.go | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/internal/repo/tag_common/tag_common_repo.go b/internal/repo/tag_common/tag_common_repo.go index a3fc2eb7..bfaa24dc 100644 --- a/internal/repo/tag_common/tag_common_repo.go +++ b/internal/repo/tag_common/tag_common_repo.go @@ -55,7 +55,7 @@ func (tr *tagCommonRepo) GetTagBySlugName(ctx context.Context, slugName string) } // GetTagListByName get tag list all like name -func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error) { +func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error) { tagList = make([]*entity.Tag, 0) cond := &entity.Tag{} session := tr.data.DB.Where("") @@ -65,10 +65,7 @@ func (tr *tagCommonRepo) GetTagListByName(ctx context.Context, name string, limi cond.Recommend = true } session.Where(builder.Eq{"status": entity.TagStatusAvailable}) - // if limit == 0 { - // session.Asc("slug_name") - // } - session.Limit(limit).Asc("slug_name") + session.Asc("slug_name") // if !hasReserved { // cond.Reserved = false // session.UseBool("recommend", "reserved") diff --git a/internal/service/tag_common/tag_common.go b/internal/service/tag_common/tag_common.go index a7a2dea7..7317c0fc 100644 --- a/internal/service/tag_common/tag_common.go +++ b/internal/service/tag_common/tag_common.go @@ -24,7 +24,7 @@ type TagCommonRepo interface { AddTagList(ctx context.Context, tagList []*entity.Tag) (err error) GetTagListByIDs(ctx context.Context, ids []string) (tagList []*entity.Tag, err error) GetTagBySlugName(ctx context.Context, slugName string) (tagInfo *entity.Tag, exist bool, err error) - GetTagListByName(ctx context.Context, name string, limit int, hasReserved bool) (tagList []*entity.Tag, err error) + GetTagListByName(ctx context.Context, name string, hasReserved bool) (tagList []*entity.Tag, err error) GetTagListByNames(ctx context.Context, names []string) (tagList []*entity.Tag, err error) GetTagByID(ctx context.Context, tagID string, includeDeleted bool) (tag *entity.Tag, exist bool, err error) GetTagPage(ctx context.Context, page, pageSize int, tag *entity.Tag, queryCond string) (tagList []*entity.Tag, total int64, err error) @@ -79,7 +79,7 @@ func NewTagCommonService( // SearchTagLike get tag list all func (ts *TagCommonService) SearchTagLike(ctx context.Context, req *schema.SearchTagLikeReq) (resp []schema.SearchTagLikeResp, err error) { - tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, 0, req.IsAdmin) + tags, err := ts.tagCommonRepo.GetTagListByName(ctx, req.Tag, req.IsAdmin) if err != nil { return } From 054f7b5c9a95653311640b141b871bab3a920de8 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:52:53 +0800 Subject: [PATCH 082/272] fix scriptPath --- internal/controller/template_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go index 5bd19710..74ffe8d2 100644 --- a/internal/controller/template_controller.go +++ b/internal/controller/template_controller.go @@ -311,7 +311,7 @@ func (tc *TemplateController) Page404(ctx *gin.Context) { func (tc *TemplateController) html(ctx *gin.Context, code int, tpl string, siteInfo *schema.TemplateSiteInfoResp, data gin.H) { data["siteinfo"] = siteInfo - data["scriptPath"] = "" //tc.scriptPath + data["scriptPath"] = tc.scriptPath data["cssPath"] = tc.cssPath data["keywords"] = siteInfo.Keywords if siteInfo.Description == "" { From d3b77e76d699c631aa2af171486d3ab0fe103f05 Mon Sep 17 00:00:00 2001 From: aichy Date: Wed, 7 Dec 2022 16:51:08 +0800 Subject: [PATCH 083/272] fix swagger --- docs/docs.go | 225 +++++++++++++++++++++-- docs/swagger.json | 225 +++++++++++++++++++++-- docs/swagger.yaml | 140 ++++++++++++-- internal/controller/answer_controller.go | 2 +- 4 files changed, 547 insertions(+), 45 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 78e53c0a..9486300e 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -105,7 +105,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/entity.AdminSetAnswerStatusRequest" + "$ref": "#/definitions/schema.AdminSetAnswerStatusRequest" } } ], @@ -822,6 +822,77 @@ const docTemplate = `{ } } }, + "/answer/admin/api/siteinfo/seo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "get site seo information", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.SiteSeoResp" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "update site seo information", + "parameters": [ + { + "description": "seo", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.SiteSeoReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/admin/api/siteinfo/write": { "get": { "security": [ @@ -2746,6 +2817,45 @@ const docTemplate = `{ } } }, + "/answer/api/v1/question/reopen": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "reopen question", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-question" + ], + "summary": "reopen question", + "parameters": [ + { + "description": "question", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.ReopenQuestionReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/api/v1/question/search": { "post": { "description": "SearchQuestionList", @@ -4672,20 +4782,29 @@ const docTemplate = `{ } } } + }, + "/robots.txt": { + "get": { + "description": "get site robots information", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "get site robots information", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { - "entity.AdminSetAnswerStatusRequest": { - "type": "object", - "properties": { - "answer_id": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, "handler.RespBody": { "type": "object", "properties": { @@ -4812,6 +4931,9 @@ const docTemplate = `{ "display_name": { "type": "string" }, + "main_tag_slug_name": { + "type": "string" + }, "object_type": { "type": "string" }, @@ -4847,6 +4969,9 @@ const docTemplate = `{ "created_at": { "type": "integer" }, + "id": { + "type": "string" + }, "object_id": { "type": "string" }, @@ -4934,6 +5059,17 @@ const docTemplate = `{ } } }, + "schema.AdminSetAnswerStatusRequest": { + "type": "object", + "properties": { + "answer_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "schema.AdminSetQuestionStatusRequest": { "type": "object", "properties": { @@ -4969,7 +5105,6 @@ const docTemplate = `{ "type": "string" }, "question_id": { - "description": "question_id", "type": "string" } } @@ -6163,6 +6298,14 @@ const docTemplate = `{ } } }, + "schema.ReopenQuestionReq": { + "type": "object", + "properties": { + "question_id": { + "type": "string" + } + } + }, "schema.ReportHandleReq": { "type": "object", "required": [ @@ -6253,7 +6396,11 @@ const docTemplate = `{ }, "user_info": { "description": "user info", - "$ref": "#/definitions/schema.UserBasicInfo" + "allOf": [ + { + "$ref": "#/definitions/schema.UserBasicInfo" + } + ] }, "vote_count": { "type": "integer" @@ -6265,7 +6412,11 @@ const docTemplate = `{ "properties": { "object": { "description": "this object", - "$ref": "#/definitions/schema.SearchObject" + "allOf": [ + { + "$ref": "#/definitions/schema.SearchObject" + } + ] }, "object_type": { "description": "object_type", @@ -6328,6 +6479,7 @@ const docTemplate = `{ "required": [ "contact_email", "name", + "permalink", "site_url" ], "properties": { @@ -6343,6 +6495,11 @@ const docTemplate = `{ "type": "string", "maxLength": 128 }, + "permalink": { + "type": "integer", + "maximum": 3, + "minimum": 0 + }, "short_description": { "type": "string", "maxLength": 255 @@ -6358,6 +6515,7 @@ const docTemplate = `{ "required": [ "contact_email", "name", + "permalink", "site_url" ], "properties": { @@ -6373,6 +6531,11 @@ const docTemplate = `{ "type": "string", "maxLength": 128 }, + "permalink": { + "type": "integer", + "maximum": 3, + "minimum": 0 + }, "short_description": { "type": "string", "maxLength": 255 @@ -6461,6 +6624,28 @@ const docTemplate = `{ } } }, + "schema.SiteSeoReq": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, + "schema.SiteSeoResp": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, "schema.SiteWriteReq": { "type": "object", "properties": { @@ -6629,7 +6814,11 @@ const docTemplate = `{ "properties": { "avatar": { "description": "avatar", - "$ref": "#/definitions/schema.AvatarInfo" + "allOf": [ + { + "$ref": "#/definitions/schema.AvatarInfo" + } + ] }, "bio": { "description": "bio", @@ -6824,6 +7013,10 @@ const docTemplate = `{ "description": "display_name", "type": "string" }, + "id": { + "description": "user_id", + "type": "string" + }, "ip_info": { "description": "ip info", "type": "string" diff --git a/docs/swagger.json b/docs/swagger.json index aaad0374..3ce7f923 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -93,7 +93,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/entity.AdminSetAnswerStatusRequest" + "$ref": "#/definitions/schema.AdminSetAnswerStatusRequest" } } ], @@ -810,6 +810,77 @@ } } }, + "/answer/admin/api/siteinfo/seo": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "get site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "get site seo information", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/handler.RespBody" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/schema.SiteSeoResp" + } + } + } + ] + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "update site seo information", + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "update site seo information", + "parameters": [ + { + "description": "seo", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.SiteSeoReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/admin/api/siteinfo/write": { "get": { "security": [ @@ -2734,6 +2805,45 @@ } } }, + "/answer/api/v1/question/reopen": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "reopen question", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "api-question" + ], + "summary": "reopen question", + "parameters": [ + { + "description": "question", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/schema.ReopenQuestionReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RespBody" + } + } + } + } + }, "/answer/api/v1/question/search": { "post": { "description": "SearchQuestionList", @@ -4660,20 +4770,29 @@ } } } + }, + "/robots.txt": { + "get": { + "description": "get site robots information", + "produces": [ + "application/json" + ], + "tags": [ + "site" + ], + "summary": "get site robots information", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + } + } + } } }, "definitions": { - "entity.AdminSetAnswerStatusRequest": { - "type": "object", - "properties": { - "answer_id": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, "handler.RespBody": { "type": "object", "properties": { @@ -4800,6 +4919,9 @@ "display_name": { "type": "string" }, + "main_tag_slug_name": { + "type": "string" + }, "object_type": { "type": "string" }, @@ -4835,6 +4957,9 @@ "created_at": { "type": "integer" }, + "id": { + "type": "string" + }, "object_id": { "type": "string" }, @@ -4922,6 +5047,17 @@ } } }, + "schema.AdminSetAnswerStatusRequest": { + "type": "object", + "properties": { + "answer_id": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, "schema.AdminSetQuestionStatusRequest": { "type": "object", "properties": { @@ -4957,7 +5093,6 @@ "type": "string" }, "question_id": { - "description": "question_id", "type": "string" } } @@ -6151,6 +6286,14 @@ } } }, + "schema.ReopenQuestionReq": { + "type": "object", + "properties": { + "question_id": { + "type": "string" + } + } + }, "schema.ReportHandleReq": { "type": "object", "required": [ @@ -6241,7 +6384,11 @@ }, "user_info": { "description": "user info", - "$ref": "#/definitions/schema.UserBasicInfo" + "allOf": [ + { + "$ref": "#/definitions/schema.UserBasicInfo" + } + ] }, "vote_count": { "type": "integer" @@ -6253,7 +6400,11 @@ "properties": { "object": { "description": "this object", - "$ref": "#/definitions/schema.SearchObject" + "allOf": [ + { + "$ref": "#/definitions/schema.SearchObject" + } + ] }, "object_type": { "description": "object_type", @@ -6316,6 +6467,7 @@ "required": [ "contact_email", "name", + "permalink", "site_url" ], "properties": { @@ -6331,6 +6483,11 @@ "type": "string", "maxLength": 128 }, + "permalink": { + "type": "integer", + "maximum": 3, + "minimum": 0 + }, "short_description": { "type": "string", "maxLength": 255 @@ -6346,6 +6503,7 @@ "required": [ "contact_email", "name", + "permalink", "site_url" ], "properties": { @@ -6361,6 +6519,11 @@ "type": "string", "maxLength": 128 }, + "permalink": { + "type": "integer", + "maximum": 3, + "minimum": 0 + }, "short_description": { "type": "string", "maxLength": 255 @@ -6449,6 +6612,28 @@ } } }, + "schema.SiteSeoReq": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, + "schema.SiteSeoResp": { + "type": "object", + "required": [ + "robots" + ], + "properties": { + "robots": { + "type": "string" + } + } + }, "schema.SiteWriteReq": { "type": "object", "properties": { @@ -6617,7 +6802,11 @@ "properties": { "avatar": { "description": "avatar", - "$ref": "#/definitions/schema.AvatarInfo" + "allOf": [ + { + "$ref": "#/definitions/schema.AvatarInfo" + } + ] }, "bio": { "description": "bio", @@ -6812,6 +7001,10 @@ "description": "display_name", "type": "string" }, + "id": { + "description": "user_id", + "type": "string" + }, "ip_info": { "description": "ip info", "type": "string" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 58aec495..d68df764 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,11 +1,4 @@ definitions: - entity.AdminSetAnswerStatusRequest: - properties: - answer_id: - type: string - status: - type: string - type: object handler.RespBody: properties: code: @@ -95,6 +88,8 @@ definitions: type: string display_name: type: string + main_tag_slug_name: + type: string object_type: type: string question_id: @@ -118,6 +113,8 @@ definitions: type: string created_at: type: integer + id: + type: string object_id: type: string object_type: @@ -179,6 +176,13 @@ definitions: - object_id - report_type type: object + schema.AdminSetAnswerStatusRequest: + properties: + answer_id: + type: string + status: + type: string + type: object schema.AdminSetQuestionStatusRequest: properties: question_id: @@ -203,7 +207,6 @@ definitions: answer_id: type: string question_id: - description: question_id type: string type: object schema.AnswerUpdateReq: @@ -1067,6 +1070,11 @@ definitions: required: - tag_id type: object + schema.ReopenQuestionReq: + properties: + question_id: + type: string + type: object schema.ReportHandleReq: properties: flagged_content: @@ -1128,7 +1136,8 @@ definitions: title: type: string user_info: - $ref: '#/definitions/schema.UserBasicInfo' + allOf: + - $ref: '#/definitions/schema.UserBasicInfo' description: user info vote_count: type: integer @@ -1136,7 +1145,8 @@ definitions: schema.SearchResp: properties: object: - $ref: '#/definitions/schema.SearchObject' + allOf: + - $ref: '#/definitions/schema.SearchObject' description: this object object_type: description: object_type @@ -1189,6 +1199,10 @@ definitions: name: maxLength: 128 type: string + permalink: + maximum: 3 + minimum: 0 + type: integer short_description: maxLength: 255 type: string @@ -1198,6 +1212,7 @@ definitions: required: - contact_email - name + - permalink - site_url type: object schema.SiteGeneralResp: @@ -1211,6 +1226,10 @@ definitions: name: maxLength: 128 type: string + permalink: + maximum: 3 + minimum: 0 + type: integer short_description: maxLength: 255 type: string @@ -1220,6 +1239,7 @@ definitions: required: - contact_email - name + - permalink - site_url type: object schema.SiteInterfaceReq: @@ -1276,6 +1296,20 @@ definitions: terms_of_service_parsed_text: type: string type: object + schema.SiteSeoReq: + properties: + robots: + type: string + required: + - robots + type: object + schema.SiteSeoResp: + properties: + robots: + type: string + required: + - robots + type: object schema.SiteWriteReq: properties: recommend_tags: @@ -1390,7 +1424,8 @@ definitions: schema.UpdateInfoRequest: properties: avatar: - $ref: '#/definitions/schema.AvatarInfo' + allOf: + - $ref: '#/definitions/schema.AvatarInfo' description: avatar bio: description: bio @@ -1535,6 +1570,9 @@ definitions: display_name: description: display_name type: string + id: + description: user_id + type: string ip_info: description: ip info type: string @@ -1749,7 +1787,7 @@ paths: name: data required: true schema: - $ref: '#/definitions/entity.AdminSetAnswerStatusRequest' + $ref: '#/definitions/schema.AdminSetAnswerStatusRequest' produces: - application/json responses: @@ -2187,6 +2225,47 @@ paths: summary: update site legal info tags: - admin + /answer/admin/api/siteinfo/seo: + get: + description: get site seo information + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/handler.RespBody' + - properties: + data: + $ref: '#/definitions/schema.SiteSeoResp' + type: object + security: + - ApiKeyAuth: [] + summary: get site seo information + tags: + - admin + put: + description: update site seo information + parameters: + - description: seo + in: body + name: data + required: true + schema: + $ref: '#/definitions/schema.SiteSeoReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.RespBody' + security: + - ApiKeyAuth: [] + summary: update site seo information + tags: + - admin /answer/admin/api/siteinfo/write: get: description: get site interface @@ -3354,6 +3433,30 @@ paths: summary: SearchQuestionList tags: - api-question + /answer/api/v1/question/reopen: + put: + consumes: + - application/json + description: reopen question + parameters: + - description: question + in: body + name: data + required: true + schema: + $ref: '#/definitions/schema.ReopenQuestionReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.RespBody' + security: + - ApiKeyAuth: [] + summary: reopen question + tags: + - api-question /answer/api/v1/question/search: post: consumes: @@ -4528,6 +4631,19 @@ paths: summary: UserList tags: - api-question + /robots.txt: + get: + description: get site robots information + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + summary: get site robots information + tags: + - site securityDefinitions: ApiKeyAuth: in: header diff --git a/internal/controller/answer_controller.go b/internal/controller/answer_controller.go index d089d509..d3d1c478 100644 --- a/internal/controller/answer_controller.go +++ b/internal/controller/answer_controller.go @@ -268,7 +268,7 @@ func (ac *AnswerController) Adopted(ctx *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param data body entity.AdminSetAnswerStatusRequest true "AdminSetAnswerStatusRequest" +// @Param data body schema.AdminSetAnswerStatusRequest true "AdminSetAnswerStatusRequest" // @Router /answer/admin/api/answer/status [put] // @Success 200 {object} handler.RespBody func (ac *AnswerController) AdminSetAnswerStatus(ctx *gin.Context) { From d8682e72642808650035ed490bd91673d895aa66 Mon Sep 17 00:00:00 2001 From: aichy126 <16996097+aichy126@users.noreply.github.com> Date: Wed, 7 Dec 2022 17:05:19 +0800 Subject: [PATCH 084/272] add question and tags Description --- internal/schema/question_schema.go | 1 + internal/schema/tag_schema.go | 2 ++ internal/service/question_service.go | 2 ++ internal/service/tag/tag_service.go | 1 + 4 files changed, 6 insertions(+) diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go index 65c66d51..25d20557 100644 --- a/internal/schema/question_schema.go +++ b/internal/schema/question_schema.go @@ -97,6 +97,7 @@ type QuestionInfo struct { Title string `json:"title" xorm:"title"` // title Content string `json:"content" xorm:"content"` // content HTML string `json:"html" xorm:"html"` // html + Description string `json:"description"` //description Tags []*TagResp `json:"tags" ` // tags ViewCount int `json:"view_count" xorm:"view_count"` // view_count UniqueViewCount int `json:"unique_view_count" xorm:"unique_view_count"` // unique_view_count diff --git a/internal/schema/tag_schema.go b/internal/schema/tag_schema.go index 796b6bb8..278b309d 100644 --- a/internal/schema/tag_schema.go +++ b/internal/schema/tag_schema.go @@ -66,6 +66,8 @@ type GetTagResp struct { OriginalText string `json:"original_text"` // parsed text ParsedText string `json:"parsed_text"` + // description text + Description string `json:"description"` // follower amount FollowCount int `json:"follow_count"` // question amount diff --git a/internal/service/question_service.go b/internal/service/question_service.go index cca6b9f2..ea46c533 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -23,6 +23,7 @@ import ( "github.com/answerdev/answer/internal/service/revision_common" tagcommon "github.com/answerdev/answer/internal/service/tag_common" usercommon "github.com/answerdev/answer/internal/service/user_common" + "github.com/answerdev/answer/pkg/htmltext" "github.com/jinzhu/copier" "github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/i18n" @@ -454,6 +455,7 @@ func (qs *QuestionService) GetQuestion(ctx context.Context, questionID, userID s if question.Status == entity.QuestionStatusClosed { per.CanClose = false } + question.Description = htmltext.FetchExcerpt(question.HTML, "...", 240) question.MemberActions = permission.GetQuestionPermission(ctx, userID, question.UserID, per.CanEdit, per.CanDelete, per.CanClose, per.CanReopen) return question, nil diff --git a/internal/service/tag/tag_service.go b/internal/service/tag/tag_service.go index 1504f4fe..8ebe610c 100644 --- a/internal/service/tag/tag_service.go +++ b/internal/service/tag/tag_service.go @@ -105,6 +105,7 @@ func (ts *TagService) GetTagInfo(ctx context.Context, req *schema.GetTagInfoReq) resp.DisplayName = tagInfo.DisplayName resp.OriginalText = tagInfo.OriginalText resp.ParsedText = tagInfo.ParsedText + resp.Description = htmltext.FetchExcerpt(tagInfo.ParsedText, "...", 240) resp.FollowCount = tagInfo.FollowCount resp.QuestionCount = tagInfo.QuestionCount resp.Recommend = tagInfo.Recommend From 19180b6133b0e718237f43397e4caa65dd6b3cd0 Mon Sep 17 00:00:00 2001 From: "haitao(lj)" Date: Wed, 7 Dec 2022 17:24:30 +0800 Subject: [PATCH 085/272] feat: page title and meta tags done --- ui/public/index.html | 25 -- ui/src/common/interface.ts | 14 +- ui/src/components/Footer/index.tsx | 3 +- ui/src/components/PageTitle/index.tsx | 25 -- ui/src/components/UploadImg/index.tsx | 2 - ui/src/components/index.ts | 2 - ui/src/hooks/index.ts | 4 +- ui/src/hooks/useHeadInfo/index.tsx | 12 - ui/src/hooks/usePageTags/index.tsx | 11 + ui/src/pages/Admin/Branding/index.tsx | 3 +- ui/src/pages/Admin/index.tsx | 8 +- ui/src/pages/Install/index.tsx | 7 +- ui/src/pages/Layout/index.tsx | 24 +- ui/src/pages/Legal/Privacy/index.tsx | 7 +- ui/src/pages/Legal/Tos/index.tsx | 6 +- ui/src/pages/Maintenance/index.tsx | 6 +- ui/src/pages/Questions/Ask/index.tsx | 357 +++++++++--------- ui/src/pages/Questions/Detail/index.tsx | 123 +++--- ui/src/pages/Questions/EditAnswer/index.tsx | 230 +++++------ ui/src/pages/Questions/index.tsx | 29 +- ui/src/pages/Review/index.tsx | 13 +- ui/src/pages/Search/index.tsx | 57 +-- ui/src/pages/Tags/Detail/index.tsx | 102 ++--- ui/src/pages/Tags/Edit/index.tsx | 246 ++++++------ ui/src/pages/Tags/Info/index.tsx | 265 +++++++------ ui/src/pages/Tags/index.tsx | 147 ++++---- ui/src/pages/Timeline/index.tsx | 8 +- ui/src/pages/Users/AccountForgot/index.tsx | 51 ++- ui/src/pages/Users/ActivationResult/index.tsx | 44 +-- ui/src/pages/Users/ActiveEmail/index.tsx | 7 +- ui/src/pages/Users/ChangeEmail/index.tsx | 21 +- ui/src/pages/Users/ConfirmNewEmail/index.tsx | 45 ++- ui/src/pages/Users/Login/index.tsx | 8 +- ui/src/pages/Users/Notifications/index.tsx | 113 +++--- ui/src/pages/Users/PasswordReset/index.tsx | 157 ++++---- ui/src/pages/Users/Personal/index.tsx | 7 +- ui/src/pages/Users/Register/index.tsx | 8 +- ui/src/pages/Users/Settings/index.tsx | 42 +-- ui/src/pages/Users/Suspended/index.tsx | 26 +- ui/src/services/admin/settings.ts | 2 +- ui/src/services/client/tag.ts | 4 +- ui/src/stores/branding.ts | 6 +- ui/src/stores/headInfo.ts | 27 -- ui/src/stores/index.ts | 4 +- ui/src/stores/pageTags.ts | 45 +++ ui/src/utils/common.ts | 1 - 46 files changed, 1159 insertions(+), 1195 deletions(-) delete mode 100644 ui/src/components/PageTitle/index.tsx delete mode 100644 ui/src/hooks/useHeadInfo/index.tsx create mode 100644 ui/src/hooks/usePageTags/index.tsx delete mode 100644 ui/src/stores/headInfo.ts create mode 100644 ui/src/stores/pageTags.ts diff --git a/ui/public/index.html b/ui/public/index.html index 304d295f..3c2b1841 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -4,22 +4,7 @@ - - - - Answer @@ -51,15 +36,5 @@
- diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 2e479879..c6aa42a5 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -287,11 +287,15 @@ export interface AdminSettingsGeneral { permalink: number; } -export interface HeadInfo { - title?: string; +export interface HelmetBase { + pageTitle?: string; description?: string; keywords?: string; - ldJSON?; +} + +export interface HelmetUpdate extends Omit { + title?: string; + subtitle?: string; } export interface AdminSettingsInterface { @@ -313,12 +317,12 @@ export interface AdminSettingsSmtp { } export interface SiteSettings { - branding: AdmingSettingBranding; + branding: AdminSettingBranding; general: AdminSettingsGeneral; interface: AdminSettingsInterface; } -export interface AdmingSettingBranding { +export interface AdminSettingBranding { logo: string; square_icon: string; mobile_logo?: string; diff --git a/ui/src/components/Footer/index.tsx b/ui/src/components/Footer/index.tsx index 65ac6f33..eb31feec 100644 --- a/ui/src/components/Footer/index.tsx +++ b/ui/src/components/Footer/index.tsx @@ -9,7 +9,8 @@ import { DEFAULT_SITE_NAME } from '@/common/constants'; const Index = () => { const fullYear = dayjs().format('YYYY'); - const siteName = siteInfoStore.getState().siteInfo.name || DEFAULT_SITE_NAME; + const siteName = + siteInfoStore((state) => state.siteInfo.name) || DEFAULT_SITE_NAME; const cc = `${fullYear} ${siteName}`; return (