github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/gate/server/server.go (about)

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2023, Filippov Alex
     4  //
     5  // This library is free software: you can redistribute it and/or
     6  // modify it under the terms of the GNU Lesser General Public
     7  // License as published by the Free Software Foundation; either
     8  // version 3 of the License, or (at your option) any later version.
     9  //
    10  // This library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  // Library General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public
    16  // License along with this library.  If not, see
    17  // <https://www.gnu.org/licenses/>.
    18  
    19  package server
    20  
    21  import (
    22  	"context"
    23  	"net/http"
    24  	"strings"
    25  
    26  	echopprof "github.com/hiko1129/echo-pprof"
    27  	"github.com/labstack/echo/v4"
    28  	"github.com/labstack/echo/v4/middleware"
    29  	"golang.org/x/crypto/acme/autocert"
    30  
    31  	"github.com/e154/smart-home/api"
    32  	publicAssets "github.com/e154/smart-home/build"
    33  	"github.com/e154/smart-home/system/gate/server/controllers"
    34  	"github.com/e154/smart-home/system/gate/server/wsp"
    35  )
    36  
    37  type Server struct {
    38  	controllers *controllers.Controllers
    39  	echo        *echo.Echo
    40  	proxy       *wsp.Server
    41  	cfg         *Config
    42  }
    43  
    44  func NewServer(cfg *Config, proxy *wsp.Server) *Server {
    45  	return &Server{
    46  		controllers: controllers.NewControllers("gate"),
    47  		proxy:       proxy,
    48  		cfg:         cfg,
    49  	}
    50  }
    51  
    52  // Start ...
    53  func (a *Server) Start() (err error) {
    54  
    55  	// HTTP
    56  	a.echo = echo.New()
    57  	a.echo.Use(middleware.BodyLimitWithConfig(middleware.BodyLimitConfig{
    58  		Skipper: middleware.DefaultSkipper,
    59  		Limit:   "128M",
    60  	}))
    61  	a.echo.Use(middleware.Recover())
    62  
    63  	if a.cfg.Debug {
    64  		var format = `INFO	gate	[${method}] ${uri} ${status} ${latency_human} ${error}` + "\n"
    65  
    66  		log.Info("debug enabled")
    67  		DefaultLoggerConfig := middleware.LoggerConfig{
    68  			Skipper:          middleware.DefaultSkipper,
    69  			Format:           format,
    70  			CustomTimeFormat: "2006-01-02 15:04:05.00000",
    71  		}
    72  		a.echo.Use(middleware.LoggerWithConfig(DefaultLoggerConfig))
    73  		a.echo.Debug = true
    74  	}
    75  
    76  	if a.cfg.Pprof {
    77  		// automatically add routers for net/http/pprof
    78  		// e.g. /debug/pprof, /debug/pprof/heap, etc.
    79  		log.Info("pprof enabled")
    80  		echopprof.Wrap(a.echo)
    81  
    82  		prefix := "/debug/pprof"
    83  		group := a.echo.Group(prefix)
    84  		echopprof.WrapGroup(prefix, group)
    85  	}
    86  
    87  	a.echo.HideBanner = true
    88  	a.echo.HidePort = true
    89  
    90  	if a.cfg.Gzip {
    91  		a.echo.Use(middleware.GzipWithConfig(middleware.DefaultGzipConfig))
    92  		a.echo.Use(middleware.Decompress())
    93  	}
    94  
    95  	a.registerHandlers()
    96  
    97  	var https bool
    98  	if a.cfg.Https {
    99  		if a.cfg.Domain == "" {
   100  			log.Warnf("domain settings is empty")
   101  		} else {
   102  			https = true
   103  			//a.echo.Pre(middleware.HTTPSRedirect())
   104  			// Cache certificates to avoid issues with rate limits (https://letsencrypt.org/docs/rate-limits)
   105  			a.echo.AutoTLSManager.Cache = autocert.DirCache("./conf")
   106  			domains := strings.Split(a.cfg.Domain, " ")
   107  			a.echo.AutoTLSManager.HostPolicy = autocert.HostWhitelist(domains...)
   108  		}
   109  	}
   110  
   111  	go func() {
   112  		var err error
   113  		if https {
   114  			log.Infof("server started at %s", a.cfg.HTTPSString())
   115  			err = a.echo.StartAutoTLS(a.cfg.HTTPSString())
   116  		} else {
   117  			log.Infof("server started at %s", a.cfg.HTTPString())
   118  			err = a.echo.Start(a.cfg.HTTPString())
   119  		}
   120  		if err.Error() != "http: Server closed" {
   121  			log.Error(err.Error())
   122  		}
   123  	}()
   124  
   125  	return
   126  }
   127  
   128  // Shutdown ...
   129  func (a *Server) Shutdown(ctx context.Context) (err error) {
   130  
   131  	if a.echo != nil {
   132  		err = a.echo.Shutdown(ctx)
   133  	}
   134  
   135  	return
   136  }
   137  
   138  func (a *Server) registerHandlers() {
   139  
   140  	// static files
   141  	a.echo.GET("/", echo.WrapHandler(a.controllers.Index(publicAssets.F)))
   142  	a.echo.GET("/*", echo.WrapHandler(http.FileServer(http.FS(publicAssets.F))))
   143  	a.echo.GET("/assets/*", echo.WrapHandler(http.FileServer(http.FS(publicAssets.F))))
   144  	var swaggerHandler = echo.WrapHandler(http.FileServer(http.FS(api.SwaggerAssets)))
   145  	a.echo.GET("/swagger-ui", swaggerHandler)
   146  	a.echo.GET("/swagger-ui/*", swaggerHandler)
   147  	a.echo.GET("/api.swagger3.yaml", swaggerHandler)
   148  	var typedocHandler = echo.WrapHandler(http.FileServer(http.FS(api.TypedocAssets)))
   149  	a.echo.GET("/typedoc", typedocHandler)
   150  	a.echo.GET("/typedoc/*", typedocHandler)
   151  
   152  	// proxy
   153  	a.echo.Any("/v1/*", a.proxyHandler)
   154  	a.echo.Any("/upload/*", a.proxyHandler)
   155  	a.echo.Any("/static/*", a.proxyHandler)
   156  	a.echo.Any("/snapshots/*", a.proxyHandler)
   157  	a.echo.GET("/v1/ws", func(c echo.Context) error {
   158  		a.proxy.Ws(c.Response(), c.Request())
   159  		return nil
   160  	})
   161  	a.echo.GET("/stream/*", func(c echo.Context) error {
   162  		a.proxy.Ws(c.Response(), c.Request())
   163  		return nil
   164  	})
   165  
   166  	// internal
   167  	a.echo.GET("/gate/register", func(c echo.Context) error {
   168  		a.proxy.Register(c.Response(), c.Request())
   169  		return nil
   170  	})
   171  
   172  	// Cors
   173  	a.echo.Use(middleware.CORSWithConfig(middleware.CORSConfig{
   174  		AllowOrigins:     []string{"*"},
   175  		AllowHeaders:     []string{"*"},
   176  		AllowCredentials: false,
   177  		AllowMethods:     []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete, http.MethodHead},
   178  	}))
   179  }
   180  
   181  func (a *Server) proxyHandler(c echo.Context) error {
   182  	a.proxy.Request(c.Response(), c.Request())
   183  	return nil
   184  }