✨ Search posts
This commit is contained in:
		| @@ -26,7 +26,7 @@ type Post struct { | ||||
| 	hyper.BaseModel | ||||
|  | ||||
| 	Type       string            `json:"type"` | ||||
| 	Body       datatypes.JSONMap `json:"body"` | ||||
| 	Body       datatypes.JSONMap `json:"body" gorm:"index:,type:gin"` | ||||
| 	Language   string            `json:"language"` | ||||
| 	Alias      *string           `json:"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.Get("/", listPost) | ||||
| 			posts.Get("/search", searchPost) | ||||
| 			posts.Get("/minimal", listPostMinimal) | ||||
| 			posts.Get("/drafts", listDraftPost) | ||||
| 			posts.Get("/:postId", getPost) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package api | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"gorm.io/gorm" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| @@ -15,6 +16,47 @@ import ( | ||||
| 	"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 { | ||||
| 	id := c.Params("postId") | ||||
|  | ||||
| @@ -57,44 +99,58 @@ func getPost(c *fiber.Ctx) error { | ||||
| 	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 { | ||||
| 	take := c.QueryInt("take", 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 { | ||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | ||||
| 	} else { | ||||
| 		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")) | ||||
| 	var err error | ||||
| 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	countTx := tx | ||||
| @@ -125,37 +181,12 @@ func listPost(c *fiber.Ctx) error { | ||||
| func listPostMinimal(c *fiber.Ctx) error { | ||||
| 	take := c.QueryInt("take", 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 { | ||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | ||||
| 	} else { | ||||
| 		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")) | ||||
| 	var err error | ||||
| 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	countTx := tx | ||||
|   | ||||
| @@ -1,8 +1,6 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/gap" | ||||
| 	"git.solsynth.dev/hydrogen/interactive/pkg/internal/models" | ||||
| @@ -14,26 +12,12 @@ import ( | ||||
| func listRecommendationNews(c *fiber.Ctx) error { | ||||
| 	take := c.QueryInt("take", 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 { | ||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | ||||
| 	} else { | ||||
| 		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) | ||||
| 	var err error | ||||
| 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	countTx := tx | ||||
| @@ -74,10 +58,13 @@ func listRecommendationFriends(c *fiber.Ctx) error { | ||||
|  | ||||
| 	take := c.QueryInt("take", 0) | ||||
| 	offset := c.QueryInt("offset", 0) | ||||
| 	realm := c.Query("realm") | ||||
|  | ||||
| 	tx := services.FilterPostDraft(database.C) | ||||
| 	tx = services.FilterPostWithUserContext(tx, &user) | ||||
| 	tx := database.C | ||||
|  | ||||
| 	var err error | ||||
| 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	friends, _ := services.ListAccountFriends(user) | ||||
| 	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) | ||||
|  | ||||
| 	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 | ||||
| 	count, err := services.CountPost(countTx) | ||||
| 	if err != nil { | ||||
| @@ -131,26 +106,12 @@ func listRecommendationFriends(c *fiber.Ctx) error { | ||||
| func listRecommendationShuffle(c *fiber.Ctx) error { | ||||
| 	take := c.QueryInt("take", 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 { | ||||
| 		tx = services.FilterPostWithUserContext(tx, &user) | ||||
| 	} else { | ||||
| 		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) | ||||
| 	var err error | ||||
| 	if tx, err = universalPostFilter(c, tx); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	countTx := tx | ||||
|   | ||||
| @@ -97,6 +97,11 @@ func FilterPostDraft(tx *gorm.DB) *gorm.DB { | ||||
| 	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 { | ||||
| 	return tx. | ||||
| 		Preload("Tags"). | ||||
|   | ||||
		Reference in New Issue
	
	Block a user