aboutsummaryrefslogtreecommitdiff
path: root/controllers
diff options
context:
space:
mode:
authorBobby <[email protected]>2025-07-19 17:06:56 +0530
committerBobby <[email protected]>2025-07-19 17:06:56 +0530
commit3f73b3c66de04a55bc101ffb96070ae19e7bf27a (patch)
tree85ca777a49e3ec533b2fbc3c709ceb6e093c89c0 /controllers
parentd31111cf0133b223a8e665e6798b8ae09aa5c8a9 (diff)
downloadimageboard-3f73b3c66de04a55bc101ffb96070ae19e7bf27a.tar.xz
imageboard-3f73b3c66de04a55bc101ffb96070ae19e7bf27a.zip
tag adding feature for images
Diffstat (limited to 'controllers')
-rw-r--r--controllers/posts.go21
-rw-r--r--controllers/tags.go279
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,
+ })
+}