Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add comments #15

Merged
merged 1 commit into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

 
Expand All @@ -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
8 changes: 7 additions & 1 deletion application/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
23 changes: 22 additions & 1 deletion controller/role/role_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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{
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
63 changes: 58 additions & 5 deletions controller/user/user_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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{
Expand All @@ -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())
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions controller/user/user_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)

Expand Down
14 changes: 8 additions & 6 deletions service/user/user_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand All @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions service/user/user_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
Loading