standard-covoiturage/server.go

351 lines
10 KiB
Go

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)
}