From db915570b3388a8dbe1123239399232a65f60042 Mon Sep 17 00:00:00 2001 From: LukmanE22 Date: Thu, 1 Feb 2024 11:06:43 +0700 Subject: [PATCH] add comments --- README.md | 6 +-- application/app.go | 8 +++- controller/role/role_controller.go | 23 ++++++++- controller/user/user_controller.go | 63 +++++++++++++++++++++++-- controller/user/user_controller_test.go | 4 +- service/user/user_service.go | 14 +++--- service/user/user_service_test.go | 4 +- 7 files changed, 100 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index e4e8611..b72fc7c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Techs and tools were used in this project: - [Github CLI](https://cli.github.com/) → Github management for repository's secrets & etc. - [Github Action](https://github.com/features/actions) → Automated testing and building across multiple versions of Go. - [Snyk](https://app.snyk.io/) → Dependency scanning. -- [SonarLint, VSCode ext.](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode) → Detects & highlights issues that can lead to bugs & vulnerabilities. +- [SonarLint as VSCode ext.](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode) → Detects & highlights issues that can lead to bugs & vulnerabilities. - [GoLint](https://github.com/golang/lint) → CLI static code analytic for code-styling & many more.   @@ -40,7 +40,3 @@ Techs and tools were used in this project: This project is under license from MIT. For more details, see the [LICENSE](LICENSE) file.   - -## Todo - -- Add password and password confirm at DeleteProfile (permanent) by User diff --git a/application/app.go b/application/app.go index 5a86d8a..aa979f1 100644 --- a/application/app.go +++ b/application/app.go @@ -53,8 +53,10 @@ var ( }) ) +// setup initializes the application +// by checking the environment and +// database configuration. func setup() { - // Check env and database env.ReadConfig("./.env") config := env.Configuration() privKey := config.GetPrivateKey() @@ -68,6 +70,7 @@ func setup() { connector.LoadRedisCache() } +// checkLocalPort checks if a given port is available for local use. func checkLocalPort(port int) { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err != nil { @@ -76,6 +79,9 @@ func checkLocalPort(port int) { defer listener.Close() } +// RunApp initializes and runs the application, +// handling setup, port checking, middleware, +// and route registration. func RunApp() { setup() checkLocalPort(port) diff --git a/controller/role/role_controller.go b/controller/role/role_controller.go index 70e0078..933f0f6 100644 --- a/controller/role/role_controller.go +++ b/controller/role/role_controller.go @@ -14,15 +14,29 @@ import ( service "github.com/Lukmanern/gost/service/role" ) +// RoleController defines all methods for handling +// role-related operations and logic. +// All these operations should be performed by an admin +// or other roles that defined in route-file. type RoleController interface { - // auth + admin + // Create handles the creation of a new role. Create(c *fiber.Ctx) error + + // Get retrieves information about a specific role. Get(c *fiber.Ctx) error + + // GetAll retrieves information about all roles. GetAll(c *fiber.Ctx) error + + // Update handles updating role information. Update(c *fiber.Ctx) error + + // Delete handles the deletion of a role. Delete(c *fiber.Ctx) error } +// RoleControllerImpl is the implementation of +// RoleController with a RoleService dependency. type RoleControllerImpl struct { service service.RoleService } @@ -32,6 +46,8 @@ var ( roleControllerImplOnce sync.Once ) +// NewRoleController creates a singleton RoleController +// instance with the provided RoleService. func NewRoleController(service service.RoleService) RoleController { roleControllerImplOnce.Do(func() { roleControllerImpl = &RoleControllerImpl{ @@ -41,6 +57,7 @@ func NewRoleController(service service.RoleService) RoleController { return roleControllerImpl } +// Create handles the creation of a new role. func (ctr *RoleControllerImpl) Create(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -73,6 +90,7 @@ func (ctr *RoleControllerImpl) Create(c *fiber.Ctx) error { return response.SuccessCreated(c, data) } +// Get retrieves information about a specific role. func (ctr *RoleControllerImpl) Get(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -98,6 +116,7 @@ func (ctr *RoleControllerImpl) Get(c *fiber.Ctx) error { return response.SuccessLoaded(c, role) } +// GetAll retrieves information about all roles. func (ctr *RoleControllerImpl) GetAll(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -135,6 +154,7 @@ func (ctr *RoleControllerImpl) GetAll(c *fiber.Ctx) error { return response.SuccessLoaded(c, responseData) } +// Update handles updating role information. func (ctr *RoleControllerImpl) Update(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -169,6 +189,7 @@ func (ctr *RoleControllerImpl) Update(c *fiber.Ctx) error { return response.SuccessNoContent(c) } +// Delete handles the deletion of a role. func (ctr *RoleControllerImpl) Delete(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { diff --git a/controller/user/user_controller.go b/controller/user/user_controller.go index 020db72..a363dac 100644 --- a/controller/user/user_controller.go +++ b/controller/user/user_controller.go @@ -16,24 +16,58 @@ import ( service "github.com/Lukmanern/gost/service/user" ) +// UserController defines methods for handling +// user-related operations. type UserController interface { - // no-auth + // NO-AUTH + + // Register handles for user registration for all roles. + // This handler will send an email for confirmation. Register(c *fiber.Ctx) error + + // AccountActivation handles the account activation + // process after registration. AccountActivation(c *fiber.Ctx) error + + // Login handles user login. Login(c *fiber.Ctx) error + + // ForgetPassword handles the forget password request. + // This handler will send an email for reset password. ForgetPassword(c *fiber.Ctx) error + + // ResetPassword handles the password reset process. ResetPassword(c *fiber.Ctx) error - // auth + + // AUTH + + // MyProfile retrieves the user's own profile information. MyProfile(c *fiber.Ctx) error + + // Logout handles user logout. + // Black-listing the user token / JWT. Logout(c *fiber.Ctx) error + + // UpdateProfile handles updating user profile information. UpdateProfile(c *fiber.Ctx) error + + // UpdatePassword handles updating user password. UpdatePassword(c *fiber.Ctx) error + + // DeleteAccount handles the account deletion process. DeleteAccount(c *fiber.Ctx) error - // auth + admin + + // AUTH and ROLE-ADMIN AREA + + // GetAll retrieves all user accounts (need admin access). GetAll(c *fiber.Ctx) error + + // BanAccount handles banning a user account (need admin access). BanAccount(c *fiber.Ctx) error } +// UserControllerImpl is the implementation of +// UserController with a UserService dependency. type UserControllerImpl struct { service service.UserService } @@ -43,6 +77,8 @@ var ( userControllerOnce sync.Once ) +// NewUserController creates a singleton UserController +// instance with the given UserService. func NewUserController(service service.UserService) UserController { userControllerOnce.Do(func() { userController = &UserControllerImpl{ @@ -53,16 +89,19 @@ func NewUserController(service service.UserService) UserController { return userController } +// Register handles for user registration for all roles. +// This handler will send an email for confirmation. func (ctr *UserControllerImpl) Register(c *fiber.Ctx) error { var user model.UserRegister if err := c.BodyParser(&user); err != nil { return response.BadRequest(c, consts.InvalidJSONBody+err.Error()) } - user.Email = strings.ToLower(user.Email) - validate := validator.New() if len(user.RoleIDs) < 1 { return response.BadRequest(c, "please choose one or more role") } + user.Email = strings.ToLower(user.Email) + + validate := validator.New() if err := validate.Struct(&user); err != nil { return response.BadRequest(c, consts.InvalidJSONBody+err.Error()) } @@ -91,6 +130,8 @@ func (ctr *UserControllerImpl) Register(c *fiber.Ctx) error { }) } +// AccountActivation handles the account activation +// process after registration. func (ctr *UserControllerImpl) AccountActivation(c *fiber.Ctx) error { var user model.UserActivation if err := c.BodyParser(&user); err != nil { @@ -119,6 +160,7 @@ func (ctr *UserControllerImpl) AccountActivation(c *fiber.Ctx) error { }) } +// Login handles user login. func (ctr *UserControllerImpl) Login(c *fiber.Ctx) error { var user model.UserLogin if err := c.BodyParser(&user); err != nil { @@ -152,6 +194,8 @@ func (ctr *UserControllerImpl) Login(c *fiber.Ctx) error { }) } +// ForgetPassword handles the forget password request. +// This handler will send an email for reset password. func (ctr *UserControllerImpl) ForgetPassword(c *fiber.Ctx) error { var user model.UserForgetPassword if err := c.BodyParser(&user); err != nil { @@ -181,6 +225,7 @@ func (ctr *UserControllerImpl) ForgetPassword(c *fiber.Ctx) error { }) } +// ResetPassword handles the password reset process. func (ctr *UserControllerImpl) ResetPassword(c *fiber.Ctx) error { var user model.UserResetPassword if err := c.BodyParser(&user); err != nil { @@ -213,6 +258,7 @@ func (ctr *UserControllerImpl) ResetPassword(c *fiber.Ctx) error { }) } +// MyProfile retrieves the user's own profile information. func (ctr *UserControllerImpl) MyProfile(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -233,6 +279,8 @@ func (ctr *UserControllerImpl) MyProfile(c *fiber.Ctx) error { return response.SuccessLoaded(c, userProfile) } +// Logout handles user logout. +// Black-listing the user token / JWT. func (ctr *UserControllerImpl) Logout(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -245,6 +293,7 @@ func (ctr *UserControllerImpl) Logout(c *fiber.Ctx) error { return response.SuccessNoContent(c) } +// UpdateProfile handles updating user profile information. func (ctr *UserControllerImpl) UpdateProfile(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -275,6 +324,7 @@ func (ctr *UserControllerImpl) UpdateProfile(c *fiber.Ctx) error { return response.SuccessNoContent(c) } +// UpdatePassword handles updating user password. func (ctr *UserControllerImpl) UpdatePassword(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -312,6 +362,7 @@ func (ctr *UserControllerImpl) UpdatePassword(c *fiber.Ctx) error { return response.SuccessNoContent(c) } +// DeleteAccount handles the account deletion process. func (ctr *UserControllerImpl) DeleteAccount(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -350,6 +401,7 @@ func (ctr *UserControllerImpl) DeleteAccount(c *fiber.Ctx) error { return response.SuccessNoContent(c) } +// GetAll retrieves all user accounts (need admin access). func (ctr *UserControllerImpl) GetAll(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { @@ -387,6 +439,7 @@ func (ctr *UserControllerImpl) GetAll(c *fiber.Ctx) error { return response.SuccessLoaded(c, responseData) } +// BanAccount handles banning a user account (need admin access). func (ctr *UserControllerImpl) BanAccount(c *fiber.Ctx) error { userClaims, ok := c.Locals("claims").(*middleware.Claims) if !ok || userClaims == nil { diff --git a/controller/user/user_controller_test.go b/controller/user/user_controller_test.go index 1af7876..635c79e 100644 --- a/controller/user/user_controller_test.go +++ b/controller/user/user_controller_test.go @@ -257,7 +257,7 @@ func TestAccountActivation(t *testing.T) { assert.Nil(t, err, consts.ShouldNil, headerTestName) redisConTest := connector.LoadRedisCache() - key := validUser.Email + service.KEY_ACCOUNT_ACTIVATION + key := validUser.Email + service.KeyAccountActivation validCode := redisConTest.Get(key).Val() type testCase struct { @@ -516,7 +516,7 @@ func TestResetPassword(t *testing.T) { assert.Nil(t, err, consts.ShouldNil, headerTestName) redisConTest := connector.LoadRedisCache() - key := validUser.Email + service.KEY_FORGET_PASSWORD + key := validUser.Email + service.KeyResetPassword validCode := redisConTest.Get(key).Val() assert.True(t, len(validCode) >= 21, "should true", headerTestName) diff --git a/service/user/user_service.go b/service/user/user_service.go index ec1baae..7eff73a 100644 --- a/service/user/user_service.go +++ b/service/user/user_service.go @@ -48,9 +48,11 @@ type UserServiceImpl struct { emailService service.EmailService } +// KeyResetPassword and KeyAccountActivation are +// used for addition key to get value from redis const ( - KEY_FORGET_PASSWORD = "-forget-password" - KEY_ACCOUNT_ACTIVATION = "-account-activation" + KeyResetPassword = "-forget-password" + KeyAccountActivation = "-account-activation" ) var ( @@ -102,7 +104,7 @@ func (svc *UserServiceImpl) Register(ctx context.Context, data model.UserRegiste } code := helper.RandomString(32) // verification code - key := data.Email + KEY_ACCOUNT_ACTIVATION + key := data.Email + KeyAccountActivation exp := time.Hour * 3 redisStatus := svc.redis.Set(key, code, exp) if redisStatus.Err() != nil { @@ -132,7 +134,7 @@ func (svc *UserServiceImpl) AccountActivation(ctx context.Context, data model.Us return fiber.NewError(fiber.StatusBadRequest, "activation failed, account is active or already deleted") } - key := data.Email + KEY_ACCOUNT_ACTIVATION + key := data.Email + KeyAccountActivation redisStatus := svc.redis.Get(key) if redisStatus.Err() != nil { return errors.New("error while getting data from redis") @@ -192,7 +194,7 @@ func (svc *UserServiceImpl) ForgetPassword(ctx context.Context, data model.UserF return errors.New("error while getting user data") } - key := data.Email + KEY_FORGET_PASSWORD + key := data.Email + KeyResetPassword code := helper.RandomString(32) exp := time.Hour * 1 redisStatus := svc.redis.Set(key, code, exp) @@ -218,7 +220,7 @@ func (svc *UserServiceImpl) ResetPassword(ctx context.Context, data model.UserRe if getErr != nil || user == nil { return errors.New("error while getting user data") } - key := data.Email + KEY_FORGET_PASSWORD + key := data.Email + KeyResetPassword code := svc.redis.Get(key).Val() if code == "" || code != data.Code { return fiber.NewError(fiber.StatusNotFound, "verfication code isn't found") diff --git a/service/user/user_service_test.go b/service/user/user_service_test.go index 8b5e814..36172c3 100644 --- a/service/user/user_service_test.go +++ b/service/user/user_service_test.go @@ -140,7 +140,7 @@ func TestAccountActivation(t *testing.T) { assert.Nil(t, err, consts.ShouldNotNil, headerTestName) defer repository.Delete(ctx, id) - key := validUser.Email + KEY_ACCOUNT_ACTIVATION + key := validUser.Email + KeyAccountActivation validCode := redisConTest.Get(key).Val() assert.True(t, len(validCode) > 0, consts.ShouldNotNil, headerTestName) @@ -333,7 +333,7 @@ func TestResetPassword(t *testing.T) { err := service.ForgetPassword(ctx, model.UserForgetPassword{Email: validUser.Email}) assert.Nil(t, err, consts.ShouldNil, headerTestName) - key := validUser.Email + KEY_FORGET_PASSWORD + key := validUser.Email + KeyResetPassword validCode := redisConTest.Get(key).Val() assert.True(t, len(validCode) > 0, consts.ShouldNotNil, headerTestName)