github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/dashboard/dashboardserver/api.go (about)

     1  package dashboardserver
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"net/http"
     8  	"path"
     9  	"time"
    10  
    11  	"github.com/gin-contrib/static"
    12  	"github.com/gin-gonic/gin"
    13  	"github.com/spf13/viper"
    14  	"github.com/turbot/steampipe/pkg/constants"
    15  	"github.com/turbot/steampipe/pkg/error_helpers"
    16  	"github.com/turbot/steampipe/pkg/filepaths"
    17  	"gopkg.in/olahol/melody.v1"
    18  )
    19  
    20  func startAPIAsync(ctx context.Context, webSocket *melody.Melody) chan struct{} {
    21  	doneChan := make(chan struct{})
    22  
    23  	go func() {
    24  		gin.SetMode(gin.ReleaseMode)
    25  		router := gin.New()
    26  		// only add the Recovery middleware
    27  		router.Use(gin.Recovery())
    28  
    29  		assetsDirectory := filepaths.EnsureDashboardAssetsDir()
    30  
    31  		router.Use(static.Serve("/", static.LocalFile(assetsDirectory, true)))
    32  
    33  		router.GET("/ws", func(c *gin.Context) {
    34  			webSocket.HandleRequest(c.Writer, c.Request)
    35  		})
    36  
    37  		router.NoRoute(func(c *gin.Context) {
    38  			// https://stackoverflow.com/questions/49547/how-do-we-control-web-page-caching-across-all-browsers
    39  			c.Header("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
    40  			c.Header("Pragma", "no-cache")                                   // HTTP 1.0.
    41  			c.Header("Expires", "0")                                         // Proxies.
    42  			c.File(path.Join(assetsDirectory, "index.html"))
    43  		})
    44  
    45  		dashboardServerPort := viper.GetInt(constants.ArgDashboardPort)
    46  		dashboardServerListen := "localhost"
    47  		if viper.GetString(constants.ArgDashboardListen) == string(ListenTypeNetwork) {
    48  			dashboardServerListen = ""
    49  		}
    50  
    51  		srv := &http.Server{
    52  			Addr:    fmt.Sprintf("%s:%d", dashboardServerListen, dashboardServerPort),
    53  			Handler: router,
    54  		}
    55  
    56  		go func() {
    57  			// service connections
    58  			if err := srv.ListenAndServe(); err != nil {
    59  				log.Printf("listen: %s\n", err)
    60  			}
    61  		}()
    62  
    63  		outputReady(ctx, fmt.Sprintf("Dashboard server started on %d and listening on %s", dashboardServerPort, viper.GetString(constants.ArgDashboardListen)))
    64  		OutputMessage(ctx, fmt.Sprintf("Visit http://localhost:%d", dashboardServerPort))
    65  		OutputMessage(ctx, "Press Ctrl+C to exit")
    66  		<-ctx.Done()
    67  		log.Println("Shutdown Server…")
    68  
    69  		shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    70  		defer cancel()
    71  
    72  		if err := srv.Shutdown(shutdownCtx); err != nil {
    73  			error_helpers.ShowErrorWithMessage(ctx, err, "Server shutdown failed")
    74  		}
    75  		log.Println("[TRACE] Server exiting")
    76  
    77  		// indicate the API server is done
    78  		doneChan <- struct{}{}
    79  	}()
    80  
    81  	return doneChan
    82  }