Refactor User Management API and Update Documentation
- Changed API tags from "Admin-Users" to "Admin-Authorization" for better clarity in user management endpoints. - Removed unused endpoints for retrieving users by company ID and role, streamlining the user management functionality. - Updated user entity and forms to reflect new example values for improved clarity in API documentation. - Enhanced pagination handling in user listing responses, ensuring consistent metadata structure. - Updated API documentation to reflect changes in endpoint structure and response formats, improving clarity for API consumers.
This commit is contained in:
+25
-315
@@ -3455,7 +3455,7 @@ const docTemplate = `{
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Get authenticated user profile",
|
"summary": "Get authenticated user profile",
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -3511,7 +3511,7 @@ const docTemplate = `{
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Update authenticated user profile",
|
"summary": "Update authenticated user profile",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -3592,7 +3592,7 @@ const docTemplate = `{
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Change user password",
|
"summary": "Change user password",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -4353,12 +4353,6 @@ const docTemplate = `{
|
|||||||
"name": "role",
|
"name": "role",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Company ID filter",
|
|
||||||
"name": "company_id",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "Limit results",
|
"description": "Limit results",
|
||||||
@@ -4515,178 +4509,6 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/v1/users/company/{company_id}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get users belonging to a specific company (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Get users by company ID",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Company ID",
|
|
||||||
"name": "company_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Limit results",
|
|
||||||
"name": "limit",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Offset results",
|
|
||||||
"name": "offset",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Users with pagination metadata",
|
|
||||||
"schema": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/role/{role}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get users with a specific role (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Get users by role",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "User role",
|
|
||||||
"name": "role",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Limit results",
|
|
||||||
"name": "limit",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Offset results",
|
|
||||||
"name": "offset",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Users with pagination metadata",
|
|
||||||
"schema": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/{id}": {
|
"/admin/v1/users/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -4917,82 +4739,6 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/v1/users/{id}/role": {
|
|
||||||
"put": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Update user role (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Update user role",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "User ID",
|
|
||||||
"name": "id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Role update data",
|
|
||||||
"name": "role",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/user.UpdateUserRoleForm"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Not Found",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/{id}/status": {
|
"/admin/v1/users/{id}/status": {
|
||||||
"put": {
|
"put": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -8301,12 +8047,12 @@ const docTemplate = `{
|
|||||||
"new_password": {
|
"new_password": {
|
||||||
"description": "New password (minimum 8 characters)",
|
"description": "New password (minimum 8 characters)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "NewPass456!"
|
"example": "NewAdmin!1234"
|
||||||
},
|
},
|
||||||
"old_password": {
|
"old_password": {
|
||||||
"description": "Current password for verification",
|
"description": "Current password for verification",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "OldPassword123!"
|
"example": "Admin!1234"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8321,42 +8067,42 @@ const docTemplate = `{
|
|||||||
"email": {
|
"email": {
|
||||||
"description": "Valid email address",
|
"description": "Valid email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@company.com"
|
"example": "admin@opplens.com"
|
||||||
},
|
},
|
||||||
"full_name": {
|
"full_name": {
|
||||||
"description": "Full name of the user",
|
"description": "Full name of the user",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John Smith"
|
"example": "Admin User"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "Password (minimum 8 characters)",
|
"description": "Password (minimum 8 characters)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "SecurePass123!"
|
"example": "Admin!1234"
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"description": "Optional phone number",
|
"description": "Optional phone number",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "+1234567890"
|
"example": "+18289784438"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"description": "Optional job position",
|
"description": "Optional job position",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Senior Developer"
|
"example": "Lead"
|
||||||
},
|
},
|
||||||
"profile_image": {
|
"profile_image": {
|
||||||
"description": "Optional profile image URL",
|
"description": "Optional profile image URL",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "https://example.com/avatar.jpg"
|
"example": ""
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"description": "User role (admin, manager, operator, viewer)",
|
"description": "User role (admin, manager, operator, viewer)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "manager"
|
"example": "admin"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Unique username (alphanumeric only)",
|
"description": "Unique username (alphanumeric only)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "johnsmith"
|
"example": "admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8366,12 +8112,12 @@ const docTemplate = `{
|
|||||||
"password": {
|
"password": {
|
||||||
"description": "User password",
|
"description": "User password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Nima.1998"
|
"example": "Admin!1234"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Username or email address",
|
"description": "Username or email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "nakhostin"
|
"example": "admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8391,70 +8137,52 @@ const docTemplate = `{
|
|||||||
"email": {
|
"email": {
|
||||||
"description": "Email address for password reset",
|
"description": "Email address for password reset",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@company.com"
|
"example": "admin@opplens.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user.UpdateUserForm": {
|
"user.UpdateUserForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"description": "Updated company ID",
|
|
||||||
"type": "string",
|
|
||||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
|
||||||
},
|
|
||||||
"department": {
|
"department": {
|
||||||
"description": "Updated department",
|
"description": "Updated department",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Product Management"
|
"example": "Information Technology"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"description": "Updated email address",
|
"description": "Updated email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@newcompany.com"
|
"example": "admin@opplens.com"
|
||||||
},
|
},
|
||||||
"full_name": {
|
"full_name": {
|
||||||
"description": "Updated full name",
|
"description": "Updated full name",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John Smith"
|
"example": "Admin User"
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"description": "Updated phone number",
|
"description": "Updated phone number",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "+1234567890"
|
"example": "+18289784438"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"description": "Updated position",
|
"description": "Updated position",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Tech Lead"
|
"example": "Lead"
|
||||||
},
|
},
|
||||||
"profile_image": {
|
"profile_image": {
|
||||||
"description": "Updated profile image URL",
|
"description": "Updated profile image URL",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "https://example.com/new-avatar.jpg"
|
"example": ""
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"description": "Updated user role",
|
"description": "Updated user role",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "admin"
|
"example": "admin"
|
||||||
},
|
},
|
||||||
"status": {
|
|
||||||
"description": "Updated account status",
|
|
||||||
"type": "string",
|
|
||||||
"example": "active"
|
|
||||||
},
|
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Updated username",
|
"description": "Updated username",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "johnsmith"
|
"example": "admin"
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"user.UpdateUserRoleForm": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"role": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8469,17 +8197,8 @@ const docTemplate = `{
|
|||||||
"user.UserListResponse": {
|
"user.UserListResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"limit": {
|
"meta": {
|
||||||
"type": "integer"
|
"$ref": "#/definitions/response.Meta"
|
||||||
},
|
|
||||||
"offset": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"total": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"total_pages": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -8492,15 +8211,9 @@ const docTemplate = `{
|
|||||||
"user.UserResponse": {
|
"user.UserResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"created_by": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"department": {
|
"department": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -8537,9 +8250,6 @@ const docTemplate = `{
|
|||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"updated_by": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-315
@@ -3449,7 +3449,7 @@
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Get authenticated user profile",
|
"summary": "Get authenticated user profile",
|
||||||
"responses": {
|
"responses": {
|
||||||
@@ -3505,7 +3505,7 @@
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Update authenticated user profile",
|
"summary": "Update authenticated user profile",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -3586,7 +3586,7 @@
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Admin-Users"
|
"Admin-Authorization"
|
||||||
],
|
],
|
||||||
"summary": "Change user password",
|
"summary": "Change user password",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@@ -4347,12 +4347,6 @@
|
|||||||
"name": "role",
|
"name": "role",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Company ID filter",
|
|
||||||
"name": "company_id",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "Limit results",
|
"description": "Limit results",
|
||||||
@@ -4509,178 +4503,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/v1/users/company/{company_id}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get users belonging to a specific company (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Get users by company ID",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "Company ID",
|
|
||||||
"name": "company_id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Limit results",
|
|
||||||
"name": "limit",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Offset results",
|
|
||||||
"name": "offset",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Users with pagination metadata",
|
|
||||||
"schema": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/role/{role}": {
|
|
||||||
"get": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Get users with a specific role (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Get users by role",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "User role",
|
|
||||||
"name": "role",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Limit results",
|
|
||||||
"name": "limit",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "integer",
|
|
||||||
"description": "Offset results",
|
|
||||||
"name": "offset",
|
|
||||||
"in": "query"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Users with pagination metadata",
|
|
||||||
"schema": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/{id}": {
|
"/admin/v1/users/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -4911,82 +4733,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/admin/v1/users/{id}/role": {
|
|
||||||
"put": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"BearerAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Update user role (admin only)",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Admin-Users"
|
|
||||||
],
|
|
||||||
"summary": "Update user role",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"description": "User ID",
|
|
||||||
"name": "id",
|
|
||||||
"in": "path",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Role update data",
|
|
||||||
"name": "role",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/user.UpdateUserRoleForm"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"400": {
|
|
||||||
"description": "Bad Request",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"401": {
|
|
||||||
"description": "Unauthorized",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"403": {
|
|
||||||
"description": "Forbidden",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"404": {
|
|
||||||
"description": "Not Found",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"500": {
|
|
||||||
"description": "Internal Server Error",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/response.APIResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/admin/v1/users/{id}/status": {
|
"/admin/v1/users/{id}/status": {
|
||||||
"put": {
|
"put": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -8295,12 +8041,12 @@
|
|||||||
"new_password": {
|
"new_password": {
|
||||||
"description": "New password (minimum 8 characters)",
|
"description": "New password (minimum 8 characters)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "NewPass456!"
|
"example": "NewAdmin!1234"
|
||||||
},
|
},
|
||||||
"old_password": {
|
"old_password": {
|
||||||
"description": "Current password for verification",
|
"description": "Current password for verification",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "OldPassword123!"
|
"example": "Admin!1234"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8315,42 +8061,42 @@
|
|||||||
"email": {
|
"email": {
|
||||||
"description": "Valid email address",
|
"description": "Valid email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@company.com"
|
"example": "admin@opplens.com"
|
||||||
},
|
},
|
||||||
"full_name": {
|
"full_name": {
|
||||||
"description": "Full name of the user",
|
"description": "Full name of the user",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John Smith"
|
"example": "Admin User"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "Password (minimum 8 characters)",
|
"description": "Password (minimum 8 characters)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "SecurePass123!"
|
"example": "Admin!1234"
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"description": "Optional phone number",
|
"description": "Optional phone number",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "+1234567890"
|
"example": "+18289784438"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"description": "Optional job position",
|
"description": "Optional job position",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Senior Developer"
|
"example": "Lead"
|
||||||
},
|
},
|
||||||
"profile_image": {
|
"profile_image": {
|
||||||
"description": "Optional profile image URL",
|
"description": "Optional profile image URL",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "https://example.com/avatar.jpg"
|
"example": ""
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"description": "User role (admin, manager, operator, viewer)",
|
"description": "User role (admin, manager, operator, viewer)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "manager"
|
"example": "admin"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Unique username (alphanumeric only)",
|
"description": "Unique username (alphanumeric only)",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "johnsmith"
|
"example": "admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8360,12 +8106,12 @@
|
|||||||
"password": {
|
"password": {
|
||||||
"description": "User password",
|
"description": "User password",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Nima.1998"
|
"example": "Admin!1234"
|
||||||
},
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Username or email address",
|
"description": "Username or email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "nakhostin"
|
"example": "admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8385,70 +8131,52 @@
|
|||||||
"email": {
|
"email": {
|
||||||
"description": "Email address for password reset",
|
"description": "Email address for password reset",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@company.com"
|
"example": "admin@opplens.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"user.UpdateUserForm": {
|
"user.UpdateUserForm": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"description": "Updated company ID",
|
|
||||||
"type": "string",
|
|
||||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
|
||||||
},
|
|
||||||
"department": {
|
"department": {
|
||||||
"description": "Updated department",
|
"description": "Updated department",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Product Management"
|
"example": "Information Technology"
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"description": "Updated email address",
|
"description": "Updated email address",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "john.smith@newcompany.com"
|
"example": "admin@opplens.com"
|
||||||
},
|
},
|
||||||
"full_name": {
|
"full_name": {
|
||||||
"description": "Updated full name",
|
"description": "Updated full name",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "John Smith"
|
"example": "Admin User"
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"description": "Updated phone number",
|
"description": "Updated phone number",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "+1234567890"
|
"example": "+18289784438"
|
||||||
},
|
},
|
||||||
"position": {
|
"position": {
|
||||||
"description": "Updated position",
|
"description": "Updated position",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "Tech Lead"
|
"example": "Lead"
|
||||||
},
|
},
|
||||||
"profile_image": {
|
"profile_image": {
|
||||||
"description": "Updated profile image URL",
|
"description": "Updated profile image URL",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "https://example.com/new-avatar.jpg"
|
"example": ""
|
||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"description": "Updated user role",
|
"description": "Updated user role",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "admin"
|
"example": "admin"
|
||||||
},
|
},
|
||||||
"status": {
|
|
||||||
"description": "Updated account status",
|
|
||||||
"type": "string",
|
|
||||||
"example": "active"
|
|
||||||
},
|
|
||||||
"username": {
|
"username": {
|
||||||
"description": "Updated username",
|
"description": "Updated username",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "johnsmith"
|
"example": "admin"
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"user.UpdateUserRoleForm": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"role": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -8463,17 +8191,8 @@
|
|||||||
"user.UserListResponse": {
|
"user.UserListResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"limit": {
|
"meta": {
|
||||||
"type": "integer"
|
"$ref": "#/definitions/response.Meta"
|
||||||
},
|
|
||||||
"offset": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"total": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"total_pages": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -8486,15 +8205,9 @@
|
|||||||
"user.UserResponse": {
|
"user.UserResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"company_id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"created_by": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"department": {
|
"department": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -8531,9 +8244,6 @@
|
|||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"updated_by": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-209
@@ -1121,11 +1121,11 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
new_password:
|
new_password:
|
||||||
description: New password (minimum 8 characters)
|
description: New password (minimum 8 characters)
|
||||||
example: NewPass456!
|
example: NewAdmin!1234
|
||||||
type: string
|
type: string
|
||||||
old_password:
|
old_password:
|
||||||
description: Current password for verification
|
description: Current password for verification
|
||||||
example: OldPassword123!
|
example: Admin!1234
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
user.CreateUserForm:
|
user.CreateUserForm:
|
||||||
@@ -1136,46 +1136,46 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
email:
|
email:
|
||||||
description: Valid email address
|
description: Valid email address
|
||||||
example: john.smith@company.com
|
example: admin@opplens.com
|
||||||
type: string
|
type: string
|
||||||
full_name:
|
full_name:
|
||||||
description: Full name of the user
|
description: Full name of the user
|
||||||
example: John Smith
|
example: Admin User
|
||||||
type: string
|
type: string
|
||||||
password:
|
password:
|
||||||
description: Password (minimum 8 characters)
|
description: Password (minimum 8 characters)
|
||||||
example: SecurePass123!
|
example: Admin!1234
|
||||||
type: string
|
type: string
|
||||||
phone:
|
phone:
|
||||||
description: Optional phone number
|
description: Optional phone number
|
||||||
example: "+1234567890"
|
example: "+18289784438"
|
||||||
type: string
|
type: string
|
||||||
position:
|
position:
|
||||||
description: Optional job position
|
description: Optional job position
|
||||||
example: Senior Developer
|
example: Lead
|
||||||
type: string
|
type: string
|
||||||
profile_image:
|
profile_image:
|
||||||
description: Optional profile image URL
|
description: Optional profile image URL
|
||||||
example: https://example.com/avatar.jpg
|
example: ""
|
||||||
type: string
|
type: string
|
||||||
role:
|
role:
|
||||||
description: User role (admin, manager, operator, viewer)
|
description: User role (admin, manager, operator, viewer)
|
||||||
example: manager
|
example: admin
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Unique username (alphanumeric only)
|
description: Unique username (alphanumeric only)
|
||||||
example: johnsmith
|
example: admin
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
user.LoginForm:
|
user.LoginForm:
|
||||||
properties:
|
properties:
|
||||||
password:
|
password:
|
||||||
description: User password
|
description: User password
|
||||||
example: Nima.1998
|
example: Admin!1234
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
description: Username or email address
|
description: Username or email address
|
||||||
example: nakhostin
|
example: admin
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
user.RefreshTokenForm:
|
user.RefreshTokenForm:
|
||||||
@@ -1189,55 +1189,42 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
email:
|
email:
|
||||||
description: Email address for password reset
|
description: Email address for password reset
|
||||||
example: john.smith@company.com
|
example: admin@opplens.com
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
user.UpdateUserForm:
|
user.UpdateUserForm:
|
||||||
properties:
|
properties:
|
||||||
company_id:
|
|
||||||
description: Updated company ID
|
|
||||||
example: 123e4567-e89b-12d3-a456-426614174000
|
|
||||||
type: string
|
|
||||||
department:
|
department:
|
||||||
description: Updated department
|
description: Updated department
|
||||||
example: Product Management
|
example: Information Technology
|
||||||
type: string
|
type: string
|
||||||
email:
|
email:
|
||||||
description: Updated email address
|
description: Updated email address
|
||||||
example: john.smith@newcompany.com
|
example: admin@opplens.com
|
||||||
type: string
|
type: string
|
||||||
full_name:
|
full_name:
|
||||||
description: Updated full name
|
description: Updated full name
|
||||||
example: John Smith
|
example: Admin User
|
||||||
type: string
|
type: string
|
||||||
phone:
|
phone:
|
||||||
description: Updated phone number
|
description: Updated phone number
|
||||||
example: "+1234567890"
|
example: "+18289784438"
|
||||||
type: string
|
type: string
|
||||||
position:
|
position:
|
||||||
description: Updated position
|
description: Updated position
|
||||||
example: Tech Lead
|
example: Lead
|
||||||
type: string
|
type: string
|
||||||
profile_image:
|
profile_image:
|
||||||
description: Updated profile image URL
|
description: Updated profile image URL
|
||||||
example: https://example.com/new-avatar.jpg
|
example: ""
|
||||||
type: string
|
type: string
|
||||||
role:
|
role:
|
||||||
description: Updated user role
|
description: Updated user role
|
||||||
example: admin
|
example: admin
|
||||||
type: string
|
type: string
|
||||||
status:
|
|
||||||
description: Updated account status
|
|
||||||
example: active
|
|
||||||
type: string
|
|
||||||
username:
|
username:
|
||||||
description: Updated username
|
description: Updated username
|
||||||
example: johnsmith
|
example: admin
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
user.UpdateUserRoleForm:
|
|
||||||
properties:
|
|
||||||
role:
|
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
user.UpdateUserStatusForm:
|
user.UpdateUserStatusForm:
|
||||||
@@ -1247,14 +1234,8 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
user.UserListResponse:
|
user.UserListResponse:
|
||||||
properties:
|
properties:
|
||||||
limit:
|
meta:
|
||||||
type: integer
|
$ref: '#/definitions/response.Meta'
|
||||||
offset:
|
|
||||||
type: integer
|
|
||||||
total:
|
|
||||||
type: integer
|
|
||||||
total_pages:
|
|
||||||
type: integer
|
|
||||||
users:
|
users:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/user.UserResponse'
|
$ref: '#/definitions/user.UserResponse'
|
||||||
@@ -1262,12 +1243,8 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
user.UserResponse:
|
user.UserResponse:
|
||||||
properties:
|
properties:
|
||||||
company_id:
|
|
||||||
type: string
|
|
||||||
created_at:
|
created_at:
|
||||||
type: integer
|
type: integer
|
||||||
created_by:
|
|
||||||
type: string
|
|
||||||
department:
|
department:
|
||||||
type: string
|
type: string
|
||||||
email:
|
email:
|
||||||
@@ -1292,8 +1269,6 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
updated_at:
|
updated_at:
|
||||||
type: integer
|
type: integer
|
||||||
updated_by:
|
|
||||||
type: string
|
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
@@ -3521,7 +3496,7 @@ paths:
|
|||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: Get authenticated user profile
|
summary: Get authenticated user profile
|
||||||
tags:
|
tags:
|
||||||
- Admin-Users
|
- Admin-Authorization
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
@@ -3572,7 +3547,7 @@ paths:
|
|||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: Update authenticated user profile
|
summary: Update authenticated user profile
|
||||||
tags:
|
tags:
|
||||||
- Admin-Users
|
- Admin-Authorization
|
||||||
/admin/v1/profile/change-password:
|
/admin/v1/profile/change-password:
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -3616,7 +3591,7 @@ paths:
|
|||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
summary: Change user password
|
summary: Change user password
|
||||||
tags:
|
tags:
|
||||||
- Admin-Users
|
- Admin-Authorization
|
||||||
/admin/v1/profile/login:
|
/admin/v1/profile/login:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -4070,10 +4045,6 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: role
|
name: role
|
||||||
type: string
|
type: string
|
||||||
- description: Company ID filter
|
|
||||||
in: query
|
|
||||||
name: company_id
|
|
||||||
type: string
|
|
||||||
- description: Limit results
|
- description: Limit results
|
||||||
in: query
|
in: query
|
||||||
name: limit
|
name: limit
|
||||||
@@ -4322,55 +4293,6 @@ paths:
|
|||||||
summary: Update user
|
summary: Update user
|
||||||
tags:
|
tags:
|
||||||
- Admin-Users
|
- Admin-Users
|
||||||
/admin/v1/users/{id}/role:
|
|
||||||
put:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Update user role (admin only)
|
|
||||||
parameters:
|
|
||||||
- description: User ID
|
|
||||||
in: path
|
|
||||||
name: id
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- description: Role update data
|
|
||||||
in: body
|
|
||||||
name: role
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/user.UpdateUserRoleForm'
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"400":
|
|
||||||
description: Bad Request
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"401":
|
|
||||||
description: Unauthorized
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"403":
|
|
||||||
description: Forbidden
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"404":
|
|
||||||
description: Not Found
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
summary: Update user role
|
|
||||||
tags:
|
|
||||||
- Admin-Users
|
|
||||||
/admin/v1/users/{id}/status:
|
/admin/v1/users/{id}/status:
|
||||||
put:
|
put:
|
||||||
consumes:
|
consumes:
|
||||||
@@ -4420,112 +4342,6 @@ paths:
|
|||||||
summary: Update user status
|
summary: Update user status
|
||||||
tags:
|
tags:
|
||||||
- Admin-Users
|
- Admin-Users
|
||||||
/admin/v1/users/company/{company_id}:
|
|
||||||
get:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Get users belonging to a specific company (admin only)
|
|
||||||
parameters:
|
|
||||||
- description: Company ID
|
|
||||||
in: path
|
|
||||||
name: company_id
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- description: Limit results
|
|
||||||
in: query
|
|
||||||
name: limit
|
|
||||||
type: integer
|
|
||||||
- description: Offset results
|
|
||||||
in: query
|
|
||||||
name: offset
|
|
||||||
type: integer
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Users with pagination metadata
|
|
||||||
schema:
|
|
||||||
allOf:
|
|
||||||
- $ref: '#/definitions/response.APIResponse'
|
|
||||||
- properties:
|
|
||||||
data:
|
|
||||||
additionalProperties: true
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
"400":
|
|
||||||
description: Bad Request
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"401":
|
|
||||||
description: Unauthorized
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"403":
|
|
||||||
description: Forbidden
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
summary: Get users by company ID
|
|
||||||
tags:
|
|
||||||
- Admin-Users
|
|
||||||
/admin/v1/users/role/{role}:
|
|
||||||
get:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: Get users with a specific role (admin only)
|
|
||||||
parameters:
|
|
||||||
- description: User role
|
|
||||||
in: path
|
|
||||||
name: role
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
- description: Limit results
|
|
||||||
in: query
|
|
||||||
name: limit
|
|
||||||
type: integer
|
|
||||||
- description: Offset results
|
|
||||||
in: query
|
|
||||||
name: offset
|
|
||||||
type: integer
|
|
||||||
produces:
|
|
||||||
- application/json
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Users with pagination metadata
|
|
||||||
schema:
|
|
||||||
allOf:
|
|
||||||
- $ref: '#/definitions/response.APIResponse'
|
|
||||||
- properties:
|
|
||||||
data:
|
|
||||||
additionalProperties: true
|
|
||||||
type: object
|
|
||||||
type: object
|
|
||||||
"400":
|
|
||||||
description: Bad Request
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"401":
|
|
||||||
description: Unauthorized
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"403":
|
|
||||||
description: Forbidden
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
"500":
|
|
||||||
description: Internal Server Error
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/response.APIResponse'
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
summary: Get users by role
|
|
||||||
tags:
|
|
||||||
- Admin-Users
|
|
||||||
/api/v1/companies:
|
/api/v1/companies:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler
|
|||||||
adminUsersGP.PUT("/:id", userHandler.UpdateUser)
|
adminUsersGP.PUT("/:id", userHandler.UpdateUser)
|
||||||
adminUsersGP.DELETE("/:id", userHandler.DeleteUser)
|
adminUsersGP.DELETE("/:id", userHandler.DeleteUser)
|
||||||
adminUsersGP.PUT("/:id/status", userHandler.UpdateUserStatus)
|
adminUsersGP.PUT("/:id/status", userHandler.UpdateUserStatus)
|
||||||
adminUsersGP.PUT("/:id/role", userHandler.UpdateUserRole)
|
|
||||||
adminUsersGP.GET("/company/:company_id", userHandler.GetUsersByCompanyID)
|
|
||||||
adminUsersGP.GET("/role/:role", userHandler.GetUsersByRole)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin user profile
|
// Admin user profile
|
||||||
|
|||||||
+16
-18
@@ -28,20 +28,18 @@ const (
|
|||||||
// User represents a system user (admin/manager/operator)
|
// User represents a system user (admin/manager/operator)
|
||||||
type User struct {
|
type User struct {
|
||||||
mongo.Model `bson:",inline"`
|
mongo.Model `bson:",inline"`
|
||||||
FullName string `bson:"full_name" json:"full_name"`
|
FullName string `bson:"full_name"`
|
||||||
Username string `bson:"username" json:"username"`
|
Username string `bson:"username"`
|
||||||
Email string `bson:"email" json:"email"`
|
Email string `bson:"email"`
|
||||||
Password string `bson:"password" json:"-"` // Never serialize password
|
Password string `bson:"password" json:"-"`
|
||||||
Role UserRole `bson:"role" json:"role"`
|
Role UserRole `bson:"role"`
|
||||||
Status UserStatus `bson:"status" json:"status"`
|
Status UserStatus `bson:"status"`
|
||||||
Department *string `bson:"department,omitempty" json:"department,omitempty"`
|
Department *string `bson:"department"`
|
||||||
Position *string `bson:"position,omitempty" json:"position,omitempty"`
|
Position *string `bson:"position"`
|
||||||
Phone *string `bson:"phone,omitempty" json:"phone,omitempty"`
|
Phone *string `bson:"phone"`
|
||||||
ProfileImage *string `bson:"profile_image,omitempty" json:"profile_image,omitempty"`
|
ProfileImage *string `bson:"profile_image"`
|
||||||
IsVerified bool `bson:"is_verified" json:"is_verified"`
|
IsVerified bool `bson:"is_verified"`
|
||||||
LastLoginAt *int64 `bson:"last_login_at,omitempty" json:"last_login_at,omitempty"` // Unix timestamp
|
LastLoginAt *int64 `bson:"last_login_at"`
|
||||||
CreatedBy *string `bson:"created_by,omitempty" json:"created_by,omitempty"`
|
|
||||||
UpdatedBy *string `bson:"updated_by,omitempty" json:"updated_by,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetID sets the user ID (implements IDSetter interface)
|
// SetID sets the user ID (implements IDSetter interface)
|
||||||
@@ -54,22 +52,22 @@ func (u *User) GetID() string {
|
|||||||
return u.ID.Hex()
|
return u.ID.Hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCreatedAt sets the created timestamp (implements Timestampable interface)
|
// SetCreatedAt sets the created timestamp (implements Timestamp interface)
|
||||||
func (u *User) SetCreatedAt(timestamp int64) {
|
func (u *User) SetCreatedAt(timestamp int64) {
|
||||||
u.CreatedAt = timestamp
|
u.CreatedAt = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUpdatedAt sets the updated timestamp (implements Timestampable interface)
|
// SetUpdatedAt sets the updated timestamp (implements Timestamp interface)
|
||||||
func (u *User) SetUpdatedAt(timestamp int64) {
|
func (u *User) SetUpdatedAt(timestamp int64) {
|
||||||
u.UpdatedAt = timestamp
|
u.UpdatedAt = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCreatedAt returns the created timestamp (implements Timestampable interface)
|
// GetCreatedAt returns the created timestamp (implements Timestamp interface)
|
||||||
func (u *User) GetCreatedAt() int64 {
|
func (u *User) GetCreatedAt() int64 {
|
||||||
return u.CreatedAt
|
return u.CreatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUpdatedAt returns the updated timestamp (implements Timestampable interface)
|
// GetUpdatedAt returns the updated timestamp (implements Timestamp interface)
|
||||||
func (u *User) GetUpdatedAt() int64 {
|
func (u *User) GetUpdatedAt() int64 {
|
||||||
return u.UpdatedAt
|
return u.UpdatedAt
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-47
@@ -1,40 +1,40 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
|
import "tm/pkg/response"
|
||||||
|
|
||||||
// User Registration DTOs
|
// User Registration DTOs
|
||||||
|
|
||||||
// CreateUserForm represents the data required to create a new user account
|
// CreateUserForm represents the data required to create a new user account
|
||||||
type CreateUserForm struct {
|
type CreateUserForm struct {
|
||||||
FullName string `json:"full_name" valid:"required,length(2|100)" example:"John Smith"` // Full name of the user
|
FullName string `json:"full_name" valid:"required,length(2|100)" example:"Admin User"` // Full name of the user
|
||||||
Username string `json:"username" valid:"required,alphanum,length(3|30)" example:"johnsmith"` // Unique username (alphanumeric only)
|
Username string `json:"username" valid:"required,alphanum,length(3|30)" example:"admin"` // Unique username (alphanumeric only)
|
||||||
Email string `json:"email" valid:"required,email" example:"john.smith@company.com"` // Valid email address
|
Email string `json:"email" valid:"required,email" example:"admin@opplens.com"` // Valid email address
|
||||||
Password string `json:"password" valid:"required,length(8|128)" example:"SecurePass123!"` // Password (minimum 8 characters)
|
Password string `json:"password" valid:"required,length(8|128)" example:"Admin!1234"` // Password (minimum 8 characters)
|
||||||
Role string `json:"role" valid:"required,in(admin|manager|operator|viewer)" example:"manager"` // User role (admin, manager, operator, viewer)
|
Role string `json:"role" valid:"required,in(admin|manager|operator|viewer)" example:"admin"` // User role (admin, manager, operator, viewer)
|
||||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Information Technology"` // Optional department name
|
Department *string `json:"department" 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
|
Position *string `json:"position" valid:"optional,length(2|100)" example:"Lead"` // Optional job position
|
||||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Optional phone number
|
Phone *string `json:"phone" valid:"optional,length(10|20)" example:"+18289784438"` // Optional phone number
|
||||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:"https://example.com/avatar.jpg"` // Optional profile image URL
|
ProfileImage *string `json:"profile_image" valid:"optional,url" example:""` // Optional profile image URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserForm represents the data for updating an existing user account
|
// UpdateUserForm represents the data for updating an existing user account
|
||||||
type UpdateUserForm struct {
|
type UpdateUserForm struct {
|
||||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)" example:"John Smith"` // Updated full name
|
FullName *string `json:"full_name" valid:"optional,length(2|100)" example:"Admin User"` // Updated full name
|
||||||
Username *string `json:"username,omitempty" valid:"optional,alphanum,length(3|30)" example:"johnsmith"` // Updated username
|
Username *string `json:"username" valid:"optional,alphanum,length(3|30)" example:"admin"` // Updated username
|
||||||
Email *string `json:"email,omitempty" valid:"optional,email" example:"john.smith@newcompany.com"` // Updated email address
|
Email *string `json:"email" valid:"optional,email" example:"admin@opplens.com"` // Updated email address
|
||||||
Role *string `json:"role,omitempty" valid:"optional,in(admin|manager|operator|viewer)" example:"admin"` // Updated user role
|
Role *string `json:"role" 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" valid:"optional,length(2|100)" example:"Information Technology"` // Updated department
|
||||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Product Management"` // Updated department
|
Position *string `json:"position" valid:"optional,length(2|100)" example:"Lead"` // Updated position
|
||||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Tech Lead"` // Updated position
|
Phone *string `json:"phone" valid:"optional,length(10|20)" example:"+18289784438"` // Updated phone number
|
||||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Updated phone number
|
ProfileImage *string `json:"profile_image" valid:"optional,url" example:""` // Updated profile image URL
|
||||||
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
|
// User Authentication DTOs
|
||||||
|
|
||||||
// LoginForm represents the credentials required for user authentication
|
// LoginForm represents the credentials required for user authentication
|
||||||
type LoginForm struct {
|
type LoginForm struct {
|
||||||
Username string `json:"username" valid:"required" example:"nakhostin"` // Username or email address
|
Username string `json:"username" valid:"required" example:"admin"` // Username or email address
|
||||||
Password string `json:"password" valid:"required" example:"Nima.1998"` // User password
|
Password string `json:"password" valid:"required" example:"Admin!1234"` // User password
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshTokenForm represents the refresh token required to generate new access tokens
|
// RefreshTokenForm represents the refresh token required to generate new access tokens
|
||||||
@@ -44,22 +44,22 @@ type RefreshTokenForm struct {
|
|||||||
|
|
||||||
// ChangePasswordForm represents the data required to change user password
|
// ChangePasswordForm represents the data required to change user password
|
||||||
type ChangePasswordForm struct {
|
type ChangePasswordForm struct {
|
||||||
OldPassword string `json:"old_password" valid:"required" example:"OldPassword123!"` // Current password for verification
|
OldPassword string `json:"old_password" valid:"required" example:"Admin!1234"` // Current password for verification
|
||||||
NewPassword string `json:"new_password" valid:"required,length(8|128)" example:"NewPass456!"` // New password (minimum 8 characters)
|
NewPassword string `json:"new_password" valid:"required,length(8|128)" example:"NewAdmin!1234"` // New password (minimum 8 characters)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProfileForm represents the data for updating user profile information
|
// UpdateProfileForm represents the data for updating user profile information
|
||||||
type UpdateProfileForm struct {
|
type UpdateProfileForm struct {
|
||||||
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)" example:"John Smith"` // Updated full name
|
FullName *string `json:"full_name,omitempty" valid:"optional,length(2|100)" example:"Admin User"` // Updated full name
|
||||||
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Engineering"` // Updated department
|
Department *string `json:"department,omitempty" valid:"optional,length(2|100)" example:"Information Technology"` // Updated department
|
||||||
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Senior Engineer"` // Updated position
|
Position *string `json:"position,omitempty" valid:"optional,length(2|100)" example:"Lead"` // Updated position
|
||||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+1234567890"` // Updated phone number
|
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)" example:"+18289784438"` // Updated phone number
|
||||||
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:"https://example.com/profile.jpg"` // Updated profile image URL
|
ProfileImage *string `json:"profile_image,omitempty" valid:"optional,url" example:""` // Updated profile image URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetPasswordForm represents the email address for password reset
|
// ResetPasswordForm represents the email address for password reset
|
||||||
type ResetPasswordForm struct {
|
type ResetPasswordForm struct {
|
||||||
Email string `json:"email" valid:"required,email" example:"john.smith@company.com"` // Email address for password reset
|
Email string `json:"email" valid:"required,email" example:"admin@opplens.com"` // Email address for password reset
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin DTOs
|
// Admin DTOs
|
||||||
@@ -67,7 +67,7 @@ type ListUsersForm struct {
|
|||||||
Search *string `query:"search" valid:"optional"`
|
Search *string `query:"search" valid:"optional"`
|
||||||
Status *string `query:"status" valid:"optional,in(active|inactive|suspended)"`
|
Status *string `query:"status" valid:"optional,in(active|inactive|suspended)"`
|
||||||
Role *string `query:"role" valid:"optional,in(admin|manager|operator|viewer)"`
|
Role *string `query:"role" valid:"optional,in(admin|manager|operator|viewer)"`
|
||||||
CompanyID *string `query:"company_id" valid:"optional,uuid"`
|
CompanyID *string `query:"company_id" valid:"optional"`
|
||||||
Limit *int `query:"limit" valid:"optional,range(1|100)"`
|
Limit *int `query:"limit" valid:"optional,range(1|100)"`
|
||||||
Offset *int `query:"offset" valid:"optional,min(0)"`
|
Offset *int `query:"offset" valid:"optional,min(0)"`
|
||||||
SortBy *string `query:"sort_by" valid:"optional,in(full_name|email|username|role|created_at|last_login_at)"`
|
SortBy *string `query:"sort_by" valid:"optional,in(full_name|email|username|role|created_at|last_login_at)"`
|
||||||
@@ -90,17 +90,14 @@ type UserResponse struct {
|
|||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
CompanyID *string `json:"company_id,omitempty"`
|
Department *string `json:"department"`
|
||||||
Department *string `json:"department,omitempty"`
|
Position *string `json:"position"`
|
||||||
Position *string `json:"position,omitempty"`
|
Phone *string `json:"phone"`
|
||||||
Phone *string `json:"phone,omitempty"`
|
ProfileImage *string `json:"profile_image"`
|
||||||
ProfileImage *string `json:"profile_image,omitempty"`
|
|
||||||
IsVerified bool `json:"is_verified"`
|
IsVerified bool `json:"is_verified"`
|
||||||
LastLoginAt *int64 `json:"last_login_at,omitempty"`
|
LastLoginAt *int64 `json:"last_login_at"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
CreatedBy *string `json:"created_by,omitempty"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UpdatedBy *string `json:"updated_by,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
@@ -111,11 +108,14 @@ type AuthResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserListResponse struct {
|
type UserListResponse struct {
|
||||||
Users []*UserResponse `json:"users"`
|
Users []*UserResponse `json:"users"`
|
||||||
Total int64 `json:"total"`
|
Meta *response.Meta `json:"meta"`
|
||||||
Limit int `json:"limit"`
|
}
|
||||||
Offset int `json:"offset"`
|
|
||||||
TotalPages int `json:"total_pages"`
|
type SearchUsersForm struct {
|
||||||
|
Search *string `query:"search" valid:"optional"`
|
||||||
|
Status *string `query:"status" valid:"optional,in(active|inactive|suspended)"`
|
||||||
|
Role *string `query:"role" valid:"optional,in(admin|manager|operator|viewer)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to convert User to UserResponse
|
// Helper function to convert User to UserResponse
|
||||||
@@ -133,9 +133,7 @@ func (u *User) ToResponse() *UserResponse {
|
|||||||
ProfileImage: u.ProfileImage,
|
ProfileImage: u.ProfileImage,
|
||||||
IsVerified: u.IsVerified,
|
IsVerified: u.IsVerified,
|
||||||
LastLoginAt: u.LastLoginAt,
|
LastLoginAt: u.LastLoginAt,
|
||||||
CreatedAt: u.CreatedAt,
|
|
||||||
UpdatedAt: u.UpdatedAt,
|
UpdatedAt: u.UpdatedAt,
|
||||||
CreatedBy: u.CreatedBy,
|
CreatedAt: u.CreatedAt,
|
||||||
UpdatedBy: u.UpdatedBy,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-217
@@ -1,7 +1,6 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"tm/pkg/authorization"
|
"tm/pkg/authorization"
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
"tm/pkg/response"
|
"tm/pkg/response"
|
||||||
@@ -117,7 +116,7 @@ func (h *Handler) ResetPassword(c echo.Context) error {
|
|||||||
// GetProfile gets current user profile
|
// GetProfile gets current user profile
|
||||||
// @Summary Get authenticated user profile
|
// @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.
|
// @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 Admin-Users
|
// @Tags Admin-Authorization
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
@@ -133,7 +132,7 @@ func (h *Handler) GetProfile(c echo.Context) error {
|
|||||||
return response.Unauthorized(c, "User not authenticated")
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := h.service.GetProfile(c.Request().Context(), userID)
|
user, err := h.service.GetUserByID(c.Request().Context(), userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.NotFound(c, "User not found")
|
return response.NotFound(c, "User not found")
|
||||||
}
|
}
|
||||||
@@ -144,7 +143,7 @@ func (h *Handler) GetProfile(c echo.Context) error {
|
|||||||
// UpdateProfile updates current user profile
|
// UpdateProfile updates current user profile
|
||||||
// @Summary Update authenticated user profile
|
// @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.
|
// @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 Admin-Users
|
// @Tags Admin-Authorization
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
@@ -168,7 +167,7 @@ func (h *Handler) UpdateProfile(c echo.Context) error {
|
|||||||
return response.BadRequest(c, "Invalid request format", "")
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := h.service.UpdateUser(c.Request().Context(), userID, form, &userID)
|
user, err := h.service.Update(c.Request().Context(), userID, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, err.Error(), "")
|
return response.BadRequest(c, err.Error(), "")
|
||||||
}
|
}
|
||||||
@@ -179,7 +178,7 @@ func (h *Handler) UpdateProfile(c echo.Context) error {
|
|||||||
// ChangePassword changes current user password
|
// ChangePassword changes current user password
|
||||||
// @Summary Change 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.
|
// @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 Admin-Users
|
// @Tags Admin-Authorization
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
@@ -261,14 +260,8 @@ func (h *Handler) Logout(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Router /admin/v1/users [post]
|
// @Router /admin/v1/users [post]
|
||||||
func (h *Handler) CreateUser(c echo.Context) error {
|
func (h *Handler) CreateUser(c echo.Context) error {
|
||||||
// Get current user ID from JWT token
|
form, err := response.Parse[CreateUserForm](c)
|
||||||
currentUserID, err := GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
var form CreateUserForm
|
|
||||||
if err := c.Bind(&form); err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid request format", "")
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +271,7 @@ func (h *Handler) CreateUser(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
user, err := h.service.CreateUser(c.Request().Context(), &form, ¤tUserID)
|
user, err := h.service.Register(c.Request().Context(), form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, err.Error(), "")
|
return response.BadRequest(c, err.Error(), "")
|
||||||
}
|
}
|
||||||
@@ -296,7 +289,6 @@ func (h *Handler) CreateUser(c echo.Context) error {
|
|||||||
// @Param search query string false "Search term"
|
// @Param search query string false "Search term"
|
||||||
// @Param status query string false "User status filter"
|
// @Param status query string false "User status filter"
|
||||||
// @Param role query string false "User role filter"
|
// @Param role query string false "User role filter"
|
||||||
// @Param company_id query string false "Company ID filter"
|
|
||||||
// @Param limit query int false "Limit results"
|
// @Param limit query int false "Limit results"
|
||||||
// @Param offset query int false "Offset results"
|
// @Param offset query int false "Offset results"
|
||||||
// @Param sort_by query string false "Sort field"
|
// @Param sort_by query string false "Sort field"
|
||||||
@@ -308,8 +300,8 @@ func (h *Handler) CreateUser(c echo.Context) error {
|
|||||||
// @Failure 500 {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 {
|
func (h *Handler) ListUsers(c echo.Context) error {
|
||||||
var form ListUsersForm
|
form, err := response.Parse[SearchUsersForm](c)
|
||||||
if err := c.Bind(&form); err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid request format", "")
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +311,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
users, err := h.service.ListUsers(c.Request().Context(), &form)
|
users, err := h.service.Search(c.Request().Context(), form, response.NewPagination(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.InternalServerError(c, "Failed to list users")
|
return response.InternalServerError(c, "Failed to list users")
|
||||||
}
|
}
|
||||||
@@ -370,16 +362,10 @@ func (h *Handler) GetUserByID(c echo.Context) error {
|
|||||||
// @Failure 500 {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 {
|
func (h *Handler) UpdateUser(c echo.Context) error {
|
||||||
// Extract user ID from JWT token context
|
|
||||||
currentUserID, err := GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
|
|
||||||
var form UpdateUserForm
|
form, err := response.Parse[UpdateUserForm](c)
|
||||||
if err := c.Bind(&form); err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid request format", "")
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +375,7 @@ func (h *Handler) UpdateUser(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
user, err := h.service.UpdateUser(c.Request().Context(), idStr, &form, ¤tUserID)
|
user, err := h.service.Update(c.Request().Context(), idStr, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, err.Error(), "")
|
return response.BadRequest(c, err.Error(), "")
|
||||||
}
|
}
|
||||||
@@ -413,15 +399,9 @@ func (h *Handler) UpdateUser(c echo.Context) error {
|
|||||||
// @Failure 500 {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 {
|
func (h *Handler) DeleteUser(c echo.Context) error {
|
||||||
// Extract user ID from JWT token context
|
id := c.Param("id")
|
||||||
currentUserID, err := GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
idStr := c.Param("id")
|
err := h.service.Delete(c.Request().Context(), id)
|
||||||
|
|
||||||
err = h.service.DeleteUser(c.Request().Context(), idStr, ¤tUserID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, err.Error(), "")
|
return response.BadRequest(c, err.Error(), "")
|
||||||
}
|
}
|
||||||
@@ -448,15 +428,10 @@ func (h *Handler) DeleteUser(c echo.Context) error {
|
|||||||
// @Failure 500 {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 {
|
func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
||||||
currentUserID, err := GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
|
|
||||||
var form UpdateUserStatusForm
|
form, err := response.Parse[UpdateUserStatusForm](c)
|
||||||
if err := c.Bind(&form); err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, "Invalid request format", "")
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +441,7 @@ func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
err = h.service.UpdateUserStatus(c.Request().Context(), idStr, &form, ¤tUserID)
|
err = h.service.UpdateStatus(c.Request().Context(), idStr, form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.BadRequest(c, err.Error(), "")
|
return response.BadRequest(c, err.Error(), "")
|
||||||
}
|
}
|
||||||
@@ -475,177 +450,3 @@ func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
|||||||
"message": "User status updated successfully",
|
"message": "User status updated successfully",
|
||||||
}, "User status updated successfully")
|
}, "User status updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserRole updates user role (admin only)
|
|
||||||
// @Summary Update user role
|
|
||||||
// @Description Update user role (admin only)
|
|
||||||
// @Tags Admin-Users
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Param id path string true "User ID"
|
|
||||||
// @Param role body UpdateUserRoleForm true "Role update data"
|
|
||||||
// @Success 200 {object} response.APIResponse
|
|
||||||
// @Failure 400 {object} response.APIResponse
|
|
||||||
// @Failure 401 {object} response.APIResponse
|
|
||||||
// @Failure 403 {object} response.APIResponse
|
|
||||||
// @Failure 404 {object} response.APIResponse
|
|
||||||
// @Failure 500 {object} response.APIResponse
|
|
||||||
// @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)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
idStr := c.Param("id")
|
|
||||||
|
|
||||||
var form UpdateUserRoleForm
|
|
||||||
if err := c.Bind(&form); err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid request format", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate form
|
|
||||||
if _, err := govalidator.ValidateStruct(form); err != nil {
|
|
||||||
return response.ValidationError(c, err.Error(), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call service
|
|
||||||
err = h.service.UpdateUserRole(c.Request().Context(), idStr, &form, ¤tUserID)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, err.Error(), "")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "User role updated successfully",
|
|
||||||
}, "User role updated successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsersByCompanyID gets users by company ID (admin only)
|
|
||||||
// @Summary Get users by company ID
|
|
||||||
// @Description Get users belonging to a specific company (admin only)
|
|
||||||
// @Tags Admin-Users
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Param company_id path string true "Company ID"
|
|
||||||
// @Param limit query int false "Limit results"
|
|
||||||
// @Param offset query int false "Offset results"
|
|
||||||
// @Success 200 {object} response.APIResponse{data=map[string]interface{}} "Users with pagination metadata"
|
|
||||||
// @Failure 400 {object} response.APIResponse
|
|
||||||
// @Failure 401 {object} response.APIResponse
|
|
||||||
// @Failure 403 {object} response.APIResponse
|
|
||||||
// @Failure 500 {object} response.APIResponse
|
|
||||||
// @Router /admin/v1/users/company/{company_id} [get]
|
|
||||||
func (h *Handler) GetUsersByCompanyID(c echo.Context) error {
|
|
||||||
companyIDStr := c.Param("company_id")
|
|
||||||
|
|
||||||
// Get query parameters
|
|
||||||
limitStr := c.QueryParam("limit")
|
|
||||||
if limitStr == "" {
|
|
||||||
limitStr = "20"
|
|
||||||
}
|
|
||||||
offsetStr := c.QueryParam("offset")
|
|
||||||
if offsetStr == "" {
|
|
||||||
offsetStr = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
limit, err := strconv.Atoi(limitStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid limit parameter", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
offset, err := strconv.Atoi(offsetStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid offset parameter", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call service
|
|
||||||
users, total, err := h.service.GetUsersByCompanyID(c.Request().Context(), companyIDStr, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to get users by company")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to responses
|
|
||||||
var userResponses []*UserResponse
|
|
||||||
for _, user := range users {
|
|
||||||
userResponses = append(userResponses, user.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.SuccessWithMeta(c, map[string]interface{}{
|
|
||||||
"users": userResponses,
|
|
||||||
}, &response.Meta{
|
|
||||||
Total: int(total),
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
}, "Users retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsersByRole gets users by role (admin only)
|
|
||||||
// @Summary Get users by role
|
|
||||||
// @Description Get users with a specific role (admin only)
|
|
||||||
// @Tags Admin-Users
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Param role path string true "User role"
|
|
||||||
// @Param limit query int false "Limit results"
|
|
||||||
// @Param offset query int false "Offset results"
|
|
||||||
// @Success 200 {object} response.APIResponse{data=map[string]interface{}} "Users with pagination metadata"
|
|
||||||
// @Failure 400 {object} response.APIResponse
|
|
||||||
// @Failure 401 {object} response.APIResponse
|
|
||||||
// @Failure 403 {object} response.APIResponse
|
|
||||||
// @Failure 500 {object} response.APIResponse
|
|
||||||
// @Router /admin/v1/users/role/{role} [get]
|
|
||||||
func (h *Handler) GetUsersByRole(c echo.Context) error {
|
|
||||||
roleStr := c.Param("role")
|
|
||||||
role := UserRole(roleStr)
|
|
||||||
|
|
||||||
// Validate role
|
|
||||||
if !h.validator.IsValidRole(role) {
|
|
||||||
return response.BadRequest(c, "Invalid role", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get query parameters
|
|
||||||
limitStr := c.QueryParam("limit")
|
|
||||||
if limitStr == "" {
|
|
||||||
limitStr = "20"
|
|
||||||
}
|
|
||||||
offsetStr := c.QueryParam("offset")
|
|
||||||
if offsetStr == "" {
|
|
||||||
offsetStr = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
limit, err := strconv.Atoi(limitStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid limit parameter", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
offset, err := strconv.Atoi(offsetStr)
|
|
||||||
if err != nil {
|
|
||||||
return response.BadRequest(c, "Invalid offset parameter", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call service
|
|
||||||
users, err := h.service.GetUsersByRole(c.Request().Context(), role, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to get users by role")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to responses
|
|
||||||
var userResponses []*UserResponse
|
|
||||||
for _, user := range users {
|
|
||||||
userResponses = append(userResponses, user.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.SuccessWithMeta(c, map[string]interface{}{
|
|
||||||
"users": userResponses,
|
|
||||||
"role": role,
|
|
||||||
}, &response.Meta{
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
}, "Users retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthMiddleware and AdminMiddleware are now implemented in middleware.go
|
|
||||||
|
|||||||
+189
-326
@@ -5,44 +5,35 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
mongopkg "tm/pkg/mongo"
|
orm "tm/pkg/mongo"
|
||||||
|
"tm/pkg/response"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repository defines methods for user data access
|
// Repository defines methods for user data access
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
|
Login(ctx context.Context, id string) error
|
||||||
Create(ctx context.Context, user *User) error
|
Create(ctx context.Context, user *User) error
|
||||||
|
Update(ctx context.Context, user *User) error
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
GetByID(ctx context.Context, id string) (*User, error)
|
GetByID(ctx context.Context, id string) (*User, error)
|
||||||
GetByEmail(ctx context.Context, email string) (*User, error)
|
GetByEmail(ctx context.Context, email string) (*User, error)
|
||||||
GetByUsername(ctx context.Context, username string) (*User, error)
|
GetByUsername(ctx context.Context, username string) (*User, error)
|
||||||
Update(ctx context.Context, user *User) error
|
Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) ([]User, int64, error)
|
||||||
Delete(ctx context.Context, id string) error
|
|
||||||
List(ctx context.Context, limit, offset int) ([]*User, error)
|
|
||||||
Search(ctx context.Context, search string, status *string, role *string, companyID *string, limit, offset int, sortBy, sortOrder string) ([]*User, error)
|
|
||||||
GetByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*User, error)
|
|
||||||
GetByRole(ctx context.Context, role UserRole, limit, offset int) ([]*User, error)
|
|
||||||
UpdateLastLogin(ctx context.Context, id string) error
|
|
||||||
CountByCompanyID(ctx context.Context, companyID string) (int64, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// userRepository implements the Repository interface using the MongoDB ORM
|
// userRepository implements the Repository interface using the MongoDB ORM
|
||||||
type userRepository struct {
|
type userRepository struct {
|
||||||
ormRepo mongopkg.Repository[User]
|
ormRepo orm.Repository[User]
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserRepository creates a new user repository
|
// NewUserRepository creates a new user repository
|
||||||
func NewUserRepository(mongoManager *mongopkg.ConnectionManager, logger logger.Logger) Repository {
|
func NewUserRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
||||||
// Create indexes using the ORM's index management
|
// Create indexes using the ORM's index management
|
||||||
indexes := []mongopkg.Index{
|
indexes := []orm.Index{
|
||||||
*mongopkg.CreateUniqueIndex("email_idx", bson.D{{Key: "email", Value: 1}}),
|
*orm.CreateTextIndex("search_idx", "full_name", "email", "username"),
|
||||||
*mongopkg.CreateUniqueIndex("username_idx", bson.D{{Key: "username", Value: 1}}),
|
|
||||||
*mongopkg.NewIndex("company_id_idx", bson.D{{Key: "company_id", Value: 1}}),
|
|
||||||
*mongopkg.NewIndex("role_idx", bson.D{{Key: "role", Value: 1}}),
|
|
||||||
*mongopkg.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
|
|
||||||
*mongopkg.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
|
|
||||||
*mongopkg.CreateTextIndex("search_idx", "full_name", "email", "username", "department", "position"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create indexes
|
// Create indexes
|
||||||
@@ -54,7 +45,7 @@ func NewUserRepository(mongoManager *mongopkg.ConnectionManager, logger logger.L
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create ORM repository
|
// Create ORM repository
|
||||||
ormRepo := mongopkg.NewRepository[User](mongoManager.GetCollection("users"), logger)
|
ormRepo := orm.NewRepository[User](mongoManager.GetCollection("users"), logger)
|
||||||
|
|
||||||
return &userRepository{
|
return &userRepository{
|
||||||
ormRepo: ormRepo,
|
ormRepo: ormRepo,
|
||||||
@@ -62,300 +53,8 @@ func NewUserRepository(mongoManager *mongopkg.ConnectionManager, logger logger.L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new user
|
// Login updates the last login timestamp
|
||||||
func (r *userRepository) Create(ctx context.Context, user *User) error {
|
func (r *userRepository) Login(ctx context.Context, id string) error {
|
||||||
// Set created/updated timestamps using Unix timestamps
|
|
||||||
now := time.Now().Unix()
|
|
||||||
user.SetCreatedAt(now)
|
|
||||||
user.SetUpdatedAt(now)
|
|
||||||
|
|
||||||
// Use ORM to create user
|
|
||||||
err := r.ormRepo.Create(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to create user", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"email": user.Email,
|
|
||||||
"user_id": user.ID,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("User created successfully", map[string]interface{}{
|
|
||||||
"user_id": user.ID,
|
|
||||||
"email": user.Email,
|
|
||||||
"role": user.Role,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID retrieves a user by ID
|
|
||||||
func (r *userRepository) GetByID(ctx context.Context, id string) (*User, error) {
|
|
||||||
user, err := r.ormRepo.FindByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get user by ID", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": id,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByEmail retrieves a user by email
|
|
||||||
func (r *userRepository) GetByEmail(ctx context.Context, email string) (*User, error) {
|
|
||||||
filter := bson.M{"email": email}
|
|
||||||
user, err := r.ormRepo.FindOne(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get user by email", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"email": email,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByUsername retrieves a user by username
|
|
||||||
func (r *userRepository) GetByUsername(ctx context.Context, username string) (*User, error) {
|
|
||||||
filter := bson.M{"username": username}
|
|
||||||
user, err := r.ormRepo.FindOne(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, mongopkg.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get user by username", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"username": username,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update updates a user
|
|
||||||
func (r *userRepository) Update(ctx context.Context, user *User) error {
|
|
||||||
// Set updated timestamp using Unix timestamp
|
|
||||||
user.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Use ORM to update user
|
|
||||||
err := r.ormRepo.Update(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to update user", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": user.ID,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("User updated successfully", map[string]interface{}{
|
|
||||||
"user_id": user.ID,
|
|
||||||
"email": user.Email,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes a user (soft delete by setting status to inactive)
|
|
||||||
func (r *userRepository) Delete(ctx context.Context, id string) error {
|
|
||||||
// Get user first
|
|
||||||
user, err := r.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status to inactive
|
|
||||||
user.Status = UserStatusInactive
|
|
||||||
user.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = r.ormRepo.Update(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to delete user", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": id,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("User deleted successfully", map[string]interface{}{
|
|
||||||
"user_id": id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List retrieves users with pagination
|
|
||||||
func (r *userRepository) List(ctx context.Context, limit, offset int) ([]*User, error) {
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongopkg.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset).
|
|
||||||
SortDesc("created_at").
|
|
||||||
Build()
|
|
||||||
|
|
||||||
// Only active users by default
|
|
||||||
filter := bson.M{"status": bson.M{"$ne": UserStatusInactive}}
|
|
||||||
|
|
||||||
// Use ORM to find users
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to list users", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"limit": limit,
|
|
||||||
"offset": offset,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []User to []*User
|
|
||||||
users := make([]*User, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
users[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search retrieves users with search and filters
|
|
||||||
func (r *userRepository) Search(ctx context.Context, search string, status *string, role *string, companyID *string, limit, offset int, sortBy, sortOrder string) ([]*User, error) {
|
|
||||||
// Build filter
|
|
||||||
filter := bson.M{}
|
|
||||||
|
|
||||||
if search != "" {
|
|
||||||
filter["$text"] = bson.M{"$search": search}
|
|
||||||
}
|
|
||||||
|
|
||||||
if status != nil {
|
|
||||||
filter["status"] = *status
|
|
||||||
}
|
|
||||||
|
|
||||||
if role != nil {
|
|
||||||
filter["role"] = *role
|
|
||||||
}
|
|
||||||
|
|
||||||
if companyID != nil {
|
|
||||||
filter["company_id"] = *companyID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongopkg.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset)
|
|
||||||
|
|
||||||
// Set sort
|
|
||||||
if sortBy != "" {
|
|
||||||
if sortOrder == "desc" {
|
|
||||||
pagination.SortDesc(sortBy)
|
|
||||||
} else {
|
|
||||||
pagination.SortAsc(sortBy)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pagination.SortDesc("created_at") // Default sort
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use ORM to find users
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination.Build())
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to search users", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"search": search,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []User to []*User
|
|
||||||
users := make([]*User, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
users[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByCompanyID retrieves users by company ID with pagination
|
|
||||||
func (r *userRepository) GetByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*User, error) {
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongopkg.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset).
|
|
||||||
SortDesc("created_at").
|
|
||||||
Build()
|
|
||||||
|
|
||||||
// Filter by company ID and active status
|
|
||||||
filter := bson.M{
|
|
||||||
"company_id": companyID,
|
|
||||||
"status": bson.M{"$ne": UserStatusInactive},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use ORM to find users
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to get users by company ID", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": companyID,
|
|
||||||
"limit": limit,
|
|
||||||
"offset": offset,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []User to []*User
|
|
||||||
users := make([]*User, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
users[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByRole retrieves users by role with pagination
|
|
||||||
func (r *userRepository) GetByRole(ctx context.Context, role UserRole, limit, offset int) ([]*User, error) {
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongopkg.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset).
|
|
||||||
SortDesc("created_at").
|
|
||||||
Build()
|
|
||||||
|
|
||||||
// Filter by role and active status
|
|
||||||
filter := bson.M{
|
|
||||||
"role": role,
|
|
||||||
"status": bson.M{"$ne": UserStatusInactive},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use ORM to find users
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to get users by role", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"role": role,
|
|
||||||
"limit": limit,
|
|
||||||
"offset": offset,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []User to []*User
|
|
||||||
users := make([]*User, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
users[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLastLogin updates the last login timestamp
|
|
||||||
func (r *userRepository) UpdateLastLogin(ctx context.Context, id string) error {
|
|
||||||
// Get user first
|
// Get user first
|
||||||
user, err := r.GetByID(ctx, id)
|
user, err := r.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -383,21 +82,185 @@ func (r *userRepository) UpdateLastLogin(ctx context.Context, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CountByCompanyID counts users by company ID
|
// Create creates a new user
|
||||||
func (r *userRepository) CountByCompanyID(ctx context.Context, companyID string) (int64, error) {
|
func (r *userRepository) Create(ctx context.Context, user *User) error {
|
||||||
filter := bson.M{
|
// Set created timestamp using Unix timestamp
|
||||||
"company_id": companyID,
|
now := time.Now().Unix()
|
||||||
"status": bson.M{"$ne": UserStatusInactive},
|
user.SetCreatedAt(now)
|
||||||
}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
// Use ORM to create user
|
||||||
|
err := r.ormRepo.Create(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to count users by company ID", map[string]interface{}{
|
r.logger.Error("Failed to create user", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"company_id": companyID,
|
"email": user.Email,
|
||||||
|
"user_id": user.ID,
|
||||||
})
|
})
|
||||||
return 0, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return count, nil
|
r.logger.Info("User created successfully", map[string]interface{}{
|
||||||
|
"user_id": user.ID,
|
||||||
|
"email": user.Email,
|
||||||
|
"role": user.Role,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates a user
|
||||||
|
func (r *userRepository) Update(ctx context.Context, user *User) error {
|
||||||
|
// Set updated timestamp using Unix timestamp
|
||||||
|
user.SetUpdatedAt(time.Now().Unix())
|
||||||
|
|
||||||
|
// Use ORM to update user
|
||||||
|
err := r.ormRepo.Update(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to update user", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": user.ID,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.Info("User updated successfully", map[string]interface{}{
|
||||||
|
"user_id": user.ID,
|
||||||
|
"email": user.Email,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByID retrieves a user by ID
|
||||||
|
func (r *userRepository) GetByID(ctx context.Context, id string) (*User, error) {
|
||||||
|
user, err := r.ormRepo.FindByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
r.logger.Error("Failed to get user by ID", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByEmail retrieves a user by email
|
||||||
|
func (r *userRepository) GetByEmail(ctx context.Context, email string) (*User, error) {
|
||||||
|
user, err := r.ormRepo.FindOne(ctx, bson.M{"email": email})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
r.logger.Error("Failed to get user by email", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"email": email,
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByUsername retrieves a user by username
|
||||||
|
func (r *userRepository) GetByUsername(ctx context.Context, username string) (*User, error) {
|
||||||
|
user, err := r.ormRepo.FindOne(ctx, bson.M{"username": username})
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
r.logger.Error("Failed to get user by username", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"username": username,
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a user (soft delete by setting status to inactive)
|
||||||
|
func (r *userRepository) Delete(ctx context.Context, id string) error {
|
||||||
|
// Get user first
|
||||||
|
_, err := r.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to delete user", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete in database
|
||||||
|
err = r.ormRepo.Delete(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to delete user", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.Info("User deleted successfully", map[string]interface{}{
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List retrieves users with pagination
|
||||||
|
func (r *userRepository) Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) ([]User, int64, error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if form.Search != nil {
|
||||||
|
filter["$or"] = bson.A{
|
||||||
|
bson.M{"full_name": bson.M{"$regex": form.Search, "$options": "i"}},
|
||||||
|
bson.M{"email": bson.M{"$regex": form.Search, "$options": "i"}},
|
||||||
|
bson.M{"username": bson.M{"$regex": form.Search, "$options": "i"}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Status != nil {
|
||||||
|
filter["status"] = form.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Role != nil {
|
||||||
|
filter["role"] = form.Role
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build sort
|
||||||
|
sort := "created_at"
|
||||||
|
if pagination.SortBy != "" {
|
||||||
|
sort = pagination.SortBy
|
||||||
|
}
|
||||||
|
|
||||||
|
sortOrder := "desc"
|
||||||
|
if pagination.SortOrder != "" {
|
||||||
|
sortOrder = pagination.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build pagination
|
||||||
|
paginationBuilder := orm.NewPaginationBuilder().
|
||||||
|
Limit(pagination.Limit).
|
||||||
|
Skip(pagination.Offset).
|
||||||
|
SortBy(sort, sortOrder).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
// Use ORM to find users
|
||||||
|
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to list users", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Items, result.TotalCount, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+177
-280
@@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"tm/pkg/authorization"
|
"tm/pkg/authorization"
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
|
"tm/pkg/response"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@@ -14,21 +15,20 @@ import (
|
|||||||
|
|
||||||
// Service defines business logic for user operations
|
// Service defines business logic for user operations
|
||||||
type Service interface {
|
type Service interface {
|
||||||
CreateUser(ctx context.Context, form *CreateUserForm, createdBy *string) (*User, error)
|
// Main CRUD
|
||||||
|
Register(ctx context.Context, form *CreateUserForm) (*User, error)
|
||||||
|
Update(ctx context.Context, id string, form *UpdateUserForm) (*User, error)
|
||||||
|
UpdateStatus(ctx context.Context, id string, form *UpdateUserStatusForm) error
|
||||||
|
GetUserByID(ctx context.Context, userID string) (*User, error)
|
||||||
|
Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) (*UserListResponse, error)
|
||||||
|
Delete(ctx context.Context, id string) error
|
||||||
|
|
||||||
|
// Profile
|
||||||
Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
|
Login(ctx context.Context, form *LoginForm) (*AuthResponse, error)
|
||||||
RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
|
|
||||||
GetProfile(ctx context.Context, userID string) (*User, error)
|
|
||||||
UpdateProfile(ctx context.Context, userID string, form *UpdateProfileForm) error
|
UpdateProfile(ctx context.Context, userID string, form *UpdateProfileForm) error
|
||||||
|
RefreshToken(ctx context.Context, refreshToken string) (*AuthResponse, error)
|
||||||
ChangePassword(ctx context.Context, userID string, form *ChangePasswordForm) error
|
ChangePassword(ctx context.Context, userID string, form *ChangePasswordForm) error
|
||||||
ResetPassword(ctx context.Context, form *ResetPasswordForm) error
|
ResetPassword(ctx context.Context, form *ResetPasswordForm) error
|
||||||
GetUserByID(ctx context.Context, id string) (*User, error)
|
|
||||||
UpdateUser(ctx context.Context, id string, form *UpdateUserForm, updatedBy *string) (*User, error)
|
|
||||||
DeleteUser(ctx context.Context, id string, deletedBy *string) error
|
|
||||||
ListUsers(ctx context.Context, form *ListUsersForm) (*UserListResponse, error)
|
|
||||||
UpdateUserStatus(ctx context.Context, id string, form *UpdateUserStatusForm, updatedBy *string) error
|
|
||||||
UpdateUserRole(ctx context.Context, id string, form *UpdateUserRoleForm, updatedBy *string) error
|
|
||||||
GetUsersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*User, int64, error)
|
|
||||||
GetUsersByRole(ctx context.Context, role UserRole, limit, offset int) ([]*User, error)
|
|
||||||
Logout(ctx context.Context, userID string, accessToken string) error
|
Logout(ctx context.Context, userID string, accessToken string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@ func NewUserService(repository Repository, logger logger.Logger, authService aut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates a new user
|
// Register creates a new user
|
||||||
func (s *userService) CreateUser(ctx context.Context, form *CreateUserForm, createdBy *string) (*User, error) {
|
func (s *userService) Register(ctx context.Context, form *CreateUserForm) (*User, error) {
|
||||||
// Check if email already exists
|
// Check if email already exists
|
||||||
existingUser, _ := s.repository.GetByEmail(ctx, form.Email)
|
existingUser, _ := s.repository.GetByEmail(ctx, form.Email)
|
||||||
if existingUser != nil {
|
if existingUser != nil {
|
||||||
@@ -91,14 +91,13 @@ func (s *userService) CreateUser(ctx context.Context, form *CreateUserForm, crea
|
|||||||
Position: form.Position,
|
Position: form.Position,
|
||||||
Phone: form.Phone,
|
Phone: form.Phone,
|
||||||
ProfileImage: form.ProfileImage,
|
ProfileImage: form.ProfileImage,
|
||||||
IsVerified: false,
|
IsVerified: true,
|
||||||
CreatedBy: createdBy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save to database
|
// Save to database
|
||||||
err = s.repository.Create(ctx, user)
|
err = s.repository.Create(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to create user", map[string]interface{}{
|
s.logger.Error("Failed to register user", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"email": form.Email,
|
"email": form.Email,
|
||||||
"username": form.Username,
|
"username": form.Username,
|
||||||
@@ -106,17 +105,169 @@ func (s *userService) CreateUser(ctx context.Context, form *CreateUserForm, crea
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("User created successfully", map[string]interface{}{
|
s.logger.Info("User registered successfully", map[string]interface{}{
|
||||||
"user_id": user.ID,
|
"user_id": user.ID,
|
||||||
"email": user.Email,
|
"email": user.Email,
|
||||||
"username": user.Username,
|
"username": user.Username,
|
||||||
"role": user.Role,
|
"role": user.Role,
|
||||||
"created_by": createdBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update updates user information
|
||||||
|
func (s *userService) Update(ctx context.Context, id string, form *UpdateUserForm) (*User, error) {
|
||||||
|
// Get user
|
||||||
|
user, err := s.repository.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fields if provided
|
||||||
|
if form.FullName != nil {
|
||||||
|
user.FullName = *form.FullName
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Username != nil {
|
||||||
|
// Check if username already exists
|
||||||
|
existingUser, _ := s.repository.GetByUsername(ctx, *form.Username)
|
||||||
|
if existingUser != nil && existingUser.ID.Hex() != id {
|
||||||
|
return nil, errors.New("username already exists")
|
||||||
|
}
|
||||||
|
user.Username = *form.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Email != nil {
|
||||||
|
// Check if email already exists
|
||||||
|
existingUser, _ := s.repository.GetByEmail(ctx, *form.Email)
|
||||||
|
if existingUser != nil && existingUser.ID.Hex() != id {
|
||||||
|
return nil, errors.New("email already exists")
|
||||||
|
}
|
||||||
|
user.Email = *form.Email
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Role != nil {
|
||||||
|
role := UserRole(*form.Role)
|
||||||
|
if !s.validator.IsValidRole(role) {
|
||||||
|
return nil, errors.New("invalid role")
|
||||||
|
}
|
||||||
|
user.Role = role
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Department != nil {
|
||||||
|
user.Department = form.Department
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Position != nil {
|
||||||
|
user.Position = form.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Phone != nil {
|
||||||
|
user.Phone = form.Phone
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.ProfileImage != nil {
|
||||||
|
user.ProfileImage = form.ProfileImage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = s.repository.Update(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update user", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
return nil, errors.New("failed to update user")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("User updated successfully", map[string]interface{}{
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateStatus updates user status
|
||||||
|
func (s *userService) UpdateStatus(ctx context.Context, id string, form *UpdateUserStatusForm) error {
|
||||||
|
// Get user
|
||||||
|
user, err := s.repository.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
user.Status = UserStatus(form.Status)
|
||||||
|
|
||||||
|
// Update in database
|
||||||
|
err = s.repository.Update(ctx, user)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to update user status", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
"status": form.Status,
|
||||||
|
})
|
||||||
|
return errors.New("failed to update user status")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("User status updated successfully", map[string]interface{}{
|
||||||
|
"user_id": id,
|
||||||
|
"status": form.Status,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserByID retrieves a user by ID
|
||||||
|
func (s *userService) GetUserByID(ctx context.Context, id string) (*User, error) {
|
||||||
|
user, err := s.repository.GetByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search searches users with search and filters
|
||||||
|
func (s *userService) Search(ctx context.Context, form *SearchUsersForm, pagination *response.Pagination) (*UserListResponse, error) {
|
||||||
|
// Get users
|
||||||
|
users, total, err := s.repository.Search(ctx, form, pagination)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to list users", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, errors.New("failed to list users")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to responses
|
||||||
|
var userResponses []*UserResponse
|
||||||
|
for _, user := range users {
|
||||||
|
userResponses = append(userResponses, user.ToResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UserListResponse{
|
||||||
|
Users: userResponses,
|
||||||
|
Meta: pagination.Response(total),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes a user (hard delete)
|
||||||
|
func (s *userService) Delete(ctx context.Context, id string) error {
|
||||||
|
err := s.repository.Delete(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to delete user", map[string]interface{}{
|
||||||
|
"error": err.Error(),
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
return errors.New("failed to delete user")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("User deleted successfully", map[string]interface{}{
|
||||||
|
"user_id": id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Login authenticates a user
|
// Login authenticates a user
|
||||||
func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse, error) {
|
func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse, error) {
|
||||||
// Find user by email or username
|
// Find user by email or username
|
||||||
@@ -153,7 +304,7 @@ func (s *userService) Login(ctx context.Context, form *LoginForm) (*AuthResponse
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update last login
|
// Update last login
|
||||||
err = s.repository.UpdateLastLogin(ctx, user.ID.Hex())
|
err = s.repository.Login(ctx, user.ID.Hex())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update last login", map[string]interface{}{
|
s.logger.Error("Failed to update last login", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -315,16 +466,6 @@ func (s *userService) ResetPassword(ctx context.Context, form *ResetPasswordForm
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByID retrieves a user by ID
|
|
||||||
func (s *userService) GetUserByID(ctx context.Context, id string) (*User, error) {
|
|
||||||
user, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetProfile retrieves the current user's profile
|
// GetProfile retrieves the current user's profile
|
||||||
func (s *userService) GetProfile(ctx context.Context, userID string) (*User, error) {
|
func (s *userService) GetProfile(ctx context.Context, userID string) (*User, error) {
|
||||||
user, err := s.repository.GetByID(ctx, userID)
|
user, err := s.repository.GetByID(ctx, userID)
|
||||||
@@ -391,212 +532,8 @@ func (s *userService) UpdateProfile(ctx context.Context, userID string, form *Up
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser updates user information
|
|
||||||
func (s *userService) UpdateUser(ctx context.Context, id string, form *UpdateUserForm, updatedBy *string) (*User, error) {
|
|
||||||
// Get user
|
|
||||||
user, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update fields if provided
|
|
||||||
if form.FullName != nil {
|
|
||||||
user.FullName = *form.FullName
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Username != nil {
|
|
||||||
// Check if username already exists
|
|
||||||
existingUser, _ := s.repository.GetByUsername(ctx, *form.Username)
|
|
||||||
if existingUser != nil && existingUser.ID.Hex() != id {
|
|
||||||
return nil, errors.New("username already exists")
|
|
||||||
}
|
|
||||||
user.Username = *form.Username
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Email != nil {
|
|
||||||
// Check if email already exists
|
|
||||||
existingUser, _ := s.repository.GetByEmail(ctx, *form.Email)
|
|
||||||
if existingUser != nil && existingUser.ID.Hex() != id {
|
|
||||||
return nil, errors.New("email already exists")
|
|
||||||
}
|
|
||||||
user.Email = *form.Email
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Role != nil {
|
|
||||||
role := UserRole(*form.Role)
|
|
||||||
if !s.validator.IsValidRole(role) {
|
|
||||||
return nil, errors.New("invalid role")
|
|
||||||
}
|
|
||||||
user.Role = role
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Department != nil {
|
|
||||||
user.Department = form.Department
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Position != nil {
|
|
||||||
user.Position = form.Position
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Phone != nil {
|
|
||||||
user.Phone = form.Phone
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.ProfileImage != nil {
|
|
||||||
user.ProfileImage = form.ProfileImage
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Status != nil {
|
|
||||||
user.Status = UserStatus(*form.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updated by
|
|
||||||
user.UpdatedBy = updatedBy
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = s.repository.Update(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update user", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": id,
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to update user")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("User updated successfully", map[string]interface{}{
|
|
||||||
"user_id": id,
|
|
||||||
"updated_by": *updatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteUser deletes a user (soft delete)
|
|
||||||
func (s *userService) DeleteUser(ctx context.Context, id string, deletedBy *string) error {
|
|
||||||
// Get user to check if exists
|
|
||||||
user, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updated by before deletion
|
|
||||||
user.UpdatedBy = deletedBy
|
|
||||||
|
|
||||||
// Update user status to inactive (soft delete)
|
|
||||||
user.Status = UserStatusInactive
|
|
||||||
err = s.repository.Update(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to delete user", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to delete user")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("User deleted successfully", map[string]interface{}{
|
|
||||||
"user_id": id,
|
|
||||||
"deleted_by": *deletedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUsers lists users with search and filters
|
|
||||||
func (s *userService) ListUsers(ctx context.Context, form *ListUsersForm) (*UserListResponse, error) {
|
|
||||||
// Set defaults
|
|
||||||
limit := 20
|
|
||||||
if form.Limit != nil {
|
|
||||||
limit = *form.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if form.Offset != nil {
|
|
||||||
offset = *form.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
search := ""
|
|
||||||
if form.Search != nil {
|
|
||||||
search = *form.Search
|
|
||||||
}
|
|
||||||
|
|
||||||
sortBy := "created_at"
|
|
||||||
if form.SortBy != nil {
|
|
||||||
sortBy = *form.SortBy
|
|
||||||
}
|
|
||||||
|
|
||||||
sortOrder := "desc"
|
|
||||||
if form.SortOrder != nil {
|
|
||||||
sortOrder = *form.SortOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse company ID if provided
|
|
||||||
var companyID *string
|
|
||||||
if form.CompanyID != nil {
|
|
||||||
companyID = form.CompanyID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users
|
|
||||||
users, err := s.repository.Search(ctx, search, form.Status, form.Role, companyID, limit, offset, sortBy, sortOrder)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to list users", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to list users")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to responses
|
|
||||||
var userResponses []*UserResponse
|
|
||||||
for _, user := range users {
|
|
||||||
userResponses = append(userResponses, user.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement proper count for pagination metadata
|
|
||||||
total := int64(len(users)) // This should be a separate count query
|
|
||||||
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
||||||
|
|
||||||
return &UserListResponse{
|
|
||||||
Users: userResponses,
|
|
||||||
Total: total,
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
TotalPages: int(totalPages),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserStatus updates user status
|
|
||||||
func (s *userService) UpdateUserStatus(ctx context.Context, id string, form *UpdateUserStatusForm, updatedBy *string) error {
|
|
||||||
// Get user
|
|
||||||
user, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status
|
|
||||||
user.Status = UserStatus(form.Status)
|
|
||||||
user.UpdatedBy = updatedBy
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = s.repository.Update(ctx, user)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update user status", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"user_id": id,
|
|
||||||
"status": form.Status,
|
|
||||||
})
|
|
||||||
return errors.New("failed to update user status")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("User status updated successfully", map[string]interface{}{
|
|
||||||
"user_id": id,
|
|
||||||
"status": form.Status,
|
|
||||||
"updated_by": *updatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserRole updates user role
|
// UpdateUserRole updates user role
|
||||||
func (s *userService) UpdateUserRole(ctx context.Context, id string, form *UpdateUserRoleForm, updatedBy *string) error {
|
func (s *userService) UpdateRole(ctx context.Context, id string, form *UpdateUserRoleForm) error {
|
||||||
// Get user
|
// Get user
|
||||||
user, err := s.repository.GetByID(ctx, id)
|
user, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -611,7 +548,6 @@ func (s *userService) UpdateUserRole(ctx context.Context, id string, form *Updat
|
|||||||
|
|
||||||
// Update role
|
// Update role
|
||||||
user.Role = role
|
user.Role = role
|
||||||
user.UpdatedBy = updatedBy
|
|
||||||
|
|
||||||
// Update in database
|
// Update in database
|
||||||
err = s.repository.Update(ctx, user)
|
err = s.repository.Update(ctx, user)
|
||||||
@@ -625,52 +561,13 @@ func (s *userService) UpdateUserRole(ctx context.Context, id string, form *Updat
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Info("User role updated successfully", map[string]interface{}{
|
s.logger.Info("User role updated successfully", map[string]interface{}{
|
||||||
"user_id": id,
|
"user_id": id,
|
||||||
"role": form.Role,
|
"role": form.Role,
|
||||||
"updated_by": *updatedBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsersByCompanyID retrieves users by company ID with pagination
|
|
||||||
func (s *userService) GetUsersByCompanyID(ctx context.Context, companyID string, limit, offset int) ([]*User, int64, error) {
|
|
||||||
users, err := s.repository.GetByCompanyID(ctx, companyID, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get users by company ID", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": companyID,
|
|
||||||
})
|
|
||||||
return nil, 0, errors.New("failed to get users by company")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get total count
|
|
||||||
total, err := s.repository.CountByCompanyID(ctx, companyID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to count users by company ID", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": companyID,
|
|
||||||
})
|
|
||||||
return users, 0, nil // Return users even if count fails
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUsersByRole retrieves users by role with pagination
|
|
||||||
func (s *userService) GetUsersByRole(ctx context.Context, role UserRole, limit, offset int) ([]*User, error) {
|
|
||||||
users, err := s.repository.GetByRole(ctx, role, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get users by role", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"role": role,
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to get users by role")
|
|
||||||
}
|
|
||||||
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logout logs out a user and invalidates tokens
|
// Logout logs out a user and invalidates tokens
|
||||||
func (s *userService) Logout(ctx context.Context, userID string, accessToken string) error {
|
func (s *userService) Logout(ctx context.Context, userID string, accessToken string) error {
|
||||||
// Invalidate the access token
|
// Invalidate the access token
|
||||||
|
|||||||
+8
-4
@@ -279,20 +279,24 @@ func (pb *PaginationBuilder) Cursor(cursor string) *PaginationBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SortBy sets the sort field and order
|
// SortBy sets the sort field and order
|
||||||
func (pb *PaginationBuilder) SortBy(field string, order int) *PaginationBuilder {
|
func (pb *PaginationBuilder) SortBy(field string, order string) *PaginationBuilder {
|
||||||
pb.pagination.SortField = field
|
pb.pagination.SortField = field
|
||||||
pb.pagination.SortOrder = order
|
if order == "asc" {
|
||||||
|
pb.pagination.SortOrder = 1
|
||||||
|
} else {
|
||||||
|
pb.pagination.SortOrder = -1
|
||||||
|
}
|
||||||
return pb
|
return pb
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortAsc sorts in ascending order
|
// SortAsc sorts in ascending order
|
||||||
func (pb *PaginationBuilder) SortAsc(field string) *PaginationBuilder {
|
func (pb *PaginationBuilder) SortAsc(field string) *PaginationBuilder {
|
||||||
return pb.SortBy(field, 1)
|
return pb.SortBy(field, "asc")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortDesc sorts in descending order
|
// SortDesc sorts in descending order
|
||||||
func (pb *PaginationBuilder) SortDesc(field string) *PaginationBuilder {
|
func (pb *PaginationBuilder) SortDesc(field string) *PaginationBuilder {
|
||||||
return pb.SortBy(field, -1)
|
return pb.SortBy(field, "desc")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build returns the final pagination options
|
// Build returns the final pagination options
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package response
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@@ -32,6 +33,13 @@ type Meta struct {
|
|||||||
Pages int `json:"pages"`
|
Pages int `json:"pages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Pagination struct {
|
||||||
|
Limit int `query:"limit" valid:"optional,range(1|100)" default:"20"`
|
||||||
|
Offset int `query:"offset" valid:"optional,min(0)" default:"0"`
|
||||||
|
SortBy string `query:"sort_by" valid:"optional"`
|
||||||
|
SortOrder string `query:"sort_order" valid:"optional,in(asc|desc)"`
|
||||||
|
}
|
||||||
|
|
||||||
// Success returns a successful response
|
// Success returns a successful response
|
||||||
func Success(c echo.Context, data interface{}, message string) error {
|
func Success(c echo.Context, data interface{}, message string) error {
|
||||||
return c.JSON(http.StatusOK, APIResponse{
|
return c.JSON(http.StatusOK, APIResponse{
|
||||||
@@ -171,3 +179,34 @@ func Parse[T any](c echo.Context) (*T, error) {
|
|||||||
|
|
||||||
return &form, nil
|
return &form, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPagination(c echo.Context) *Pagination {
|
||||||
|
limit, err := strconv.Atoi(c.QueryParam("limit"))
|
||||||
|
if err != nil {
|
||||||
|
limit = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
offset, err := strconv.Atoi(c.QueryParam("offset"))
|
||||||
|
if err != nil {
|
||||||
|
offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Pagination{
|
||||||
|
Limit: limit,
|
||||||
|
Offset: offset,
|
||||||
|
SortBy: c.QueryParam("sort_by"),
|
||||||
|
SortOrder: c.QueryParam("sort_order"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination calculates pagination metadata
|
||||||
|
func (p *Pagination) Response(total int64) *Meta {
|
||||||
|
pages := (total + int64(p.Limit) - 1) / int64(p.Limit)
|
||||||
|
return &Meta{
|
||||||
|
Total: int(total),
|
||||||
|
Limit: p.Limit,
|
||||||
|
Offset: p.Offset,
|
||||||
|
Page: int(p.Offset/p.Limit) + 1,
|
||||||
|
Pages: int(pages),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user