Implement customer management domain with authorization and API enhancements
- Introduced a new customer management domain, including entities, services, handlers, and forms for customer operations. - Added JWT-based user authorization settings in the configuration file for both user and customer management. - Updated API endpoints to reflect the new structure, including changes to health check and user management routes. - Enhanced Swagger documentation to include new customer-related endpoints and authorization details. - Refactored the Makefile to include a target for generating API documentation. - Removed obsolete documentation files to streamline the project structure.
This commit is contained in:
@@ -1,214 +0,0 @@
|
||||
# User Management Service
|
||||
|
||||
This service provides comprehensive user management functionality for the Tender Management system, following Clean Architecture principles and Domain-Driven Design patterns.
|
||||
|
||||
## Features
|
||||
|
||||
### User Authentication
|
||||
- **Login**: Authenticate users with email/username and password
|
||||
- **Refresh Token**: Refresh access tokens
|
||||
- **Logout**: Securely log out users
|
||||
- **Password Management**: Change password and reset password functionality
|
||||
|
||||
### User Management
|
||||
- **Create User**: Create new users with roles and permissions
|
||||
- **Update User**: Update user information and profile
|
||||
- **Delete User**: Soft delete users (set status to inactive)
|
||||
- **Get User**: Retrieve user information by ID
|
||||
- **List Users**: Search and filter users with pagination
|
||||
|
||||
### Role-Based Access Control
|
||||
- **User Roles**: admin, manager, operator, viewer
|
||||
- **Role Management**: Update user roles
|
||||
- **Status Management**: Activate, deactivate, or suspend users
|
||||
|
||||
### Company Management
|
||||
- **Company Users**: Get users by company ID
|
||||
- **User Count**: Count users per company
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Public Endpoints
|
||||
|
||||
#### Authentication
|
||||
```
|
||||
POST /api/v1/users/login
|
||||
POST /api/v1/users/refresh-token
|
||||
POST /api/v1/users/reset-password
|
||||
```
|
||||
|
||||
### Protected Endpoints (Require Authentication)
|
||||
|
||||
#### User Profile
|
||||
```
|
||||
GET /api/v1/users/profile
|
||||
PUT /api/v1/users/profile
|
||||
PUT /api/v1/users/change-password
|
||||
POST /api/v1/users/logout
|
||||
```
|
||||
|
||||
### Admin Endpoints (Require Admin Role)
|
||||
|
||||
#### User Management
|
||||
```
|
||||
POST /api/v1/users/admin/
|
||||
GET /api/v1/users/admin/
|
||||
GET /api/v1/users/admin/:id
|
||||
PUT /api/v1/users/admin/:id
|
||||
DELETE /api/v1/users/admin/:id
|
||||
```
|
||||
|
||||
#### User Status & Role Management
|
||||
```
|
||||
PUT /api/v1/users/admin/:id/status
|
||||
PUT /api/v1/users/admin/:id/role
|
||||
```
|
||||
|
||||
#### Company & Role Queries
|
||||
```
|
||||
GET /api/v1/users/admin/company/:company_id
|
||||
GET /api/v1/users/admin/role/:role
|
||||
```
|
||||
|
||||
## Request/Response Examples
|
||||
|
||||
### Create User
|
||||
```json
|
||||
POST /api/v1/users/admin/
|
||||
{
|
||||
"full_name": "John Doe",
|
||||
"username": "johndoe",
|
||||
"email": "john.doe@example.com",
|
||||
"password": "securepassword123",
|
||||
"role": "manager",
|
||||
"company_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"department": "Engineering",
|
||||
"position": "Senior Manager",
|
||||
"phone": "+1234567890",
|
||||
"profile_image": "https://example.com/avatar.jpg"
|
||||
}
|
||||
```
|
||||
|
||||
### Login
|
||||
```json
|
||||
POST /api/v1/users/login
|
||||
{
|
||||
"email_or_username": "john.doe@example.com",
|
||||
"password": "securepassword123"
|
||||
}
|
||||
```
|
||||
|
||||
### Update User
|
||||
```json
|
||||
PUT /api/v1/users/admin/:id
|
||||
{
|
||||
"full_name": "John Smith",
|
||||
"department": "Product Management",
|
||||
"status": "active"
|
||||
}
|
||||
```
|
||||
|
||||
### List Users with Filters
|
||||
```
|
||||
GET /api/v1/users/admin/?search=john&role=manager&status=active&limit=20&offset=0&sort_by=created_at&sort_order=desc
|
||||
```
|
||||
|
||||
## Data Models
|
||||
|
||||
### User Entity
|
||||
```go
|
||||
type User struct {
|
||||
ID uuid.UUID `bson:"_id"`
|
||||
FullName string `bson:"full_name"`
|
||||
Username string `bson:"username"`
|
||||
Email string `bson:"email"`
|
||||
Password string `bson:"password"`
|
||||
Role UserRole `bson:"role"`
|
||||
Status UserStatus `bson:"status"`
|
||||
CompanyID *uuid.UUID `bson:"company_id,omitempty"`
|
||||
Department *string `bson:"department,omitempty"`
|
||||
Position *string `bson:"position,omitempty"`
|
||||
Phone *string `bson:"phone,omitempty"`
|
||||
ProfileImage *string `bson:"profile_image,omitempty"`
|
||||
IsVerified bool `bson:"is_verified"`
|
||||
LastLoginAt *int64 `bson:"last_login_at,omitempty"`
|
||||
CreatedAt int64 `bson:"created_at"`
|
||||
UpdatedAt int64 `bson:"updated_at"`
|
||||
CreatedBy *uuid.UUID `bson:"created_by,omitempty"`
|
||||
UpdatedBy *uuid.UUID `bson:"updated_by,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
### User Roles
|
||||
- `admin`: Full system access
|
||||
- `manager`: Company and team management
|
||||
- `operator`: Operational tasks
|
||||
- `viewer`: Read-only access
|
||||
|
||||
### User Status
|
||||
- `active`: User can access the system
|
||||
- `inactive`: User account is disabled
|
||||
- `suspended`: User account is temporarily suspended
|
||||
|
||||
## Security Features
|
||||
|
||||
### Password Security
|
||||
- Passwords are hashed using bcrypt
|
||||
- Minimum password length: 8 characters
|
||||
- Maximum password length: 128 characters
|
||||
|
||||
### Authentication
|
||||
- JWT-based authentication (to be implemented)
|
||||
- Token refresh mechanism
|
||||
- Secure logout with token invalidation
|
||||
|
||||
### Authorization
|
||||
- Role-based access control
|
||||
- Admin-only endpoints for user management
|
||||
- Company-scoped access for multi-tenant support
|
||||
|
||||
## Database Indexes
|
||||
|
||||
The service creates the following MongoDB indexes for optimal performance:
|
||||
|
||||
- **Email Index**: Unique index on email field
|
||||
- **Username Index**: Unique index on username field
|
||||
- **Company ID Index**: For company-based queries
|
||||
- **Role Index**: For role-based filtering
|
||||
- **Status Index**: For status-based filtering
|
||||
- **Created At Index**: For sorting by creation date
|
||||
- **Text Search Index**: For full-text search across name, email, username, department, and position
|
||||
|
||||
## Error Handling
|
||||
|
||||
The service provides comprehensive error handling with:
|
||||
|
||||
- **Validation Errors**: Detailed validation messages using govalidator
|
||||
- **Business Logic Errors**: Clear error messages for business rule violations
|
||||
- **Database Errors**: Proper handling of database constraints and connection issues
|
||||
- **Security Errors**: Secure error messages that don't leak sensitive information
|
||||
|
||||
## Logging
|
||||
|
||||
All operations are logged with structured logging including:
|
||||
|
||||
- **Operation Context**: User ID, company ID, operation type
|
||||
- **Error Details**: Full error context for debugging
|
||||
- **Security Events**: Login attempts, password changes, role updates
|
||||
- **Performance Metrics**: Operation timing and resource usage
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned Features
|
||||
- **JWT Token Management**: Complete JWT implementation with refresh tokens
|
||||
- **Email Verification**: Email verification for new user accounts
|
||||
- **Password Reset**: Complete password reset flow with email
|
||||
- **Audit Trail**: Comprehensive audit logging for all user operations
|
||||
- **Bulk Operations**: Bulk user import/export functionality
|
||||
- **Advanced Search**: Elasticsearch integration for advanced search capabilities
|
||||
|
||||
### Integration Points
|
||||
- **Email Service**: For password reset and verification emails
|
||||
- **Notification Service**: For user activity notifications
|
||||
- **Audit Service**: For comprehensive audit logging
|
||||
- **Permission Service**: For fine-grained permission management
|
||||
+16
-16
@@ -72,7 +72,7 @@ func (h *Handler) RegisterRoutes(e *echo.Echo) {
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /login [post]
|
||||
// @Router /admin/v1/login [post]
|
||||
func (h *Handler) Login(c echo.Context) error {
|
||||
form, err := response.Parse[LoginForm](c)
|
||||
if err != nil {
|
||||
@@ -99,7 +99,7 @@ func (h *Handler) Login(c echo.Context) error {
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /refresh-token [post]
|
||||
// @Router /admin/v1/refresh-token [post]
|
||||
func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
form, err := response.Parse[RefreshTokenForm](c)
|
||||
if err != nil {
|
||||
@@ -124,7 +124,7 @@ func (h *Handler) RefreshToken(c echo.Context) error {
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /reset-password [post]
|
||||
// @Router /admin/v1/reset-password [post]
|
||||
func (h *Handler) ResetPassword(c echo.Context) error {
|
||||
form, err := response.Parse[ResetPasswordForm](c)
|
||||
if err != nil {
|
||||
@@ -152,7 +152,7 @@ func (h *Handler) ResetPassword(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /profile [get]
|
||||
// @Router /admin/v1/profile [get]
|
||||
func (h *Handler) GetProfile(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -180,7 +180,7 @@ func (h *Handler) GetProfile(c echo.Context) error {
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /profile [put]
|
||||
// @Router /admin/v1/profile [put]
|
||||
func (h *Handler) UpdateProfile(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -213,7 +213,7 @@ func (h *Handler) UpdateProfile(c echo.Context) error {
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /change-password [put]
|
||||
// @Router /admin/v1/change-password [put]
|
||||
func (h *Handler) ChangePassword(c echo.Context) error {
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
@@ -245,7 +245,7 @@ func (h *Handler) ChangePassword(c echo.Context) error {
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /logout [delete]
|
||||
// @Router /admin/v1/logout [delete]
|
||||
func (h *Handler) Logout(c echo.Context) error {
|
||||
// Extract user ID and access token from context
|
||||
userID, err := GetUserIDFromContext(c)
|
||||
@@ -281,7 +281,7 @@ func (h *Handler) Logout(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users [post]
|
||||
// @Router /admin/v1/users [post]
|
||||
func (h *Handler) CreateUser(c echo.Context) error {
|
||||
// Get current user ID from JWT token
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -328,7 +328,7 @@ func (h *Handler) CreateUser(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users [get]
|
||||
// @Router /admin/v1/users [get]
|
||||
func (h *Handler) ListUsers(c echo.Context) error {
|
||||
var form ListUsersForm
|
||||
if err := c.Bind(&form); err != nil {
|
||||
@@ -363,7 +363,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/{id} [get]
|
||||
// @Router /admin/v1/users/{id} [get]
|
||||
func (h *Handler) GetUserByID(c echo.Context) error {
|
||||
idStr := c.Param("id")
|
||||
userID, err := uuid.Parse(idStr)
|
||||
@@ -394,7 +394,7 @@ func (h *Handler) GetUserByID(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/{id} [put]
|
||||
// @Router /admin/v1/users/{id} [put]
|
||||
func (h *Handler) UpdateUser(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -441,7 +441,7 @@ func (h *Handler) UpdateUser(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/{id} [delete]
|
||||
// @Router /admin/v1/users/{id} [delete]
|
||||
func (h *Handler) DeleteUser(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -480,7 +480,7 @@ func (h *Handler) DeleteUser(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/{id}/status [put]
|
||||
// @Router /admin/v1/users/{id}/status [put]
|
||||
func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
if err != nil {
|
||||
@@ -529,7 +529,7 @@ func (h *Handler) UpdateUserStatus(c echo.Context) error {
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 404 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/{id}/role [put]
|
||||
// @Router /admin/v1/users/{id}/role [put]
|
||||
func (h *Handler) UpdateUserRole(c echo.Context) error {
|
||||
// Extract user ID from JWT token context
|
||||
currentUserID, err := GetUserIDFromContext(c)
|
||||
@@ -579,7 +579,7 @@ func (h *Handler) UpdateUserRole(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/company/{company_id} [get]
|
||||
// @Router /admin/v1/users/company/{company_id} [get]
|
||||
func (h *Handler) GetUsersByCompanyID(c echo.Context) error {
|
||||
companyIDStr := c.Param("company_id")
|
||||
companyID, err := uuid.Parse(companyIDStr)
|
||||
@@ -643,7 +643,7 @@ func (h *Handler) GetUsersByCompanyID(c echo.Context) error {
|
||||
// @Failure 401 {object} response.APIResponse
|
||||
// @Failure 403 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /users/role/{role} [get]
|
||||
// @Router /admin/v1/users/role/{role} [get]
|
||||
func (h *Handler) GetUsersByRole(c echo.Context) error {
|
||||
roleStr := c.Param("role")
|
||||
role := UserRole(roleStr)
|
||||
|
||||
Reference in New Issue
Block a user