| -rw-r--r-- | .github/workflows/build-and-deploy-prod.yml | 2 | ||||
| -rw-r--r-- | config/config.go | 5 | ||||
| -rw-r--r-- | config/config.prod.yaml | 2 | ||||
| -rw-r--r-- | deploy/playbook.yml | 1 | ||||
| -rw-r--r-- | internal/router/basic-handler.go | 10 | ||||
| -rw-r--r-- | internal/router/handlers/lang-blog-title.go | 30 | ||||
| -rw-r--r-- | internal/router/handlers/lang-blog.go | 9 | ||||
| -rw-r--r-- | internal/router/handlers/lang-user.go | 11 | ||||
| -rw-r--r-- | internal/router/handlers/lang.go | 12 | ||||
| -rw-r--r-- | internal/router/handlers/root.go | 19 | ||||
| -rw-r--r-- | internal/router/router.go | 39 | ||||
| -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/layouts/general-page.html | 31 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 10 | ||||
| -rw-r--r-- | views/pages/global-map.html | 8 |
17 files changed, 148 insertions, 44 deletions
diff --git a/.github/workflows/build-and-deploy-prod.yml b/.github/workflows/build-and-deploy-prod.yml index 413f402..24b6e0e 100644 --- a/.github/workflows/build-and-deploy-prod.yml +++ b/.github/workflows/build-and-deploy-prod.yml @@ -75,4 +75,4 @@ jobs: ANSIBLE_HOST_KEY_CHECKING: False working-directory: ./deploy run: | - ansible-playbook -i inventory.yml -l prod playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" -e saya_today_web_b2_key_id="${{ secrets.B2_KEY_ID }}" -e saya_today_web_b2_application_key="${{ secrets.B2_APPLICATION_KEY }}" -e saya_today_web_environment=prod -e saya_today_web_tag=${{ github.ref_name }} -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" + ansible-playbook -i inventory.yml -l prod playbook.yml --private-key ~/.ssh/id_ed25519 -u svc_github -e saya_today_web_auth_salt="${{ secrets.AUTH_SALT }}" -e saya_today_web_b2_key_id="${{ secrets.B2_KEY_ID }}" -e saya_today_web_b2_application_key="${{ secrets.B2_APPLICATION_KEY }}" -e saya_today_web_environment=prod -e saya_today_web_tag=${{ github.ref_name }} -e saya_today_web_mail_salt="${{ secrets.MAIL_SALT }}" -e saya_today_web_mail_host="${{ secrets.MAIL_HOST }}" -e saya_today_web_mail_address="${{ secrets.MAIL_ADDRESS }}" -e saya_today_web_mail_username="${{ secrets.MAIL_USERNAME }}" -e saya_today_web_mail_password="${{ secrets.MAIL_PASSWORD }}" -e saya_today_google_site_verification="${{ secrets.GOOGLE_SITE_VERIFICATION }}" diff --git a/config/config.go b/config/config.go index fd8c480..eb4a91d 100644 --- a/config/config.go +++ b/config/config.go @@ -17,6 +17,7 @@ type Config struct { Auth AuthConfig `json:"Auth" yaml:"auth" validate:"required"` Mail MailConfig `json:"Mail" yaml:"mail" validate:"required"` CanonicalEndpoint string `json:"CanonicalEndpoint" yaml:"canonicalEndpoint" validate:"required"` + Meta MetaConfig `json:"Meta" yaml:"meta"` } type BlogPagesConfig struct { @@ -77,6 +78,10 @@ type TriggerConfig struct { OnNewPost string `json:"OnNewPost" yaml:"onNewPost" validate:"cron,required"` } +type MetaConfig struct { + GoogleSiteVerification string `json:"GoogleSiteVerification" yaml:"googleSiteVerification"` +} + func LoadConfig(path string, config *Config) error { fileBytes, err := os.ReadFile(path) if err != nil { diff --git a/config/config.prod.yaml b/config/config.prod.yaml index 4de853c..b40ba57 100644 --- a/config/config.prod.yaml +++ b/config/config.prod.yaml @@ -45,3 +45,5 @@ mail: trigger: onNewPost: "0/5 * * * *" canonicalEndpoint: 'https://${FQDN}' +meta: + googleSiteVerification: '${GOOGLE_SITE_VERIFICATION}' diff --git a/deploy/playbook.yml b/deploy/playbook.yml index 2516f4e..b0202bd 100644 --- a/deploy/playbook.yml +++ b/deploy/playbook.yml @@ -24,6 +24,7 @@ MAIL_PASSWORD: "{{ saya_today_web_mail_password }}" MAIL_SALT: "{{ saya_today_web_mail_salt }}" FQDN: "{{ saya_today_web_listen_address }}" + GOOGLE_SITE_VERIFICATION: "{{ saya_today_google_site_verification | default('') }}" network_mode: caddy networks: - name: caddy diff --git a/internal/router/basic-handler.go b/internal/router/basic-handler.go index 98a7d84..b973d01 100644 --- a/internal/router/basic-handler.go +++ b/internal/router/basic-handler.go @@ -49,10 +49,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) AddMeta(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (meta []MetaField, err error) { + return []MetaField{{Name: "robots", Content: "noindex,nofollow"}}, nil +} + +func (r *BasicHandler) AddLinkedData(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (ld map[string]any, err error) { + return nil, nil } func (r *BasicHandler) RenderHeader(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { diff --git a/internal/router/handlers/lang-blog-title.go b/internal/router/handlers/lang-blog-title.go index 9eac084..5bfc6e7 100644 --- a/internal/router/handlers/lang-blog-title.go +++ b/internal/router/handlers/lang-blog-title.go @@ -65,17 +65,35 @@ func (r *BlogPageHandler) SitemapInfo(supplements *router.Supplements) []router. return sitemapInfo } -func (r *BlogPageHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta map[string]string, err error) { +func (r *BlogPageHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, 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", + return []router.MetaField{ + {Property: "og:title", Content: metadata.Title}, + {Property: "og:description", Content: fmt.Sprintf("%s [%s]", metadata.ShortDescription, metadata.ActionDate)}, + {Property: "og:image", Content: fmt.Sprintf("https://f003.backblazeb2.com/file/sayana-photos/webp-320p/%s.webp", metadata.Thumbnail)}, + {Property: "og:url", Content: fmt.Sprintf("%s/%s/blog/%s", templateMap["CanonicalEndpoint"], lang, c.Params("title"))}, + {Property: "og:type", Content: "website"}, + {Name: "twitter:card", Content: "summary_large_image"}, + }, nil +} + +func (r *BlogPageHandler) AddLinkedData(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (ld map[string]any, 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]any{ + "@context": "https://schema.org", + "@type": "Article", + "headline": metadata.Title, + "description": fmt.Sprintf("%s [%s]", metadata.ShortDescription, metadata.ActionDate), + "author": map[string]string{"@type": "Person", "name": "Saya Andy"}, + "datePublished": metadata.PublishedTime.UTC().Format(time.RFC3339), }, nil } diff --git a/internal/router/handlers/lang-blog.go b/internal/router/handlers/lang-blog.go index ffac77e..709d3dc 100644 --- a/internal/router/handlers/lang-blog.go +++ b/internal/router/handlers/lang-blog.go @@ -55,6 +55,15 @@ func (r *CatalogueHandler) SitemapInfo(supplements *router.Supplements) []router return sitemapInfo } +func (r *CatalogueHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, err error) { + return []router.MetaField{ + {Property: "og:title", Content: supplements.Localization[lang].BlogSearch.Header}, + {Property: "og:description", Content: supplements.Localization[lang].BlogSearch.Description}, + {Property: "og:url", Content: fmt.Sprintf("%s/%s/blog", templateMap["CanonicalEndpoint"], lang)}, + {Property: "og:type", Content: "website"}, + }, nil +} + func (r *CatalogueHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { querySort := c.Query("sort") if querySort == "" { diff --git a/internal/router/handlers/lang-user.go b/internal/router/handlers/lang-user.go index c22bcac..eae1685 100644 --- a/internal/router/handlers/lang-user.go +++ b/internal/router/handlers/lang-user.go @@ -37,10 +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, +func (r *UserHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, err error) { + return []router.MetaField{ + {Name: "robots", Content: "noindex,nofollow"}, + {Property: "og:title", Content: supplements.Localization[lang].UserProfile.Header}, + {Property: "og:description", Content: supplements.Localization[lang].UserProfile.Description}, + {Property: "og:url", Content: fmt.Sprintf("%s/%s/user", templateMap["CanonicalEndpoint"], lang)}, + {Property: "og:type", Content: "website"}, }, nil } diff --git a/internal/router/handlers/lang.go b/internal/router/handlers/lang.go index 5430773..c618353 100644 --- a/internal/router/handlers/lang.go +++ b/internal/router/handlers/lang.go @@ -49,11 +49,13 @@ func (r *HomeHandler) SitemapInfo(supplements *router.Supplements) []router.Site return sitemapInfo } -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), +func (r *HomeHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, err error) { + return []router.MetaField{ + {Property: "og:title", Content: supplements.Localization[lang].HomePage.Header}, + {Property: "og:description", Content: supplements.Localization[lang].HomePage.HomePageDescription}, + {Property: "og:image", Content: fmt.Sprintf("https://f003.backblazeb2.com/file/sayana-static/home-page-gifs/otter-%d.gif", rand.Int()%3+1)}, + {Property: "og:url", Content: fmt.Sprintf("%s/%s", templateMap["CanonicalEndpoint"], lang)}, + {Property: "og:type", Content: "website"}, }, nil } diff --git a/internal/router/handlers/root.go b/internal/router/handlers/root.go index 89b9c63..a8890fa 100644 --- a/internal/router/handlers/root.go +++ b/internal/router/handlers/root.go @@ -1,6 +1,7 @@ package handlers import ( + "fmt" "time" "github.com/SayaAndy/saya-today-web/internal/router" @@ -36,14 +37,20 @@ func (r *RootHandler) CacheDuration() time.Duration { } func (r *RootHandler) SitemapInfo(_ *router.Supplements) []router.SitemapInfo { - return []router.SitemapInfo{{Loc: "", Priority: 0.7}} + return []router.SitemapInfo{{Loc: "/", Priority: 0.7}} } -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) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, err error) { + meta = []router.MetaField{ + {Property: "og:title", Content: "Saya Blog"}, + {Property: "og:description", Content: "The blog of a 25 years old, travelling God knows where, proud to be not mainstream // Личный блог 25-летки, странствующего по ебеням, зато не мейнстрим"}, + {Property: "og:url", Content: fmt.Sprint(templateMap["CanonicalEndpoint"]) + "/"}, + {Property: "og:type", Content: "website"}, + } + if supplements.Meta.GoogleSiteVerification != "" { + meta = append(meta, router.MetaField{Name: "google-site-verification", Content: supplements.Meta.GoogleSiteVerification}) + } + return meta, nil } func (r *RootHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, _ string, templateMap fiber.Map) (statusCode int, err error) { diff --git a/internal/router/router.go b/internal/router/router.go index 56313ff..3242759 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -2,8 +2,10 @@ package router import ( "database/sql" + "encoding/json" "errors" "fmt" + "html/template" "log/slog" "net/url" "slices" @@ -21,7 +23,9 @@ import ( "github.com/SayaAndy/saya-today-web/locale" "github.com/dgraph-io/ristretto/v2" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/etag" "github.com/golang-migrate/migrate/v4" "github.com/golang-migrate/migrate/v4/database/sqlite3" "github.com/yuin/goldmark" @@ -59,6 +63,12 @@ type SitemapInfo struct { Priority float32 } +type MetaField struct { + Name string + Property string + Content string +} + type Route interface { Filter() (method string, path string) IsTemplated() bool @@ -69,7 +79,8 @@ type Route interface { SitemapInfo(supplements *Supplements) []SitemapInfo ContentType() 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) + AddMeta(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (meta []MetaField, err error) + AddLinkedData(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (ld map[string]any, 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) @@ -89,6 +100,7 @@ type Supplements struct { BlogTrigger *blogtrigger.BlogTriggerScheduler TemplateManager *templatemanager.TemplateManager MarkdownRenderer goldmark.Markdown + Meta config.MetaConfig } type Router struct { @@ -204,6 +216,8 @@ func NewRouter(cfg *config.Config) (*Router, error) { return nil, fmt.Errorf("fail to initialize template manager: %w", err) } + supplements.Meta = cfg.Meta + enablePrintRoutes := false if cfg.LogLevel <= slog.LevelDebug { enablePrintRoutes = true @@ -220,6 +234,20 @@ func NewRouter(cfg *config.Config) (*Router, error) { AllowHeaders: "Origin, Content-Type, Accept", })) + app.Use(compress.New(compress.Config{ + Level: compress.LevelBestSpeed, + })) + + app.Use(etag.New()) + + app.Use(func(c *fiber.Ctx) error { + path := c.Path() + if len(path) > 1 && path[len(path)-1] == '/' { + return c.Redirect(strings.TrimRight(path, "/"), fiber.StatusMovedPermanently) + } + return c.Next() + }) + templatedRoutes := make(map[string]map[string]Route) templatedPathMatcher := NewPathMatcher() @@ -406,6 +434,15 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { slog.Warn("failed to receive meta", slog.String("path", path), slog.String("error", err.Error())) } + var ld map[string]any + if ld, err = route.AddLinkedData(c, r.supplements, lang, valueMap); err != nil { + slog.Warn("failed to receive linked data", slog.String("path", path), slog.String("error", err.Error())) + } + if ld != nil { + ldBytes, _ := json.Marshal(ld) + valueMap["LinkedData"] = template.JS(ldBytes) + } + 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())) diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml index e165866..9923881 100644 --- a/locale/localization.en.yaml +++ b/locale/localization.en.yaml @@ -48,6 +48,7 @@ Mail: CapturedOn: 'Captured on' UserProfile: Header: 'Personal Settings' + Description: 'Set and verify your e-mail here, as well as subscribe to favorite blog tags' EmailHeader: 'E-Mail' VerificationCodeHeader: 'Verification Code' TypeEmail: 'Please type your email' diff --git a/locale/localization.go b/locale/localization.go index a4f36f1..7080355 100644 --- a/locale/localization.go +++ b/locale/localization.go @@ -85,6 +85,7 @@ type NewPostConfig struct { type UserProfileConfig struct { Header string `yaml:"Header" json:"Header"` + Description string `yaml:"Description" json:"Description"` EmailHeader string `yaml:"EmailHeader" json:"EmailHeader"` VerificationCodeHeader string `yaml:"VerificationCodeHeader" json:"VerificationCodeHeader"` TypeEmail string `yaml:"TypeEmail" json:"TypeEmail"` diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml index e65848a..34e5387 100644 --- a/locale/localization.ru.yaml +++ b/locale/localization.ru.yaml @@ -48,6 +48,7 @@ Mail: CapturedOn: 'Снималось' UserProfile: Header: 'Личные настройки' + Description: 'Здесь можно настроить и верифицировать свою электронную почту, а также подписаться на избранные темы блога' EmailHeader: 'E-Mail' VerificationCodeHeader: 'Код верификации' TypeEmail: 'Пожалуйста, введите свою почту' diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index 3f5bcfd..2d3bff3 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -3,13 +3,24 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - {{- range $k, $v := .Meta }} - <meta name="{{ $k }}" content="{{ $v }}"> + {{- range .Meta }} + <meta {{with .Name}}name="{{.}}" {{end}}{{with .Property}}property="{{.}}" {{end}}{{with .Content}}content="{{.}}"{{end}}> + {{- if eq .Property "og:description" }} + <meta name="description" content="{{.Content}}"> + {{- end }} + {{- if eq .Property "og:url" }} + <link rel="canonical" href="{{.Content}}"> + {{- end }} {{- 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"> <link href="/output.css" rel="stylesheet"> + {{- with .LinkedData }} + <script type="application/ld+json"> + {{ . }} + </script> + {{- end }} </head> <body data-theme="ram" class="font-andika text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh flex flex-row"> @@ -180,7 +191,7 @@ flex-row sticky w-dvw h-fit xs:w-[90dvw] sm:left-0 sm:flex-col sm:fixed sm:w-fit z-20 shadow-[0_0_1rem_black]"> - <div class="text-[3rem] flex flex-row sm:flex-col gap-0 relative z-20"> + <nav aria-label="Main navigation" class="text-[3rem] flex flex-row sm:flex-col gap-0 relative z-20"> <a onclick="return changeUrl('/../', true);" href="./" id="sidebar-back-button" class="themed-button themed-button-sidebar w-fit h-fit hidden" draggable="false"> <i class="fas fa-circle-left flex w-18 h-18 content-center text-center text-sidebar"></i> </a> @@ -199,7 +210,7 @@ <a onclick="return changeUrl('/{{ .Lang }}/user');" href="/{{ .Lang }}/user" id="sidebar-user-button" class="themed-button themed-button-sidebar w-fit h-fit hidden" draggable="false"> <i class="fas fa-user flex w-18 h-18 content-center text-center text-sidebar"></i> </a> - </div> + </nav> <div class="theme-wheel" id="theme-wheel"> <div class="theme-icon" data-theme="olive"></div> <div class="theme-icon" data-theme="lettuce"></div> @@ -247,21 +258,21 @@ <div class="bg-background-dark border-r-2 border-dashed border-main-hard"></div> <div class="max-h-full max-w-full flex flex-col grow bg-background-dark relative pt-4 ml-8 pr-4"> - <div id="general-page-header" class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col basis-8" + <header id="general-page-header" class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col basis-8" hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> + </header> <div id="general-page-header-trigger" hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-header" hx-swap="innerHTML"></div> <hr class="max-xs:hidden [@media(max-height:32rem)]:hidden border-t-4 border-dotted border-main-hard mb-2"> - <div id="general-page-body" class="relative flex flex-col grow overflow-y-auto" + <main id="general-page-body" class="relative flex flex-col grow overflow-y-auto" hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> + </main> <div id="general-page-body-trigger" hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-body" hx-swap="innerHTML"></div> - <div id="general-page-footer" class="flex flex-row basis-4 my-1" + <footer id="general-page-footer" class="flex flex-row basis-4 my-1" hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> + </footer> <div id="general-page-footer-trigger" hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-footer" hx-swap="innerHTML"></div> <hr class="border-t-4 border-dotted border-main-hard mb-2"> diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index c81406f..3fc4526 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -1,12 +1,12 @@ {{ 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"> - <p class="max-xs:block [@media(max-height:32rem)]:block hidden font-andika text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1">{{ .Title }}</p> +<article class="relative bg-background-light flex flex-col px-8 py-4 overflow-y-auto"> + <h1 class="max-xs:block [@media(max-height:32rem)]:block hidden font-andika text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1">{{ .Title }}</h1> <p class="block grow text-lg font-andika text-secondary"> <b>{{ .L.Metadata.Published }}</b>: - <span hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> + <time datetime="{{ .PublishedDate }}" hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> {{ .PublishedDate }} - </span> + </time> </p> <p class="block grow text-lg font-andika text-secondary"><b>{{ .L.Metadata.Action }}</b>: {{ .ActionDate }}</p> <p class="block grow text-md font-andika italic text-main-hard">{{ .ShortDescription }}</p> @@ -60,7 +60,7 @@ }); </script> {{- end }} -</div> +</article> {{ end }} {{ define "header" }} diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 4b07bd2..604b52e 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -3,9 +3,13 @@ <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <meta name="og:title" content="{{ .L.GlobalMap.Header }}"> - <meta name="og:description" content="{{ .L.GlobalMap.Description }}"> + <meta name="description" content="{{ .L.GlobalMap.Description }}"> + <meta property="og:title" content="{{ .L.GlobalMap.Header }}"> + <meta property="og:description" content="{{ .L.GlobalMap.Description }}"> + <meta property="og:url" content="{{ .CanonicalEndpoint }}/{{ .Lang }}/map"> + <meta property="og:type" content="site"> <title>{{ .L.GlobalMap.Header }} // SAYA TODAY</title> + <link rel="canonical" href="{{ .CanonicalEndpoint }}/{{ .Lang }}/map"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> {{ block "header" . }}{{ end }} <link href="/output.css" rel="stylesheet"> |