chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/middleware"
|
||||
"github.com/apache/answer/internal/controller"
|
||||
"github.com/apache/answer/internal/controller_admin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AnswerAPIRouter struct {
|
||||
langController *controller.LangController
|
||||
userController *controller.UserController
|
||||
commentController *controller.CommentController
|
||||
reportController *controller.ReportController
|
||||
voteController *controller.VoteController
|
||||
tagController *controller.TagController
|
||||
followController *controller.FollowController
|
||||
collectionController *controller.CollectionController
|
||||
questionController *controller.QuestionController
|
||||
answerController *controller.AnswerController
|
||||
searchController *controller.SearchController
|
||||
revisionController *controller.RevisionController
|
||||
rankController *controller.RankController
|
||||
adminUserController *controller_admin.UserAdminController
|
||||
reasonController *controller.ReasonController
|
||||
themeController *controller_admin.ThemeController
|
||||
adminSiteInfoController *controller_admin.SiteInfoController
|
||||
siteInfoController *controller.SiteInfoController
|
||||
notificationController *controller.NotificationController
|
||||
dashboardController *controller.DashboardController
|
||||
uploadController *controller.UploadController
|
||||
activityController *controller.ActivityController
|
||||
roleController *controller_admin.RoleController
|
||||
pluginController *controller_admin.PluginController
|
||||
permissionController *controller.PermissionController
|
||||
userPluginController *controller.UserPluginController
|
||||
reviewController *controller.ReviewController
|
||||
metaController *controller.MetaController
|
||||
badgeController *controller.BadgeController
|
||||
adminBadgeController *controller_admin.BadgeController
|
||||
apiKeyController *controller_admin.AdminAPIKeyController
|
||||
aiController *controller.AIController
|
||||
aiConversationController *controller.AIConversationController
|
||||
aiConversationAdminController *controller_admin.AIConversationAdminController
|
||||
mcpController *controller.MCPController
|
||||
}
|
||||
|
||||
func NewAnswerAPIRouter(
|
||||
langController *controller.LangController,
|
||||
userController *controller.UserController,
|
||||
commentController *controller.CommentController,
|
||||
reportController *controller.ReportController,
|
||||
voteController *controller.VoteController,
|
||||
tagController *controller.TagController,
|
||||
followController *controller.FollowController,
|
||||
collectionController *controller.CollectionController,
|
||||
questionController *controller.QuestionController,
|
||||
answerController *controller.AnswerController,
|
||||
searchController *controller.SearchController,
|
||||
revisionController *controller.RevisionController,
|
||||
rankController *controller.RankController,
|
||||
adminUserController *controller_admin.UserAdminController,
|
||||
reasonController *controller.ReasonController,
|
||||
themeController *controller_admin.ThemeController,
|
||||
adminSiteInfoController *controller_admin.SiteInfoController,
|
||||
siteInfoController *controller.SiteInfoController,
|
||||
notificationController *controller.NotificationController,
|
||||
dashboardController *controller.DashboardController,
|
||||
uploadController *controller.UploadController,
|
||||
activityController *controller.ActivityController,
|
||||
roleController *controller_admin.RoleController,
|
||||
pluginController *controller_admin.PluginController,
|
||||
permissionController *controller.PermissionController,
|
||||
userPluginController *controller.UserPluginController,
|
||||
reviewController *controller.ReviewController,
|
||||
metaController *controller.MetaController,
|
||||
badgeController *controller.BadgeController,
|
||||
adminBadgeController *controller_admin.BadgeController,
|
||||
apiKeyController *controller_admin.AdminAPIKeyController,
|
||||
aiController *controller.AIController,
|
||||
aiConversationController *controller.AIConversationController,
|
||||
aiConversationAdminController *controller_admin.AIConversationAdminController,
|
||||
mcpController *controller.MCPController,
|
||||
) *AnswerAPIRouter {
|
||||
return &AnswerAPIRouter{
|
||||
langController: langController,
|
||||
userController: userController,
|
||||
commentController: commentController,
|
||||
reportController: reportController,
|
||||
voteController: voteController,
|
||||
tagController: tagController,
|
||||
followController: followController,
|
||||
collectionController: collectionController,
|
||||
questionController: questionController,
|
||||
answerController: answerController,
|
||||
searchController: searchController,
|
||||
revisionController: revisionController,
|
||||
rankController: rankController,
|
||||
adminUserController: adminUserController,
|
||||
reasonController: reasonController,
|
||||
themeController: themeController,
|
||||
adminSiteInfoController: adminSiteInfoController,
|
||||
notificationController: notificationController,
|
||||
siteInfoController: siteInfoController,
|
||||
dashboardController: dashboardController,
|
||||
uploadController: uploadController,
|
||||
activityController: activityController,
|
||||
roleController: roleController,
|
||||
pluginController: pluginController,
|
||||
permissionController: permissionController,
|
||||
userPluginController: userPluginController,
|
||||
reviewController: reviewController,
|
||||
metaController: metaController,
|
||||
badgeController: badgeController,
|
||||
adminBadgeController: adminBadgeController,
|
||||
apiKeyController: apiKeyController,
|
||||
aiController: aiController,
|
||||
aiConversationController: aiConversationController,
|
||||
aiConversationAdminController: aiConversationAdminController,
|
||||
mcpController: mcpController,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(authUserMiddleware *middleware.AuthUserMiddleware, r *gin.RouterGroup) {
|
||||
// i18n
|
||||
r.GET("/language/config", a.langController.GetLangMapping)
|
||||
r.GET("/language/options", a.langController.GetUserLangOptions)
|
||||
|
||||
// siteinfo
|
||||
r.GET("/siteinfo", a.siteInfoController.GetSiteInfo)
|
||||
r.GET("/siteinfo/legal", a.siteInfoController.GetSiteLegalInfo)
|
||||
|
||||
// user
|
||||
r.GET("/user/info", a.userController.GetUserInfoByUserID)
|
||||
r.GET("/user/action/record", authUserMiddleware.Auth(), a.userController.ActionRecord)
|
||||
routerGroup := r.Group("", middleware.BanAPIForUserCenter)
|
||||
routerGroup.POST("/user/login/email", a.userController.UserEmailLogin)
|
||||
routerGroup.POST("/user/register/email", a.userController.UserRegisterByEmail)
|
||||
routerGroup.POST("/user/email/verification", a.userController.UserVerifyEmail)
|
||||
routerGroup.PUT("/user/email", a.userController.UserChangeEmailVerify)
|
||||
routerGroup.POST("/user/password/reset", a.userController.RetrievePassWord)
|
||||
routerGroup.POST("/user/password/replacement", a.userController.UseRePassWord)
|
||||
routerGroup.PUT("/user/notification/unsubscribe", a.userController.UserUnsubscribeNotification)
|
||||
|
||||
// plugins
|
||||
r.GET("/plugin/status", a.pluginController.GetAllPluginStatus)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
// user
|
||||
r.GET("/personal/user/info", a.userController.GetOtherUserInfoByUsername)
|
||||
r.GET("/user/ranking", a.userController.UserRanking)
|
||||
r.GET("/user/staff", a.userController.UserStaff)
|
||||
|
||||
// answer
|
||||
r.GET("/answer/info", a.answerController.GetAnswerInfo)
|
||||
r.GET("/answer/page", a.answerController.AnswerList)
|
||||
r.GET("/personal/answer/page", a.questionController.PersonalAnswerPage)
|
||||
|
||||
// question
|
||||
r.GET("/question/info", a.questionController.GetQuestion)
|
||||
r.GET("/question/invite", a.questionController.GetQuestionInviteUserInfo)
|
||||
r.GET("/question/page", a.questionController.QuestionPage)
|
||||
r.GET("/question/recommend/page", a.questionController.QuestionRecommendPage)
|
||||
r.GET("/question/similar/tag", a.questionController.SimilarQuestion)
|
||||
r.GET("/personal/qa/top", a.questionController.UserTop)
|
||||
r.GET("/personal/question/page", a.questionController.PersonalQuestionPage)
|
||||
r.GET("/question/link", a.questionController.GetQuestionLink)
|
||||
|
||||
// comment
|
||||
r.GET("/comment/page", a.commentController.GetCommentWithPage)
|
||||
r.GET("/personal/comment/page", a.commentController.GetCommentPersonalWithPage)
|
||||
r.GET("/comment", a.commentController.GetComment)
|
||||
|
||||
// tag
|
||||
r.GET("/tags/page", a.tagController.GetTagWithPage)
|
||||
r.GET("/tags/following", a.tagController.GetFollowingTags)
|
||||
r.GET("/tag", a.tagController.GetTagInfo)
|
||||
r.GET("/tags", a.tagController.GetTagsBySlugName)
|
||||
r.GET("/tag/synonyms", a.tagController.GetTagSynonyms)
|
||||
|
||||
// search
|
||||
r.GET("/search", a.searchController.Search)
|
||||
r.GET("/search/desc", a.searchController.SearchDesc)
|
||||
|
||||
// rank
|
||||
r.GET("/personal/rank/page", a.rankController.GetRankPersonalWithPage)
|
||||
|
||||
// reaction
|
||||
r.GET("/meta/reaction", a.metaController.GetReaction)
|
||||
|
||||
// badges
|
||||
r.GET("/badge", a.badgeController.GetBadgeInfo)
|
||||
r.GET("/badge/awards/page", a.badgeController.GetBadgeAwardList)
|
||||
r.GET("/badge/user/awards/recent", a.badgeController.GetRecentBadgeAwardListByUsername)
|
||||
r.GET("/badge/user/awards", a.badgeController.GetAllBadgeAwardListByUsername)
|
||||
r.GET("/badges", a.badgeController.GetBadgeList)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterAuthUserWithAnyStatusAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
r.GET("/user/logout", a.userController.UserLogout)
|
||||
r.POST("/user/email/change/code", middleware.BanAPIForUserCenter, a.userController.UserChangeEmailSendCode)
|
||||
r.POST("/user/email/verification/send", middleware.BanAPIForUserCenter, a.userController.UserVerifyEmailSend)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
// revisions
|
||||
r.GET("/revisions", a.revisionController.GetRevisionList)
|
||||
r.GET("/revisions/unreviewed", a.revisionController.GetUnreviewedRevisionList)
|
||||
r.PUT("/revisions/audit", a.revisionController.RevisionAudit)
|
||||
r.GET("/revisions/edit/check", a.revisionController.CheckCanUpdateRevision)
|
||||
r.GET("/reviewing/type", a.revisionController.GetReviewingType)
|
||||
|
||||
// comment
|
||||
r.POST("/comment", a.commentController.AddComment)
|
||||
r.DELETE("/comment", a.commentController.RemoveComment)
|
||||
r.PUT("/comment", a.commentController.UpdateComment)
|
||||
|
||||
// report
|
||||
r.POST("/report", a.reportController.AddReport)
|
||||
r.GET("/report/unreviewed/post", a.reportController.GetUnreviewedReportPostPage)
|
||||
r.PUT("/report/review", a.reportController.ReviewReport)
|
||||
|
||||
// review
|
||||
r.GET("/review/pending/post/page", a.reviewController.GetUnreviewedPostPage)
|
||||
r.PUT("/review/pending/post", a.reviewController.UpdateReview)
|
||||
|
||||
// vote
|
||||
r.POST("/vote/up", a.voteController.VoteUp)
|
||||
r.POST("/vote/down", a.voteController.VoteDown)
|
||||
|
||||
// follow
|
||||
r.POST("/follow", a.followController.Follow)
|
||||
r.PUT("/follow/tags", a.followController.UpdateFollowTags)
|
||||
|
||||
// tag
|
||||
r.GET("/question/tags", a.tagController.SearchTagLike)
|
||||
r.POST("/tag", a.tagController.AddTag)
|
||||
r.PUT("/tag", a.tagController.UpdateTag)
|
||||
r.POST("/tag/recover", a.tagController.RecoverTag)
|
||||
r.DELETE("/tag", a.tagController.RemoveTag)
|
||||
r.PUT("/tag/synonym", a.tagController.UpdateTagSynonym)
|
||||
r.POST("/tag/merge", a.tagController.MergeTag)
|
||||
|
||||
// collection
|
||||
r.POST("/collection/switch", a.collectionController.CollectionSwitch)
|
||||
r.GET("/personal/collection/page", a.questionController.PersonalCollectionPage)
|
||||
|
||||
// question
|
||||
r.POST("/question", a.questionController.AddQuestion)
|
||||
r.POST("/question/answer", a.questionController.AddQuestionByAnswer)
|
||||
r.PUT("/question", a.questionController.UpdateQuestion)
|
||||
r.PUT("/question/invite", a.questionController.UpdateQuestionInviteUser)
|
||||
r.DELETE("/question", a.questionController.RemoveQuestion)
|
||||
r.PUT("/question/status", a.questionController.CloseQuestion)
|
||||
r.PUT("/question/operation", a.questionController.OperationQuestion)
|
||||
r.PUT("/question/reopen", a.questionController.ReopenQuestion)
|
||||
r.GET("/question/similar", a.questionController.GetSimilarQuestions)
|
||||
r.POST("/question/recover", a.questionController.QuestionRecover)
|
||||
|
||||
// answer
|
||||
r.POST("/answer", a.answerController.AddAnswer)
|
||||
r.PUT("/answer", a.answerController.UpdateAnswer)
|
||||
r.POST("/answer/acceptance", a.answerController.AcceptAnswer)
|
||||
r.DELETE("/answer", a.answerController.RemoveAnswer)
|
||||
r.POST("/answer/recover", a.answerController.RecoverAnswer)
|
||||
|
||||
// user
|
||||
r.PUT("/user/password", middleware.BanAPIForUserCenter, a.userController.UserModifyPassWord)
|
||||
r.PUT("/user/info", a.userController.UserUpdateInfo)
|
||||
r.PUT("/user/interface", a.userController.UserUpdateInterface)
|
||||
r.GET("/user/notification/config", a.userController.GetUserNotificationConfig)
|
||||
r.PUT("/user/notification/config", a.userController.UpdateUserNotificationConfig)
|
||||
r.GET("/user/info/search", a.userController.SearchUserListByName)
|
||||
|
||||
// vote
|
||||
r.GET("/personal/vote/page", a.voteController.UserVotes)
|
||||
|
||||
// reason
|
||||
r.GET("/reasons", a.reasonController.Reasons)
|
||||
|
||||
// permission
|
||||
r.GET("/permission", a.permissionController.GetPermission)
|
||||
|
||||
// notification
|
||||
r.GET("/notification/status", a.notificationController.GetRedDot)
|
||||
r.PUT("/notification/status", a.notificationController.ClearRedDot)
|
||||
r.GET("/notification/page", a.notificationController.GetList)
|
||||
r.PUT("/notification/read/state/all", a.notificationController.ClearUnRead)
|
||||
r.PUT("/notification/read/state", a.notificationController.ClearIDUnRead)
|
||||
|
||||
// upload file
|
||||
r.POST("/file", a.uploadController.UploadFile)
|
||||
r.POST("/post/render", a.uploadController.PostRender)
|
||||
|
||||
// activity
|
||||
r.GET("/activity/timeline", a.activityController.GetObjectTimeline)
|
||||
r.GET("/activity/timeline/detail", a.activityController.GetObjectTimelineDetail)
|
||||
|
||||
// plugin
|
||||
r.GET("/user/plugin/configs", a.userPluginController.GetUserPluginList)
|
||||
r.GET("/user/plugin/config", a.userPluginController.GetUserPluginConfig)
|
||||
r.PUT("/user/plugin/config", a.userPluginController.UpdatePluginUserConfig)
|
||||
|
||||
// meta
|
||||
r.PUT("/meta/reaction", a.metaController.AddOrUpdateReaction)
|
||||
|
||||
// AI chat
|
||||
r.POST("/chat/completions", a.aiController.ChatCompletions)
|
||||
|
||||
// AI conversation
|
||||
r.GET("/ai/conversation/page", a.aiConversationController.GetConversationList)
|
||||
r.GET("/ai/conversation", a.aiConversationController.GetConversationDetail)
|
||||
r.POST("/ai/conversation/vote", a.aiConversationController.VoteRecord)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) {
|
||||
r.GET("/question/page", a.questionController.AdminQuestionPage)
|
||||
r.PUT("/question/status", a.questionController.AdminUpdateQuestionStatus)
|
||||
r.GET("/answer/page", a.questionController.AdminAnswerPage)
|
||||
r.PUT("/answer/status", a.answerController.AdminUpdateAnswerStatus)
|
||||
|
||||
// user
|
||||
r.GET("/users/page", a.adminUserController.GetUserPage)
|
||||
r.PUT("/user/status", a.adminUserController.UpdateUserStatus)
|
||||
r.PUT("/user/role", a.adminUserController.UpdateUserRole)
|
||||
r.GET("/user/activation", a.adminUserController.GetUserActivation)
|
||||
r.POST("/user/activation", a.adminUserController.SendUserActivation)
|
||||
r.POST("/user", a.adminUserController.AddUser)
|
||||
r.POST("/users", a.adminUserController.AddUsers)
|
||||
r.PUT("/user/password", a.adminUserController.UpdateUserPassword)
|
||||
r.PUT("/user/profile", a.adminUserController.EditUserProfile)
|
||||
|
||||
r.DELETE("/delete/permanently", a.adminUserController.DeletePermanently)
|
||||
|
||||
// reason
|
||||
r.GET("/reasons", a.reasonController.Reasons)
|
||||
|
||||
// language
|
||||
r.GET("/language/options", a.langController.GetAdminLangOptions)
|
||||
|
||||
// theme
|
||||
r.GET("/theme/options", a.themeController.GetThemeOptions)
|
||||
|
||||
// siteinfo
|
||||
r.GET("/siteinfo/general", a.adminSiteInfoController.GetGeneral)
|
||||
r.PUT("/siteinfo/general", a.adminSiteInfoController.UpdateGeneral)
|
||||
|
||||
r.GET("/siteinfo/interface", a.adminSiteInfoController.GetInterface)
|
||||
r.PUT("/siteinfo/interface", a.adminSiteInfoController.UpdateInterface)
|
||||
r.GET("/siteinfo/users-settings", a.adminSiteInfoController.GetUsersSettings)
|
||||
r.PUT("/siteinfo/users-settings", a.adminSiteInfoController.UpdateUsersSettings)
|
||||
|
||||
r.GET("/siteinfo/branding", a.adminSiteInfoController.GetSiteBranding)
|
||||
r.PUT("/siteinfo/branding", a.adminSiteInfoController.UpdateBranding)
|
||||
|
||||
r.GET("/siteinfo/question", a.adminSiteInfoController.GetSiteQuestion)
|
||||
r.PUT("/siteinfo/question", a.adminSiteInfoController.UpdateSiteQuestion)
|
||||
r.GET("/siteinfo/tag", a.adminSiteInfoController.GetSiteTag)
|
||||
r.PUT("/siteinfo/tag", a.adminSiteInfoController.UpdateSiteTag)
|
||||
r.GET("/siteinfo/advanced", a.adminSiteInfoController.GetSiteAdvanced)
|
||||
r.PUT("/siteinfo/advanced", a.adminSiteInfoController.UpdateSiteAdvanced)
|
||||
|
||||
r.GET("/siteinfo/polices", a.adminSiteInfoController.GetSitePolicies)
|
||||
r.PUT("/siteinfo/polices", a.adminSiteInfoController.UpdateSitePolices)
|
||||
r.GET("/siteinfo/security", a.adminSiteInfoController.GetSiteSecurity)
|
||||
r.PUT("/siteinfo/security", a.adminSiteInfoController.UpdateSiteSecurity)
|
||||
|
||||
r.GET("/siteinfo/seo", a.adminSiteInfoController.GetSeo)
|
||||
r.PUT("/siteinfo/seo", a.adminSiteInfoController.UpdateSeo)
|
||||
r.GET("/siteinfo/login", a.adminSiteInfoController.GetSiteLogin)
|
||||
r.PUT("/siteinfo/login", a.adminSiteInfoController.UpdateSiteLogin)
|
||||
r.GET("/siteinfo/custom-css-html", a.adminSiteInfoController.GetSiteCustomCssHTML)
|
||||
r.PUT("/siteinfo/custom-css-html", a.adminSiteInfoController.UpdateSiteCustomCssHTML)
|
||||
r.GET("/siteinfo/theme", a.adminSiteInfoController.GetSiteTheme)
|
||||
r.PUT("/siteinfo/theme", a.adminSiteInfoController.SaveSiteTheme)
|
||||
r.GET("/siteinfo/users", a.adminSiteInfoController.GetSiteUsers)
|
||||
r.PUT("/siteinfo/users", a.adminSiteInfoController.UpdateSiteUsers)
|
||||
r.GET("/setting/smtp", a.adminSiteInfoController.GetSMTPConfig)
|
||||
r.PUT("/setting/smtp", a.adminSiteInfoController.UpdateSMTPConfig)
|
||||
r.GET("/setting/privileges", a.adminSiteInfoController.GetPrivilegesConfig)
|
||||
r.PUT("/setting/privileges", a.adminSiteInfoController.UpdatePrivilegesConfig)
|
||||
|
||||
// dashboard
|
||||
r.GET("/dashboard", a.dashboardController.DashboardInfo)
|
||||
|
||||
// roles
|
||||
r.GET("/roles", a.roleController.GetRoleList)
|
||||
|
||||
// plugin
|
||||
r.GET("/plugins", a.pluginController.GetPluginList)
|
||||
r.PUT("/plugin/status", a.pluginController.UpdatePluginStatus)
|
||||
r.GET("/plugin/config", a.pluginController.GetPluginConfig)
|
||||
r.PUT("/plugin/config", a.pluginController.UpdatePluginConfig)
|
||||
|
||||
// badge
|
||||
r.GET("/badges", a.adminBadgeController.GetBadgeList)
|
||||
r.PUT("/badge/status", a.adminBadgeController.UpdateBadgeStatus)
|
||||
|
||||
// api key
|
||||
r.GET("/api-key/all", a.apiKeyController.GetAllAPIKeys)
|
||||
r.POST("/api-key", a.apiKeyController.AddAPIKey)
|
||||
r.PUT("/api-key", a.apiKeyController.UpdateAPIKey)
|
||||
r.DELETE("/api-key", a.apiKeyController.DeleteAPIKey)
|
||||
|
||||
// ai config
|
||||
r.GET("/ai-config", a.adminSiteInfoController.GetAIConfig)
|
||||
r.PUT("/ai-config", a.adminSiteInfoController.UpdateAIConfig)
|
||||
r.GET("/ai-provider", a.adminSiteInfoController.GetAIProvider)
|
||||
r.POST("/ai-models", a.adminSiteInfoController.RequestAIModels)
|
||||
|
||||
// mcp config
|
||||
r.GET("/mcp-config", a.adminSiteInfoController.GetMCPConfig)
|
||||
r.PUT("/mcp-config", a.adminSiteInfoController.UpdateMCPConfig)
|
||||
|
||||
// AI conversation management
|
||||
r.GET("/ai/conversation/page", a.aiConversationAdminController.GetConversationList)
|
||||
r.GET("/ai/conversation", a.aiConversationAdminController.GetConversationDetail)
|
||||
r.DELETE("/ai/conversation", a.aiConversationAdminController.DeleteConversation)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
// SwaggerConfig struct describes configure for the Swagger API endpoint
|
||||
type SwaggerConfig struct {
|
||||
Show bool `json:"show" mapstructure:"show" yaml:"show"`
|
||||
Protocol string `json:"protocol" mapstructure:"protocol" yaml:"protocol"`
|
||||
Host string `json:"host" mapstructure:"host" yaml:"host"`
|
||||
Address string `json:"address" mapstructure:"address" yaml:"address"`
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/schema/mcp_tools"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
)
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterMCPRouter(r *gin.RouterGroup) {
|
||||
s := server.NewMCPServer("Answer Enterprise MCP Server", "1.0.0")
|
||||
|
||||
s.AddTool(mcp_tools.NewQuestionsTool(), a.mcpController.MCPQuestionsHandler())
|
||||
s.AddTool(mcp_tools.NewAnswersTool(), a.mcpController.MCPAnswersHandler())
|
||||
s.AddTool(mcp_tools.NewCommentsTool(), a.mcpController.MCPCommentsHandler())
|
||||
s.AddTool(mcp_tools.NewTagsTool(), a.mcpController.MCPTagsHandler())
|
||||
s.AddTool(mcp_tools.NewTagDetailTool(), a.mcpController.MCPTagDetailsHandler())
|
||||
s.AddTool(mcp_tools.NewUserTool(), a.mcpController.MCPUserDetailsHandler())
|
||||
|
||||
sseServer := server.NewSSEServer(s,
|
||||
server.WithSSEEndpoint("/answer/api/v1/mcp/see"),
|
||||
server.WithMessageEndpoint("/answer/api/v1/mcp/message"),
|
||||
)
|
||||
r.GET("/mcp/sse", gin.WrapH(sseServer.SSEHandler()))
|
||||
r.POST("/mcp/message", gin.WrapH(sseServer.MessageHandler()))
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/controller"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type PluginAPIRouter struct {
|
||||
connectorController *controller.ConnectorController
|
||||
userCenterController *controller.UserCenterController
|
||||
captchaController *controller.CaptchaController
|
||||
embedController *controller.EmbedController
|
||||
renderController *controller.RenderController
|
||||
sidebarController *controller.SidebarController
|
||||
}
|
||||
|
||||
func NewPluginAPIRouter(
|
||||
connectorController *controller.ConnectorController,
|
||||
userCenterController *controller.UserCenterController,
|
||||
captchaController *controller.CaptchaController,
|
||||
embedController *controller.EmbedController,
|
||||
renderController *controller.RenderController,
|
||||
sidebarController *controller.SidebarController,
|
||||
) *PluginAPIRouter {
|
||||
return &PluginAPIRouter{
|
||||
connectorController: connectorController,
|
||||
userCenterController: userCenterController,
|
||||
captchaController: captchaController,
|
||||
embedController: embedController,
|
||||
renderController: renderController,
|
||||
sidebarController: sidebarController,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *PluginAPIRouter) RegisterUnAuthConnectorRouter(r *gin.RouterGroup) {
|
||||
// connector plugin
|
||||
connectorController := pr.connectorController
|
||||
r.GET(controller.ConnectorLoginRouterPrefix+":name", connectorController.ConnectorLoginDispatcher)
|
||||
r.GET(controller.ConnectorRedirectRouterPrefix+":name", connectorController.ConnectorRedirectDispatcher)
|
||||
r.GET("/connector/info", connectorController.ConnectorsInfo)
|
||||
r.POST("/connector/binding/email", connectorController.ExternalLoginBindingUserSendEmail)
|
||||
|
||||
// user center plugin
|
||||
r.GET("/user-center/agent", pr.userCenterController.UserCenterAgent)
|
||||
r.GET("/user-center/personal/branding", pr.userCenterController.UserCenterPersonalBranding)
|
||||
r.GET(controller.UserCenterLoginRouter, pr.userCenterController.UserCenterLoginRedirect)
|
||||
r.GET(controller.UserCenterSignUpRedirectRouter, pr.userCenterController.UserCenterSignUpRedirect)
|
||||
r.GET("/user-center/login/callback", pr.userCenterController.UserCenterLoginCallback)
|
||||
r.GET("/user-center/sign-up/callback", pr.userCenterController.UserCenterSignUpCallback)
|
||||
|
||||
// captcha plugin
|
||||
r.GET("/captcha/config", pr.captchaController.GetCaptchaConfig)
|
||||
r.GET("/embed/config", pr.embedController.GetEmbedConfig)
|
||||
r.GET("/render/config", pr.renderController.GetRenderConfig)
|
||||
|
||||
// sidebar plugin
|
||||
r.GET("/sidebar/config", pr.sidebarController.GetSidebarConfig)
|
||||
}
|
||||
|
||||
func (pr *PluginAPIRouter) RegisterAuthUserConnectorRouter(r *gin.RouterGroup) {
|
||||
connectorController := pr.connectorController
|
||||
r.GET("/connector/user/info", connectorController.ConnectorsUserInfo)
|
||||
r.DELETE("/connector/user/unbinding", connectorController.ExternalLoginUnbinding)
|
||||
|
||||
r.GET("/user-center/user/settings", pr.userCenterController.UserCenterUserSettings)
|
||||
}
|
||||
|
||||
func (pr *PluginAPIRouter) RegisterAuthAdminConnectorRouter(r *gin.RouterGroup) {
|
||||
r.GET("/user-center/agent", pr.userCenterController.UserCenterAdminFunctionAgent)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSetRouter is providers.
|
||||
var ProviderSetRouter = wire.NewSet(
|
||||
NewAnswerAPIRouter,
|
||||
NewSwaggerRouter,
|
||||
NewStaticRouter,
|
||||
NewUIRouter,
|
||||
NewTemplateRouter,
|
||||
NewPluginAPIRouter,
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/internal/service/service_config"
|
||||
"github.com/apache/answer/pkg/dir"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// StaticRouter static api router
|
||||
type StaticRouter struct {
|
||||
serviceConfig *service_config.ServiceConfig
|
||||
}
|
||||
|
||||
// NewStaticRouter new static api router
|
||||
func NewStaticRouter(serviceConfig *service_config.ServiceConfig) *StaticRouter {
|
||||
return &StaticRouter{
|
||||
serviceConfig: serviceConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterStaticRouter register static api router
|
||||
func (a *StaticRouter) RegisterStaticRouter(r *gin.RouterGroup) {
|
||||
r.Static("/uploads/"+constant.AvatarSubPath, filepath.Join(a.serviceConfig.UploadPath, constant.AvatarSubPath))
|
||||
r.Static("/uploads/"+constant.AvatarThumbSubPath, filepath.Join(a.serviceConfig.UploadPath, constant.AvatarThumbSubPath))
|
||||
r.Static("/uploads/"+constant.PostSubPath, filepath.Join(a.serviceConfig.UploadPath, constant.PostSubPath))
|
||||
r.Static("/uploads/"+constant.BrandingSubPath, filepath.Join(a.serviceConfig.UploadPath, constant.BrandingSubPath))
|
||||
r.GET("/uploads/"+constant.FilesPostSubPath+"/*filepath", func(c *gin.Context) {
|
||||
// The filepath such as hash/123.pdf
|
||||
filePath := c.Param("filepath")
|
||||
// The original filename is 123.pdf
|
||||
originalFilename := filepath.Base(filePath)
|
||||
// The real filename is hash.pdf
|
||||
realFilename := strings.TrimSuffix(filePath, "/"+originalFilename) + filepath.Ext(originalFilename)
|
||||
// The file local path is /uploads/files/post/hash.pdf
|
||||
fileLocalPath := filepath.Join(a.serviceConfig.UploadPath, constant.FilesPostSubPath, realFilename)
|
||||
// If the file is not exist, return 404
|
||||
if !dir.CheckFileExist(fileLocalPath) {
|
||||
c.Redirect(http.StatusFound, "/404")
|
||||
return
|
||||
}
|
||||
c.FileAttachment(fileLocalPath, originalFilename)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/apache/answer/docs"
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerfiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
// SwaggerRouter swagger api router
|
||||
type SwaggerRouter struct {
|
||||
config *SwaggerConfig
|
||||
}
|
||||
|
||||
// NewSwaggerRouter new swagger api router
|
||||
func NewSwaggerRouter(config *SwaggerConfig) *SwaggerRouter {
|
||||
return &SwaggerRouter{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Register register swagger api router
|
||||
func (a *SwaggerRouter) Register(r *gin.RouterGroup) {
|
||||
if a.config.Show {
|
||||
a.InitSwaggerDocs()
|
||||
gofmt := fmt.Sprintf("%s://%s%s/swagger/doc.json", a.config.Protocol, a.config.Host, a.config.Address)
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler, ginSwagger.URL(gofmt)))
|
||||
}
|
||||
}
|
||||
|
||||
// InitSwaggerDocs init swagger docs
|
||||
func (a *SwaggerRouter) InitSwaggerDocs() {
|
||||
docs.SwaggerInfo.Host = fmt.Sprintf("%s%s", a.config.Host, a.config.Address)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/apache/answer/internal/base/middleware"
|
||||
"github.com/apache/answer/internal/controller"
|
||||
templaterender "github.com/apache/answer/internal/controller/template_render"
|
||||
"github.com/apache/answer/internal/controller_admin"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type TemplateRouter struct {
|
||||
templateController *controller.TemplateController
|
||||
templateRenderController *templaterender.TemplateRenderController
|
||||
siteInfoController *controller_admin.SiteInfoController
|
||||
authUserMiddleware *middleware.AuthUserMiddleware
|
||||
}
|
||||
|
||||
func NewTemplateRouter(
|
||||
templateController *controller.TemplateController,
|
||||
templateRenderController *templaterender.TemplateRenderController,
|
||||
siteInfoController *controller_admin.SiteInfoController,
|
||||
authUserMiddleware *middleware.AuthUserMiddleware,
|
||||
|
||||
) *TemplateRouter {
|
||||
return &TemplateRouter{
|
||||
templateController: templateController,
|
||||
templateRenderController: templateRenderController,
|
||||
siteInfoController: siteInfoController,
|
||||
authUserMiddleware: authUserMiddleware,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTemplateRouter template router
|
||||
func (a *TemplateRouter) RegisterTemplateRouter(r *gin.RouterGroup, baseURLPath string) {
|
||||
seoNoAuth := r.Group(baseURLPath)
|
||||
seoNoAuth.GET("/sitemap.xml", a.templateController.Sitemap)
|
||||
seoNoAuth.GET("/sitemap/:page", a.templateController.SitemapPage)
|
||||
|
||||
seoNoAuth.GET("/robots.txt", a.siteInfoController.GetRobots)
|
||||
seoNoAuth.GET("/custom.css", a.siteInfoController.GetCss)
|
||||
|
||||
seoNoAuth.GET("/404", a.templateController.Page404)
|
||||
|
||||
seoNoAuth.GET("/opensearch.xml", a.templateController.OpenSearch)
|
||||
|
||||
seo := r.Group(baseURLPath)
|
||||
seo.Use(a.authUserMiddleware.CheckPrivateMode())
|
||||
seo.GET("/", a.templateController.Index)
|
||||
seo.GET("/questions", a.templateController.QuestionList)
|
||||
seo.GET("/questions/:id", a.templateController.QuestionInfo)
|
||||
seo.GET("/questions/:id/:title", a.templateController.QuestionInfo)
|
||||
seo.GET("/questions/:id/:title/:answerid", a.templateController.QuestionInfo)
|
||||
seo.GET("/tags", a.templateController.TagList)
|
||||
seo.GET("/tags/:tag", a.templateController.TagInfo)
|
||||
seo.GET("/users/:username", a.templateController.UserInfo)
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/controller"
|
||||
"github.com/apache/answer/internal/service/siteinfo_common"
|
||||
"github.com/apache/answer/pkg/htmltext"
|
||||
"github.com/apache/answer/plugin"
|
||||
"github.com/apache/answer/ui"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
const UIIndexFilePath = "build/index.html"
|
||||
const UIRootFilePath = "build"
|
||||
const UIStaticPath = "build/static"
|
||||
|
||||
// UIRouter is an interface that provides ui static file routers
|
||||
type UIRouter struct {
|
||||
siteInfoController *controller.SiteInfoController
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService
|
||||
}
|
||||
|
||||
// NewUIRouter creates a new UIRouter instance with the embed resources
|
||||
func NewUIRouter(
|
||||
siteInfoController *controller.SiteInfoController,
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService,
|
||||
) *UIRouter {
|
||||
return &UIRouter{
|
||||
siteInfoController: siteInfoController,
|
||||
siteInfoService: siteInfoService,
|
||||
}
|
||||
}
|
||||
|
||||
// _resource is an interface that provides static file, it's a private interface
|
||||
type _resource struct {
|
||||
fs embed.FS
|
||||
}
|
||||
|
||||
// Open to implement the interface by http.FS required
|
||||
func (r *_resource) Open(name string) (fs.File, error) {
|
||||
name = fmt.Sprintf(UIStaticPath+"/%s", name)
|
||||
log.Debugf("open static path %s", name)
|
||||
return r.fs.Open(name)
|
||||
}
|
||||
|
||||
// Register a new static resource which generated by ui directory
|
||||
func (a *UIRouter) Register(r *gin.Engine, baseURLPath string) {
|
||||
staticPath := os.Getenv("ANSWER_STATIC_PATH")
|
||||
|
||||
// if ANSWER_STATIC_PATH is set and not empty, ignore embed resource
|
||||
if staticPath != "" {
|
||||
info, err := os.Stat(staticPath)
|
||||
|
||||
if err != nil || !info.IsDir() {
|
||||
log.Error(err)
|
||||
} else {
|
||||
log.Debugf("registering static path %s", staticPath)
|
||||
|
||||
r.LoadHTMLGlob(staticPath + "/*.html")
|
||||
r.Static(baseURLPath+"/static", staticPath+"/static")
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{})
|
||||
})
|
||||
|
||||
// return immediately if the static path is set
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// handle the static file by default ui static files
|
||||
r.StaticFS(baseURLPath+"/static", http.FS(&_resource{
|
||||
fs: ui.Build,
|
||||
}))
|
||||
|
||||
// specify the not router for default routes and redirect
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
urlPath := c.Request.URL.Path
|
||||
if len(baseURLPath) > 0 {
|
||||
urlPath = strings.TrimPrefix(urlPath, baseURLPath)
|
||||
}
|
||||
filePath := ""
|
||||
switch urlPath {
|
||||
case "/favicon.ico":
|
||||
branding, err := a.siteInfoService.GetSiteBranding(c)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
if branding != nil && branding.Favicon != "" {
|
||||
c.String(http.StatusOK, htmltext.GetPicByUrl(branding.Favicon))
|
||||
return
|
||||
} else if branding != nil && branding.SquareIcon != "" {
|
||||
c.String(http.StatusOK, htmltext.GetPicByUrl(branding.SquareIcon))
|
||||
return
|
||||
} else {
|
||||
c.Header("content-type", "image/vnd.microsoft.icon")
|
||||
filePath = UIRootFilePath + urlPath
|
||||
}
|
||||
case "/manifest.json":
|
||||
a.siteInfoController.GetManifestJson(c)
|
||||
return
|
||||
case "/install":
|
||||
// if answer is running by run command user can not access install page.
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
return
|
||||
default:
|
||||
filePath = UIIndexFilePath
|
||||
c.Header("content-type", "text/html;charset=utf-8")
|
||||
c.Header("X-Frame-Options", "DENY")
|
||||
}
|
||||
file, err := ui.Build.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
c.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
cdnPrefix := ""
|
||||
_ = plugin.CallCDN(func(fn plugin.CDN) error {
|
||||
cdnPrefix = fn.GetStaticPrefix()
|
||||
return nil
|
||||
})
|
||||
if cdnPrefix != "" {
|
||||
if cdnPrefix[len(cdnPrefix)-1:] == "/" {
|
||||
cdnPrefix = strings.TrimSuffix(cdnPrefix, "/")
|
||||
}
|
||||
c.String(http.StatusOK, strings.ReplaceAll(string(file), "/static", cdnPrefix+"/static"))
|
||||
return
|
||||
}
|
||||
|
||||
// This part is to solve the problem of returning 404 when the access path does not exist.
|
||||
// However, there is no way to check whether the current route exists in the frontend.
|
||||
// We can only hand over the route to the frontend for processing.
|
||||
// And the plugin, frontend routes can now be dynamically registered,
|
||||
// so there's no good way to get all frontend routes
|
||||
//if filePath == UIIndexFilePath {
|
||||
// c.String(http.StatusNotFound, string(file))
|
||||
// return
|
||||
//}
|
||||
|
||||
c.String(http.StatusOK, string(file))
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/apache/answer/internal/service/mock"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func TestUIRouter_FaviconWithNilBranding(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
mockSiteInfoService := mock.NewMockSiteInfoCommonService(ctrl)
|
||||
|
||||
// Simulate a database error
|
||||
mockSiteInfoService.EXPECT().
|
||||
GetSiteBranding(gomock.Any()).
|
||||
Return(nil, errors.New("database connection failed"))
|
||||
|
||||
router := &UIRouter{
|
||||
siteInfoService: mockSiteInfoService,
|
||||
}
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
router.Register(r, "")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/favicon.ico", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
}
|
||||
Reference in New Issue
Block a user