Initial commit
This commit is contained in:
commit
0a854f708a
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
idfm-api
|
||||||
|
arrets-lignes.json
|
||||||
|
referentiel-des-lignes.json
|
||||||
|
/vendor
|
30
api/api.go
Normal file
30
api/api.go
Normal file
@ -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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
9
api/destinations.go
Normal file
9
api/destinations.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func declareDestinationsRoutes(route *gin.RouterGroup) {
|
||||||
|
|
||||||
|
}
|
138
api/lines.go
Normal file
138
api/lines.go
Normal file
@ -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,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
9
api/missions.go
Normal file
9
api/missions.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func declareMissionsRoutes(route *gin.RouterGroup) {
|
||||||
|
|
||||||
|
}
|
18
api/routes.go
Normal file
18
api/routes.go
Normal file
@ -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)
|
||||||
|
}
|
153
api/schedules.go
Normal file
153
api/schedules.go
Normal file
@ -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)
|
||||||
|
})
|
||||||
|
}
|
73
api/stations.go
Normal file
73
api/stations.go
Normal file
@ -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,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
9
api/traffic.go
Normal file
9
api/traffic.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func declareTrafficRoutes(route *gin.RouterGroup) {
|
||||||
|
|
||||||
|
}
|
66
app.go
Normal file
66
app.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
44
config/cli.go
Normal file
44
config/cli.go
Normal file
@ -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
|
||||||
|
}
|
29
config/config.go
Normal file
29
config/config.go
Normal file
@ -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
|
||||||
|
}
|
44
config/custom.go
Normal file
44
config/custom.go
Normal file
@ -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
|
||||||
|
}
|
21
config/env.go
Normal file
21
config/env.go
Normal file
@ -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
|
||||||
|
}
|
26
go.mod
Normal file
26
go.mod
Normal file
@ -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
|
||||||
|
)
|
86
go.sum
Normal file
86
go.sum
Normal file
@ -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=
|
37
main.go
Normal file
37
main.go
Normal file
@ -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")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user