✨ Search posts
This commit is contained in:
		| @@ -26,7 +26,7 @@ type Post struct { | |||||||
| 	hyper.BaseModel | 	hyper.BaseModel | ||||||
|  |  | ||||||
| 	Type       string            `json:"type"` | 	Type       string            `json:"type"` | ||||||
| 	Body       datatypes.JSONMap `json:"body"` | 	Body       datatypes.JSONMap `json:"body" gorm:"index:,type:gin"` | ||||||
| 	Language   string            `json:"language"` | 	Language   string            `json:"language"` | ||||||
| 	Alias      *string           `json:"alias"` | 	Alias      *string           `json:"alias"` | ||||||
| 	AreaAlias  *string           `json:"area_alias"` | 	AreaAlias  *string           `json:"area_alias"` | ||||||
|   | |||||||
| @@ -34,6 +34,7 @@ func MapAPIs(app *fiber.App, baseURL string) { | |||||||
| 		posts := api.Group("/posts").Name("Posts API") | 		posts := api.Group("/posts").Name("Posts API") | ||||||
| 		{ | 		{ | ||||||
| 			posts.Get("/", listPost) | 			posts.Get("/", listPost) | ||||||
|  | 			posts.Get("/search", searchPost) | ||||||
| 			posts.Get("/minimal", listPostMinimal) | 			posts.Get("/minimal", listPostMinimal) | ||||||
| 			posts.Get("/drafts", listDraftPost) | 			posts.Get("/drafts", listDraftPost) | ||||||
| 			posts.Get("/:postId", getPost) | 			posts.Get("/:postId", getPost) | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package api | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"gorm.io/gorm" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| @@ -15,6 +16,47 @@ import ( | |||||||
| 	"github.com/samber/lo" | 	"github.com/samber/lo" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func universalPostFilter(c *fiber.Ctx, tx *gorm.DB) (*gorm.DB, error) { | ||||||
|  | 	realm := c.Query("realm") | ||||||
|  |  | ||||||
|  | 	tx = services.FilterPostDraft(tx) | ||||||
|  |  | ||||||
|  | 	if user, authenticated := c.Locals("user").(models.Account); authenticated { | ||||||
|  | 		tx = services.FilterPostWithUserContext(tx, &user) | ||||||
|  | 	} else { | ||||||
|  | 		tx = services.FilterPostWithUserContext(tx, nil) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(realm) > 0 { | ||||||
|  | 		if realm, err := services.GetRealmWithAlias(realm); err != nil { | ||||||
|  | 			return tx, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) | ||||||
|  | 		} else { | ||||||
|  | 			tx = services.FilterPostWithRealm(tx, realm.ID) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if c.QueryBool("noReply", true) { | ||||||
|  | 		tx = services.FilterPostReply(tx) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(c.Query("author")) > 0 { | ||||||
|  | 		var author models.Account | ||||||
|  | 		if err := database.C.Where(&hyper.BaseUser{Name: c.Query("author")}).First(&author).Error; err != nil { | ||||||
|  | 			return tx, fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||||
|  | 		} | ||||||
|  | 		tx = tx.Where("author_id = ?", author.ID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(c.Query("category")) > 0 { | ||||||
|  | 		tx = services.FilterPostWithCategory(tx, c.Query("category")) | ||||||
|  | 	} | ||||||
|  | 	if len(c.Query("tag")) > 0 { | ||||||
|  | 		tx = services.FilterPostWithTag(tx, c.Query("tag")) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return tx, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func getPost(c *fiber.Ctx) error { | func getPost(c *fiber.Ctx) error { | ||||||
| 	id := c.Params("postId") | 	id := c.Params("postId") | ||||||
|  |  | ||||||
| @@ -57,44 +99,58 @@ func getPost(c *fiber.Ctx) error { | |||||||
| 	return c.JSON(item) | 	return c.JSON(item) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func searchPost(c *fiber.Ctx) error { | ||||||
|  | 	take := c.QueryInt("take", 0) | ||||||
|  | 	offset := c.QueryInt("offset", 0) | ||||||
|  |  | ||||||
|  | 	tx := database.C | ||||||
|  |  | ||||||
|  | 	probe := c.Query("probe") | ||||||
|  | 	if len(probe) == 0 { | ||||||
|  | 		return fiber.NewError(fiber.StatusBadRequest, "probe is required") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tx = services.FilterPostWithFuzzySearch(tx, probe) | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	countTx := tx | ||||||
|  | 	count, err := services.CountPost(countTx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	items, err := services.ListPost(tx, take, offset, "published_at DESC") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if c.QueryBool("truncate", true) { | ||||||
|  | 		for _, item := range items { | ||||||
|  | 			if item != nil { | ||||||
|  | 				item = lo.ToPtr(services.TruncatePostContent(*item)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return c.JSON(fiber.Map{ | ||||||
|  | 		"count": count, | ||||||
|  | 		"data":  items, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| func listPost(c *fiber.Ctx) error { | func listPost(c *fiber.Ctx) error { | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
| 	realm := c.Query("realm") |  | ||||||
|  |  | ||||||
| 	tx := services.FilterPostDraft(database.C) | 	tx := database.C | ||||||
|  |  | ||||||
| 	if user, authenticated := c.Locals("user").(models.Account); authenticated { | 	var err error | ||||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
| 	} else { | 		return err | ||||||
| 		tx = services.FilterPostWithUserContext(tx, nil) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(realm) > 0 { |  | ||||||
| 		if realm, err := services.GetRealmWithAlias(realm); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) |  | ||||||
| 		} else { |  | ||||||
| 			tx = services.FilterPostWithRealm(tx, realm.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if c.QueryBool("noReply", true) { |  | ||||||
| 		tx = services.FilterPostReply(tx) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(c.Query("author")) > 0 { |  | ||||||
| 		var author models.Account |  | ||||||
| 		if err := database.C.Where(&hyper.BaseUser{Name: c.Query("author")}).First(&author).Error; err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusNotFound, err.Error()) |  | ||||||
| 		} |  | ||||||
| 		tx = tx.Where("author_id = ?", author.ID) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(c.Query("category")) > 0 { |  | ||||||
| 		tx = services.FilterPostWithCategory(tx, c.Query("category")) |  | ||||||
| 	} |  | ||||||
| 	if len(c.Query("tag")) > 0 { |  | ||||||
| 		tx = services.FilterPostWithTag(tx, c.Query("tag")) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	countTx := tx | 	countTx := tx | ||||||
| @@ -125,37 +181,12 @@ func listPost(c *fiber.Ctx) error { | |||||||
| func listPostMinimal(c *fiber.Ctx) error { | func listPostMinimal(c *fiber.Ctx) error { | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
| 	realm := c.Query("realm") |  | ||||||
|  |  | ||||||
| 	tx := services.FilterPostDraft(database.C) | 	tx := database.C | ||||||
|  |  | ||||||
| 	if user, authenticated := c.Locals("user").(models.Account); authenticated { | 	var err error | ||||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
| 	} else { | 		return err | ||||||
| 		tx = services.FilterPostWithUserContext(tx, nil) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(realm) > 0 { |  | ||||||
| 		if realm, err := services.GetRealmWithAlias(realm); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) |  | ||||||
| 		} else { |  | ||||||
| 			tx = services.FilterPostWithRealm(tx, realm.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(c.Query("author")) > 0 { |  | ||||||
| 		var author models.Account |  | ||||||
| 		if err := database.C.Where(&hyper.BaseUser{Name: c.Query("author")}).First(&author).Error; err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusNotFound, err.Error()) |  | ||||||
| 		} |  | ||||||
| 		tx = tx.Where("author_id = ?", author.ID) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(c.Query("category")) > 0 { |  | ||||||
| 		tx = services.FilterPostWithCategory(tx, c.Query("category")) |  | ||||||
| 	} |  | ||||||
| 	if len(c.Query("tag")) > 0 { |  | ||||||
| 		tx = services.FilterPostWithTag(tx, c.Query("tag")) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	countTx := tx | 	countTx := tx | ||||||
|   | |||||||
| @@ -1,8 +1,6 @@ | |||||||
| package api | package api | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
|  |  | ||||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/database" | 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/database" | ||||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/gap" | 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/gap" | ||||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/models" | 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/models" | ||||||
| @@ -14,26 +12,12 @@ import ( | |||||||
| func listRecommendationNews(c *fiber.Ctx) error { | func listRecommendationNews(c *fiber.Ctx) error { | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
| 	realm := c.Query("realm") |  | ||||||
|  |  | ||||||
| 	tx := services.FilterPostDraft(database.C) | 	tx := database.C | ||||||
|  |  | ||||||
| 	if user, authenticated := c.Locals("user").(models.Account); authenticated { | 	var err error | ||||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
| 	} else { | 		return err | ||||||
| 		tx = services.FilterPostWithUserContext(tx, nil) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(realm) > 0 { |  | ||||||
| 		if realm, err := services.GetRealmWithAlias(realm); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) |  | ||||||
| 		} else { |  | ||||||
| 			tx = services.FilterPostWithRealm(tx, realm.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if c.QueryBool("noReply", true) { |  | ||||||
| 		tx = services.FilterPostReply(tx) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	countTx := tx | 	countTx := tx | ||||||
| @@ -74,10 +58,13 @@ func listRecommendationFriends(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
| 	realm := c.Query("realm") |  | ||||||
|  |  | ||||||
| 	tx := services.FilterPostDraft(database.C) | 	tx := database.C | ||||||
| 	tx = services.FilterPostWithUserContext(tx, &user) |  | ||||||
|  | 	var err error | ||||||
|  | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	friends, _ := services.ListAccountFriends(user) | 	friends, _ := services.ListAccountFriends(user) | ||||||
| 	friendList := lo.Map(friends, func(item models.Account, index int) uint { | 	friendList := lo.Map(friends, func(item models.Account, index int) uint { | ||||||
| @@ -86,18 +73,6 @@ func listRecommendationFriends(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	tx = tx.Where("author_id IN ?", friendList) | 	tx = tx.Where("author_id IN ?", friendList) | ||||||
|  |  | ||||||
| 	if len(realm) > 0 { |  | ||||||
| 		if realm, err := services.GetRealmWithAlias(realm); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) |  | ||||||
| 		} else { |  | ||||||
| 			tx = services.FilterPostWithRealm(tx, realm.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if c.QueryBool("noReply", true) { |  | ||||||
| 		tx = services.FilterPostReply(tx) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	countTx := tx | 	countTx := tx | ||||||
| 	count, err := services.CountPost(countTx) | 	count, err := services.CountPost(countTx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -131,26 +106,12 @@ func listRecommendationFriends(c *fiber.Ctx) error { | |||||||
| func listRecommendationShuffle(c *fiber.Ctx) error { | func listRecommendationShuffle(c *fiber.Ctx) error { | ||||||
| 	take := c.QueryInt("take", 0) | 	take := c.QueryInt("take", 0) | ||||||
| 	offset := c.QueryInt("offset", 0) | 	offset := c.QueryInt("offset", 0) | ||||||
| 	realm := c.Query("realm") |  | ||||||
|  |  | ||||||
| 	tx := services.FilterPostDraft(database.C) | 	tx := database.C | ||||||
|  |  | ||||||
| 	if user, authenticated := c.Locals("user").(models.Account); authenticated { | 	var err error | ||||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||||
| 	} else { | 		return err | ||||||
| 		tx = services.FilterPostWithUserContext(tx, nil) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(realm) > 0 { |  | ||||||
| 		if realm, err := services.GetRealmWithAlias(realm); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("realm was not found: %v", err)) |  | ||||||
| 		} else { |  | ||||||
| 			tx = services.FilterPostWithRealm(tx, realm.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if c.QueryBool("noReply", true) { |  | ||||||
| 		tx = services.FilterPostReply(tx) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	countTx := tx | 	countTx := tx | ||||||
|   | |||||||
| @@ -97,6 +97,11 @@ func FilterPostDraft(tx *gorm.DB) *gorm.DB { | |||||||
| 	return tx.Where("is_draft = ? OR is_draft IS NULL", false) | 	return tx.Where("is_draft = ? OR is_draft IS NULL", false) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func FilterPostWithFuzzySearch(tx *gorm.DB, probe string) *gorm.DB { | ||||||
|  | 	probe = "%" + probe + "%" | ||||||
|  | 	return tx.Where("? AND body->>'content' ILIKE ?", gorm.Expr("body ? 'content'"), probe) | ||||||
|  | } | ||||||
|  |  | ||||||
| func PreloadGeneral(tx *gorm.DB) *gorm.DB { | func PreloadGeneral(tx *gorm.DB) *gorm.DB { | ||||||
| 	return tx. | 	return tx. | ||||||
| 		Preload("Tags"). | 		Preload("Tags"). | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user