feat: add role field to user

This commit is contained in:
Steven 2023-03-16 08:08:36 +08:00
parent 4756fba2d6
commit a6a8775153
6 changed files with 31 additions and 35 deletions

View File

@ -19,6 +19,7 @@ type User struct {
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
PasswordHash string `json:"-"` PasswordHash string `json:"-"`
OpenID string `json:"openId"` OpenID string `json:"openId"`
Role Role `json:"role"`
UserSettingList []*UserSetting `json:"userSettingList"` UserSettingList []*UserSetting `json:"userSettingList"`
} }
@ -28,15 +29,16 @@ type UserCreate struct {
Password string `json:"password"` Password string `json:"password"`
PasswordHash string `json:"-"` PasswordHash string `json:"-"`
OpenID string `json:"-"` OpenID string `json:"-"`
Role Role `json:"-"`
} }
func (create UserCreate) Validate() error { func (create UserCreate) Validate() error {
if !common.ValidateEmail(create.Email) {
return fmt.Errorf("invalid email format")
}
if len(create.Email) < 3 { if len(create.Email) < 3 {
return fmt.Errorf("email is too short, minimum length is 6") return fmt.Errorf("email is too short, minimum length is 6")
} }
if !common.ValidateEmail(create.Email) {
return fmt.Errorf("invalid email format")
}
if len(create.Password) < 3 { if len(create.Password) < 3 {
return fmt.Errorf("password is too short, minimum length is 6") return fmt.Errorf("password is too short, minimum length is 6")
} }
@ -69,6 +71,7 @@ type UserFind struct {
Email *string `json:"email"` Email *string `json:"email"`
DisplayName *string `json:"displayName"` DisplayName *string `json:"displayName"`
OpenID *string `json:"openId"` OpenID *string `json:"openId"`
Role *Role `json:"-"`
} }
type UserDelete struct { type UserDelete struct {

View File

@ -21,11 +21,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch user list").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch user list").SetInternal(err)
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) return c.JSON(http.StatusOK, composeResponse(userList))
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(userList)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user list response").SetInternal(err)
}
return nil
}) })
// GET /api/user/me is used to check if the user is logged in. // GET /api/user/me is used to check if the user is logged in.
@ -52,11 +48,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
} }
user.UserSettingList = userSettingList user.UserSettingList = userSettingList
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) return c.JSON(http.StatusOK, composeResponse(user))
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(user)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user response").SetInternal(err)
}
return nil
}) })
g.GET("/user/:id", func(c echo.Context) error { g.GET("/user/:id", func(c echo.Context) error {
@ -73,11 +65,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch user").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to fetch user").SetInternal(err)
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) return c.JSON(http.StatusOK, composeResponse(user))
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(user)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user response").SetInternal(err)
}
return nil
}) })
g.PATCH("/user/:id", func(c echo.Context) error { g.PATCH("/user/:id", func(c echo.Context) error {
@ -133,11 +121,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to patch user").SetInternal(err)
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) return c.JSON(http.StatusOK, composeResponse(user))
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(user)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user response").SetInternal(err)
}
return nil
}) })
g.POST("/user/setting", func(c echo.Context) error { g.POST("/user/setting", func(c echo.Context) error {
@ -161,11 +145,7 @@ func (s *Server) registerUserRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert user setting").SetInternal(err)
} }
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) return c.JSON(http.StatusOK, composeResponse(userSetting))
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(userSetting)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode user setting response").SetInternal(err)
}
return nil
}) })
g.DELETE("/user/:id", func(c echo.Context) error { g.DELETE("/user/:id", func(c echo.Context) error {

View File

@ -13,7 +13,8 @@ CREATE TABLE user (
email TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL, display_name TEXT NOT NULL,
password_hash TEXT NOT NULL, password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE open_id TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'USER')) DEFAULT 'USER'
); );
INSERT INTO INSERT INTO

View File

@ -13,7 +13,8 @@ CREATE TABLE user (
email TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL, display_name TEXT NOT NULL,
password_hash TEXT NOT NULL, password_hash TEXT NOT NULL,
open_id TEXT NOT NULL UNIQUE open_id TEXT NOT NULL UNIQUE,
role TEXT NOT NULL CHECK (role IN ('ADMIN', 'USER')) DEFAULT 'USER'
); );
INSERT INTO INSERT INTO

View File

@ -13,7 +13,7 @@ type Store struct {
profile *profile.Profile profile *profile.Profile
userCache sync.Map // map[int]*userRaw userCache sync.Map // map[int]*userRaw
workspaceCache sync.Map // map[int]*memoRaw workspaceCache sync.Map // map[int]*workspaceRaw
shortcutCache sync.Map // map[int]*shortcutRaw shortcutCache sync.Map // map[int]*shortcutRaw
} }

View File

@ -25,6 +25,7 @@ type userRaw struct {
DisplayName string DisplayName string
PasswordHash string PasswordHash string
OpenID string OpenID string
Role api.Role
} }
func (raw *userRaw) toUser() *api.User { func (raw *userRaw) toUser() *api.User {
@ -39,6 +40,7 @@ func (raw *userRaw) toUser() *api.User {
DisplayName: raw.DisplayName, DisplayName: raw.DisplayName,
PasswordHash: raw.PasswordHash, PasswordHash: raw.PasswordHash,
OpenID: raw.OpenID, OpenID: raw.OpenID,
Role: raw.Role,
} }
} }
@ -161,10 +163,11 @@ func createUser(ctx context.Context, tx *sql.Tx, create *api.UserCreate) (*userR
email, email,
display_name, display_name,
password_hash, password_hash,
open_id open_id,
role
) )
VALUES (?, ?, ?, ?) VALUES (?, ?, ?, ?)
RETURNING id, created_ts, updated_ts, row_status, email, display_name, password_hash, open_id RETURNING id, created_ts, updated_ts, row_status, email, display_name, password_hash, open_id, role
` `
var userRaw userRaw var userRaw userRaw
if err := tx.QueryRowContext(ctx, query, if err := tx.QueryRowContext(ctx, query,
@ -172,6 +175,7 @@ func createUser(ctx context.Context, tx *sql.Tx, create *api.UserCreate) (*userR
create.DisplayName, create.DisplayName,
create.PasswordHash, create.PasswordHash,
create.OpenID, create.OpenID,
create.Role,
).Scan( ).Scan(
&userRaw.ID, &userRaw.ID,
&userRaw.CreatedTs, &userRaw.CreatedTs,
@ -181,6 +185,7 @@ func createUser(ctx context.Context, tx *sql.Tx, create *api.UserCreate) (*userR
&userRaw.DisplayName, &userRaw.DisplayName,
&userRaw.PasswordHash, &userRaw.PasswordHash,
&userRaw.OpenID, &userRaw.OpenID,
&userRaw.Role,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -213,7 +218,7 @@ func patchUser(ctx context.Context, tx *sql.Tx, patch *api.UserPatch) (*userRaw,
UPDATE user UPDATE user
SET ` + strings.Join(set, ", ") + ` SET ` + strings.Join(set, ", ") + `
WHERE id = ? WHERE id = ?
RETURNING id, created_ts, updated_ts, row_status, email, display_name, password_hash, open_id RETURNING id, created_ts, updated_ts, row_status, email, display_name, password_hash, open_id, role
` `
row, err := tx.QueryContext(ctx, query, args...) row, err := tx.QueryContext(ctx, query, args...)
if err != nil { if err != nil {
@ -232,6 +237,7 @@ func patchUser(ctx context.Context, tx *sql.Tx, patch *api.UserPatch) (*userRaw,
&userRaw.DisplayName, &userRaw.DisplayName,
&userRaw.PasswordHash, &userRaw.PasswordHash,
&userRaw.OpenID, &userRaw.OpenID,
&userRaw.Role,
); err != nil { ); err != nil {
return nil, err return nil, err
} }
@ -258,6 +264,9 @@ func findUserList(ctx context.Context, tx *sql.Tx, find *api.UserFind) ([]*userR
if v := find.OpenID; v != nil { if v := find.OpenID; v != nil {
where, args = append(where, "open_id = ?"), append(args, *v) where, args = append(where, "open_id = ?"), append(args, *v)
} }
if v := find.Role; v != nil {
where, args = append(where, "role = ?"), append(args, *v)
}
query := ` query := `
SELECT SELECT
@ -268,7 +277,8 @@ func findUserList(ctx context.Context, tx *sql.Tx, find *api.UserFind) ([]*userR
email, email,
display_name, display_name,
password_hash, password_hash,
open_id open_id,
role
FROM user FROM user
WHERE ` + strings.Join(where, " AND ") + ` WHERE ` + strings.Join(where, " AND ") + `
ORDER BY updated_ts DESC, created_ts DESC ORDER BY updated_ts DESC, created_ts DESC
@ -291,6 +301,7 @@ func findUserList(ctx context.Context, tx *sql.Tx, find *api.UserFind) ([]*userR
&userRaw.DisplayName, &userRaw.DisplayName,
&userRaw.PasswordHash, &userRaw.PasswordHash,
&userRaw.OpenID, &userRaw.OpenID,
&userRaw.Role,
); err != nil { ); err != nil {
return nil, err return nil, err
} }