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

     1  // This file is part of the Smart Home
     2  // Program complex distribution https://github.com/e154/smart-home
     3  // Copyright (C) 2016-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 libraryc 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 api
    20  
    21  import (
    22  	"context"
    23  	"crypto/tls"
    24  	"fmt"
    25  	"net/http"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    30  	echopprof "github.com/hiko1129/echo-pprof"
    31  	echoCacheMiddleware "github.com/kenshin579/echo-http-cache"
    32  	"github.com/labstack/echo/v4"
    33  	"github.com/labstack/echo/v4/middleware"
    34  	"go.uber.org/atomic"
    35  
    36  	"github.com/e154/smart-home/adaptors"
    37  	"github.com/e154/smart-home/api/controllers"
    38  	"github.com/e154/smart-home/api/stub"
    39  	publicAssets "github.com/e154/smart-home/build"
    40  	"github.com/e154/smart-home/common/events"
    41  	"github.com/e154/smart-home/common/logger"
    42  	"github.com/e154/smart-home/system/bus"
    43  	"github.com/e154/smart-home/system/rbac"
    44  )
    45  
    46  var (
    47  	log = logger.MustGetLogger("api")
    48  )
    49  
    50  // Api ...
    51  type Api struct {
    52  	controllers *controllers.Controllers
    53  	echoFilter  *rbac.EchoAccessFilter
    54  	echo        *echo.Echo
    55  	cfg         Config
    56  	certPublic  string
    57  	certKey     string
    58  	adaptors    *adaptors.Adaptors
    59  	eventBus    bus.Bus
    60  	httpServer  http.Server
    61  	tlsServer   http.Server
    62  	tlsStarted  *atomic.Bool
    63  }
    64  
    65  // NewApi ...
    66  func NewApi(controllers *controllers.Controllers,
    67  	echoFilter *rbac.EchoAccessFilter,
    68  	cfg Config,
    69  	eventBus bus.Bus,
    70  	adaptors *adaptors.Adaptors) (api *Api) {
    71  	api = &Api{
    72  		controllers: controllers,
    73  		echoFilter:  echoFilter,
    74  		cfg:         cfg,
    75  		adaptors:    adaptors,
    76  		eventBus:    eventBus,
    77  		tlsStarted:  atomic.NewBool(false),
    78  	}
    79  	return
    80  }
    81  
    82  // Start ...
    83  func (a *Api) Start() (err error) {
    84  
    85  	// HTTP
    86  	a.echo = echo.New()
    87  	a.echo.Use(middleware.BodyLimitWithConfig(middleware.BodyLimitConfig{
    88  		Skipper: middleware.DefaultSkipper,
    89  		Limit:   "128M",
    90  	}))
    91  	a.echo.Use(controllers.NewMiddlewareContextValue)
    92  	a.echo.Use(middleware.Recover())
    93  
    94  	if a.cfg.Debug {
    95  		var format = `INFO	api/v1	[${method}] ${uri} ${status} ${latency_human} ${error}` + "\n"
    96  
    97  		log.Info("debug enabled")
    98  		DefaultLoggerConfig := middleware.LoggerConfig{
    99  			Skipper:          middleware.DefaultSkipper,
   100  			Format:           format,
   101  			CustomTimeFormat: "2006-01-02 15:04:05.00000",
   102  		}
   103  		a.echo.Use(middleware.LoggerWithConfig(DefaultLoggerConfig))
   104  		a.echo.Debug = true
   105  	}
   106  
   107  	if a.cfg.Pprof {
   108  		// automatically add routers for net/http/pprof
   109  		// e.g. /debug/pprof, /debug/pprof/heap, etc.
   110  		log.Info("pprof enabled")
   111  		echopprof.Wrap(a.echo)
   112  
   113  		prefix := "/debug/pprof"
   114  		group := a.echo.Group(prefix)
   115  		echopprof.WrapGroup(prefix, group)
   116  	}
   117  
   118  	a.echo.HideBanner = true
   119  	a.echo.HidePort = true
   120  
   121  	if a.cfg.Gzip {
   122  		a.echo.Use(middleware.GzipWithConfig(middleware.DefaultGzipConfig))
   123  		a.echo.Use(middleware.Decompress())
   124  		a.echo.Use(echoCacheMiddleware.CacheWithConfig(echoCacheMiddleware.CacheConfig{
   125  			Store: echoCacheMiddleware.NewCacheMemoryStoreWithConfig(echoCacheMiddleware.CacheMemoryStoreConfig{
   126  				Capacity:  5,
   127  				Algorithm: echoCacheMiddleware.LFU,
   128  			}),
   129  			Expiration: 10 * time.Second,
   130  		}))
   131  
   132  	}
   133  
   134  	a.registerHandlers()
   135  
   136  	go a.startTlsServer()
   137  	go a.startServer()
   138  
   139  	a.eventBus.Subscribe("system/models/variables/+", a.eventHandler, false)
   140  	a.eventBus.Publish("system/services/api", events.EventServiceStarted{Service: "Api"})
   141  
   142  	return nil
   143  }
   144  
   145  // Shutdown ...
   146  func (a *Api) Shutdown(ctx context.Context) (err error) {
   147  	a.httpServer.Shutdown(ctx)
   148  	a.tlsServer.Shutdown(ctx)
   149  	if a.echo != nil {
   150  		err = a.echo.Shutdown(ctx)
   151  	}
   152  	a.eventBus.Unsubscribe("system/models/variables/+", a.eventHandler)
   153  	a.eventBus.Publish("system/services/api", events.EventServiceStopped{Service: "Api"})
   154  
   155  	return
   156  }
   157  
   158  func (a *Api) startServer() {
   159  	log.Infof("HTTP Server started at :%d", a.cfg.HttpPort)
   160  	a.httpServer = http.Server{
   161  		Addr:    fmt.Sprintf(":%d", a.cfg.HttpPort),
   162  		Handler: a.echo,
   163  	}
   164  	if err := a.httpServer.ListenAndServe(); err != http.ErrServerClosed {
   165  		log.Errorf("error when starting HTTP server: %w", err)
   166  	} else {
   167  		log.Info("HTTP server stopped serving requests")
   168  	}
   169  }
   170  
   171  func (a *Api) startTlsServer() {
   172  	if !a.tlsStarted.CompareAndSwap(false, true) {
   173  		return
   174  	}
   175  	defer a.tlsStarted.Store(false)
   176  
   177  	a.getCerts()
   178  	if a.certPublic == "" || a.certKey == "" {
   179  		return
   180  	}
   181  
   182  	// Generate a key pair from your pem-encoded cert and key ([]byte).
   183  	cert, err := tls.X509KeyPair([]byte(a.certPublic), []byte(a.certKey))
   184  	if err != nil {
   185  		log.Error(err.Error())
   186  		return
   187  	}
   188  	log.Infof("HTTPS Server started at :%d", a.cfg.HttpsPort)
   189  
   190  	a.tlsServer = http.Server{
   191  		Addr:    fmt.Sprintf(":%d", a.cfg.HttpsPort),
   192  		Handler: a.echo,
   193  		TLSConfig: &tls.Config{
   194  			Certificates: []tls.Certificate{cert},
   195  		},
   196  	}
   197  	if err = a.tlsServer.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
   198  		log.Errorf("error when starting HTTPS server: %w", err)
   199  	} else {
   200  		log.Info("HTTPS server stopped serving requests")
   201  	}
   202  }
   203  
   204  // CustomMatcher ...
   205  func (a *Api) CustomMatcher(key string) (string, bool) {
   206  	switch key {
   207  	case "X-Api-Key":
   208  		return key, true
   209  	default:
   210  		return runtime.DefaultHeaderMatcher(key)
   211  	}
   212  }
   213  
   214  func (a *Api) registerHandlers() {
   215  
   216  	// Swagger
   217  	if a.cfg.Swagger {
   218  		var contentHandler = echo.WrapHandler(http.FileServer(http.FS(SwaggerAssets)))
   219  		a.echo.GET("/swagger-ui", contentHandler)
   220  		a.echo.GET("/swagger-ui/*", contentHandler)
   221  		a.echo.GET("/api.swagger3.yaml", contentHandler)
   222  	}
   223  
   224  	var typedocHandler = echo.WrapHandler(http.FileServer(http.FS(TypedocAssets)))
   225  	a.echo.GET("/typedoc", typedocHandler)
   226  	a.echo.GET("/typedoc/*", typedocHandler)
   227  
   228  	wrapper := stub.ServerInterfaceWrapper{
   229  		Handler: a.controllers,
   230  	}
   231  
   232  	v1 := a.echo.Group("/v1")
   233  	v1.GET("/access_list", a.echoFilter.Auth(wrapper.AuthServiceAccessList))
   234  	v1.POST("/action", a.echoFilter.Auth(wrapper.ActionServiceAddAction))
   235  	v1.DELETE("/action/:id", a.echoFilter.Auth(wrapper.ActionServiceDeleteAction))
   236  	v1.GET("/action/:id", a.echoFilter.Auth(wrapper.ActionServiceGetActionById))
   237  	v1.PUT("/action/:id", a.echoFilter.Auth(wrapper.ActionServiceUpdateAction))
   238  	v1.GET("/actions", a.echoFilter.Auth(wrapper.ActionServiceGetActionList))
   239  	v1.GET("/actions/search", a.echoFilter.Auth(wrapper.ActionServiceSearchAction))
   240  	v1.POST("/area", a.echoFilter.Auth(wrapper.AreaServiceAddArea))
   241  	v1.DELETE("/area/:id", a.echoFilter.Auth(wrapper.AreaServiceDeleteArea))
   242  	v1.GET("/area/:id", a.echoFilter.Auth(wrapper.AreaServiceGetAreaById))
   243  	v1.PUT("/area/:id", a.echoFilter.Auth(wrapper.AreaServiceUpdateArea))
   244  	v1.GET("/areas", a.echoFilter.Auth(wrapper.AreaServiceGetAreaList))
   245  	v1.GET("/areas/search", a.echoFilter.Auth(wrapper.AreaServiceSearchArea))
   246  	v1.GET("/backups", a.echoFilter.Auth(wrapper.BackupServiceGetBackupList))
   247  	v1.POST("/backups", a.echoFilter.Auth(wrapper.BackupServiceNewBackup))
   248  	v1.POST("/backup/upload", a.echoFilter.Auth(wrapper.BackupServiceUploadBackup))
   249  	v1.POST("/backup/apply", a.echoFilter.Auth(wrapper.BackupServiceApplyState))
   250  	v1.POST("/backup/rollback", a.echoFilter.Auth(wrapper.BackupServiceRevertState))
   251  	v1.PUT("/backup/:name", a.echoFilter.Auth(wrapper.BackupServiceRestoreBackup))
   252  	v1.DELETE("/backup/:name", a.echoFilter.Auth(wrapper.BackupServiceDeleteBackup))
   253  	v1.POST("/condition", a.echoFilter.Auth(wrapper.ConditionServiceAddCondition))
   254  	v1.DELETE("/condition/:id", a.echoFilter.Auth(wrapper.ConditionServiceDeleteCondition))
   255  	v1.GET("/condition/:id", a.echoFilter.Auth(wrapper.ConditionServiceGetConditionById))
   256  	v1.PUT("/condition/:id", a.echoFilter.Auth(wrapper.ConditionServiceUpdateCondition))
   257  	v1.GET("/conditions", a.echoFilter.Auth(wrapper.ConditionServiceGetConditionList))
   258  	v1.GET("/conditions/search", a.echoFilter.Auth(wrapper.ConditionServiceSearchCondition))
   259  	v1.POST("/dashboard", a.echoFilter.Auth(wrapper.DashboardServiceAddDashboard))
   260  	v1.DELETE("/dashboard/:id", a.echoFilter.Auth(wrapper.DashboardServiceDeleteDashboard))
   261  	v1.GET("/dashboard/:id", a.echoFilter.Auth(wrapper.DashboardServiceGetDashboardById))
   262  	v1.PUT("/dashboard/:id", a.echoFilter.Auth(wrapper.DashboardServiceUpdateDashboard))
   263  	v1.POST("/dashboard_card", a.echoFilter.Auth(wrapper.DashboardCardServiceAddDashboardCard))
   264  	v1.POST("/dashboard_card/import", a.echoFilter.Auth(wrapper.DashboardCardServiceImportDashboardCard))
   265  	v1.DELETE("/dashboard_card/:id", a.echoFilter.Auth(wrapper.DashboardCardServiceDeleteDashboardCard))
   266  	v1.GET("/dashboard_card/:id", a.echoFilter.Auth(wrapper.DashboardCardServiceGetDashboardCardById))
   267  	v1.PUT("/dashboard_card/:id", a.echoFilter.Auth(wrapper.DashboardCardServiceUpdateDashboardCard))
   268  	v1.POST("/dashboard_card_item", a.echoFilter.Auth(wrapper.DashboardCardItemServiceAddDashboardCardItem))
   269  	v1.DELETE("/dashboard_card_item/:id", a.echoFilter.Auth(wrapper.DashboardCardItemServiceDeleteDashboardCardItem))
   270  	v1.GET("/dashboard_card_item/:id", a.echoFilter.Auth(wrapper.DashboardCardItemServiceGetDashboardCardItemById))
   271  	v1.PUT("/dashboard_card_item/:id", a.echoFilter.Auth(wrapper.DashboardCardItemServiceUpdateDashboardCardItem))
   272  	v1.GET("/dashboard_card_items", a.echoFilter.Auth(wrapper.DashboardCardItemServiceGetDashboardCardItemList))
   273  	v1.GET("/dashboard_cards", a.echoFilter.Auth(wrapper.DashboardCardServiceGetDashboardCardList))
   274  	v1.POST("/dashboard_tab", a.echoFilter.Auth(wrapper.DashboardTabServiceAddDashboardTab))
   275  	v1.DELETE("/dashboard_tab/:id", a.echoFilter.Auth(wrapper.DashboardTabServiceDeleteDashboardTab))
   276  	v1.GET("/dashboard_tab/:id", a.echoFilter.Auth(wrapper.DashboardTabServiceGetDashboardTabById))
   277  	v1.PUT("/dashboard_tab/:id", a.echoFilter.Auth(wrapper.DashboardTabServiceUpdateDashboardTab))
   278  	v1.POST("/dashboard_tabs/import", a.echoFilter.Auth(wrapper.DashboardTabServiceImportDashboardTab))
   279  	v1.GET("/dashboard_tabs", a.echoFilter.Auth(wrapper.DashboardTabServiceGetDashboardTabList))
   280  	v1.GET("/dashboards", a.echoFilter.Auth(wrapper.DashboardServiceGetDashboardList))
   281  	v1.POST("/dashboards/import", a.echoFilter.Auth(wrapper.DashboardServiceImportDashboard))
   282  	v1.GET("/dashboards/search", a.echoFilter.Auth(wrapper.DashboardServiceSearchDashboard))
   283  	v1.POST("/developer_tools/automation/call_action", a.echoFilter.Auth(wrapper.DeveloperToolsServiceCallAction))
   284  	v1.POST("/developer_tools/automation/call_trigger", a.echoFilter.Auth(wrapper.DeveloperToolsServiceCallTrigger))
   285  	v1.GET("/developer_tools/bus/state", a.echoFilter.Auth(wrapper.DeveloperToolsServiceGetEventBusStateList))
   286  	v1.POST("/developer_tools/entity/reload", a.echoFilter.Auth(wrapper.DeveloperToolsServiceReloadEntity))
   287  	v1.POST("/developer_tools/entity/set_state", a.echoFilter.Auth(wrapper.DeveloperToolsServiceEntitySetState))
   288  	v1.GET("/entities", a.echoFilter.Auth(wrapper.EntityServiceGetEntityList))
   289  	v1.POST("/entities/import", a.echoFilter.Auth(wrapper.EntityServiceImportEntity))
   290  	v1.POST("/entity", a.echoFilter.Auth(wrapper.EntityServiceAddEntity))
   291  	v1.GET("/entity/search", a.echoFilter.Auth(wrapper.EntityServiceSearchEntity))
   292  	v1.DELETE("/entity/:id", a.echoFilter.Auth(wrapper.EntityServiceDeleteEntity))
   293  	v1.GET("/entity/:id", a.echoFilter.Auth(wrapper.EntityServiceGetEntity))
   294  	v1.PUT("/entity/:id", a.echoFilter.Auth(wrapper.EntityServiceUpdateEntity))
   295  	v1.POST("/entity/:id/disable", a.echoFilter.Auth(wrapper.EntityServiceDisabledEntity))
   296  	v1.POST("/entity/:id/enable", a.echoFilter.Auth(wrapper.EntityServiceEnabledEntity))
   297  	v1.GET("/entity_storage", a.echoFilter.Auth(wrapper.EntityStorageServiceGetEntityStorageList))
   298  	v1.GET("/entities/statistic", a.echoFilter.Auth(wrapper.EntityServiceGetStatistic))
   299  	v1.POST("/image", a.echoFilter.Auth(wrapper.ImageServiceAddImage))
   300  	v1.POST("/image/upload", a.echoFilter.Auth(wrapper.ImageServiceUploadImage))
   301  	v1.DELETE("/image/:id", a.echoFilter.Auth(wrapper.ImageServiceDeleteImageById))
   302  	v1.GET("/image/:id", a.echoFilter.Auth(wrapper.ImageServiceGetImageById))
   303  	v1.PUT("/image/:id", a.echoFilter.Auth(wrapper.ImageServiceUpdateImageById))
   304  	v1.GET("/images", a.echoFilter.Auth(wrapper.ImageServiceGetImageList))
   305  	v1.GET("/images/filter_list", a.echoFilter.Auth(wrapper.ImageServiceGetImageFilterList))
   306  	v1.GET("/images/filtered", a.echoFilter.Auth(wrapper.ImageServiceGetImageListByDate))
   307  	v1.POST("/interact/entity/call_action", a.echoFilter.Auth(wrapper.InteractServiceEntityCallAction))
   308  	v1.GET("/logs", a.echoFilter.Auth(wrapper.LogServiceGetLogList))
   309  	v1.GET("/message_delivery", a.echoFilter.Auth(wrapper.MessageDeliveryServiceGetMessageDeliveryList))
   310  	v1.GET("/metric", a.echoFilter.Auth(wrapper.MetricServiceGetMetric))
   311  	v1.GET("/mqtt/client/:id", a.echoFilter.Auth(wrapper.MqttServiceGetClientById))
   312  	v1.GET("/mqtt/clients", a.echoFilter.Auth(wrapper.MqttServiceGetClientList))
   313  	v1.GET("/mqtt/subscriptions", a.echoFilter.Auth(wrapper.MqttServiceGetSubscriptionList))
   314  	v1.POST("/password_reset", a.echoFilter.Auth(wrapper.AuthServicePasswordReset))
   315  	v1.GET("/plugin/:name", a.echoFilter.Auth(wrapper.PluginServiceGetPlugin))
   316  	v1.POST("/plugin/:name/disable", a.echoFilter.Auth(wrapper.PluginServiceDisablePlugin))
   317  	v1.POST("/plugin/:name/enable", a.echoFilter.Auth(wrapper.PluginServiceEnablePlugin))
   318  	v1.PUT("/plugin/:name/settings", a.echoFilter.Auth(wrapper.PluginServiceUpdatePluginSettings))
   319  	v1.GET("/plugins", a.echoFilter.Auth(wrapper.PluginServiceGetPluginList))
   320  	v1.GET("/plugins/search", a.echoFilter.Auth(wrapper.PluginServiceSearchPlugin))
   321  	v1.GET("/plugin/:name/readme", a.echoFilter.Auth(wrapper.PluginServiceGetPluginReadme))
   322  	v1.POST("/role", a.echoFilter.Auth(wrapper.RoleServiceAddRole))
   323  	v1.DELETE("/role/:name", a.echoFilter.Auth(wrapper.RoleServiceDeleteRoleByName))
   324  	v1.GET("/role/:name", a.echoFilter.Auth(wrapper.RoleServiceGetRoleByName))
   325  	v1.PUT("/role/:name", a.echoFilter.Auth(wrapper.RoleServiceUpdateRoleByName))
   326  	v1.GET("/role/:name/access_list", a.echoFilter.Auth(wrapper.RoleServiceGetRoleAccessList))
   327  	v1.PUT("/role/:name/access_list", a.echoFilter.Auth(wrapper.RoleServiceUpdateRoleAccessList))
   328  	v1.GET("/roles", a.echoFilter.Auth(wrapper.RoleServiceGetRoleList))
   329  	v1.GET("/roles/search", a.echoFilter.Auth(wrapper.RoleServiceSearchRoleByName))
   330  	v1.POST("/script", a.echoFilter.Auth(wrapper.ScriptServiceAddScript))
   331  	v1.POST("/script/exec_src", a.echoFilter.Auth(wrapper.ScriptServiceExecSrcScriptById))
   332  	v1.DELETE("/script/:id", a.echoFilter.Auth(wrapper.ScriptServiceDeleteScriptById))
   333  	v1.GET("/script/:id", a.echoFilter.Auth(wrapper.ScriptServiceGetScriptById))
   334  	v1.GET("/script/:id/compiled", a.echoFilter.Auth(wrapper.ScriptServiceGetCompiledScriptById))
   335  	v1.PUT("/script/:id", a.echoFilter.Auth(wrapper.ScriptServiceUpdateScriptById))
   336  	v1.POST("/script/:id/copy", a.echoFilter.Auth(wrapper.ScriptServiceCopyScriptById))
   337  	v1.POST("/script/:id/exec", a.echoFilter.Auth(wrapper.ScriptServiceExecScriptById))
   338  	v1.GET("/scripts", a.echoFilter.Auth(wrapper.ScriptServiceGetScriptList))
   339  	v1.GET("/scripts/search", a.echoFilter.Auth(wrapper.ScriptServiceSearchScript))
   340  	v1.GET("/scripts/statistic", a.echoFilter.Auth(wrapper.ScriptServiceGetStatistic))
   341  	v1.GET("/tags/search", a.echoFilter.Auth(wrapper.TagServiceSearchTag))
   342  	v1.GET("/tags", a.echoFilter.Auth(wrapper.TagServiceGetTagList))
   343  	v1.DELETE("/tag/:id", a.echoFilter.Auth(wrapper.TagServiceDeleteTagById))
   344  	v1.GET("/tag/:id", a.echoFilter.Auth(wrapper.TagServiceGetTagById))
   345  	v1.PUT("/tag/:id", a.echoFilter.Auth(wrapper.TagServiceUpdateTagById))
   346  	v1.POST("/signin", wrapper.AuthServiceSignin)
   347  	v1.POST("/signout", a.echoFilter.Auth(wrapper.AuthServiceSignout))
   348  	v1.POST("/task", a.echoFilter.Auth(wrapper.AutomationServiceAddTask))
   349  	v1.DELETE("/task/:id", a.echoFilter.Auth(wrapper.AutomationServiceDeleteTask))
   350  	v1.GET("/task/:id", a.echoFilter.Auth(wrapper.AutomationServiceGetTask))
   351  	v1.PUT("/task/:id", a.echoFilter.Auth(wrapper.AutomationServiceUpdateTask))
   352  	v1.POST("/task/:id/disable", a.echoFilter.Auth(wrapper.AutomationServiceDisableTask))
   353  	v1.POST("/task/:id/enable", a.echoFilter.Auth(wrapper.AutomationServiceEnableTask))
   354  	v1.GET("/tasks", a.echoFilter.Auth(wrapper.AutomationServiceGetTaskList))
   355  	v1.POST("/tasks/import", a.echoFilter.Auth(wrapper.AutomationServiceImportTask))
   356  	v1.POST("/trigger", a.echoFilter.Auth(wrapper.TriggerServiceAddTrigger))
   357  	v1.DELETE("/trigger/:id", a.echoFilter.Auth(wrapper.TriggerServiceDeleteTrigger))
   358  	v1.GET("/trigger/:id", a.echoFilter.Auth(wrapper.TriggerServiceGetTriggerById))
   359  	v1.PUT("/trigger/:id", a.echoFilter.Auth(wrapper.TriggerServiceUpdateTrigger))
   360  	v1.GET("/triggers", a.echoFilter.Auth(wrapper.TriggerServiceGetTriggerList))
   361  	v1.GET("/triggers/search", a.echoFilter.Auth(wrapper.TriggerServiceSearchTrigger))
   362  	v1.POST("/triggers/:id/disable", a.echoFilter.Auth(wrapper.TriggerServiceDisableTrigger))
   363  	v1.POST("/triggers/:id/enable", a.echoFilter.Auth(wrapper.TriggerServiceEnableTrigger))
   364  	v1.POST("/user", a.echoFilter.Auth(wrapper.UserServiceAddUser))
   365  	v1.DELETE("/user/:id", a.echoFilter.Auth(wrapper.UserServiceDeleteUserById))
   366  	v1.GET("/user/:id", a.echoFilter.Auth(wrapper.UserServiceGetUserById))
   367  	v1.PUT("/user/:id", a.echoFilter.Auth(wrapper.UserServiceUpdateUserById))
   368  	v1.GET("/users", a.echoFilter.Auth(wrapper.UserServiceGetUserList))
   369  	v1.POST("/variable", a.echoFilter.Auth(wrapper.VariableServiceAddVariable))
   370  	v1.DELETE("/variable/:name", a.echoFilter.Auth(wrapper.VariableServiceDeleteVariable))
   371  	v1.GET("/variable/:name", a.echoFilter.Auth(wrapper.VariableServiceGetVariableByName))
   372  	v1.PUT("/variable/:name", a.echoFilter.Auth(wrapper.VariableServiceUpdateVariable))
   373  	v1.GET("/variables", a.echoFilter.Auth(wrapper.VariableServiceGetVariableList))
   374  	v1.GET("/variables/search", a.echoFilter.Auth(wrapper.VariableServiceSearchVariable))
   375  	v1.GET("/zigbee2mqtt/bridge", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceGetBridgeList))
   376  	v1.POST("/zigbee2mqtt/bridge", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceAddZigbee2mqttBridge))
   377  	v1.DELETE("/zigbee2mqtt/bridge/:id", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceDeleteBridgeById))
   378  	v1.GET("/zigbee2mqtt/bridge/:id", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceGetZigbee2mqttBridge))
   379  	v1.PUT("/zigbee2mqtt/bridge/:id/bridge", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceUpdateBridgeById))
   380  	v1.GET("/zigbee2mqtt/bridge/:id/devices", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceDeviceList))
   381  	v1.GET("/zigbee2mqtt/bridge/:id/networkmap", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceNetworkmap))
   382  	v1.POST("/zigbee2mqtt/bridge/:id/networkmap", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceUpdateNetworkmap))
   383  	v1.POST("/zigbee2mqtt/bridge/:id/reset", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceResetBridgeById))
   384  	v1.POST("/zigbee2mqtt/device_ban", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceDeviceBan))
   385  	v1.POST("/zigbee2mqtt/device_rename", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceDeviceRename))
   386  	v1.POST("/zigbee2mqtt/device_whitelist", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceDeviceWhitelist))
   387  	v1.GET("/zigbee2mqtt/search_device", a.echoFilter.Auth(wrapper.Zigbee2mqttServiceSearchDevice))
   388  	v1.GET("/ws", a.echoFilter.Auth(wrapper.StreamServiceSubscribe))
   389  
   390  	// static files
   391  	a.echo.GET("/", echo.WrapHandler(a.controllers.Index(publicAssets.F)))
   392  	a.echo.GET("/*", echo.WrapHandler(http.FileServer(http.FS(publicAssets.F))))
   393  	a.echo.GET("/assets/*", echo.WrapHandler(http.FileServer(http.FS(publicAssets.F))))
   394  	fileServer := http.FileServer(http.Dir("./data/file_storage"))
   395  	a.echo.Any("/upload/*", echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   396  		r.RequestURI = strings.ReplaceAll(r.RequestURI, "/upload/", "/")
   397  		r.URL, _ = r.URL.Parse(r.RequestURI)
   398  		fileServer.ServeHTTP(w, r)
   399  	})))
   400  	staticServer := http.FileServer(http.Dir("./data/static"))
   401  	a.echo.Any("/static/*", echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   402  		r.RequestURI = strings.ReplaceAll(r.RequestURI, "/static/", "/")
   403  		r.URL, _ = r.URL.Parse(r.RequestURI)
   404  		staticServer.ServeHTTP(w, r)
   405  	})))
   406  	snapshotServer := http.FileServer(http.Dir("./snapshots"))
   407  	a.echo.GET("/snapshots/*", a.echoFilter.Auth(echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   408  		r.RequestURI = strings.ReplaceAll(r.RequestURI, "/snapshots/", "/")
   409  		r.URL, _ = r.URL.Parse(r.RequestURI)
   410  		snapshotServer.ServeHTTP(w, r)
   411  	}))))
   412  	// webdav
   413  	webdav := echo.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   414  		//r.RequestURI = strings.ReplaceAll(r.RequestURI, "/webdav/", "/")
   415  		//r.URL, _ = r.URL.Parse(r.RequestURI)
   416  		a.controllers.Webdav(w, r)
   417  	}))
   418  	a.echo.Any("/webdav", webdav)
   419  	a.echo.Any("/webdav/*", webdav)
   420  
   421  	// media
   422  	a.echo.Any("/stream/:entity_id/channel/:channel/mse", a.echoFilter.Auth(a.controllers.StreamMSE)) //Auth
   423  	//a.echo.Any("/stream/:entity_id/channel/:channel/hlsll/live/init.mp4", a.controllers.Media.StreamHLSLLInit)
   424  	//a.echo.Any("/stream/:entity_id/channel/:channel/hlsll/live/index.m3u8", a.controllers.Media.StreamHLSLLM3U8)
   425  	//a.echo.Any("/stream/:entity_id/channel/:channel/hlsll/live/segment/:segment/:any", a.controllers.Media.StreamHLSLLM4Segment)
   426  	//a.echo.Any("/stream/:entity_id/channel/:channel/hlsll/live/fragment/:segment/:fragment/:any", a.controllers.Media.StreamHLSLLM4Fragment)
   427  
   428  	// Cors
   429  	a.echo.Use(middleware.CORSWithConfig(middleware.CORSConfig{
   430  		AllowOrigins:     []string{"*"},
   431  		AllowHeaders:     []string{"*"},
   432  		AllowCredentials: false,
   433  		AllowMethods:     []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete, http.MethodHead},
   434  	}))
   435  
   436  }
   437  
   438  func (a *Api) Echo() *echo.Echo {
   439  	return a.echo
   440  }
   441  
   442  func (a *Api) getCerts() {
   443  	certPublicVar, err := a.adaptors.Variable.GetByName(context.Background(), "certPublic")
   444  	if err != nil {
   445  		return
   446  	}
   447  	a.certPublic = strings.TrimSpace(certPublicVar.Value)
   448  	certKeyVar, err := a.adaptors.Variable.GetByName(context.Background(), "certKey")
   449  	if err != nil {
   450  		return
   451  	}
   452  	a.certKey = strings.TrimSpace(certKeyVar.Value)
   453  }
   454  
   455  func (a *Api) eventHandler(_ string, message interface{}) {
   456  	switch v := message.(type) {
   457  	case events.EventUpdatedVariableModel:
   458  		switch v.Name {
   459  		case "certPublic", "certKey":
   460  			log.Infof("updated settings name %s", v.Name)
   461  			a.tlsServer.Shutdown(context.Background())
   462  			a.startTlsServer()
   463  		}
   464  	}
   465  }