mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
refactor server routes
This commit is contained in:
parent
d5b1472560
commit
c1b2114dd2
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -44,16 +45,12 @@ type Server struct {
|
|||
Environ *bbgo.Environment
|
||||
Trader *bbgo.Trader
|
||||
Setup *Setup
|
||||
Bind string
|
||||
OpenInBrowser bool
|
||||
|
||||
srv *http.Server
|
||||
}
|
||||
|
||||
func (s *Server) Run(ctx context.Context) error {
|
||||
userConfig := s.Config
|
||||
environ := s.Environ
|
||||
|
||||
func (s *Server) newEngine() *gin.Engine {
|
||||
r := gin.Default()
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"*"},
|
||||
|
@ -76,7 +73,7 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
r.GET("/api/trades", func(c *gin.Context) {
|
||||
if environ.TradeService == nil {
|
||||
if s.Environ.TradeService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
|
||||
return
|
||||
}
|
||||
|
@ -91,7 +88,7 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
return
|
||||
}
|
||||
|
||||
trades, err := environ.TradeService.Query(service.QueryTradesOptions{
|
||||
trades, err := s.Environ.TradeService.Query(service.QueryTradesOptions{
|
||||
Exchange: types.ExchangeName(exchange),
|
||||
Symbol: symbol,
|
||||
LastGID: lastGID,
|
||||
|
@ -108,91 +105,8 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
})
|
||||
})
|
||||
|
||||
r.GET("/api/orders/closed", func(c *gin.Context) {
|
||||
if environ.OrderService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
exchange := c.Query("exchange")
|
||||
symbol := c.Query("symbol")
|
||||
gidStr := c.DefaultQuery("gid", "0")
|
||||
|
||||
lastGID, err := strconv.ParseInt(gidStr, 10, 64)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("last gid parse error")
|
||||
c.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
orders, err := environ.OrderService.Query(service.QueryOrdersOptions{
|
||||
Exchange: types.ExchangeName(exchange),
|
||||
Symbol: symbol,
|
||||
LastGID: lastGID,
|
||||
Ordering: "DESC",
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusBadRequest)
|
||||
logrus.WithError(err).Error("order query error")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"orders": orders,
|
||||
})
|
||||
})
|
||||
|
||||
r.GET("/api/trading-volume", func(c *gin.Context) {
|
||||
if environ.TradeService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
period := c.DefaultQuery("period", "day")
|
||||
segment := c.DefaultQuery("segment", "exchange")
|
||||
startTimeStr := c.Query("start-time")
|
||||
|
||||
var startTime time.Time
|
||||
|
||||
if startTimeStr != "" {
|
||||
v, err := time.Parse(time.RFC3339, startTimeStr)
|
||||
if err != nil {
|
||||
c.Status(http.StatusBadRequest)
|
||||
logrus.WithError(err).Error("start-time format incorrect")
|
||||
return
|
||||
}
|
||||
startTime = v
|
||||
|
||||
} else {
|
||||
switch period {
|
||||
case "day":
|
||||
startTime = time.Now().AddDate(0, 0, -30)
|
||||
|
||||
case "month":
|
||||
startTime = time.Now().AddDate(0, -6, 0)
|
||||
|
||||
case "year":
|
||||
startTime = time.Now().AddDate(-2, 0, 0)
|
||||
|
||||
default:
|
||||
startTime = time.Now().AddDate(0, 0, -7)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := environ.TradeService.QueryTradingVolume(startTime, service.TradingVolumeQueryOptions{
|
||||
SegmentBy: segment,
|
||||
GroupByPeriod: period,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("trading volume query error")
|
||||
c.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"tradingVolumes": rows})
|
||||
return
|
||||
})
|
||||
r.GET("/api/orders/closed", s.listClosedOrders)
|
||||
r.GET("/api/trading-volume", s.tradingVolume)
|
||||
|
||||
r.POST("/api/sessions/test", func(c *gin.Context) {
|
||||
var sessionConfig bbgo.ExchangeSession
|
||||
|
@ -212,12 +126,12 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
var anyErr error
|
||||
_, openOrdersErr := session.Exchange.QueryOpenOrders(ctx, "BTCUSDT")
|
||||
_, openOrdersErr := session.Exchange.QueryOpenOrders(c, "BTCUSDT")
|
||||
if openOrdersErr != nil {
|
||||
anyErr = openOrdersErr
|
||||
}
|
||||
|
||||
_, balanceErr := session.Exchange.QueryAccountBalances(ctx)
|
||||
_, balanceErr := session.Exchange.QueryAccountBalances(c)
|
||||
if balanceErr != nil {
|
||||
anyErr = balanceErr
|
||||
}
|
||||
|
@ -232,7 +146,7 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
|
||||
r.GET("/api/sessions", func(c *gin.Context) {
|
||||
var sessions []*bbgo.ExchangeSession
|
||||
for _, session := range environ.Sessions() {
|
||||
for _, session := range s.Environ.Sessions() {
|
||||
sessions = append(sessions, session)
|
||||
}
|
||||
|
||||
|
@ -260,120 +174,33 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
return
|
||||
}
|
||||
|
||||
if userConfig.Sessions == nil {
|
||||
userConfig.Sessions = make(map[string]*bbgo.ExchangeSession)
|
||||
if s.Config.Sessions == nil {
|
||||
s.Config.Sessions = make(map[string]*bbgo.ExchangeSession)
|
||||
}
|
||||
userConfig.Sessions[sessionConfig.Name] = session
|
||||
s.Config.Sessions[sessionConfig.Name] = session
|
||||
|
||||
environ.AddExchangeSession(sessionConfig.Name, session)
|
||||
s.Environ.AddExchangeSession(sessionConfig.Name, session)
|
||||
|
||||
if err := session.Init(ctx, environ); err != nil {
|
||||
if err := session.Init(c, s.Environ); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
})
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
})
|
||||
|
||||
r.GET("/api/assets", func(c *gin.Context) {
|
||||
totalAssets := types.AssetMap{}
|
||||
|
||||
for _, session := range environ.Sessions() {
|
||||
balances := session.Account.Balances()
|
||||
|
||||
if err := session.UpdatePrices(ctx); err != nil {
|
||||
logrus.WithError(err).Error("price update failed")
|
||||
c.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
assets := balances.Assets(session.LastPrices())
|
||||
|
||||
for currency, asset := range assets {
|
||||
totalAssets[currency] = asset
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"assets": totalAssets})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions/:session", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"session": session})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions/:session/trades", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"trades": session.Trades})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions/:session/open-orders", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
marketOrders := make(map[string][]types.Order)
|
||||
for symbol, orderStore := range session.OrderStores() {
|
||||
marketOrders[symbol] = orderStore.Orders()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"orders": marketOrders})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions/:session/account", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"account": session.Account})
|
||||
})
|
||||
|
||||
r.GET("/api/sessions/:session/account/balances", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
if session.Account == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("the account of session %s is nil", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"balances": session.Account.Balances()})
|
||||
})
|
||||
r.GET("/api/assets", s.listAssets)
|
||||
r.GET("/api/sessions/:session", s.listSessions)
|
||||
r.GET("/api/sessions/:session/trades", s.listSessionTrades)
|
||||
r.GET("/api/sessions/:session/open-orders", s.listSessionOpenOrders)
|
||||
r.GET("/api/sessions/:session/account", s.getSessionAccount)
|
||||
r.GET("/api/sessions/:session/account/balances", s.getSessionAccountBalance)
|
||||
|
||||
r.GET("/api/sessions/:session/symbols", func(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := environ.Session(sessionName)
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
|
@ -407,29 +234,67 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
r.GET("/api/strategies/single", s.listStrategies)
|
||||
r.NoRoute(s.pkgerHandler)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Server) Run(ctx context.Context, bindArgs ...string) error {
|
||||
r := s.newEngine()
|
||||
bind := resolveBind(bindArgs)
|
||||
if s.OpenInBrowser {
|
||||
if runtime.GOOS == "darwin" {
|
||||
baseURL := "http://" + DefaultBindAddress
|
||||
if len(s.Bind) > 0 {
|
||||
baseURL = "http://" + s.Bind
|
||||
}
|
||||
|
||||
go pingAndOpenURL(ctx, baseURL)
|
||||
} else {
|
||||
logrus.Warnf("%s is not supported for opening browser automatically", runtime.GOOS)
|
||||
}
|
||||
s.openBrowser(ctx, bind)
|
||||
}
|
||||
|
||||
bind := DefaultBindAddress
|
||||
if len(s.Bind) > 0 {
|
||||
bind = s.Bind
|
||||
s.srv = newServer(r, bind)
|
||||
return listenAndServe(s.srv)
|
||||
}
|
||||
|
||||
func (s *Server) openBrowser(ctx context.Context, bind string) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
baseURL := "http://" + bind
|
||||
go pingAndOpenURL(ctx, baseURL)
|
||||
} else {
|
||||
logrus.Warnf("%s is not supported for opening browser automatically", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func resolveBind(a []string) string {
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return DefaultBindAddress
|
||||
|
||||
case 1:
|
||||
return a[0]
|
||||
|
||||
default:
|
||||
panic("too many parameters for binding")
|
||||
}
|
||||
|
||||
s.srv = &http.Server{
|
||||
return ""
|
||||
}
|
||||
|
||||
func newServer(r http.Handler, bind string) *http.Server {
|
||||
return &http.Server{
|
||||
Addr: bind,
|
||||
Handler: r,
|
||||
}
|
||||
}
|
||||
|
||||
func serve(srv *http.Server, l net.Listener) (err error) {
|
||||
defer func() {
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
logrus.WithError(err).Error("unexpected http server error")
|
||||
}
|
||||
}()
|
||||
|
||||
err = srv.Serve(l)
|
||||
if err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listenAndServe(srv *http.Server) error {
|
||||
var err error
|
||||
|
||||
defer func() {
|
||||
|
@ -438,7 +303,7 @@ func (s *Server) Run(ctx context.Context) error {
|
|||
}
|
||||
}()
|
||||
|
||||
err = s.srv.ListenAndServe()
|
||||
err = srv.ListenAndServe()
|
||||
if err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
@ -576,6 +441,40 @@ func (s *Server) setupRestart(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
func (s *Server) listClosedOrders(c *gin.Context) {
|
||||
if s.Environ.OrderService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
exchange := c.Query("exchange")
|
||||
symbol := c.Query("symbol")
|
||||
gidStr := c.DefaultQuery("gid", "0")
|
||||
|
||||
lastGID, err := strconv.ParseInt(gidStr, 10, 64)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("last gid parse error")
|
||||
c.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
orders, err := s.Environ.OrderService.Query(service.QueryOrdersOptions{
|
||||
Exchange: types.ExchangeName(exchange),
|
||||
Symbol: symbol,
|
||||
LastGID: lastGID,
|
||||
Ordering: "DESC",
|
||||
})
|
||||
if err != nil {
|
||||
c.Status(http.StatusBadRequest)
|
||||
logrus.WithError(err).Error("order query error")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"orders": orders,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) listStrategies(c *gin.Context) {
|
||||
var stashes []map[string]interface{}
|
||||
|
||||
|
@ -597,23 +496,113 @@ func (s *Server) listStrategies(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"strategies": stashes})
|
||||
}
|
||||
|
||||
func (s *Server) setupSaveConfig(c *gin.Context) {
|
||||
userConfig := s.Config
|
||||
environ := s.Environ
|
||||
func (s *Server) listSessions(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if len(userConfig.Sessions) == 0 {
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"session": session})
|
||||
}
|
||||
|
||||
func (s *Server) listSessionTrades(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"trades": session.Trades})
|
||||
}
|
||||
|
||||
func (s *Server) getSessionAccount(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"account": session.Account})
|
||||
}
|
||||
|
||||
func (s *Server) getSessionAccountBalance(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
if session.Account == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("the account of session %s is nil", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"balances": session.Account.Balances()})
|
||||
}
|
||||
|
||||
func (s *Server) listSessionOpenOrders(c *gin.Context) {
|
||||
sessionName := c.Param("session")
|
||||
session, ok := s.Environ.Session(sessionName)
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": fmt.Sprintf("session %s not found", sessionName)})
|
||||
return
|
||||
}
|
||||
|
||||
marketOrders := make(map[string][]types.Order)
|
||||
for symbol, orderStore := range session.OrderStores() {
|
||||
marketOrders[symbol] = orderStore.Orders()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"orders": marketOrders})
|
||||
}
|
||||
|
||||
func (s *Server) listAssets(c *gin.Context) {
|
||||
totalAssets := types.AssetMap{}
|
||||
|
||||
for _, session := range s.Environ.Sessions() {
|
||||
balances := session.Account.Balances()
|
||||
|
||||
if err := session.UpdatePrices(c); err != nil {
|
||||
logrus.WithError(err).Error("price update failed")
|
||||
c.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
assets := balances.Assets(session.LastPrices())
|
||||
|
||||
for currency, asset := range assets {
|
||||
totalAssets[currency] = asset
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"assets": totalAssets})
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) setupSaveConfig(c *gin.Context) {
|
||||
if len(s.Config.Sessions) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "session is not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
envVars, err := collectSessionEnvVars(userConfig.Sessions)
|
||||
envVars, err := collectSessionEnvVars(s.Config.Sessions)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if len(s.Environ.MysqlURL) > 0 {
|
||||
envVars["MYSQL_URL"] = environ.MysqlURL
|
||||
envVars["MYSQL_URL"] = s.Environ.MysqlURL
|
||||
}
|
||||
|
||||
dotenvFile := ".env.local"
|
||||
|
@ -627,7 +616,7 @@ func (s *Server) setupSaveConfig(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
out, err := userConfig.YAML()
|
||||
out, err := s.Config.YAML()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
|
@ -711,6 +700,58 @@ func collectSessionEnvVars(sessions map[string]*bbgo.ExchangeSession) (envVars m
|
|||
return
|
||||
}
|
||||
|
||||
func (s *Server) tradingVolume(c *gin.Context) {
|
||||
if s.Environ.TradeService == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "database is not configured"})
|
||||
return
|
||||
}
|
||||
|
||||
period := c.DefaultQuery("period", "day")
|
||||
segment := c.DefaultQuery("segment", "exchange")
|
||||
startTimeStr := c.Query("start-time")
|
||||
|
||||
var startTime time.Time
|
||||
|
||||
if startTimeStr != "" {
|
||||
v, err := time.Parse(time.RFC3339, startTimeStr)
|
||||
if err != nil {
|
||||
c.Status(http.StatusBadRequest)
|
||||
logrus.WithError(err).Error("start-time format incorrect")
|
||||
return
|
||||
}
|
||||
startTime = v
|
||||
|
||||
} else {
|
||||
switch period {
|
||||
case "day":
|
||||
startTime = time.Now().AddDate(0, 0, -30)
|
||||
|
||||
case "month":
|
||||
startTime = time.Now().AddDate(0, -6, 0)
|
||||
|
||||
case "year":
|
||||
startTime = time.Now().AddDate(-2, 0, 0)
|
||||
|
||||
default:
|
||||
startTime = time.Now().AddDate(0, 0, -7)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := s.Environ.TradeService.QueryTradingVolume(startTime, service.TradingVolumeQueryOptions{
|
||||
SegmentBy: segment,
|
||||
GroupByPeriod: period,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("trading volume query error")
|
||||
c.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"tradingVolumes": rows})
|
||||
return
|
||||
}
|
||||
|
||||
func getJSON(url string, data interface{}) error {
|
||||
var client = &http.Client{Timeout: 500 * time.Millisecond}
|
||||
r, err := client.Get(url)
|
||||
|
|
Loading…
Reference in New Issue
Block a user