diff options
| author | Bobby <[email protected]> | 2025-07-19 17:06:56 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2025-07-19 17:06:56 +0530 |
| commit | 3f73b3c66de04a55bc101ffb96070ae19e7bf27a (patch) | |
| tree | 85ca777a49e3ec533b2fbc3c709ceb6e093c89c0 /controllers | |
| parent | d31111cf0133b223a8e665e6798b8ae09aa5c8a9 (diff) | |
| download | imageboard-3f73b3c66de04a55bc101ffb96070ae19e7bf27a.tar.xz imageboard-3f73b3c66de04a55bc101ffb96070ae19e7bf27a.zip | |
tag adding feature for images
Diffstat (limited to 'controllers')
| -rw-r--r-- | controllers/posts.go | 21 | ||||
| -rw-r--r-- | controllers/tags.go | 279 |
2 files changed, 282 insertions, 18 deletions
diff --git a/controllers/posts.go b/controllers/posts.go index 57738b5..da2df95 100644 --- a/controllers/posts.go +++ b/controllers/posts.go @@ -4,7 +4,6 @@ import ( "errors" "imageboard/config" "imageboard/database" - "imageboard/models" "imageboard/utils/auth" "imageboard/utils/format" "imageboard/utils/handlers" @@ -380,22 +379,9 @@ func PostsSinglePostEditPageController(ctx *fiber.Ctx) error { return InternalServerErrorController(ctx, err) } - postTags := make([]map[string]models.Tag, 0, len(post.Tags)) - for _, tag := range post.Tags { - switch tag.Type { - case config.TagTypeGeneral: - postTags = append(postTags, map[string]models.Tag{"general": tag}) - case config.TagTypeArtist: - postTags = append(postTags, map[string]models.Tag{"artist": tag}) - case config.TagTypeCharacter: - postTags = append(postTags, map[string]models.Tag{"character": tag}) - case config.TagTypeCopyright: - postTags = append(postTags, map[string]models.Tag{"copyright": tag}) - case config.TagTypeMeta: - postTags = append(postTags, map[string]models.Tag{"meta": tag}) - default: - postTags = append(postTags, map[string]models.Tag{"general": tag}) - } + postTags, err := database.GetImageTags(post.ID) + if err != nil { + return InternalServerErrorController(ctx, err) } ctx.Locals("Title", config.PT_POST_EDIT+" #"+format.Int64ToString(int64(post.ID))) @@ -511,6 +497,5 @@ func PostsSinglePostEditPostController(ctx *fiber.Ctx) error { if nextURL == "" { nextURL = "/posts/" + format.Int64ToString(int64(post.ID)) } - return ctx.Redirect(nextURL, fiber.StatusSeeOther) } 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, + }) +} |
