diff options
Diffstat (limited to 'fetchers/cache.go')
| -rw-r--r-- | fetchers/cache.go | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/fetchers/cache.go b/fetchers/cache.go new file mode 100644 index 0000000..dc43c53 --- /dev/null +++ b/fetchers/cache.go @@ -0,0 +1,166 @@ +package fetchers + +import ( + "context" + "encoding/json" + "fmt" + "llostre/notes" + "sort" + "time" + + "github.com/redis/go-redis/v9" +) + +type Cache struct { + client *redis.Client + ctx context.Context + expiryTime time.Duration +} + +func NewCache(addr, password string, expiryMinutes int) (*Cache, error) { + client := redis.NewClient(&redis.Options{ + Addr: addr, + Password: password, + DB: 0, + }) + + ctx := context.Background() + if err := client.Ping(ctx).Err(); err != nil { + return nil, fmt.Errorf("redis connection failed: %w", err) + } + + return &Cache{ + client: client, + ctx: ctx, + expiryTime: time.Duration(expiryMinutes) * time.Minute, + }, nil +} + +func (c *Cache) Get(uri string) (*notes.Note, bool) { + key := fmt.Sprintf("post:%s", uri) + data, err := c.client.Get(c.ctx, key).Bytes() + if err != nil { + return nil, false + } + + var note notes.Note + if err := json.Unmarshal(data, ¬e); err != nil { + return nil, false + } + + return ¬e, true +} + +func (c *Cache) Set(uri string, note *notes.Note) { + key := fmt.Sprintf("post:%s", uri) + data, err := json.Marshal(note) + if err != nil { + return + } + + c.client.Set(c.ctx, key, data, c.expiryTime) +} + +func (c *Cache) GetAuthor(did string) (*notes.Author, bool) { + key := fmt.Sprintf("profile:%s", did) + data, err := c.client.Get(c.ctx, key).Bytes() + if err != nil { + return nil, false + } + + var author notes.Author + if err := json.Unmarshal(data, &author); err != nil { + return nil, false + } + + return &author, true +} + +func (c *Cache) SetAuthor(did string, author *notes.Author) { + key := fmt.Sprintf("profile:%s", did) + data, err := json.Marshal(author) + if err != nil { + return + } + + c.client.Set(c.ctx, key, data, c.expiryTime) +} + +func (c *Cache) GetCursor(did string) (string, bool) { + key := fmt.Sprintf("cursor:%s", did) + cursor, err := c.client.Get(c.ctx, key).Result() + if err != nil { + return "", false + } + return cursor, true +} + +func (c *Cache) SetCursor(did, cursor string) { + key := fmt.Sprintf("cursor:%s", did) + if cursor == "" { + c.client.Del(c.ctx, key) + return + } + c.client.Set(c.ctx, key, cursor, 24*time.Hour) +} + +func (c *Cache) GetPostsByDID(did string) ([]notes.Note, error) { + pattern := fmt.Sprintf("post:at://%s/*", did) + var posts []notes.Note + + iter := c.client.Scan(c.ctx, 0, pattern, 0).Iterator() + for iter.Next(c.ctx) { + data, err := c.client.Get(c.ctx, iter.Val()).Bytes() + if err != nil { + continue + } + var note notes.Note + if err := json.Unmarshal(data, ¬e); err != nil { + continue + } + posts = append(posts, note) + } + + if err := iter.Err(); err != nil { + return nil, err + } + + sort.Slice(posts, func(i, j int) bool { + return posts[i].CreatedAt.After(posts[j].CreatedAt) + }) + + return posts, nil +} + +// track oldest sent post per profile +func (c *Cache) GetOldestSent(did string) (time.Time, bool) { + key := fmt.Sprintf("oldest_sent:%s", did) + ts, err := c.client.Get(c.ctx, key).Int64() + if err != nil { + return time.Time{}, false + } + return time.Unix(ts, 0), true +} + +func (c *Cache) SetOldestSent(did string, timestamp time.Time) { + key := fmt.Sprintf("oldest_sent:%s", did) + c.client.Set(c.ctx, key, timestamp.Unix(), 24*time.Hour) +} + +// count posts older than timestamp for a profile +func (c *Cache) CountPostsOlderThan(did string, timestamp time.Time) (int, error) { + posts, err := c.GetPostsByDID(did) + if err != nil { + return 0, err + } + for i, p := range posts { + if p.CreatedAt.Before(timestamp) { + return len(posts) - i, nil + } + } + return 0, nil +} + +func (c *Cache) Close() error { + return c.client.Close() +} |
