1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-15 18:57:04 +00:00

Refactor HTTP Client and LocalizedError packages

This commit is contained in:
Frédéric Guillot 2023-10-21 19:50:29 -07:00
parent 120aabfbce
commit 14e25ab9fe
104 changed files with 1277 additions and 10672 deletions

View file

@ -9,6 +9,7 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/ui/form"
"miniflux.app/v2/internal/ui/session"
@ -32,14 +33,14 @@ func (h *handler) saveAPIKey(w http.ResponseWriter, r *http.Request) {
view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
if err := apiKeyForm.Validate(); err != nil {
view.Set("errorMessage", err.Error())
if validationErr := apiKeyForm.Validate(); validationErr != nil {
view.Set("errorMessage", validationErr.Translate(user.Language))
html.OK(w, r, view.Render("create_api_key"))
return
}
if h.store.APIKeyExists(user.ID, apiKeyForm.Description) {
view.Set("errorMessage", "error.api_key_already_exists")
view.Set("errorMessage", locale.NewLocalizedError("error.api_key_already_exists").Translate(user.Language))
html.OK(w, r, view.Render("create_api_key"))
return
}

View file

@ -36,7 +36,7 @@ func (h *handler) saveCategory(w http.ResponseWriter, r *http.Request) {
categoryRequest := &model.CategoryRequest{Title: categoryForm.Title}
if validationErr := validator.ValidateCategoryCreation(h.store, loggedUser.ID, categoryRequest); validationErr != nil {
view.Set("errorMessage", validationErr.TranslationKey)
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("create_category"))
return
}

View file

@ -52,7 +52,7 @@ func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
}
if validationErr := validator.ValidateCategoryModification(h.store, loggedUser.ID, category.ID, categoryRequest); validationErr != nil {
view.Set("errorMessage", validationErr.TranslationKey)
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("create_category"))
return
}

View file

@ -66,7 +66,7 @@ func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
}
if validationErr := validator.ValidateFeedModification(h.store, loggedUser.ID, feedModificationRequest); validationErr != nil {
view.Set("errorMessage", validationErr.TranslationKey)
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("edit_feed"))
return
}

View file

@ -6,7 +6,7 @@ package form // import "miniflux.app/v2/internal/ui/form"
import (
"net/http"
"miniflux.app/v2/internal/errors"
"miniflux.app/v2/internal/locale"
)
// APIKeyForm represents the API Key form.
@ -15,9 +15,9 @@ type APIKeyForm struct {
}
// Validate makes sure the form values are valid.
func (a APIKeyForm) Validate() error {
func (a APIKeyForm) Validate() *locale.LocalizedError {
if a.Description == "" {
return errors.NewLocalizedError("error.fields_mandatory")
return locale.NewLocalizedError("error.fields_mandatory")
}
return nil

View file

@ -7,7 +7,7 @@ import (
"net/http"
"strings"
"miniflux.app/v2/internal/errors"
"miniflux.app/v2/internal/locale"
)
// AuthForm represents the authentication form.
@ -17,9 +17,9 @@ type AuthForm struct {
}
// Validate makes sure the form values are valid.
func (a AuthForm) Validate() error {
func (a AuthForm) Validate() *locale.LocalizedError {
if a.Username == "" || a.Password == "" {
return errors.NewLocalizedError("error.fields_mandatory")
return locale.NewLocalizedError("error.fields_mandatory")
}
return nil

View file

@ -7,7 +7,7 @@ import (
"net/http"
"strconv"
"miniflux.app/v2/internal/errors"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/model"
)
@ -64,13 +64,13 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
}
// Validate makes sure the form values are valid.
func (s *SettingsForm) Validate() error {
func (s *SettingsForm) Validate() *locale.LocalizedError {
if s.Username == "" || s.Theme == "" || s.Language == "" || s.Timezone == "" || s.EntryDirection == "" || s.DisplayMode == "" || s.DefaultHomePage == "" {
return errors.NewLocalizedError("error.settings_mandatory_fields")
return locale.NewLocalizedError("error.settings_mandatory_fields")
}
if s.CJKReadingSpeed <= 0 || s.DefaultReadingSpeed <= 0 {
return errors.NewLocalizedError("error.settings_reading_speed_is_positive")
return locale.NewLocalizedError("error.settings_reading_speed_is_positive")
}
if s.Confirmation == "" {
@ -80,7 +80,7 @@ func (s *SettingsForm) Validate() error {
s.Password = ""
} else if s.Password != "" {
if s.Password != s.Confirmation {
return errors.NewLocalizedError("error.different_passwords")
return locale.NewLocalizedError("error.different_passwords")
}
}

View file

@ -7,7 +7,7 @@ import (
"net/http"
"strconv"
"miniflux.app/v2/internal/errors"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/validator"
)
@ -29,26 +29,26 @@ type SubscriptionForm struct {
UrlRewriteRules string
}
// Validate makes sure the form values are valid.
func (s *SubscriptionForm) Validate() error {
// Validate makes sure the form values locale.are valid.
func (s *SubscriptionForm) Validate() *locale.LocalizedError {
if s.URL == "" || s.CategoryID == 0 {
return errors.NewLocalizedError("error.feed_mandatory_fields")
return locale.NewLocalizedError("error.feed_mandatory_fields")
}
if !validator.IsValidURL(s.URL) {
return errors.NewLocalizedError("error.invalid_feed_url")
return locale.NewLocalizedError("error.invalid_feed_url")
}
if !validator.IsValidRegex(s.BlocklistRules) {
return errors.NewLocalizedError("error.feed_invalid_blocklist_rule")
return locale.NewLocalizedError("error.feed_invalid_blocklist_rule")
}
if !validator.IsValidRegex(s.KeeplistRules) {
return errors.NewLocalizedError("error.feed_invalid_keeplist_rule")
return locale.NewLocalizedError("error.feed_invalid_keeplist_rule")
}
if !validator.IsValidRegex(s.UrlRewriteRules) {
return errors.NewLocalizedError("error.feed_invalid_urlrewrite_rule")
return locale.NewLocalizedError("error.feed_invalid_urlrewrite_rule")
}
return nil

View file

@ -6,7 +6,7 @@ package form // import "miniflux.app/v2/internal/ui/form"
import (
"net/http"
"miniflux.app/v2/internal/errors"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/model"
)
@ -19,31 +19,31 @@ type UserForm struct {
}
// ValidateCreation validates user creation.
func (u UserForm) ValidateCreation() error {
func (u UserForm) ValidateCreation() *locale.LocalizedError {
if u.Username == "" || u.Password == "" || u.Confirmation == "" {
return errors.NewLocalizedError("error.fields_mandatory")
return locale.NewLocalizedError("error.fields_mandatory")
}
if u.Password != u.Confirmation {
return errors.NewLocalizedError("error.different_passwords")
return locale.NewLocalizedError("error.different_passwords")
}
return nil
}
// ValidateModification validates user modification.
func (u UserForm) ValidateModification() error {
func (u UserForm) ValidateModification() *locale.LocalizedError {
if u.Username == "" {
return errors.NewLocalizedError("error.user_mandatory_fields")
return locale.NewLocalizedError("error.user_mandatory_fields")
}
if u.Password != "" {
if u.Password != u.Confirmation {
return errors.NewLocalizedError("error.different_passwords")
return locale.NewLocalizedError("error.different_passwords")
}
if len(u.Password) < 6 {
return errors.NewLocalizedError("error.password_min_length")
return locale.NewLocalizedError("error.password_min_length")
}
}

View file

@ -12,6 +12,7 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/ui/form"
"miniflux.app/v2/internal/ui/session"
"miniflux.app/v2/internal/ui/view"
@ -23,16 +24,17 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
authForm := form.NewAuthForm(r)
view := view.New(h.tpl, r, sess)
view.Set("errorMessage", "error.bad_credentials")
view.Set("errorMessage", locale.NewLocalizedError("error.bad_credentials").Translate(request.UserLanguage(r)))
view.Set("form", authForm)
if err := authForm.Validate(); err != nil {
if validationErr := authForm.Validate(); validationErr != nil {
translatedErrorMessage := validationErr.Translate(request.UserLanguage(r))
slog.Warn("Validation error during login check",
slog.Bool("authentication_failed", true),
slog.String("client_ip", clientIP),
slog.String("user_agent", r.UserAgent()),
slog.String("username", authForm.Username),
slog.Any("error", err),
slog.Any("error", translatedErrorMessage),
)
html.OK(w, r, view.Render("login"))
return

View file

@ -6,12 +6,14 @@ package ui // import "miniflux.app/v2/internal/ui"
import (
"log/slog"
"net/http"
"strings"
"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/client"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/reader/fetcher"
"miniflux.app/v2/internal/reader/opml"
"miniflux.app/v2/internal/ui/session"
"miniflux.app/v2/internal/ui/view"
@ -51,7 +53,7 @@ func (h *handler) uploadOPML(w http.ResponseWriter, r *http.Request) {
view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
if fileHeader.Size == 0 {
view.Set("errorMessage", "error.empty_file")
view.Set("errorMessage", locale.NewLocalizedError("error.empty_file").Translate(user.Language))
html.OK(w, r, view.Render("import"))
return
}
@ -73,15 +75,15 @@ func (h *handler) fetchOPML(w http.ResponseWriter, r *http.Request) {
return
}
url := r.FormValue("url")
if url == "" {
opmlFileURL := strings.TrimSpace(r.FormValue("url"))
if opmlFileURL == "" {
html.Redirect(w, r, route.Path(h.router, "import"))
return
}
slog.Info("Fetching OPML file remotely",
slog.Int64("user_id", loggedUserID),
slog.String("opml_file_url", url),
slog.String("opml_file_url", opmlFileURL),
)
sess := session.New(h.store, request.SessionID(r))
@ -91,15 +93,21 @@ func (h *handler) fetchOPML(w http.ResponseWriter, r *http.Request) {
view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
clt := client.NewClientWithConfig(url, config.Opts)
resp, err := clt.Get()
if err != nil {
view.Set("errorMessage", err)
requestBuilder := fetcher.NewRequestBuilder()
requestBuilder.WithTimeout(config.Opts.HTTPClientTimeout())
requestBuilder.WithProxy(config.Opts.HTTPClientProxy())
responseHandler := fetcher.NewResponseHandler(requestBuilder.ExecuteRequest(opmlFileURL))
defer responseHandler.Close()
if localizedError := responseHandler.LocalizedError(); localizedError != nil {
slog.Warn("Unable to fetch OPML file", slog.String("opml_file_url", opmlFileURL), slog.Any("error", localizedError.Error()))
view.Set("errorMessage", localizedError.Translate(user.Language))
html.OK(w, r, view.Render("import"))
return
}
if impErr := opml.NewHandler(h.store).Import(user.ID, resp.Body); impErr != nil {
if impErr := opml.NewHandler(h.store).Import(user.ID, responseHandler.Body(config.Opts.HTTPClientMaxBodySize())); impErr != nil {
view.Set("errorMessage", impErr)
html.OK(w, r, view.Render("import"))
return

View file

@ -44,8 +44,8 @@ func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) {
view.Set("countUnread", h.store.CountUnreadEntries(loggedUser.ID))
view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(loggedUser.ID))
if err := settingsForm.Validate(); err != nil {
view.Set("errorMessage", err.Error())
if validationErr := settingsForm.Validate(); validationErr != nil {
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("settings"))
return
}
@ -66,7 +66,7 @@ func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) {
}
if validationErr := validator.ValidateUserModification(h.store, loggedUser.ID, userModificationRequest); validationErr != nil {
view.Set("errorMessage", validationErr.TranslationKey)
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("settings"))
return
}

View file

@ -41,14 +41,14 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
view.Set("defaultUserAgent", config.Opts.HTTPClientUserAgent())
subscriptionForm := form.NewSubscriptionForm(r)
if err := subscriptionForm.Validate(); err != nil {
if validationErr := subscriptionForm.Validate(); validationErr != nil {
view.Set("form", subscriptionForm)
view.Set("errorMessage", err.Error())
view.Set("errorMessage", validationErr.Translate(user.Language))
html.OK(w, r, view.Render("add_subscription"))
return
}
feed, err := feedHandler.CreateFeed(h.store, user.ID, &model.FeedCreationRequest{
feed, localizedError := feedHandler.CreateFeed(h.store, user.ID, &model.FeedCreationRequest{
CategoryID: subscriptionForm.CategoryID,
FeedURL: subscriptionForm.URL,
Crawler: subscriptionForm.Crawler,
@ -64,9 +64,9 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
UrlRewriteRules: subscriptionForm.UrlRewriteRules,
FetchViaProxy: subscriptionForm.FetchViaProxy,
})
if err != nil {
if localizedError != nil {
view.Set("form", subscriptionForm)
view.Set("errorMessage", err)
view.Set("errorMessage", localizedError.Translate(user.Language))
html.OK(w, r, view.Render("add_subscription"))
return
}

View file

@ -10,6 +10,7 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/model"
feedHandler "miniflux.app/v2/internal/reader/handler"
"miniflux.app/v2/internal/reader/subscription"
@ -43,9 +44,9 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
v.Set("hasProxyConfigured", config.Opts.HasHTTPClientProxyConfigured())
subscriptionForm := form.NewSubscriptionForm(r)
if err := subscriptionForm.Validate(); err != nil {
if validationErr := subscriptionForm.Validate(); validationErr != nil {
v.Set("form", subscriptionForm)
v.Set("errorMessage", err.Error())
v.Set("errorMessage", validationErr.Translate(user.Language))
html.OK(w, r, v.Render("add_subscription"))
return
}
@ -55,7 +56,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
rssbridgeURL = intg.RSSBridgeURL
}
subscriptions, findErr := subscription.FindSubscriptions(
subscriptions, localizedError := subscription.FindSubscriptions(
subscriptionForm.URL,
subscriptionForm.UserAgent,
subscriptionForm.Cookie,
@ -65,9 +66,9 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
subscriptionForm.AllowSelfSignedCertificates,
rssbridgeURL,
)
if findErr != nil {
if localizedError != nil {
v.Set("form", subscriptionForm)
v.Set("errorMessage", findErr)
v.Set("errorMessage", localizedError.Translate(user.Language))
html.OK(w, r, v.Render("add_subscription"))
return
}
@ -76,10 +77,10 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
switch {
case n == 0:
v.Set("form", subscriptionForm)
v.Set("errorMessage", "error.subscription_not_found")
v.Set("errorMessage", locale.NewLocalizedError("error.subscription_not_found").Translate(user.Language))
html.OK(w, r, v.Render("add_subscription"))
case n == 1:
feed, err := feedHandler.CreateFeed(h.store, user.ID, &model.FeedCreationRequest{
feed, localizedError := feedHandler.CreateFeed(h.store, user.ID, &model.FeedCreationRequest{
CategoryID: subscriptionForm.CategoryID,
FeedURL: subscriptions[0].URL,
Crawler: subscriptionForm.Crawler,
@ -95,9 +96,9 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
UrlRewriteRules: subscriptionForm.UrlRewriteRules,
FetchViaProxy: subscriptionForm.FetchViaProxy,
})
if err != nil {
if localizedError != nil {
v.Set("form", subscriptionForm)
v.Set("errorMessage", err)
v.Set("errorMessage", localizedError.Translate(user.Language))
html.OK(w, r, v.Render("add_subscription"))
return
}

View file

@ -9,6 +9,7 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/model"
"miniflux.app/v2/internal/ui/form"
"miniflux.app/v2/internal/ui/session"
@ -38,14 +39,14 @@ func (h *handler) saveUser(w http.ResponseWriter, r *http.Request) {
view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
view.Set("form", userForm)
if err := userForm.ValidateCreation(); err != nil {
view.Set("errorMessage", err.Error())
if validationErr := userForm.ValidateCreation(); validationErr != nil {
view.Set("errorMessage", validationErr.Translate(user.Language))
html.OK(w, r, view.Render("create_user"))
return
}
if h.store.UserExists(userForm.Username) {
view.Set("errorMessage", "error.user_already_exists")
view.Set("errorMessage", locale.NewLocalizedError("error.user_already_exists").Translate(user.Language))
html.OK(w, r, view.Render("create_user"))
return
}
@ -57,7 +58,7 @@ func (h *handler) saveUser(w http.ResponseWriter, r *http.Request) {
}
if validationErr := validator.ValidateUserCreationWithPassword(h.store, userCreationRequest); validationErr != nil {
view.Set("errorMessage", validationErr.TranslationKey)
view.Set("errorMessage", validationErr.Translate(user.Language))
html.OK(w, r, view.Render("create_user"))
return
}

View file

@ -9,6 +9,7 @@ import (
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
"miniflux.app/v2/internal/locale"
"miniflux.app/v2/internal/ui/form"
"miniflux.app/v2/internal/ui/session"
"miniflux.app/v2/internal/ui/view"
@ -49,14 +50,14 @@ func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
view.Set("selected_user", selectedUser)
view.Set("form", userForm)
if err := userForm.ValidateModification(); err != nil {
view.Set("errorMessage", err.Error())
if validationErr := userForm.ValidateModification(); validationErr != nil {
view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
html.OK(w, r, view.Render("edit_user"))
return
}
if h.store.AnotherUserExists(selectedUser.ID, userForm.Username) {
view.Set("errorMessage", "error.user_already_exists")
view.Set("errorMessage", locale.NewLocalizedError("error.user_already_exists").Translate(loggedUser.Language))
html.OK(w, r, view.Render("edit_user"))
return
}