Diffstat (limited to 'internal/router/handlers')
18 files changed, 0 insertions, 1554 deletions
diff --git a/internal/router/handlers/api-v1-blog-search.go b/internal/router/handlers/api-v1-blog-search.go deleted file mode 100644 index b59f09a..0000000 --- a/internal/router/handlers/api-v1-blog-search.go +++ /dev/null @@ -1,145 +0,0 @@ -package handlers - -import ( - "cmp" - "encoding/json" - "fmt" - "log/slog" - "net/url" - "regexp" - "slices" - "time" - - "github.com/SayaAndy/saya-today-web/internal/blog" - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type BlogSearchHandler struct { - router.BasicHandler - getTagsQuery *regexp.Regexp -} - -func init() { - router.Routes = append(router.Routes, &BlogSearchHandler{getTagsQuery: regexp.MustCompile(`tags\[\]=([\w]+)`)}) -} - -func (r *BlogSearchHandler) Filter() (method string, path string) { - return "GET", "/api/v1/blog-search" -} - -func (r *BlogSearchHandler) IsTemplated() bool { - return false -} - -func (r *BlogSearchHandler) TemplatesToInject() []string { - return []string{"views/partials/catalogue-blog-cards.html", "views/partials/catalogue-blog-card-tags.html"} -} - -func (r *BlogSearchHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *BlogSearchHandler) ToValidateLang() router.LangSetting { - return router.InForm -} - -func (r *BlogSearchHandler) RateLimiter() *fiber.Handler { - return &router.RateLimiterLoose -} - -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 { - loc = time.UTC - slog.Warn("unable to parse a client timezone, defaulting to UTC", slog.String("error", err.Error()), slog.String("tz", tz)) - } - - cacheKey := "blog-search." + lang + ".pages-list" - - var pages []*blog.Page - if pagesBytes, ok := supplements.PageCache.Get(cacheKey); pagesBytes != nil || ok { - json.Unmarshal(pagesBytes, &pages) - } else { - pages, err = supplements.BlogClient.Scan(lang + "/") - if err != nil { - return fiber.StatusInternalServerError, fmt.Errorf("failed to scan pages for '%s' lang: %w", lang, err) - } - pagesBytes, _ := json.Marshal(pages) - supplements.PageCache.SetWithTTL(cacheKey, pagesBytes, int64(len(pagesBytes)), r.CacheDuration()) - } - - encodedQuery := c.Request().URI().QueryString() - decodedQuery, _ := url.QueryUnescape(string(encodedQuery)) - matches := r.getTagsQuery.FindAllStringSubmatch(decodedQuery, -1) - - tags := make([]string, 0, len(matches)) - for _, match := range matches { - tags = append(tags, string(match[1])) - } - - pageMeta := make([]fiber.Map, 0, len(pages)) - 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 - } - } - } - } - - slices.SortFunc(pageMeta, func(a, b fiber.Map) int { - switch sort { - case "titleAsc": - return cmp.Compare(a["Title"].(string), b["Title"].(string)) - case "titleDesc": - return cmp.Compare(b["Title"].(string), a["Title"].(string)) - case "actionDateAsc": - return cmp.Compare(a["ActionDate"].(string), b["ActionDate"].(string)) - case "actionDateDesc": - return cmp.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)) - return publishedTimeA.Compare(publishedTimeB) - case "publicationDateDesc": - 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 deleted file mode 100644 index b8ae162..0000000 --- a/internal/router/handlers/api-v1-email-is-in-verification.go +++ /dev/null @@ -1,60 +0,0 @@ -package handlers - -import ( - "fmt" - "html/template" - "strings" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" - "github.com/gofiber/fiber/v2" -) - -type OngoingVerificationHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &OngoingVerificationHandler{}) -} - -func (r *OngoingVerificationHandler) Filter() (method string, path string) { - return "GET", "/api/v1/email/is-in-verification" -} - -func (r *OngoingVerificationHandler) IsTemplated() bool { - return false -} - -func (r *OngoingVerificationHandler) TemplatesToInject() []string { - return []string{"views/partials/personal-page-status.html"} -} - -func (r *OngoingVerificationHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *OngoingVerificationHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *OngoingVerificationHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - isAllowed, whenAllowed, codeExpiry := supplements.Mailer.IsAllowedToRetryVerification(c.IP()) - - sterileDataset := make(map[string]any) - sterileDataset["code-expiry-time"] = template.HTMLAttr(fmt.Sprintf("data-code-expiry-time=\"%d\"", codeExpiry.UnixMilli())) - - templateMap["StatusId"] = "email-message" - templateMap["DataAttributes"] = sterileDataset - - 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")) - } else { - templateMap["Status"] = "OK" - templateMap["Message"] = "" - } - - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/api-v1-email-send-verification-code.go b/internal/router/handlers/api-v1-email-send-verification-code.go deleted file mode 100644 index ea8df4d..0000000 --- a/internal/router/handlers/api-v1-email-send-verification-code.go +++ /dev/null @@ -1,102 +0,0 @@ -package handlers - -import ( - "fmt" - "html/template" - "log/slog" - "strings" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" - "github.com/gofiber/fiber/v2" -) - -type SendVerificationCodeHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &SendVerificationCodeHandler{}) -} - -func (r *SendVerificationCodeHandler) Filter() (method string, path string) { - return "POST", "/api/v1/email/send-verification-code" -} - -func (r *SendVerificationCodeHandler) IsTemplated() bool { - return false -} - -func (r *SendVerificationCodeHandler) TemplatesToInject() []string { - return []string{"views/partials/personal-page-status.html"} -} - -func (r *SendVerificationCodeHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *SendVerificationCodeHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *SendVerificationCodeHandler) RateLimiter() *fiber.Handler { - return &router.RateLimiterStrict -} - -func (r *SendVerificationCodeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - id := c.IP() - templateMap["StatusId"] = "email-message" - - email := c.FormValue("email") - if email == "" { - templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "EmailEmpty").(string) - 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) - 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) - 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["DataAttributes"] = map[string]any{ - "striked-end-time": template.HTMLAttr(fmt.Sprintf("data-striked-end-time=\"%d\"", whenAllowed.UnixMilli())), - } - return fiber.StatusUnprocessableEntity, nil - } - - if previousEmail, _, _ := supplements.Mailer.GetInfo(supplements.Mailer.GetHash(id)); previousEmail == email { - templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "EmailAlreadyValidated").(string) - 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) - 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["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())), - } - - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/api-v1-email-verify.go b/internal/router/handlers/api-v1-email-verify.go deleted file mode 100644 index 65ad6d9..0000000 --- a/internal/router/handlers/api-v1-email-verify.go +++ /dev/null @@ -1,67 +0,0 @@ -package handlers - -import ( - "html/template" - "log/slog" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" - "github.com/gofiber/fiber/v2" -) - -type VerifyCodeHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &VerifyCodeHandler{}) -} - -func (r *VerifyCodeHandler) Filter() (method string, path string) { - return "POST", "/api/v1/email/verify" -} - -func (r *VerifyCodeHandler) IsTemplated() bool { - return false -} - -func (r *VerifyCodeHandler) TemplatesToInject() []string { - return []string{"views/partials/personal-page-status.html"} -} - -func (r *VerifyCodeHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *VerifyCodeHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *VerifyCodeHandler) RateLimiter() *fiber.Handler { - return &router.RateLimiterStrict -} - -func (r *VerifyCodeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - verificationCode := c.FormValue("email_code") - templateMap["StatusId"] = "verification-message" - - if verificationCode == "" { - templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationEmpty").(string) - 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) - return fiber.StatusUnprocessableEntity, nil - } - - templateMap["Status"] = "OK" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "VerificationSuccess").(string) - templateMap["DataAttributes"] = map[string]any{ - "hide-verification-panel": template.HTMLAttr("data-code-expiry-time=\"true\""), - } - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/api-v1-like-get.go b/internal/router/handlers/api-v1-like-get.go deleted file mode 100644 index 9e2c856..0000000 --- a/internal/router/handlers/api-v1-like-get.go +++ /dev/null @@ -1,63 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type GetLikeHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &GetLikeHandler{}) -} - -func (r *GetLikeHandler) Filter() (method string, path string) { - return "GET", "/api/v1/like" -} - -func (r *GetLikeHandler) IsTemplated() bool { - return false -} - -func (r *GetLikeHandler) TemplatesToInject() []string { - return []string{"views/partials/blog-page-like-button.html"} -} - -func (r *GetLikeHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *GetLikeHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *GetLikeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - path, pathParts, _, err := router.GetPathFromReferer(c) - if err != nil { - return fiber.StatusBadRequest, fmt.Errorf("error getting path from referer: %w", err) - } - - if len(pathParts) != 3 || pathParts[1] != "blog" { - return fiber.StatusBadRequest, fmt.Errorf("invalid path format: expected '/:lang/blog/:page', got '%s'", path) - } - - page := pathParts[2] - pageLink := lang + "/" + page + ".md" - if pages, _ := supplements.BlogClient.Scan(pageLink); len(pages) == 0 { - return fiber.StatusNotFound, fmt.Errorf("server did not find '%s' article", pageLink) - } - - ip := c.IP() - likeStatus := supplements.ClientCache.GetLikeStatus(ip, page) - - slog.Debug("someone requested the like status!", slog.String("ip", ip), slog.String("page", page), slog.Bool("like_status", likeStatus)) - templateMap["Liked"] = likeStatus - templateMap["LikedCount"] = supplements.ClientCache.GetLikeCount(page) - - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/api-v1-like-put.go b/internal/router/handlers/api-v1-like-put.go deleted file mode 100644 index 8305d2b..0000000 --- a/internal/router/handlers/api-v1-like-put.go +++ /dev/null @@ -1,78 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - "strconv" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type PutLikeHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &PutLikeHandler{}) -} - -func (r *PutLikeHandler) Filter() (method string, path string) { - return "PUT", "/api/v1/like" -} - -func (r *PutLikeHandler) IsTemplated() bool { - return false -} - -func (r *PutLikeHandler) TemplatesToInject() []string { - return []string{"views/partials/blog-page-like-button.html"} -} - -func (r *PutLikeHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *PutLikeHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *PutLikeHandler) RateLimiter() *fiber.Handler { - return &router.RateLimiterMedium -} - -func (r *PutLikeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - path, pathParts, _, err := router.GetPathFromReferer(c) - if err != nil { - return fiber.StatusBadRequest, fmt.Errorf("error getting path from referer: %w", err) - } - - if len(pathParts) != 3 && pathParts[1] != "blog" { - return fiber.StatusBadRequest, fmt.Errorf("invalid path format: expected '/:lang/blog/:page', got '%s'", path) - } - - page := pathParts[2] - pageLink := lang + "/" + page + ".md" - if pages, _ := supplements.BlogClient.Scan(pageLink); len(pages) == 0 { - return fiber.StatusNotFound, fmt.Errorf("server did not find '%s' article", pageLink) - } - - newLikeStatus, err := strconv.ParseBool(c.FormValue("like", "true")) - if err != nil { - return fiber.StatusBadRequest, fmt.Errorf("invalid like value '%s'", c.FormValue("like")) - } - - ip := c.IP() - if newLikeStatus { - supplements.ClientCache.LikeOn(ip, page) - } else { - supplements.ClientCache.LikeOff(ip, page) - } - - slog.Debug("someone pressed the like button!", slog.String("ip", ip), slog.String("page", page), slog.String("new_like_status", fmt.Sprint(newLikeStatus))) - templateMap["Liked"] = newLikeStatus - templateMap["LikedCount"] = supplements.ClientCache.GetLikeCount(page) - templateMap["StatusId"] = "email-message" - - return fiber.StatusOK, nil -} 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 deleted file mode 100644 index 12dc5cf..0000000 --- a/internal/router/handlers/api-v1-subs-put.go +++ /dev/null @@ -1,70 +0,0 @@ -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" -) - -type PutSubsHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &PutSubsHandler{}) -} - -func (r *PutSubsHandler) Filter() (method string, path string) { - return "PUT", "/api/v1/subs" -} - -func (r *PutSubsHandler) IsTemplated() bool { - return false -} - -func (r *PutSubsHandler) TemplatesToInject() []string { - return []string{"views/partials/personal-page-status.html"} -} - -func (r *PutSubsHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *PutSubsHandler) ToValidateLang() router.LangSetting { - return router.InReferer -} - -func (r *PutSubsHandler) RateLimiter() *fiber.Handler { - return &router.RateLimiterMedium -} - -func (r *PutSubsHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["StatusId"] = "subs-message" - - subscriptionType := c.FormValue("tags") - var subscriptionTypeEnum mailer.SubscriptionType - switch subscriptionType { - case "all": - subscriptionTypeEnum = mailer.All - case "none": - subscriptionTypeEnum = mailer.None - case "specific": - subscriptionTypeEnum = mailer.Specific - default: - templateMap["Status"] = "Failed" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "SubscribeInvalidType").(string) - 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) - return fiber.StatusUnprocessableEntity, nil - } - - templateMap["Status"] = "OK" - templateMap["Message"] = l10n.T.GetPath(lang, "UserProfile", "SubscribedSuccessfully").(string) - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/api-v1-tz-get.go b/internal/router/handlers/api-v1-tz-get.go deleted file mode 100644 index 8b08113..0000000 --- a/internal/router/handlers/api-v1-tz-get.go +++ /dev/null @@ -1,65 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - "time" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type GetTimezoneHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &GetTimezoneHandler{}) -} - -func (r *GetTimezoneHandler) Filter() (method string, path string) { - return "GET", "/api/v1/tz" -} - -func (r *GetTimezoneHandler) IsTemplated() bool { - return false -} - -func (r *GetTimezoneHandler) TemplatesToInject() []string { - return []string{} -} - -func (r *GetTimezoneHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *GetTimezoneHandler) ToValidateLang() router.LangSetting { - return router.NotRequired -} - -func (r *GetTimezoneHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - timestampString := c.Query("timestamp") - tz := c.Query("tz") - - loc, err := time.LoadLocation(tz) - if err != nil { - loc = time.UTC - slog.Warn("unable to parse a client timezone, defaulting to UTC", slog.String("error", err.Error()), slog.String("tz", tz)) - } - - var format string - var timestamp time.Time - for _, format = range []string{"2006-01-02 15:04:05 -07:00", time.RFC3339} { - if timestamp, err = time.Parse(format, timestampString); err == nil { - break - } - } - - if err != nil { - return fiber.StatusBadRequest, fmt.Errorf("invalid timestamp format for '%s'", timestampString) - } - - templateMap["Output"] = []byte(timestamp.In(loc).Format("2006-01-02 15:04:05 -07:00")) - - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/lang-blog-title.go b/internal/router/handlers/lang-blog-title.go deleted file mode 100644 index 76dc335..0000000 --- a/internal/router/handlers/lang-blog-title.go +++ /dev/null @@ -1,182 +0,0 @@ -package handlers - -import ( - "bytes" - "fmt" - "html/template" - "strings" - "time" - - "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" -) - -func init() { - router.Routes = append(router.Routes, &BlogPageHandler{}) -} - -type BlogPageHandler struct { - router.BasicHandler -} - -func (r *BlogPageHandler) Filter() (method string, path string) { - return "GET", "/:lang/blog/:title" -} - -func (r *BlogPageHandler) IsTemplated() bool { - return true -} - -func (r *BlogPageHandler) TemplatesToInject() []string { - return []string{"views/pages/blog-page.html"} -} - -func (r *BlogPageHandler) ToCache() router.CacheSetting { - return router.ByUrlOnly -} - -func (r *BlogPageHandler) CacheDuration() time.Duration { - return 15 * time.Minute -} - -func (r *BlogPageHandler) ToValidateLang() router.LangSetting { - return router.InPath -} - -func (r *BlogPageHandler) SitemapInfo(supplements *router.Supplements) []router.SitemapInfo { - sitemapInfo := []router.SitemapInfo{} - - pages, err := supplements.BlogClient.Scan("") - if err != nil { - return sitemapInfo - } - - for _, page := range pages { - sitemapInfo = append(sitemapInfo, router.SitemapInfo{ - Loc: "/" + page.Lang + "/blog/" + page.FileName, - LastModified: page.ModifiedTime, - Priority: 1.0, - }) - } - - return sitemapInfo -} - -func (r *BlogPageHandler) AddMeta(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (meta []router.MetaField, err error) { - metadata, _, err := supplements.BlogClient.ReadFrontmatter(lang + "/" + c.Params("title") + ".md") - 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:description", Content: fmt.Sprintf("%s [%s]", metadata.ShortDescription, metadata.ActionDate)}, - {Property: "og:image", Content: fmt.Sprintf(supplements.PhotoStorage.Thumbnail560p.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"}, - }, 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.BlogClient.ReadFrontmatter(lang + "/" + c.Params("title") + ".md") - 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, - "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 -} - -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 { - 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) - if err != nil { - return fiber.StatusNotFound, fmt.Errorf("failed to find '%s' post: %w", title, err) - } - - geolocationParts := strings.Split(metadata.Geolocation, " ") - var x, y, areaError string - if len(geolocationParts) >= 2 { - x = geolocationParts[0] - y = geolocationParts[1] - } - if len(geolocationParts) >= 3 { - areaError = geolocationParts[2] - } - - templateMap["MapLocationX"] = x - 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) - - return fiber.StatusOK, nil -} - -func (r *BlogPageHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - path, pathParts, _, err := router.GetPathFromReferer(c) - if err != nil { - return fiber.StatusBadRequest, fmt.Errorf("failed to get path from referer: %w", err) - } - - metadata, _, err := supplements.BlogClient.ReadFrontmatter(lang + "/" + pathParts[2] + ".md") - if err != nil { - 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 -} - -func readBlogPost(md goldmark.Markdown, blogClient blog.Client, sourceName string) (metadata *frontmatter.Metadata, html string, err error) { - metadata, markdown, err := blogClient.ReadFrontmatter(sourceName + ".md") - if err != nil { - return nil, "", fmt.Errorf("failed to read a frontmatter file: %w", err) - } - - var buf bytes.Buffer - if err := md.Convert(markdown, &buf); err != nil { - return nil, "", fmt.Errorf("convert source context from md to html: %w", err) - } - - return metadata, buf.String(), nil -} diff --git a/internal/router/handlers/lang-blog.go b/internal/router/handlers/lang-blog.go deleted file mode 100644 index c31c2eb..0000000 --- a/internal/router/handlers/lang-blog.go +++ /dev/null @@ -1,129 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - "net/url" - "regexp" - "slices" - "strings" - "time" - - "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" -) - -func init() { - router.Routes = append(router.Routes, &CatalogueHandler{getTagsQuery: regexp.MustCompile(`tags\[\]=([\w]+)`)}) -} - -type CatalogueHandler struct { - router.BasicHandler - getTagsQuery *regexp.Regexp -} - -func (r *CatalogueHandler) Filter() (method string, path string) { - return "GET", "/:lang/blog" -} - -func (r *CatalogueHandler) IsTemplated() bool { - return true -} - -func (r *CatalogueHandler) TemplatesToInject() []string { - return []string{"views/pages/blog-catalogue.html"} -} - -func (r *CatalogueHandler) ToCache() router.CacheSetting { - return router.ByUrlAndQuery -} - -func (r *CatalogueHandler) CacheDuration() time.Duration { - return 5 * time.Minute -} - -func (r *CatalogueHandler) ToValidateLang() router.LangSetting { - return router.InPath -} - -func (r *CatalogueHandler) SitemapInfo(supplements *router.Supplements) []router.SitemapInfo { - sitemapInfo := []router.SitemapInfo{} - for _, lang := range supplements.AvailableLanguages { - sitemapInfo = append(sitemapInfo, router.SitemapInfo{Loc: "/" + lang.Name + "/blog", Priority: 0.3}) - } - 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: l10n.T.GetPath(lang, "BlogSearch", "Header").(string)}, - {Property: "og:description", Content: l10n.T.GetPath(lang, "BlogSearch", "Description").(string)}, - {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") - - encodedQuery := c.Request().URI().QueryString() - decodedQuery, _ := url.QueryUnescape(string(encodedQuery)) - matches := r.getTagsQuery.FindAllStringSubmatch(decodedQuery, -1) - - queryTags := make([]string, 0, len(matches)) - for _, match := range matches { - queryTags = append(queryTags, string(match[1])) - } - - tagsArray, err := getTags(supplements.BlogClient, lang) - if err != nil { - return fiber.StatusInternalServerError, fmt.Errorf("failed to get the available tags") - } - - templateMap["Tags"] = tagsArray - templateMap["QuerySort"] = querySort - templateMap["QueryTags"] = strings.Join(queryTags, ",") - templateMap["Title"] = l10n.T.GetPath(lang, "BlogSearch", "Header").(string) - templateMap["PreviousBlogPage"] = previousBlogPage - - 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) - return fiber.StatusOK, nil -} - -type Tag struct { - Name string `json:"Name" yaml:"name"` - Count int `json:"Count" yaml:"count"` -} - -func getTags(blogClient blog.Client, lang string) (tags []Tag, err error) { - pages, err := blogClient.Scan(lang + "/") - if err != nil { - slog.Warn("failed to scan pages via b2", slog.String("error", err.Error())) - return nil, fmt.Errorf("failed to scan pages via b2: %w", err) - } - - tagsMap := make(map[string]int) - for _, page := range pages { - for _, tag := range page.Metadata.Tags { - tagsMap[tag]++ - } - } - slog.Debug("enlist pages for catalogue", slog.Int("tag_count", len(tagsMap)), slog.Int("page_count", len(pages)), slog.String("lang", lang)) - - tagsArray := make([]Tag, 0, len(tagsMap)) - for tag, count := range tagsMap { - tagsArray = append(tagsArray, Tag{tag, count}) - } - slices.SortFunc(tagsArray, func(a Tag, b Tag) int { - return strings.Compare(a.Name, b.Name) - }) - - return tagsArray, nil -} diff --git a/internal/router/handlers/lang-map.go b/internal/router/handlers/lang-map.go deleted file mode 100644 index 32ab35e..0000000 --- a/internal/router/handlers/lang-map.go +++ /dev/null @@ -1,46 +0,0 @@ -package handlers - -import ( - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type MapHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &MapHandler{}) -} - -func (r *MapHandler) Filter() (method string, path string) { - return "GET", "/:lang/map" -} - -func (r *MapHandler) IsTemplated() bool { - return false -} - -func (r *MapHandler) TemplatesToInject() []string { - return []string{"views/pages/global-map.html"} -} - -func (r *MapHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -func (r *MapHandler) ToValidateLang() router.LangSetting { - return router.InPath -} - -func (r *MapHandler) SitemapInfo(supplements *router.Supplements) []router.SitemapInfo { - sitemapInfo := []router.SitemapInfo{} - for _, lang := range supplements.AvailableLanguages { - sitemapInfo = append(sitemapInfo, router.SitemapInfo{Loc: "/" + lang.Name + "/map", Priority: 0.3}) - } - return sitemapInfo -} - -func (r *MapHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/lang-user-unsubscribe.go b/internal/router/handlers/lang-user-unsubscribe.go deleted file mode 100644 index e22d854..0000000 --- a/internal/router/handlers/lang-user-unsubscribe.go +++ /dev/null @@ -1,69 +0,0 @@ -package handlers - -import ( - "log/slog" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" - "github.com/gofiber/fiber/v2" -) - -type UnsubscribeHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &UnsubscribeHandler{}) -} - -func (r *UnsubscribeHandler) Filter() (method string, path string) { - return "GET", "/:lang/user/unsubscribe" -} - -func (r *UnsubscribeHandler) IsTemplated() bool { - return false -} - -func (r *UnsubscribeHandler) TemplatesToInject() []string { - return []string{"views/pages/unsubscribe-page.html"} -} - -func (r *UnsubscribeHandler) ToValidateLang() router.LangSetting { - return router.InPath -} - -func (r *UnsubscribeHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - var statusEmoji, statusText, statusColor string - var status int - - unsubscribeCode := c.FormValue("code") - if unsubscribeCode == "" { - statusColor = "0, 0, 255" - statusEmoji = "(╭ರ_•́)" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "UnsetCode").(string) - 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) - 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) - status = fiber.ErrInternalServerError.Code - } else { - statusColor = "0, 255, 0" - statusEmoji = "♡⸜(˶˃ ᵕ ˂˶)⸝♡" - statusText = l10n.T.GetPath(lang, "UnsubscribePage", "Success").(string) - status = fiber.StatusOK - } - - templateMap["StatusColor"] = statusColor - templateMap["StatusEmoji"] = statusEmoji - templateMap["StatusText"] = statusText - - return status, nil -} diff --git a/internal/router/handlers/lang-user.go b/internal/router/handlers/lang-user.go deleted file mode 100644 index 085a9aa..0000000 --- a/internal/router/handlers/lang-user.go +++ /dev/null @@ -1,89 +0,0 @@ -package handlers - -import ( - "fmt" - "log/slog" - - "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" -) - -func init() { - router.Routes = append(router.Routes, &UserHandler{}) -} - -type UserHandler struct { - router.BasicHandler -} - -func (r *UserHandler) Filter() (method string, path string) { - return "GET", "/:lang/user" -} - -func (r *UserHandler) IsTemplated() bool { - return true -} - -func (r *UserHandler) TemplatesToInject() []string { - return []string{"views/pages/user-page.html"} -} - -func (r *UserHandler) ToCache() router.CacheSetting { - return router.Disabled -} - -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 []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: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) - - email, _, err := supplements.Mailer.GetInfo(supplements.Mailer.GetHash(c.IP())) - if err != nil { - slog.Error("get info from mailer about a client", slog.String("error", err.Error())) - } - - tagsArray, err := getTags(supplements.BlogClient, lang) - if err != nil { - return fiber.ErrInternalServerError.Code, fmt.Errorf("failed to get the available tags") - } - - subscriptionType, tags, err := supplements.Mailer.GetSubscriptions(c.IP()) - if err != nil { - return fiber.ErrInternalServerError.Code, fmt.Errorf("failed to get the user subscriptions") - } - - switch subscriptionType { - case mailer.None: - templateMap["TagsPicked"] = "none" - case mailer.All: - templateMap["TagsPicked"] = "all" - case mailer.Specific: - templateMap["TagsPicked"] = "specific" - } - - templateMap["TagsPickedList"] = tags - - templateMap["Email"] = email - templateMap["EmailCode"] = c.Query("email_code") - templateMap["ExistingTags"] = tagsArray - return fiber.StatusOK, nil -} - -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) - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/lang.go b/internal/router/handlers/lang.go deleted file mode 100644 index cc0b33b..0000000 --- a/internal/router/handlers/lang.go +++ /dev/null @@ -1,81 +0,0 @@ -package handlers - -import ( - "fmt" - "math/rand/v2" - "time" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/SayaAndy/saya-today-web/l10n" - "github.com/gofiber/fiber/v2" -) - -func init() { - router.Routes = append(router.Routes, &HomeHandler{}) -} - -type HomeHandler struct { - router.BasicHandler -} - -func (r *HomeHandler) Filter() (method string, path string) { - return "GET", "/:lang<len(2)>" -} - -func (r *HomeHandler) IsTemplated() bool { - return true -} - -func (r *HomeHandler) TemplatesToInject() []string { - return []string{"views/pages/home-page.html"} -} - -func (r *HomeHandler) ToCache() router.CacheSetting { - return router.ByUrlOnly -} - -func (r *HomeHandler) CacheDuration() time.Duration { - return 10 * time.Minute -} - -func (r *HomeHandler) ToValidateLang() router.LangSetting { - return router.InPath -} - -func (r *HomeHandler) SitemapInfo(supplements *router.Supplements) []router.SitemapInfo { - sitemapInfo := []router.SitemapInfo{} - for _, lang := range supplements.AvailableLanguages { - sitemapInfo = append(sitemapInfo, router.SitemapInfo{Loc: "/" + lang.Name, Priority: 0.7}) - } - return sitemapInfo -} - -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:image", Content: fmt.Sprintf( - supplements.PhotoStorage.HomePageGifs.BaseUrl, - supplements.PhotoStorage.HomePageGifs.Indexes[rand.Int()%len(supplements.PhotoStorage.HomePageGifs.Indexes)], - )}, - {Property: "og:url", Content: fmt.Sprintf("%s/%s", templateMap["CanonicalEndpoint"], lang)}, - {Property: "og:type", Content: "website"}, - }, nil -} - -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["FilledHeartCount"] = uint(40) - templateMap["OutlineHeartCount"] = uint(40) - templateMap["GifUrl"] = fmt.Sprintf( - supplements.PhotoStorage.HomePageGifs.BaseUrl, - supplements.PhotoStorage.HomePageGifs.Indexes[rand.Int()%len(supplements.PhotoStorage.HomePageGifs.Indexes)], - ) - templateMap["FunFacts"] = supplements.FactGiver.Give(lang) - return fiber.StatusOK, nil -} - -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) - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/robots.txt.go b/internal/router/handlers/robots.txt.go deleted file mode 100644 index fc60af4..0000000 --- a/internal/router/handlers/robots.txt.go +++ /dev/null @@ -1,48 +0,0 @@ -package handlers - -import ( - "time" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type RobotsTxtHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &RobotsTxtHandler{}) -} - -func (r *RobotsTxtHandler) Filter() (method string, path string) { - return "GET", "/robots.txt" -} - -func (r *RobotsTxtHandler) IsTemplated() bool { - return false -} - -func (r *RobotsTxtHandler) TemplatesToInject() []string { - return []string{"views/pages/robots.txt"} -} - -func (r *RobotsTxtHandler) ToCache() router.CacheSetting { - return router.ByUrlOnly -} - -func (r *RobotsTxtHandler) CacheDuration() time.Duration { - return time.Hour -} - -func (r *RobotsTxtHandler) ToValidateLang() router.LangSetting { - return router.NotRequired -} - -func (r *RobotsTxtHandler) ContentType() string { - return fiber.MIMETextPlainCharsetUTF8 -} - -func (r *RobotsTxtHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/root.go b/internal/router/handlers/root.go deleted file mode 100644 index 4fc4e31..0000000 --- a/internal/router/handlers/root.go +++ /dev/null @@ -1,65 +0,0 @@ -package handlers - -import ( - "fmt" - "time" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -func init() { - router.Routes = append(router.Routes, &RootHandler{}) -} - -type RootHandler struct { - router.BasicHandler -} - -func (r *RootHandler) Filter() (method string, path string) { - return "GET", "/" -} - -func (r *RootHandler) IsTemplated() bool { - return true -} - -func (r *RootHandler) TemplatesToInject() []string { - return []string{"views/pages/language-pick.html"} -} - -func (r *RootHandler) ToCache() router.CacheSetting { - return router.ByUrlOnly -} - -func (r *RootHandler) CacheDuration() time.Duration { - return 24 * time.Hour -} - -func (r *RootHandler) SitemapInfo(_ *router.Supplements) []router.SitemapInfo { - return []router.SitemapInfo{{Loc: "/", Priority: 0.7}} -} - -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"}, - } - for _, field := range supplements.Meta { - meta = append(meta, router.MetaField{Name: field.Name, Content: field.Value}) - } - return meta, 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 - return fiber.StatusOK, nil -} - -func (r *RootHandler) RenderHeader(c *fiber.Ctx, supplements *router.Supplements, _ string, templateMap fiber.Map) (statusCode int, err error) { - templateMap["Title"] = "Choose Your Language" - return fiber.StatusOK, nil -} diff --git a/internal/router/handlers/sitemap.xml.go b/internal/router/handlers/sitemap.xml.go deleted file mode 100644 index bb1df93..0000000 --- a/internal/router/handlers/sitemap.xml.go +++ /dev/null @@ -1,79 +0,0 @@ -package handlers - -import ( - "log/slog" - "time" - - "github.com/SayaAndy/saya-today-web/internal/router" - "github.com/gofiber/fiber/v2" -) - -type SitemapXmlHandler struct { - router.BasicHandler -} - -func init() { - router.Routes = append(router.Routes, &SitemapXmlHandler{}) -} - -func (r *SitemapXmlHandler) Filter() (method string, path string) { - return "GET", "/sitemap.xml" -} - -func (r *SitemapXmlHandler) IsTemplated() bool { - return false -} - -func (r *SitemapXmlHandler) TemplatesToInject() []string { - return []string{"views/pages/sitemap.xml"} -} - -func (r *SitemapXmlHandler) ToCache() router.CacheSetting { - return router.ByUrlOnly -} - -func (r *SitemapXmlHandler) CacheDuration() time.Duration { - return time.Hour -} - -func (r *SitemapXmlHandler) ToValidateLang() router.LangSetting { - return router.NotRequired -} - -func (r *SitemapXmlHandler) ContentType() string { - return fiber.MIMEApplicationXMLCharsetUTF8 -} - -func (r *SitemapXmlHandler) Render(c *fiber.Ctx, supplements *router.Supplements, lang string, templateMap fiber.Map) (statusCode int, err error) { - urls := []router.SitemapInfo{} - for _, route := range router.Routes { - sitemapInfo := route.SitemapInfo(supplements) - if sitemapInfo == nil { - continue - } - - method, match := route.Filter() - lastMod, err := supplements.TemplateManager.GetLastModified(method + " " + match) - if err != nil { - slog.Warn("failed to get last modified for a route", - slog.String("error", err.Error()), - slog.String("method", method), - slog.String("match", match)) - lastMod = time.Now() - } - - for i := range sitemapInfo { - if sitemapInfo[i].LastModified.Before(lastMod) { - sitemapInfo[i].LastModified = lastMod - } - if sitemapInfo[i].ChangeFreq == "" { - sitemapInfo[i].ChangeFreq = "monthly" - } - urls = append(urls, sitemapInfo[i]) - } - } - - templateMap["URLs"] = urls - - return fiber.StatusOK, nil -} |