Enhance API documentation and restructure routes for improved clarity
- Updated README.md to include comprehensive Swagger documentation details, highlighting new features and endpoint categories. - Introduced a new router structure to streamline route registration for admin and public endpoints, enhancing maintainability. - Updated health check endpoint documentation to reflect comprehensive server status information. - Enhanced customer and company management routes with improved descriptions and examples in Swagger. - Refactored customer and user handlers to remove obsolete route registrations, aligning with the new router structure. - Improved response handling in customer and user services to utilize string IDs consistently. - Updated validation rules and forms for customer management to support multiple company assignments. - Enhanced logging practices across services to ensure better traceability and error handling.
This commit is contained in:
@@ -2,6 +2,8 @@ package user
|
||||
|
||||
import (
|
||||
"tm/pkg/mongo"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// UserRole represents user roles in the system
|
||||
@@ -25,7 +27,7 @@ const (
|
||||
|
||||
// User represents a system user (admin/manager/operator)
|
||||
type User struct {
|
||||
mongo.Model
|
||||
mongo.Model `bson:",inline"`
|
||||
FullName string `bson:"full_name" json:"full_name"`
|
||||
Username string `bson:"username" json:"username"`
|
||||
Email string `bson:"email" json:"email"`
|
||||
@@ -45,12 +47,12 @@ type User struct {
|
||||
|
||||
// SetID sets the user ID (implements IDSetter interface)
|
||||
func (u *User) SetID(id string) {
|
||||
u.ID = id
|
||||
u.ID, _ = primitive.ObjectIDFromHex(id)
|
||||
}
|
||||
|
||||
// GetID returns the user ID (implements IDGetter interface)
|
||||
func (u *User) GetID() string {
|
||||
return u.ID
|
||||
return u.ID.Hex()
|
||||
}
|
||||
|
||||
// SetCreatedAt sets the created timestamp (implements Timestampable interface)
|
||||
|
||||
+41
-32
@@ -1,57 +1,66 @@
|
||||
package user
|
||||
|
||||
// User Registration DTOs
|
||||
|
||||
// CreateUserForm represents the data required to create a new user account
|
||||
type CreateUserForm struct {
|
||||
FullName string `json:"full_name" valid:"required,length(2|100)"`
|
||||
Username string `json:"username" valid:"required,alphanum,length(3|30)"`
|
||||
Email string `json:"email" valid:"required,email"`
|
||||
Password string `json:"password" valid:"required,length(8|128)"`
|
||||
Role string `json:"role" valid:"required,in(admin|manager|operator|viewer)"`
|
||||
CompanyID *string `json:"company_id,omitempty" valid:"optional,uuid"`
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)"`
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)"`
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url"`
|
||||
FullName string `json:"full_name" valid:"required,length(2|100)" example:"John Smith"` // Full name of the user
|
||||
Username string `json:"username" valid:"required,alphanum,length(3|30)" example:"johnsmith"` // Unique username (alphanumeric only)
|
||||
Email string `json:"email" valid:"required,email" example:"john.smith@company.com"` // Valid email address
|
||||
Password string `json:"password" valid:"required,length(8|128)" example:"SecurePass123!"` // Password (minimum 8 characters)
|
||||
Role string `json:"role" valid:"required,in(admin|manager|operator|viewer)" example:"manager"` // User role (admin, manager, operator, viewer)
|
||||
CompanyID *string `json:"company_id,omitempty" valid:"optional,uuid" example:"123e4567-e89b-12d3-a456-426614174000"` // Optional company UUID
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Information Technology"` // Optional department name
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Senior Developer"` // Optional job position
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Optional phone number
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:"https://example.com/avatar.jpg"` // Optional profile image URL
|
||||
}
|
||||
|
||||
// UpdateUserForm represents the data for updating an existing user account
|
||||
type UpdateUserForm struct {
|
||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)"`
|
||||
Username *string `json:"username,omitempty" valid:"optional,alphanum,length(3|30)"`
|
||||
Email *string `json:"email,omitempty" valid:"optional,email"`
|
||||
Role *string `json:"role,omitempty" valid:"optional,in(admin|manager|operator|viewer)"`
|
||||
CompanyID *string `json:"company_id,omitempty" valid:"optional,uuid"`
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)"`
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)"`
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url"`
|
||||
Status *string `json:"status,omitempty" valid:"optional,in(active|inactive|suspended)"`
|
||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)" example:"John Smith"` // Updated full name
|
||||
Username *string `json:"username,omitempty" valid:"optional,alphanum,length(3|30)" example:"johnsmith"` // Updated username
|
||||
Email *string `json:"email,omitempty" valid:"optional,email" example:"john.smith@newcompany.com"` // Updated email address
|
||||
Role *string `json:"role,omitempty" valid:"optional,in(admin|manager|operator|viewer)" example:"admin"` // Updated user role
|
||||
CompanyID *string `json:"company_id,omitempty" valid:"optional,uuid" example:"123e4567-e89b-12d3-a456-426614174000"` // Updated company ID
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Product Management"` // Updated department
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Tech Lead"` // Updated position
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Updated phone number
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:"https://example.com/new-avatar.jpg"` // Updated profile image URL
|
||||
Status *string `json:"status,omitempty" valid:"optional,in(active|inactive|suspended)" example:"active"` // Updated account status
|
||||
}
|
||||
|
||||
// User Authentication DTOs
|
||||
|
||||
// LoginForm represents the credentials required for user authentication
|
||||
type LoginForm struct {
|
||||
Username string `json:"username" valid:"required"`
|
||||
Password string `json:"password" valid:"required"`
|
||||
Username string `json:"username" valid:"required" example:"nakhostin"` // Username or email address
|
||||
Password string `json:"password" valid:"required" example:"Nima.1998"` // User password
|
||||
}
|
||||
|
||||
// RefreshTokenForm represents the refresh token required to generate new access tokens
|
||||
type RefreshTokenForm struct {
|
||||
RefreshToken string `json:"refresh_token" valid:"required"`
|
||||
RefreshToken string `json:"refresh_token" valid:"required" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."` // Valid refresh token
|
||||
}
|
||||
|
||||
// ChangePasswordForm represents the data required to change user password
|
||||
type ChangePasswordForm struct {
|
||||
OldPassword string `json:"old_password" valid:"required"`
|
||||
NewPassword string `json:"new_password" valid:"required,length(8|128)"`
|
||||
OldPassword string `json:"old_password" valid:"required" example:"OldPassword123!"` // Current password for verification
|
||||
NewPassword string `json:"new_password" valid:"required,length(8|128)" example:"NewPass456!"` // New password (minimum 8 characters)
|
||||
}
|
||||
|
||||
// UpdateProfileForm represents the data for updating user profile information
|
||||
type UpdateProfileForm struct {
|
||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)"`
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)"`
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)"`
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url"`
|
||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)" example:"John Smith"` // Updated full name
|
||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Engineering"` // Updated department
|
||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Senior Engineer"` // Updated position
|
||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Updated phone number
|
||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:"https://example.com/profile.jpg"` // Updated profile image URL
|
||||
}
|
||||
|
||||
// ResetPasswordForm represents the email address for password reset
|
||||
type ResetPasswordForm struct {
|
||||
Email string `json:"email" valid:"required,email"`
|
||||
Email string `json:"email" valid:"required,email" example:"john.smith@company.com"` // Email address for password reset
|
||||
}
|
||||
|
||||
// Admin DTOs
|
||||
@@ -113,7 +122,7 @@ type UserListResponse struct {
|
||||
// Helper function to convert User to UserResponse
|
||||
func (u *User) ToResponse() *UserResponse {
|
||||
return &UserResponse{
|
||||
ID: u.ID,
|
||||
ID: u.ID.Hex(),
|
||||
FullName: u.FullName,
|
||||
Username: u.Username,
|
||||
Email: u.Email,
|
||||
|
||||
+80
-101
@@ -28,50 +28,20 @@ func NewUserHandler(service Service, logger logger.Logger, validator ValidationS
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoutes registers user routes
|
||||
func (h *Handler) RegisterRoutes(e *echo.Echo) {
|
||||
v1 := e.Group("/admin/v1")
|
||||
|
||||
// Public routes (no authentication required)
|
||||
authorizationGP := v1.Group("")
|
||||
authorizationGP.POST("/login", h.Login)
|
||||
authorizationGP.POST("/refresh-token", h.RefreshToken)
|
||||
authorizationGP.POST("/reset-password", h.ResetPassword)
|
||||
|
||||
// Protected routes (require authentication)
|
||||
profileGP := v1.Group("")
|
||||
profileGP.Use(h.AuthMiddleware())
|
||||
profileGP.GET("/profile", h.GetProfile)
|
||||
profileGP.PUT("/profile", h.UpdateProfile)
|
||||
profileGP.PUT("/change-password", h.ChangePassword)
|
||||
profileGP.DELETE("/logout", h.Logout)
|
||||
|
||||
// Admin routes (require admin role)
|
||||
adminGroup := v1.Group("/users")
|
||||
adminGroup.Use(h.AuthMiddleware(), h.AdminMiddleware())
|
||||
adminGroup.POST("", h.CreateUser)
|
||||
adminGroup.GET("", h.ListUsers)
|
||||
adminGroup.GET("/:id", h.GetUserByID)
|
||||
adminGroup.PUT("/:id", h.UpdateUser)
|
||||
adminGroup.DELETE("/:id", h.DeleteUser)
|
||||
adminGroup.PUT("/:id/status", h.UpdateUserStatus)
|
||||
adminGroup.PUT("/:id/role", h.UpdateUserRole)
|
||||
adminGroup.GET("/company/:company_id", h.GetUsersByCompanyID)
|
||||
adminGroup.GET("/role/:role", h.GetUsersByRole)
|
||||
}
|
||||
|
||||
// Login handles user login
|
||||
// @Summary Login user
|
||||
// @Description Authenticate user with username and password
|
||||
// @Summary Authenticate user login
|
||||
// @Description Authenticate user with username/email and password to obtain access and refresh tokens for web panel administration. This endpoint validates credentials and returns JWT tokens for subsequent API calls.
|
||||
// @Tags Authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param login body LoginForm true "Login credentials"
|
||||
// @Success 200 {object} response.APIResponse{data=AuthResponse}
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/login [post]
|
||||
// @Param login body LoginForm true "User login credentials including username/email and password"
|
||||
// @Success 200 {object} response.APIResponse{data=AuthResponse} "Login successful with access and refresh tokens"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid request format or missing fields"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid credentials or account locked"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid input data format"
|
||||
// @Failure 429 {object} response.APIResponse "Too many requests - Rate limit exceeded"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/profile/login [post]
|
||||
func (h *Handler) Login(c echo.Context) error {
|
||||
form, err := response.Parse[LoginForm](c)
|
||||
if err != nil {
|
||||
@@ -89,16 +59,17 @@ func (h *Handler) Login(c echo.Context) error {
|
||||
|
||||
// RefreshToken handles token refresh
|
||||
// @Summary Refresh access token
|
||||
// @Description Refresh access token using refresh token
|
||||
// @Description Generate a new access token using a valid refresh token. This endpoint allows clients to obtain fresh access tokens without re-authentication, maintaining session continuity.
|
||||
// @Tags Authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param refresh body RefreshTokenForm true "Refresh token"
|
||||
// @Success 200 {object} response.APIResponse{data=AuthResponse}
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/refresh-token [post]
|
||||
// @Param refresh body RefreshTokenForm true "Refresh token for generating new access token"
|
||||
// @Success 200 {object} response.APIResponse{data=AuthResponse} "Token refreshed successfully with new access token"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid request format"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired refresh token"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid token format"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/profile/refresh-token [post]
|
||||
func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
form, err := response.Parse[RefreshTokenForm](c)
|
||||
if err != nil {
|
||||
@@ -114,16 +85,19 @@ func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
}
|
||||
|
||||
// ResetPassword handles password reset request
|
||||
// @Summary Reset password
|
||||
// @Description Send password reset email to user
|
||||
// @Summary Initiate password reset process
|
||||
// @Description Send password reset email to user with reset token. This endpoint validates the email address and sends a secure reset link to the user's registered email address.
|
||||
// @Tags Authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param reset body ResetPasswordForm true "Password reset request"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/reset-password [post]
|
||||
// @Param reset body ResetPasswordForm true "Email address for password reset request"
|
||||
// @Success 200 {object} response.APIResponse "Password reset email sent successfully"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid request format or email address"
|
||||
// @Failure 404 {object} response.APIResponse "Not found - Email address not registered"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid email format"
|
||||
// @Failure 429 {object} response.APIResponse "Too many requests - Rate limit for reset emails exceeded"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error - Failed to send email"
|
||||
// @Router /admin/v1/profile/reset-password [post]
|
||||
func (h *Handler) ResetPassword(c echo.Context) error {
|
||||
form, err := response.Parse[ResetPasswordForm](c)
|
||||
if err != nil {
|
||||
@@ -141,17 +115,17 @@ func (h *Handler) ResetPassword(c echo.Context) error {
|
||||
}
|
||||
|
||||
// GetProfile gets current user profile
|
||||
// @Summary Get user profile
|
||||
// @Description Get current user profile information
|
||||
// @Summary Get authenticated user profile
|
||||
// @Description Retrieve complete profile information for the currently authenticated user including personal details, role information, permissions, and account status. This endpoint requires valid authentication token.
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} response.APIResponse{data=UserResponse}
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/profile [get]
|
||||
// @Success 200 {object} response.APIResponse{data=UserResponse} "Profile retrieved successfully with user details"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired authentication token"
|
||||
// @Failure 404 {object} response.APIResponse "Not found - User profile not found"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/profile [get]
|
||||
func (h *Handler) GetProfile(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -168,18 +142,20 @@ func (h *Handler) GetProfile(c echo.Context) error {
|
||||
}
|
||||
|
||||
// UpdateProfile updates current user profile
|
||||
// @Summary Update user profile
|
||||
// @Description Update current user profile information
|
||||
// @Summary Update authenticated user profile
|
||||
// @Description Update profile information for the currently authenticated user including personal details, contact information, and preferences. Only the authenticated user can update their own profile.
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param profile body UpdateUserForm true "Profile update data"
|
||||
// @Success 200 {object} response.APIResponse{data=UserResponse}
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/profile [put]
|
||||
// @Param profile body UpdateUserForm true "Profile update data including name, email, phone, and other personal information"
|
||||
// @Success 200 {object} response.APIResponse{data=UserResponse} "Profile updated successfully with updated user details"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid request format or missing required fields"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired authentication token"
|
||||
// @Failure 409 {object} response.APIResponse "Conflict - Email address already exists for another user"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid input data format"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/profile [put]
|
||||
func (h *Handler) UpdateProfile(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -201,18 +177,19 @@ func (h *Handler) UpdateProfile(c echo.Context) error {
|
||||
}
|
||||
|
||||
// ChangePassword changes current user password
|
||||
// @Summary Change password
|
||||
// @Description Change current user password
|
||||
// @Summary Change user password
|
||||
// @Description Change password for the currently authenticated user. Requires current password verification and enforces password policy. This operation invalidates all existing sessions and requires re-authentication.
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param password body ChangePasswordForm true "Password change data"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/change-password [put]
|
||||
// @Param password body ChangePasswordForm true "Password change data including current password and new password"
|
||||
// @Success 200 {object} response.APIResponse "Password changed successfully - user must re-authenticate"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid request format or password policy violation"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid authentication token or incorrect current password"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid password format or policy requirements not met"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/profile/change-password [put]
|
||||
func (h *Handler) ChangePassword(c echo.Context) error {
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
@@ -235,16 +212,16 @@ func (h *Handler) ChangePassword(c echo.Context) error {
|
||||
}
|
||||
|
||||
// Logout handles user logout
|
||||
// @Summary Logout user
|
||||
// @Description Logout current user and invalidate tokens
|
||||
// @Tags Users
|
||||
// @Summary Logout authenticated user
|
||||
// @Description Logout the currently authenticated user by invalidating their access and refresh tokens. This endpoint ensures secure session termination and prevents further use of the user's tokens.
|
||||
// @Tags Authorization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/logout [delete]
|
||||
// @Success 200 {object} response.APIResponse "Logout successful - all tokens invalidated"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired authentication token"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error - Failed to invalidate tokens"
|
||||
// @Router /admin/v1/profile/logout [delete]
|
||||
func (h *Handler) Logout(c echo.Context) error {
|
||||
// Extract user ID and access token from context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -268,19 +245,21 @@ func (h *Handler) Logout(c echo.Context) error {
|
||||
}
|
||||
|
||||
// CreateUser creates a new user (admin only)
|
||||
// @Summary Create user
|
||||
// @Description Create a new user (admin only)
|
||||
// @Summary Create new user account
|
||||
// @Description Create a new user account with specified role and permissions. This endpoint is restricted to administrators only and allows creation of various user types including admins, managers, and operators with appropriate access levels.
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param user body CreateUserForm true "User creation data"
|
||||
// @Success 201 {object} response.APIResponse{data=UserResponse}
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users [post]
|
||||
// @Param user body CreateUserForm true "User creation data including username, email, password, role, and profile information"
|
||||
// @Success 201 {object} response.APIResponse{data=UserResponse} "User created successfully with assigned role and permissions"
|
||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data or missing required fields"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized - Invalid or expired authentication token"
|
||||
// @Failure 403 {object} response.APIResponse "Forbidden - Insufficient privileges to create users"
|
||||
// @Failure 409 {object} response.APIResponse "Conflict - Username or email already exists"
|
||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid data format or password policy violation"
|
||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||
// @Router /admin/v1/users [post]
|
||||
func (h *Handler) CreateUser(c echo.Context) error {
|
||||
// Get current user ID from JWT token
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -327,7 +306,7 @@ func (h *Handler) CreateUser(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users [get]
|
||||
// @Router /admin/v1/users [get]
|
||||
func (h *Handler) ListUsers(c echo.Context) error {
|
||||
var form ListUsersForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
@@ -362,7 +341,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/{id} [get]
|
||||
// @Router /admin/v1/users/{id} [get]
|
||||
func (h *Handler) GetUserByID(c echo.Context) error {
|
||||
idStr := c.Param("id")
|
||||
|
||||
@@ -389,7 +368,7 @@ func (h *Handler) GetUserByID(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/{id} [put]
|
||||
// @Router /admin/v1/users/{id} [put]
|
||||
func (h *Handler) UpdateUser(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -432,7 +411,7 @@ func (h *Handler) UpdateUser(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/{id} [delete]
|
||||
// @Router /admin/v1/users/{id} [delete]
|
||||
func (h *Handler) DeleteUser(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -467,7 +446,7 @@ func (h *Handler) DeleteUser(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/{id}/status [put]
|
||||
// @Router /admin/v1/users/{id}/status [put]
|
||||
func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
@@ -512,7 +491,7 @@ func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/{id}/role [put]
|
||||
// @Router /admin/v1/users/{id}/role [put]
|
||||
func (h *Handler) UpdateUserRole(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -558,7 +537,7 @@ func (h *Handler) UpdateUserRole(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/company/{company_id} [get]
|
||||
// @Router /admin/v1/users/company/{company_id} [get]
|
||||
func (h *Handler) GetUsersByCompanyID(c echo.Context) error {
|
||||
companyIDStr := c.Param("company_id")
|
||||
|
||||
@@ -618,7 +597,7 @@ func (h *Handler) GetUsersByCompanyID(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /admin/v1/users/role/{role} [get]
|
||||
// @Router /admin/v1/users/role/{role} [get]
|
||||
func (h *Handler) GetUsersByRole(c echo.Context) error {
|
||||
roleStr := c.Param("role")
|
||||
role := UserRole(roleStr)
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
"tm/pkg/response"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// AuthMiddleware validates JWT access tokens and extracts user information
|
||||
@@ -44,7 +44,7 @@ func (h *Handler) AuthMiddleware() echo.MiddlewareFunc {
|
||||
}
|
||||
|
||||
// Extract user information from token
|
||||
userID, err := uuid.Parse(validationResult.Claims.UserID)
|
||||
userID, err := primitive.ObjectIDFromHex(validationResult.Claims.UserID)
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to parse user ID from token", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
@@ -53,7 +53,7 @@ func (h *Handler) AuthMiddleware() echo.MiddlewareFunc {
|
||||
}
|
||||
|
||||
// Store user information in context for handlers to use
|
||||
c.Set("user_id", userID.String())
|
||||
c.Set("user_id", userID.Hex())
|
||||
c.Set("user_email", validationResult.Claims.Email)
|
||||
c.Set("user_role", validationResult.Claims.Role)
|
||||
c.Set("company_id", validationResult.Claims.CompanyID)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"tm/pkg/authorization"
|
||||
"tm/pkg/logger"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -118,7 +118,7 @@ func (s *userService) CreateUser(ctx context.Context, form *CreateUserForm, crea
|
||||
"email": user.Email,
|
||||
"username": user.Username,
|
||||
"role": user.Role,
|
||||
"created_by": *createdBy,
|
||||
"created_by": createdBy,
|
||||
})
|
||||
|
||||
return user, nil
|
||||
@@ -160,7 +160,7 @@ func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse
|
||||
}
|
||||
|
||||
// Update last login
|
||||
err = s.repository.UpdateLastLogin(ctx, user.ID)
|
||||
err = s.repository.UpdateLastLogin(ctx, user.ID.Hex())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update last login", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
@@ -175,7 +175,7 @@ func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse
|
||||
}
|
||||
|
||||
tokenPair, err := s.authService.GenerateTokenPair(
|
||||
user.ID,
|
||||
user.ID.Hex(),
|
||||
user.Email,
|
||||
string(user.Role),
|
||||
companyID,
|
||||
@@ -230,7 +230,7 @@ func (s *userService) RefreshToken(ctx context.Context, refreshToken string) (*A
|
||||
}
|
||||
|
||||
// Get user information
|
||||
userID, err := uuid.Parse(validationResult.Claims.UserID)
|
||||
userID, err := primitive.ObjectIDFromHex(validationResult.Claims.UserID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to parse user ID from token", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
@@ -238,11 +238,11 @@ func (s *userService) RefreshToken(ctx context.Context, refreshToken string) (*A
|
||||
return nil, errors.New("invalid token format")
|
||||
}
|
||||
|
||||
user, err := s.repository.GetByID(ctx, userID.String())
|
||||
user, err := s.repository.GetByID(ctx, userID.Hex())
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get user for token refresh", map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
"user_id": userID.String(),
|
||||
"user_id": userID.Hex(),
|
||||
})
|
||||
return nil, errors.New("user not found")
|
||||
}
|
||||
@@ -420,7 +420,7 @@ func (s *userService) UpdateUser(ctx context.Context, id string, form *UpdateUse
|
||||
if form.Username != nil {
|
||||
// Check if username already exists
|
||||
existingUser, _ := s.repository.GetByUsername(ctx, *form.Username)
|
||||
if existingUser != nil && existingUser.ID != id {
|
||||
if existingUser != nil && existingUser.ID.Hex() != id {
|
||||
return nil, errors.New("username already exists")
|
||||
}
|
||||
user.Username = *form.Username
|
||||
@@ -429,7 +429,7 @@ func (s *userService) UpdateUser(ctx context.Context, id string, form *UpdateUse
|
||||
if form.Email != nil {
|
||||
// Check if email already exists
|
||||
existingUser, _ := s.repository.GetByEmail(ctx, *form.Email)
|
||||
if existingUser != nil && existingUser.ID != id {
|
||||
if existingUser != nil && existingUser.ID.Hex() != id {
|
||||
return nil, errors.New("email already exists")
|
||||
}
|
||||
user.Email = *form.Email
|
||||
|
||||
Reference in New Issue
Block a user