diff --git a/internal/accessor/staff/get_all.go b/internal/accessor/staff/get_by_email.go similarity index 91% rename from internal/accessor/staff/get_all.go rename to internal/accessor/staff/get_by_email.go index c21df0c..aaaea7d 100644 --- a/internal/accessor/staff/get_all.go +++ b/internal/accessor/staff/get_by_email.go @@ -12,7 +12,7 @@ func (sr *StaffRepository) GetStaffByEmail(email string) (*authdomain.Staff, err var staff authdomain.Staff if email == "" { - return nil, errors.New("email is empty") + return nil, errors.New("email is required") } if err := sr.DB.Where("email = ?", email).First(&staff).Error; err != nil { diff --git a/internal/accessor/staff/get_by_id.go b/internal/accessor/staff/get_by_id.go new file mode 100644 index 0000000..c8bb14e --- /dev/null +++ b/internal/accessor/staff/get_by_id.go @@ -0,0 +1,25 @@ +package staffrepository + +import ( + "errors" + authdomain "legalgo-BE-go/internal/domain/auth" + + "gorm.io/gorm" +) + +func (sr *StaffRepository) GetStaffByID(ID string) (*authdomain.Staff, error) { + var staff authdomain.Staff + + if ID == "" { + return nil, errors.New("id is required") + } + + if err := sr.DB.Where("id = ? ", ID).First(&staff).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New("staff not found") + } + return nil, err + } + + return &staff, nil +} diff --git a/internal/accessor/staff/impl.go b/internal/accessor/staff/impl.go index 1e3ef6e..c8901aa 100644 --- a/internal/accessor/staff/impl.go +++ b/internal/accessor/staff/impl.go @@ -11,7 +11,9 @@ type StaffRepository struct { type StaffIntf interface { GetStaffByEmail(string) (*authdomain.Staff, error) + GetStaffByID(string) (*authdomain.Staff, error) Create(*authdomain.Staff) (*authdomain.Staff, error) + Update(authdomain.Staff) error } func New(db *database.DB) StaffIntf { diff --git a/internal/accessor/staff/update.go b/internal/accessor/staff/update.go index 3165d57..b0c6ae2 100644 --- a/internal/accessor/staff/update.go +++ b/internal/accessor/staff/update.go @@ -1,7 +1,19 @@ package staffrepository -func (ur *StaffRepository) Update() error { - // if err := ur.DB.Update(column string, value interface{}) +import ( + authdomain "legalgo-BE-go/internal/domain/auth" + "legalgo-BE-go/internal/utilities/utils" +) + +func (ur *StaffRepository) Update(spec authdomain.Staff) error { + val, err := utils.StructToMap(spec) + if err != nil { + return err + } + + if err := ur.DB.Model(&authdomain.Staff{}).Where("id = ?", spec.ID).Updates(val).Error; err != nil { + return err + } return nil } diff --git a/internal/accessor/user_repository/get_user.go b/internal/accessor/user_repository/get_user_by_email.go similarity index 100% rename from internal/accessor/user_repository/get_user.go rename to internal/accessor/user_repository/get_user_by_email.go diff --git a/internal/accessor/user_repository/get_user_by_id.go b/internal/accessor/user_repository/get_user_by_id.go new file mode 100644 index 0000000..695a35e --- /dev/null +++ b/internal/accessor/user_repository/get_user_by_id.go @@ -0,0 +1,30 @@ +package userrepository + +import ( + "errors" + + authdomain "legalgo-BE-go/internal/domain/auth" + + "gorm.io/gorm" +) + +func (ur *UserRepository) GetUserByID(email string) (*authdomain.UserProfile, error) { + var users []authdomain.UserProfile + + if email == "" { + return nil, errors.New("email is empty") + } + + if err := ur.DB.Table("users u"). + Select("u.email, u.id, s.status as subscribe_status, sp.code as subscribe_plan_code, sp.name as subscribe_plan_name"). + Joins("join subscribes s on s.id = u.subscribe_id"). + Joins("join subscribe_plans sp on s.subscribe_plan_id = sp.id"). + Scan(&users).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New("user not found") + } + return nil, err + } + + return &users[0], nil +} diff --git a/internal/accessor/user_repository/impl.go b/internal/accessor/user_repository/impl.go index 8ae5a93..3d47b1a 100644 --- a/internal/accessor/user_repository/impl.go +++ b/internal/accessor/user_repository/impl.go @@ -11,6 +11,7 @@ type UserRepository struct { type UserIntf interface { GetUserByEmail(string) (*authdomain.User, error) + GetUserByID(string) (*authdomain.UserProfile, error) CreateUser(*authdomain.User) (*authdomain.User, error) } diff --git a/internal/api/http/auth/module.go b/internal/api/http/auth/module.go index ebddcd6..320e1de 100644 --- a/internal/api/http/auth/module.go +++ b/internal/api/http/auth/module.go @@ -8,5 +8,8 @@ var Module = fx.Module("auth-api", LoginUser, RegisterUser, RegisterStaff, + UpdateStaff, + GetStaffProfile, + GetUserProfile, ), ) diff --git a/internal/api/http/auth/profile.go b/internal/api/http/auth/profile.go new file mode 100644 index 0000000..92e2859 --- /dev/null +++ b/internal/api/http/auth/profile.go @@ -0,0 +1,82 @@ +package authhttp + +import ( + "errors" + authsvc "legalgo-BE-go/internal/services/auth" + "legalgo-BE-go/internal/utilities/response" + "net/http" + + "github.com/go-chi/chi/v5" +) + +func GetStaffProfile( + router chi.Router, + authSvc authsvc.AuthIntf, +) { + router.Get("/staff/{id}/profile", func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + if id == "" { + response.ResponseWithErrorCode( + ctx, + w, + errors.New("provided id is empty"), + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + "required params is not provided", + ) + return + } + + staffProfile, err := authSvc.GetStaffProfile(id) + if err != nil { + response.ResponseWithErrorCode( + ctx, + w, + err, + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + err.Error(), + ) + return + } + + response.RespondJsonSuccess(ctx, w, staffProfile) + }) +} + +func GetUserProfile( + router chi.Router, + authSvc authsvc.AuthIntf, +) { + router.Get("/user/{id}/profile", func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + id := chi.URLParam(r, "id") + if id == "" { + response.ResponseWithErrorCode( + ctx, + w, + errors.New("provided id is empty"), + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + "required params is not provided", + ) + return + } + + userProfile, err := authSvc.GetUserProfile(id) + if err != nil { + response.ResponseWithErrorCode( + ctx, + w, + err, + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + err.Error(), + ) + return + } + + response.RespondJsonSuccess(ctx, w, userProfile) + }) +} diff --git a/internal/api/http/auth/update.go b/internal/api/http/auth/update.go new file mode 100644 index 0000000..e5c6537 --- /dev/null +++ b/internal/api/http/auth/update.go @@ -0,0 +1,75 @@ +package authhttp + +import ( + "errors" + authdomain "legalgo-BE-go/internal/domain/auth" + authsvc "legalgo-BE-go/internal/services/auth" + "legalgo-BE-go/internal/utilities/response" + "legalgo-BE-go/internal/utilities/utils" + "net/http" + + "github.com/go-chi/chi/v5" +) + +func UpdateStaff( + router chi.Router, + authSvc authsvc.AuthIntf, +) { + router.Patch("/staff/{id}/update", func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + id := chi.URLParam(r, "id") + if id == "" { + response.ResponseWithErrorCode( + ctx, + w, + errors.New("provided id is empty"), + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + "required params is not provided", + ) + return + } + + var spec authdomain.RegisterStaffReq + + if err := utils.UnmarshalBody(r, &spec); err != nil { + response.ResponseWithErrorCode( + ctx, + w, + err, + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + "failed to unmarshal request", + ) + return + } + + staff := authdomain.Staff{ + ID: id, + Email: spec.Email, + Password: spec.Password, + Username: spec.Username, + } + + if err := authSvc.UpdateStaff(staff); err != nil { + response.ResponseWithErrorCode( + ctx, + w, + err, + response.ErrBadRequest.Code, + response.ErrBadRequest.HttpCode, + err.Error(), + ) + return + } + + responsePayload := struct{ + Message string + } { + Message: "update staff success", + } + + response.RespondJsonSuccess(ctx, w, responsePayload) + }) +} diff --git a/internal/domain/auth/login.go b/internal/domain/auth/login.go index c57784b..d78ad40 100644 --- a/internal/domain/auth/login.go +++ b/internal/domain/auth/login.go @@ -13,4 +13,19 @@ type LoginRepoResponse struct { ID string `json:"id"` Email string `json:"email"` Password string `json:"password"` + Username string `json:"username"` +} + +type StaffProfile struct { + ID string `json:"id"` + Email string `json:"email"` + Username string `json:"username"` +} + +type UserProfile struct { + ID string `json:"id"` + Email string `json:"email"` + SubscribePlanCode string `json:"subscribe_plan_code"` + SubscribePlanName string `json:"subscribe_plan_name"` + SubscribeStatus string `json:"subscribe_status"` } diff --git a/internal/domain/auth/register.go b/internal/domain/auth/register.go index ccb26d1..80f57e3 100644 --- a/internal/domain/auth/register.go +++ b/internal/domain/auth/register.go @@ -21,6 +21,12 @@ type RegisterStaffReq struct { Username string `json:"username" validate:"required"` } +type UpdateStaffReq struct { + Email string `json:"email"` + Password string `json:"password"` + Username string `json:"username"` +} + type Staff struct { ID string `json:"id"` Email string `json:"email"` diff --git a/internal/services/auth/get_staff.go b/internal/services/auth/get_staff.go new file mode 100644 index 0000000..8f891a1 --- /dev/null +++ b/internal/services/auth/get_staff.go @@ -0,0 +1,18 @@ +package authsvc + +import authdomain "legalgo-BE-go/internal/domain/auth" + +func (as *AuthSvc) GetStaffProfile(id string) (*authdomain.StaffProfile, error) { + staff, err := as.staffRepo.GetStaffByID(id) + if err != nil { + return nil, err + } + + profile := &authdomain.StaffProfile{ + ID: staff.ID, + Username: staff.Username, + Email: staff.Email, + } + + return profile, nil +} diff --git a/internal/services/auth/get_user.go b/internal/services/auth/get_user.go new file mode 100644 index 0000000..793e4df --- /dev/null +++ b/internal/services/auth/get_user.go @@ -0,0 +1,12 @@ +package authsvc + +import authdomain "legalgo-BE-go/internal/domain/auth" + +func (as *AuthSvc) GetUserProfile(id string) (*authdomain.UserProfile, error) { + user, err := as.userRepo.GetUserByID(id) + if err != nil { + return nil, err + } + + return user, nil +} diff --git a/internal/services/auth/impl.go b/internal/services/auth/impl.go index d7c1647..2fce25c 100644 --- a/internal/services/auth/impl.go +++ b/internal/services/auth/impl.go @@ -17,9 +17,13 @@ type AuthSvc struct { type AuthIntf interface { LoginAsStaff(authdomain.LoginReq) (string, error) - LoginAsUser(authdomain.LoginReq) (string, error) RegisterUser(authdomain.RegisterUserReq) (string, error) + GetUserProfile(string) (*authdomain.UserProfile, error) + + LoginAsUser(authdomain.LoginReq) (string, error) RegisterStaff(authdomain.RegisterStaffReq) (string, error) + UpdateStaff(authdomain.Staff) error + GetStaffProfile(string) (*authdomain.StaffProfile, error) } func New( diff --git a/internal/services/auth/register_staff.go b/internal/services/auth/register_staff.go index 3122f0e..e846a49 100644 --- a/internal/services/auth/register_staff.go +++ b/internal/services/auth/register_staff.go @@ -23,6 +23,7 @@ func (a *AuthSvc) RegisterStaff(spec authdomain.RegisterStaffReq) (string, error ID: uuid.NewString(), Email: spec.Email, Password: hashedPwd, + Username: spec.Username, } _, err = a.staffRepo.Create(&user) diff --git a/internal/services/auth/update_staff.go b/internal/services/auth/update_staff.go new file mode 100644 index 0000000..0e4ead1 --- /dev/null +++ b/internal/services/auth/update_staff.go @@ -0,0 +1,15 @@ +package authsvc + +import authdomain "legalgo-BE-go/internal/domain/auth" + +func (as *AuthSvc) UpdateStaff(spec authdomain.Staff) error { + if _, err := as.staffRepo.GetStaffByID(spec.ID); err != nil { + return err + } + + if err := as.staffRepo.Update(spec); err != nil { + return err + } + + return nil +} diff --git a/internal/utilities/utils/struct_to_map.go b/internal/utilities/utils/struct_to_map.go new file mode 100644 index 0000000..23b7efd --- /dev/null +++ b/internal/utilities/utils/struct_to_map.go @@ -0,0 +1,29 @@ +package utils + +import ( + "errors" + "reflect" +) + +func StructToMap(s any) (map[string]any, error) { + result := make(map[string]any) + + val := reflect.ValueOf(s) + + if val.Kind() != reflect.Struct { + return nil, errors.New("provided value is not struct") + } + + for i := range val.NumField() { + field := val.Type().Field(i) + value := val.Field(i) + + if value.IsZero() || value.IsNil() { + continue + } + + result[field.Name] = value.Interface() + } + + return result, nil +} diff --git a/openapi.yml b/openapi.yml index e78518d..eff9256 100644 --- a/openapi.yml +++ b/openapi.yml @@ -120,6 +120,98 @@ paths: message: type: string + /staff/{id}/profile: + get: + summary: "get staff profile" + tags: + - staff + parameters: + - name: "id" + in: query + description: "staff id" + required: true + schema: + type: string + responses: + "200": + description: Success get profile + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + id: + type: string + email: + type: string + username: + type: string + "400": + description: Bad request + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: + type: string + message: + type: string + + /user/{id}/profile: + get: + summary: "get staff profile" + tags: + - user + parameters: + - name: "id" + in: query + description: "user id" + required: true + schema: + type: string + responses: + "200": + description: Success get profile + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + id: + type: string + email: + type: string + subscribe_plan_code: + type: string + subscribe_plan_name: + type: string + subscribe_status: + type: string + "400": + description: Bad request + content: + application/json: + schema: + type: object + properties: + error: + type: object + properties: + code: + type: string + message: + type: string + /user/login: post: summary: Login for user