Diffstat (limited to 'internal/router')
| -rw-r--r-- | internal/router/handlers/api-v1-blog-search.go | 53 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-email-is-in-verification.go | 3 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-email-send-verification-code.go | 15 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-email-verify.go | 7 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-like-get.go | 2 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-map-get.go | 116 | ||||
| -rw-r--r-- | internal/router/handlers/api-v1-subs-put.go | 7 | ||||
| -rw-r--r-- | internal/router/handlers/lang-blog-title.go | 31 | ||||
| -rw-r--r-- | internal/router/handlers/lang-blog.go | 16 | ||||
| -rw-r--r-- | internal/router/handlers/lang-map.go | 65 | ||||
| -rw-r--r-- | internal/router/handlers/lang-user-unsubscribe.go | 9 | ||||
| -rw-r--r-- | internal/router/handlers/lang-user.go | 9 | ||||
| -rw-r--r-- | internal/router/handlers/lang.go | 9 | ||||
| -rw-r--r-- | internal/router/handlers/root.go | 4 | ||||
| -rw-r--r-- | internal/router/router.go | 152 |
15 files changed, 172 insertions, 326 deletions
diff --git a/internal/router/handlers/api-v1-blog-search.go b/internal/router/handlers/api-v1-blog-search.go index b59f09a..38004a1 100644 --- a/internal/router/handlers/api-v1-blog-search.go +++ b/internal/router/handlers/api-v1-blog-search.go @@ -1,13 +1,13 @@ package handlers import ( - "cmp" "encoding/json" "fmt" "log/slog" "net/url" "regexp" "slices" + "strings" "time" "github.com/SayaAndy/saya-today-web/internal/blog" @@ -51,10 +51,6 @@ func (r *BlogSearchHandler) RateLimiter() *fiber.Handler { func (r *BlogSearchHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { sort := c.Query("sort") tz := c.Query("tz") - medley := c.Query("medley") - highlight := c.Query("highlight") - hideTags := c.QueryBool("hideTags", false) - hidePublishedTime := c.QueryBool("hidePublishedTime", false) loc, err := time.LoadLocation(tz) if err != nil { @@ -89,26 +85,21 @@ func (r *BlogSearchHandler) Render(c *fiber.Ctx, supplements *router.Supplements for _, page := range pages { for _, tag := range page.Metadata.Tags { if len(tags) == 0 || slices.Contains(tags, tag) { - if medley == "" || medley == page.Metadata.Medley { - pageMeta = append(pageMeta, fiber.Map{ - "Link": page.Link, - "ArticleLink": "/" + lang + "/blog/" + page.FileName, - "Title": page.Metadata.Title, - "PublishedTime": page.Metadata.PublishedTime.In(loc).Format("2006-01-02 15:04:05 -07:00"), - "ActionDate": page.Metadata.ActionDate, - "ShortDescription": page.Metadata.ShortDescription, - "Thumbnail": page.Metadata.Thumbnail, - "Tags": page.Metadata.Tags, - "LikeCount": supplements.ClientCache.GetLikeCount(page.FileName), - "Liked": supplements.ClientCache.GetLikeStatus(c.IP(), page.FileName), - "ViewCount": supplements.ClientCache.GetViewCount(page.FileName), - "Viewed": supplements.ClientCache.GetViewStatus(c.IP(), page.FileName), - "Medley": page.Metadata.Medley, - "MedleyPart": page.Metadata.MedleyPart, - "ToHighlight": page.FileName == highlight, - }) - break - } + pageMeta = append(pageMeta, fiber.Map{ + "Link": page.Link, + "ArticleLink": "/" + lang + "/blog/" + page.FileName, + "Title": page.Metadata.Title, + "PublishedTime": page.Metadata.PublishedTime.In(loc).Format("2006-01-02 15:04:05 -07:00"), + "ActionDate": page.Metadata.ActionDate, + "ShortDescription": page.Metadata.ShortDescription, + "Thumbnail": page.Metadata.Thumbnail, + "Tags": page.Metadata.Tags, + "LikeCount": supplements.ClientCache.GetLikeCount(page.FileName), + "Liked": supplements.ClientCache.GetLikeStatus(c.IP(), page.FileName), + "ViewCount": supplements.ClientCache.GetViewCount(page.FileName), + "Viewed": supplements.ClientCache.GetViewStatus(c.IP(), page.FileName), + }) + break } } } @@ -116,13 +107,13 @@ func (r *BlogSearchHandler) Render(c *fiber.Ctx, supplements *router.Supplements slices.SortFunc(pageMeta, func(a, b fiber.Map) int { switch sort { case "titleAsc": - return cmp.Compare(a["Title"].(string), b["Title"].(string)) + return strings.Compare(a["Title"].(string), b["Title"].(string)) case "titleDesc": - return cmp.Compare(b["Title"].(string), a["Title"].(string)) + return strings.Compare(b["Title"].(string), a["Title"].(string)) case "actionDateAsc": - return cmp.Compare(a["ActionDate"].(string), b["ActionDate"].(string)) + return strings.Compare(a["ActionDate"].(string), b["ActionDate"].(string)) case "actionDateDesc": - return cmp.Compare(b["ActionDate"].(string), a["ActionDate"].(string)) + return strings.Compare(b["ActionDate"].(string), a["ActionDate"].(string)) case "publicationDateAsc": publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"].(string)) publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"].(string)) @@ -131,15 +122,11 @@ func (r *BlogSearchHandler) Render(c *fiber.Ctx, supplements *router.Supplements publishedTimeA, _ := time.Parse("2006-01-02 15:04:05 -07:00", a["PublishedTime"].(string)) publishedTimeB, _ := time.Parse("2006-01-02 15:04:05 -07:00", b["PublishedTime"].(string)) return publishedTimeB.Compare(publishedTimeA) - case "medley": - return cmp.Compare(a["MedleyPart"].(int), b["MedleyPart"].(int)) } return 0 }) templateMap["BlogPages"] = pageMeta - templateMap["HideTags"] = hideTags - templateMap["HidePublishedTime"] = hidePublishedTime return fiber.StatusOK, nil } diff --git a/internal/router/handlers/api-v1-email-is-in-verification.go b/internal/router/handlers/api-v1-email-is-in-verification.go index b8ae162..4f05f48 100644 --- a/internal/router/handlers/api-v1-email-is-in-verification.go +++ b/internal/router/handlers/api-v1-email-is-in-verification.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -50,7 +49,7 @@ func (r *OngoingVerificationHandler) Render(c *fiber.Ctx, supplements *router.Su if !isAllowed { sterileDataset["striked-end-time"] = template.HTMLAttr(fmt.Sprintf("data-striked-end-time=\"%d\"", whenAllowed.UnixMilli())) templateMap["Status"] = "Neutral" - templateMap["Message"] = strings.ReplaceAll(l10n.T.GetPath(lang, "UserProfile", "DelayTilVerification").(string), "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")) + templateMap["Message"] = strings.ReplaceAll(supplements.Localization[lang].UserProfile.DelayTilVerification, "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")) } else { templateMap["Status"] = "OK" templateMap["Message"] = "" diff --git a/internal/router/handlers/api-v1-email-send-verification-code.go b/internal/router/handlers/api-v1-email-send-verification-code.go index ea8df4d..0b53efa 100644 --- a/internal/router/handlers/api-v1-email-send-verification-code.go +++ b/internal/router/handlers/api-v1-email-send-verification-code.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -50,27 +49,27 @@ func (r *SendVerificationCodeHandler) Render(c *fiber.Ctx, supplements *router.S email := c.FormValue("email") if email == "" { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "EmailEmpty").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.EmailEmpty return fiber.StatusUnprocessableEntity, nil } isTaken, err := supplements.Mailer.MailIsTaken(email) if err != nil { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationCodeSendingError").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationCodeSendingError slog.Error("failed to check if address is already taken", slog.String("error", err.Error())) return fiber.StatusUnprocessableEntity, nil } if isTaken { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "EmailTaken").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.EmailTaken return fiber.StatusUnprocessableEntity, nil } if isAllowed, whenAllowed, _ := supplements.Mailer.IsAllowedToRetryVerification(id); !isAllowed { templateMap["Status"] = "Failed" - templateMap["Message"] = strings.ReplaceAll(l10n.T.GetPath(lang, "UserProfile", "DelayTilVerification").(string), "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")) + templateMap["Message"] = strings.ReplaceAll(supplements.Localization[lang].UserProfile.DelayTilVerification, "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")) templateMap["DataAttributes"] = map[string]any{ "striked-end-time": template.HTMLAttr(fmt.Sprintf("data-striked-end-time=\"%d\"", whenAllowed.UnixMilli())), } @@ -79,20 +78,20 @@ func (r *SendVerificationCodeHandler) Render(c *fiber.Ctx, supplements *router.S if previousEmail, _, _ := supplements.Mailer.GetInfo(supplements.Mailer.GetHash(id)); previousEmail == email { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "EmailAlreadyValidated").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.EmailAlreadyValidated return fiber.StatusUnprocessableEntity, nil } if err = supplements.Mailer.SendVerificationCode(id, email, lang); err != nil { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationCodeSendingError").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationCodeSendingError slog.Error("failed to send a verification code", slog.String("error", err.Error())) return fiber.StatusUnprocessableEntity, nil } _, endTime, codeExpiry := supplements.Mailer.IsAllowedToRetryVerification(id) templateMap["Status"] = "OK" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationCodeSent").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationCodeSent templateMap["DataAttributes"] = map[string]any{ "striked-end-time": template.HTMLAttr(fmt.Sprintf("data-striked-end-time=\"%d\"", endTime.UnixMilli())), "code-expiry-time": template.HTMLAttr(fmt.Sprintf("data-code-expiry-time=\"%d\"", codeExpiry.UnixMilli())), diff --git a/internal/router/handlers/api-v1-email-verify.go b/internal/router/handlers/api-v1-email-verify.go index 65ad6d9..ace1870 100644 --- a/internal/router/handlers/api-v1-email-verify.go +++ b/internal/router/handlers/api-v1-email-verify.go @@ -5,7 +5,6 @@ import ( "log/slog" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -47,19 +46,19 @@ func (r *VerifyCodeHandler) Render(c *fiber.Ctx, supplements *router.Supplements if verificationCode == "" { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationEmpty").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationEmpty return fiber.StatusUnprocessableEntity, nil } if err = supplements.Mailer.Verify(verificationCode, lang); err != nil { slog.Warn("verification code is invalid", slog.String("verification_code", verificationCode), slog.String("error", err.Error())) templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationFailed").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationFailed return fiber.StatusUnprocessableEntity, nil } templateMap["Status"] = "OK" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationSuccess").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.VerificationSuccess templateMap["DataAttributes"] = map[string]any{ "hide-verification-panel": template.HTMLAttr("data-code-expiry-time=\"true\""), } diff --git a/internal/router/handlers/api-v1-like-get.go b/internal/router/handlers/api-v1-like-get.go index 9e2c856..0eaf5f6 100644 --- a/internal/router/handlers/api-v1-like-get.go +++ b/internal/router/handlers/api-v1-like-get.go @@ -42,7 +42,7 @@ func (r *GetLikeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, l return fiber.StatusBadRequest, fmt.Errorf("error getting path from referer: %w", err) } - if len(pathParts) != 3 || pathParts[1] != "blog" { + if len(pathParts) != 3 && pathParts[1] != "blog" { return fiber.StatusBadRequest, fmt.Errorf("invalid path format: expected '/:lang/blog/:page', got '%s'", path) } diff --git a/internal/router/handlers/api-v1-map-get.go b/internal/router/handlers/api-v1-map-get.go deleted file mode 100644 index e19c54a..0000000 --- a/internal/router/handlers/api-v1-map-get.go +++ /dev/null @@ -1,116 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - "slices" - "strconv" - "strings" - - "github.com/SayaAndy/saya-today-web/internal/blog" - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type GetMapHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &GetMapHandler{}) -} - -func (r *GetMapHandler) Filter() (method string, path string) { - return "GET", "/api/v1/map" -} - -func (r *GetMapHandler) IsTemplated() bool { - return false -} - -func (r *GetMapHandler) TemplatesToInject() []string { - return []string{"views/partials/global-map-widget.html"} -} - -func (r *GetMapHandler) ToCache() router.CacheSetting { - return router.ByUrlAndQuery -} - -func (r *GetMapHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *GetMapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - codename := c.Query("codename") - zoom := c.QueryInt("zoom", 4) - zoomPosition := c.Query("zoomPosition") - - pages, err := supplements.BlogClient.Scan(lang + "/") - status := fiber.StatusOK - if err != nil { - slog.Error("received an error while scanning blog pages", - slog.String("error", err.Error()), - slog.String("lang", lang), - ) - status = fiber.StatusPartialContent - pages = []*blog.Page{} - } - slices.SortFunc(pages, func(a *blog.Page, b *blog.Page) int { - return a.Metadata.PublishedTime.Compare(b.Metadata.PublishedTime) - }) - - type MapMarker struct { - Index int `json:"Index"` - Title string `json:"Title"` - PageLink string `json:"PageLink"` - Lat float64 `json:"Lat"` - Long float64 `json:"Long"` - AccuracyMeters int64 `json:"AccuracyMeters"` - Thumbnail string `json:"Thumbnail"` - ToHighlight bool `json:"ToHighlight"` - } - - templateMap["MapLocationLat"] = 45.4507 - templateMap["MapLocationLong"] = 68.8319 - templateMap["MapLocationZoom"] = zoom - templateMap["ZoomPosition"] = zoomPosition - - mapMarkers := make([]*MapMarker, 0, len(pages)) - for i, page := range pages { - geolocationParts := strings.Split(page.Metadata.Geolocation, " ") - if len(geolocationParts) < 2 { - continue - } - - var x, y float64 - var areaError int64 - if len(geolocationParts) >= 2 { - x, _ = strconv.ParseFloat(geolocationParts[0], 64) - y, _ = strconv.ParseFloat(geolocationParts[1], 64) - } - if len(geolocationParts) >= 3 { - areaError, _ = strconv.ParseInt(geolocationParts[2], 10, 64) - } - - toHighlight := page.FileName == codename - if toHighlight { - templateMap["MapLocationLat"] = x - templateMap["MapLocationLong"] = y - } - - mapMarkers = append(mapMarkers, &MapMarker{ - Index: i, - Title: page.Metadata.Title, - PageLink: fmt.Sprintf("/%s/blog/%s", lang, page.FileName), - Lat: x, - Long: y, - AccuracyMeters: areaError, - Thumbnail: page.Metadata.Thumbnail, - ToHighlight: toHighlight, - }) - } - - templateMap["MapMarkers"] = mapMarkers - - return status, nil -} diff --git a/internal/router/handlers/api-v1-subs-put.go b/internal/router/handlers/api-v1-subs-put.go index 12dc5cf..32fba7e 100644 --- a/internal/router/handlers/api-v1-subs-put.go +++ b/internal/router/handlers/api-v1-subs-put.go @@ -3,7 +3,6 @@ package handlers import ( "github.com/SayaAndy/saya-today-web/internal/mailer" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -53,18 +52,18 @@ func (r *PutSubsHandler) Render(c *fiber.Ctx, supplements *router.Supplements, l subscriptionTypeEnum = mailer.Specific default: templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "SubscribeInvalidType").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.SubscribeInvalidType return fiber.StatusUnprocessableEntity, nil } specificTags := c.FormValue("tags_picked") if err = supplements.Mailer.Subscribe(supplements.Mailer.GetHash(c.IP()), subscriptionTypeEnum, specificTags); err != nil { templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "FailedToSubscribe").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.FailedToSubscribe return fiber.StatusUnprocessableEntity, nil } templateMap["Status"] = "OK" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "SubscribedSuccessfully").(string) + templateMap["Message"] = supplements.Localization[lang].UserProfile.SubscribedSuccessfully return fiber.StatusOK, nil } diff --git a/internal/router/handlers/lang-blog-title.go b/internal/router/handlers/lang-blog-title.go index 76dc335..49744c1 100644 --- a/internal/router/handlers/lang-blog-title.go +++ b/internal/router/handlers/lang-blog-title.go @@ -10,7 +10,6 @@ import ( "github.com/SayaAndy/saya-today-web/internal/blog" "github.com/SayaAndy/saya-today-web/internal/frontmatter" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" "github.com/yuin/goldmark" ) @@ -71,15 +70,11 @@ func (r *BlogPageHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, if err != nil { return nil, fmt.Errorf("failed to read frontmatter of the desired blog post: %w", err) } - title := metadata.Title - if metadata.Medley != "" { - title += " // " + l10n.T.GetPath(lang, "Medleys", metadata.Medley).(string) - } return []router.MetaField{ - {Property: "og:title", Content: title}, + {Property: "og:title", Content: metadata.Title}, {Property: "og:description", Content: fmt.Sprintf("%s [%s]", metadata.ShortDescription, metadata.ActionDate)}, - {Property: "og:image", Content: fmt.Sprintf(supplements.PhotoStorage.Thumbnail560p.BaseUrl, metadata.Thumbnail)}, + {Property: "og:image", Content: fmt.Sprintf(supplements.PhotoStorage.Thumbnail320p.BaseUrl, 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"}, @@ -91,15 +86,11 @@ func (r *BlogPageHandler) AddLinkedData(c *fiber.Ctx, supplements *router.Supple if err != nil { return nil, fmt.Errorf("failed to read frontmatter of the desired blog post: %w", err) } - title := metadata.Title - if metadata.Medley != "" { - title += " // " + l10n.T.GetPath(lang, "Medleys", metadata.Medley).(string) - } return map[string]any{ "@context": "https://schema.org", "@type": "Article", - "headline": title, + "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), @@ -111,11 +102,10 @@ func (r *BlogPageHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplemen if err != nil { return fiber.StatusBadRequest, fmt.Errorf("failed to get path from referer: %w", err) } - title := pathParts[2] - metadata, parsedMarkdown, err := readBlogPost(supplements.MarkdownRenderer, supplements.BlogClient, lang+"/"+title) + metadata, parsedMarkdown, err := readBlogPost(supplements.MarkdownRenderer, supplements.BlogClient, lang+"/"+pathParts[2]) if err != nil { - return fiber.StatusNotFound, fmt.Errorf("failed to find '%s' post: %w", title, err) + return fiber.StatusNotFound, fmt.Errorf("failed to find '%s' post: %w", pathParts[2], err) } geolocationParts := strings.Split(metadata.Geolocation, " ") @@ -132,15 +122,12 @@ func (r *BlogPageHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplemen templateMap["MapLocationY"] = y templateMap["MapLocationAreaMeters"] = areaError templateMap["Title"] = metadata.Title - templateMap["Codename"] = title templateMap["ParsedMarkdown"] = template.HTML(parsedMarkdown) templateMap["PublishedDate"] = metadata.PublishedTime.Format("2006-01-02 15:04:05 -07:00") templateMap["ActionDate"] = metadata.ActionDate templateMap["ShortDescription"] = metadata.ShortDescription - templateMap["Thumbnail"] = metadata.Thumbnail - templateMap["Medley"] = metadata.Medley - go supplements.ClientCache.View(c.IP(), title) + go supplements.ClientCache.View(c.IP(), pathParts[2]) return fiber.StatusOK, nil } @@ -156,13 +143,7 @@ func (r *BlogPageHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplem return fiber.StatusNotFound, fmt.Errorf("could not read '%s' for metadata: %w", path, err) } - pageTitle := metadata.Title - if metadata.Medley != "" { - pageTitle += " // " + l10n.T.GetPath(lang, "Medleys", metadata.Medley).(string) - } - templateMap["Title"] = metadata.Title - templateMap["PageTitle"] = pageTitle return fiber.StatusOK, nil } diff --git a/internal/router/handlers/lang-blog.go b/internal/router/handlers/lang-blog.go index c31c2eb..79ec9f3 100644 --- a/internal/router/handlers/lang-blog.go +++ b/internal/router/handlers/lang-blog.go @@ -11,7 +11,6 @@ import ( "github.com/SayaAndy/saya-today-web/internal/blog" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -58,16 +57,18 @@ func (r *CatalogueHandler) SitemapInfo(supplements *router.Supplements) []router 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: l10n.T.GetPath(lang, "BlogSearch", "Header").(string)}, - {Property: "og:description", Content: l10n.T.GetPath(lang, "BlogSearch", "Description").(string)}, + {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", "publicationDateDesc") - previousBlogPage := c.Query("codename") + querySort := c.Query("sort") + if querySort == "" { + querySort = "publicationDateDesc" + } encodedQuery := c.Request().URI().QueryString() decodedQuery, _ := url.QueryUnescape(string(encodedQuery)) @@ -86,14 +87,13 @@ func (r *CatalogueHandler) RenderBody(c *fiber.Ctx, supplements *router.Suppleme templateMap["Tags"] = tagsArray templateMap["QuerySort"] = querySort templateMap["QueryTags"] = strings.Join(queryTags, ",") - templateMap["Title"] = l10n.T.GetPath(lang, "BlogSearch", "Header").(string) - templateMap["PreviousBlogPage"] = previousBlogPage + templateMap["Title"] = supplements.Localization[lang].BlogSearch.Header return fiber.StatusOK, nil } func (r *CatalogueHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = l10n.T.GetPath(lang, "BlogSearch", "Header").(string) + templateMap["Title"] = supplements.Localization[lang].BlogSearch.Header return fiber.StatusOK, nil } diff --git a/internal/router/handlers/lang-map.go b/internal/router/handlers/lang-map.go index 32ab35e..d452bc7 100644 --- a/internal/router/handlers/lang-map.go +++ b/internal/router/handlers/lang-map.go @@ -1,6 +1,13 @@ package handlers import ( + "fmt" + "log/slog" + "slices" + "strconv" + "strings" + + "github.com/SayaAndy/saya-today-web/internal/blog" "github.com/SayaAndy/saya-today-web/internal/router" "github.com/gofiber/fiber/v2" ) @@ -42,5 +49,61 @@ func (r *MapHandler) SitemapInfo(supplements *router.Supplements) []router.Sitem } func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - return fiber.StatusOK, nil + pages, err := supplements.BlogClient.Scan(lang + "/") + slices.SortFunc(pages, func(a *blog.Page, b *blog.Page) int { + return a.Metadata.PublishedTime.Compare(b.Metadata.PublishedTime) + }) + status := fiber.StatusOK + if err != nil { + slog.Error("received an error while scanning b2 pages", + slog.String("error", err.Error()), + slog.String("lang", lang), + ) + status = fiber.StatusPartialContent + pages = []*blog.Page{} + } + + type MapMarker struct { + Index int `json:"Index"` + Title string `json:"Title"` + PageLink string `json:"PageLink"` + Lat float64 `json:"Lat"` + Long float64 `json:"Long"` + AccuracyMeters int64 `json:"AccuracyMeters"` + Thumbnail string `json:"Thumbnail"` + } + + mapMarkers := make([]*MapMarker, 0, len(pages)) + for i, page := range pages { + geolocationParts := strings.Split(page.Metadata.Geolocation, " ") + if len(geolocationParts) < 2 { + continue + } + + var x, y float64 + var areaError int64 + if len(geolocationParts) >= 2 { + x, _ = strconv.ParseFloat(geolocationParts[0], 64) + y, _ = strconv.ParseFloat(geolocationParts[1], 64) + } + if len(geolocationParts) >= 3 { + areaError, _ = strconv.ParseInt(geolocationParts[2], 10, 64) + } + + mapMarkers = append(mapMarkers, &MapMarker{ + Index: i, + Title: page.Metadata.Title, + PageLink: fmt.Sprintf("/%s/blog/%s", lang, page.FileName), + Lat: x, + Long: y, + AccuracyMeters: areaError, + Thumbnail: page.Metadata.Thumbnail, + }) + } + + templateMap["MapMarkers"] = mapMarkers + templateMap["MapLocationLat"] = 45.4507 + templateMap["MapLocationLong"] = 68.8319 + + return status, nil } diff --git a/internal/router/handlers/lang-user-unsubscribe.go b/internal/router/handlers/lang-user-unsubscribe.go index e22d854..4fbd244 100644 --- a/internal/router/handlers/lang-user-unsubscribe.go +++ b/internal/router/handlers/lang-user-unsubscribe.go @@ -4,7 +4,6 @@ import ( "log/slog" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -40,24 +39,24 @@ func (r *UnsubscribeHandler) Render(c *fiber.Ctx, supplements *router.Supplement if unsubscribeCode == "" { statusColor = "0, 0, 255" statusEmoji = "(╭ರ_•́)" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "UnsetCode").(string) + statusText = supplements.Localization[lang].UnsubscribePage.UnsetCode status = fiber.ErrBadRequest.Code } else if clientError, serverError := supplements.Mailer.Unsubscribe(unsubscribeCode); clientError != nil { slog.Info("got a client error when unsubscribing", slog.String("error", clientError.Error())) statusColor = "255, 0, 0" statusEmoji = "(͠≖~≖ ͡ )" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "InvalidCode").(string) + statusText = supplements.Localization[lang].UnsubscribePage.InvalidCode status = fiber.ErrBadRequest.Code } else if serverError != nil { slog.Error("got a server error when unsubscribing", slog.String("error", serverError.Error())) statusColor = "255, 128, 0" statusEmoji = "( ˶°ㅁ°) !!" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "OnServerError").(string) + statusText = supplements.Localization[lang].UnsubscribePage.OnServerError status = fiber.ErrInternalServerError.Code } else { statusColor = "0, 255, 0" statusEmoji = "♡⸜(˶˃ ᵕ ˂˶)⸝♡" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "Success").(string) + statusText = supplements.Localization[lang].UnsubscribePage.Success status = fiber.StatusOK } diff --git a/internal/router/handlers/lang-user.go b/internal/router/handlers/lang-user.go index 085a9aa..0dda529 100644 --- a/internal/router/handlers/lang-user.go +++ b/internal/router/handlers/lang-user.go @@ -6,7 +6,6 @@ import ( "github.com/SayaAndy/saya-today-web/internal/mailer" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -41,15 +40,15 @@ func (r *UserHandler) ToValidateLang() router.LangSetting { 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: l10n.T.GetPath(lang, "UserProfile", "Header").(string)}, - {Property: "og:description", Content: l10n.T.GetPath(lang, "UserProfile", "Description").(string)}, + {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 } func (r *UserHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = l10n.T.GetPath(lang, "UserProfile", "Header").(string) + templateMap["Title"] = supplements.Localization[lang].UserProfile.Header email, _, err := supplements.Mailer.GetInfo(supplements.Mailer.GetHash(c.IP())) if err != nil { @@ -84,6 +83,6 @@ func (r *UserHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, } func (r *UserHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = l10n.T.GetPath(lang, "UserProfile", "Header").(string) + templateMap["Title"] = supplements.Localization[lang].UserProfile.Header return fiber.StatusOK, nil } diff --git a/internal/router/handlers/lang.go b/internal/router/handlers/lang.go index cc0b33b..25b5036 100644 --- a/internal/router/handlers/lang.go +++ b/internal/router/handlers/lang.go @@ -6,7 +6,6 @@ import ( "time" "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" "github.com/gofiber/fiber/v2" ) @@ -52,8 +51,8 @@ func (r *HomeHandler) SitemapInfo(supplements *router.Supplements) []router.Site 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: l10n.T.GetPath(lang, "HomePage", "Header").(string)}, - {Property: "og:description", Content: l10n.T.GetPath(lang, "HomePage", "HomePageDescription").(string)}, + {Property: "og:title", Content: supplements.Localization[lang].HomePage.Header}, + {Property: "og:description", Content: supplements.Localization[lang].HomePage.HomePageDescription}, {Property: "og:image", Content: fmt.Sprintf( supplements.PhotoStorage.HomePageGifs.BaseUrl, supplements.PhotoStorage.HomePageGifs.Indexes[rand.Int()%len(supplements.PhotoStorage.HomePageGifs.Indexes)], @@ -64,7 +63,7 @@ func (r *HomeHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lan } func (r *HomeHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = l10n.T.GetPath(lang, "HomePage", "Header").(string) + templateMap["Title"] = supplements.Localization[lang].HomePage.Header templateMap["FilledHeartCount"] = uint(40) templateMap["OutlineHeartCount"] = uint(40) templateMap["GifUrl"] = fmt.Sprintf( @@ -76,6 +75,6 @@ func (r *HomeHandler) RenderBody(c *fiber.Ctx, supplements *router.Supplements, } func (r *HomeHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = l10n.T.GetPath(lang, "HomePage", "Header").(string) + templateMap["Title"] = supplements.Localization[lang].HomePage.Header return fiber.StatusOK, nil } diff --git a/internal/router/handlers/root.go b/internal/router/handlers/root.go index 4fc4e31..a8890fa 100644 --- a/internal/router/handlers/root.go +++ b/internal/router/handlers/root.go @@ -47,8 +47,8 @@ func (r *RootHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lan {Property: "og:url", Content: fmt.Sprint(templateMap["CanonicalEndpoint"]) + "/"}, {Property: "og:type", Content: "website"}, } - for _, field := range supplements.Meta { - meta = append(meta, router.MetaField{Name: field.Name, Content: field.Value}) + if supplements.Meta.GoogleSiteVerification != "" { + meta = append(meta, router.MetaField{Name: "google-site-verification", Content: supplements.Meta.GoogleSiteVerification}) } return meta, nil } diff --git a/internal/router/router.go b/internal/router/router.go index a11a934..5230e74 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -7,11 +7,8 @@ import ( "fmt" "html/template" "log/slog" - "net" "net/url" - "os" "slices" - "strconv" "strings" "time" @@ -23,6 +20,7 @@ import ( "github.com/SayaAndy/saya-today-web/internal/mailer" "github.com/SayaAndy/saya-today-web/internal/tailwind" "github.com/SayaAndy/saya-today-web/internal/templatemanager" + "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" @@ -94,6 +92,7 @@ type Route interface { type Supplements struct { DB *sql.DB BlogClient blog.Client + Localization map[string]*locale.LocaleConfig AvailableLanguages []config.AvailableLanguageConfig ClientCache *ClientCache PageCache *ristretto.Cache[string, []byte] @@ -102,9 +101,9 @@ type Supplements struct { BlogTrigger *blogtrigger.BlogTriggerScheduler TemplateManager *templatemanager.TemplateManager MarkdownRenderer goldmark.Markdown - Meta []config.MetaConfig + Meta config.MetaConfig PhotoStorage config.PhotoStorageConfig - StaticStorage config.StaticStorageConfig + StaticStorage config.PhotoTypeConfig } type Router struct { @@ -113,7 +112,6 @@ type Router struct { templatedRoutes map[string]map[string]Route templatedPathMatcher *PathMatcher canonicalEndpoint string - endpoint config.EndpointConfig } func NewRouter(cfg *config.Config) (*Router, error) { @@ -147,7 +145,16 @@ func NewRouter(cfg *config.Config) (*Router, error) { supplements.BlogClient, err = blog.NewClientMap[cfg.BlogPages.Storage.Type](&cfg.BlogPages.Storage) if err != nil { - return nil, fmt.Errorf("fail to initialize blog client: type %s: %w", cfg.BlogPages.Storage.Type, err) + return nil, fmt.Errorf("fail to initialize b2 client: %w", err) + } + + supplements.Localization = make(map[string]*locale.LocaleConfig, len(cfg.AvailableLanguages)) + for _, lang := range cfg.AvailableLanguages { + localeCfg, err := locale.InitConfig(cfg.LocalePath + lang.LocFile) + if err != nil { + return nil, fmt.Errorf("fail to initialize a locale: %w", err) + } + supplements.Localization[lang.Name] = localeCfg } supplements.MarkdownRenderer = goldmark.New( @@ -189,7 +196,7 @@ func NewRouter(cfg *config.Config) (*Router, error) { } supplements.Mailer, err = mailer.NewMailer(supplements.DB, cfg.Mail.ClientHost, cfg.Mail.MailHost, - cfg.Mail.PublicName, cfg.Mail.MailAddress, cfg.Mail.Username, cfg.Mail.Password, []byte(cfg.Mail.Salt)) + cfg.Mail.PublicName, cfg.Mail.MailAddress, cfg.Mail.Username, cfg.Mail.Password, []byte(cfg.Mail.Salt), supplements.Localization) if err != nil { return nil, fmt.Errorf("fail to initialize mailer: %w", err) } @@ -249,7 +256,7 @@ func NewRouter(cfg *config.Config) (*Router, error) { templatedRoutes := make(map[string]map[string]Route) templatedPathMatcher := NewPathMatcher() - return &Router{supplements, app, templatedRoutes, templatedPathMatcher, cfg.CanonicalEndpoint, cfg.Endpoint}, nil + return &Router{supplements, app, templatedRoutes, templatedPathMatcher, cfg.CanonicalEndpoint}, nil } func (r *Router) InitRoutes() (err error) { @@ -298,17 +305,16 @@ func (r *Router) InitRoutes() (err error) { cacheKey = fmt.Sprintf("%s.full-page.%s", method, trimmedPath) case ByUrlAndQuery: cacheKey = fmt.Sprintf("%s.full-page.%s.%s", method, trimmedPath, queryString) - case Disabled: - c.Set("Cache-Control", "no-store, no-cache, must-revalidate") } defaultMap := fiber.Map{ - "Lang": lang, - "Path": trimmedPath, - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "Path": trimmedPath, + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } statusCode, err := currentRoute.Render(c, r.supplements, lang, defaultMap) @@ -361,7 +367,6 @@ func (r *Router) InitRoutes() (err error) { c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("unknown segment '%s'", part)) } - c.Set("Cache-Control", "no-store, no-cache, must-revalidate") err := r.generalPageSegment(c, part) return err }) @@ -377,35 +382,10 @@ func (r *Router) InitRoutes() (err error) { return nil } -func (r *Router) Listen() error { - switch r.endpoint.Type { - case "unix": - unixConfig := r.endpoint.Config.(*config.UnixConfig) - endpoint, _ := strings.CutPrefix(unixConfig.Path, "unix://") - - if err := os.Remove(endpoint); err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("error while cleaning up existing unix socket: %w", err) - } - - ln, err := net.Listen("unix", endpoint) - if err != nil { - return fmt.Errorf("error while initializing unix listener: %w", err) - } - chmod, _ := strconv.ParseUint(unixConfig.Chmod[1:], 8, 32) - os.Chmod(unixConfig.Path, os.FileMode(chmod)) - if err := r.app.Listener(ln); err != nil { - return fmt.Errorf("error while running fiber server: %w", err) - } - case "http": - httpConfig := r.endpoint.Config.(*config.HttpConfig) - fmt.Print(httpConfig) - if err := r.app.Listen(httpConfig.ListenOn); err != nil { - return fmt.Errorf("error while running fiber server: %w", err) - } - default: - return fmt.Errorf("error with initializing fiber server: invalid endpoint type (supported are unix and http)") +func (r *Router) Listen(endpoint string) error { + if err := r.app.Listen(endpoint); err != nil { + return fmt.Errorf("error while running fiber server: %w", err) } - return nil } @@ -429,7 +409,6 @@ func (r *Router) Close() (err error) { } slog.Debug("closing page cache") r.supplements.PageCache.Close() - return errors.Join(allErrors...) } @@ -439,8 +418,8 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { path := c.Path() method := c.Method() trimmedPath := strings.Trim(path, "/") - queryString := c.Request().URI().QueryString() cacheKey := "" + queryString := c.Request().URI().QueryString() switch route.ToCache() { case ByUrlOnly: @@ -455,12 +434,12 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { } valueMap := fiber.Map{ - "Lang": lang, - "Path": trimmedPath, - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } var err error @@ -477,52 +456,10 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { valueMap["LinkedData"] = template.JS(ldBytes) } - syntheticReferer := path - if len(queryString) > 0 { - syntheticReferer += "?" + string(queryString) - } - c.Request().Header.Set("Referer", syntheticReferer) - - parts := []struct { - name string - key string - render func(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (int, error) - }{ - {"top-embeds", "RenderedTopEmbeds", route.RenderTopEmbeds}, - {"header", "RenderedHeader", route.RenderHeader}, - {"body", "RenderedBody", route.RenderBody}, - {"footer", "RenderedFooter", route.RenderFooter}, - {"bottom-embeds", "RenderedBottomEmbeds", route.RenderBottomEmbeds}, - } - - for _, p := range parts { - statusCode, err := p.render(c, r.supplements, lang, valueMap) - if err != nil { - slog.Error("failed to render segment for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(statusCode).SendString(err.Error()) - } - segContent, err := r.supplements.TemplateManager.Render("general-page-"+p.name, valueMap, route.TemplatesToInject()...) - if err != nil { - slog.Error("failed to render segment template for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate segment") - } - valueMap[p.key] = template.HTML(segContent) - } - content, err := r.supplements.TemplateManager.Render("general-page", valueMap) if err != nil { - slog.Warn("failed to generate full page", slog.String("path", path), slog.String("error", err.Error())) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate full page") + 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") } go r.supplements.PageCache.SetWithTTL(cacheKey, content, int64(len(content)), route.CacheDuration()) @@ -563,12 +500,11 @@ func (r *Router) generalPageSegment(c *fiber.Ctx, part string) error { cacheKey := "" trimmedPath := strings.Trim(path, "/") - requestQuery := string(c.Request().URI().QueryString()) switch route.ToCache() { case ByUrlOnly: cacheKey = fmt.Sprintf("%s.%s.%s", method, part, trimmedPath) case ByUrlAndQuery: - cacheKey = fmt.Sprintf("%s.%s.%s.%s.%s", method, part, trimmedPath, queryString, requestQuery) + cacheKey = fmt.Sprintf("%s.%s.%s.%s", method, part, trimmedPath, queryString) } if route.ToCache() != Disabled { @@ -580,12 +516,13 @@ func (r *Router) generalPageSegment(c *fiber.Ctx, part string) error { var statusCode int defaultMap := fiber.Map{ - "Lang": lang, - "Path": strings.Trim(path, "/"), - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "Path": strings.Trim(path, "/"), + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } switch part { @@ -677,7 +614,8 @@ func (r *Router) getAndValidateLang(c *fiber.Ctx, langSetting LangSetting, defau return lang, nil } } - return "", fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + return "", c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) } func GetPathFromReferer(c *fiber.Ctx) (path string, pathParts []string, queryString string, err error) { |