diff --git a/api/user.go b/api/user.go index ef8acbd..5437fed 100644 --- a/api/user.go +++ b/api/user.go @@ -2,8 +2,7 @@ package api import ( "fmt" - - "github.com/boojack/shortify/common" + "net/mail" ) type User struct { @@ -15,12 +14,11 @@ type User struct { RowStatus RowStatus `json:"rowStatus"` // Domain specific fields - Email string `json:"email"` - DisplayName string `json:"displayName"` - PasswordHash string `json:"-"` - OpenID string `json:"openId"` - Role Role `json:"role"` - UserSettingList []*UserSetting `json:"userSettingList"` + Email string `json:"email"` + DisplayName string `json:"displayName"` + PasswordHash string `json:"-"` + OpenID string `json:"openId"` + Role Role `json:"role"` } type UserCreate struct { @@ -36,7 +34,7 @@ func (create UserCreate) Validate() error { if len(create.Email) < 3 { return fmt.Errorf("email is too short, minimum length is 6") } - if !common.ValidateEmail(create.Email) { + if !validateEmail(create.Email) { return fmt.Errorf("invalid email format") } if len(create.Password) < 3 { @@ -77,3 +75,11 @@ type UserFind struct { type UserDelete struct { ID int } + +// validateEmail validates the email. +func validateEmail(email string) bool { + if _, err := mail.ParseAddress(email); err != nil { + return false + } + return true +} diff --git a/api/v1/user.go b/api/v1/user.go index 07f9945..baaf989 100644 --- a/api/v1/user.go +++ b/api/v1/user.go @@ -2,8 +2,8 @@ package v1 import ( "fmt" + "net/mail" - "github.com/boojack/shortify/common" "github.com/labstack/echo/v4" ) @@ -57,7 +57,7 @@ func (create UserCreate) Validate() error { if len(create.Email) < 3 { return fmt.Errorf("email is too short, minimum length is 6") } - if !common.ValidateEmail(create.Email) { + if !validateEmail(create.Email) { return fmt.Errorf("invalid email format") } if len(create.Password) < 3 { @@ -104,3 +104,11 @@ func (*APIV1Service) RegisterUserRoutes(g *echo.Group) { return c.String(200, "GET /user") }) } + +// validateEmail validates the email. +func validateEmail(email string) bool { + if _, err := mail.ParseAddress(email); err != nil { + return false + } + return true +} diff --git a/common/util.go b/common/util.go deleted file mode 100644 index 1c8a024..0000000 --- a/common/util.go +++ /dev/null @@ -1,30 +0,0 @@ -package common - -import ( - "net/mail" - "strings" - - "github.com/google/uuid" -) - -// HasPrefixes returns true if the string s has any of the given prefixes. -func HasPrefixes(src string, prefixes ...string) bool { - for _, prefix := range prefixes { - if strings.HasPrefix(src, prefix) { - return true - } - } - return false -} - -// ValidateEmail validates the email. -func ValidateEmail(email string) bool { - if _, err := mail.ParseAddress(email); err != nil { - return false - } - return true -} - -func GenUUID() string { - return uuid.New().String() -} diff --git a/common/util_test.go b/common/util_test.go deleted file mode 100644 index 9cd7af8..0000000 --- a/common/util_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package common - -import ( - "testing" -) - -func TestValidateEmail(t *testing.T) { - tests := []struct { - email string - want bool - }{ - { - email: "t@gmail.com", - want: true, - }, - { - email: "@qq.com", - want: false, - }, - { - email: "1@gmail", - want: true, - }, - } - for _, test := range tests { - result := ValidateEmail(test.email) - if result != test.want { - t.Errorf("Validate Email %s: got result %v, want %v.", test.email, result, test.want) - } - } -} diff --git a/server/acl.go b/server/acl.go index dd63e16..30d6a58 100644 --- a/server/acl.go +++ b/server/acl.go @@ -6,7 +6,6 @@ import ( "strconv" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" @@ -69,7 +68,7 @@ func aclMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc { ID: &userID, } user, err := s.Store.FindUser(ctx, userFind) - if err != nil && common.ErrorCode(err) != common.NotFound { + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by ID: %d", userID)).SetInternal(err) } if user != nil { @@ -80,7 +79,7 @@ func aclMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc { } } - if common.HasPrefixes(path, "/api/ping", "/api/status", "/api/workspace") && c.Request().Method == http.MethodGet { + if hasPrefixes(path, "/api/ping", "/api/status", "/api/workspace") && c.Request().Method == http.MethodGet { return next(c) } diff --git a/server/auth.go b/server/auth.go index 0f84d7f..280db86 100644 --- a/server/auth.go +++ b/server/auth.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" + "github.com/google/uuid" "github.com/labstack/echo/v4" "golang.org/x/crypto/bcrypt" @@ -24,7 +24,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { Email: &signin.Email, } user, err := s.Store.FindUser(ctx, userFind) - if err != nil && common.ErrorCode(err) != common.NotFound { + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Failed to find user by email %s", signin.Email)).SetInternal(err) } if user == nil { @@ -56,7 +56,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { Email: signup.Email, DisplayName: signup.DisplayName, Password: signup.Password, - OpenID: common.GenUUID(), + OpenID: genUUID(), } if err := userCreate.Validate(); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid user create format.").SetInternal(err) @@ -102,3 +102,7 @@ func (s *Server) registerAuthRoutes(g *echo.Group) { return nil }) } + +func genUUID() string { + return uuid.New().String() +} diff --git a/server/common.go b/server/common.go index 39fc5cd..daf8c62 100644 --- a/server/common.go +++ b/server/common.go @@ -1,8 +1,9 @@ package server import ( + "strings" + "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" "github.com/labstack/echo/v4" ) @@ -16,9 +17,19 @@ func composeResponse(data any) any { } } +// hasPrefixes returns true if the string s has any of the given prefixes. +func hasPrefixes(src string, prefixes ...string) bool { + for _, prefix := range prefixes { + if strings.HasPrefix(src, prefix) { + return true + } + } + return false +} + func defaultAPIRequestSkipper(c echo.Context) bool { path := c.Path() - return common.HasPrefixes(path, "/api", "/o") + return hasPrefixes(path, "/api", "/o") } func (server *Server) defaultAuthSkipper(c echo.Context) bool { @@ -26,7 +37,7 @@ func (server *Server) defaultAuthSkipper(c echo.Context) bool { path := c.Path() // Skip auth. - if common.HasPrefixes(path, "/api/auth") { + if hasPrefixes(path, "/api/auth") { return true } @@ -37,7 +48,7 @@ func (server *Server) defaultAuthSkipper(c echo.Context) bool { OpenID: &openID, } user, err := server.Store.FindUser(ctx, userFind) - if err != nil && common.ErrorCode(err) != common.NotFound { + if err != nil { return false } if user != nil { diff --git a/server/shortcut.go b/server/shortcut.go index 5e27615..9401f23 100644 --- a/server/shortcut.go +++ b/server/shortcut.go @@ -7,7 +7,6 @@ import ( "strconv" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" "github.com/labstack/echo/v4" ) @@ -30,7 +29,7 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { Name: &shortcutCreate.Name, WorkspaceID: &shortcutCreate.WorkspaceID, }) - if err != nil && common.ErrorCode(err) != common.NotFound { + if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find shortcut").SetInternal(err) } if existingShortcut != nil { @@ -178,9 +177,6 @@ func (s *Server) registerShortcutRoutes(g *echo.Group) { ID: &shortcutID, } if err := s.Store.DeleteShortcut(ctx, shortcutDelete); err != nil { - if common.ErrorCode(err) == common.NotFound { - return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Shortcut ID not found: %d", shortcutID)) - } return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete shortcut").SetInternal(err) } diff --git a/server/user.go b/server/user.go index aaa3188..e513cd4 100644 --- a/server/user.go +++ b/server/user.go @@ -4,10 +4,10 @@ import ( "encoding/json" "fmt" "net/http" + "net/mail" "strconv" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" "github.com/labstack/echo/v4" "golang.org/x/crypto/bcrypt" @@ -40,14 +40,6 @@ func (s *Server) registerUserRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find user").SetInternal(err) } - userSettingList, err := s.Store.FindUserSettingList(ctx, &api.UserSettingFind{ - UserID: userID, - }) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find userSettingList").SetInternal(err) - } - user.UserSettingList = userSettingList - return c.JSON(http.StatusOK, composeResponse(user)) }) @@ -97,7 +89,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted patch user request").SetInternal(err) } - if userPatch.Email != nil && !common.ValidateEmail(*userPatch.Email) { + if userPatch.Email != nil && !validateEmail(*userPatch.Email) { return echo.NewHTTPError(http.StatusBadRequest, "Invalid email format") } @@ -112,7 +104,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) { } if userPatch.ResetOpenID != nil && *userPatch.ResetOpenID { - uuid := common.GenUUID() + uuid := genUUID() userPatch.OpenID = &uuid } @@ -124,30 +116,6 @@ func (s *Server) registerUserRoutes(g *echo.Group) { return c.JSON(http.StatusOK, composeResponse(user)) }) - g.POST("/user/setting", func(c echo.Context) error { - ctx := c.Request().Context() - userID, ok := c.Get(getUserIDContextKey()).(int) - if !ok { - return echo.NewHTTPError(http.StatusUnauthorized, "Missing auth session") - } - - userSettingUpsert := &api.UserSettingUpsert{} - if err := json.NewDecoder(c.Request().Body).Decode(userSettingUpsert); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post user setting upsert request").SetInternal(err) - } - if err := userSettingUpsert.Validate(); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid user setting format").SetInternal(err) - } - - userSettingUpsert.UserID = userID - userSetting, err := s.Store.UpsertUserSetting(ctx, userSettingUpsert) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err) - } - - return c.JSON(http.StatusOK, composeResponse(userSetting)) - }) - g.DELETE("/user/:id", func(c echo.Context) error { ctx := c.Request().Context() currentUserID, ok := c.Get(getUserIDContextKey()).(int) @@ -173,12 +141,17 @@ func (s *Server) registerUserRoutes(g *echo.Group) { ID: userID, } if err := s.Store.DeleteUser(ctx, userDelete); err != nil { - if common.ErrorCode(err) == common.NotFound { - return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("User ID not found: %d", userID)) - } return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete user").SetInternal(err) } return c.JSON(http.StatusOK, true) }) } + +// validateEmail validates the email. +func validateEmail(email string) bool { + if _, err := mail.ParseAddress(email); err != nil { + return false + } + return true +} diff --git a/server/workspace.go b/server/workspace.go index f216689..63d4850 100644 --- a/server/workspace.go +++ b/server/workspace.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" + "github.com/boojack/shortify/internal/errorutil" "github.com/labstack/echo/v4" ) @@ -108,7 +108,7 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) { Name: &workspaceName, }) if err != nil { - if common.ErrorCode(err) == common.NotFound { + if errorutil.ErrorCode(err) == errorutil.NotFound { return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("workspace not found by name %s", workspaceName)) } return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find workspace with name %s", workspaceName)).SetInternal(err) @@ -119,9 +119,6 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) { Name: &shortcutName, }) if err != nil { - if common.ErrorCode(err) == common.NotFound { - return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("shortcut not found by name %s", shortcutName)) - } return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find shortcut with name %s", shortcutName)).SetInternal(err) } @@ -140,9 +137,6 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) { UserID: &userID, }) if err != nil { - if common.ErrorCode(err) == common.NotFound { - return echo.NewHTTPError(http.StatusNotFound, "workspace user not found") - } return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find workspace user").SetInternal(err) } if workspaceUser == nil { @@ -224,8 +218,8 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) { if err := s.Store.DeleteWorkspace(ctx, &api.WorkspaceDelete{ ID: workspaceID, }); err != nil { - if common.ErrorCode(err) == common.NotFound { - return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("User ID not found: %d", userID)) + if errorutil.ErrorCode(err) == errorutil.NotFound { + return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("workspace %d not found", workspaceID)) } return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete user").SetInternal(err) } diff --git a/server/workspace_user.go b/server/workspace_user.go index 3fe9dd9..72dd274 100644 --- a/server/workspace_user.go +++ b/server/workspace_user.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/boojack/shortify/api" - "github.com/boojack/shortify/common" + "github.com/boojack/shortify/internal/errorutil" "github.com/labstack/echo/v4" ) @@ -136,7 +136,7 @@ func (s *Server) registerWorkspaceUserRoutes(g *echo.Group) { UserID: userID, } if err := s.Store.DeleteWorkspaceUser(ctx, workspaceUserDelete); err != nil { - if common.ErrorCode(err) == common.NotFound { + if errorutil.ErrorCode(err) == errorutil.NotFound { return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("Workspace user not found with workspace id %d and user id %d", workspaceID, userID)) } return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete workspace user").SetInternal(err) @@ -147,9 +147,7 @@ func (s *Server) registerWorkspaceUserRoutes(g *echo.Group) { WorkspaceID: &workspaceID, } if err := s.Store.DeleteShortcut(ctx, shortcutDelete); err != nil { - if common.ErrorCode(err) != common.NotFound { - return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete shortcut").SetInternal(err) - } + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete shortcut").SetInternal(err) } return c.JSON(http.StatusOK, true)