Initial commit: MyCrib API in Go
Complete rewrite of Django REST API to Go with: - Gin web framework for HTTP routing - GORM for database operations - GoAdmin for admin panel - Gorush integration for push notifications - Redis for caching and job queues Features implemented: - User authentication (login, register, logout, password reset) - Residence management (CRUD, sharing, share codes) - Task management (CRUD, kanban board, completions) - Contractor management (CRUD, specialties) - Document management (CRUD, warranties) - Notifications (preferences, push notifications) - Subscription management (tiers, limits) Infrastructure: - Docker Compose for local development - Database migrations and seed data - Admin panel for data management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
163
internal/services/cache_service.go
Normal file
163
internal/services/cache_service.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/config"
|
||||
)
|
||||
|
||||
// CacheService provides Redis caching functionality
|
||||
type CacheService struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
var cacheInstance *CacheService
|
||||
|
||||
// NewCacheService creates a new cache service
|
||||
func NewCacheService(cfg *config.RedisConfig) (*CacheService, error) {
|
||||
opt, err := redis.ParseURL(cfg.URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Redis URL: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Password != "" {
|
||||
opt.Password = cfg.Password
|
||||
}
|
||||
if cfg.DB != 0 {
|
||||
opt.DB = cfg.DB
|
||||
}
|
||||
|
||||
client := redis.NewClient(opt)
|
||||
|
||||
// Test connection
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := client.Ping(ctx).Err(); err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to Redis: %w", err)
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("url", cfg.URL).
|
||||
Int("db", opt.DB).
|
||||
Msg("Connected to Redis")
|
||||
|
||||
cacheInstance = &CacheService{client: client}
|
||||
return cacheInstance, nil
|
||||
}
|
||||
|
||||
// GetCache returns the cache service instance
|
||||
func GetCache() *CacheService {
|
||||
return cacheInstance
|
||||
}
|
||||
|
||||
// Client returns the underlying Redis client
|
||||
func (c *CacheService) Client() *redis.Client {
|
||||
return c.client
|
||||
}
|
||||
|
||||
// Set stores a value with expiration
|
||||
func (c *CacheService) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal value: %w", err)
|
||||
}
|
||||
|
||||
return c.client.Set(ctx, key, data, expiration).Err()
|
||||
}
|
||||
|
||||
// Get retrieves a value by key
|
||||
func (c *CacheService) Get(ctx context.Context, key string, dest interface{}) error {
|
||||
data, err := c.client.Get(ctx, key).Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, dest)
|
||||
}
|
||||
|
||||
// GetString retrieves a string value by key
|
||||
func (c *CacheService) GetString(ctx context.Context, key string) (string, error) {
|
||||
return c.client.Get(ctx, key).Result()
|
||||
}
|
||||
|
||||
// SetString stores a string value with expiration
|
||||
func (c *CacheService) SetString(ctx context.Context, key string, value string, expiration time.Duration) error {
|
||||
return c.client.Set(ctx, key, value, expiration).Err()
|
||||
}
|
||||
|
||||
// Delete removes a key
|
||||
func (c *CacheService) Delete(ctx context.Context, keys ...string) error {
|
||||
return c.client.Del(ctx, keys...).Err()
|
||||
}
|
||||
|
||||
// Exists checks if a key exists
|
||||
func (c *CacheService) Exists(ctx context.Context, keys ...string) (int64, error) {
|
||||
return c.client.Exists(ctx, keys...).Result()
|
||||
}
|
||||
|
||||
// Close closes the Redis connection
|
||||
func (c *CacheService) Close() error {
|
||||
if c.client != nil {
|
||||
return c.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Auth token cache helpers
|
||||
const (
|
||||
AuthTokenPrefix = "auth_token_"
|
||||
TokenCacheTTL = 5 * time.Minute
|
||||
)
|
||||
|
||||
// CacheAuthToken caches a user ID for a token
|
||||
func (c *CacheService) CacheAuthToken(ctx context.Context, token string, userID uint) error {
|
||||
key := AuthTokenPrefix + token
|
||||
return c.SetString(ctx, key, fmt.Sprintf("%d", userID), TokenCacheTTL)
|
||||
}
|
||||
|
||||
// GetCachedAuthToken gets a cached user ID for a token
|
||||
func (c *CacheService) GetCachedAuthToken(ctx context.Context, token string) (uint, error) {
|
||||
key := AuthTokenPrefix + token
|
||||
val, err := c.GetString(ctx, key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var userID uint
|
||||
_, err = fmt.Sscanf(val, "%d", &userID)
|
||||
return userID, err
|
||||
}
|
||||
|
||||
// InvalidateAuthToken removes a cached token
|
||||
func (c *CacheService) InvalidateAuthToken(ctx context.Context, token string) error {
|
||||
key := AuthTokenPrefix + token
|
||||
return c.Delete(ctx, key)
|
||||
}
|
||||
|
||||
// Static data cache helpers
|
||||
const (
|
||||
StaticDataKey = "static_data"
|
||||
StaticDataTTL = 1 * time.Hour
|
||||
)
|
||||
|
||||
// CacheStaticData caches static lookup data
|
||||
func (c *CacheService) CacheStaticData(ctx context.Context, data interface{}) error {
|
||||
return c.Set(ctx, StaticDataKey, data, StaticDataTTL)
|
||||
}
|
||||
|
||||
// GetCachedStaticData retrieves cached static data
|
||||
func (c *CacheService) GetCachedStaticData(ctx context.Context, dest interface{}) error {
|
||||
return c.Get(ctx, StaticDataKey, dest)
|
||||
}
|
||||
|
||||
// InvalidateStaticData removes cached static data
|
||||
func (c *CacheService) InvalidateStaticData(ctx context.Context) error {
|
||||
return c.Delete(ctx, StaticDataKey)
|
||||
}
|
||||
Reference in New Issue
Block a user