summaryrefslogtreecommitdiff
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
-rw-r--r--internal/router/api-v1-blog-search.go1
-rw-r--r--internal/router/client-cache.go99
-rw-r--r--internal/router/lang-blog.go2
-rw-r--r--main.go11
-rw-r--r--static/favicon.icobin5694 -> 0 bytes
-rw-r--r--static/input.css4
-rw-r--r--views/pages/blog-catalogue.html75
-rw-r--r--views/partials/catalogue-blog-cards.html3
8 files changed, 74 insertions, 121 deletions
diff --git a/internal/router/api-v1-blog-search.go b/internal/router/api-v1-blog-search.go
index e22fabe..a809783 100644
--- a/internal/router/api-v1-blog-search.go
+++ b/internal/router/api-v1-blog-search.go
@@ -69,7 +69,6 @@ func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []string, b2Clie
"ShortDescription": page.Metadata.ShortDescription,
"Thumbnail": page.Metadata.Thumbnail,
"Tags": page.Metadata.Tags,
- "LikeCount": CCache.GetLikeCount(page.FileName),
})
break
}
diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go
index 81d7641..56cd595 100644
--- a/internal/router/client-cache.go
+++ b/internal/router/client-cache.go
@@ -18,14 +18,11 @@ type PageLike struct {
type ClientCache struct {
hashMap map[string]string
- hashMapMutex sync.RWMutex
-
- likePageMap map[string]map[string]struct{}
- pageMutexMap map[string]*sync.RWMutex
- pageMutexMapMutex sync.Mutex
-
- salt []byte
- db *sql.DB
+ mutexLikeMap map[string]*sync.Mutex
+ mutexHashMap map[string]*sync.Mutex
+ likePageMap map[string]map[string]struct{}
+ salt []byte
+ db *sql.DB
}
var CCache *ClientCache
@@ -43,21 +40,20 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) {
}
likePageMap := make(map[string]map[string]struct{})
- pageMutexMap := make(map[string]*sync.RWMutex)
for rows.Next() {
- var pageRef string
- var userId []byte
+ pageRef := make([]byte, 32)
+ userId := make([]byte, 32)
if err = rows.Scan(&pageRef, &userId); err != nil {
tx.Rollback()
return nil, fmt.Errorf("fail scanning blog_likes to fill cache: %w", err)
}
+ pageRefString := string(pageRef)
userIdString := base64.RawStdEncoding.EncodeToString(userId)
- if _, ok := likePageMap[pageRef]; !ok {
- likePageMap[pageRef] = make(map[string]struct{})
- pageMutexMap[pageRef] = &sync.RWMutex{}
+ if _, ok := likePageMap[pageRefString]; !ok {
+ likePageMap[pageRefString] = make(map[string]struct{})
}
- likePageMap[pageRef][userIdString] = struct{}{}
+ likePageMap[pageRefString][userIdString] = struct{}{}
}
if err = tx.Commit(); err != nil {
@@ -66,8 +62,9 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) {
return &ClientCache{
hashMap: make(map[string]string),
+ mutexLikeMap: make(map[string]*sync.Mutex),
+ mutexHashMap: make(map[string]*sync.Mutex),
likePageMap: likePageMap,
- pageMutexMap: pageMutexMap,
salt: salt,
db: db,
}, nil
@@ -79,20 +76,17 @@ func (c *ClientCache) Close() error {
return fmt.Errorf("fail to init transaction with db to dump cache: %w", err)
}
- if _, err = tx.Exec("delete from blog_likes;"); err != nil {
- tx.Rollback()
- return fmt.Errorf("fail to truncate table blog_likes: %w", err)
- }
-
userIdBytes := make(map[string][]byte)
sqlStatement := fmt.Sprintf(`
INSERT OR IGNORE INTO blog_likes (page_ref, user_id)
VALUES %s(?, ?);
`, strings.Repeat("(?, ?), ", 99))
- sqlStatementVars := make([]any, 0, 200)
+ sqlStatementVars := make([]interface{}, 0, 200)
for pageRef, userSet := range c.likePageMap {
+ pageRefBytes := []byte(pageRef)
+
for userId := range userSet {
if _, ok := userIdBytes[userId]; !ok {
userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId)
@@ -102,7 +96,7 @@ func (c *ClientCache) Close() error {
}
}
- sqlStatementVars = append(sqlStatementVars, any(pageRef), any(userIdBytes[userId]))
+ sqlStatementVars = append(sqlStatementVars, interface{}(pageRefBytes), interface{}(userIdBytes[userId]))
if len(sqlStatementVars) < 200 {
continue
}
@@ -111,7 +105,7 @@ func (c *ClientCache) Close() error {
slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error()))
}
- sqlStatementVars = make([]any, 0, 200)
+ sqlStatementVars = make([]interface{}, 0, 200)
}
}
@@ -130,16 +124,16 @@ func (c *ClientCache) Close() error {
}
func (c *ClientCache) GetHash(id string) string {
- c.hashMapMutex.RLock()
if val, ok := c.hashMap[id]; ok {
- c.hashMapMutex.RUnlock()
slog.Debug("gave an old hash", slog.String("hash", val))
return val
}
- c.hashMapMutex.RUnlock()
- c.hashMapMutex.Lock()
- defer c.hashMapMutex.Unlock()
+ if _, ok := c.mutexHashMap[id]; !ok {
+ c.mutexHashMap[id] = &sync.Mutex{}
+ }
+ c.mutexHashMap[id].Lock()
+ defer c.mutexHashMap[id].Unlock()
if val, ok := c.hashMap[id]; ok {
slog.Debug("gave a newly generated hash", slog.String("hash", val))
@@ -151,25 +145,7 @@ func (c *ClientCache) GetHash(id string) string {
return c.hashMap[id]
}
-func (c *ClientCache) getPageMutex(page string) *sync.RWMutex {
- c.pageMutexMapMutex.Lock()
- defer c.pageMutexMapMutex.Unlock()
-
- if mutex, ok := c.pageMutexMap[page]; ok {
- return mutex
- }
-
- c.pageMutexMap[page] = &sync.RWMutex{}
- return c.pageMutexMap[page]
-}
-
func (c *ClientCache) GetLikeStatus(id string, page string) bool {
- page = strings.Clone(page)
-
- mutex := c.getPageMutex(page)
- mutex.RLock()
- defer mutex.RUnlock()
-
if _, ok := c.likePageMap[page]; !ok {
return false
}
@@ -178,12 +154,6 @@ func (c *ClientCache) GetLikeStatus(id string, page string) bool {
}
func (c *ClientCache) GetLikeCount(page string) int {
- page = strings.Clone(page)
-
- mutex := c.getPageMutex(page)
- mutex.RLock()
- defer mutex.RUnlock()
-
if userSet, ok := c.likePageMap[page]; ok {
return len(userSet)
}
@@ -191,12 +161,13 @@ func (c *ClientCache) GetLikeCount(page string) int {
}
func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) {
- page = strings.Clone(page)
- hash := c.GetHash(id)
+ if _, ok := c.mutexLikeMap[id]; !ok {
+ c.mutexLikeMap[id] = &sync.Mutex{}
+ }
+ c.mutexLikeMap[id].Lock()
+ defer c.mutexLikeMap[id].Unlock()
- mutex := c.getPageMutex(page)
- mutex.Lock()
- defer mutex.Unlock()
+ hash := c.GetHash(id)
if userSet, ok := c.likePageMap[page]; ok {
_, alreadyLiked = userSet[hash]
@@ -210,16 +181,16 @@ func (c *ClientCache) LikeOn(id string, page string) (alreadyLiked bool) {
}
func (c *ClientCache) LikeOff(id string, page string) (alreadyUnliked bool) {
- page = strings.Clone(page)
- hash := c.GetHash(id)
-
- mutex := c.getPageMutex(page)
- mutex.Lock()
- defer mutex.Unlock()
+ 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
}
diff --git a/internal/router/lang-blog.go b/internal/router/lang-blog.go
index dd3f079..e5220c6 100644
--- a/internal/router/lang-blog.go
+++ b/internal/router/lang-blog.go
@@ -83,7 +83,7 @@ func Lang_Blog(l map[string]*locale.LocaleConfig, langs []string, b2Client *b2.B
"Title": l[lang].BlogSearch.Header,
})
if err != nil {
- slog.Warn("failed to generate page", slog.String("path", c.Path()), slog.String("error", err.Error()))
+ slog.Warn("failed to generate page", slog.String("page", "/"+lang+"/blog"), slog.String("error", err.Error()))
c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page")
}
diff --git a/main.go b/main.go
index 020f8ef..2f08ba6 100644
--- a/main.go
+++ b/main.go
@@ -143,14 +143,7 @@ func main() {
<-sigChan
slog.Info("gracefully shutting down...")
- if err = app.Shutdown(); err != nil {
- slog.Error("fail to shutdown fiber server", slog.String("error", err.Error()))
- }
- if err = router.CCache.Close(); err != nil {
- slog.Error("fail to dump cache", slog.String("error", err.Error()))
- }
- if err = db.Close(); err != nil {
- slog.Error("fail to close db connection", slog.String("error", err.Error()))
- }
+ app.Shutdown()
+ router.CCache.Close()
db.Close()
}
diff --git a/static/favicon.ico b/static/favicon.ico
deleted file mode 100644
index 33eb226..0000000
--- a/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/static/input.css b/static/input.css
index 47e7d16..f254970 100644
--- a/static/input.css
+++ b/static/input.css
@@ -581,10 +581,6 @@ body[data-theme~="ram"] .leaflet-tile {
width: 100vw;
}
- .ar-lt-0\.8\:max-h-\[25\%\] {
- max-height: 25%;
- }
-
.ar-lt-0\.8\:max-h-\[80\%\] {
max-height: 80%;
}
diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html
index 1d0a2de..25b0685 100644
--- a/views/pages/blog-catalogue.html
+++ b/views/pages/blog-catalogue.html
@@ -1,47 +1,44 @@
{{ define "body" }}
<div class="flex flex-row ar-lt-0.8:flex-col max-h-[calc(100%-7vmax)] ar-lt-0.8:max-h-[calc(100%-25vh)] flex-1 text-[1vmax]">
<form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML"
- class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] ar-lt-0.8:max-h-[25%] flex shrink-0 flex-col ar-gt-0.8:mr-[1.2vmin]">
- <div class="flex flex-col overflow-y-auto">
- <fieldset>
- <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.TagsHeader }}</legend>
- <div class="flex flex-col ar-lt-0.8:grid grid-cols-5 grid-flow-row-dense">
- <div class="m-[0.4vmin]">
- <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();"> {{ .L.BlogSearch.ChooseAllTags }}</label>
- </div>
- {{- range .Tags }}
- <div class="m-[0.4vmin]">
- <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label>
- </div>
- {{- end }}
+ class="tags-list ar-gt-0.8:min-w-[10vh] ar-gt-0.8:max-w-[20vh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] flex shrink-0 flex-col overflow-y-auto ar-gt-0.8:mr-[1.2vmin]">
+ <fieldset>
+ <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.TagsHeader }}</legend>
+ <div class="flex flex-col ar-lt-0.8:grid grid-cols-5 grid-flow-row-dense">
+ <div class="m-[0.4vmin]">
+ <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();"> {{ .L.BlogSearch.ChooseAllTags }}</label>
</div>
- </fieldset>
- <fieldset class="mt-[0.8vmin]">
- <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.OrderByHeader }}</legend>
- <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col">
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-a-z"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-z-a"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
- </div>
- <div class="m-[0.4vmin]">
- <label><input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
- </div>
+ {{- range .Tags }}
+ <div class="m-[0.4vmin]">
+ <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label>
</div>
- </fieldset>
- </div>
- <hr class="border-t-[0.2vmax] border-dotted border-main-dark my-[0.4vmin]">
- <div class="flex flex-row grow bg-background-light my-[2vmin] p-[0.4vmin] rounded-[0.4vmin] justify-items-center">
+ {{- end }}
+ </div>
+ </fieldset>
+ <fieldset class="mt-[0.8vmin]">
+ <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.OrderByHeader }}</legend>
+ <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col">
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-a-z"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-z-a"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label>
+ </div>
+ <div class="m-[0.4vmin]">
+ <label><input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label>
+ </div>
+ </div>
+ </fieldset>
+ <div class="flex flex-row bg-background-light my-[2vmin] p-[0.4vmin] rounded-[0.4vmin] justify-items-center">
<i onclick="defaultSearchParams(); cleanHrefParams();" class="fas fa-rotate-left flex-1 text-center text-[1.5vmax] hover:bg-background-dark cursor-pointer transition-colors duration-300"></i>
<div class="w-[0.5vmin] bg-background-dark grow-0"></div>
<button class="flex-1" type="submit">
diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html
index 35c7ad7..62e931a 100644
--- a/views/partials/catalogue-blog-cards.html
+++ b/views/partials/catalogue-blog-cards.html
@@ -8,9 +8,6 @@
<span class="font-extrabold">{{ .Title }}</span>
<span class="font-extrabold select-none text-secondary">//</span>
<span class="italic text-secondary">{{ .ActionDate }}</span>
- <span class="font-extrabold select-none text-secondary">//</span>
- <i class="fas fa-thumbs-up w-[1vmax] h-[1vmax]"></i>
- <span class="italic text-secondary">{{ .LikeCount }}</span>
</a>
<p class="flex-3/12 grow-0 text-[1vmax] italic text-secondary text-right">{{ .PublishedTime }}</p>
</div>