Enhance cursor rules and add user management functionality
- Updated cursor rules to emphasize the importance of dependency injection and logging practices. - Introduced a new user management domain, including entities, services, handlers, and validation. - Implemented user authentication features such as login, registration, and role-based access control. - Added Redis integration for token management and caching. - Enhanced API documentation with Swagger for user-related endpoints. - Updated configuration to support new JWT settings and Redis connection parameters.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# Redis Package
|
||||
|
||||
This package provides Redis client functionality for the Tender Management system, specifically designed for token blacklisting and caching operations.
|
||||
|
||||
## Features
|
||||
|
||||
- **Connection Management**: Handles Redis client connections with proper error handling
|
||||
- **Interface-based Design**: Uses interfaces for easy testing and mocking
|
||||
- **Configuration Support**: Supports Redis configuration from environment variables
|
||||
- **Connection Testing**: Validates connections on startup
|
||||
- **Resource Cleanup**: Proper connection closing and cleanup
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```go
|
||||
import "tm/pkg/redis"
|
||||
|
||||
// Create Redis configuration
|
||||
config := &redis.Config{
|
||||
Host: "localhost",
|
||||
Port: 6379,
|
||||
Password: "password",
|
||||
DB: 0,
|
||||
PoolSize: 10,
|
||||
}
|
||||
|
||||
// Create Redis client
|
||||
client, err := redis.NewClient(config, logger)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Use the client
|
||||
err = client.Set(ctx, "key", "value", time.Hour)
|
||||
```
|
||||
|
||||
### Token Blacklisting
|
||||
|
||||
The Redis client is primarily used for JWT token blacklisting:
|
||||
|
||||
```go
|
||||
// Add token to blacklist
|
||||
err = client.Set(ctx, "blacklist:token_hash", "1", tokenExpiration)
|
||||
|
||||
// Check if token is blacklisted
|
||||
exists, err := client.Exists(ctx, "blacklist:token_hash")
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
if exists > 0 {
|
||||
// Token is blacklisted
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The Redis configuration supports the following fields:
|
||||
|
||||
- `Host`: Redis server hostname (default: localhost)
|
||||
- `Port`: Redis server port (default: 6379)
|
||||
- `Password`: Redis authentication password
|
||||
- `DB`: Redis database number (default: 0)
|
||||
- `PoolSize`: Connection pool size (default: 10)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The configuration can be loaded from environment variables:
|
||||
|
||||
- `TM_CACHE_REDIS_HOST`
|
||||
- `TM_CACHE_REDIS_PORT`
|
||||
- `TM_CACHE_REDIS_PASSWORD`
|
||||
- `TM_CACHE_REDIS_DB`
|
||||
- `TM_CACHE_REDIS_POOL_SIZE`
|
||||
|
||||
## Error Handling
|
||||
|
||||
The package provides comprehensive error handling:
|
||||
|
||||
- Connection failures are logged with context
|
||||
- All Redis operations return proper errors
|
||||
- Connection timeouts are handled gracefully
|
||||
- Resource cleanup is guaranteed with defer statements
|
||||
|
||||
## Testing
|
||||
|
||||
The interface-based design makes it easy to mock Redis operations in tests:
|
||||
|
||||
```go
|
||||
type MockRedisClient struct {
|
||||
// Implement the Client interface
|
||||
}
|
||||
|
||||
// Use in tests
|
||||
client := &MockRedisClient{}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `github.com/redis/go-redis/v9`: Redis client library
|
||||
- `tm/pkg/logger`: Logging interface
|
||||
@@ -0,0 +1,96 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"tm/pkg/logger"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// Config holds Redis configuration
|
||||
type Config struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Password string `json:"password"`
|
||||
DB int `json:"db"`
|
||||
PoolSize int `json:"pool_size"`
|
||||
}
|
||||
|
||||
// Client represents a Redis client interface
|
||||
type Client interface {
|
||||
Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error
|
||||
Get(ctx context.Context, key string) (string, error)
|
||||
Del(ctx context.Context, keys ...string) error
|
||||
Exists(ctx context.Context, keys ...string) (int64, error)
|
||||
Close() error
|
||||
}
|
||||
|
||||
// client implements the Client interface
|
||||
type client struct {
|
||||
rdb *redis.Client
|
||||
}
|
||||
|
||||
// NewClient creates a new Redis client
|
||||
func NewClient(config *Config, logger logger.Logger) (Client, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("redis config is required")
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", config.Host, config.Port)
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Password: config.Password,
|
||||
DB: config.DB,
|
||||
PoolSize: config.PoolSize,
|
||||
})
|
||||
|
||||
// Test the connection
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||
logger.Error("Failed to connect to Redis", map[string]interface{}{
|
||||
"host": config.Host,
|
||||
"port": config.Port,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return nil, fmt.Errorf("failed to connect to Redis: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Successfully connected to Redis", map[string]interface{}{
|
||||
"host": config.Host,
|
||||
"port": config.Port,
|
||||
"db": config.DB,
|
||||
})
|
||||
|
||||
return &client{rdb: rdb}, nil
|
||||
}
|
||||
|
||||
// Set sets a key-value pair with expiration
|
||||
func (c *client) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
return c.rdb.Set(ctx, key, value, expiration).Err()
|
||||
}
|
||||
|
||||
// Get retrieves a value by key
|
||||
func (c *client) Get(ctx context.Context, key string) (string, error) {
|
||||
return c.rdb.Get(ctx, key).Result()
|
||||
}
|
||||
|
||||
// Del deletes one or more keys
|
||||
func (c *client) Del(ctx context.Context, keys ...string) error {
|
||||
return c.rdb.Del(ctx, keys...).Err()
|
||||
}
|
||||
|
||||
// Exists checks if one or more keys exist
|
||||
func (c *client) Exists(ctx context.Context, keys ...string) (int64, error) {
|
||||
return c.rdb.Exists(ctx, keys...).Result()
|
||||
}
|
||||
|
||||
// Close closes the Redis connection
|
||||
func (c *client) Close() error {
|
||||
return c.rdb.Close()
|
||||
}
|
||||
Reference in New Issue
Block a user