From 0a854f708ac8d554678439beaf71a122392fa8ed Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Oct 2022 23:54:17 +0200 Subject: [PATCH] Initial commit --- .gitignore | 4 ++ api/api.go | 30 +++++++++ api/destinations.go | 9 +++ api/lines.go | 138 +++++++++++++++++++++++++++++++++++++++ api/missions.go | 9 +++ api/routes.go | 18 ++++++ api/schedules.go | 153 ++++++++++++++++++++++++++++++++++++++++++++ api/stations.go | 73 +++++++++++++++++++++ api/traffic.go | 9 +++ app.go | 66 +++++++++++++++++++ config/cli.go | 44 +++++++++++++ config/config.go | 29 +++++++++ config/custom.go | 44 +++++++++++++ config/env.go | 21 ++++++ go.mod | 26 ++++++++ go.sum | 86 +++++++++++++++++++++++++ main.go | 37 +++++++++++ 17 files changed, 796 insertions(+) create mode 100644 .gitignore create mode 100644 api/api.go create mode 100644 api/destinations.go create mode 100644 api/lines.go create mode 100644 api/missions.go create mode 100644 api/routes.go create mode 100644 api/schedules.go create mode 100644 api/stations.go create mode 100644 api/traffic.go create mode 100644 app.go create mode 100644 config/cli.go create mode 100644 config/config.go create mode 100644 config/custom.go create mode 100644 config/env.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9342572 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +idfm-api +arrets-lignes.json +referentiel-des-lignes.json +/vendor \ No newline at end of file diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..e643b2a --- /dev/null +++ b/api/api.go @@ -0,0 +1,30 @@ +package api + +import ( + "fmt" + "time" + + "github.com/gin-gonic/gin" +) + +type apiMetadata struct { + Call string `json:"call"` + Date time.Time `json:"date"` + Version int `json:"version"` +} + +type apiResult struct { + Result interface{} `json:"result"` + Metadata apiMetadata `json:"_metadata"` +} + +func APIResult(c *gin.Context, res interface{}) apiResult { + return apiResult{ + Result: res, + Metadata: apiMetadata{ + Call: fmt.Sprintf("%s %s", c.Request.Method, c.Request.URL.Path), + Date: time.Now(), + Version: 4, + }, + } +} diff --git a/api/destinations.go b/api/destinations.go new file mode 100644 index 0000000..ce44483 --- /dev/null +++ b/api/destinations.go @@ -0,0 +1,9 @@ +package api + +import ( + "github.com/gin-gonic/gin" +) + +func declareDestinationsRoutes(route *gin.RouterGroup) { + +} diff --git a/api/lines.go b/api/lines.go new file mode 100644 index 0000000..2a2ef6d --- /dev/null +++ b/api/lines.go @@ -0,0 +1,138 @@ +package api + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +type IDFMLine struct { + DatasetID string `json:"datasetid"` + RecordIDs string `json:"recordid"` + Fields struct { + IdLine string `json:"id_line"` + Name string `json:"name_line"` + ShortName string `json:"shortname_line"` + TransportMode string `json:"transportmode"` + TransportSubmode string `json:"transportsubmode"` + Type string `json:"type"` + OperatorRef string `json:"operatorref"` + AdditionalOperators string `json:"additionaloperators,omitempty"` + OperatorName string `json:"operatorname"` + NetworkName string `json:"networkname,omitempty"` + WebColor string `json:"colourweb_hexa"` + PrintColor string `json:"colourprint_cmjn"` + WebTextColor string `json:"textcolourweb_hexa"` + TextPrintColor string `json:"textcolourprint_hexa"` + Accessibility string `json:"accessibility"` + AudibleSignAvailable string `json:"audiblesigns_available"` + VisualSignAvailable string `json:"visualsigns_available"` + NoticeTitle string `json:"notice_title"` + NoticeText string `json:"notice_text"` + Status string `json:"status"` + ShortNameGroup string `json:"shortname_groupoflines"` + IdGroup string `json:"id_groupoflines"` + ExternalCode string `json:"externalcode_line"` + } `json:"fields"` + RecordTimestamp time.Time `json:"record_timestamp"` +} + +type PGLine struct { + Code string `json:"code"` + Name string `json:"name"` + Directions string `json:"directions"` + Id string `json:"id"` +} + +var IDFMLines []IDFMLine + +func init() { + fd, err := os.Open("referentiel-des-lignes.json") + if err != nil { + log.Fatal("Unable to open `referentiel-des-lignes.json`:", err.Error()) + } + defer fd.Close() + + dec := json.NewDecoder(fd) + if err = dec.Decode(&IDFMLines); err != nil { + log.Fatal("Unable to decode `referentiel-des-lignes.json`:", err.Error()) + } +} + +func convertLineType(old string) string { + switch old { + case "buses": + return "bus" + case "metros": + return "metro" + case "noctiliens": + return "noctilien" + case "rers": + return "rail" + case "tramways": + return "tram" + default: + return old + } +} + +func declareLinesRoutes(router *gin.RouterGroup) { + router.GET("/lines", func(c *gin.Context) { + var modes []string + for _, line := range IDFMLines { + mode := line.Fields.TransportMode + + found := false + for _, m := range modes { + if mode == m { + found = true + break + } + } + + if !found { + modes = append(modes, mode) + } + } + + c.JSON(http.StatusOK, APIResult(c, modes)) + }) + + router.GET("/lines/:type", func(c *gin.Context) { + t := convertLineType(string(c.Param("type"))) + + var lines []PGLine + for _, line := range IDFMLines { + if line.Fields.TransportMode == t || strings.ToLower(line.Fields.NetworkName) == t { + name := line.Fields.Name + + if line.Fields.ShortNameGroup != "" { + if strings.Contains(line.Fields.ShortNameGroup, name) { + name = line.Fields.ShortNameGroup + } else if name == line.Fields.ShortName { + name = fmt.Sprintf("%s - %s", line.Fields.Name, line.Fields.ShortNameGroup) + } + } + + pgline := PGLine{ + Code: fmt.Sprintf("IDFM:%s", line.Fields.IdLine), + Name: name, + Directions: "", + Id: line.Fields.IdLine, + } + + lines = append(lines, pgline) + } + } + + c.JSON(http.StatusOK, APIResult(c, map[string][]PGLine{ + t: lines, + })) + }) +} diff --git a/api/missions.go b/api/missions.go new file mode 100644 index 0000000..bcf2e02 --- /dev/null +++ b/api/missions.go @@ -0,0 +1,9 @@ +package api + +import ( + "github.com/gin-gonic/gin" +) + +func declareMissionsRoutes(route *gin.RouterGroup) { + +} diff --git a/api/routes.go b/api/routes.go new file mode 100644 index 0000000..915f233 --- /dev/null +++ b/api/routes.go @@ -0,0 +1,18 @@ +package api + +import ( + "github.com/gin-gonic/gin" + + "git.nemunai.re/nemunaire/idfm-api/config" +) + +func DeclareRoutes(router *gin.Engine, cfg *config.Config) { + apiRoutes := router.Group("/api") + + declareDestinationsRoutes(apiRoutes) + declareLinesRoutes(apiRoutes) + declareMissionsRoutes(apiRoutes) + declareSchedulesRoutes(apiRoutes) + declareStationsRoutes(apiRoutes) + declareTrafficRoutes(apiRoutes) +} diff --git a/api/schedules.go b/api/schedules.go new file mode 100644 index 0000000..4b46e03 --- /dev/null +++ b/api/schedules.go @@ -0,0 +1,153 @@ +package api + +import ( + "encoding/json" + "flag" + "fmt" + "math" + "net/http" + "net/url" + "time" + + "github.com/gin-gonic/gin" +) + +const IDFM_BASEURL = "https://prim.iledefrance-mobilites.fr/marketplace" + +var IDFM_TOKEN = "" + +func init() { + flag.StringVar(&IDFM_TOKEN, "token-IDFM", IDFM_TOKEN, "Token to access IDFM API") +} + +type IDFMSchedule struct { + Siri struct { + ServiceDelivery struct { + ResponseTimestamp time.Time `json:"ResponseTimestamp"` + ProducerRef string `json:"ProducerRef"` + ResponseMessageIdentifier string `json:"ResponseMessageIdentifier"` + StopMonitoringDelivery []struct { + ResponseTimestamp time.Time `json:"ResponseTimestamp"` + Version string `json:"Version"` + Status string `json:"Status"` + MonitoredStopVisit []struct { + RecordedAtTime time.Time `json:"RecordedAtTime"` + ItemIdentifier string `json:"ItemIdentifier"` + MonitoringRef struct { + Value string `json:"value"` + } `json:"MonitoringRef"` + MonitoredVehicleJourney struct { + LineRef struct { + Value string `json:"value"` + } `json:"LineRef"` + OperatorRef struct { + Value string `json:"value"` + } `json:"OperatorRef"` + FramedVehicleJourneyRef struct { + DataFrameRef struct { + Value string `json:"value"` + } `json:"DataFrameRef"` + DatedVehicleJourneyRef string `json:"DatedVehicleJourneyRef"` + } `json:"FramedVehicleJourneyRef"` + DirectionName []struct { + Value string `json:"value"` + } `json:"DirectionName"` + DestinationRef struct { + Value string `json:"value"` + } `json:"DestinationRef"` + DestinationName []struct { + Value string `json:"value"` + } `json:"DestinationName"` + JourneyNote []struct { + Value string `json:"value"` + } `json:"JourneyNote"` + MonitoredCall struct { + StopPointName []struct { + Value string `json:"value"` + } `json:"StopPointName"` + VehicleAtStop bool `json:"VehicleAtStop"` + DestinationDisplay []struct { + Value string `json:"value"` + } `json:"DestinationDisplay"` + ExpectedArrivalTime time.Time `json:"ExpectedArrivalTime"` + ExpectedDepartureTime time.Time `json:"ExpectedDepartureTime"` + DepartureStatus string `json:"DepartureStatus"` + } `json:"MonitoredCall"` + } `json:"MonitoredVehicleJourney"` + } `json:"MonitoredStopVisit"` + } `json:"StopMonitoringDelivery"` + } `json:"ServiceDelivery"` + } `json:"siri"` +} + +type PGSchedule struct { + Destination string `json:"destionation"` + Message string `json:"message"` +} + +func declareSchedulesRoutes(router *gin.RouterGroup) { + router.GET("/schedules/:type/:code/:station/:way", func(c *gin.Context) { + t := string(c.Param("type")) + station := convertLineType(string(c.Param("station"))) + + rurl, err := url.JoinPath(IDFM_BASEURL, "stop-monitoring") + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + requrl, err := url.Parse(rurl) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + reqquery := url.Values{} + reqquery.Add("MonitoringRef", station) + requrl.RawQuery = reqquery.Encode() + + req, err := http.NewRequest("GET", requrl.String(), nil) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + req.Header.Add("Accept", "application/json") + req.Header.Add("apikey", IDFM_TOKEN) + + res, err := http.DefaultClient.Do(req) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + defer res.Body.Close() + + var schedules IDFMSchedule + + dec := json.NewDecoder(res.Body) + if err = dec.Decode(&schedules); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) + return + } + + pgs := []PGSchedule{} + for _, vehicule := range schedules.Siri.ServiceDelivery.StopMonitoringDelivery[0].MonitoredStopVisit { + msg := vehicule.MonitoredVehicleJourney.MonitoredCall.ExpectedDepartureTime.String() + + if t == "metros" || t == "buses" || t == "tramways" { + if vehicule.MonitoredVehicleJourney.MonitoredCall.VehicleAtStop { + msg = "À quai" + } else { + msg = fmt.Sprintf("%d mn", int(math.Floor(time.Until(vehicule.MonitoredVehicleJourney.MonitoredCall.ExpectedDepartureTime).Minutes()))) + } + } + + pgs = append(pgs, PGSchedule{ + Destination: vehicule.MonitoredVehicleJourney.MonitoredCall.DestinationDisplay[0].Value, + Message: msg, + }) + } + + c.JSON(http.StatusOK, pgs) + }) +} diff --git a/api/stations.go b/api/stations.go new file mode 100644 index 0000000..9366906 --- /dev/null +++ b/api/stations.go @@ -0,0 +1,73 @@ +package api + +import ( + "encoding/json" + "log" + "net/http" + "os" + + "github.com/gin-gonic/gin" +) + +type IDFMStation struct { + DatasetID string `json:"datasetid"` + RecordIDs string `json:"recordid"` + Fields struct { + Id string `json:"id"` + PointGeo []float64 `json:"pointgeo"` + StopId string `json:"stop_id"` + StopName string `json:"stop_name"` + OperatorName string `json:"operatorname"` + NomCommune string `json:"nom_commune"` + RouteLongName string `json:"route_long_name"` + StopLat string `json:"stop_lat"` + StopLon string `json:"stop_lon"` + CodeINSEE string `json:"code_insee"` + } `json:"fields"` + Geometry struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + } `json:"geometry"` +} + +type PGStation struct { + Id string `json:"slug"` + Name string `json:"name"` +} + +var IDFMStations []IDFMStation + +func init() { + fd, err := os.Open("arrets-lignes.json") + if err != nil { + log.Fatal("Unable to open `arrets-lignes.json`:", err.Error()) + } + defer fd.Close() + + dec := json.NewDecoder(fd) + if err = dec.Decode(&IDFMStations); err != nil { + log.Fatal("Unable to decode `arrets-lignes.json`:", err.Error()) + } +} + +func declareStationsRoutes(router *gin.RouterGroup) { + router.GET("/stations/:type/:code", func(c *gin.Context) { + code := convertLineType(string(c.Param("code"))) + + var stations []PGStation + for _, station := range IDFMStations { + if station.Fields.Id == code { + pgstation := PGStation{ + Id: station.Fields.StopId, + Name: station.Fields.StopName, + } + + stations = append(stations, pgstation) + } + } + + c.JSON(http.StatusOK, APIResult(c, map[string][]PGStation{ + "stations": stations, + })) + }) +} diff --git a/api/traffic.go b/api/traffic.go new file mode 100644 index 0000000..8a08325 --- /dev/null +++ b/api/traffic.go @@ -0,0 +1,9 @@ +package api + +import ( + "github.com/gin-gonic/gin" +) + +func declareTrafficRoutes(route *gin.RouterGroup) { + +} diff --git a/app.go b/app.go new file mode 100644 index 0000000..ca84aca --- /dev/null +++ b/app.go @@ -0,0 +1,66 @@ +package main + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/gin-gonic/gin" + + "git.nemunai.re/nemunaire/idfm-api/api" + "git.nemunai.re/nemunaire/idfm-api/config" +) + +type App struct { + cfg *config.Config + router *gin.Engine + srv *http.Server +} + +func NewApp(cfg *config.Config) *App { + if cfg.DevProxy == "" { + gin.SetMode(gin.ReleaseMode) + } + gin.ForceConsoleColor() + router := gin.Default() + + router.Use(func(c *gin.Context) { + c.Next() + }) + + // Prepare struct + app := &App{ + cfg: cfg, + router: router, + } + + // Register routes + api.DeclareRoutes(router, cfg) + + router.GET("/api/version", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"version": Version}) + }) + + return app +} + +func (app *App) Start() { + app.srv = &http.Server{ + Addr: app.cfg.Bind, + Handler: app.router, + } + + log.Printf("Ready, listening on %s\n", app.cfg.Bind) + if err := app.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } +} + +func (app *App) Stop() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := app.srv.Shutdown(ctx); err != nil { + log.Fatal("Server Shutdown:", err) + } +} diff --git a/config/cli.go b/config/cli.go new file mode 100644 index 0000000..f1323f4 --- /dev/null +++ b/config/cli.go @@ -0,0 +1,44 @@ +package config + +import ( + "flag" +) + +// declareFlags registers flags for the structure Options. +func (c *Config) declareFlags() { + flag.Var(&c.ExternalURL, "external-url", "Public URL of the service") + flag.StringVar(&c.BaseURL, "baseurl", c.BaseURL, "URL prepended to each URL") + flag.StringVar(&c.Bind, "bind", c.Bind, "Bind port/socket") + + // Others flags are declared in some other files when they need specials configurations +} + +func Consolidated() (cfg *Config, err error) { + // Define defaults options + cfg = &Config{ + Bind: "127.0.0.1:8080", + } + + cfg.declareFlags() + + // Then, overwrite that by what is present in the environment + err = cfg.FromEnv() + if err != nil { + return + } + + // Finaly, command line takes precedence + err = cfg.parseCLI() + if err != nil { + return + } + + return +} + +// parseCLI parse the flags and treats extra args as configuration filename. +func (c *Config) parseCLI() error { + flag.Parse() + + return nil +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..0077d3b --- /dev/null +++ b/config/config.go @@ -0,0 +1,29 @@ +package config + +import ( + "flag" + "strings" +) + +type Config struct { + DevProxy string + Bind string + ExternalURL URL + BaseURL string +} + +// parseLine treats a config line and place the read value in the variable +// declared to the corresponding flag. +func (c *Config) parseLine(line string) (err error) { + fields := strings.SplitN(line, "=", 2) + orig_key := strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + key := strings.TrimPrefix(orig_key, "IDFM_") + key = strings.Replace(key, "_", "-", -1) + key = strings.ToLower(key) + + err = flag.Set(key, value) + + return +} diff --git a/config/custom.go b/config/custom.go new file mode 100644 index 0000000..716a2f7 --- /dev/null +++ b/config/custom.go @@ -0,0 +1,44 @@ +package config + +import ( + "encoding/base64" + "net/url" +) + +type JWTSecretKey []byte + +func (i *JWTSecretKey) String() string { + return base64.StdEncoding.EncodeToString(*i) +} + +func (i *JWTSecretKey) Set(value string) error { + z, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return err + } + + *i = z + return nil +} + +type URL struct { + URL *url.URL +} + +func (i *URL) String() string { + if i.URL != nil { + return i.URL.String() + } else { + return "" + } +} + +func (i *URL) Set(value string) error { + u, err := url.Parse(value) + if err != nil { + return err + } + + i.URL = u + return nil +} diff --git a/config/env.go b/config/env.go new file mode 100644 index 0000000..3fb7fd3 --- /dev/null +++ b/config/env.go @@ -0,0 +1,21 @@ +package config + +import ( + "fmt" + "os" + "strings" +) + +// FromEnv analyzes all the environment variables to find each one +// starting by GUSTUS_ +func (c *Config) FromEnv() error { + for _, line := range os.Environ() { + if strings.HasPrefix(line, "IDFM_") { + err := c.parseLine(line) + if err != nil { + return fmt.Errorf("error in environment (%q): %w", line, err) + } + } + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b3657c0 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module git.nemunai.re/nemunaire/idfm-api + +go 1.18 + +require github.com/gin-gonic/gin v1.8.1 + +require ( + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.10.0 // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect + golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f99c8fe --- /dev/null +++ b/go.sum @@ -0,0 +1,86 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..47ec915 --- /dev/null +++ b/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "log" + "math/rand" + "os" + "os/signal" + "syscall" + "time" + + "git.nemunai.re/nemunaire/idfm-api/config" +) + +var ( + Version = "custom-build" +) + +func main() { + seed := time.Now().Unix() + rand.Seed(seed) + + cfg, err := config.Consolidated() + if err != nil { + log.Fatal("Unable to read configuration:", err) + } + + // Start app + a := NewApp(cfg) + go a.Start() + + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt, syscall.SIGTERM) + <-quit + log.Println("Stopping the service...") + a.Stop() + log.Println("Stopped") +}