diff options
author | RandomChars <random@chars.jp> | 2021-08-16 20:08:52 +0900 |
---|---|---|
committer | RandomChars <random@chars.jp> | 2021-08-16 20:08:52 +0900 |
commit | c74891017a33d12f1fc5b415869ba466c40fdb86 (patch) | |
tree | 58ae884cdc293a15dbd7a19f7670f07394262516 | |
parent | 84f07d558e155ae1d331838094f445eefbb51434 (diff) |
everything in API updated to use snowflake instead of hash, implement multi-tag searchv0.7.4
-rw-r--r-- | api.go | 58 | ||||
-rw-r--r-- | store/image.go | 75 |
2 files changed, 109 insertions, 24 deletions
@@ -7,6 +7,7 @@ import ( "net/http" "random.chars.jp/git/image-board/store" "strconv" + "strings" ) type UserPayload struct { @@ -271,6 +272,12 @@ func registerAPI() { context.JSON(http.StatusOK, instance.UserImages(context.Param("flake"))) }) + router.GET("/api/search/:tags", func(context *gin.Context) { + tagsPayload := context.Param("tags") + tags := strings.Split(tagsPayload, "!") + context.JSON(http.StatusOK, instance.ImageSearch(tags)) + }) + router.GET("/api/image/snowflake", func(context *gin.Context) { info, ok := getUser(context) @@ -378,11 +385,11 @@ func registerAPI() { context.JSON(http.StatusOK, image) }) - router.GET("/api/image/:hash", func(context *gin.Context) { - context.JSON(http.StatusOK, instance.Image(context.Param("hash"))) + router.GET("/api/image/:flake", func(context *gin.Context) { + context.JSON(http.StatusOK, instance.ImageSnowflake(context.Param("flake"))) }) - router.PATCH("/api/image/:hash", func(context *gin.Context) { + router.PATCH("/api/image/:flake", func(context *gin.Context) { user, ok := getUser(context) // Require sign in @@ -391,7 +398,7 @@ func registerAPI() { return } - info := instance.Image(context.Param("hash")) + info := instance.ImageSnowflake(context.Param("flake")) if !user.Privileged && (info.User != user.Snowflake) { context.JSON(http.StatusForbidden, denied) return @@ -408,7 +415,7 @@ func registerAPI() { instance.ImageSource(info.Hash, payload.Source) }) - router.DELETE("/api/image/:hash", func(context *gin.Context) { + router.DELETE("/api/image/:flake", func(context *gin.Context) { info, ok := getUser(context) // Require sign in @@ -417,17 +424,19 @@ func registerAPI() { return } - hash := context.Param("hash") - if !info.Privileged && (info.Snowflake != instance.Image(hash).User) { + flake := context.Param("flake") + image := instance.ImageSnowflake(flake) + if !info.Privileged && (info.Snowflake != image.User) { context.JSON(http.StatusForbidden, denied) return } - instance.ImageDestroy(hash) + instance.ImageDestroy(image.Hash) }) - router.GET("/api/image/:hash/file", func(context *gin.Context) { - image, data := instance.ImageData(context.Param("hash"), false) - if image.Hash == "" { + router.GET("/api/image/:flake/file", func(context *gin.Context) { + flake := context.Param("flake") + image, data := instance.ImageData(instance.ImageSnowflakeHash(flake), false) + if image.Snowflake == flake { context.JSON(http.StatusNotFound, gin.H{ "error": "not found", }) @@ -436,9 +445,10 @@ func registerAPI() { context.Data(http.StatusOK, "image/"+image.Type, data) }) - router.GET("/api/image/:hash/preview", func(context *gin.Context) { - image, data := instance.ImageData(context.Param("hash"), true) - if image.Hash == "" { + router.GET("/api/image/:flake/preview", func(context *gin.Context) { + flake := context.Param("flake") + image, data := instance.ImageData(instance.ImageSnowflakeHash(flake), true) + if image.Snowflake == flake { context.JSON(http.StatusNotFound, gin.H{ "error": "not found", }) @@ -447,11 +457,11 @@ func registerAPI() { context.Data(http.StatusOK, "image/jpeg", data) }) - router.GET("/api/image/:hash/tag", func(context *gin.Context) { - context.JSON(http.StatusOK, instance.ImageTags(context.Param("hash"))) + router.GET("/api/image/:flake/tag", func(context *gin.Context) { + context.JSON(http.StatusOK, instance.ImageTags(context.Param("flake"))) }) - router.PUT("/api/image/:hash/tag/:tag", func(context *gin.Context) { + router.PUT("/api/image/:flake/tag/:tag", func(context *gin.Context) { info, ok := getUser(context) // Require sign in @@ -460,15 +470,15 @@ func registerAPI() { return } - hash := context.Param("hash") - if !info.Privileged && (info.Snowflake != instance.Image(hash).User) { + flake := context.Param("flake") + if !info.Privileged && (info.Snowflake != instance.ImageSnowflake(flake).User) { context.JSON(http.StatusForbidden, denied) return } - instance.ImageTagAdd(hash, context.Param("tag")) + instance.ImageTagAdd(flake, context.Param("tag")) }) - router.DELETE("/api/image/:hash/tag/:tag", func(context *gin.Context) { + router.DELETE("/api/image/:flake/tag/:tag", func(context *gin.Context) { info, ok := getUser(context) // Require sign in @@ -477,12 +487,12 @@ func registerAPI() { return } - hash := context.Param("hash") - if !info.Privileged && (info.Snowflake != instance.Image(hash).User) { + flake := context.Param("flake") + if !info.Privileged && (info.Snowflake != instance.ImageSnowflake(flake).User) { context.JSON(http.StatusForbidden, denied) return } - instance.ImageTagRemove(hash, context.Param("tag")) + instance.ImageTagRemove(flake, context.Param("tag")) }) router.GET("/api/tag", func(context *gin.Context) { diff --git a/store/image.go b/store/image.go index 5e57acf..323fa45 100644 --- a/store/image.go +++ b/store/image.go @@ -129,6 +129,81 @@ func (s *Store) imageTags(flake string) []string { return tags } +// ImageHasTag figures out if an image has a tag. +func (s *Store) ImageHasTag(flake string, tag string) bool { + return s.file(s.ImageTagsPath(flake) + "/" + tag) +} + +//ImageSearch searches for images with specific tags. +func (s *Store) ImageSearch(tags []string) []string { + if len(tags) < 1 || tags == nil { + return nil + } + + // Check if every tag matches name regex and exists + for _, tag := range tags { + if !nameRegex.MatchString(tag) || !s.file(s.TagPath(tag)) { + return nil + } + } + + // Return if there's only one tag to search for + if len(tags) == 1 { + return s.Tag(tags[0]) + } + + // Find entry with the least pages + entry := struct { + min int + index int + }{ + min: s.PageTotal("tag_" + tags[0]), + index: 0, + } + for i := 1; i < len(tags); i++ { + if entry.min <= 1 { + break + } + + pages := s.PageTotal("tag_" + tags[i]) + if pages < entry.min { + entry.min = pages + entry.index = i + } + } + + // Get initial tag + initial := s.Tag(tags[entry.index]) + + // Result slice + var result []string + + // Walk flakes from initial tag + for _, flake := range initial { + match := true + // Walk all remaining tags + for i, tag := range tags { + // Skip the entrypoint entry + if i == entry.index { + continue + } + + // Check if match + if !s.ImageHasTag(flake, tag) { + match = false + break + } + } + + // Append flake if all tags matched + if match { + result = append(result, flake) + } + } + + return result +} + // ImageAdd adds an image to the store. func (s *Store) ImageAdd(data []byte, flake string) Image { if !s.flake(flake) || !s.dir(s.UserPath(flake)) { |