aboutsummaryrefslogtreecommitdiff
path: root/controllers/tags.go
diff options
context:
space:
mode:
Diffstat (limited to 'controllers/tags.go')
-rw-r--r--controllers/tags.go279
1 files changed, 279 insertions, 0 deletions
diff --git a/controllers/tags.go b/controllers/tags.go
new file mode 100644
index 0000000..833c11d
--- /dev/null
+++ b/controllers/tags.go
@@ -0,0 +1,279 @@
+package controllers
+
+import (
+ "imageboard/config"
+ "imageboard/database"
+ "imageboard/models"
+ "imageboard/utils/auth"
+ "imageboard/utils/format"
+ "strconv"
+ "strings"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func TagsSearchJSONController(ctx *fiber.Ctx) error {
+ tagName := ctx.Query("name")
+ tagType := config.TagType(ctx.Query("type"))
+ limit := ctx.QueryInt("limit", 20)
+ offset := ctx.QueryInt("offset", 0)
+ if tagName == "" {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Tag name is required",
+ })
+ }
+
+ tags, err := database.SearchTags(tagName, limit, offset, &tagType)
+ if err != nil {
+ return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Failed to fetch tags",
+ })
+ }
+
+ if len(tags) == 0 {
+ return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "error": "No tags found",
+ })
+ }
+
+ return ctx.JSON(tags)
+}
+
+func FindOrCreateTagJSONController(ctx *fiber.Ctx) error {
+ var request struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+ }
+
+ if !auth.IsAuthenticated(ctx) {
+ return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
+ "error": "Unauthorized",
+ })
+ }
+
+ currentUser := auth.GetCurrentUser(ctx)
+ if !currentUser.CanCreateTags() {
+ return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{
+ "error": "You do not have permission to create tags",
+ })
+ }
+
+ if err := ctx.BodyParser(&request); err != nil {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Invalid request body",
+ })
+ }
+
+ tag, err := database.FindOrCreateTag(request.Name, config.TagType(request.Type))
+ if err != nil {
+ return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Failed to find or create tag",
+ })
+ }
+
+ return ctx.JSON(tag)
+}
+
+func TagsSearchForImageJSONController(ctx *fiber.Ctx) error {
+ if !auth.IsAuthenticated(ctx) {
+ return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
+ "error": "Unauthorized",
+ })
+ }
+
+ query := ctx.Query("q")
+ imageID := ctx.Query("image_id")
+ tagType := ctx.Query("type")
+
+ if query == "" {
+ return ctx.JSON([]interface{}{})
+ }
+
+ var uintImageID uint
+ if imageID != "" {
+ if id, err := strconv.ParseUint(imageID, 10, 32); err == nil {
+ uintImageID = uint(id)
+ }
+ }
+
+ var tagTypeEnum *config.TagType
+ if tagType != "" {
+ t := config.TagType(tagType)
+ tagTypeEnum = &t
+ }
+
+ var tags []models.Tag
+ var err error
+
+ if uintImageID > 0 {
+ tags, err = database.SearchTagsExcluding(query, uintImageID, 10, tagTypeEnum)
+ } else {
+ tags, err = database.SearchTags(query, 10, 0, tagTypeEnum)
+ }
+
+ if err != nil {
+ return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Search failed",
+ })
+ }
+
+ currentUser := auth.GetCurrentUser(ctx)
+ canCreate := currentUser != nil && currentUser.CanCreateTags()
+
+ result := fiber.Map{
+ "tags": tags,
+ "can_create": canCreate,
+ "query": query,
+ }
+
+ return ctx.JSON(result)
+}
+
+func TagsAddToImageJSONController(ctx *fiber.Ctx) error {
+ if !auth.IsAuthenticated(ctx) {
+ return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
+ "error": "Unauthorized",
+ })
+ }
+
+ imageID := ctx.Query("image_id")
+ if imageID == "" {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Image ID required",
+ })
+ }
+
+ uintImageID, err := format.StringToUint(imageID)
+ if err != nil {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Invalid image ID",
+ })
+ }
+
+ var request struct {
+ TagName string `json:"tag_name"`
+ TagType string `json:"tag_type"`
+ }
+
+ if err := ctx.BodyParser(&request); err != nil {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Invalid request body",
+ })
+ }
+
+ if request.TagName == "" {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Tag name required",
+ })
+ }
+
+ if request.TagType == "" {
+ request.TagType = "general"
+ }
+
+ tagTypeEnum := config.TagType(request.TagType)
+ currentUser := auth.GetCurrentUser(ctx)
+
+ tag, err := database.FindOrCreateTag(request.TagName, tagTypeEnum)
+ if err != nil {
+ // Check if this is a cross-category error
+ if strings.Contains(err.Error(), "already exists as") || strings.Contains(err.Error(), "previously existed as") {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": err.Error(),
+ })
+ }
+ // For other errors, check permissions
+ if !currentUser.CanCreateTags() {
+ return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{
+ "error": "Cannot create new tags",
+ })
+ }
+ return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Failed to create tag",
+ })
+ }
+
+ var tagsToAdd []uint
+ if tag.ParentID != nil {
+ _, ancestors, err := database.GetTagWithAncestors(tag.ID)
+ if err == nil {
+ for _, ancestor := range ancestors {
+ tagsToAdd = append(tagsToAdd, ancestor.ID)
+ }
+ }
+ }
+ tagsToAdd = append(tagsToAdd, tag.ID)
+
+ for _, tagID := range tagsToAdd {
+ database.AddTagToImage(uintImageID, tagID)
+ }
+
+ return ctx.JSON(fiber.Map{
+ "success": true,
+ "tag": tag,
+ })
+}
+
+func TagsRemoveFromImageJSONController(ctx *fiber.Ctx) error {
+ if !auth.IsAuthenticated(ctx) {
+ return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
+ "error": "Unauthorized",
+ })
+ }
+
+ imageID := ctx.Query("image_id")
+ if imageID == "" {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Image ID required",
+ })
+ }
+
+ uintImageID, err := format.StringToUint(imageID)
+ if err != nil {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Invalid image ID",
+ })
+ }
+
+ var request struct {
+ TagID uint `json:"tag_id"`
+ }
+
+ if err := ctx.BodyParser(&request); err != nil {
+ return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "error": "Invalid request body",
+ })
+ }
+
+ currentUser := auth.GetCurrentUser(ctx)
+ post, err := database.GetPostByID(uintImageID)
+ if err != nil {
+ return ctx.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "error": "Post not found",
+ })
+ }
+
+ if post.Uploader.Username != currentUser.Username && !currentUser.CanEditPosts() {
+ return ctx.Status(fiber.StatusForbidden).JSON(fiber.Map{
+ "error": "Cannot edit this post",
+ })
+ }
+
+ _, descendants, err := database.GetTagWithDescendants(request.TagID)
+ if err == nil {
+ for _, descendant := range descendants {
+ database.RemoveTagFromImage(uintImageID, descendant.ID)
+ }
+ }
+
+ err = database.RemoveTagFromImage(uintImageID, request.TagID)
+ if err != nil {
+ return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "error": "Failed to remove tag",
+ })
+ }
+
+ return ctx.JSON(fiber.Map{
+ "success": true,
+ })
+}