Deretardify the caching

This commit is contained in:
2025-10-11 10:33:03 +02:00
parent 6c1e9310c5
commit 22b218c7d3
6 changed files with 401 additions and 476 deletions

View File

@@ -1,132 +1,132 @@
package constants package constants
var PI_TYPES_MAP = map[int]string{ var PITypesMap = map[int64]string{
44: "Enriched Uranium", 44: "Enriched Uranium",
2073: "Microorganisms", 2073: "Microorganisms",
2256: "Temperate Launchpad", 2256: "Temperate Launchpad",
2257: "Ice Storage Facility", 2257: "Ice Storage Facility",
2267: "Base Metals", 2267: "Base Metals",
2268: "Aqueous Liquids", 2268: "Aqueous Liquids",
2270: "Noble Metals", 2270: "Noble Metals",
2272: "Heavy Metals", 2272: "Heavy Metals",
2286: "Planktic Colonies", 2286: "Planktic Colonies",
2287: "Complex Organisms", 2287: "Complex Organisms",
2288: "Carbon Compounds", 2288: "Carbon Compounds",
2305: "Autotrophs", 2305: "Autotrophs",
2306: "Non-CS Crystals", 2306: "Non-CS Crystals",
2307: "Felsic Magma", 2307: "Felsic Magma",
2308: "Suspended Plasma", 2308: "Suspended Plasma",
2309: "Ionic Solutions", 2309: "Ionic Solutions",
2310: "Noble Gas", 2310: "Noble Gas",
2311: "Reactive Gas", 2311: "Reactive Gas",
2312: "Supertensile Plastics", 2312: "Supertensile Plastics",
2317: "Oxides", 2317: "Oxides",
2319: "Test Cultures", 2319: "Test Cultures",
2321: "Polyaramids", 2321: "Polyaramids",
2327: "Microfiber Shielding", 2327: "Microfiber Shielding",
2328: "Water-Cooled CPU", 2328: "Water-Cooled CPU",
2329: "Biocells", 2329: "Biocells",
2344: "Condensates", 2344: "Condensates",
2345: "Camera Drones", 2345: "Camera Drones",
2346: "Synthetic Synapses", 2346: "Synthetic Synapses",
2348: "Gel-Matrix Biopaste", 2348: "Gel-Matrix Biopaste",
2349: "Supercomputers", 2349: "Supercomputers",
2351: "Smartfab Units", 2351: "Smartfab Units",
2352: "Nuclear Reactors", 2352: "Nuclear Reactors",
2354: "Neocoms", 2354: "Neocoms",
2358: "Biotech Research Reports", 2358: "Biotech Research Reports",
2360: "Industrial Explosives", 2360: "Industrial Explosives",
2361: "Hermetic Membranes", 2361: "Hermetic Membranes",
2366: "Hazmat Detection Systems", 2366: "Hazmat Detection Systems",
2367: "Cryoprotectant Solution", 2367: "Cryoprotectant Solution",
2389: "Plasmoids", 2389: "Plasmoids",
2390: "Electrolytes", 2390: "Electrolytes",
2392: "Oxidizing Compound", 2392: "Oxidizing Compound",
2393: "Bacteria", 2393: "Bacteria",
2395: "Proteins", 2395: "Proteins",
2396: "Biofuels", 2396: "Biofuels",
2397: "Industrial Fibers", 2397: "Industrial Fibers",
2398: "Reactive Metals", 2398: "Reactive Metals",
2399: "Precious Metals", 2399: "Precious Metals",
2400: "Toxic Metals", 2400: "Toxic Metals",
2401: "Chiral Structures", 2401: "Chiral Structures",
2463: "Nanites", 2463: "Nanites",
2469: "Lava Basic Industry Facility", 2469: "Lava Basic Industry Facility",
2470: "Lava Advanced Industry Facility", 2470: "Lava Advanced Industry Facility",
2471: "Plasma Basic Industry Facility", 2471: "Plasma Basic Industry Facility",
2472: "Plasma Advanced Industry Facility", 2472: "Plasma Advanced Industry Facility",
2473: "Barren Basic Industry Facility", 2473: "Barren Basic Industry Facility",
2474: "Barren Advanced Industry Facility", 2474: "Barren Advanced Industry Facility",
2475: "Barren High-Tech Production Plant", 2475: "Barren High-Tech Production Plant",
2480: "Temperate Advanced Industry Facility", 2480: "Temperate Advanced Industry Facility",
2481: "Temperate Basic Industry Facility", 2481: "Temperate Basic Industry Facility",
2482: "Temperate High-Tech Production Plant", 2482: "Temperate High-Tech Production Plant",
2483: "Storm Basic Industry Facility", 2483: "Storm Basic Industry Facility",
2484: "Storm Advanced Industry Facility", 2484: "Storm Advanced Industry Facility",
2485: "Oceanic Advanced Industry Facility", 2485: "Oceanic Advanced Industry Facility",
2490: "Oceanic Basic Industry Facility", 2490: "Oceanic Basic Industry Facility",
2491: "Ice Advanced Industry Facility", 2491: "Ice Advanced Industry Facility",
2492: "Gas Basic Industry Facility", 2492: "Gas Basic Industry Facility",
2493: "Ice Basic Industry Facility", 2493: "Ice Basic Industry Facility",
2494: "Gas Advanced Industry Facility", 2494: "Gas Advanced Industry Facility",
2535: "Oceanic Storage Facility", 2535: "Oceanic Storage Facility",
2536: "Gas Storage Facility", 2536: "Gas Storage Facility",
2541: "Barren Storage Facility", 2541: "Barren Storage Facility",
2542: "Oceanic Launchpad", 2542: "Oceanic Launchpad",
2543: "Gas Launchpad", 2543: "Gas Launchpad",
2544: "Barren Launchpad", 2544: "Barren Launchpad",
2552: "Ice Launchpad", 2552: "Ice Launchpad",
2555: "Lava Launchpad", 2555: "Lava Launchpad",
2556: "Plasma Launchpad", 2556: "Plasma Launchpad",
2557: "Storm Launchpad", 2557: "Storm Launchpad",
2558: "Lava Storage Facility", 2558: "Lava Storage Facility",
2560: "Plasma Storage Facility", 2560: "Plasma Storage Facility",
2561: "Storm Storage Facility", 2561: "Storm Storage Facility",
2562: "Temperate Storage Facility", 2562: "Temperate Storage Facility",
2848: "Barren Extractor Control Unit", 2848: "Barren Extractor Control Unit",
2867: "Broadcast Node", 2867: "Broadcast Node",
2868: "Integrity Response Drones", 2868: "Integrity Response Drones",
2869: "Nano-Factory", 2869: "Nano-Factory",
2870: "Organic Mortar Applicators", 2870: "Organic Mortar Applicators",
2871: "Recursive Computing Module", 2871: "Recursive Computing Module",
2872: "Self-Harmonizing Power Core", 2872: "Self-Harmonizing Power Core",
2875: "Sterile Conduits", 2875: "Sterile Conduits",
2876: "Wetware Mainframe", 2876: "Wetware Mainframe",
3060: "Gas Extractor Control Unit", 3060: "Gas Extractor Control Unit",
3061: "Ice Extractor Control Unit", 3061: "Ice Extractor Control Unit",
3062: "Lava Extractor Control Unit", 3062: "Lava Extractor Control Unit",
3063: "Oceanic Extractor Control Unit", 3063: "Oceanic Extractor Control Unit",
3064: "Plasma Extractor Control Unit", 3064: "Plasma Extractor Control Unit",
3067: "Storm Extractor Control Unit", 3067: "Storm Extractor Control Unit",
3068: "Temperate Extractor Control Unit", 3068: "Temperate Extractor Control Unit",
3645: "Water", 3645: "Water",
3683: "Oxygen", 3683: "Oxygen",
3689: "Mechanical Parts", 3689: "Mechanical Parts",
3691: "Synthetic Oil", 3691: "Synthetic Oil",
3693: "Fertilizer", 3693: "Fertilizer",
3695: "Polytextiles", 3695: "Polytextiles",
3697: "Silicate Glass", 3697: "Silicate Glass",
3725: "Livestock", 3725: "Livestock",
3775: "Viral Agent", 3775: "Viral Agent",
3779: "Biomass", 3779: "Biomass",
3828: "Construction Blocks", 3828: "Construction Blocks",
9828: "Silicon", 9828: "Silicon",
9830: "Rocket Fuel", 9830: "Rocket Fuel",
9832: "Coolant", 9832: "Coolant",
9834: "Guidance Systems", 9834: "Guidance Systems",
9836: "Consumer Electronics", 9836: "Consumer Electronics",
9838: "Superconductors", 9838: "Superconductors",
9840: "Transmitter", 9840: "Transmitter",
9842: "Miniature Electronics", 9842: "Miniature Electronics",
9846: "Planetary Vehicles", 9846: "Planetary Vehicles",
9848: "Robotics", 9848: "Robotics",
12836: "Transcranial Microcontrollers", 12836: "Transcranial Microcontrollers",
15317: "Genetically Enhanced Livestock", 15317: "Genetically Enhanced Livestock",
17136: "Ukomi Superconductors", 17136: "Ukomi Superconductors",
17392: "Data Chips", 17392: "Data Chips",
17898: "High-Tech Transmitters", 17898: "High-Tech Transmitters",
28974: "Vaccines", 28974: "Vaccines",
} }
var PITypes = map[int]string{ var PITypes = map[int]string{
2469: "Lava Basic Industry Facility", 2469: "Lava Basic Industry Facility",
@@ -275,135 +275,135 @@ var StorageIDs = []int{
2557, // Storm Launchpad 2557, // Storm Launchpad
} }
var PIProductVolumes = map[int]float64{ var PIProductVolumes = map[int64]float64{
44: 0.75, // Enriched Uranium 44: 0.75, // Enriched Uranium
2073: 0.005, // Microorganisms 2073: 0.005, // Microorganisms
2256: 0.0, // Temperate Launchpad 2256: 0.0, // Temperate Launchpad
2257: 0.0, // Ice Storage Facility 2257: 0.0, // Ice Storage Facility
2267: 0.005, // Base Metals 2267: 0.005, // Base Metals
2268: 0.005, // Aqueous Liquids 2268: 0.005, // Aqueous Liquids
2270: 0.005, // Noble Metals 2270: 0.005, // Noble Metals
2272: 0.005, // Heavy Metals 2272: 0.005, // Heavy Metals
2286: 0.005, // Planktic Colonies 2286: 0.005, // Planktic Colonies
2287: 0.005, // Complex Organisms 2287: 0.005, // Complex Organisms
2288: 0.005, // Carbon Compounds 2288: 0.005, // Carbon Compounds
2305: 0.005, // Autotrophs 2305: 0.005, // Autotrophs
2306: 0.005, // Non-CS Crystals 2306: 0.005, // Non-CS Crystals
2307: 0.005, // Felsic Magma 2307: 0.005, // Felsic Magma
2308: 0.005, // Suspended Plasma 2308: 0.005, // Suspended Plasma
2309: 0.005, // Ionic Solutions 2309: 0.005, // Ionic Solutions
2310: 0.005, // Noble Gas 2310: 0.005, // Noble Gas
2311: 0.005, // Reactive Gas 2311: 0.005, // Reactive Gas
2312: 0.75, // Supertensile Plastics 2312: 0.75, // Supertensile Plastics
2317: 0.75, // Oxides 2317: 0.75, // Oxides
2319: 0.75, // Test Cultures 2319: 0.75, // Test Cultures
2321: 0.75, // Polyaramids 2321: 0.75, // Polyaramids
2327: 0.75, // Microfiber Shielding 2327: 0.75, // Microfiber Shielding
2328: 0.75, // Water-Cooled CPU 2328: 0.75, // Water-Cooled CPU
2329: 0.75, // Biocells 2329: 0.75, // Biocells
2344: 3.0, // Condensates 2344: 3.0, // Condensates
2345: 3.0, // Camera Drones 2345: 3.0, // Camera Drones
2346: 3.0, // Synthetic Synapses 2346: 3.0, // Synthetic Synapses
2348: 3.0, // Gel-Matrix Biopaste 2348: 3.0, // Gel-Matrix Biopaste
2349: 3.0, // Supercomputers 2349: 3.0, // Supercomputers
2351: 3.0, // Smartfab Units 2351: 3.0, // Smartfab Units
2352: 3.0, // Nuclear Reactors 2352: 3.0, // Nuclear Reactors
2354: 3.0, // Neocoms 2354: 3.0, // Neocoms
2358: 3.0, // Biotech Research Reports 2358: 3.0, // Biotech Research Reports
2360: 3.0, // Industrial Explosives 2360: 3.0, // Industrial Explosives
2361: 3.0, // Hermetic Membranes 2361: 3.0, // Hermetic Membranes
2366: 3.0, // Hazmat Detection Systems 2366: 3.0, // Hazmat Detection Systems
2367: 3.0, // Cryoprotectant Solution 2367: 3.0, // Cryoprotectant Solution
2389: 0.19, // Plasmoids 2389: 0.19, // Plasmoids
2390: 0.19, // Electrolytes 2390: 0.19, // Electrolytes
2392: 0.19, // Oxidizing Compound 2392: 0.19, // Oxidizing Compound
2393: 0.19, // Bacteria 2393: 0.19, // Bacteria
2395: 0.19, // Proteins 2395: 0.19, // Proteins
2396: 0.19, // Biofuels 2396: 0.19, // Biofuels
2397: 0.19, // Industrial Fibers 2397: 0.19, // Industrial Fibers
2398: 0.19, // Reactive Metals 2398: 0.19, // Reactive Metals
2399: 0.19, // Precious Metals 2399: 0.19, // Precious Metals
2400: 0.19, // Toxic Metals 2400: 0.19, // Toxic Metals
2401: 0.19, // Chiral Structures 2401: 0.19, // Chiral Structures
2463: 0.75, // Nanites 2463: 0.75, // Nanites
2469: 0.0, // Lava Basic Industry Facility 2469: 0.0, // Lava Basic Industry Facility
2470: 0.0, // Lava Advanced Industry Facility 2470: 0.0, // Lava Advanced Industry Facility
2471: 0.0, // Plasma Basic Industry Facility 2471: 0.0, // Plasma Basic Industry Facility
2472: 0.0, // Plasma Advanced Industry Facility 2472: 0.0, // Plasma Advanced Industry Facility
2473: 0.0, // Barren Basic Industry Facility 2473: 0.0, // Barren Basic Industry Facility
2474: 0.0, // Barren Advanced Industry Facility 2474: 0.0, // Barren Advanced Industry Facility
2475: 0.0, // Barren High-Tech Production Plant 2475: 0.0, // Barren High-Tech Production Plant
2480: 0.0, // Temperate Advanced Industry Facility 2480: 0.0, // Temperate Advanced Industry Facility
2481: 0.0, // Temperate Basic Industry Facility 2481: 0.0, // Temperate Basic Industry Facility
2482: 0.0, // Temperate High-Tech Production Plant 2482: 0.0, // Temperate High-Tech Production Plant
2483: 0.0, // Storm Basic Industry Facility 2483: 0.0, // Storm Basic Industry Facility
2484: 0.0, // Storm Advanced Industry Facility 2484: 0.0, // Storm Advanced Industry Facility
2485: 0.0, // Oceanic Advanced Industry Facility 2485: 0.0, // Oceanic Advanced Industry Facility
2490: 0.0, // Oceanic Basic Industry Facility 2490: 0.0, // Oceanic Basic Industry Facility
2491: 0.0, // Ice Advanced Industry Facility 2491: 0.0, // Ice Advanced Industry Facility
2492: 0.0, // Gas Basic Industry Facility 2492: 0.0, // Gas Basic Industry Facility
2493: 0.0, // Ice Basic Industry Facility 2493: 0.0, // Ice Basic Industry Facility
2494: 0.0, // Gas Advanced Industry Facility 2494: 0.0, // Gas Advanced Industry Facility
2535: 0.0, // Oceanic Storage Facility 2535: 0.0, // Oceanic Storage Facility
2536: 0.0, // Gas Storage Facility 2536: 0.0, // Gas Storage Facility
2541: 0.0, // Barren Storage Facility 2541: 0.0, // Barren Storage Facility
2542: 0.0, // Oceanic Launchpad 2542: 0.0, // Oceanic Launchpad
2543: 0.0, // Gas Launchpad 2543: 0.0, // Gas Launchpad
2544: 0.0, // Barren Launchpad 2544: 0.0, // Barren Launchpad
2552: 0.0, // Ice Launchpad 2552: 0.0, // Ice Launchpad
2555: 0.0, // Lava Launchpad 2555: 0.0, // Lava Launchpad
2556: 0.0, // Plasma Launchpad 2556: 0.0, // Plasma Launchpad
2557: 0.0, // Storm Launchpad 2557: 0.0, // Storm Launchpad
2558: 0.0, // Lava Storage Facility 2558: 0.0, // Lava Storage Facility
2560: 0.0, // Plasma Storage Facility 2560: 0.0, // Plasma Storage Facility
2561: 0.0, // Storm Storage Facility 2561: 0.0, // Storm Storage Facility
2562: 0.0, // Temperate Storage Facility 2562: 0.0, // Temperate Storage Facility
2848: 0.0, // Barren Extractor Control Unit 2848: 0.0, // Barren Extractor Control Unit
2867: 50.0, // Broadcast Node 2867: 50.0, // Broadcast Node
2868: 50.0, // Integrity Response Drones 2868: 50.0, // Integrity Response Drones
2869: 50.0, // Nano-Factory 2869: 50.0, // Nano-Factory
2870: 50.0, // Organic Mortar Applicators 2870: 50.0, // Organic Mortar Applicators
2871: 50.0, // Recursive Computing Module 2871: 50.0, // Recursive Computing Module
2872: 50.0, // Self-Harmonizing Power Core 2872: 50.0, // Self-Harmonizing Power Core
2875: 50.0, // Sterile Conduits 2875: 50.0, // Sterile Conduits
2876: 50.0, // Wetware Mainframe 2876: 50.0, // Wetware Mainframe
3060: 0.0, // Gas Extractor Control Unit 3060: 0.0, // Gas Extractor Control Unit
3061: 0.0, // Ice Extractor Control Unit 3061: 0.0, // Ice Extractor Control Unit
3062: 0.0, // Lava Extractor Control Unit 3062: 0.0, // Lava Extractor Control Unit
3063: 0.0, // Oceanic Extractor Control Unit 3063: 0.0, // Oceanic Extractor Control Unit
3064: 0.0, // Plasma Extractor Control Unit 3064: 0.0, // Plasma Extractor Control Unit
3067: 0.0, // Storm Extractor Control Unit 3067: 0.0, // Storm Extractor Control Unit
3068: 0.0, // Temperate Extractor Control Unit 3068: 0.0, // Temperate Extractor Control Unit
3645: 0.19, // Water 3645: 0.19, // Water
3683: 0.19, // Oxygen 3683: 0.19, // Oxygen
3689: 0.75, // Mechanical Parts 3689: 0.75, // Mechanical Parts
3691: 0.75, // Synthetic Oil 3691: 0.75, // Synthetic Oil
3693: 0.75, // Fertilizer 3693: 0.75, // Fertilizer
3695: 0.75, // Polytextiles 3695: 0.75, // Polytextiles
3697: 0.75, // Silicate Glass 3697: 0.75, // Silicate Glass
3725: 0.75, // Livestock 3725: 0.75, // Livestock
3775: 0.75, // Viral Agent 3775: 0.75, // Viral Agent
3779: 0.19, // Biomass 3779: 0.19, // Biomass
3828: 0.75, // Construction Blocks 3828: 0.75, // Construction Blocks
9828: 0.19, // Silicon 9828: 0.19, // Silicon
9830: 0.75, // Rocket Fuel 9830: 0.75, // Rocket Fuel
9832: 0.75, // Coolant 9832: 0.75, // Coolant
9834: 3.0, // Guidance Systems 9834: 3.0, // Guidance Systems
9836: 0.75, // Consumer Electronics 9836: 0.75, // Consumer Electronics
9838: 0.75, // Superconductors 9838: 0.75, // Superconductors
9840: 0.75, // Transmitter 9840: 0.75, // Transmitter
9842: 0.75, // Miniature Electronics 9842: 0.75, // Miniature Electronics
9846: 3.0, // Planetary Vehicles 9846: 3.0, // Planetary Vehicles
9848: 3.0, // Robotics 9848: 3.0, // Robotics
12836: 3.0, // Transcranial Microcontrollers 12836: 3.0, // Transcranial Microcontrollers
15317: 0.75, // Genetically Enhanced Livestock 15317: 0.75, // Genetically Enhanced Livestock
17136: 3.0, // Ukomi Superconductors 17136: 3.0, // Ukomi Superconductors
17392: 3.0, // Data Chips 17392: 3.0, // Data Chips
17898: 3.0, // High-Tech Transmitters 17898: 3.0, // High-Tech Transmitters
28974: 3.0, // Vaccines 28974: 3.0, // Vaccines
} }
var StorageCapacities = map[int]int{ var StorageCapacities = map[int64]int{
2257: 12000, // Ice Storage Facility 2257: 12000, // Ice Storage Facility
2535: 12000, // Oceanic Storage Facility 2535: 12000, // Oceanic Storage Facility
2536: 12000, // Gas Storage Facility 2536: 12000, // Gas Storage Facility
@@ -422,7 +422,7 @@ var StorageCapacities = map[int]int{
2557: 10000, // Storm Launchpad 2557: 10000, // Storm Launchpad
} }
var LaunchpadIDs = map[int]struct{}{ var LaunchpadIDs = map[int64]struct{}{
2256: {}, 2256: {},
2542: {}, 2542: {},
2543: {}, 2543: {},

View File

@@ -2,8 +2,6 @@ package esi
import ( import (
"context" "context"
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
@@ -16,14 +14,14 @@ import (
) )
type CacheDB interface { type CacheDB interface {
GetCacheEntry(urlHash string) (*types.CacheEntry, error) GetCacheEntry(hash string) (*types.CacheEntry, error)
SaveCacheEntry(entry *types.CacheEntry) error SaveCacheEntry(entry *types.CacheEntry) error
} }
// CachedESI implements ESIInterface with caching // CachedESI implements ESIInterface with caching
type CachedESI struct { type CachedESI struct {
direct ESIInterface direct ESIInterface
db CacheDB db CacheDB
cacheValidity time.Duration cacheValidity time.Duration
} }
@@ -47,135 +45,127 @@ func NewCachedESI(direct ESIInterface, db CacheDB) *CachedESI {
// GetPlanetPI retrieves detailed information about a specific planet with caching // 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) { 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 := logger.Default.WithPrefix("CachedESI.GetPlanetPI").WithPrefix(fmt.Sprintf("characterID=%d", characterID)).WithPrefix(fmt.Sprintf("planetID=%d", planetID))
url := fmt.Sprintf("/v3/characters/%d/planets/%d/", characterID, planetID)
funclog.Info("Starting") funclog.Info("Starting")
fetchFunc := func() (*PlanetPI, error) { hash := fmt.Sprintf("GetPlanetPI-%d-%d", characterID, planetID)
funclog.Info("Calling direct ESI") cached, err := c.db.GetCacheEntry(hash)
result, err := c.direct.GetPlanetPI(ctx, characterID, planetID, accessToken) if err != nil {
if err != nil { funclog.Warning("Failed to get cache entry: %v", err)
funclog.Error("Direct ESI call failed: %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 nil, err
} }
if result != nil { return &planetPI, nil
funclog.Info("Direct ESI returned planet details with %d pins", len(result.Pins))
} else {
funclog.Info("Direct ESI returned nil planet details")
}
return result, nil
} }
funclog.Info("Cache miss for GetPlanetPI-%d-%d", characterID, planetID)
result, err := getCachedResponse(c, url, fetchFunc) apiresp, err := c.direct.GetPlanetPI(ctx, characterID, planetID, accessToken)
if err != nil { if err != nil {
funclog.Error("Failed to get planet details: %v", err) funclog.Error("Failed to get planet PI: %v", err)
return nil, err return nil, err
} }
apirespjson, err := json.Marshal(apiresp)
if result != nil {
funclog.Info("Successfully retrieved planet details with %d pins", len(result.Pins))
} else {
funclog.Info("Successfully retrieved nil planet details")
}
return result, nil
}
// getCachedResponse handles caching logic with generics
func getCachedResponse[T any](c *CachedESI, url string, fetchFunc func() (T, error)) (T, error) {
funclog := logger.Default.WithPrefix("getCachedResponse").WithPrefix(fmt.Sprintf("url=%s", url))
// Generate cache key
hash := sha256.Sum256([]byte(url))
urlHash := hex.EncodeToString(hash[:])
funclog.Debug("Generated cache key %s for URL: %s", urlHash[:8], url)
// Check cache using the interface
funclog.Debug("Checking cache for URL: %s", url)
cacheEntry, err := c.db.GetCacheEntry(urlHash)
if err == nil {
// Check if cache is still valid
age := time.Since(cacheEntry.CachedAt)
if age < c.cacheValidity {
funclog.Info("Cache HIT for URL: %s (cached %v ago, valid for %v more)", url, age, c.cacheValidity-age)
// Parse cached response
var result T
if err := json.Unmarshal([]byte(cacheEntry.Response), &result); err == nil {
funclog.Debug("Successfully unmarshaled cached response for URL: %s", url)
return result, nil
}
funclog.Warning("Failed to unmarshal cached response for URL: %s: %v", url, err)
} else {
logger.Info("Cache EXPIRED for URL: %s (cached %v ago, validity: %v)", url, age, c.cacheValidity)
}
} else {
funclog.Info("Cache MISS for URL: %s (no cache entry found): %v", url, err)
}
// Cache miss or invalid, fetch from API
funclog.Info("Fetching from API for URL: %s", url)
result, err := fetchFunc()
if err != nil { if err != nil {
funclog.Error("API fetch failed for URL: %s: %v", url, err) funclog.Error("Failed to marshal planet PI: %v", err)
var zero T return nil, err
return zero, err
} }
funclog.Debug("API fetch successful for URL: %s", url) cacheEntry := &types.CacheEntry{
Hash: hash,
// Store in cache Data: string(apirespjson),
funclog.Debug("Marshaling response for caching URL: %s", url)
responseBytes, err := json.Marshal(result)
if err != nil {
funclog.Warning("Failed to marshal response for caching URL: %s: %v", url, err)
return result, nil
}
funclog.Debug("Response marshaled successfully for URL: %s (%d bytes)", url, len(responseBytes))
cacheEntry = &types.CacheEntry{
URLHash: urlHash,
Response: string(responseBytes),
CachedAt: time.Now(), CachedAt: time.Now(),
} }
err = c.db.SaveCacheEntry(cacheEntry)
funclog.Debug("Saving cache entry for URL: %s", url) if err != nil {
if err := c.db.SaveCacheEntry(cacheEntry); err != nil { funclog.Warning("Failed to save cache entry: %v", err)
funclog.Warning("Failed to cache response for URL: %s: %v", url, err)
} else {
funclog.Info("Cached response for URL: %s (valid for %v, %d bytes)", url, c.cacheValidity, len(responseBytes))
} }
return apiresp, nil
return result, nil
} }
// GetPlanetName retrieves planet name with caching // GetPlanetName retrieves planet name with caching
func (c *CachedESI) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error) { func (c *CachedESI) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error) {
funclog := logger.Default.WithPrefix("CachedESI.GetPlanetName").WithPrefix(fmt.Sprintf("planetID=%d", planetID)) funclog := logger.Default.WithPrefix("CachedESI.GetPlanetName").WithPrefix(fmt.Sprintf("planetID=%d", planetID))
url := fmt.Sprintf("/v1/universe/planets/%d/", planetID)
funclog.Info("Starting") funclog.Info("Starting")
fetchFunc := func() (*PlanetName, error) { hash := fmt.Sprintf("GetPlanetName-%d", planetID)
funclog.Info("Calling direct ESI for planet %d name", planetID) cached, err := c.db.GetCacheEntry(hash)
result, err := c.direct.GetPlanetName(ctx, planetID) if err != nil {
if err != nil { funclog.Warning("Failed to get cache entry: %v", err)
funclog.Error("Direct ESI call failed for planet %d: %v", planetID, 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 nil, err
} }
if result != nil { return &planetPI, nil
funclog.Info("Direct ESI returned planet name '%s' for planet %d", result.Name, planetID)
} else {
funclog.Info("Direct ESI returned nil planet name for planet %d", planetID)
}
return result, nil
} }
funclog.Info("Cache miss for GetPlanetName-%d", planetID)
result, err := getCachedResponse(c, url, fetchFunc) apiresp, err := c.direct.GetPlanetName(ctx, planetID)
if err != nil { if err != nil {
funclog.Error("Failed to get planet name for planet %d: %v", planetID, err) funclog.Error("Failed to get planet name: %v", err)
return nil, err return nil, err
} }
apirespjson, err := json.Marshal(apiresp)
if result != nil { if err != nil {
funclog.Info("Successfully retrieved planet name '%s' for planet %d", result.Name, planetID) funclog.Error("Failed to marshal planet name: %v", err)
} else { return nil, err
funclog.Info("Successfully retrieved nil planet name for planet %d", planetID)
} }
return result, nil 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{}

View File

@@ -20,69 +20,6 @@ const (
ESIBaseURL = "https://esi.evetech.net" ESIBaseURL = "https://esi.evetech.net"
) )
type Planet struct {
PlanetID int `json:"planet_id"`
PlanetType string `json:"planet_type"`
SolarSystemID int `json:"solar_system_id"`
UpgradeLevel int `json:"upgrade_level"`
NumPins int `json:"num_pins"`
LastUpdate string `json:"last_update"`
OwnerID int `json:"owner_id"`
PlanetName string `json:"planet_name"`
PlanetTypeID int `json:"planet_type_id"`
Position struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
} `json:"position"`
}
type PlanetDetail struct {
Links []Link `json:"links"`
Pins []Pin `json:"pins"`
Routes []Route `json:"routes"`
LastUpdate string `json:"last_update"`
}
type Link struct {
DestinationPinID int `json:"destination_pin_id"`
LinkLevel int `json:"link_level"`
SourcePinID int `json:"source_pin_id"`
}
type Pin struct {
Contents []StorageContent `json:"contents"`
ExpiryTime *string `json:"expiry_time"`
ExtractorDetails *ExtractorDetails `json:"extractor_details"`
FactoryDetails *FactoryDetails `json:"factory_details"`
InstallTime *string `json:"install_time"`
LastCycleStart *string `json:"last_cycle_start"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
PinID int `json:"pin_id"`
SchematicID *int `json:"schematic_id"`
TypeID int `json:"type_id"`
}
type StorageContent struct {
Amount int `json:"amount"`
TypeID int `json:"type_id"`
}
type ExtractorDetails struct {
CycleTime *int `json:"cycle_time"`
HeadRadius *float64 `json:"head_radius"`
Heads []Head `json:"heads"`
ProductTypeID *int `json:"product_type_id"`
QtyPerCycle *int `json:"qty_per_cycle"`
}
type Head struct {
HeadID int `json:"head_id"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
type PlanetName struct { type PlanetName struct {
Name string `json:"name"` Name string `json:"name"`
PlanetID int `json:"planet_id"` PlanetID int `json:"planet_id"`
@@ -99,20 +36,11 @@ type FactoryDetails struct {
SchematicID int `json:"schematic_id"` SchematicID int `json:"schematic_id"`
} }
type Route struct {
ContentTypeID int `json:"content_type_id"`
DestinationPinID int `json:"destination_pin_id"`
Quantity float64 `json:"quantity"`
RouteID int `json:"route_id"`
SourcePinID int `json:"source_pin_id"`
Waypoints []int `json:"waypoints"`
}
// ESIInterface defines the contract for ESI API interactions // ESIInterface defines the contract for ESI API interactions
type ESIInterface interface { type ESIInterface interface {
GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error)
GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error) GetPlanetPI(ctx context.Context, characterID int, planetID int64, accessToken string) (*PlanetPI, error)
GetPlanetName(ctx context.Context, planetID int) (*PlanetName, error) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error)
} }
// DirectESI implements ESIInterface with direct API calls // DirectESI implements ESIInterface with direct API calls
@@ -138,13 +66,14 @@ func NewDirectESI() *DirectESI {
// GetCharacterPlanets retrieves a list of planets for a character // GetCharacterPlanets retrieves a list of planets for a character
func (d *DirectESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) { func (d *DirectESI) GetCharacterPlanets(ctx context.Context, characterID int, accessToken string) ([]Planet, error) {
logger.Debug("Fetching planets for character ID %d", characterID) funclog := logger.Default.WithPrefix("DirectESI.GetCharacterPlanets").WithPrefix(fmt.Sprintf("characterID=%d", characterID))
funclog.Debug("Fetching planets")
url := fmt.Sprintf("%s/v1/characters/%d/planets/", ESIBaseURL, characterID) url := fmt.Sprintf("%s/v3/characters/%d/planets/", ESIBaseURL, characterID)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
logger.Error("Failed to create request for character planets: %v", err) funclog.Error("Failed to create request for character planets: %v", err)
return nil, err return nil, err
} }
@@ -153,36 +82,37 @@ func (d *DirectESI) GetCharacterPlanets(ctx context.Context, characterID int, ac
resp, err := d.httpClient.Do(req) resp, err := d.httpClient.Do(req)
if err != nil { if err != nil {
logger.Error("Failed to fetch character planets: %v", err) funclog.Error("Failed to fetch character planets: %v", err)
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
logger.Error("Character planets API returned status %d: %s", resp.StatusCode, string(body)) funclog.Error("Character planets API returned status %d: %s", resp.StatusCode, string(body))
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
} }
var planets []Planet var planets []Planet
if err := json.NewDecoder(resp.Body).Decode(&planets); err != nil { if err := json.NewDecoder(resp.Body).Decode(&planets); err != nil {
logger.Error("Failed to decode character planets response: %v", err) funclog.Error("Failed to decode character planets response: %v", err)
return nil, err return nil, err
} }
logger.Info("Successfully fetched %d planets for character ID %d", len(planets), characterID) funclog.Info("Successfully fetched %d planets", len(planets))
return planets, nil return planets, nil
} }
// GetPlanetDetails retrieves detailed information about a specific planet // GetPlanetPI retrieves detailed information about a specific planet
func (d *DirectESI) GetPlanetDetails(ctx context.Context, characterID, planetID int, accessToken string) (*PlanetDetail, error) { func (d *DirectESI) GetPlanetPI(ctx context.Context, characterID int, planetID int64, accessToken string) (*PlanetPI, error) {
logger.Debug("Fetching planet details for character ID %d, planet ID %d", characterID, planetID) funclog := logger.Default.WithPrefix("DirectESI.GetPlanetPI").WithPrefix(fmt.Sprintf("characterID=%d", characterID)).WithPrefix(fmt.Sprintf("planetID=%d", planetID))
funclog.Debug("Fetching planet details")
url := fmt.Sprintf("%s/v3/characters/%d/planets/%d/", ESIBaseURL, characterID, planetID) url := fmt.Sprintf("%s/v3/characters/%d/planets/%d/", ESIBaseURL, characterID, planetID)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
logger.Error("Failed to create request for planet details: %v", err) funclog.Error("Failed to create request for planet details: %v", err)
return nil, err return nil, err
} }
@@ -191,36 +121,37 @@ func (d *DirectESI) GetPlanetDetails(ctx context.Context, characterID, planetID
resp, err := d.httpClient.Do(req) resp, err := d.httpClient.Do(req)
if err != nil { if err != nil {
logger.Error("Failed to fetch planet details: %v", err) funclog.Error("Failed to fetch planet details: %v", err)
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body) body, _ := io.ReadAll(resp.Body)
logger.Error("Planet details API returned status %d: %s", resp.StatusCode, string(body)) funclog.Error("Planet details API returned status %d: %s", resp.StatusCode, string(body))
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
} }
var planetDetail PlanetDetail var pi PlanetPI
if err := json.NewDecoder(resp.Body).Decode(&planetDetail); err != nil { if err := json.NewDecoder(resp.Body).Decode(&pi); err != nil {
logger.Error("Failed to decode planet details response: %v", err) funclog.Error("Failed to decode planet details response: %v", err)
return nil, err return nil, err
} }
logger.Info("Successfully fetched planet details for character ID %d, planet ID %d", characterID, planetID) funclog.Info("Successfully fetched planet details")
return &planetDetail, nil return &pi, nil
} }
// GetPlanetName retrieves planet name from universe endpoint // GetPlanetName retrieves planet name from universe endpoint
func (d *DirectESI) GetPlanetName(ctx context.Context, planetID int) (*PlanetName, error) { func (d *DirectESI) GetPlanetName(ctx context.Context, planetID int64) (*PlanetName, error) {
logger.Debug("Fetching planet name for planet ID %d", planetID) funclog := logger.Default.WithPrefix("DirectESI.GetPlanetName").WithPrefix(fmt.Sprintf("planetID=%d", planetID))
funclog.Debug("Fetching planet name")
url := fmt.Sprintf("%s/v1/universe/planets/%d/", ESIBaseURL, planetID) url := fmt.Sprintf("%s/v1/universe/planets/%d/", ESIBaseURL, planetID)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
logger.Error("Failed to create request for planet name: %v", err) funclog.Error("Failed to create request for planet name: %v", err)
return nil, err return nil, err
} }
@@ -228,28 +159,30 @@ func (d *DirectESI) GetPlanetName(ctx context.Context, planetID int) (*PlanetNam
resp, err := d.httpClient.Do(req) resp, err := d.httpClient.Do(req)
if err != nil { if err != nil {
logger.Error("Failed to fetch planet name: %v", err) funclog.Error("Failed to fetch planet name: %v", err)
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
logger.Error("Failed to read planet name response body: %v", err) funclog.Error("Failed to read planet name response body: %v", err)
return nil, err return nil, err
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
logger.Error("API request failed with status %d: %s", resp.StatusCode, string(body)) funclog.Error("API request failed with status %d: %s", resp.StatusCode, string(body))
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body))
} }
var planetName PlanetName var planetName PlanetName
if err := json.NewDecoder(bytes.NewReader(body)).Decode(&planetName); err != nil { if err := json.NewDecoder(bytes.NewReader(body)).Decode(&planetName); err != nil {
logger.Error("Failed to decode planet name response: %v", err) funclog.Error("Failed to decode planet name response: %v", err)
return nil, err return nil, err
} }
logger.Info("Successfully fetched planet name for planet ID %d: %s", planetID, planetName.Name) funclog.Info("Successfully fetched planet name: %s", planetName.Name)
return &planetName, nil return &planetName, nil
} }
var _ ESIInterface = &DirectESI{}

View File

@@ -32,7 +32,7 @@ type Content struct {
type Route struct { type Route struct {
ContentTypeID int64 `json:"content_type_id"` ContentTypeID int64 `json:"content_type_id"`
DestinationPinID int64 `json:"destination_pin_id"` DestinationPinID int64 `json:"destination_pin_id"`
Quantity int64 `json:"quantity"` Quantity float64 `json:"quantity"`
RouteID int64 `json:"route_id"` RouteID int64 `json:"route_id"`
SourcePinID int64 `json:"source_pin_id"` SourcePinID int64 `json:"source_pin_id"`
Waypoints []int64 `json:"waypoints"` Waypoints []int64 `json:"waypoints"`

View File

@@ -1,6 +1,7 @@
package repositories package repositories
import ( import (
"fmt"
"go-eve-pi/types" "go-eve-pi/types"
logger "git.site.quack-lab.dev/dave/cylogger" logger "git.site.quack-lab.dev/dave/cylogger"
@@ -21,25 +22,27 @@ func NewCacheRepository(db *gorm.DB) *CacheRepository {
} }
// GetCacheEntry retrieves a cache entry by URL hash // GetCacheEntry retrieves a cache entry by URL hash
func (r *CacheRepository) GetCacheEntry(urlHash string) (*types.CacheEntry, error) { func (r *CacheRepository) GetCacheEntry(hash string) (*types.CacheEntry, error) {
logger.Debug("Fetching cache entry for hash: %s", urlHash) funclog := logger.Default.WithPrefix("CacheRepository.GetCacheEntry").WithPrefix(fmt.Sprintf("hash=%s", hash))
funclog.Info("Starting")
var entry types.CacheEntry var entry types.CacheEntry
err := r.db.Where("url_hash = ?", urlHash).First(&entry).Error err := r.db.Where("hash = ?", hash).First(&entry).Error
if err != nil { if err != nil {
logger.Debug("No cache entry found for hash %s: %v", urlHash, err) funclog.Debug("No cache entry found: %v", err)
return nil, err return nil, err
} }
logger.Debug("Cache entry found for hash %s", urlHash) funclog.Debug("Cache entry found")
return &entry, nil return &entry, nil
} }
// SaveCacheEntry saves a cache entry to the database // SaveCacheEntry saves a cache entry to the database
func (r *CacheRepository) SaveCacheEntry(entry *types.CacheEntry) error { func (r *CacheRepository) SaveCacheEntry(entry *types.CacheEntry) error {
logger.Debug("Saving cache entry for hash: %s", entry.URLHash) funclog := logger.Default.WithPrefix("CacheRepository.SaveCacheEntry").WithPrefix(fmt.Sprintf("hash=%s", entry.Hash))
funclog.Info("Starting")
err := r.db.Save(entry).Error err := r.db.Save(entry).Error
if err != nil { if err != nil {
return err return err
} }
logger.Debug("Cache entry saved successfully for hash %s", entry.URLHash) funclog.Debug("Cache entry saved successfully")
return nil return nil
} }

View File

@@ -13,10 +13,9 @@ type Character struct {
CreatedAt time.Time CreatedAt time.Time
} }
// CacheEntry represents a cached API response
type CacheEntry struct { type CacheEntry struct {
ID uint `gorm:"primaryKey"` ID uint `gorm:"primaryKey"`
URLHash string `gorm:"uniqueIndex"` Hash string `gorm:"uniqueIndex"`
Response string `gorm:"type:text"` Data string `gorm:"type:text"`
CachedAt time.Time `gorm:"index"` CachedAt time.Time `gorm:"index"`
} }