-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuser.go
More file actions
149 lines (132 loc) · 4.29 KB
/
Copy pathuser.go
File metadata and controls
149 lines (132 loc) · 4.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package deploykit
import (
"context"
"time"
)
// Role represents a user's authorization level.
type Role string
const (
RoleAdmin Role = "admin"
RoleMember Role = "member"
)
// ValidRole reports whether r is a known role.
func ValidRole(r Role) bool {
return r == RoleAdmin || r == RoleMember
}
// User represents a registered user account.
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Role Role `json:"role"`
PasswordHash string `json:"-"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// UserService manages users.
type UserService interface {
// CreateUser creates a new user. The password is hashed by the
// implementation. The ID and timestamps are set automatically.
// Returns ECONFLICT if the email is already taken.
CreateUser(ctx context.Context, create UserCreate) (*User, error)
// GetUser returns a user by ID.
// Returns ENOTFOUND if the user does not exist.
GetUser(ctx context.Context, id string) (*User, error)
// ListUsers returns a filtered, paginated list of users
// and the total matching count.
ListUsers(ctx context.Context, filter UserFilter) ([]*User, int, error)
// UpdateUser applies a partial update to a user by ID.
// Returns the updated user. Returns ENOTFOUND if not found.
// Returns ECONFLICT if the new email is already taken.
UpdateUser(ctx context.Context, id string, update UserUpdate) (*User, error)
// DeleteUser permanently removes a user by ID.
// Returns ENOTFOUND if not found.
DeleteUser(ctx context.Context, id string) error
}
// UserCreate holds fields required to create a user.
type UserCreate struct {
Email string `json:"email"`
Name string `json:"name"`
Password string `json:"password"`
Role Role `json:"role"`
}
// Validate checks that all required fields are present.
func (c *UserCreate) Validate() error {
ve := NewValidationErrors()
if c.Email == "" {
ve.Add("email", "Email is required.")
}
if c.Name == "" {
ve.Add("name", "Name is required.")
}
if c.Password == "" {
ve.Add("password", "Password is required.")
} else if len(c.Password) < 8 {
ve.Add("password", "Password must be at least 8 characters.")
}
return ve.Err()
}
// UserUpdate holds fields that can be updated on a user.
// Nil pointer fields are left unchanged.
type UserUpdate struct {
Email *string `json:"email"`
Name *string `json:"name"`
Password *string `json:"password"`
Role *Role `json:"role"`
}
// Validate checks update fields.
func (u *UserUpdate) Validate() error {
ve := NewValidationErrors()
if u.Email != nil && *u.Email == "" {
ve.Add("email", "Email cannot be empty.")
}
if u.Name != nil && *u.Name == "" {
ve.Add("name", "Name cannot be empty.")
}
if u.Password != nil && *u.Password == "" {
ve.Add("password", "Password cannot be empty.")
} else if u.Password != nil && len(*u.Password) < 8 {
ve.Add("password", "Password must be at least 8 characters.")
}
if u.Role != nil && !ValidRole(*u.Role) {
ve.Add("role", "Role must be 'admin' or 'member'.")
}
return ve.Err()
}
// ProfileUpdate holds fields a user can update on their own profile.
// CurrentPassword is required only when changing the password.
// Role is intentionally excluded — users cannot change their own role.
type ProfileUpdate struct {
Email *string `json:"email"`
Name *string `json:"name"`
NewPassword *string `json:"new_password"`
CurrentPassword string `json:"current_password"`
}
// Validate checks profile update fields.
func (u *ProfileUpdate) Validate() error {
ve := NewValidationErrors()
if u.Email != nil && *u.Email == "" {
ve.Add("email", "Email cannot be empty.")
}
if u.Name != nil && *u.Name == "" {
ve.Add("name", "Name cannot be empty.")
}
if u.NewPassword != nil {
if *u.NewPassword == "" {
ve.Add("new_password", "New password cannot be empty.")
} else if len(*u.NewPassword) < 8 {
ve.Add("new_password", "New password must be at least 8 characters.")
}
if u.CurrentPassword == "" {
ve.Add("current_password", "Current password is required to change your password.")
}
}
return ve.Err()
}
// UserFilter controls filtering and pagination for listing users.
type UserFilter struct {
Email *string
Role *Role
Offset int
Limit int
}