package main import ( "context" "log" "net/http" "os" "github.com/go-redis/redis/v8" "trip-planner/internal/cache" "trip-planner/internal/metrics" "trip-planner/internal/routing" "trip-planner/internal/yandex" ) func main() { redisClient := initRedis() router := routing.NewGraph() m := metrics.New() apiKey := os.Getenv("YANDEX_API_KEY") if apiKey == "" { apiKey = "default-key" // fallback for development } yandexClient := yandex.NewClient(apiKey, yandex.WithMetrics(m)) handlerCtx := NewHandlerContext(redisClient, router, yandexClient, m) // Cache warm-up: load city directory into Redis cache // ensures the API functions correctly on cold start and after cache expiry loadCityDirectoryIntoCache(context.Background(), handlerCtx.Cache) http.HandleFunc("/v1/cities", makeHandler(CityAutocomplete, handlerCtx)) http.HandleFunc("/v1/cities/", makeHandler(CityStations, handlerCtx)) http.HandleFunc("/v1/routes/search", makeHandler(RouteSearch, handlerCtx)) http.HandleFunc("/v1/routes/", makeHandler(RouteGeoJSON, handlerCtx)) http.HandleFunc("/v1/stations/", makeHandler(StationStatus, handlerCtx)) http.HandleFunc("/internal/admin/stations/", makeHandler(AdminStationStatus, handlerCtx)) http.HandleFunc("/metrics", makeHandler(MetricsHandler, handlerCtx)) // User preferences routes http.HandleFunc("/v1/preferences/saved-cities", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: GetSavedCities(handlerCtx, w, r) case http.MethodPost: AddSavedCity(handlerCtx, w, r) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) http.HandleFunc("/v1/preferences/saved-cities/", func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodDelete { RemoveSavedCity(handlerCtx, w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) http.HandleFunc("/v1/preferences/search-history", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: GetSearchHistory(handlerCtx, w, r) case http.MethodPost: AddSearchHistory(handlerCtx, w, r) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }) log.Println("Trip Planner API starting on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } // initRedis initializes a Redis client connection. func initRedis() *redis.Client { rdb := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) return rdb } // loadCityDirectoryIntoCache loads city data into Redis cache from stored records. // ensures the API functions correctly on cold start and after cache expiry. func loadCityDirectoryIntoCache(ctx context.Context, cache cache.Cache) { // In a full implementation, would load from Postgres directory // For now, this is a no-op since we don't have Postgres integration _ = ctx _ = cache } // makeHandler wraps a standalone handler function (which takes *HandlerContext) // into an http.HandlerFunc (which takes http.ResponseWriter and *http.Request). func makeHandler(handler func(*HandlerContext, http.ResponseWriter, *http.Request), hc *HandlerContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { handler(hc, w, r) } }