summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandomChars <random@chars.jp>2021-08-16 20:08:52 +0900
committerRandomChars <random@chars.jp>2021-08-16 20:08:52 +0900
commitc74891017a33d12f1fc5b415869ba466c40fdb86 (patch)
tree58ae884cdc293a15dbd7a19f7670f07394262516
parent84f07d558e155ae1d331838094f445eefbb51434 (diff)
everything in API updated to use snowflake instead of hash, implement multi-tag searchv0.7.4
-rw-r--r--api.go58
-rw-r--r--store/image.go75
2 files changed, 109 insertions, 24 deletions
diff --git a/api.go b/api.go
index e4bfea2..1a034f4 100644
--- a/api.go
+++ b/api.go
@@ -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)) {