Skip to content

Commit 6672fa3

Browse files
new user model fields and services
1 parent b86f893 commit 6672fa3

File tree

12 files changed

+230
-100
lines changed

12 files changed

+230
-100
lines changed

data/seed_users.json

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
"first_name": "John",
88
"last_name": "Doe",
99
"is_active": true,
10-
"role": "admin"
10+
"role": "admin",
11+
"avatar_url": "https://i.pravatar.cc/150?img=3",
12+
"bio": "Just a regular John Doe.",
13+
"location": "Ağrı, Turkey",
14+
"favorite_genre": [ "Science Fiction", "Fantasy" ],
15+
"reading_goal": 20
1116
},
1217
{
1318
"username": "janedoe",
@@ -16,7 +21,12 @@
1621
"first_name": "Jane",
1722
"last_name": "Doe",
1823
"is_active": true,
19-
"role": "user"
24+
"role": "user",
25+
"avatar_url": "https://i.pravatar.cc/150?img=5",
26+
"bio": "Avid reader and book lover.",
27+
"location": "Istanbul, Turkey",
28+
"favorite_genre": [ "Mystery", "Romance" ],
29+
"reading_goal": 15
2030
},
2131
{
2232
"username": "testuser1",
@@ -25,7 +35,12 @@
2535
"first_name": "Test",
2636
"last_name": "User1",
2737
"is_active": true,
28-
"role": "user"
38+
"role": "user",
39+
"avatar_url": "https://i.pravatar.cc/150?img=10",
40+
"bio": "Just a test user.",
41+
"location": "Ankara, Turkey",
42+
"favorite_genre": [ "Non-Fiction", "Biography" ],
43+
"reading_goal": 10
2944
},
3045
{
3146
"username": "testuser2",
@@ -34,7 +49,12 @@
3449
"first_name": "Test",
3550
"last_name": "User2",
3651
"is_active": true,
37-
"role": "user"
52+
"role": "user",
53+
"avatar_url": "https://i.pravatar.cc/150?img=15",
54+
"bio": "Another test user.",
55+
"location": "Izmir, Turkey",
56+
"favorite_genre": [ "Horror", "Thriller" ],
57+
"reading_goal": 5
3858
},
3959
{
4060
"username": "inactiveuser",
@@ -43,7 +63,12 @@
4363
"first_name": "Test",
4464
"last_name": "User2",
4565
"is_active": false,
46-
"role": "user"
66+
"role": "user",
67+
"avatar_url": "https://i.pravatar.cc/150?img=20",
68+
"bio": "Inactive user account.",
69+
"location": "Bursa, Turkey",
70+
"favorite_genre": [ "Poetry", "Drama" ],
71+
"reading_goal": 0
4772
},
4873
{
4974
"username": "admin",
@@ -52,7 +77,12 @@
5277
"first_name": "Admin",
5378
"last_name": "User",
5479
"is_active": true,
55-
"role": "admin"
80+
"role": "admin",
81+
"avatar_url": "https://i.pravatar.cc/150?img=25",
82+
"bio": "Administrator of the platform.",
83+
"location": "Metropolis, Earth",
84+
"favorite_genre": [ "All" ],
85+
"reading_goal": 50
5686
},
5787
{
5888
"username": "qa_tester",
@@ -61,7 +91,12 @@
6191
"first_name": "QA",
6292
"last_name": "Tester",
6393
"is_active": true,
64-
"role": "user"
94+
"role": "user",
95+
"avatar_url": "https://i.pravatar.cc/150?img=30",
96+
"bio": "Quality Assurance Tester.",
97+
"location": "Testville, Testland",
98+
"favorite_genre": [ "Technical", "Educational" ],
99+
"reading_goal": 25
65100
}
66101
]
67102
}

internal/database/seed.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ import (
1111
)
1212

1313
type SeedUser struct {
14-
Username string `json:"username"`
15-
Email string `json:"email"`
16-
Password string `json:"password"`
17-
FirstName string `json:"first_name"`
18-
LastName string `json:"last_name"`
19-
IsActive bool `json:"is_active"`
20-
Role string `json:"role"`
14+
Username string `json:"username"`
15+
Email string `json:"email"`
16+
Password string `json:"password"`
17+
FirstName string `json:"first_name"`
18+
LastName string `json:"last_name"`
19+
IsActive bool `json:"is_active"`
20+
Role string `json:"role"`
21+
AvatarURL string `json:"avatar_url"`
22+
Bio string `json:"bio"`
23+
Location string `json:"location"`
24+
FavoriteGenres []string `json:"favorite_genre"`
25+
ReadingGoal int `json:"reading_goal"`
2126
}
2227

2328
type SeedData struct {
@@ -26,9 +31,9 @@ type SeedData struct {
2631

2732
func Seed(db *gorm.DB) error {
2833
log.Println("starting database seeding")
29-
34+
3035
var userCount int64
31-
36+
3237
db.Model(&models.User{}).Count(&userCount)
3338
if userCount > 0 {
3439
log.Println("database already seeded")
@@ -55,13 +60,18 @@ func Seed(db *gorm.DB) error {
5560
}
5661

5762
user := &models.User{
58-
Username: seedUser.Username,
59-
Email: seedUser.Email,
60-
PasswordHash: hashedPassword,
61-
FirstName: seedUser.FirstName,
62-
LastName: seedUser.LastName,
63-
IsActive: seedUser.IsActive,
64-
Role: seedUser.Role,
63+
Username: seedUser.Username,
64+
Email: seedUser.Email,
65+
PasswordHash: hashedPassword,
66+
FirstName: seedUser.FirstName,
67+
LastName: seedUser.LastName,
68+
IsActive: seedUser.IsActive,
69+
Role: seedUser.Role,
70+
AvatarURL: &seedUser.AvatarURL,
71+
Bio: &seedUser.Bio,
72+
Location: &seedUser.Location,
73+
FavoriteGenres: seedUser.FavoriteGenres,
74+
ReadingGoal: seedUser.ReadingGoal,
6575
}
6676

6777
if err := db.Create(user).Error; err != nil {
@@ -86,4 +96,4 @@ func SeedForTest(db *gorm.DB) error {
8696
}
8797

8898
return Seed(db)
89-
}
99+
}

internal/middleware/auth.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ func AuthMiddleware(cfg *config.Config) gin.HandlerFunc {
2828
}
2929

3030
c.Set("user_id", claims.UserID)
31-
c.Set("role", claims.Role)
31+
c.Set("user_email", claims.Email)
32+
c.Set("user_role", claims.Role)
3233
c.Next()
3334
}
3435
}
3536

37+
/** Ensure that the user can only access their own resources
38+
* If the user ID in the path parameter does not match the user ID in the token, return 403 Forbidden
39+
* If there is no user ID in the path parameter, allow access (for routes that do not require a specific user ID)
40+
*/
3641
func AuthorizeSelf() gin.HandlerFunc {
3742
return func (c *gin.Context) {
3843
ctxUserIDRaw, exists := c.Get("user_id")
@@ -72,9 +77,12 @@ func AuthorizeSelf() gin.HandlerFunc {
7277
}
7378
}
7479

80+
/** Restrict access to certain roles
81+
* @param allowedRoles - list of roles that are allowed to access the route
82+
*/
7583
func RestrictToRoles(allowedRoles ...string) gin.HandlerFunc {
7684
return func(c *gin.Context) {
77-
userRoleRaw, exists := c.Get("role")
85+
userRoleRaw, exists := c.Get("user_role")
7886
if !exists {
7987
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
8088
c.Abort()

internal/models/annotation.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import (
1010
type Annotation struct {
1111
ID uint `json:"id" gorm:"primaryKey"`
1212
BookID uint `json:"book_id" gorm:"not null;foreignKey:BookID"`
13-
AuthorID uint `json:"author_id" gorm:"not null;foreignKey:AuthorID"`
13+
UserID uint `json:"user_id" gorm:"not null;foreignKey:UserID"`
1414
Quote string `json:"quote" gorm:"type:text;not null"`
1515
PageNumber *int `json:"page,omitempty"`
1616
Thoughts string `json:"thoughts,omitempty" gorm:"type:text"`
1717
IsPublic bool `json:"is_public" gorm:"default:true"`
1818
LikesCount int `json:"likes_count" gorm:"default:0"`
1919
Tags pq.StringArray `json:"tags" gorm:"type:text[]"`
2020
Book Book `json:"book" gorm:"foreignKey:BookID"`
21-
Author User `json:"author" gorm:"foreignKey:AuthorID"`
21+
User User `json:"user" gorm:"foreignKey:UserID"`
2222

2323
CreatedAt time.Time `json:"created_at"`
2424
UpdatedAt time.Time `json:"updated_at"`

internal/models/book.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
type Book struct {
1010
ID uint `json:"id" gorm:"primaryKey"`
1111
Title string `json:"title" gorm:"size:255;not null"`
12-
Author *string `json:"author,omitempty" gorm:"size:255"`
12+
User *string `json:"user,omitempty" gorm:"size:255"`
1313
CoverURL *string `json:"cover_url,omitempty" gorm:"type:text"`
1414
Genre *string `json:"genre,omitempty" gorm:"size:100"`
1515
Pages *int `json:"pages,omitempty"`

internal/models/club.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010

1111
type CurrentBook struct {
1212
Title string `json:"title"`
13-
Author *string `json:"author,omitempty"`
13+
User *string `json:"user,omitempty"`
1414
CoverURL *string `json:"cover_url,omitempty"`
1515
BookID *uint `json:"book_id,omitempty"`
1616
Progress *int `json:"progress,omitempty"`

internal/models/comment.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import (
99
type Comment struct {
1010
ID uint `json:"id" gorm:"primaryKey"`
1111
PostID uint `json:"post_id" gorm:"not null; foreignKey:PostID"`
12-
AuthorID uint `json:"author_id" gorm:"not null;foreignKey:AuthorID"`
12+
UserID uint `json:"user_id" gorm:"not null;foreignKey:UserID"`
1313
Content string `json:"content" gorm:"type:text;not null"`
1414
LikesCount int `json:"likes_count" gorm:"default:0"`
15-
Author User `json:"author" gorm:"foreignKey:AuthorID"`
15+
User User `json:"user" gorm:"foreignKey:UserID"`
1616
Likes []CommentLike `json:"likes,omitempty" gorm:"foreignKey:CommentID"`
1717

1818
CreatedAt time.Time `json:"created_at"`

internal/models/post.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ type Post struct {
1515
LikesCount int `json:"likes_count" gorm:"default:0"`
1616
CommentsCount int `json:"comments_count" gorm:"default:0"`
1717
ViewsCount int `json:"views_count" gorm:"default:0"`
18-
AuthorID uint `json:"author_id"`
18+
UserID uint `json:"user_id"`
1919
ClubID uint `json:"club_id"`
20-
Author User `json:"author" gorm:"foreignKey:AuthorID"`
20+
User User `json:"user" gorm:"foreignKey:UserID"`
2121
Comments []Comment `json:"comments,omitempty" gorm:"foreignKey:PostID"`
2222
Likes []PostLike `json:"likes,omitempty" gorm:"foreignKey:PostID"`
2323

internal/models/user.go

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ type User struct {
2929

3030
OwnedClubs []Club `json:"owned_clubs,omitempty" gorm:"foreignKey:OwnerID"`
3131
ClubMemberships []ClubMembership `json:"club_memberships,omitempty" gorm:"foreignKey:UserID"`
32-
Posts []Post `json:"posts,omitempty" gorm:"foreignKey:AuthorID"`
33-
Comments []Comment `json:"comments,omitempty" gorm:"foreignKey:AuthorID"`
32+
Posts []Post `json:"posts,omitempty" gorm:"foreignKey:UserID"`
33+
Comments []Comment `json:"comments,omitempty" gorm:"foreignKey:UserID"`
3434
Annotations []Annotation `json:"annotations,omitempty" gorm:"foreignKey:UserID"`
3535
PostLikes []PostLike `json:"post_likes,omitempty" gorm:"foreignKey:UserID"`
3636
CommentLikes []CommentLike `json:"comment_likes,omitempty" gorm:"foreignKey:UserID"`
@@ -41,13 +41,32 @@ type User struct {
4141
}
4242

4343
type UserResponse struct {
44-
ID uint `json:"id"`
45-
Username string `json:"username"`
46-
Email string `json:"email"`
47-
FirstName string `json:"first_name"`
48-
LastName string `json:"last_name"`
49-
IsActive bool `json:"is_active"`
50-
Role string `json:"role"`
44+
ID uint `json:"id"`
45+
Username string `json:"username"`
46+
Email string `json:"email"`
47+
FirstName string `json:"first_name"`
48+
LastName string `json:"last_name"`
49+
IsActive bool `json:"is_active"`
50+
Role string `json:"role"`
51+
52+
AvatarURL *string `json:"avatar_url"`
53+
Location *string `json:"location"`
54+
FavoriteGenres pq.StringArray `json:"favorite_genres"`
55+
Bio *string `json:"bio"`
56+
ReadingGoal *int `json:"reading_goal"`
57+
BooksRead int `json:"books_read"`
58+
Badges pq.StringArray `json:"badges"`
59+
IsOnline bool `json:"is_online"`
60+
LastSeen *time.Time `json:"last_seen"`
61+
62+
OwnedClubs []Club `json:"owned_clubs,omitempty"`
63+
ClubMemberships []ClubMembership `json:"club_memberships,omitempty"`
64+
Posts []Post `json:"posts,omitempty"`
65+
Comments []Comment `json:"comments,omitempty"`
66+
Annotations []Annotation `json:"annotations,omitempty"`
67+
PostLikes []PostLike `json:"post_likes,omitempty"`
68+
CommentLikes []CommentLike `json:"comment_likes,omitempty"`
69+
5170
CreatedAt time.Time `json:"created_at"`
5271
}
5372

@@ -64,36 +83,54 @@ type RegisterRequest struct {
6483
LastName string `json:"last_name" validate:"required,min=2,max=50"`
6584
Role string `json:"role" validate:"omitempty,oneof=admin user moderator support superuser" gorm:"default:'user'"`
6685

86+
AvatarURL string `json:"avatar_url"`
6787
Location string `json:"location"`
6888
FavoriteGenres []string `json:"favorite_genres"`
6989
Bio string `json:"bio"`
7090
ReadingGoal int `json:"reading_goal"`
7191
}
7292

7393
type UpdateUserRequest struct {
74-
Username *string `json:"username" validate:"omitempty,min=3,max=50"`
75-
Email *string `json:"email" validate:"omitempty,email"`
76-
Password *string `json:"password" validate:"omitempty,min=6"`
77-
FirstName *string `json:"first_name" validate:"omitempty,min=2,max=50"`
78-
LastName *string `json:"last_name" validate:"omitempty,min=2,max=50"`
94+
Username *string `json:"username" validate:"omitempty,min=3,max=50"`
95+
Email *string `json:"email" validate:"omitempty,email"`
96+
Password *string `json:"password" validate:"omitempty,min=6"`
97+
FirstName *string `json:"first_name" validate:"omitempty,min=2,max=50"`
98+
LastName *string `json:"last_name" validate:"omitempty,min=2,max=50"`
99+
Role *string `json:"role" validate:"omitempty,oneof=admin user moderator support superuser"`
100+
IsActive *bool `json:"is_active"`
101+
79102
AvatarURL *string `json:"avatar_url" validate:"omitempty,url"`
80103
Location *string `json:"location" validate:"omitempty,max=255"`
81104
FavoriteGenres *[]string `json:"favorite_genres"`
82105
Bio *string `json:"bio" validate:"omitempty"`
83106
ReadingGoal *int `json:"reading_goal" validate:"omitempty,gte=0"`
84-
Role *string `json:"role" validate:"omitempty,oneof=admin user moderator support superuser"`
85-
IsActive *bool `json:"is_active"`
86107
}
87108

88109
func (u *User) ToResponse() UserResponse {
89110
return UserResponse{
90-
ID: u.ID,
91-
Username: u.Username,
92-
Email: u.Email,
93-
FirstName: u.FirstName,
94-
LastName: u.LastName,
95-
IsActive: u.IsActive,
96-
Role: u.Role,
97-
CreatedAt: u.CreatedAt,
111+
ID: u.ID,
112+
Username: u.Username,
113+
Email: u.Email,
114+
FirstName: u.FirstName,
115+
LastName: u.LastName,
116+
IsActive: u.IsActive,
117+
Role: u.Role,
118+
AvatarURL: u.AvatarURL,
119+
Location: u.Location,
120+
FavoriteGenres: u.FavoriteGenres,
121+
Bio: u.Bio,
122+
ReadingGoal: &u.ReadingGoal,
123+
BooksRead: u.BooksRead,
124+
Badges: u.Badges,
125+
IsOnline: u.IsOnline,
126+
LastSeen: u.LastSeen,
127+
OwnedClubs: u.OwnedClubs,
128+
ClubMemberships: u.ClubMemberships,
129+
Posts: u.Posts,
130+
Comments: u.Comments,
131+
Annotations: u.Annotations,
132+
PostLikes: u.PostLikes,
133+
CommentLikes: u.CommentLikes,
134+
CreatedAt: u.CreatedAt,
98135
}
99136
}

0 commit comments

Comments
 (0)