117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
package esi
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"go-eve-pi/options"
|
|
"go-eve-pi/types"
|
|
|
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
|
)
|
|
|
|
// CacheEntry is now defined in the types package
|
|
|
|
// CachedESI implements ESIInterface with caching
|
|
type CachedESI struct {
|
|
direct ESIInterface
|
|
db interface {
|
|
GetCacheEntry(urlHash string) (*types.CacheEntry, error)
|
|
SaveCacheEntry(entry *types.CacheEntry) error
|
|
}
|
|
cacheValidity time.Duration
|
|
}
|
|
|
|
// NewCachedESI creates a new CachedESI instance
|
|
func NewCachedESI(direct ESIInterface, db interface {
|
|
GetCacheEntry(urlHash string) (*types.CacheEntry, error)
|
|
SaveCacheEntry(entry *types.CacheEntry) error
|
|
}) *CachedESI {
|
|
// Parse cache validity ONCE at initialization
|
|
cacheValidity, err := time.ParseDuration(options.GlobalOptions.CacheValidity)
|
|
if err != nil {
|
|
logger.Warning("Invalid cache validity duration %s, using 10m default: %v", options.GlobalOptions.CacheValidity, err)
|
|
cacheValidity = 10 * time.Minute
|
|
}
|
|
|
|
return &CachedESI{
|
|
direct: direct,
|
|
db: db,
|
|
cacheValidity: cacheValidity,
|
|
}
|
|
}
|
|
|
|
// GetCharacterPlanets retrieves a list of planets for a character with caching
|
|
func (c *CachedESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) {
|
|
url := fmt.Sprintf("/v1/characters/%d/planets/", characterID)
|
|
|
|
fetchFunc := func() ([]Planet, error) {
|
|
return c.direct.GetCharacterPlanets(ctx, characterID, accessToken)
|
|
}
|
|
|
|
return getCachedResponse(c, url, fetchFunc)
|
|
}
|
|
|
|
// GetPlanetDetails retrieves detailed information about a specific planet with caching
|
|
func (c *CachedESI) GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error) {
|
|
url := fmt.Sprintf("/v3/characters/%d/planets/%d/", characterID, planetID)
|
|
fetchFunc := func() (*PlanetDetail, error) {
|
|
return c.direct.GetPlanetDetails(ctx, characterID, planetID, accessToken)
|
|
}
|
|
|
|
return getCachedResponse(c, url, fetchFunc)
|
|
}
|
|
|
|
// getCachedResponse handles caching logic with generics
|
|
func getCachedResponse[T any](c *CachedESI, url string, fetchFunc func() (T, error)) (T, error) {
|
|
// Generate cache key
|
|
hash := sha256.Sum256([]byte(url))
|
|
urlHash := hex.EncodeToString(hash[:])
|
|
|
|
// Check cache using the interface
|
|
cacheEntry, err := c.db.GetCacheEntry(urlHash)
|
|
if err == nil {
|
|
// Check if cache is still valid
|
|
if time.Since(cacheEntry.CachedAt) < c.cacheValidity {
|
|
logger.Debug("Cache hit for URL: %s", url)
|
|
// Parse cached response
|
|
var result T
|
|
if err := json.Unmarshal([]byte(cacheEntry.Response), &result); err == nil {
|
|
return result, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache miss or invalid, fetch from API
|
|
logger.Debug("Cache miss for URL: %s", url)
|
|
result, err := fetchFunc()
|
|
if err != nil {
|
|
var zero T
|
|
return zero, err
|
|
}
|
|
|
|
// Store in cache
|
|
responseBytes, err := json.Marshal(result)
|
|
if err != nil {
|
|
logger.Warning("Failed to marshal response for caching: %v", err)
|
|
return result, nil
|
|
}
|
|
|
|
cacheEntry = &types.CacheEntry{
|
|
URLHash: urlHash,
|
|
Response: string(responseBytes),
|
|
CachedAt: time.Now(),
|
|
}
|
|
|
|
if err := c.db.SaveCacheEntry(cacheEntry); err != nil {
|
|
logger.Warning("Failed to cache response: %v", err)
|
|
}
|
|
|
|
logger.Debug("Cached response for URL: %s", url)
|
|
return result, nil
|
|
}
|