team management + minor fixes
This commit is contained in:
parent
834a807edc
commit
97c4f3d28f
|
|
@ -72,10 +72,10 @@ VALUES
|
||||||
'Admin',
|
'Admin',
|
||||||
'Female',
|
'Female',
|
||||||
'1995-01-01',
|
'1995-01-01',
|
||||||
'admin@yimaru.com',
|
'yaredyemane1@gmail.com',
|
||||||
'0911001100',
|
'0911001100',
|
||||||
'ADMIN',
|
'ADMIN',
|
||||||
crypt('password@123', gen_salt('bf'))::bytea,
|
crypt('password123', gen_salt('bf'))::bytea,
|
||||||
'35_44',
|
'35_44',
|
||||||
'Master',
|
'Master',
|
||||||
'Ethiopia',
|
'Ethiopia',
|
||||||
|
|
@ -410,3 +410,172 @@ VALUES
|
||||||
(3, 12, 1),
|
(3, 12, 1),
|
||||||
(4, 10, 1), (4, 12, 2)
|
(4, 10, 1), (4, 12, 2)
|
||||||
ON CONFLICT (question_set_id, user_id) DO NOTHING;
|
ON CONFLICT (question_set_id, user_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- ======================================================
|
||||||
|
-- Team Members (Internal LMS Staff)
|
||||||
|
-- ======================================================
|
||||||
|
|
||||||
|
INSERT INTO team_members (
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
email,
|
||||||
|
phone_number,
|
||||||
|
password,
|
||||||
|
team_role,
|
||||||
|
department,
|
||||||
|
job_title,
|
||||||
|
employment_type,
|
||||||
|
hire_date,
|
||||||
|
bio,
|
||||||
|
status,
|
||||||
|
email_verified,
|
||||||
|
permissions,
|
||||||
|
created_at
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
'Yared',
|
||||||
|
'Yemane',
|
||||||
|
'yared@yimaru.com',
|
||||||
|
'0911001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'SUPER_ADMIN',
|
||||||
|
'Engineering',
|
||||||
|
'CTO',
|
||||||
|
'full_time',
|
||||||
|
'2024-01-01',
|
||||||
|
'Platform super administrator with full system access.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["*"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
2,
|
||||||
|
'Admin',
|
||||||
|
'User',
|
||||||
|
'admin@yimaru.com',
|
||||||
|
'0922001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'ADMIN',
|
||||||
|
'Operations',
|
||||||
|
'Operations Manager',
|
||||||
|
'full_time',
|
||||||
|
'2024-02-01',
|
||||||
|
'Administrative staff managing day-to-day operations.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["users.manage", "courses.manage", "settings.manage"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
3,
|
||||||
|
'Content',
|
||||||
|
'MANAGER',
|
||||||
|
'content@yimaru.com',
|
||||||
|
'0933001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'CONTENT_MANAGER',
|
||||||
|
'Content',
|
||||||
|
'Content Lead',
|
||||||
|
'full_time',
|
||||||
|
'2024-03-01',
|
||||||
|
'Manages all course content and curriculum.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["courses.manage", "courses.publish", "content.manage"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
4,
|
||||||
|
'Support',
|
||||||
|
'AGENT',
|
||||||
|
'support-team@yimaru.com',
|
||||||
|
'0944001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'SUPPORT_AGENT',
|
||||||
|
'Support',
|
||||||
|
'Customer Support Specialist',
|
||||||
|
'full_time',
|
||||||
|
'2024-03-15',
|
||||||
|
'Handles customer inquiries and support tickets.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["users.view", "tickets.manage", "support.manage"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
5,
|
||||||
|
'INSTRUCTOR',
|
||||||
|
'Demo',
|
||||||
|
'instructor@yimaru.com',
|
||||||
|
'0955001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'INSTRUCTOR',
|
||||||
|
'Education',
|
||||||
|
'Senior Instructor',
|
||||||
|
'full_time',
|
||||||
|
'2024-04-01',
|
||||||
|
'Creates and manages course materials.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["courses.create", "courses.edit", "students.view"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
6,
|
||||||
|
'FINANCE',
|
||||||
|
'Officer',
|
||||||
|
'finance@yimaru.com',
|
||||||
|
'0966001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'FINANCE',
|
||||||
|
'Finance',
|
||||||
|
'Finance Officer',
|
||||||
|
'full_time',
|
||||||
|
'2024-04-15',
|
||||||
|
'Manages payments, subscriptions, and financial reports.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["payments.manage", "subscriptions.manage", "reports.finance"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
7,
|
||||||
|
'HR',
|
||||||
|
'MANAGER',
|
||||||
|
'hr@yimaru.com',
|
||||||
|
'0977001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'HR',
|
||||||
|
'Human Resources',
|
||||||
|
'HR Manager',
|
||||||
|
'full_time',
|
||||||
|
'2024-05-01',
|
||||||
|
'Manages team members and HR operations.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["team.manage", "team.create", "team.delete"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
),
|
||||||
|
(
|
||||||
|
8,
|
||||||
|
'Data',
|
||||||
|
'Analyst',
|
||||||
|
'analyst@yimaru.com',
|
||||||
|
'0988001100',
|
||||||
|
crypt('password@123', gen_salt('bf'))::bytea,
|
||||||
|
'ANALYST',
|
||||||
|
'Analytics',
|
||||||
|
'Data Analyst',
|
||||||
|
'contract',
|
||||||
|
'2024-06-01',
|
||||||
|
'Generates reports and analyzes platform metrics.',
|
||||||
|
'active',
|
||||||
|
TRUE,
|
||||||
|
'["reports.view", "analytics.view", "users.view"]'::jsonb,
|
||||||
|
CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
ON CONFLICT (id) DO NOTHING;
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@ CREATE TABLE IF NOT EXISTS team_members (
|
||||||
-- Role within the team (different from learner roles)
|
-- Role within the team (different from learner roles)
|
||||||
team_role VARCHAR(50) NOT NULL CHECK (
|
team_role VARCHAR(50) NOT NULL CHECK (
|
||||||
team_role IN (
|
team_role IN (
|
||||||
'super_admin', -- Full system access
|
'SUPER_ADMIN', -- Full system access
|
||||||
'admin', -- Administrative tasks
|
'ADMIN', -- Administrative tasks
|
||||||
'content_manager', -- Manages courses, content
|
'CONTENT_MANAGER', -- Manages courses, content
|
||||||
'support_agent', -- Customer support
|
'SUPPORT_AGENT', -- Customer support
|
||||||
'instructor', -- Creates/manages courses
|
'INSTRUCTOR', -- Creates/manages courses
|
||||||
'finance', -- Payment/subscription management
|
'FINANCE', -- Payment/subscription management
|
||||||
'hr', -- Team member management
|
'HR', -- Team member management
|
||||||
'analyst' -- Reports and analytics
|
'ANALYST' -- Reports and analytics
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ package domain
|
||||||
type Role string
|
type Role string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RoleSuperAdmin Role = "super_admin"
|
RoleSuperAdmin Role = "SUPER_ADMIN"
|
||||||
RoleAdmin Role = "admin"
|
RoleAdmin Role = "ADMIN"
|
||||||
RoleStudent Role = "student"
|
RoleStudent Role = "STUDENT"
|
||||||
RoleInstructor Role = "instructor"
|
RoleInstructor Role = "INSTRUCTOR"
|
||||||
RoleSupport Role = "support"
|
RoleSupport Role = "SUPPORT"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r Role) IsValid() bool {
|
func (r Role) IsValid() bool {
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,8 @@ type CreateUserReq struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordReq struct {
|
type ResetPasswordReq struct {
|
||||||
UserID int64
|
Email string
|
||||||
|
PhoneNumber string
|
||||||
Password string
|
Password string
|
||||||
OtpCode string
|
OtpCode string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ type UserStore interface {
|
||||||
phone string,
|
phone string,
|
||||||
) (domain.User, error)
|
) (domain.User, error)
|
||||||
UpdatePassword(ctx context.Context, password string, userID int64) error
|
UpdatePassword(ctx context.Context, password string, userID int64) error
|
||||||
|
UpdatePasswordHash(ctx context.Context, hashedPassword []byte, userID int64) error
|
||||||
RegisterDevice(ctx context.Context, userID int64, deviceToken, platform string) error
|
RegisterDevice(ctx context.Context, userID int64, deviceToken, platform string) error
|
||||||
GetUserDeviceTokens(ctx context.Context, userID int64) ([]string, error)
|
GetUserDeviceTokens(ctx context.Context, userID int64) ([]string, error)
|
||||||
DeactivateDevice(ctx context.Context, userID int64, deviceToken string) error
|
DeactivateDevice(ctx context.Context, userID int64, deviceToken string) error
|
||||||
|
|
|
||||||
|
|
@ -751,7 +751,7 @@ func (s *Store) GetUserByEmailPhone(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword updates a user's password
|
// UpdatePassword updates a user's password (deprecated - use UpdatePasswordHash)
|
||||||
func (s *Store) UpdatePassword(ctx context.Context, password string, userID int64) error {
|
func (s *Store) UpdatePassword(ctx context.Context, password string, userID int64) error {
|
||||||
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||||
Password: []byte(password),
|
Password: []byte(password),
|
||||||
|
|
@ -759,6 +759,14 @@ func (s *Store) UpdatePassword(ctx context.Context, password string, userID int6
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdatePasswordHash updates a user's password with a pre-hashed value
|
||||||
|
func (s *Store) UpdatePasswordHash(ctx context.Context, hashedPassword []byte, userID int64) error {
|
||||||
|
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||||
|
Password: hashedPassword,
|
||||||
|
ID: userID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// mapUser converts dbgen.User to domain.User
|
// mapUser converts dbgen.User to domain.User
|
||||||
func mapCreateUserResult(
|
func mapCreateUserResult(
|
||||||
userRes dbgen.CreateUserRow,
|
userRes dbgen.CreateUserRow,
|
||||||
|
|
|
||||||
|
|
@ -3,54 +3,91 @@ package user
|
||||||
import (
|
import (
|
||||||
"Yimaru-Backend/internal/domain"
|
"Yimaru-Backend/internal/domain"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) SendResetCode(ctx context.Context, userID int64, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider) error {
|
func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string, provider domain.SMSProvider) error {
|
||||||
|
var user domain.User
|
||||||
var err error
|
var err error
|
||||||
// check if user exists
|
|
||||||
|
// Look up user by email or phone to get the actual userID
|
||||||
switch medium {
|
switch medium {
|
||||||
case domain.OtpMediumEmail:
|
case domain.OtpMediumEmail:
|
||||||
_, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "")
|
user, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "")
|
||||||
case domain.OtpMediumSms:
|
case domain.OtpMediumSms:
|
||||||
_, err = s.userStore.GetUserByEmailPhone(ctx, "", sentTo)
|
user, err = s.userStore.GetUserByEmailPhone(ctx, "", sentTo)
|
||||||
|
default:
|
||||||
|
return errors.New("invalid OTP medium")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.SendOtp(ctx, userID, sentTo, domain.OtpReset, medium, provider)
|
// Use the actual user ID when storing OTP
|
||||||
|
return s.SendOtp(ctx, user.ID, sentTo, domain.OtpReset, medium, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswordReq) error {
|
func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswordReq) error {
|
||||||
|
// Look up user by email or phone (don't trust client-provided user_id)
|
||||||
|
var user domain.User
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if resetReq.Email != "" {
|
||||||
|
user, err = s.userStore.GetUserByEmailPhone(ctx, resetReq.Email, "")
|
||||||
|
} else if resetReq.PhoneNumber != "" {
|
||||||
|
user, err = s.userStore.GetUserByEmailPhone(ctx, "", resetReq.PhoneNumber)
|
||||||
|
} else {
|
||||||
|
return errors.New("email or phone number is required")
|
||||||
|
}
|
||||||
|
|
||||||
otp, err := s.otpStore.GetOtp(ctx, resetReq.UserID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
// Get OTP for the actual user
|
||||||
// if err != nil {
|
otp, err := s.otpStore.GetOtp(ctx, user.ID)
|
||||||
// return err
|
if err != nil {
|
||||||
// }
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate OTP purpose (should be for reset, not registration)
|
||||||
|
if otp.For != domain.OtpReset {
|
||||||
|
return domain.ErrInvalidOtp
|
||||||
|
}
|
||||||
|
|
||||||
if otp.Used {
|
if otp.Used {
|
||||||
return domain.ErrOtpAlreadyUsed
|
return domain.ErrOtpAlreadyUsed
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(otp.ExpiresAt) {
|
if time.Now().After(otp.ExpiresAt) {
|
||||||
return domain.ErrOtpExpired
|
return domain.ErrOtpExpired
|
||||||
}
|
}
|
||||||
if otp.Otp != resetReq.OtpCode {
|
|
||||||
|
// Use constant-time comparison for OTP
|
||||||
|
if subtle.ConstantTimeCompare([]byte(otp.Otp), []byte(resetReq.OtpCode)) != 1 {
|
||||||
return domain.ErrInvalidOtp
|
return domain.ErrInvalidOtp
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.userStore.UpdatePassword(ctx, resetReq.Password, resetReq.UserID)
|
// Hash the new password before storing
|
||||||
|
hashedPassword, err := hashPassword(resetReq.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update password with hashed value
|
||||||
|
err = s.userStore.UpdatePasswordHash(ctx, hashedPassword, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark OTP as used to prevent replay attacks
|
||||||
|
err = s.otpStore.MarkOtpAsUsed(ctx, otp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@ func (h *Handler) GoogleAndroidLogin(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
Message: "Login successful",
|
Message: "Login successful",
|
||||||
Data: fiber.Map{
|
Data: loginUserRes{
|
||||||
"accessToken": accessToken,
|
AccessToken: accessToken,
|
||||||
"refreshToken": loginRes.RfToken,
|
RefreshToken: loginRes.RfToken,
|
||||||
"userId": loginRes.UserId,
|
Role: string(loginRes.Role),
|
||||||
"role": loginRes.Role,
|
UserID: loginRes.UserId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -145,11 +145,11 @@ func (h *Handler) GoogleCallback(c *fiber.Ctx) error {
|
||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||||
Message: "Login successful",
|
Message: "Login successful",
|
||||||
Data: fiber.Map{
|
Data: loginUserRes{
|
||||||
"accessToken": accessToken,
|
AccessToken: accessToken,
|
||||||
"refreshToken": loginRes.RfToken,
|
RefreshToken: loginRes.RfToken,
|
||||||
"userId": loginRes.UserId,
|
Role: string(loginRes.Role),
|
||||||
"role": loginRes.Role,
|
UserID: loginRes.UserId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1001,7 +1001,7 @@ func (h *Handler) SendResetCode(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
|
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.userSvc.SendResetCode(c.Context(), 0, medium, sentTo, domain.AfroMessage); err != nil {
|
if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil {
|
||||||
h.mongoLoggerSvc.Error("Failed to send reset code",
|
h.mongoLoggerSvc.Error("Failed to send reset code",
|
||||||
zap.String("medium", string(medium)),
|
zap.String("medium", string(medium)),
|
||||||
zap.String("sentTo", string(sentTo)),
|
zap.String("sentTo", string(sentTo)),
|
||||||
|
|
@ -1068,7 +1068,7 @@ func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
|
return fiber.NewError(fiber.StatusBadRequest, "Email or PhoneNumber must be provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.userSvc.SendResetCode(c.Context(), 0, medium, sentTo, domain.AfroMessage); err != nil {
|
if err := h.userSvc.SendResetCode(c.Context(), medium, sentTo, domain.AfroMessage); err != nil {
|
||||||
h.mongoLoggerSvc.Error("Failed to send reset code",
|
h.mongoLoggerSvc.Error("Failed to send reset code",
|
||||||
zap.String("medium", string(medium)),
|
zap.String("medium", string(medium)),
|
||||||
zap.String("sentTo", string(sentTo)),
|
zap.String("sentTo", string(sentTo)),
|
||||||
|
|
@ -1083,7 +1083,8 @@ func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordReq struct {
|
type ResetPasswordReq struct {
|
||||||
UserID int64 `json:"user_name" validate:"required" example:"johndoe"`
|
Email string `json:"email" example:"john.doe@example.com"`
|
||||||
|
PhoneNumber string `json:"phone_number" example:"1234567890"`
|
||||||
Password string `json:"password" validate:"required,min=8" example:"newpassword123"`
|
Password string `json:"password" validate:"required,min=8" example:"newpassword123"`
|
||||||
Otp string `json:"otp" validate:"required" example:"123456"`
|
Otp string `json:"otp" validate:"required" example:"123456"`
|
||||||
}
|
}
|
||||||
|
|
@ -1100,7 +1101,6 @@ type ResetPasswordReq struct {
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /api/v1/user/resetPassword [post]
|
// @Router /api/v1/user/resetPassword [post]
|
||||||
func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
||||||
|
|
||||||
var req ResetPasswordReq
|
var req ResetPasswordReq
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
h.mongoLoggerSvc.Info("Failed to parse ResetPassword request",
|
h.mongoLoggerSvc.Info("Failed to parse ResetPassword request",
|
||||||
|
|
@ -1119,37 +1119,21 @@ func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, errMsg)
|
return fiber.NewError(fiber.StatusBadRequest, errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// user, err := h.userSvc.GetUserByUserName(c.Context(), req.UserName)
|
if req.Email == "" && req.PhoneNumber == "" {
|
||||||
// if err != nil {
|
return fiber.NewError(fiber.StatusBadRequest, "Email or phone number is required")
|
||||||
// h.mongoLoggerSvc.Info("Failed to get user by user name",
|
}
|
||||||
// zap.String("user_name", req.UserName),
|
|
||||||
// zap.Int("status_code", fiber.StatusBadRequest),
|
|
||||||
// zap.Error(err),
|
|
||||||
// zap.Time("timestamp", time.Now()),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
// medium, err := getMedium(user.Email, user.PhoneNumber)
|
|
||||||
// if err != nil {
|
|
||||||
// h.mongoLoggerSvc.Info("Failed to determine medium for ResetPassword",
|
|
||||||
// zap.String("Email", user.Email),
|
|
||||||
// zap.String("Phone Number", user.PhoneNumber),
|
|
||||||
// zap.Int("status_code", fiber.StatusBadRequest),
|
|
||||||
// zap.Error(err),
|
|
||||||
// zap.Time("timestamp", time.Now()),
|
|
||||||
// )
|
|
||||||
// return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
resetReq := domain.ResetPasswordReq{
|
resetReq := domain.ResetPasswordReq{
|
||||||
UserID: req.UserID,
|
Email: req.Email,
|
||||||
|
PhoneNumber: req.PhoneNumber,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
OtpCode: req.Otp,
|
OtpCode: req.Otp,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.userSvc.ResetPassword(c.Context(), resetReq); err != nil {
|
if err := h.userSvc.ResetPassword(c.Context(), resetReq); err != nil {
|
||||||
h.mongoLoggerSvc.Error("Failed to reset password",
|
h.mongoLoggerSvc.Error("Failed to reset password",
|
||||||
zap.Any("userID", resetReq),
|
zap.String("email", req.Email),
|
||||||
|
zap.String("phone", req.PhoneNumber),
|
||||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
zap.Time("timestamp", time.Now()),
|
zap.Time("timestamp", time.Now()),
|
||||||
|
|
@ -1172,59 +1156,8 @@ func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
||||||
// @Failure 500 {object} response.APIResponse
|
// @Failure 500 {object} response.APIResponse
|
||||||
// @Router /api/v1/{tenant_slug}/user/resetPassword [post]
|
// @Router /api/v1/{tenant_slug}/user/resetPassword [post]
|
||||||
func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
|
func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
|
||||||
// companyID := c.Locals("company_id").(domain.ValidInt64)
|
// Reuse the main ResetPassword handler
|
||||||
// if !companyID.Valid {
|
return h.ResetPassword(c)
|
||||||
// h.BadRequestLogger().Error("invalid company id")
|
|
||||||
// return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
|
|
||||||
// }
|
|
||||||
|
|
||||||
var req ResetPasswordReq
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
h.mongoLoggerSvc.Info("Failed to parse ResetPassword request",
|
|
||||||
zap.Int("status_code", fiber.StatusBadRequest),
|
|
||||||
zap.Error(err),
|
|
||||||
zap.Time("timestamp", time.Now()),
|
|
||||||
)
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if valErrs, ok := h.validator.Validate(c, req); !ok {
|
|
||||||
var errMsg string
|
|
||||||
for field, msg := range valErrs {
|
|
||||||
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
|
|
||||||
}
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// medium, err := getMedium(req.Email, req.PhoneNumber)
|
|
||||||
// if err != nil {
|
|
||||||
// h.mongoLoggerSvc.Info("Failed to determine medium for ResetPassword",
|
|
||||||
// zap.String("Email", req.Email),
|
|
||||||
// zap.String("Phone Number", req.PhoneNumber),
|
|
||||||
// zap.Int("status_code", fiber.StatusBadRequest),
|
|
||||||
// zap.Error(err),
|
|
||||||
// zap.Time("timestamp", time.Now()),
|
|
||||||
// )
|
|
||||||
// return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
// }
|
|
||||||
|
|
||||||
resetReq := domain.ResetPasswordReq{
|
|
||||||
UserID: req.UserID,
|
|
||||||
Password: req.Password,
|
|
||||||
OtpCode: req.Otp,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := h.userSvc.ResetPassword(c.Context(), resetReq); err != nil {
|
|
||||||
h.mongoLoggerSvc.Error("Failed to reset password",
|
|
||||||
zap.Any("userID", resetReq),
|
|
||||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
||||||
zap.Error(err),
|
|
||||||
zap.Time("timestamp", time.Now()),
|
|
||||||
)
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to reset password:"+err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.WriteJSON(c, fiber.StatusOK, "Password reset successful", nil, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomerProfile godoc
|
// CustomerProfile godoc
|
||||||
|
|
|
||||||
|
|
@ -288,9 +288,6 @@ func (a *App) initAppRoutes() {
|
||||||
groupV1.Post("/user/sendResetCode", h.SendResetCode)
|
groupV1.Post("/user/sendResetCode", h.SendResetCode)
|
||||||
groupV1.Post("/user/verify-otp", h.VerifyOtp)
|
groupV1.Post("/user/verify-otp", h.VerifyOtp)
|
||||||
groupV1.Post("/user/resend-otp", h.ResendOtp)
|
groupV1.Post("/user/resend-otp", h.ResendOtp)
|
||||||
|
|
||||||
groupV1.Post("/user/resetPassword", h.ResetTenantPassword)
|
|
||||||
groupV1.Post("/user/sendResetCode", h.SendTenantResetCode)
|
|
||||||
groupV1.Post("/user/register", h.RegisterUser)
|
groupV1.Post("/user/register", h.RegisterUser)
|
||||||
groupV1.Post("/user/sendRegisterCode", h.SendRegisterCode)
|
groupV1.Post("/user/sendRegisterCode", h.SendRegisterCode)
|
||||||
groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
|
groupV1.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
|
||||||
|
|
@ -314,7 +311,7 @@ func (a *App) initAppRoutes() {
|
||||||
// groupV1.Put("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateTransactionApprover)
|
// groupV1.Put("/t-approver/:id", a.authMiddleware, a.OnlyAdminAndAbove, h.UpdateTransactionApprover)
|
||||||
|
|
||||||
//mongoDB logs
|
//mongoDB logs
|
||||||
groupV1.Get("/logs", a.authMiddleware, a.SuperAdminOnly, handlers.GetLogsHandler(context.Background()))
|
groupV1.Get("/logs", a.authMiddleware, a.OnlyAdminAndAbove, handlers.GetLogsHandler(context.Background()))
|
||||||
|
|
||||||
// groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions)
|
// groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions)
|
||||||
// groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID)
|
// groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user