package esi import ( "context" "encoding/json" "fmt" "os" "time" "go-eve-pi/options" "go-eve-pi/types" logger "git.site.quack-lab.dev/dave/cylogger" ) type CacheDB interface { GetCacheEntry(hash string) (*types.CacheEntry, error) SaveCacheEntry(entry *types.CacheEntry) error } // CachedESI implements ESIInterface with caching type CachedESI struct { direct ESIInterface db CacheDB cacheValidity time.Duration } // NewCachedESI creates a new CachedESI instance func NewCachedESI(direct ESIInterface, db CacheDB) *CachedESI { // Parse cache validity ONCE at initialization cacheValidity, err := time.ParseDuration(options.GlobalOptions.CacheValidity) if err != nil { logger.Error("Invalid cache validity duration %s: %v", options.GlobalOptions.CacheValidity, err) os.Exit(1) } logger.Info("Initializing CachedESI with cache validity: %v", cacheValidity) return &CachedESI{ direct: direct, db: db, cacheValidity: cacheValidity, } } // GetPlanetPI retrieves detailed information about a specific planet with caching func (c *CachedESI) GetPlanetPI(ctx context.Context, characterID int, planetID int64, accessToken string) (*PlanetPI, error) { funclog := logger.Default.WithPrefix("CachedESI.GetPlanetPI").WithPrefix(fmt.Sprintf("characterID=%d", characterID)).WithPrefix(fmt.Sprintf("planetID=%d", planetID)) funclog.Info("Starting") hash := fmt.Sprintf("GetPlanetPI-%d-%d", characterID, planetID) cached, err := c.db.GetCacheEntry(hash) if err != nil { funclog.Warning("Failed to get cache entry: %v", err) } if cached != nil { funclog.Info("Cache hit for GetPlanetPI-%d-%d", characterID, planetID) var planetPI PlanetPI if err := json.Unmarshal([]byte(cached.Data), &planetPI); err != nil { funclog.Error("Failed to unmarshal cached data: %v", err) return nil, err } return &planetPI, nil } funclog.Info("Cache miss for GetPlanetPI-%d-%d", characterID, planetID) apiresp, err := c.direct.GetPlanetPI(ctx, characterID, planetID, accessToken) if err != nil { funclog.Error("Failed to get planet PI: %v", err) return nil, err } apirespjson, err := json.Marshal(apiresp) if err != nil { funclog.Error("Failed to marshal planet PI: %v", err) return nil, err } cacheEntry := &types.CacheEntry{ Hash: hash, Data: string(apirespjson), CachedAt: time.Now(), } err = c.db.SaveCacheEntry(cacheEntry) if err != nil { funclog.Warning("Failed to save cache entry: %v", err) } return apiresp, nil } // GetPlanetName retrieves planet name with caching func (c *CachedESI) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error) { funclog := logger.Default.WithPrefix("CachedESI.GetPlanetName").WithPrefix(fmt.Sprintf("planetID=%d", planetID)) funclog.Info("Starting") hash := fmt.Sprintf("GetPlanetName-%d", planetID) cached, err := c.db.GetCacheEntry(hash) if err != nil { funclog.Warning("Failed to get cache entry: %v", err) } if cached != nil { funclog.Info("Cache hit for GetPlanetName-%d", planetID) var planetPI PlanetName if err := json.Unmarshal([]byte(cached.Data), &planetPI); err != nil { funclog.Error("Failed to unmarshal cached data: %v", err) return nil, err } return &planetPI, nil } funclog.Info("Cache miss for GetPlanetName-%d", planetID) apiresp, err := c.direct.GetPlanetName(ctx, planetID) if err != nil { funclog.Error("Failed to get planet name: %v", err) return nil, err } apirespjson, err := json.Marshal(apiresp) if err != nil { funclog.Error("Failed to marshal planet name: %v", err) return nil, err } cacheEntry := &types.CacheEntry{ Hash: hash, Data: string(apirespjson), CachedAt: time.Now(), } err = c.db.SaveCacheEntry(cacheEntry) if err != nil { funclog.Warning("Failed to save cache entry: %v", err) } return apiresp, nil } func (c *CachedESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) { funclog := logger.Default.WithPrefix("CachedESI.GetCharacterPlanets").WithPrefix(fmt.Sprintf("characterID=%d", characterID)) funclog.Info("Starting") hash := fmt.Sprintf("GetCharacterPlanets-%d", characterID) cached, err := c.db.GetCacheEntry(hash) if err != nil { funclog.Warning("Failed to get cache entry: %v", err) } if cached != nil { funclog.Info("Cache hit for GetCharacterPlanets-%d", characterID) var planets []Planet if err := json.Unmarshal([]byte(cached.Data), &planets); err != nil { funclog.Error("Failed to unmarshal cached data: %v", err) return nil, err } return planets, nil } funclog.Info("Cache miss for GetCharacterPlanets-%d", characterID) apiresp, err := c.direct.GetCharacterPlanets(ctx, characterID, accessToken) if err != nil { funclog.Error("Failed to get character planets: %v", err) return nil, err } apirespjson, err := json.Marshal(apiresp) if err != nil { funclog.Error("Failed to marshal character planets: %v", err) return nil, err } cacheEntry := &types.CacheEntry{ Hash: hash, Data: string(apirespjson), CachedAt: time.Now(), } err = c.db.SaveCacheEntry(cacheEntry) if err != nil { funclog.Warning("Failed to save cache entry: %v", err) } return apiresp, nil } var _ ESIInterface = &CachedESI{}