You've already forked top-wallpaper
Implement Redis caching for Reddit image fetching
This commit is contained in:
118
redis/client.go
Normal file
118
redis/client.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user