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 }