summaryrefslogtreecommitdiffci
path: root/internal/router
diff options
from:
to:
context:
space:
mode:
Diffstat (limited to 'internal/router')
-rw-r--r--internal/router/api-v1-blog-search.go2
-rw-r--r--internal/router/api-v1-email-is-in-verification.go58
-rw-r--r--internal/router/api-v1-email-send-verification-code.go98
-rw-r--r--internal/router/api-v1-email-verify.go52
-rw-r--r--internal/router/api-v1-general-page-body.go38
-rw-r--r--internal/router/api-v1-general-page-bottom-embeds.go4
-rw-r--r--internal/router/api-v1-general-page-footer.go4
-rw-r--r--internal/router/api-v1-general-page-header.go5
-rw-r--r--internal/router/api-v1-general-page-top-embeds.go4
-rw-r--r--internal/router/api-v1-general-page.go8
-rw-r--r--internal/router/api-v1-like.go2
-rw-r--r--internal/router/lang-blog-title.go86
-rw-r--r--internal/router/lang-blog.go98
-rw-r--r--internal/router/lang-map.go2
-rw-r--r--internal/router/root.go27
15 files changed, 263 insertions, 225 deletions
diff --git a/internal/router/api-v1-blog-search.go b/internal/router/api-v1-blog-search.go
index ab5425c..5b1d14d 100644
--- a/internal/router/api-v1-blog-search.go
+++ b/internal/router/api-v1-blog-search.go
@@ -17,7 +17,7 @@ import (
)
func init() {
- tm.Add("catalogue-blog-cards", "views/partials/catalogue-blog-cards.html", "views/partials/catalogue-blog-card-tags.html")
+ assert(0, tm.Add("catalogue-blog-cards", "views/partials/catalogue-blog-cards.html", "views/partials/catalogue-blog-card-tags.html"))
}
func Api_V1_BlogSearch(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client) func(c *fiber.Ctx) error {
diff --git a/internal/router/api-v1-email-is-in-verification.go b/internal/router/api-v1-email-is-in-verification.go
new file mode 100644
index 0000000..83c783c
--- /dev/null
+++ b/internal/router/api-v1-email-is-in-verification.go
@@ -0,0 +1,58 @@
+package router
+
+import (
+ "fmt"
+ "net/url"
+ "strings"
+
+ "github.com/SayaAndy/saya-today-web/locale"
+ "github.com/gofiber/fiber/v2"
+)
+
+func init() {
+ assert(0, tm.Add("personal-page-status", "views/partials/personal-page-status.html"))
+}
+
+func Api_V1_Email_IsInVerification(l map[string]*locale.LocaleConfig) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
+
+ id := c.IP()
+ lang := "en"
+
+ var referer, path string
+ var pathParts []string
+ var urlStruct *url.URL
+ var err error
+
+ referer = c.Get("Referer", "")
+ if referer == "" {
+ goto skipFetchingLang
+ }
+
+ urlStruct, err = url.ParseRequestURI(referer)
+ if err != nil {
+ goto skipFetchingLang
+ }
+
+ path = urlStruct.EscapedPath()
+ pathParts = strings.Split(strings.Trim(path, "/"), "/")
+ if len(pathParts) == 0 {
+ goto skipFetchingLang
+ }
+
+ lang = pathParts[0]
+
+ skipFetchingLang:
+ isAllowed, whenAllowed, codeExpiry := Mailer.IsAllowedToRetryVerification(id)
+ if !isAllowed {
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Neutral", fiber.StatusOK, lang, strings.ReplaceAll(l[lang].UserProfile.DelayTilVerification, "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")), map[string]string{
+ "striked-end-time": fmt.Sprint(whenAllowed.UnixMilli()),
+ "code-expiry-time": fmt.Sprint(codeExpiry.UnixMilli()),
+ })
+ }
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "OK", fiber.StatusOK, lang, "", map[string]string{
+ "code-expiry-time": fmt.Sprint(codeExpiry.UnixMilli()),
+ })
+ }
+}
diff --git a/internal/router/api-v1-email-send-verification-code.go b/internal/router/api-v1-email-send-verification-code.go
new file mode 100644
index 0000000..2cde1bd
--- /dev/null
+++ b/internal/router/api-v1-email-send-verification-code.go
@@ -0,0 +1,98 @@
+package router
+
+import (
+ "fmt"
+ "html/template"
+ "log/slog"
+ "net/url"
+ "strings"
+
+ "github.com/SayaAndy/saya-today-web/locale"
+ "github.com/gofiber/fiber/v2"
+)
+
+func init() {
+ assert(0, tm.Add("personal-page-status", "views/partials/personal-page-status.html"))
+}
+
+func Api_V1_Email_SendVerificationCode(l map[string]*locale.LocaleConfig) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
+
+ referer := c.Get("Referer", "")
+ if referer == "" {
+ return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is empty")
+ }
+
+ urlStruct, err := url.ParseRequestURI(referer)
+ if err != nil {
+ return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("'Referer' header is invalid: %s", err.Error()))
+ }
+
+ path := urlStruct.EscapedPath()
+ pathParts := strings.Split(strings.Trim(path, "/"), "/")
+ if len(pathParts) != 2 {
+ return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/user'")
+ }
+
+ lang := pathParts[0]
+ id := c.IP()
+
+ email := c.FormValue("email")
+ if email == "" {
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.EmailEmpty, map[string]string{})
+ }
+
+ isTaken, err := Mailer.MailIsTaken(email)
+ if err != nil {
+ slog.Error("failed to check if address is already taken", slog.String("error", err.Error()))
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.VerificationCodeSendingError, map[string]string{})
+ }
+ if isTaken {
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.EmailTaken, map[string]string{})
+ }
+
+ if isAllowed, whenAllowed, _ := Mailer.IsAllowedToRetryVerification(id); !isAllowed {
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, strings.ReplaceAll(l[lang].UserProfile.DelayTilVerification, "{}", whenAllowed.Format("2006-01-02 15:04:05 MST")), map[string]string{
+ "striked-end-time": fmt.Sprint(whenAllowed.UnixMilli()),
+ })
+ }
+
+ if previousEmail, _, _ := Mailer.GetInfo(id); previousEmail == email {
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.EmailAlreadyValidated, map[string]string{})
+ }
+
+ if err = Mailer.SendVerificationCode(id, email, lang); err != nil {
+ slog.Error("failed to send a verification code", slog.String("error", err.Error()))
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.VerificationCodeSendingError, map[string]string{})
+ }
+
+ _, endTime, codeExpiry := Mailer.IsAllowedToRetryVerification(id)
+ return api_v1_email_sendStatusHtml(c, "email-message", l, "OK", fiber.StatusOK, lang, strings.ReplaceAll(l[lang].UserProfile.VerificationCodeSent, "{}", endTime.Format("2006-01-02 15:04:05 MST")), map[string]string{
+ "striked-end-time": fmt.Sprint(endTime.UnixMilli()),
+ "code-expiry-time": fmt.Sprint(codeExpiry.UnixMilli()),
+ })
+ }
+}
+
+func api_v1_email_sendStatusHtml(c *fiber.Ctx, divId string, l map[string]*locale.LocaleConfig, status string, code int, lang string, message string, dataAttributes map[string]string) error {
+ sterileDataset := make(map[string]interface{})
+ for k, v := range dataAttributes {
+ sterileDataset[k] = template.HTMLAttr(fmt.Sprintf("data-%s=\"%s\"", k, template.HTMLEscapeString(v)))
+ }
+
+ content, err := tm.Render("personal-page-status", fiber.Map{
+ "L": l[lang],
+ "Status": status,
+ "Message": message,
+ "StatusId": divId,
+ "DataAttributes": sterileDataset,
+ })
+ if err != nil {
+ c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
+ slog.Error("failed to render the email status message", slog.String("error", err.Error()), slog.String("div_id", divId), slog.String("message", message))
+ return c.Status(fiber.ErrInternalServerError.Code).SendString("Failed to render the email status message, please ask administrator for a more detailed cause")
+ }
+ c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8)
+ return c.Status(code).Send(content)
+}
diff --git a/internal/router/api-v1-email-verify.go b/internal/router/api-v1-email-verify.go
new file mode 100644
index 0000000..fad174b
--- /dev/null
+++ b/internal/router/api-v1-email-verify.go
@@ -0,0 +1,52 @@
+package router
+
+import (
+ "fmt"
+ "log/slog"
+ "net/url"
+ "strings"
+
+ "github.com/SayaAndy/saya-today-web/locale"
+ "github.com/gofiber/fiber/v2"
+)
+
+func init() {
+ assert(0, tm.Add("personal-page-status", "views/partials/personal-page-status.html"))
+}
+
+func Api_V1_Email_Verify(l map[string]*locale.LocaleConfig) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
+
+ referer := c.Get("Referer", "")
+ if referer == "" {
+ return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is empty")
+ }
+
+ urlStruct, err := url.ParseRequestURI(referer)
+ if err != nil {
+ return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("'Referer' header is invalid: %s", err.Error()))
+ }
+
+ path := urlStruct.EscapedPath()
+ pathParts := strings.Split(strings.Trim(path, "/"), "/")
+ if len(pathParts) != 2 {
+ return c.Status(fiber.ErrBadRequest.Code).SendString("'Referer' header is invalid: expect format '/{lang}/user'")
+ }
+
+ lang := pathParts[0]
+
+ verificationCode := c.FormValue("email_code")
+ if verificationCode == "" {
+ return api_v1_email_sendStatusHtml(c, "verification-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.VerificationEmpty, map[string]string{})
+ }
+
+ if err = Mailer.Verify(verificationCode, lang); err != nil {
+ slog.Error("verification code is invalid", slog.String("verification_code", verificationCode), slog.String("error", err.Error()))
+ return api_v1_email_sendStatusHtml(c, "verification-message", l, "Failed", fiber.ErrUnprocessableEntity.Code, lang, l[lang].UserProfile.VerificationFailed, map[string]string{})
+ }
+ return api_v1_email_sendStatusHtml(c, "verification-message", l, "OK", fiber.StatusOK, lang, l[lang].UserProfile.VerificationSuccess, map[string]string{
+ "hide-verification-panel": "true",
+ })
+ }
+}
diff --git a/internal/router/api-v1-general-page-body.go b/internal/router/api-v1-general-page-body.go
index fe21957..6f856d3 100644
--- a/internal/router/api-v1-general-page-body.go
+++ b/internal/router/api-v1-general-page-body.go
@@ -1,6 +1,7 @@
package router
import (
+ "bytes"
"fmt"
"html/template"
"log/slog"
@@ -14,15 +15,18 @@ import (
"github.com/SayaAndy/saya-today-web/config"
"github.com/SayaAndy/saya-today-web/internal/b2"
"github.com/SayaAndy/saya-today-web/internal/factgiver"
+ "github.com/SayaAndy/saya-today-web/internal/frontmatter"
+ "github.com/SayaAndy/saya-today-web/internal/mailer"
"github.com/SayaAndy/saya-today-web/locale"
"github.com/gofiber/fiber/v2"
"github.com/yuin/goldmark"
)
var FactGiver *factgiver.FactGiver
+var Mailer *mailer.Mailer
func init() {
- tm.Add("general-page-body", "views/partials/general-page-body.html")
+ assert(0, tm.Add("general-page-body", "views/partials/general-page-body.html"))
}
func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client, md goldmark.Markdown) func(c *fiber.Ctx) error {
@@ -39,15 +43,16 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []config.A
}
path := urlStruct.EscapedPath()
+ trimmedPath := strings.Trim(path, "/")
- cacheKey := fmt.Sprintf("body.%s", path)
- if val, ok := PCache.Get(cacheKey); val != nil && ok {
+ cacheKey := fmt.Sprintf("body.%s", trimmedPath)
+ if val, ok := PCache.Get(cacheKey); !strings.HasSuffix(trimmedPath, "user") && val != nil && ok {
c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).Type("html").Send(val)
}
lang := ""
- pathParts := strings.Split(strings.Trim(path, "/"), "/")
+ pathParts := strings.Split(trimmedPath, "/")
if len(pathParts) == 1 && pathParts[0] == "" {
pathParts = []string{}
}
@@ -123,6 +128,17 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []config.A
values["Title"] = l[lang].BlogSearch.Header
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
+ } else if len(pathParts) == 2 && pathParts[1] == "user" {
+ values["Title"] = l[lang].UserProfile.Header
+
+ email, _, err := Mailer.GetInfo(c.IP())
+ if err != nil {
+ slog.Error("get info from mailer about a client", slog.String("error", err.Error()))
+ }
+ values["Email"] = email
+ values["EmailCode"] = c.Query("email_code")
+
+ additionalTemplates = append(additionalTemplates, "views/pages/user-page.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
metadata, parsedMarkdown, err := readBlogPost(md, b2Client, lang+"/"+pathParts[2])
if err != nil {
@@ -174,3 +190,17 @@ func Api_V1_GeneralPage_Body(l map[string]*locale.LocaleConfig, langs []config.A
return c.Status(fiber.StatusOK).Type("html").Send(content)
}
}
+
+func readBlogPost(md goldmark.Markdown, b2Client *b2.B2Client, sourceName string) (metadata *frontmatter.Metadata, html string, err error) {
+ metadata, markdown, err := b2Client.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/api-v1-general-page-bottom-embeds.go b/internal/router/api-v1-general-page-bottom-embeds.go
index 0a2030f..b408187 100644
--- a/internal/router/api-v1-general-page-bottom-embeds.go
+++ b/internal/router/api-v1-general-page-bottom-embeds.go
@@ -14,7 +14,7 @@ import (
)
func init() {
- tm.Add("general-page-bottom-embeds", "views/partials/general-page-bottom-embeds.html")
+ assert(0, tm.Add("general-page-bottom-embeds", "views/partials/general-page-bottom-embeds.html"))
}
func Api_V1_GeneralPage_BottomEmbeds(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client) func(c *fiber.Ctx) error {
@@ -63,6 +63,8 @@ func Api_V1_GeneralPage_BottomEmbeds(l map[string]*locale.LocaleConfig, langs []
if len(pathParts) == 2 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
+ } else if len(pathParts) == 2 && pathParts[1] == "user" {
+ additionalTemplates = append(additionalTemplates, "views/pages/user-page.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
} else if len(pathParts) == 1 {
diff --git a/internal/router/api-v1-general-page-footer.go b/internal/router/api-v1-general-page-footer.go
index 2030b3b..99d899a 100644
--- a/internal/router/api-v1-general-page-footer.go
+++ b/internal/router/api-v1-general-page-footer.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- tm.Add("general-page-footer", "views/partials/general-page-footer.html")
+ assert(0, tm.Add("general-page-footer", "views/partials/general-page-footer.html"))
}
func Api_V1_GeneralPage_Footer(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig) func(c *fiber.Ctx) error {
@@ -63,6 +63,8 @@ func Api_V1_GeneralPage_Footer(l map[string]*locale.LocaleConfig, langs []config
if len(pathParts) == 2 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
+ } else if len(pathParts) == 2 && pathParts[1] == "user" {
+ additionalTemplates = append(additionalTemplates, "views/pages/user-page.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
} else if len(pathParts) == 1 {
diff --git a/internal/router/api-v1-general-page-header.go b/internal/router/api-v1-general-page-header.go
index a2c832b..768417d 100644
--- a/internal/router/api-v1-general-page-header.go
+++ b/internal/router/api-v1-general-page-header.go
@@ -14,7 +14,7 @@ import (
)
func init() {
- tm.Add("general-page-header", "views/partials/general-page-header.html")
+ assert(0, tm.Add("general-page-header", "views/partials/general-page-header.html"))
}
func Api_V1_GeneralPage_Header(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client) func(c *fiber.Ctx) error {
@@ -65,6 +65,9 @@ func Api_V1_GeneralPage_Header(l map[string]*locale.LocaleConfig, langs []config
if len(pathParts) == 2 && pathParts[1] == "blog" {
values["Title"] = l[lang].BlogSearch.Header
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
+ } else if len(pathParts) == 2 && pathParts[1] == "user" {
+ values["Title"] = l[lang].UserProfile.Header
+ additionalTemplates = append(additionalTemplates, "views/pages/user-page.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
metadata, _, err := b2Client.ReadFrontmatter(lang + "/" + pathParts[2] + ".md")
if err != nil {
diff --git a/internal/router/api-v1-general-page-top-embeds.go b/internal/router/api-v1-general-page-top-embeds.go
index 544e3a4..f3e480b 100644
--- a/internal/router/api-v1-general-page-top-embeds.go
+++ b/internal/router/api-v1-general-page-top-embeds.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- tm.Add("general-page-top-embeds", "views/partials/general-page-top-embeds.html")
+ assert(0, tm.Add("general-page-top-embeds", "views/partials/general-page-top-embeds.html"))
}
func Api_V1_GeneralPage_TopEmbeds(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig) func(c *fiber.Ctx) error {
@@ -63,6 +63,8 @@ func Api_V1_GeneralPage_TopEmbeds(l map[string]*locale.LocaleConfig, langs []con
if len(pathParts) == 2 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-catalogue.html")
+ } else if len(pathParts) == 2 && pathParts[1] == "user" {
+ additionalTemplates = append(additionalTemplates, "views/pages/user-page.html")
} else if len(pathParts) == 3 && pathParts[1] == "blog" {
additionalTemplates = append(additionalTemplates, "views/pages/blog-page.html")
} else if len(pathParts) == 1 {
diff --git a/internal/router/api-v1-general-page.go b/internal/router/api-v1-general-page.go
index a77fc5e..c8b4362 100644
--- a/internal/router/api-v1-general-page.go
+++ b/internal/router/api-v1-general-page.go
@@ -1,6 +1,7 @@
package router
import (
+ "fmt"
"log/slog"
"strings"
@@ -10,7 +11,7 @@ import (
)
func init() {
- tm.Add("general-page", "views/layouts/general-page.html")
+ assert(0, tm.Add("general-page", "views/layouts/general-page.html"))
}
func Api_V1_GeneralPage(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig) func(c *fiber.Ctx) error {
@@ -34,7 +35,8 @@ func Api_V1_GeneralPage(l map[string]*locale.LocaleConfig, langs []config.Availa
}
langIsAvailable:
- cacheKey := "general-page." + lang
+ queryString := string(c.Request().URI().QueryString())
+ cacheKey := fmt.Sprintf("general-page.%s?%s", lang, queryString)
if val, ok := PCache.Get(cacheKey); val != nil && ok {
c.Set(fiber.HeaderContentType, fiber.MIMETextHTMLCharsetUTF8)
return c.Status(fiber.StatusOK).Type("html").Send(val)
@@ -43,7 +45,7 @@ func Api_V1_GeneralPage(l map[string]*locale.LocaleConfig, langs []config.Availa
content, err := tm.Render("general-page", fiber.Map{
"L": l[lang],
"Lang": lang,
- "QueryString": string(c.Request().URI().QueryString()),
+ "QueryString": queryString,
})
if err != nil {
slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error()))
diff --git a/internal/router/api-v1-like.go b/internal/router/api-v1-like.go
index 327dafa..323c457 100644
--- a/internal/router/api-v1-like.go
+++ b/internal/router/api-v1-like.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- tm.Add("blog-page-like-button", "views/partials/blog-page-like-button.html")
+ assert(0, tm.Add("blog-page-like-button", "views/partials/blog-page-like-button.html"))
}
func Api_V1_Like_Put(l map[string]*locale.LocaleConfig, b2 *b2.B2Client) func(c *fiber.Ctx) error {
diff --git a/internal/router/lang-blog-title.go b/internal/router/lang-blog-title.go
deleted file mode 100644
index 6e80671..0000000
--- a/internal/router/lang-blog-title.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package router
-
-import (
- "bytes"
- "fmt"
- "html/template"
- "log/slog"
- "strconv"
- "strings"
-
- "github.com/SayaAndy/saya-today-web/config"
- "github.com/SayaAndy/saya-today-web/internal/b2"
- "github.com/SayaAndy/saya-today-web/internal/frontmatter"
- "github.com/SayaAndy/saya-today-web/locale"
- "github.com/gofiber/fiber/v2"
- "github.com/yuin/goldmark"
-)
-
-func init() {
- tm.Add("blog-page", "views/layouts/general-page.html", "views/pages/blog-page.html")
-}
-
-func Lang_Blog_Title(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client, md goldmark.Markdown) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- ip := c.IP()
- slog.Debug("client entering blog page", slog.String("ip", ip), slog.String("page", c.Path()))
-
- lang := c.Params("lang")
- for _, availableLang := range langs {
- if availableLang.Name == lang {
- goto langIsAvailable
- }
- }
- return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server does not support '%s' language", lang))
-
- langIsAvailable:
- metadata, parsedMarkdown, err := readBlogPost(md, b2Client, lang+"/"+c.Params("title"))
- if err != nil {
- c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
- return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("failed to find '%s' post", c.Params("title")))
- }
-
- 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]
- }
-
- content, err := tm.Render("blog-page", fiber.Map{
- "Title": metadata.Title,
- "PublishedDate": metadata.PublishedTime.Format("2006-01-02 15:04:05 -07:00"),
- "PublishedYear": strconv.Itoa(metadata.PublishedTime.Year()),
- "ParsedMarkdown": template.HTML(parsedMarkdown),
- "MapLocationX": x,
- "MapLocationY": y,
- "MapLocationAreaMeters": areaError,
- "Lang": lang,
- "QueryString": string(c.Request().URI().QueryString()),
- })
- if err != nil {
- slog.Warn("failed to generate page", slog.String("page", "/"+lang+"/blog/"+c.Params("title")), slog.String("error", err.Error()))
- c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
- return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page")
- }
-
- return c.Type("html").Send(content)
- }
-}
-
-func readBlogPost(md goldmark.Markdown, b2Client *b2.B2Client, sourceName string) (metadata *frontmatter.Metadata, html string, err error) {
- metadata, markdown, err := b2Client.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/lang-blog.go b/internal/router/lang-blog.go
deleted file mode 100644
index 960ac3d..0000000
--- a/internal/router/lang-blog.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package router
-
-import (
- "fmt"
- "log/slog"
- "net/url"
- "regexp"
- "slices"
- "strings"
-
- "github.com/SayaAndy/saya-today-web/config"
- "github.com/SayaAndy/saya-today-web/internal/b2"
- "github.com/SayaAndy/saya-today-web/locale"
- "github.com/gofiber/fiber/v2"
-)
-
-func init() {
- tm.Add("blog-catalogue", "views/layouts/general-page.html", "views/pages/blog-catalogue.html")
-}
-
-func Lang_Blog(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- lang := c.Params("lang")
- for _, availableLang := range langs {
- if availableLang.Name == lang {
- goto langIsAvailable
- }
- }
- return c.Status(fiber.ErrNotFound.Code).SendString(fmt.Sprintf("server does not support '%s' language", lang))
-
- langIsAvailable:
- querySort := c.Query("sort")
- if querySort == "" {
- querySort = "publicationDateDesc"
- }
-
- encodedQuery := c.Request().URI().QueryString()
- re, err := regexp.Compile(`tags\[\]=([\w]+)`)
- if err != nil {
- slog.Warn("failed to generate regex for tags gathering", slog.String("error", err.Error()))
- c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
- return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate regex for tags gathering")
- }
- decodedQuery, _ := url.QueryUnescape(string(encodedQuery))
- matches := re.FindAllStringSubmatch(decodedQuery, -1)
-
- queryTags := make([]string, 0, len(matches))
- for _, match := range matches {
- queryTags = append(queryTags, string(match[1]))
- }
-
- pages, err := b2Client.Scan(lang + "/")
- status := fiber.StatusOK
- if err != nil {
- status = fiber.StatusPartialContent
- pages = []*b2.BlogPage{}
- }
-
- 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("path", c.Path()))
-
- type Tag struct {
- Name string `json:"Name" yaml:"name"`
- Count int `json:"Count" yaml:"count"`
- }
-
- 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)
- })
-
- content, err := tm.Render("blog-catalogue", fiber.Map{
- "QuerySort": querySort,
- "QueryTags": strings.Join(queryTags, ","),
- "QueryString": string(c.Request().URI().QueryString()),
- "Tags": tagsArray,
- "Lang": lang,
- "L": l[lang],
- "PublishedYear": "2025",
- "Title": l[lang].BlogSearch.Header,
- })
- if err != nil {
- slog.Warn("failed to generate page", slog.String("path", c.Path()), slog.String("error", err.Error()))
- c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
- return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page")
- }
-
- return c.Type("html").Status(status).Send(content)
- }
-}
diff --git a/internal/router/lang-map.go b/internal/router/lang-map.go
index a744f02..619b05b 100644
--- a/internal/router/lang-map.go
+++ b/internal/router/lang-map.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- tm.Add("global-map", "views/pages/global-map.html")
+ assert(0, tm.Add("global-map", "views/pages/global-map.html"))
}
func Lang_Map(l map[string]*locale.LocaleConfig, langs []config.AvailableLanguageConfig, b2Client *b2.B2Client) func(c *fiber.Ctx) error {
diff --git a/internal/router/root.go b/internal/router/root.go
deleted file mode 100644
index f4a0692..0000000
--- a/internal/router/root.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package router
-
-import (
- "log/slog"
-
- "github.com/SayaAndy/saya-today-web/config"
- "github.com/gofiber/fiber/v2"
-)
-
-func init() {
- tm.Add("index", "views/index.html")
-}
-
-func Root(localeCfg []config.AvailableLanguageConfig) func(c *fiber.Ctx) error {
- return func(c *fiber.Ctx) error {
- content, err := tm.Render("index", fiber.Map{
- "AvailableLanguages": localeCfg,
- })
- if err != nil {
- slog.Warn("failed to generate page", slog.String("page", "/"), slog.String("error", err.Error()))
- c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8)
- return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate page")
- }
-
- return c.Type("html").Send(content)
- }
-}