Files
trip-planner/cmd/api/main.go
Vladimir Zagainov b14682f424 fix: address code review findings
- Create README.md with project overview, quick start, Docker deployment, CI/CD info
- Update CLAUDE.md with Deployment section
- Fix cmd/api/main.go to use REDIS_ADDR env var with fallback
- Fix Dockerfile to use golang:1.22-alpine instead of golang:1.26-alpine
- Fix .gitea/workflows/deploy.yml to include Docker registry prefix in image tags
2026-08-19 01:11:55 +03:00

117 lines
3.7 KiB
Go

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 == "" {
log.Fatal("YANDEX_API_KEY environment variable is not set")
}
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)
}
})
// Serve static files from static/ directory
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
// Serve frontend
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
http.ServeFile(w, r, "static/index.html")
return
}
http.NotFound(w, r)
})
log.Println("Trip Planner API starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
// initRedis initializes a Redis client connection.
func initRedis() *redis.Client {
redisAddr := os.Getenv("REDIS_ADDR")
if redisAddr == "" {
redisAddr = "localhost:6379"
}
rdb := redis.NewClient(&redis.Options{
Addr: redisAddr,
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)
}
}