From 0d261d2f7d080b610f50102576073e334a89e8cb Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Thu, 23 Oct 2025 01:15:26 +0700 Subject: feat: email linking --- internal/router/api-v1-email-verify.go | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 internal/router/api-v1-email-verify.go (limited to 'internal/router/api-v1-email-verify.go') 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", + }) + } +} -- cgit v1.3.1+13 From f101d6a555c6d3453def332f2c230b37ae5c81b4 Mon Sep 17 00:00:00 2001 From: SayaAndy Date: Thu, 23 Oct 2025 19:32:11 +0700 Subject: fix: disable subscription form if user has no verified email fix: check properly if the email was already taken --- internal/mailer/mailer.go | 7 ++++++- internal/router/api-v1-email-verify.go | 2 +- locale/localization.en.yaml | 1 + locale/localization.go | 1 + locale/localization.ru.yaml | 1 + views/pages/user-page.html | 8 ++++++++ 6 files changed, 18 insertions(+), 2 deletions(-) (limited to 'internal/router/api-v1-email-verify.go') diff --git a/internal/mailer/mailer.go b/internal/mailer/mailer.go index cd5eda8..ba2202e 100644 --- a/internal/mailer/mailer.go +++ b/internal/mailer/mailer.go @@ -145,7 +145,8 @@ func (m *Mailer) MailIsTaken(email string) (bool, error) { defer tx.Commit() defer rows.Close() - return rows.Next(), nil + isTaken := rows.Next() + return isTaken, nil } func (m *Mailer) GetInfo(userId string) (email string, lang string, err error) { @@ -355,3 +356,7 @@ func (m *Mailer) Subscribe(userId string, subscriptionType SubscriptionType, tag return nil } + +func SendNewPost() { + +} diff --git a/internal/router/api-v1-email-verify.go b/internal/router/api-v1-email-verify.go index fad174b..7fbfdec 100644 --- a/internal/router/api-v1-email-verify.go +++ b/internal/router/api-v1-email-verify.go @@ -45,7 +45,7 @@ func Api_V1_Email_Verify(l map[string]*locale.LocaleConfig) func(c *fiber.Ctx) e 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{ + return api_v1_email_sendStatusHtml(c, "verification-message", l, "OK", fiber.StatusOK, lang, l[lang].UserProfile.VerificationSuccess+"\n\n"+l[lang].UserProfile.RefreshPage, map[string]string{ "hide-verification-panel": "true", }) } diff --git a/locale/localization.en.yaml b/locale/localization.en.yaml index 8816f2e..ce334d9 100644 --- a/locale/localization.en.yaml +++ b/locale/localization.en.yaml @@ -62,3 +62,4 @@ UserProfile: SubscribeInvalidType: 'Invalid subscription type.' FailedToSubscribe: 'Failed to subscribe by new settings, please ask administrator for a more detailed cause.' SubscribedSuccessfully: 'Successfully subscribed you by new settings!' + RefreshPage: 'Please refresh the profile page on the device, where you requested the code.' diff --git a/locale/localization.go b/locale/localization.go index cd83aa1..1028c9e 100644 --- a/locale/localization.go +++ b/locale/localization.go @@ -96,6 +96,7 @@ type UserProfileConfig struct { SubscribeInvalidType string `yaml:"SubscribeInvalidType" json:"SubscribeInvalidType"` FailedToSubscribe string `yaml:"FailedToSubscribe" json:"FailedToSubscribe"` SubscribedSuccessfully string `yaml:"SubscribedSuccessfully" json:"SubscribedSuccessfully"` + RefreshPage string `yaml:"RefreshPage" json:"RefreshPage"` } func LoadConfig(path string, config *LocaleConfig) error { diff --git a/locale/localization.ru.yaml b/locale/localization.ru.yaml index 8a0ec5e..8894e22 100644 --- a/locale/localization.ru.yaml +++ b/locale/localization.ru.yaml @@ -62,3 +62,4 @@ UserProfile: SubscribeInvalidType: 'Неизвестный тип подписки.' FailedToSubscribe: 'Неудачная попытка записать новую подписку, более подробно спрашивайте у администратора.' SubscribedSuccessfully: 'Вы успешно подписались по новым настройкам!' + RefreshPage: 'Пожалуйста, обновите страницу с профилем на устройстве, откуда запрашивали код.' diff --git a/views/pages/user-page.html b/views/pages/user-page.html index 13eaa0b..7d39825 100644 --- a/views/pages/user-page.html +++ b/views/pages/user-page.html @@ -105,7 +105,9 @@ htmx.on("htmx:afterSwap", (e) => { emailMessage = htmx.find("#email-message"); verificationMessage = htmx.find("#verification-message"); emailForm = htmx.find("#email-form"); + emailInput = htmx.find("#email-input"); emailVerificationForm = htmx.find("#email-verification-form"); + subsForm = htmx.find("#subs-form"); tryAgainTimeDiff = parseInt(emailMessage.dataset.strikedEndTime || "0") - new Date().getTime(); verificationWaitingTimeDiff = parseInt(emailMessage.dataset.codeExpiryTime || "0") - new Date().getTime(); @@ -129,6 +131,12 @@ htmx.on("htmx:afterSwap", (e) => { if ((verificationMessage.dataset.hideVerificationPanel || "false") == "true") { emailVerificationForm.classList.add("hidden"); } + + if (emailInput.dataset.validatedEmail != "") { + subsForm.classList.remove("disabled"); + } else { + subsForm.classList.add("disabled"); + } }); function checkIfValidated(elem) { -- cgit v1.3.1+13