| -rw-r--r-- | internal/router/api-v1-blog-search.go | 1 | ||||
| -rw-r--r-- | internal/router/api-v1-general-page-body.go | 2 | ||||
| -rw-r--r-- | internal/router/client-cache.go | 164 | ||||
| -rw-r--r-- | locale/localization.en.yaml | 1 | ||||
| -rw-r--r-- | locale/localization.go | 1 | ||||
| -rw-r--r-- | locale/localization.ru.yaml | 1 | ||||
| -rw-r--r-- | views/pages/home-page.html | 22 | ||||
| -rw-r--r-- | views/partials/catalogue-blog-cards.html | 11 | ||||
| -rw-r--r-- | views/partials/general-page-header.html | 6 |
9 files changed, 49 insertions, 160 deletions
diff --git a/internal/router/api-v1-blog-search.go b/internal/router/api-v1-blog-search.go index 69be651..946b830 100644 --- a/internal/router/api-v1-blog-search.go +++ b/internal/router/api-v1-blog-search.go @@ -79,7 +79,6 @@ func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []string, b2Clie "Thumbnail": page.Metadata.Thumbnail, "Tags": page.Metadata.Tags, "LikeCount": CCache.GetLikeCount(page.FileName), - "ViewCount": CCache.GetViewCount(page.FileName), }) break } diff --git a/internal/router/api-v1-general-page-body.go b/internal/router/api-v1-general-page-body.go index b2fa268..f27e0c0 100644 --- a/internal/router/api-v1-general-page-body.go +++ b/internal/router/api-v1-general-page-body.go @@ -138,8 +138,6 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []string, values["ParsedMarkdown"] = template.HTML(parsedMarkdown) additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html") - - go CCache.View(c.IP(), pathParts[2]) } else if len(pathParts) == 1 { values["Title"] = l[lang].HomePage.Header values["FilledHeartCount"] = uint(40) diff --git a/internal/router/client-cache.go b/internal/router/client-cache.go index d607cdd..e448fb3 100644 --- a/internal/router/client-cache.go +++ b/internal/router/client-cache.go @@ -16,7 +16,6 @@ type ClientCache struct { hashMapMutex sync.RWMutex likePageMap map[string]map[string]struct{} - viewPageMap map[string]map[string]struct{} pageMutexMap map[string]*sync.RWMutex pageMutexMapMutex sync.Mutex @@ -39,7 +38,6 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { } likePageMap := make(map[string]map[string]struct{}) - viewPageMap := make(map[string]map[string]struct{}) pageMutexMap := make(map[string]*sync.RWMutex) for rows.Next() { @@ -52,32 +50,9 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { userIdString := base64.RawStdEncoding.EncodeToString(userId) if _, ok := likePageMap[pageRef]; !ok { likePageMap[pageRef] = make(map[string]struct{}) - viewPageMap[pageRef] = make(map[string]struct{}) pageMutexMap[pageRef] = &sync.RWMutex{} } likePageMap[pageRef][userIdString] = struct{}{} - viewPageMap[pageRef][userIdString] = struct{}{} - } - - rows, err = tx.Query("select * from blog_views;") - if err != nil { - tx.Rollback() - return nil, fmt.Errorf("fail to query db for blog_views to fill cache: %w", err) - } - - for rows.Next() { - var pageRef string - var userId []byte - if err = rows.Scan(&pageRef, &userId); err != nil { - tx.Rollback() - return nil, fmt.Errorf("fail scanning blog_views to fill cache: %w", err) - } - userIdString := base64.RawStdEncoding.EncodeToString(userId) - if _, ok := viewPageMap[pageRef]; !ok { - viewPageMap[pageRef] = make(map[string]struct{}) - pageMutexMap[pageRef] = &sync.RWMutex{} - } - viewPageMap[pageRef][userIdString] = struct{}{} } if err = tx.Commit(); err != nil { @@ -87,7 +62,6 @@ func NewClientCache(db *sql.DB, salt []byte) (*ClientCache, error) { return &ClientCache{ hashMap: make(map[string]string), likePageMap: likePageMap, - viewPageMap: viewPageMap, pageMutexMap: pageMutexMap, salt: salt, db: db, @@ -100,14 +74,51 @@ func (c *ClientCache) Close() error { return fmt.Errorf("fail to init transaction with db to dump cache: %w", err) } - if err = batchSave(tx, "blog_likes", c.likePageMap); err != nil { + if _, err = tx.Exec("delete from blog_likes;"); err != nil { tx.Rollback() - return fmt.Errorf("fail to save blog_likes: %s", err) + return fmt.Errorf("fail to truncate table blog_likes: %w", err) } - if err = batchSave(tx, "blog_views", c.viewPageMap); err != nil { - tx.Rollback() - return fmt.Errorf("fail to save blog_views: %s", 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) + + for pageRef, userSet := range c.likePageMap { + for userId := range userSet { + if _, ok := userIdBytes[userId]; !ok { + userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId) + if err != nil { + slog.Warn("couldn't parse one of user hashes into bytes back", slog.String("hash", userId), slog.String("error", err.Error())) + continue + } + } + + sqlStatementVars = append(sqlStatementVars, any(pageRef), any(userIdBytes[userId])) + if len(sqlStatementVars) < 200 { + continue + } + + if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { + slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error())) + } + + sqlStatementVars = make([]any, 0, 200) + } + } + + if len(sqlStatementVars) > 0 { + sqlStatement = fmt.Sprintf(` + INSERT OR IGNORE INTO blog_likes (page_ref, user_id) + VALUES %s(?, ?); + `, strings.Repeat("(?, ?), ", len(sqlStatementVars)/2-1)) + + if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { + slog.Warn("couldn't insert blog like pairs into db", slog.String("error", err.Error())) + } } return tx.Commit() @@ -211,94 +222,3 @@ func (c *ClientCache) LikeOff(id string, page string) (alreadyUnliked bool) { delete(c.likePageMap[page], hash) return false } - -func (c *ClientCache) GetViewStatus(id string, page string) bool { - page = strings.Clone(page) - - mutex := c.getPageMutex(page) - mutex.RLock() - defer mutex.RUnlock() - - if _, ok := c.viewPageMap[page]; !ok { - return false - } - _, ok := c.viewPageMap[page][c.GetHash(id)] - return ok -} - -func (c *ClientCache) GetViewCount(page string) int { - page = strings.Clone(page) - - mutex := c.getPageMutex(page) - mutex.RLock() - defer mutex.RUnlock() - - if userSet, ok := c.viewPageMap[page]; ok { - return len(userSet) - } - return 0 -} - -func (c *ClientCache) View(id string, page string) { - page = strings.Clone(page) - hash := c.GetHash(id) - - mutex := c.getPageMutex(page) - mutex.Lock() - defer mutex.Unlock() - - if _, ok := c.viewPageMap[page]; !ok { - c.viewPageMap[page] = make(map[string]struct{}) - } - c.viewPageMap[page][hash] = struct{}{} -} - -func batchSave(tx *sql.Tx, table string, pageMap map[string]map[string]struct{}) (err error) { - if _, err = tx.Exec(fmt.Sprintf("delete from %s;", table)); err != nil { - return fmt.Errorf("fail to truncate table %s: %w", table, err) - } - - userIdBytes := make(map[string][]byte) - - sqlStatement := fmt.Sprintf(` - INSERT OR IGNORE INTO %s (page_ref, user_id) - VALUES %s(?, ?); - `, table, strings.Repeat("(?, ?), ", 99)) - sqlStatementVars := make([]any, 0, 200) - - for pageRef, userSet := range pageMap { - for userId := range userSet { - if _, ok := userIdBytes[userId]; !ok { - userIdBytes[userId], err = base64.RawStdEncoding.DecodeString(userId) - if err != nil { - slog.Warn("couldn't parse one of user hashes into bytes back", slog.String("hash", userId), slog.String("error", err.Error())) - continue - } - } - - sqlStatementVars = append(sqlStatementVars, any(pageRef), any(userIdBytes[userId])) - if len(sqlStatementVars) < 200 { - continue - } - - if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { - slog.Warn("couldn't insert blog stat pairs into db", slog.String("table", table), slog.String("error", err.Error())) - } - - sqlStatementVars = make([]any, 0, 200) - } - } - - if len(sqlStatementVars) > 0 { - sqlStatement = fmt.Sprintf(` - INSERT OR IGNORE INTO %s (page_ref, user_id) - VALUES %s(?, ?); - `, table, strings.Repeat("(?, ?), ", len(sqlStatementVars)/2-1)) - - if _, err := tx.Exec(sqlStatement, sqlStatementVars...); err != nil { - slog.Warn("couldn't insert blog stat pairs into db", slog.String("table", table), slog.String("error", err.Error())) - } - } - - return nil -} diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml index 1b3654b..b3fca58 100644 --- a/locale/localization.en.yaml +++ b/locale/localization.en.yaml @@ -11,7 +11,6 @@ GlobalMap: Header: 'Global Map' HomePage: Header: 'Home Page' - Contacts: 'Contacts' DidYouKnowThat: 'Did you know, that...' SidebarDescription: 'Describing the Sidebar' HomePageDescription: 'Home Page -- where we are right now!' diff --git a/locale/localization.go b/locale/localization.go index 088783f..5917932 100644 --- a/locale/localization.go +++ b/locale/localization.go @@ -30,7 +30,6 @@ type GlobalMapConfig struct { type HomePageConfig struct { Header string `yaml:"Header" json:"Header"` - Contacts string `yaml:"Contacts" json:"Contacts"` DidYouKnowThat string `yaml:"DidYouKnowThat" json:"DidYouKnowThat"` SidebarDescription string `yaml:"SidebarDescription" json:"SidebarDescription"` HomePageDescription string `yaml:"HomePageDescription" json:"HomePageDescription"` diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml index 9d7084f..5e8d39f 100644 --- a/locale/localization.ru.yaml +++ b/locale/localization.ru.yaml @@ -11,7 +11,6 @@ GlobalMap: Header: 'Глобальная карта' HomePage: Header: 'Домашняя страница' - Contacts: 'Контакты' DidYouKnowThat: 'А вы знали, что...' SidebarDescription: 'Поясняем за сайдбар' HomePageDescription: 'Домашняя страница -- где мы сейчас и находимся!' diff --git a/views/pages/home-page.html b/views/pages/home-page.html index 6fd716b..9775ac3 100644 --- a/views/pages/home-page.html +++ b/views/pages/home-page.html @@ -10,7 +10,6 @@ <p class="text-[2vmax]/[2] -indent-[2vmax] ml-[2vmax] p-[0.4vmax]">{{ .L.HomePage.Hymn3 }}</p> <p class="text-[2vmax]/[2] -indent-[2vmax] ml-[2vmax] p-[0.4vmax]">{{ .L.HomePage.Hymn4 }}</p> </div> - <div class="flex flex-col justify-center items-center"> <p class="text-[2vmax] w-full font-spectral italic text-left border-l-[0.4vmax] border-solid border-(--background-dark-color) @@ -25,27 +24,6 @@ </div> <div class="ar-gt-0.6:flex-2/5 justify-center content-start p-[5%]"> - <div class="flex flex-col justify-center items-start mb-[2vmax]"> - <p class="text-[2vmax] w-full font-spectral italic text-left - border-l-[0.4vmax] border-solid border-(--background-dark-color) - bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.Contacts }}</p> - <a href="https://t.me/EarlInisMona"> - <i class="fa-brands fa-telegram w-[1vmax] h-[1vmax] mr-[0.4vmax]"></i> - <span class="font-bold">Telegram:</span> - <span>@EarlInisMona</span> - </a> - <a href="https://discord.com/users/356057270951346176"> - <i class="fa-brands fa-discord w-[1vmax] h-[1vmax] mr-[0.4vmax]"></i> - <span class="font-bold">Discord:</span> - <span>montferrat</span> - </a> - <a href="mailto:saya.andy@posteo.com"> - <i class="fas fa-envelope w-[1vmax] h-[1vmax] mr-[0.4vmax]"></i> - <span class="font-bold">E-mail:</span> - <span>saya.andy@posteo.com</span> - </a> - </div> - <div class="flex relative aspect-square justify-center content-center mb-[2vmax]"> {{- range $_ := iterate .FilledHeartCount }} <div class="home-page-heart z-50">♥</div> diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html index 37b3f57..06d99d5 100644 --- a/views/partials/catalogue-blog-cards.html +++ b/views/partials/catalogue-blog-cards.html @@ -8,17 +8,14 @@ <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> + <div hx-trigger="click from:#user_search_bar" hx-get="/endpoint1" hx-target="#users-list"></div> <p class="flex-3/12 grow-0 text-[1vmax] italic text-secondary text-right">{{ .PublishedTime }}</p> </div> <p class="text-[1vmax] text-main-dark">{{ .ShortDescription }}</p> - <p class="text-[1vmax] text-secondary italic"> - <i class="fas fa-thumbs-up w-[1vmax] h-[1vmax]"></i> - <span>{{ .LikeCount }}</span> - <span class="font-extrabold select-none">//</span> - <i class="fas fa-eye w-[1vmax] h-[1vmax]"></i> - <span>{{ .ViewCount }}</span> - </p> <p class="text-[1vmax] text-secondary">{{ $.L.TagsLabel }} {{ template "catalogue-blog-card-tags.html" . }}</p> </div> </div> diff --git a/views/partials/general-page-header.html b/views/partials/general-page-header.html index 6cb87b7..63803bd 100644 --- a/views/partials/general-page-header.html +++ b/views/partials/general-page-header.html @@ -1,8 +1,8 @@ <div {{ if .PublishedDate }} hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="#published-date" hx-swap="innerHTML" hx-trigger="load" {{ end }} class="flex flex-row mb-[0.4vmin]"> <i onclick="changeUrl('/../', true);" class="fas fa-circle-left hover:bg-main-dark hover:bg-opacity-10 cursor-pointer transition-colors duration-300 rounded-md text-[2vmax] mt-auto mb-auto px-[0.8vmin]"></i> - <p class="text-[2.5vmin] font-spectral italic font-extrabold select-none mt-auto pl-[0.8vmin] pr-[1.2vmin]">//</p> - <p class="text-[2.5vmin] font-spectral italic text-left mt-auto">{{ .Title }}</p> - <p id="published-date" class="grow text-[1.75vmin] font-spectral text-secondary text-right mt-auto">{{ .PublishedDate }}</p> + <p class="text-[2vmax] font-spectral italic font-extrabold select-none mt-auto pl-[0.8vmin] pr-[1.2vmin]">//</p> + <p class="text-[2vmax] font-spectral italic text-left mt-auto">{{ .Title }}</p> + <p id="published-date" class="grow text-[1.5vmax] font-spectral text-secondary text-right mt-auto">{{ .PublishedDate }}</p> </div> {{ block "header" . }}{{ end }} |