Diffstat (limited to 'internal')
| -rw-r--r-- | internal/router/api-v1-like.go | 117 | ||||
| -rw-r--r-- | internal/router/client-cache.go | 82 | ||||
| -rw-r--r-- | internal/router/lang-blog-title.go | 1 |
3 files changed, 17 insertions, 183 deletions
diff --git a/internal/router/api-v1-like.go b/internal/router/api-v1-like.go deleted file mode 100644 index 1732c77..0000000 --- a/internal/router/api-v1-like.go +++ /dev/null @@ -1,117 +0,0 @@ -package router - -import ( - "fmt" - "log/slog" - "net/url" - "strconv" - "strings" - - "github.com/SayaAndy/saya-today-web/internal/b2" - "github.com/gofiber/fiber/v2" -) - -func init() { - tm.Add("blog-page-like-button", "views/partials/blog-page-like-button.html") -} - -func Api_V1_Like_Put(b2 *b2.B2Client) func(c *fiber.Ctx) error { - return func(c *fiber.Ctx) error { - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - - referer := c.Get("Referer", "") - if referer == "" { - return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is empty") - } - urlStruct, err := url.ParseRequestURI(referer) - if err != nil { - return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("'Referer' header is invalid: %s", err.Error())) - } - - path := urlStruct.EscapedPath() - pathParts := strings.Split(strings.Trim(path, "/"), "/") - if len(pathParts) != 3 { - return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/blog/{page}'") - } - - lang, page := pathParts[0], pathParts[2] - - pageLink := lang + "/" + page + ".md" - if pages, _ := b2.Scan(pageLink); len(pages) == 0 { - return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server did not find '%s' article", pageLink)) - } - - ip := c.IP() - newLikeStatus, err := strconv.ParseBool(c.FormValue("like", "true")) - if err != nil { - return c.Status(fiber.ErrBadRequest.Code).SendString("invalid 'like' value") - } - - if newLikeStatus { - CCache.LikeOn(ip, page) - } else { - CCache.LikeOff(ip, page) - } - - slog.Debug("someone pressed the like button!", slog.String("ip", ip), slog.String("page", page), slog.String("new_like_status", fmt.Sprint(newLikeStatus))) - if c.Get("HX-Request", "false") == "true" { - content, err := tm.Render("blog-page-like-button", fiber.Map{ - "Liked": newLikeStatus, - }) - if err != nil { - slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate div") - } - c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8) - return c.Status(fiber.StatusOK).Send(content) - } - - return c.Status(fiber.StatusOK).SendString(fmt.Sprint(newLikeStatus)) - } -} - -func Api_V1_Like_Get(b2 *b2.B2Client) func(c *fiber.Ctx) error { - return func(c *fiber.Ctx) error { - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - - referer := c.Get("Referer", "") - if referer == "" { - return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is empty") - } - urlStruct, err := url.ParseRequestURI(referer) - if err != nil { - return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("'Referer' header is invalid: %s", err.Error())) - } - - path := urlStruct.EscapedPath() - pathParts := strings.Split(strings.Trim(path, "/"), "/") - if len(pathParts) != 3 { - return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/blog/{page}'") - } - - lang, page := pathParts[0], pathParts[2] - - pageLink := lang + "/" + page + ".md" - if pages, _ := b2.Scan(pageLink); len(pages) == 0 { - return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server did not find '%s' article", pageLink)) - } - - ip := c.IP() - likeStatus := CCache.GetLikeStatus(ip, page) - - slog.Debug("someone requested the like status!", slog.String("ip", ip), slog.String("page", page), slog.Bool("like_status", likeStatus)) - if c.Get("HX-Request", "false") == "true" { - content, err := tm.Render("blog-page-like-button", fiber.Map{ - "Liked": likeStatus, - }) - if err != nil { - slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate div") - } - c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8) - return c.Status(fiber.StatusOK).Send(content) - } - - return c.Status(fiber.StatusOK).SendString(fmt.Sprint(likeStatus)) - } -} diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go index 3104a66..a4a0cc2 100644 --- a/internal/router/client-cache.go +++ b/internal/router/client-cache.go @@ -9,90 +9,40 @@ import ( ) type ClientCache struct { - hashMap map[string]string - mutexLikeMap map[string]*sync.Mutex - mutexHashMap map[string]*sync.Mutex - likePageMap map[string]map[string]struct{} - salt []byte + hashMap map[string][]byte + mutexMap map[string]*sync.Mutex + salt []byte } var CCache *ClientCache func NewClientCache(salt []byte) *ClientCache { return &ClientCache{ - hashMap: make(map[string]string), - mutexLikeMap: make(map[string]*sync.Mutex), - mutexHashMap: make(map[string]*sync.Mutex), - likePageMap: make(map[string]map[string]struct{}), - salt: salt, + hashMap: make(map[string][]byte), + mutexMap: make(map[string]*sync.Mutex), + salt: salt, } } -func (c *ClientCache) GetHash(id string) string { +func (c *ClientCache) GetHash(id string) []byte { + if val, ok := c.hashMap[id]; ok { - slog.Debug("gave an old hash", slog.String("hash", val)) + slog.Debug("gave an old hash", slog.String("hash", base64.RawStdEncoding.EncodeToString(val))) return val } - if _, ok := c.mutexHashMap[id]; !ok { - c.mutexHashMap[id] = &sync.Mutex{} + if _, ok := c.mutexMap[id]; !ok { + c.mutexMap[id] = &sync.Mutex{} } - c.mutexHashMap[id].Lock() - defer c.mutexHashMap[id].Unlock() + c.mutexMap[id].Lock() + defer c.mutexMap[id].Unlock() if val, ok := c.hashMap[id]; ok { - slog.Debug("gave a newly generated hash", slog.String("hash", val)) + slog.Debug("gave a newly generated hash", slog.String("hash", base64.RawStdEncoding.EncodeToString(val))) return val } - c.hashMap[id] = base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(id), c.salt, 1, 64*1024, 4, 32)) - slog.Debug("generated hash", slog.String("hash", c.hashMap[id])) + c.hashMap[id] = argon2.IDKey([]byte(id), c.salt, 1, 64*1024, 4, 32) + slog.Debug("generated hash", slog.String("hash", base64.RawStdEncoding.EncodeToString(c.hashMap[id]))) return c.hashMap[id] } - -func (c *ClientCache) GetLikeStatus(id string, page string) bool { - if _, ok := c.likePageMap[page]; !ok { - return false - } - _, ok := c.likePageMap[page][c.GetHash(id)] - return ok -} - -func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) { - if _, ok := c.mutexLikeMap[id]; !ok { - c.mutexLikeMap[id] = &sync.Mutex{} - } - c.mutexLikeMap[id].Lock() - defer c.mutexLikeMap[id].Unlock() - - hash := c.GetHash(id) - - if userSet, ok := c.likePageMap[page]; ok { - _, alreadyLiked = userSet[hash] - c.likePageMap[page][hash] = struct{}{} - return - } - - c.likePageMap[page] = make(map[string]struct{}) - c.likePageMap[page][hash] = struct{}{} - return -} - -func (c *ClientCache) LikeOff(id string, page string) (alreadyUnliked bool) { - if _, ok := c.mutexLikeMap[id]; !ok { - c.mutexLikeMap[id] = &sync.Mutex{} - } - c.mutexLikeMap[id].Lock() - defer c.mutexLikeMap[id].Unlock() - - if _, ok := c.likePageMap[page]; !ok { - return true - } - hash := c.GetHash(id) - if _, ok := c.likePageMap[page][hash]; !ok { - return true - } - - delete(c.likePageMap[page], hash) - return false -} diff --git a/internal/router/lang-blog-title.go b/internal/router/lang-blog-title.go index bafb0d2..5b80ec8 100644 --- a/internal/router/lang-blog-title.go +++ b/internal/router/lang-blog-title.go @@ -24,6 +24,7 @@ func Lang_Blog_Title(l map[string]*locale.LocaleConfig, langs []string, b2Client return func(c *fiber.Ctx) error { ip := c.IP() slog.Debug("client entering blog page", slog.String("ip", ip), slog.String("page", c.Path())) + go CCache.GetHash(ip) lang := c.Params("lang") if !slices.Contains(langs, lang) { |