Skip to content
Merged

Dev #37

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
26 changes: 26 additions & 0 deletions Internal/adapters/repository/authRepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,29 @@ func (ar *AuthRepo) LoginRepo(email string) (*domain.User, error) {
}
return &user, nil
}

func (ar *AuthRepo) CreateInvite(invite *domain.Invitation) error {
if err := ar.DB.Gorm.Create(invite).Error; err != nil {
return err
}
return nil
}

func (ar *AuthRepo) FindInviteByToken(token string) (*domain.Invitation, error) {
var invite domain.Invitation
if err := ar.DB.Gorm.Where("token = ?", token).First(&invite).Error; err != nil {
return nil, err
}
return &invite, nil
}

func (ar *AuthRepo) MarkInviteUsed(token string) error {
var invite domain.Invitation
if err := ar.DB.Gorm.Where("token = ?", token).First(&invite).Error; err != nil {
return err
}
if err := ar.DB.Gorm.Model(&invite).Update("used", true).Error; err != nil {
return err
}
return nil
}
1 change: 1 addition & 0 deletions Internal/adapters/repository/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func ConnectDB() (*DBContainer, error) {
func (db *DBContainer) Migrate() error {
err := db.Gorm.AutoMigrate(
&domain.User{},
&domain.Invitation{},
)
if err != nil {
log.Fatalf("Failed to migrate users: %v", err)
Expand Down
20 changes: 20 additions & 0 deletions Internal/adapters/repository/userRepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ func (ur *UserRepository) FindById(id uint) (*domain.User, error) {
}
return &user, nil
}

func (ur *UserRepository) AllUser() ([]domain.User, error) {
var users []domain.User
err := ur.db.Gorm.Find(&users).Error
if err != nil {
return nil, err
}
return users, nil
}

func (ur *UserRepository) RoleUpdate(id uint, role string) (*domain.User, error) {
var user domain.User
if err := ur.db.Gorm.First(&user, id).Error; err != nil {
return nil, err
}
if err := ur.db.Gorm.Model(&user).Update("role", role).Error; err != nil {
return nil, err
}
return &user, nil
}
6 changes: 6 additions & 0 deletions Internal/api/Routes/routev1.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ func SetupRouterV1(r *gin.Engine, deps Deps) {
{
user.GET("/me", deps.User.GetUserHandler)
}
admin := protected.Group("/admin")
{
admin.POST("/invite", deps.Auth.CreateInviteHandler)
admin.GET("/alluser", deps.User.AllUserHandler)
admin.PUT("/updaterole", deps.User.UpdateRoleHandler)
}
}
}
}
31 changes: 31 additions & 0 deletions Internal/api/handlers/authHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ type AuthHandler struct {
Service *services.AuthService
}

type InviteInput struct {
Email string `json:"email"`
Role string `json:"role"`
}

func NewAuthHandler(service *services.AuthService) *AuthHandler {
return &AuthHandler{Service: service}
}
Expand Down Expand Up @@ -69,3 +74,29 @@ func (ah *AuthHandler) LogoutHandler(c *gin.Context) {
"data": nil,
})
}

func (ah *AuthHandler) CreateInviteHandler(c *gin.Context) {
var req InviteInput
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{
"message": "Invalid Input",
"data": err.Error(),
})
return
}
if req.Role == "" {
req.Role = "user"
}
link, err := ah.Service.GenerateInviteService(req.Email, req.Role)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "Invite service failed",
"data": err.Error(),
})
return
}
c.JSON(200, gin.H{
"message": "Invite service success",
"data": link,
})
}
53 changes: 53 additions & 0 deletions Internal/api/handlers/userHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,56 @@ func (uh *UserHandler) GetUserHandler(c *gin.Context) {
"data": user,
})
}

func (uh *UserHandler) AllUserHandler(c *gin.Context) {
userRole, _ := c.Get("userRole")
if userRole != "admin" {
c.JSON(400, gin.H{
"message": "Not allowed to get all users",
"data": false,
})
return
}
users, err := uh.Service.GetAllUser()
if err != nil {
c.JSON(400, gin.H{
"message": "Unable to get all users",
"data": false,
})
}
c.JSON(200, gin.H{
"message": "Successfully get all users",
"data": users,
})
}

func (uh *UserHandler) UpdateRoleHandler(c *gin.Context) {
var input services.RoleInput
//userRole, _ := c.Get("userRole")
//if userRole != "admin" {
// c.JSON(400, gin.H{
// "message": "Not allowed to update role",
// "data": false,
// })
// return
//}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(400, gin.H{
"message": "input not valid",
"data": err.Error(),
})
return
}
user, err := uh.Service.UpdateRole(&input)
if err != nil {
c.JSON(400, gin.H{
"message": "Unable to update role",
"data": err,
})
return
}
c.JSON(200, gin.H{
"message": "Successfully update role",
"data": user,
})
}
4 changes: 0 additions & 4 deletions Internal/api/middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ func AuthMiddleware(userRepo *repository.UserRepository, cfg *config.Config) gin
c.Set("userRole", user.Role)
c.Next()

c.JSON(200, gin.H{
"message": "Success",
"data": " ",
})
} else {
c.JSON(401, gin.H{
"message": "Invalid token",
Expand Down
13 changes: 13 additions & 0 deletions Internal/core/domain/invitationdb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package domain

import "time"

type Invitation struct {
ID uint `gorm:"primary_key;AUTO_INCREMENT" json:"id"`
Email string `gorm:"size:255; not null" json:"email"`
Token string `gorm:"size:255; not null; unique; index" json:"token"`
Role string `gorm:"default:'user'; size:255; not null" json:"role"`
Used bool `gorm:"default:false" json:"used"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
93 changes: 82 additions & 11 deletions Internal/core/services/authService.go
Original file line number Diff line number Diff line change
@@ -1,53 +1,93 @@
package services

import (
"errors"
"time"

"github.com/Arjuna-Ragil/Localbase/Internal/adapters/repository"
"github.com/Arjuna-Ragil/Localbase/Internal/config"
"github.com/Arjuna-Ragil/Localbase/Internal/core/domain"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
)

type AuthService struct {
Repo *repository.AuthRepo
AuthRepo *repository.AuthRepo
SysRepo *repository.SystemRepo
}

type RegisterInput struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"`
Role string `json:"role" binding:"required"`
Token string `json:"token"`
}

type LoginInput struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"`
}

func NewAuthService(repo *repository.AuthRepo) *AuthService {
return &AuthService{Repo: repo}
func NewAuthService(auth *repository.AuthRepo, sys *repository.SystemRepo) *AuthService {
return &AuthService{AuthRepo: auth, SysRepo: sys}
}

func (as *AuthService) RegisterService(input *RegisterInput) error {
user := domain.User{
Username: input.Username,
Email: input.Email,
Password: input.Password,
Role: input.Role,
}
err := as.Repo.RegisterRepo(&user)
isInitialized, err := as.SysRepo.IsAppInitialized()
if err != nil {
return err
}

if !isInitialized {
user := domain.User{
Username: input.Username,
Email: input.Email,
Password: input.Password,
Role: "admin",
}

if err := as.AuthRepo.RegisterRepo(&user); err != nil {
return err
}

} else {
if input.Token == "" {
return errors.New("token is required")
}

invite, err := as.VerifyInviteService(input.Token)
if err != nil {
return err
}

if invite.Email == "" && invite.Email != input.Email {
return errors.New("email does not match")
}

user := domain.User{
Username: input.Username,
Email: invite.Email,
Password: input.Password,
Role: invite.Role,
}

if err := as.AuthRepo.MarkInviteUsed(input.Token); err != nil {
return err
}

if err := as.AuthRepo.RegisterRepo(&user); err != nil {
return err
}
}
return nil
}

func (as *AuthService) LoginService(input *LoginInput) (string, error) {
cfg := config.LoadConfig()

user, err := as.Repo.LoginRepo(input.Email)
user, err := as.AuthRepo.LoginRepo(input.Email)
if err != nil {
return "", err
}
Expand All @@ -64,3 +104,34 @@ func (as *AuthService) LoginService(input *LoginInput) (string, error) {
}
return tokenString, nil
}

func (as *AuthService) GenerateInviteService(email string, role string) (string, error) {
token := uuid.NewString()
expiresAt := time.Now().Add(time.Hour * 24)
invite := domain.Invitation{
Email: email,
Token: token,
Role: role,
Used: false,
ExpiresAt: expiresAt,
}
if err := as.AuthRepo.CreateInvite(&invite); err != nil {
return "", err
}
linkInvite := "http://localhost:5173/registerinvite?token=" + token //Change link if different
return linkInvite, nil
}

func (as *AuthService) VerifyInviteService(token string) (*domain.Invitation, error) {
invite, err := as.AuthRepo.FindInviteByToken(token)
if err != nil {
return nil, err
}
if invite.Used == true {
return nil, errors.New("invite already used")
}
if time.Now().After(invite.ExpiresAt) {
return nil, errors.New("invite expired")
}
return invite, nil
}
21 changes: 21 additions & 0 deletions Internal/core/services/userService.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,31 @@ func NewUserService(repo *repository.UserRepository) *UserService {
return &UserService{Repo: repo}
}

type RoleInput struct {
UserID uint `json:"id"`
Role string `json:"role"`
}

func (us *UserService) GetUser(id uint) (*domain.User, error) {
user, err := us.Repo.FindById(id)
if err != nil {
return nil, errors.New("user not found")
}
return user, nil
}

func (us *UserService) GetAllUser() ([]domain.User, error) {
users, err := us.Repo.AllUser()
if err != nil {
return nil, errors.New("user not found")
}
return users, nil
}

func (us *UserService) UpdateRole(input *RoleInput) (*domain.User, error) {
user, err := us.Repo.RoleUpdate(input.UserID, input.Role)
if err != nil {
return nil, errors.New("user not found")
}
return user, nil
}
Loading
Loading