package redis import ( "context" "errors" "fmt" "os" "strconv" "time" "github.com/redis/go-redis/v9" ) type contextKey string const redisKey contextKey = "redisClient" const CacheKey = "top-wallpaper:latestImage" type Cache interface { Get(key string) (string, error) Set(key string, value string, expiration int64) error Close() error } type Redis struct { client *redis.Client } func NewRedis() (*Redis, error) { redisUrl := os.Getenv("REDIS_URL") if redisUrl == "" { return nil, errors.New("REDIS_URL not set, cannot initialize Redis client") } redisPort := os.Getenv("REDIS_PORT") if redisPort == "" { redisPort = "6379" } redisDb := os.Getenv("REDIS_DB") if redisDb == "" { redisDb = "0" } redisDbInt, err := strconv.ParseInt(redisDb, 10, 64) if err != nil { return nil, errors.New("REDIS_DB is not a valid integer, cannot initialize Redis client") } rc := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%s", redisUrl, redisPort), Password: os.Getenv("REDIS_PASSWORD"), DB: int(redisDbInt), }) cmd := rc.Ping(context.Background()) if cmd.Err() != nil { return nil, fmt.Errorf("could not connect to Redis: %v", cmd.Err()) } if cmd.Val() != "PONG" { return nil, fmt.Errorf("unexpected response from Redis: %s", cmd.Val()) } return &Redis{ client: rc, }, nil } func (r *Redis) Get(key string) (string, error) { cmd := r.client.Get(context.Background(), key) if cmd.Err() != nil { if errors.Is(cmd.Err(), redis.Nil) { return "", nil // Key does not exist } return "", fmt.Errorf("could not get value for key %s: %v", key, cmd.Err()) } return cmd.Val(), nil } func (r *Redis) Set(key string, value string, expiration time.Duration) error { cmd := r.client.Set(context.Background(), key, value, expiration) if cmd.Err() != nil { return fmt.Errorf("could not set value for key %s: %v", key, cmd.Err()) } if cmd.Val() != "OK" { return fmt.Errorf("unexpected response from Redis when setting key %s: %s", key, cmd.Val()) } return nil } func (r *Redis) Close() error { if r.client == nil { return nil } err := r.client.Close() if err != nil { return fmt.Errorf("could not close Redis client: %v", err) } return nil } func WithContext(ctx context.Context, r *Redis) context.Context { return context.WithValue(ctx, redisKey, r) } func FromContext(ctx context.Context) (*Redis, error) { r, ok := ctx.Value(redisKey).(*Redis) if !ok { return nil, errors.New("no Redis client found in context") } if r == nil { return nil, errors.New("redis client is nil") } return r, nil }