package routes import ( "context" "encoding/json" "strconv" "go-eve-pi/esi" "go-eve-pi/types" wh "go-eve-pi/webhook" logger "git.site.quack-lab.dev/dave/cylogger" "github.com/fasthttp/router" "github.com/valyala/fasthttp" ) // RouteHandler contains dependencies needed for route handlers type RouteHandler struct { SSO SSOInterface Webhook wh.Webhook ESI esi.ESIInterface } // SSOInterface defines the interface for SSO operations type SSOInterface interface { GetCharacter(ctx context.Context, characterName string) (types.Character, error) } // NewRouteHandler creates a new route handler with dependencies func NewRouteHandler(sso SSOInterface, webhook wh.Webhook, esi esi.ESIInterface) *RouteHandler { return &RouteHandler{ SSO: sso, Webhook: webhook, ESI: esi, } } // SetupRoutes configures all routes on the given router func (rh *RouteHandler) SetupRoutes(r *router.Router) { // Root route r.GET("/", rh.handleRoot) // Authentication route r.GET("/login/{character}", rh.handleLogin) // Planet routes r.GET("/planets/{character}", rh.handlePlanets) r.GET("/planet/{character}/{planet_id}", rh.handlePlanetDetails) r.GET("/planets-full/{character}", rh.handlePlanetsFull) // New endpoints r.GET("/extractors/{characterName}", rh.handleGetExtractors) r.GET("/storage/{characterName}", rh.handleGetStorage) } // handleRoot handles the root endpoint func (rh *RouteHandler) handleRoot(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(fasthttp.StatusOK) ctx.WriteString("EVE PI Server Running") } // handleLogin handles character authentication func (rh *RouteHandler) handleLogin(ctx *fasthttp.RequestCtx) { charName := ctx.UserValue("character") charNameStr, ok := charName.(string) if !ok || charNameStr == "" { ctx.SetStatusCode(fasthttp.StatusBadRequest) ctx.WriteString("Missing character parameter") return } logger.Info("Login requested for character %s", charNameStr) // Trigger the auth flow (will register callback if needed) char, err := rh.SSO.GetCharacter(context.Background(), charNameStr) if err != nil { logger.Error("Failed to authenticate character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Authentication failed") return } logger.Info("Successfully authenticated character %s", charNameStr) ctx.SetContentType("text/plain") ctx.SetStatusCode(fasthttp.StatusOK) ctx.WriteString("Authenticated! Access token: " + char.AccessToken) } // handlePlanets handles fetching planets for a character func (rh *RouteHandler) handlePlanets(ctx *fasthttp.RequestCtx) { charName := ctx.UserValue("character") charNameStr, ok := charName.(string) if !ok || charNameStr == "" { ctx.SetStatusCode(fasthttp.StatusBadRequest) ctx.WriteString("Missing character parameter") return } logger.Info("Fetching planets for character %s", charNameStr) // Get access token for character char, err := rh.SSO.GetCharacter(context.Background(), charNameStr) if err != nil { logger.Error("Failed to get token for character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Authentication failed") return } // Fetch planets using ESI API planets, err := rh.ESI.GetCharacterPlanets(context.Background(), int(char.ID), char.AccessToken) if err != nil { logger.Error("Failed to fetch planets for character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Failed to fetch planets") return } // Return planets as JSON ctx.SetContentType("application/json") ctx.SetStatusCode(fasthttp.StatusOK) json.NewEncoder(ctx).Encode(planets) } // handlePlanetDetails handles fetching detailed planet information func (rh *RouteHandler) handlePlanetDetails(ctx *fasthttp.RequestCtx) { charName := ctx.UserValue("character") charNameStr, ok := charName.(string) if !ok || charNameStr == "" { ctx.SetStatusCode(fasthttp.StatusBadRequest) ctx.WriteString("Missing character parameter") return } planetIDStr := ctx.UserValue("planet_id") planetID, err := strconv.Atoi(planetIDStr.(string)) if err != nil { ctx.SetStatusCode(fasthttp.StatusBadRequest) ctx.WriteString("Invalid planet ID") return } logger.Info("Fetching planet details for character %s, planet %d", charNameStr, planetID) // Get access token for character char, err := rh.SSO.GetCharacter(context.Background(), charNameStr) if err != nil { logger.Error("Failed to get token for character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Authentication failed") return } // Fetch planet details using ESI API planetDetail, err := rh.ESI.GetPlanetDetails(context.Background(), int(char.ID), planetID, char.AccessToken) if err != nil { logger.Error("Failed to fetch planet details for character %s, planet %d: %v", charNameStr, planetID, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Failed to fetch planet details") return } // Return planet details as JSON ctx.SetContentType("application/json") ctx.SetStatusCode(fasthttp.StatusOK) json.NewEncoder(ctx).Encode(planetDetail) } // handlePlanetsFull handles fetching all planets with their details for a character func (rh *RouteHandler) handlePlanetsFull(ctx *fasthttp.RequestCtx) { charName := ctx.UserValue("character") charNameStr, ok := charName.(string) if !ok || charNameStr == "" { ctx.SetStatusCode(fasthttp.StatusBadRequest) ctx.WriteString("Missing character parameter") return } logger.Info("Fetching full planet data for character %s", charNameStr) // Get access token for character char, err := rh.SSO.GetCharacter(context.Background(), charNameStr) if err != nil { logger.Error("Failed to get token for character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Authentication failed") return } // Fetch planets list planets, err := rh.ESI.GetCharacterPlanets(context.Background(), int(char.ID), char.AccessToken) if err != nil { logger.Error("Failed to fetch planets for character %s: %v", charNameStr, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.WriteString("Failed to fetch planets") return } // Fetch details for each planet type PlanetWithDetails struct { Planet esi.Planet Details *esi.PlanetDetail } var planetsWithDetails []PlanetWithDetails for _, planet := range planets { details, err := rh.ESI.GetPlanetDetails(context.Background(), int(char.ID), planet.PlanetID, char.AccessToken) if err != nil { logger.Warning("Failed to fetch details for planet %d: %v", planet.PlanetID, err) // Continue with other planets even if one fails details = nil } planetsWithDetails = append(planetsWithDetails, PlanetWithDetails{ Planet: planet, Details: details, }) } // Return planets with details as JSON ctx.SetContentType("application/json") ctx.SetStatusCode(fasthttp.StatusOK) json.NewEncoder(ctx).Encode(planetsWithDetails) } // ExtractorInfo represents extractor information type ExtractorInfo struct { PlanetName string `json:"planet_name"` ExtractorNumber int `json:"extractor_number"` ExpiryDate string `json:"expiry_date"` } // StorageInfo represents storage information type StorageInfo struct { PlanetName string `json:"planet_name"` StorageType string `json:"storage_type"` Utilization float64 `json:"utilization"` } // handleGetExtractors returns extractor information for a character func (rh *RouteHandler) handleGetExtractors(ctx *fasthttp.RequestCtx) { characterName := ctx.UserValue("characterName").(string) logger.Info("Getting extractors for character: %s", characterName) // Get character info char, err := rh.SSO.GetCharacter(context.Background(), characterName) if err != nil { logger.Error("Failed to get character %s: %v", characterName, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get character") return } // Get planets planets, err := rh.ESI.GetCharacterPlanets(context.Background(), int(char.ID), char.AccessToken) if err != nil { logger.Error("Failed to get planets for character %s: %v", characterName, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get planets") return } var extractors []ExtractorInfo // Get details for each planet and extract extractor info for _, planet := range planets { details, err := rh.ESI.GetPlanetDetails(context.Background(), int(char.ID), planet.PlanetID, char.AccessToken) if err != nil { logger.Warning("Failed to fetch details for planet %d: %v", planet.PlanetID, err) continue } if details != nil { // Get planet name from universe endpoint planetNameData, err := rh.ESI.GetPlanetName(context.Background(), planet.PlanetID) if err != nil { logger.Error("Failed to get planet name for planet ID %d: %v", planet.PlanetID, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get planet name") return } // Count extractors and get expiry dates extractorCount := 0 for _, pin := range details.Pins { if pin.ExtractorDetails != nil { extractorCount++ expiryDate := "N/A" if pin.ExpiryTime != nil { expiryDate = *pin.ExpiryTime } extractors = append(extractors, ExtractorInfo{ PlanetName: planetNameData.Name, ExtractorNumber: extractorCount, ExpiryDate: expiryDate, }) } } } } // Return extractors as JSON ctx.SetContentType("application/json") ctx.SetStatusCode(fasthttp.StatusOK) json.NewEncoder(ctx).Encode(extractors) } // handleGetStorage returns storage information for a character func (rh *RouteHandler) handleGetStorage(ctx *fasthttp.RequestCtx) { characterName := ctx.UserValue("characterName").(string) logger.Info("Getting storage for character: %s", characterName) // Get character info char, err := rh.SSO.GetCharacter(context.Background(), characterName) if err != nil { logger.Error("Failed to get character %s: %v", characterName, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get character") return } // Get planets planets, err := rh.ESI.GetCharacterPlanets(context.Background(), int(char.ID), char.AccessToken) if err != nil { logger.Error("Failed to get planets for character %s: %v", characterName, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get planets") return } var storage []StorageInfo // Get details for each planet and extract storage info for _, planet := range planets { details, err := rh.ESI.GetPlanetDetails(context.Background(), int(char.ID), planet.PlanetID, char.AccessToken) if err != nil { logger.Warning("Failed to fetch details for planet %d: %v", planet.PlanetID, err) continue } if details != nil { // Get planet name from universe endpoint planetNameData, err := rh.ESI.GetPlanetName(context.Background(), planet.PlanetID) if err != nil { logger.Error("Failed to get planet name for planet ID %d: %v", planet.PlanetID, err) ctx.SetStatusCode(fasthttp.StatusInternalServerError) ctx.SetBodyString("Failed to get planet name") return } // Analyze storage utilization for _, pin := range details.Pins { if len(pin.Contents) > 0 { // Calculate utilization based on contents totalAmount := 0 for _, content := range pin.Contents { totalAmount += content.Amount } // Determine storage type based on pin type storageType := "Unknown" switch pin.TypeID { case 2254, 2255, 2256: // Launch pads storageType = "Launch Pad" case 2524, 2525, 2526: // Storage facilities storageType = "Storage" } // Calculate utilization percentage (assuming max capacity of 10000) utilization := float64(totalAmount) / 10000.0 * 100.0 if utilization > 100.0 { utilization = 100.0 } storage = append(storage, StorageInfo{ PlanetName: planetNameData.Name, StorageType: storageType, Utilization: utilization, }) } } } } // Return storage as JSON ctx.SetContentType("application/json") ctx.SetStatusCode(fasthttp.StatusOK) json.NewEncoder(ctx).Encode(storage) }