Files
go-github-dashboard/pkg/api/cache.go
2025-05-05 18:22:30 -04:00

139 lines
2.8 KiB
Go

package api
import (
"encoding/gob"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"github.com/go-i2p/go-github-dashboard/pkg/types"
)
// CacheItem represents a cached item with its expiration time
type CacheItem struct {
Value interface{}
Expiration time.Time
}
// Cache provides a simple caching mechanism for API responses
type Cache struct {
items map[string]CacheItem
mutex sync.RWMutex
dir string
ttl time.Duration
}
// NewCache creates a new cache with the given directory and TTL
func NewCache(config *types.Config) *Cache {
// Register types for gob encoding
gob.Register([]types.Repository{})
gob.Register([]types.PullRequest{})
gob.Register([]types.Issue{})
gob.Register([]types.Discussion{})
gob.Register([]types.WorkflowRun{})
cache := &Cache{
items: make(map[string]CacheItem),
dir: config.CacheDir,
ttl: config.CacheTTL,
}
// Load cache from disk
cache.loadCache()
return cache
}
// Get retrieves a value from the cache
func (c *Cache) Get(key string) (interface{}, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
item, found := c.items[key]
if !found {
return nil, false
}
// Check if the item has expired
if time.Now().After(item.Expiration) {
return nil, false
}
return item.Value, true
}
// Set stores a value in the cache
func (c *Cache) Set(key string, value interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.items[key] = CacheItem{
Value: value,
Expiration: time.Now().Add(c.ttl),
}
// Save the cache to disk (in a separate goroutine to avoid blocking)
go c.saveCache()
}
// Clear clears all items from the cache
func (c *Cache) Clear() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.items = make(map[string]CacheItem)
go c.saveCache()
}
// saveCache saves the cache to disk
func (c *Cache) saveCache() {
c.mutex.RLock()
defer c.mutex.RUnlock()
cacheFile := filepath.Join(c.dir, "cache.gob")
file, err := os.Create(cacheFile)
if err != nil {
fmt.Printf("Error creating cache file: %v\n", err)
return
}
defer file.Close()
encoder := gob.NewEncoder(file)
err = encoder.Encode(c.items)
if err != nil {
fmt.Printf("Error encoding cache: %v\n", err)
}
}
// loadCache loads the cache from disk
func (c *Cache) loadCache() {
cacheFile := filepath.Join(c.dir, "cache.gob")
file, err := os.Open(cacheFile)
if err != nil {
// If the file doesn't exist, that's not an error
if !os.IsNotExist(err) {
fmt.Printf("Error opening cache file: %v\n", err)
}
return
}
defer file.Close()
decoder := gob.NewDecoder(file)
err = decoder.Decode(&c.items)
if err != nil {
fmt.Printf("Error decoding cache: %v\n", err)
// If there's an error decoding, start with a fresh cache
c.items = make(map[string]CacheItem)
}
// Remove expired items
now := time.Now()
for key, item := range c.items {
if now.After(item.Expiration) {
delete(c.items, key)
}
}
}