summaryrefslogtreecommitdiff
diff options
from:
to:
context:
space:
mode:
authorGravatar SayaAndy <saya.andy@posteo.com> 2026-03-29 02:05:06 +0700
committerGravatar SayaAndy <saya.andy@posteo.com> 2026-03-29 02:05:06 +0700
commit0a9de353b2456505b0b8214fab4a7c844d31735c (patch)
treeae130f4617bab4b05f13521b1e701cd88f48d69f
parent610341bc9d59f9724e8e71f663b28df1904cb038 (diff)
downloadweb-0a9de353b2456505b0b8214fab4a7c844d31735c.tar.gz
web-0a9de353b2456505b0b8214fab4a7c844d31735c.zip
fix: add page metadata correctly
feat: link marker fill saturation and brightness in the global map to the publication date
-rw-r--r--internal/frontmatter/parser.go19
-rw-r--r--internal/router/basic-handler.go6
-rw-r--r--internal/router/handlers/lang-blog-title.go14
-rw-r--r--internal/router/handlers/lang-map.go8
-rw-r--r--internal/router/handlers/lang-user.go7
-rw-r--r--internal/router/handlers/lang.go8
-rw-r--r--internal/router/handlers/root.go7
-rw-r--r--internal/router/router.go12
-rw-r--r--internal/templatemanager/templatemanager.go3
-rw-r--r--locale/localization.en.yaml2
-rw-r--r--locale/localization.go4
-rw-r--r--locale/localization.ru.yaml2
-rw-r--r--views/layouts/general-page.html4
-rw-r--r--views/pages/blog-catalogue.html5
-rw-r--r--views/pages/blog-page.html7
-rw-r--r--views/pages/global-map.html27
-rw-r--r--views/pages/home-page.html6
-rw-r--r--views/pages/language-pick.html5
-rw-r--r--views/pages/user-page.html5
19 files changed, 94 insertions, 57 deletions
diff --git a/internal/frontmatter/parser.go b/internal/frontmatter/parser.go
index 83312dc..6062113 100644
--- a/internal/frontmatter/parser.go
+++ b/internal/frontmatter/parser.go
@@ -9,15 +9,16 @@ import (
)
type Metadata struct {
- Title string `yaml:"title"`
- ShortDescription string `yaml:"shortDescription"`
- ActionDate string `yaml:"actionDate"`
- PublishedTime time.Time `yaml:"publishedTime"`
- Thumbnail string `yaml:"thumbnail"`
- Tags []string `yaml:"tags"`
- Geolocation string `yaml:"geolocation"`
- Medley string `yaml:"medley"`
- MedleyPart int `yaml:"medleyPart"`
+ Title string `yaml:"title"`
+ ShortDescription string `yaml:"shortDescription"`
+ ActionDate string `yaml:"actionDate"`
+ PublishedTime time.Time `yaml:"publishedTime"`
+ Thumbnail string `yaml:"thumbnail"`
+ Tags []string `yaml:"tags"`
+ Geolocation string `yaml:"geolocation"`
+ Medley string `yaml:"medley"`
+ MedleyPart int `yaml:"medleyPart"`
+ ContentSettings map[string]string `yaml:"contentSettings"`
}
func ParseFrontmatter(content []byte) (metadata *Metadata, markdown []byte, err error) {
diff --git a/internal/router/basic-handler.go b/internal/router/basic-handler.go
index a859d10..4a94a48 100644
--- a/internal/router/basic-handler.go
+++ b/internal/router/basic-handler.go
@@ -41,6 +41,12 @@ func (r *BasicHandler) Render(c *fiber.Ctx, supplements *Supplements, lang strin
return fiber.StatusNoContent, nil
}
+func (r *BasicHandler) AddMeta(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) {
+ return map[string]string{
+ "robots": "noindex,nofollow",
+ }, nil
+}
+
func (r *BasicHandler) RenderHeader(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
return fiber.StatusNoContent, nil
}
diff --git a/internal/router/handlers/lang-blog-title.go b/internal/router/handlers/lang-blog-title.go
index 0f4f564..97de089 100644
--- a/internal/router/handlers/lang-blog-title.go
+++ b/internal/router/handlers/lang-blog-title.go
@@ -46,6 +46,20 @@ func (r *BlogPageHandler) ToValidateLang() router.LangSetting {
return router.InPath
}
+func (r *BlogPageHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) {
+ metadata, _, err := supplements.B2Client.ReadFrontmatter(lang + "/" + c.Params("title") + ".md")
+ if err != nil {
+ return nil, fmt.Errorf("failed to read frontmatter of the desired blog post: %w", err)
+ }
+
+ return map[string]string{
+ "og:title": metadata.Title,
+ "og:description": fmt.Sprintf("%s [%s]", metadata.ShortDescription, metadata.ActionDate),
+ "og:image": fmt.Sprintf("https://f003.backblazeb2.com/file/sayana-photos/webp-320p/%s.webp", metadata.Thumbnail),
+ "twitter:card": "summary_large_image",
+ }, nil
+}
+
func (r *BlogPageHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
_, pathParts, _, err := router.GetPathFromReferer(c)
if err != nil {
diff --git a/internal/router/handlers/lang-map.go b/internal/router/handlers/lang-map.go
index dbf1437..7df6104 100644
--- a/internal/router/handlers/lang-map.go
+++ b/internal/router/handlers/lang-map.go
@@ -3,6 +3,7 @@ package handlers
import (
"fmt"
"log/slog"
+ "slices"
"strconv"
"strings"
@@ -41,6 +42,9 @@ func (r *MapHandler) ToValidateLang() router.LangSetting {
func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
pages, err := supplements.B2Client.Scan(lang + "/")
+ slices.SortFunc(pages, func(a *b2.BlogPage, b *b2.BlogPage) int {
+ return a.Metadata.PublishedTime.Compare(b.Metadata.PublishedTime)
+ })
status := fiber.StatusOK
if err != nil {
slog.Error("received an error while scanning b2 pages",
@@ -52,6 +56,7 @@ func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang
}
type MapMarker struct {
+ Index int `json:"Index"`
Title string `json:"Title"`
PageLink string `json:"PageLink"`
Lat float64 `json:"Lat"`
@@ -61,7 +66,7 @@ func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang
}
mapMarkers := make([]*MapMarker, 0, len(pages))
- for _, page := range pages {
+ for i, page := range pages {
geolocationParts := strings.Split(page.Metadata.Geolocation, " ")
if len(geolocationParts) < 2 {
continue
@@ -78,6 +83,7 @@ func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang
}
mapMarkers = append(mapMarkers, &MapMarker{
+ Index: i,
Title: page.Metadata.Title,
PageLink: fmt.Sprintf("/%s/blog/%s", lang, page.FileName),
Lat: x,
diff --git a/internal/router/handlers/lang-user.go b/internal/router/handlers/lang-user.go
index db566fe..c22bcac 100644
--- a/internal/router/handlers/lang-user.go
+++ b/internal/router/handlers/lang-user.go
@@ -37,6 +37,13 @@ func (r *UserHandler) ToValidateLang() router.LangSetting {
return router.InPath
}
+func (r *UserHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) {
+ return map[string]string{
+ "og:title": supplements.Localization[lang].BlogSearch.Header,
+ "og:description": supplements.Localization[lang].BlogSearch.Description,
+ }, nil
+}
+
func (r *UserHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
templateMap["Title"] = supplements.Localization[lang].UserProfile.Header
diff --git a/internal/router/handlers/lang.go b/internal/router/handlers/lang.go
index fc4deb6..74166ae 100644
--- a/internal/router/handlers/lang.go
+++ b/internal/router/handlers/lang.go
@@ -41,6 +41,14 @@ func (r *HomeHandler) ToValidateLang() router.LangSetting {
return router.InPath
}
+func (r *HomeHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) {
+ return map[string]string{
+ "og:title": supplements.Localization[lang].HomePage.Header,
+ "og:description": supplements.Localization[lang].HomePage.HomePageDescription,
+ "og:image": "https://f003.backblazeb2.com/file/sayana-static/home-page-gifs/" + fmt.Sprintf("otter-%d.gif", rand.Int()%3+1),
+ }, nil
+}
+
func (r *HomeHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) {
templateMap["Title"] = supplements.Localization[lang].HomePage.Header
templateMap["FilledHeartCount"] = uint(40)
diff --git a/internal/router/handlers/root.go b/internal/router/handlers/root.go
index c8c4cd4..9f15782 100644
--- a/internal/router/handlers/root.go
+++ b/internal/router/handlers/root.go
@@ -35,6 +35,13 @@ func (r *RootHandler) CacheDuration() time.Duration {
return 24 * time.Hour
}
+func (r *RootHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) {
+ return map[string]string{
+ "og:title": "Saya Blog",
+ "og:description": "The blog of a 25 years old, travelling God knows where, proud to be not mainstream // Личный блог 25-летки, странствующего по ебеням, зато не мейнстрим",
+ }, nil
+}
+
func (r *RootHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, _ string, templateMap fiber.Map) (statusCode int, err error) {
templateMap["Title"] = "Choose Your Language"
templateMap["AvailableLanguages"] = supplements.AvailableLanguages
diff --git a/internal/router/router.go b/internal/router/router.go
index c0556f0..f28add2 100644
--- a/internal/router/router.go
+++ b/internal/router/router.go
@@ -60,6 +60,7 @@ type Route interface {
ToValidateLang() LangSetting
TemplatesToInject() []string
Render(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error)
+ AddMeta(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error)
RenderHeader(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error)
RenderBody(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error)
RenderFooter(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error)
@@ -382,11 +383,18 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error {
return c.Status(fiber.StatusOK).Type("html").Send(val)
}
- content, err := r.supplements.TemplateManager.Render("general-page", fiber.Map{
+ valueMap := fiber.Map{
"L": r.supplements.Localization[lang],
"Lang": lang,
"QueryString": queryString,
- })
+ }
+
+ var err error
+ if valueMap["Meta"], err = route.AddMeta(c, r.supplements, lang, valueMap); err != nil {
+ slog.Warn("failed to receive meta", slog.String("path", path), slog.String("error", err.Error()))
+ }
+
+ content, err := r.supplements.TemplateManager.Render("general-page", valueMap)
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")
diff --git a/internal/templatemanager/templatemanager.go b/internal/templatemanager/templatemanager.go
index 75309e4..b23e2e8 100644
--- a/internal/templatemanager/templatemanager.go
+++ b/internal/templatemanager/templatemanager.go
@@ -32,6 +32,9 @@ var templateFuncMap = template.FuncMap{
return items
},
"replace": strings.ReplaceAll,
+ "fdiv": func(a, b int) float64 {
+ return float64(a) / float64(b)
+ },
}
func NewTemplateManager(templates ...TemplateManagerTemplates) (*TemplateManager, error) {
diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml
index c7f2df8..e165866 100644
--- a/locale/localization.en.yaml
+++ b/locale/localization.en.yaml
@@ -1,6 +1,7 @@
TagsLabel: 'Tags:'
BlogSearch:
Header: 'Blog Search'
+ Description: "The Saya Blog posts' list"
TagsHeader: 'Tags'
OrderByHeader: 'Order by...'
TitleOrdered: 'Title'
@@ -9,6 +10,7 @@ BlogSearch:
ChooseAllTags: 'Choose All'
GlobalMap:
Header: 'Global Map'
+ Description: "Global map with all the tags of places I've been to"
HomePage:
Header: 'Home Page'
Contacts: 'Contacts'
diff --git a/locale/localization.go b/locale/localization.go
index c6467c4..a4f36f1 100644
--- a/locale/localization.go
+++ b/locale/localization.go
@@ -20,6 +20,7 @@ type LocaleConfig struct {
type BlogSearchConfig struct {
Header string `yaml:"Header" json:"Header"`
+ Description string `yaml:"Description" json:"Description"`
TagsHeader string `yaml:"TagsHeader" json:"TagsHeader"`
OrderByHeader string `yaml:"OrderByHeader" json:"OrderByHeader"`
TitleOrdered string `yaml:"TitleOrdered" json:"TitleOrdered"`
@@ -29,7 +30,8 @@ type BlogSearchConfig struct {
}
type GlobalMapConfig struct {
- Header string `yaml:"Header" json:"Header"`
+ Header string `yaml:"Header" json:"Header"`
+ Description string `yaml:"Description" json:"Description"`
}
type HomePageConfig struct {
diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml
index 5df82da..e65848a 100644
--- a/locale/localization.ru.yaml
+++ b/locale/localization.ru.yaml
@@ -1,6 +1,7 @@
TagsLabel: 'Тэги:'
BlogSearch:
Header: 'Поиск по блогу'
+ Description: 'Каталог записей Saya Blog'
TagsHeader: 'Тэги'
OrderByHeader: 'Упорядочить по...'
TitleOrdered: 'названию'
@@ -9,6 +10,7 @@ BlogSearch:
ChooseAllTags: 'Выбрать все'
GlobalMap:
Header: 'Глобальная карта'
+ Description: 'Глобальная карта с метками мест, где я побывал'
HomePage:
Header: 'Домашняя страница'
Contacts: 'Контакты'
diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html
index 2bf548f..3f5bcfd 100644
--- a/views/layouts/general-page.html
+++ b/views/layouts/general-page.html
@@ -3,7 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- {{ block "meta" . }}{{ end }}
+ {{- range $k, $v := .Meta }}
+ <meta name="{{ $k }}" content="{{ $v }}">
+ {{- end }}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css">
<link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/glightbox/3.3.0/css/glightbox.min.css">
diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html
index 349e58f..d2c74ae 100644
--- a/views/pages/blog-catalogue.html
+++ b/views/pages/blog-catalogue.html
@@ -1,8 +1,3 @@
-{{ define "meta" }}
-<meta name="og:title" content="{{ .L.BlogSearch.Header }}">
-<meta name="og:description" content="{{ .L.BlogSearch.Description }}">
-{{ end }}
-
{{ define "body" }}
<div class="flex flex-row ar-lt-0.8:flex-col text-base h-full">
<div class="relative flex shrink-0 ar-gt-0.8:min-w-[10dvh] ar-gt-0.8:max-w-[20dvh] ar-gt-0.8:w-fit ar-lt-0.8:w-full ar-lt-0.8:max-h-[30%] ar-gt-0.8:mr-6">
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html
index 241ef43..c81406f 100644
--- a/views/pages/blog-page.html
+++ b/views/pages/blog-page.html
@@ -1,10 +1,3 @@
-{{ define "meta" }}
-<meta name="og:title" content="{{ .Title }}">
-<meta name="og:description" content="{{ .ShortDescription }} [{{ .ActionDate }}]">
-<meta name="og:image" content="{{ .Thumbnail }}">
-<meta name="twitter:card" content="summary_large_image">
-{{ end }}
-
{{ define "body" }}
<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div>
<div class="relative bg-background-light flex flex-col px-8 py-4 overflow-y-auto">
diff --git a/views/pages/global-map.html b/views/pages/global-map.html
index a19f3fa..4b07bd2 100644
--- a/views/pages/global-map.html
+++ b/views/pages/global-map.html
@@ -245,35 +245,32 @@
map.addLayer(markers);
}
- function stringToColor(str) {
+ function markerToHsl(str, newCoef) {
let hash = 0;
- for (let i = 0; i < str.length; i++) {
+ for (let i = 0; i < str.length / 2; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
+ hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash);
+ hash %= 360;
}
- let color = '#';
- for (let i = 0; i < 3; i++) {
- const value = (hash >> (i * 8)) & 0xFF;
- color += ('00' + value.toString(16)).substr(-2);
- }
-
- return color;
+ return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)];
}
- function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "") {
- const titleColor = stringToColor(title);
+ function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1) {
+ const [h, s, l] = markerToHsl(title, newCoef);
+ const color = `hsl(${h} ${s}% ${l}%)`;
if (relativeErrorMeters != 0) {
var circle = L.circle([x, y], {
- color: titleColor,
- fillColor: titleColor,
+ color: color,
+ fillColor: color,
fillOpacity: 0.15,
radius: relativeErrorMeters
}).addTo(map);
}
var marker = L.marker([x, y], {
- icon: createCustomIcon(titleColor),
+ icon: createCustomIcon(color),
title: title,
}).bindPopup(`
<div class="flex flex-row justify-center justify-items-center">
@@ -287,7 +284,7 @@
document.addEventListener('DOMContentLoaded', () => {
initLocationMap();
{{- range .MapMarkers }}
- mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }});
+ mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }});
{{- end }}
});
diff --git a/views/pages/home-page.html b/views/pages/home-page.html
index 4ba6b8e..6d397fa 100644
--- a/views/pages/home-page.html
+++ b/views/pages/home-page.html
@@ -1,9 +1,3 @@
-{{ define "meta" }}
-<meta name="og:title" content="{{ .Title }}">
-<meta name="og:description" content="{{ .L.HomePage.Description }}">
-<meta name="og:image" content="https://f003.backblazeb2.com/file/sayana-static/home-page-gifs/{{ .GifName }}">
-{{ end }}
-
{{ define "body" }}
<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div>
<div class="relative bg-background-light flex flex-row ar-lt-0.6:flex-col w-full h-full overflow-y-auto text-base/loose tracking-wide z-10">
diff --git a/views/pages/language-pick.html b/views/pages/language-pick.html
index 3f6f672..775c18e 100644
--- a/views/pages/language-pick.html
+++ b/views/pages/language-pick.html
@@ -1,8 +1,3 @@
-{{ define "meta" }}
-<meta name="og:title" content="{{ .Root.Header }}">
-<meta name="og:description" content="{{ .Root.Description }}">
-{{ end }}
-
{{ define "body" }}
<div class="flex flex-col justify-center text-3xl h-full">
{{- range .AvailableLanguages }}
diff --git a/views/pages/user-page.html b/views/pages/user-page.html
index c9f7373..fda7e66 100644
--- a/views/pages/user-page.html
+++ b/views/pages/user-page.html
@@ -1,8 +1,3 @@
-{{ define "meta" }}
-<meta name="og:title" content="{{ .L.BlogSearch.Header }}">
-<meta name="og:description" content="{{ .L.BlogSearch.Description }}">
-{{ end }}
-
{{ define "body" }}
<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div>
<div class="relative bg-background-light flex flex-col px-8 py-4">