package standardcovoiturage import ( "context" "encoding/json" "errors" "fmt" "net/http" "strings" "time" "github.com/gorilla/schema" "golang.org/x/crypto/bcrypt" ) const OperatorContextKey = "operator" type Handler interface { GetDriverJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, count *int64) ([]DriverJourney, error) GetPassengerJourneys(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureDate time.Time, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, count *int64) ([]PassengerJourney, error) GetDriverRegularTrips(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureTimeOfDay string, departureWeekDays *[]string, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, minDepartureDate *time.Time, maxDepartureDate *time.Time, count *int64) ([]DriverTrip, error) GetPassengerRegularTrips(ctx context.Context, departureLat float64, departureLng float64, arrivalLat float64, arrivalLng float64, departureTimeOfDay string, departureWeekDays *[]string, timeDelta *time.Duration, departureRadius *float64, arrivalRadius *float64, minDepartureDate *time.Time, maxDepartureDate *time.Time, count *int64) ([]PassengerTrip, error) } type AuthorizedOperator struct { Operator string ApiKey string // encoded using bcrypt } type GetDriverJourneysRequest struct { DepartureLat float64 `schema:"departureLat,required"` DepartureLng float64 `schema:"departureLng,required"` ArrivalLat float64 `schema:"arrivalLat,required"` ArrivalLng float64 `schema:"arrivalLng,required"` DepartureDate int64 `schema:"departureDate,required"` TimeDelta *int64 `schema:"timeDelta"` DepartureRadius *float64 `schema:"departureRadius"` ArrivalRadius *float64 `schema:"arrivalRadius"` Count *int64 `schema:"count"` } type GetPassengerJourneysRequest struct { DepartureLat float64 `schema:"departureLat,required"` DepartureLng float64 `schema:"departureLng,required"` ArrivalLat float64 `schema:"arrivalLat,required"` ArrivalLng float64 `schema:"arrivalLng,required"` DepartureDate int64 `schema:"departureDate,required"` TimeDelta *int64 `schema:"timeDelta"` DepartureRadius *float64 `schema:"departureRadius"` ArrivalRadius *float64 `schema:"arrivalRadius"` Count *int64 `schema:"count"` } type GetDriverRegularTripsRequest struct { DepartureLat float64 `schema:"departureLat,required"` DepartureLng float64 `schema:"departureLng,required"` ArrivalLat float64 `schema:"arrivalLat,required"` ArrivalLng float64 `schema:"arrivalLng,required"` DepartureTimeOfDay string `schema:"departureTimeOfDay,required"` DepartureWeekDays *[]string `schema:"departureWeekdays"` TimeDelta *int64 `schema:"timeDelta"` DepartureRadius *float64 `schema:"departureRadius"` ArrivalRadius *float64 `schema:"arrivalRadius"` MinDepartureDate *int64 `schema:"minDepartureDate"` MaxDepartureDate *int64 `schema:"maxDepartureDate"` Count *int64 `schema:"count"` } type GetPassengerRegularTripsRequest struct { DepartureLat float64 `schema:"departureLat,required"` DepartureLng float64 `schema:"departureLng,required"` ArrivalLat float64 `schema:"arrivalLat,required"` ArrivalLng float64 `schema:"arrivalLng,required"` DepartureTimeOfDay string `schema:"departureTimeOfDay,required"` DepartureWeekDays *[]string `schema:"departureWeekdays"` TimeDelta *int64 `schema:"timeDelta"` DepartureRadius *float64 `schema:"departureRadius"` ArrivalRadius *float64 `schema:"arrivalRadius"` MinDepartureDate *int64 `schema:"minDepartureDate"` MaxDepartureDate *int64 `schema:"maxDepartureDate"` Count *int64 `schema:"count"` } type Server struct { Handler Handler AuthorizedOperators []AuthorizedOperator } func NewServer(handler Handler) *Server { return &Server{ Handler: handler, } } func (s *Server) FindApiKey(key string) (operator string, err error) { for _, o := range s.AuthorizedOperators { if e := bcrypt.CompareHashAndPassword([]byte(o.ApiKey), []byte(key)); e == nil { return o.Operator, nil } } return "", errors.New("operator not found") } func (s *Server) AddOperator(operator string, apiKey string) error { encryptedKey, err := bcrypt.GenerateFromPassword([]byte(apiKey), bcrypt.DefaultCost) if err != nil { return err } s.AuthorizedOperators = append(s.AuthorizedOperators, AuthorizedOperator{ Operator: operator, ApiKey: string(encryptedKey), }) return nil } func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { apiKey := r.Header.Get("X-Api-Key") operator, err := s.FindApiKey(apiKey) if err != nil { fmt.Println(err) w.WriteHeader(http.StatusUnauthorized) return } ctx := context.WithValue(r.Context(), OperatorContextKey, operator) p := strings.Split(r.URL.Path, "/")[1:] n := len(p) switch { case r.Method == "GET" && n == 1 && p[0] == "driver_journeys": s.getDriverJourneys(w, r.WithContext(ctx)) return case r.Method == "GET" && n == 1 && p[0] == "passenger_journeys": s.getPassengerJourneys(w, r.WithContext(ctx)) return case r.Method == "GET" && n == 1 && p[0] == "driver_regular_trips": s.getDriverRegularTrips(w, r.WithContext(ctx)) return case r.Method == "GET" && n == 1 && p[0] == "passenger_regular_trips": s.getPassengerRegularTrips(w, r.WithContext(ctx)) return default: w.WriteHeader(http.StatusNotFound) } } func (s *Server) getDriverJourneys(w http.ResponseWriter, r *http.Request) { var request GetDriverJourneysRequest var decoder = schema.NewDecoder() if err := decoder.Decode(&request, r.URL.Query()); err != nil { fmt.Println(err) badRequest(w, fmt.Errorf("could not parse the request : %s", err)) return } departureDate := time.Unix(request.DepartureDate, 0) var timeDelta *time.Duration timeDelta = nil if request.TimeDelta != nil { duration := time.Duration(*request.TimeDelta) timeDelta = &duration } driverJourneys, err := s.Handler.GetDriverJourneys( r.Context(), request.DepartureLat, request.DepartureLng, request.ArrivalLat, request.ArrivalLng, departureDate, timeDelta, request.DepartureRadius, request.ArrivalRadius, request.Count) if err != nil { fmt.Println(err) jsonError(w, err, http.StatusInternalServerError) return } jsonResponse(w, driverJourneys, http.StatusOK) } func (s *Server) getPassengerJourneys(w http.ResponseWriter, r *http.Request) { var request GetPassengerJourneysRequest var decoder = schema.NewDecoder() if err := decoder.Decode(&request, r.URL.Query()); err != nil { fmt.Println(err) badRequest(w, fmt.Errorf("could not parse the request : %s", err)) return } departureDate := time.Unix(request.DepartureDate, 0) var timeDelta *time.Duration timeDelta = nil if request.TimeDelta != nil { duration := time.Duration(*request.TimeDelta) timeDelta = &duration } passengerJourneys, err := s.Handler.GetPassengerJourneys( r.Context(), request.DepartureLat, request.DepartureLng, request.ArrivalLat, request.ArrivalLng, departureDate, timeDelta, request.DepartureRadius, request.ArrivalRadius, request.Count) if err != nil { fmt.Println(err) jsonError(w, err, http.StatusInternalServerError) return } jsonResponse(w, passengerJourneys, http.StatusOK) } func (s *Server) getDriverRegularTrips(w http.ResponseWriter, r *http.Request) { var request GetDriverRegularTripsRequest var decoder = schema.NewDecoder() if err := decoder.Decode(&request, r.URL.Query()); err != nil { fmt.Println(err) badRequest(w, fmt.Errorf("could not parse the request : %s", err)) return } var minDepartureDate *time.Time if request.MinDepartureDate != nil { d := time.Unix(*request.MinDepartureDate, 0) minDepartureDate = &d } var maxDepartureDate *time.Time if request.MinDepartureDate != nil { d := time.Unix(*request.MinDepartureDate, 0) maxDepartureDate = &d } var timeDelta *time.Duration timeDelta = nil if request.TimeDelta != nil { duration := time.Duration(*request.TimeDelta) timeDelta = &duration } driverJourneys, err := s.Handler.GetDriverRegularTrips( r.Context(), request.DepartureLat, request.DepartureLng, request.ArrivalLat, request.ArrivalLng, request.DepartureTimeOfDay, request.DepartureWeekDays, timeDelta, request.DepartureRadius, request.ArrivalRadius, minDepartureDate, maxDepartureDate, request.Count) if err != nil { fmt.Println(err) jsonError(w, err, http.StatusInternalServerError) return } jsonResponse(w, driverJourneys, http.StatusOK) } func (s *Server) getPassengerRegularTrips(w http.ResponseWriter, r *http.Request) { var request GetPassengerRegularTripsRequest var decoder = schema.NewDecoder() if err := decoder.Decode(&request, r.URL.Query()); err != nil { fmt.Println(err) badRequest(w, fmt.Errorf("could not parse the request : %s", err)) return } var minDepartureDate *time.Time if request.MinDepartureDate != nil { d := time.Unix(*request.MinDepartureDate, 0) minDepartureDate = &d } var maxDepartureDate *time.Time if request.MinDepartureDate != nil { d := time.Unix(*request.MinDepartureDate, 0) maxDepartureDate = &d } var timeDelta *time.Duration timeDelta = nil if request.TimeDelta != nil { duration := time.Duration(*request.TimeDelta) timeDelta = &duration } passengerTrips, err := s.Handler.GetPassengerRegularTrips( r.Context(), request.DepartureLat, request.DepartureLng, request.ArrivalLat, request.ArrivalLng, request.DepartureTimeOfDay, request.DepartureWeekDays, timeDelta, request.DepartureRadius, request.ArrivalRadius, minDepartureDate, maxDepartureDate, request.Count) if err != nil { fmt.Println(err) jsonError(w, err, http.StatusInternalServerError) return } jsonResponse(w, passengerTrips, http.StatusOK) } func jsonResponse(w http.ResponseWriter, response any, statuscode int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(statuscode) err := json.NewEncoder(w).Encode(response) fmt.Println(err) } func jsonError(w http.ResponseWriter, err error, statuscode int) { jsonResponse(w, map[string]any{"error": err.Error()}, statuscode) } func badRequest(w http.ResponseWriter, err error) { jsonError(w, err, http.StatusBadRequest) }