277 lines
6.7 KiB
Go
277 lines
6.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/joho/godotenv"
|
|
|
|
"bahndb_rest/queries"
|
|
)
|
|
|
|
var pool *pgxpool.Pool
|
|
|
|
func convErrorHandler(str string, err error) string {
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
type RequestError struct {
|
|
Code string `json:"code"`
|
|
Message string `json:"messaage"`
|
|
}
|
|
|
|
func run() error {
|
|
// load environment file
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
log.Fatal("Error loading .env file")
|
|
}
|
|
|
|
connectstr := "host=" + os.Getenv("DB_HOST") + " port=" + os.Getenv("DB_PORT") + " user=" + os.Getenv("DB_USER") + " password=" + os.Getenv("DB_PASSWORD") + " dbname=" + os.Getenv("DB_DATABASE") + " pool_max_conns=70 pool_max_conn_lifetime=1h30m"
|
|
|
|
// connect to db
|
|
pool, err = pgxpool.New(context.Background(), connectstr)
|
|
if err != nil {
|
|
log.Fatal("Error creating pool", err.Error())
|
|
}
|
|
fmt.Println("Connected to database")
|
|
|
|
//create new request multiplexer
|
|
mux := http.NewServeMux()
|
|
|
|
// register stopinfo handler
|
|
mux.Handle("/stop/search", &stopInfoHandler{})
|
|
mux.Handle("/stop/{id}/info", &stopIdInfoHandler{})
|
|
mux.Handle("/stop/{id}/departures", &stopDeparturesHandler{})
|
|
|
|
fmt.Println("Starting server on port 8080")
|
|
|
|
err = http.ListenAndServe(":8080", mux)
|
|
if err != nil {
|
|
log.Fatal("Error serving server", err.Error())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
if err := run(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Search For a Stop by Name
|
|
type stopInfoHandler struct{}
|
|
|
|
func (h *stopInfoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
enc := json.NewEncoder(w)
|
|
ctx := context.Background()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
conn, err := pool.Acquire(ctx)
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
defer conn.Release()
|
|
|
|
// load all generated queries
|
|
queries := bahndb_rest.New(conn)
|
|
|
|
// Parse query text
|
|
var searchQuery = r.FormValue("query")
|
|
|
|
if searchQuery == "" {
|
|
w.WriteHeader(400)
|
|
_ = enc.Encode(RequestError{Code: "0000", Message: "'query' is required"})
|
|
return
|
|
}
|
|
|
|
if r.FormValue("exact") != "true" {
|
|
searchQuery = "%" + searchQuery + "%"
|
|
}
|
|
|
|
var longDistance pgtype.Bool
|
|
|
|
// Parse whether only long distance stations shall be returned
|
|
if r.FormValue("longDistance") == "true" {
|
|
longDistance = pgtype.Bool{Bool: true, Valid: true}
|
|
} else {
|
|
longDistance = pgtype.Bool{Bool: false, Valid: false}
|
|
}
|
|
|
|
stations, err := queries.GetStationsByName(ctx, bahndb_rest.GetStationsByNameParams{SearchParams: searchQueries, LongDistance: longDistance})
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
err = enc.Encode(stations)
|
|
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0002", Message: "Could not write response: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
type stopIdInfoHandler struct{}
|
|
|
|
func (h *stopIdInfoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
enc := json.NewEncoder(w)
|
|
ctx := context.Background()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
conn, err := pool.Acquire(ctx)
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
defer conn.Release()
|
|
|
|
// load all generated queries
|
|
queries := bahndb_rest.New(conn)
|
|
|
|
// Parse stop id
|
|
var stopId = r.PathValue("id")
|
|
if stopId == "" {
|
|
w.WriteHeader(400)
|
|
_ = enc.Encode(RequestError{Code: "0000", Message: "'id' is required"})
|
|
return
|
|
}
|
|
|
|
// Execute DB Query
|
|
stations, err := queries.GetStationById(ctx, stopId)
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Return
|
|
w.WriteHeader(http.StatusOK)
|
|
err = enc.Encode(stations)
|
|
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0002", Message: "Could not write response: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Return departures at specific stop
|
|
type stopDeparturesHandler struct{}
|
|
|
|
func (h *stopDeparturesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
enc := json.NewEncoder(w)
|
|
ctx := context.Background()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// Acquire DB Connection
|
|
conn, err := pool.Acquire(ctx)
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0000", Message: "'id' is required"})
|
|
return
|
|
}
|
|
defer conn.Release()
|
|
|
|
queries := bahndb_rest.New(conn)
|
|
|
|
// Parse stop id
|
|
var stopId = r.PathValue("id")
|
|
if stopId == "" {
|
|
w.WriteHeader(400)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
stopIdPg := pgtype.Text{String: stopId, Valid: true}
|
|
|
|
// Parse time from string, default to now
|
|
timeFromStr := strings.Replace(r.FormValue("from"), " ", "+", -1)
|
|
var timeFrom time.Time
|
|
|
|
if timeFromStr == "" {
|
|
timeFrom = time.Now()
|
|
} else {
|
|
timeFrom, err = time.Parse("2006-01-02T15:04:05-0700", timeFromStr)
|
|
}
|
|
|
|
timeFromPg := pgtype.Timestamptz{Time: timeFrom, Valid: true}
|
|
|
|
// Parse Duration to get departures for, default to 60
|
|
duration, err := strconv.Atoi(r.FormValue("duration"))
|
|
if err != nil {
|
|
duration = 60
|
|
}
|
|
|
|
// Parse upper Time limit, default to from duration minutes
|
|
timeToStr := strings.Replace(r.FormValue("to"), " ", "+", -1)
|
|
var timeTo time.Time
|
|
if timeToStr == "" {
|
|
timeTo = timeFrom.Add(time.Duration(duration) * time.Minute)
|
|
} else {
|
|
timeTo, err = time.Parse("2006-01-02T15:04:05-0700", timeToStr)
|
|
}
|
|
timeToPg := pgtype.Timestamptz{Time: timeTo, Valid: true}
|
|
|
|
// Parse realtime point in time, default to most recent
|
|
timeRtUpdateStr := strings.Replace(r.FormValue("dataAt"), " ", "+", -1)
|
|
var timeRtUpdate time.Time
|
|
if timeRtUpdateStr == "" {
|
|
timeRtUpdate = time.Now().Add(time.Duration(48) * time.Hour)
|
|
} else {
|
|
timeRtUpdate, err = time.Parse("2006-01-02T15:04:05-0700", timeRtUpdateStr)
|
|
}
|
|
timeRtUpdatePg := pgtype.Timestamptz{Time: timeRtUpdate, Valid: true}
|
|
|
|
// Execute Query
|
|
stations, err := queries.GetDeparturesById(ctx, bahndb_rest.GetDeparturesByIdParams{Stop: stopIdPg,
|
|
Departure: timeFromPg,
|
|
Departure_2: timeToPg,
|
|
Realtimedataupdatedat: timeRtUpdatePg})
|
|
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0001", Message: "DB Error: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
// Return Data
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
err = enc.Encode(stations)
|
|
|
|
if err != nil {
|
|
w.WriteHeader(500)
|
|
_ = enc.Encode(RequestError{Code: "0002", Message: "Could not write response: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|