package main import ( "context" "flag" "fmt" "net/http" "os" "os/signal" "strings" logger "git.site.quack-lab.dev/dave/cylogger" "github.com/joho/godotenv" ) type Options struct { ClientID string RedirectURI string Scopes []string DBPath string } var options Options func main() { flag.Parse() logger.InitFlag() logger.Info("Starting Eve PI") var err error options, err = LoadOptions() if err != nil { logger.Error("Failed to load options %v", err) return } // Create SSO instance sso, err := NewSSO( options.ClientID, options.RedirectURI, options.Scopes, ) if err != nil { logger.Error("Failed to create SSO instance %v", err) return } // Setup HTTP server mux := http.NewServeMux() // Configure SSO to use existing muxer sso.SetMuxer(mux) // Add your own routes mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("EVE PI Server Running")) }) server := &http.Server{ Addr: ":3000", Handler: mux, } logger.Info("Starting web server on :3000") go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Error("Server failed: %v", err) } }() mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) { charName := r.URL.Query().Get("character") if charName == "" { http.Error(w, "Missing character parameter", http.StatusBadRequest) return } logger.Info("Login requested for character %s", charName) // Trigger the auth flow (will register callback if needed) token, err := sso.GetToken(r.Context(), charName) if err != nil { logger.Error("Failed to authenticate character %s: %v", charName, err) http.Error(w, "Authentication failed", http.StatusInternalServerError) return } logger.Info("Successfully authenticated character %s", charName) w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte("Authenticated! Access token: " + token)) }) // Listen for SIGINT and gracefully shut down the server sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) <-sigCh logger.Info("SIGINT received, shutting down web server gracefully...") if err := server.Shutdown(context.Background()); err != nil { logger.Error("Error shutting down server: %v", err) } else { logger.Info("Web server shut down cleanly") } } func LoadOptions() (Options, error) { // Load environment variables strictly from .env file (fail if there's an error loading) if err := godotenv.Load(); err != nil { return Options{}, fmt.Errorf("error loading .env file: %w", err) } clientID := os.Getenv("ESI_CLIENT_ID") if clientID == "" { return Options{}, fmt.Errorf("ESI_CLIENT_ID is required in .env file") } redirectURI := os.Getenv("ESI_REDIRECT_URI") if redirectURI == "" { return Options{}, fmt.Errorf("ESI_REDIRECT_URI is required in .env file") } rawScopes := os.Getenv("ESI_SCOPES") if rawScopes == "" { return Options{}, fmt.Errorf("ESI_SCOPES is required in .env file") } scopes := strings.Fields(rawScopes) dbPath := os.Getenv("DB_PATH") if dbPath == "" { return Options{}, fmt.Errorf("DB_PATH is required in .env file") } return Options{ ClientID: clientID, RedirectURI: redirectURI, Scopes: scopes, DBPath: dbPath, }, nil }