BREAKING CHANGE: The original Go module name was `github.com/ArtalkJS/Artalk`, which has now been modified to `github.com/artalkjs/artalk/v2` (all lowercase). This change may affect the CI pipeline or other areas if you are compiling the Artalk server and have specified the fixed Go module name. Regarding the original `-ldflags -X github.com/ArtalkJS/Artalk/internal/config.Version` and `CommitHash` definitions, please remove them, as they are now obsolete. The `Version` has been hardcoded into the Go code, and the `CommitHash` is now read from the Go build info via runtime/debug’s vcs.revision value. This change only affects the server developers and does not impact the frontend UI client.
166 lines
5.0 KiB
Go
166 lines
5.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/artalkjs/artalk/v2/internal/core"
|
|
"github.com/artalkjs/artalk/v2/internal/dao"
|
|
"github.com/artalkjs/artalk/v2/internal/entity"
|
|
"github.com/artalkjs/artalk/v2/internal/log"
|
|
"github.com/artalkjs/artalk/v2/server/common"
|
|
cog "github.com/artalkjs/artalk/v2/server/handler/comments_get"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type ParamsCommentList struct {
|
|
PageKey string `query:"page_key" json:"page_key" validate:"required"` // The comment page_key
|
|
SiteName string `query:"site_name" json:"site_name" validate:"optional"` // The site name of your content scope
|
|
|
|
Limit int `query:"limit" json:"limit" validate:"optional"` // The limit for pagination
|
|
Offset int `query:"offset" json:"offset" validate:"optional"` // The offset for pagination
|
|
|
|
FlatMode bool `query:"flat_mode" json:"flat_mode" validate:"optional"` // Enable flat_mode
|
|
SortBy string `query:"sort_by" json:"sort_by" enums:"date_asc,date_desc,vote" validate:"optional"` // Sort by condition
|
|
ViewOnlyAdmin bool `query:"view_only_admin" json:"view_only_admin" validate:"optional"` // Only show comments by admin
|
|
|
|
Search string `query:"search" json:"search" validate:"optional"` // Search keywords
|
|
|
|
Type string `query:"type" json:"type" enums:"all,mentions,mine,pending" validate:"optional"` // Message center show type
|
|
Scope string `query:"scope" json:"scope" enums:"page,user,site" validate:"optional"` // The scope of comments
|
|
Name string `query:"name" json:"name" validate:"optional"` // The username
|
|
Email string `query:"email" json:"email" validate:"optional"` // The user email
|
|
}
|
|
|
|
type ResponseCommentList struct {
|
|
Comments []entity.CookedComment `json:"comments"`
|
|
Count int64 `json:"count"`
|
|
RootsCount int64 `json:"roots_count"`
|
|
Page *entity.CookedPage `json:"page,omitempty"`
|
|
}
|
|
|
|
// @Id GetComments
|
|
// @Summary Get Comment List
|
|
// @Description Get a list of comments by some conditions
|
|
// @Tags Comment
|
|
// @Security ApiKeyAuth
|
|
// @Param options query ParamsCommentList true "The options"
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} ResponseCommentList
|
|
// @Failure 500 {object} Map{msg=string}
|
|
// @Router /comments [get]
|
|
func CommentList(app *core.App, router fiber.Router) {
|
|
router.Get("/comments", func(c *fiber.Ctx) error {
|
|
var p ParamsCommentList
|
|
if isOK, resp := common.ParamsDecode(c, &p); !isOK {
|
|
return resp
|
|
}
|
|
|
|
// Get current user
|
|
user, err := common.GetUserByReq(app, c)
|
|
if errors.Is(err, common.ErrTokenNotProvided) {
|
|
// If not login, find user by name and email
|
|
user = app.Dao().FindUser(p.Name, p.Email)
|
|
|
|
// If user is admin, but not login yet, clear user
|
|
if user.IsAdmin {
|
|
user = entity.User{}
|
|
}
|
|
}
|
|
|
|
// Query scope
|
|
scope := cog.ScopePage
|
|
if p.Scope != "" {
|
|
scope = cog.Scope(p.Scope)
|
|
}
|
|
|
|
// if query scope is page, check page exist
|
|
if scope == cog.ScopePage {
|
|
if _, ok, resp := common.CheckSiteExist(app, c, p.SiteName); !ok {
|
|
return resp
|
|
}
|
|
}
|
|
|
|
// Query options
|
|
queryOpts := cog.QueryOptions{
|
|
User: user,
|
|
|
|
Scope: scope,
|
|
|
|
SitePayload: cog.SitePayload{
|
|
Type: cog.SiteScopeType(p.Type),
|
|
SiteName: p.SiteName,
|
|
},
|
|
|
|
PagePayload: cog.PageScopePayload{
|
|
Tags: lo.If(p.ViewOnlyAdmin, []cog.PageScopeTag{cog.AdminOnly}).Else([]cog.PageScopeTag{}),
|
|
PageKey: p.PageKey,
|
|
SiteName: p.SiteName,
|
|
},
|
|
|
|
UserPayload: cog.UserScopePayload{
|
|
Type: cog.UserScopeType(p.Type),
|
|
},
|
|
|
|
SortBy: cog.SortRule(p.SortBy),
|
|
Search: p.Search,
|
|
}
|
|
|
|
// Generate query by options
|
|
comments, count, rootsCount := cog.FindComments(app.Dao(), queryOpts, cog.FindOptions{
|
|
Limit: p.Limit,
|
|
Offset: p.Offset,
|
|
Nested: !p.FlatMode,
|
|
})
|
|
|
|
// Get IP region
|
|
comments = findIPRegionForComments(app, comments)
|
|
|
|
// The response data
|
|
resp := ResponseCommentList{
|
|
Comments: comments,
|
|
Count: count,
|
|
RootsCount: rootsCount,
|
|
}
|
|
|
|
// If query scope is page, extra query page data
|
|
if scope == cog.ScopePage {
|
|
resp.Page = findPageData(app.Dao(), p.PageKey, p.SiteName)
|
|
}
|
|
|
|
return common.RespData(c, resp)
|
|
})
|
|
}
|
|
|
|
func findPageData(dao *dao.Dao, pageKey string, siteName string) *entity.CookedPage {
|
|
page := dao.FindPage(pageKey, siteName)
|
|
if page.IsEmpty() {
|
|
// If page not found, create a new one but not save it
|
|
page = entity.Page{
|
|
Key: pageKey,
|
|
SiteName: siteName,
|
|
}
|
|
}
|
|
cooked := dao.CookPage(&page)
|
|
return &cooked
|
|
}
|
|
|
|
// Find the IP region of each comment
|
|
func findIPRegionForComments(app *core.App, comments []entity.CookedComment) []entity.CookedComment {
|
|
if !app.Conf().IPRegion.Enabled {
|
|
return comments
|
|
}
|
|
|
|
ipRegionService, err := core.AppService[*core.IPRegionService](app)
|
|
if err == nil {
|
|
for i, c := range comments {
|
|
comments[i].IPRegion = ipRegionService.Query(c.IP)
|
|
}
|
|
} else {
|
|
log.Error("[IPRegionService] err: ", err)
|
|
}
|
|
|
|
return comments
|
|
}
|