Skip to content

Commit 9ed81a0

Browse files
committed
Merge branch 'development' of https://github.com/hammercode-dev/lms-be into be-02/login-logout
2 parents 8dcc950 + 234568f commit 9ed81a0

36 files changed

Lines changed: 919 additions & 88 deletions

.github/workflows/development.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ jobs:
1111

1212
steps:
1313
- name: Checkout Code
14-
uses: actions/checkout@v2
14+
uses: actions/checkout@v4
1515

1616
- name: Set up Go
17-
uses: actions/setup-go@v2
17+
uses: actions/setup-go@v5
1818
with:
19-
go-version: '1.20'
19+
go-version: '1.24'
2020

2121
- name: Test
2222
run: go test -v ./...
@@ -28,7 +28,7 @@ jobs:
2828
run: tar -czvf main.tar.gz main
2929

3030
- name: Upload Artifact
31-
uses: actions/upload-artifact@v3
31+
uses: actions/upload-artifact@v4
3232
with:
3333
name: build-artifact
3434
path: main.tar.gz
@@ -39,10 +39,10 @@ jobs:
3939
environment: development
4040
steps:
4141
- name: Checkout Code
42-
uses: actions/checkout@v2
42+
uses: actions/checkout@v4
4343

4444
- name: Download Build Artifact
45-
uses: actions/download-artifact@v3
45+
uses: actions/download-artifact@v4
4646
with:
4747
name: build-artifact
4848

app/app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func InitApp(
4444
middleware := middlewares.InitMiddleware(jwtInstance, userRepo)
4545

4646
// usecase
47-
userUsecase := users.InitUsecase(userRepo, dbTx, jwtInstance)
47+
userUsecase := users.InitUsecase(cfg, userRepo, dbTx, jwtInstance)
4848
newsletterUC := newsletters.InitUsecase(cfg, newsletterRepo, dbTx, jwt.NewJwt(cfg.JWT_SECRET_KEY))
4949
eventUC := events.InitUsecase(eventRepo, imgRepo, dbTx)
5050
imgUc := images.InitUsecase(imgRepo, dbTx)

app/middlewares/auth_middleware.go

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,62 @@ import (
88
"github.com/hammer-code/lms-be/utils"
99
)
1010

11-
func (m *Middleware) AuthMiddleware(next http.Handler) http.Handler {
12-
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
13-
token := utils.ExtractBearerToken(request)
14-
if len(*token) < 5 {
15-
utils.Response(domain.HttpResponse{
16-
Code: 401,
17-
Message: "Forbidden",
18-
Data: nil,
19-
}, writer)
20-
return
21-
}
22-
23-
verifyToken, err := m.Jwt.VerifyToken(*token)
24-
if err != nil {
25-
utils.Response(domain.HttpResponse{
26-
Code: 500,
27-
Message: err.Error(),
28-
Data: nil,
29-
}, writer)
30-
return
31-
}
32-
33-
user, err := m.UserRepo.FindByEmail(request.Context(), verifyToken.Email)
34-
if err != nil {
35-
utils.Response(domain.HttpResponse{
36-
Code: 401,
37-
Message: "Forbidden",
38-
Data: nil,
39-
}, writer)
40-
return
41-
}
42-
43-
writer.Header().Set("x-user-id", strconv.Itoa(user.ID))
44-
writer.Header().Set("x-username", user.Username)
45-
46-
next.ServeHTTP(writer, request)
47-
})
11+
func (m *Middleware) AuthMiddleware(allowedRole string) domain.MiddlewareFunc {
12+
return func(next http.Handler) http.Handler {
13+
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
14+
token := utils.ExtractBearerToken(request)
15+
if len(*token) < 5 {
16+
utils.Response(domain.HttpResponse{
17+
Code: 401,
18+
Message: "Unauthorized",
19+
Data: nil,
20+
}, writer)
21+
return
22+
}
23+
24+
verifyToken, err := m.Jwt.VerifyToken(*token)
25+
if err != nil {
26+
utils.Response(domain.HttpResponse{
27+
Code: 500,
28+
Message: "failed to verify token",
29+
Data: nil,
30+
}, writer)
31+
return
32+
}
33+
34+
// tokenLogoutErr := m.UserRepo.ExpiredToken(request.Context(), *token)
35+
// if tokenLogoutErr == nil {
36+
// utils.Response(domain.HttpResponse{
37+
// Code: 401,
38+
// Message: "Token expired",
39+
// Data: nil,
40+
// }, writer)
41+
// return
42+
// }
43+
44+
user, err := m.UserRepo.FindByEmail(request.Context(), verifyToken.Email)
45+
if err != nil {
46+
utils.Response(domain.HttpResponse{
47+
Code: 401,
48+
Message: "Unauthorized",
49+
Data: nil,
50+
}, writer)
51+
return
52+
}
53+
54+
if user.Role != allowedRole {
55+
utils.Response(domain.HttpResponse{
56+
Code: 401,
57+
Message: "Unauthorized",
58+
Data: nil,
59+
}, writer)
60+
return
61+
}
62+
63+
writer.Header().Set("x-user-id", strconv.Itoa(user.ID))
64+
writer.Header().Set("x-username", user.Username)
65+
66+
next.ServeHTTP(writer, request)
67+
})
68+
}
4869
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package http
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"regexp"
8+
9+
"github.com/hammer-code/lms-be/domain"
10+
"github.com/hammer-code/lms-be/utils"
11+
)
12+
13+
func (h Handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
14+
reEmail := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`)
15+
16+
bodyBytes, err := io.ReadAll(r.Body)
17+
if err != nil {
18+
utils.Response(domain.HttpResponse{
19+
Code: 500,
20+
Message: err.Error(),
21+
Data: nil,
22+
}, w)
23+
return
24+
}
25+
26+
forgotPassword := domain.ForgotPassword{}
27+
if err = json.Unmarshal(bodyBytes, &forgotPassword); err != nil {
28+
utils.Response(domain.HttpResponse{
29+
Code: 500,
30+
Message: err.Error(),
31+
}, w)
32+
return
33+
}
34+
35+
if isValidEmail := reEmail.MatchString(forgotPassword.Email); !isValidEmail {
36+
utils.Response(domain.HttpResponse{
37+
Code: 400,
38+
Message: "Email is not valid",
39+
}, w)
40+
return
41+
}
42+
43+
if err := h.usecase.ForgotPassword(r.Context(), forgotPassword); err != nil {
44+
utils.Response(domain.HttpResponse{
45+
Code: 500,
46+
Message: err.Error(),
47+
}, w)
48+
return
49+
}
50+
51+
utils.Response(domain.HttpResponse{
52+
Code: 200,
53+
Message: "Request reset password success check your email",
54+
Data: nil,
55+
}, w)
56+
57+
}

app/users/delivery/http/register_users.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package http
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"io"
67
"net/http"
78

@@ -50,17 +51,17 @@ func (h Handler) Register(w http.ResponseWriter, r *http.Request) {
5051
}
5152

5253
userInput := domain.RegistToUser(user)
53-
resultUser, err := h.usecase.Register(r.Context(), userInput)
54+
_, err = h.usecase.Register(r.Context(), userInput)
5455
if err != nil {
5556
logrus.Error("userUsecase: failed to register user")
5657
resp := utils.CostumErr(err.Error())
5758
utils.Response(resp, w)
5859
return
5960
}
61+
message := fmt.Sprintf("User %s has been registered", user.Username)
6062

6163
utils.Response(domain.HttpResponse{
6264
Code: 200,
63-
Message: "success",
64-
Data: resultUser,
65+
Message: message,
6566
}, w)
6667
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package http
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http"
7+
"regexp"
8+
9+
"github.com/hammer-code/lms-be/domain"
10+
"github.com/hammer-code/lms-be/utils"
11+
)
12+
13+
func (h Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
14+
15+
passwordRegex := regexp.MustCompile(`^[a-zA-Z\d]{8,}$`)
16+
17+
bodyBytes, err := io.ReadAll(r.Body)
18+
if err != nil {
19+
utils.Response(domain.HttpResponse{
20+
Code: 500,
21+
Message: "Failed to read request body :" + err.Error(),
22+
}, w)
23+
return
24+
}
25+
26+
forgotPasswordInstance := domain.ForgotPassword{}
27+
28+
if err := json.Unmarshal(bodyBytes, &forgotPasswordInstance); err != nil {
29+
utils.Response(domain.HttpResponse{
30+
Code: 500,
31+
Message: "Failed to unmarshal request body :" + err.Error(),
32+
}, w)
33+
return
34+
}
35+
36+
if forgotPasswordInstance.Password != forgotPasswordInstance.ConfirmPassword {
37+
utils.Response(domain.HttpResponse{
38+
Code: 400,
39+
Message: "Password and confirm password must be the same",
40+
}, w)
41+
return
42+
}
43+
44+
if isValidPass := passwordRegex.MatchString(forgotPasswordInstance.Password); !isValidPass {
45+
utils.Response(domain.HttpResponse{
46+
Code: 400,
47+
Message: "Password must contain at least 8 characters, one uppercase letter, one lowercase letter, and one number",
48+
}, w)
49+
return
50+
}
51+
52+
if err := h.usecase.ResetPassword(r.Context(), forgotPasswordInstance); err != nil {
53+
utils.Response(domain.HttpResponse{
54+
Code: 500,
55+
Message: "Failed to reset password :" + err.Error(),
56+
}, w)
57+
return
58+
59+
}
60+
61+
utils.Response(domain.HttpResponse{
62+
Code: 200,
63+
Message: "Reset password success",
64+
}, w)
65+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package repository
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/hammer-code/lms-be/domain"
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
func (repo *repository) ForgotPassword(ctx context.Context, token string, expiredAt time.Time, user domain.User) (err error) {
12+
err = repo.db.DB(ctx).Create(&domain.ResetPasswordToken{
13+
Token: token,
14+
UserID: uint64(user.ID),
15+
ExpiryDate: expiredAt,
16+
}).Error
17+
if err != nil {
18+
logrus.Error("repo.ForgotPassword : failed to create reset password token")
19+
return err
20+
}
21+
22+
return nil
23+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package repository
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/hammer-code/lms-be/domain"
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
func (repo *repository) ResetPassword(ctx context.Context, email, password, token string) error {
12+
if err := repo.db.StartTransaction(ctx, func(ctx context.Context) error {
13+
resetPasswordTokenInstance := domain.ResetPasswordToken{}
14+
if err := repo.db.DB(ctx).Model(resetPasswordTokenInstance).Where("token = ?", token).First(&resetPasswordTokenInstance).Error; err != nil {
15+
logrus.Error("repo.ResetPassword: failed to find token")
16+
return err
17+
}
18+
19+
if resetPasswordTokenInstance.IsUsed {
20+
logrus.Error("repo.ResetPassword: token already used")
21+
return errors.New("token already used")
22+
}
23+
24+
if err := repo.db.DB(ctx).Model(domain.User{}).Where("email = ?", email).Update("password", password).Error; err != nil {
25+
logrus.Error("repo.ResetPassword: failed to update password")
26+
return err
27+
}
28+
29+
if err := repo.db.DB(ctx).Model(domain.ResetPasswordToken{}).Where("token = ?", token).Update("is_used", true).Error; err != nil {
30+
logrus.Error("repo.ResetPassword: failed to update token state")
31+
return err
32+
}
33+
return nil
34+
}); err != nil {
35+
logrus.Error("repo.ResetPassword: failed to reset password")
36+
return err
37+
}
38+
return nil
39+
}

0 commit comments

Comments
 (0)