team management + minor fixes
This commit is contained in:
parent
834a807edc
commit
97c4f3d28f
|
|
@ -72,10 +72,10 @@ VALUES
|
|||
'Admin',
|
||||
'Female',
|
||||
'1995-01-01',
|
||||
'admin@yimaru.com',
|
||||
'yaredyemane1@gmail.com',
|
||||
'0911001100',
|
||||
'ADMIN',
|
||||
crypt('password@123', gen_salt('bf'))::bytea,
|
||||
crypt('password123', gen_salt('bf'))::bytea,
|
||||
'35_44',
|
||||
'Master',
|
||||
'Ethiopia',
|
||||
|
|
@ -410,3 +410,172 @@ VALUES
|
|||
(3, 12, 1),
|
||||
(4, 10, 1), (4, 12, 2)
|
||||
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)
|
||||
team_role VARCHAR(50) NOT NULL CHECK (
|
||||
team_role IN (
|
||||
'super_admin', -- Full system access
|
||||
'admin', -- Administrative tasks
|
||||
'content_manager', -- Manages courses, content
|
||||
'support_agent', -- Customer support
|
||||
'instructor', -- Creates/manages courses
|
||||
'finance', -- Payment/subscription management
|
||||
'hr', -- Team member management
|
||||
'analyst' -- Reports and analytics
|
||||
'SUPER_ADMIN', -- Full system access
|
||||
'ADMIN', -- Administrative tasks
|
||||
'CONTENT_MANAGER', -- Manages courses, content
|
||||
'SUPPORT_AGENT', -- Customer support
|
||||
'INSTRUCTOR', -- Creates/manages courses
|
||||
'FINANCE', -- Payment/subscription management
|
||||
'HR', -- Team member management
|
||||
'ANALYST' -- Reports and analytics
|
||||
)
|
||||
),
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package domain
|
|||
type Role string
|
||||
|
||||
const (
|
||||
RoleSuperAdmin Role = "super_admin"
|
||||
RoleAdmin Role = "admin"
|
||||
RoleStudent Role = "student"
|
||||
RoleInstructor Role = "instructor"
|
||||
RoleSupport Role = "support"
|
||||
RoleSuperAdmin Role = "SUPER_ADMIN"
|
||||
RoleAdmin Role = "ADMIN"
|
||||
RoleStudent Role = "STUDENT"
|
||||
RoleInstructor Role = "INSTRUCTOR"
|
||||
RoleSupport Role = "SUPPORT"
|
||||
)
|
||||
|
||||
func (r Role) IsValid() bool {
|
||||
|
|
|
|||
|
|
@ -169,7 +169,8 @@ type CreateUserReq struct {
|
|||
}
|
||||
|
||||
type ResetPasswordReq struct {
|
||||
UserID int64
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password string
|
||||
OtpCode string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ type UserStore interface {
|
|||
phone string,
|
||||
) (domain.User, 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
|
||||
GetUserDeviceTokens(ctx context.Context, userID int64) ([]string, error)
|
||||
DeactivateDevice(ctx context.Context, userID int64, deviceToken string) error
|
||||
|
|
|
|||
|
|
@ -751,7 +751,7 @@ func (s *Store) GetUserByEmailPhone(
|
|||
}, 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 {
|
||||
return s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||
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
|
||||
func mapCreateUserResult(
|
||||
userRes dbgen.CreateUserRow,
|
||||
|
|
|
|||
|
|
@ -3,54 +3,91 @@ package user
|
|||
import (
|
||||
"Yimaru-Backend/internal/domain"
|
||||
"context"
|
||||
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"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
|
||||
// check if user exists
|
||||
|
||||
// Look up user by email or phone to get the actual userID
|
||||
switch medium {
|
||||
case domain.OtpMediumEmail:
|
||||
_, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "")
|
||||
user, err = s.userStore.GetUserByEmailPhone(ctx, sentTo, "")
|
||||
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 {
|
||||
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 {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
// user, err := s.userStore.GetUserByUserName(ctx, resetReq.UserName)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// Get OTP for the actual user
|
||||
otp, err := s.otpStore.GetOtp(ctx, user.ID)
|
||||
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 {
|
||||
return domain.ErrOtpAlreadyUsed
|
||||
}
|
||||
|
||||
if time.Now().After(otp.ExpiresAt) {
|
||||
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
|
||||
}
|
||||
|
||||
err = s.userStore.UpdatePassword(ctx, resetReq.Password, resetReq.UserID)
|
||||
// Hash the new password before storing
|
||||
hashedPassword, err := hashPassword(resetReq.Password)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,11 +73,11 @@ func (h *Handler) GoogleAndroidLogin(c *fiber.Ctx) error {
|
|||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "Login successful",
|
||||
Data: fiber.Map{
|
||||
"accessToken": accessToken,
|
||||
"refreshToken": loginRes.RfToken,
|
||||
"userId": loginRes.UserId,
|
||||
"role": loginRes.Role,
|
||||
Data: loginUserRes{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: loginRes.RfToken,
|
||||
Role: string(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{
|
||||
Message: "Login successful",
|
||||
Data: fiber.Map{
|
||||
"accessToken": accessToken,
|
||||
"refreshToken": loginRes.RfToken,
|
||||
"userId": loginRes.UserId,
|
||||
"role": loginRes.Role,
|
||||
Data: loginUserRes{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: loginRes.RfToken,
|
||||
Role: string(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")
|
||||
}
|
||||
|
||||
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",
|
||||
zap.String("medium", string(medium)),
|
||||
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")
|
||||
}
|
||||
|
||||
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",
|
||||
zap.String("medium", string(medium)),
|
||||
zap.String("sentTo", string(sentTo)),
|
||||
|
|
@ -1083,7 +1083,8 @@ func (h *Handler) SendTenantResetCode(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
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"`
|
||||
Otp string `json:"otp" validate:"required" example:"123456"`
|
||||
}
|
||||
|
|
@ -1100,7 +1101,6 @@ type ResetPasswordReq struct {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /api/v1/user/resetPassword [post]
|
||||
func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
||||
|
||||
var req ResetPasswordReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// user, err := h.userSvc.GetUserByUserName(c.Context(), req.UserName)
|
||||
// if err != nil {
|
||||
// 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())
|
||||
// }
|
||||
if req.Email == "" && req.PhoneNumber == "" {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Email or phone number is required")
|
||||
}
|
||||
|
||||
resetReq := domain.ResetPasswordReq{
|
||||
UserID: req.UserID,
|
||||
Email: req.Email,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
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.String("email", req.Email),
|
||||
zap.String("phone", req.PhoneNumber),
|
||||
zap.Int("status_code", fiber.StatusInternalServerError),
|
||||
zap.Error(err),
|
||||
zap.Time("timestamp", time.Now()),
|
||||
|
|
@ -1172,59 +1156,8 @@ func (h *Handler) ResetPassword(c *fiber.Ctx) error {
|
|||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /api/v1/{tenant_slug}/user/resetPassword [post]
|
||||
func (h *Handler) ResetTenantPassword(c *fiber.Ctx) error {
|
||||
// companyID := c.Locals("company_id").(domain.ValidInt64)
|
||||
// if !companyID.Valid {
|
||||
// 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)
|
||||
// Reuse the main ResetPassword handler
|
||||
return h.ResetPassword(c)
|
||||
}
|
||||
|
||||
// CustomerProfile godoc
|
||||
|
|
|
|||
|
|
@ -288,9 +288,6 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/user/sendResetCode", h.SendResetCode)
|
||||
groupV1.Post("/user/verify-otp", h.VerifyOtp)
|
||||
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/sendRegisterCode", h.SendRegisterCode)
|
||||
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)
|
||||
|
||||
//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/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user