diff --git a/app/server.go b/app/server.go index 312059c4a4..cb6adec74a 100644 --- a/app/server.go +++ b/app/server.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "os" + "path" "strings" "sync" "sync/atomic" @@ -41,11 +42,14 @@ import ( "github.com/mattermost/mattermost-server/v5/services/filesstore" "github.com/mattermost/mattermost-server/v5/services/httpservice" "github.com/mattermost/mattermost-server/v5/services/imageproxy" + "github.com/mattermost/mattermost-server/v5/services/mailservice" "github.com/mattermost/mattermost-server/v5/services/searchengine" "github.com/mattermost/mattermost-server/v5/services/searchengine/bleveengine" "github.com/mattermost/mattermost-server/v5/services/timezones" "github.com/mattermost/mattermost-server/v5/services/tracing" "github.com/mattermost/mattermost-server/v5/store" + "github.com/mattermost/mattermost-server/v5/store/localcachelayer" + "github.com/mattermost/mattermost-server/v5/store/searchlayer" "github.com/mattermost/mattermost-server/v5/store/sqlstore" "github.com/mattermost/mattermost-server/v5/utils" ) @@ -275,10 +279,157 @@ func NewServer(options ...Option) (*Server, error) { Size: model.STATUS_CACHE_SIZE, }) - if err := s.RunOldAppInitialization(); err != nil { + s.createPushNotificationsHub() + + if err := utils.InitTranslations(s.Config().LocalizationSettings); err != nil { + return nil, errors.Wrapf(err, "unable to load Mattermost translation files") + } + + s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) { + s.configOrLicenseListener() + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil) + + message.Add("config", s.ClientConfigWithComputed()) + s.Go(func() { + s.Publish(message) + }) + }) + s.licenseListenerId = s.AddLicenseListener(func(oldLicense, newLicense *model.License) { + s.configOrLicenseListener() + + message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil) + message.Add("license", s.GetSanitizedClientLicense()) + s.Go(func() { + s.Publish(message) + }) + + }) + + if err := s.setupInviteEmailRateLimiting(); err != nil { return nil, err } + mlog.Info("Server is initializing...") + + s.initEnterprise() + + if s.newStore == nil { + s.newStore = func() store.Store { + s.sqlStore = sqlstore.NewSqlSupplier(s.Config().SqlSettings, s.Metrics) + searchStore := searchlayer.NewSearchLayer( + localcachelayer.NewLocalCacheLayer( + s.sqlStore, + s.Metrics, + s.Cluster, + s.CacheProvider, + ), + s.SearchEngine, + s.Config(), + ) + + s.AddConfigListener(func(prevCfg, cfg *model.Config) { + searchStore.UpdateConfig(cfg) + }) + + s.sqlStore.UpdateLicense(s.License()) + s.AddLicenseListener(func(oldLicense, newLicense *model.License) { + s.sqlStore.UpdateLicense(newLicense) + }) + + return store.NewTimerLayer( + searchStore, + s.Metrics, + ) + } + } + + if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil { + mlog.Error("Failed to parse server templates", mlog.Err(err)) + } else { + s.htmlTemplateWatcher = htmlTemplateWatcher + } + + s.Store = s.newStore() + + if model.BuildEnterpriseReady == "true" { + s.LoadLicense() + } + + s.initJobs() + + if s.joinCluster && s.Cluster != nil { + s.Cluster.StartInterNodeCommunication() + } + + if err := s.ensureAsymmetricSigningKey(); err != nil { + return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key") + } + + if err := s.ensurePostActionCookieSecret(); err != nil { + return nil, errors.Wrapf(err, "unable to ensure PostAction cookie secret") + } + + if err := s.ensureInstallationDate(); err != nil { + return nil, errors.Wrapf(err, "unable to ensure installation date") + } + + if err := s.ensureFirstServerRunTimestamp(); err != nil { + return nil, errors.Wrapf(err, "unable to ensure first run timestamp") + } + + s.ensureDiagnosticId() + s.regenerateClientConfig() + + s.clusterLeaderListenerId = s.AddClusterLeaderChangedListener(func() { + mlog.Info("Cluster leader changed. Determining if job schedulers should be running:", mlog.Bool("isLeader", s.IsLeader())) + if s.Jobs != nil && s.Jobs.Schedulers != nil { + s.Jobs.Schedulers.HandleClusterLeaderChange(s.IsLeader()) + } + }) + + subpath, err := utils.GetSubpathFromConfig(s.Config()) + if err != nil { + return nil, errors.Wrap(err, "failed to parse SiteURL subpath") + } + s.Router = s.RootRouter.PathPrefix(subpath).Subrouter() + + // FakeApp: remove this when we have the ServePluginRequest and ServePluginPublicRequest migrated in the server + fakeApp := New(ServerConnector(s)) + pluginsRoute := s.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() + pluginsRoute.HandleFunc("", fakeApp.ServePluginRequest) + pluginsRoute.HandleFunc("/public/{public_file:.*}", fakeApp.ServePluginPublicRequest) + pluginsRoute.HandleFunc("/{anything:.*}", fakeApp.ServePluginRequest) + + // If configured with a subpath, redirect 404s at the root back into the subpath. + if subpath != "/" { + s.RootRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + r.URL.Path = path.Join(subpath, r.URL.Path) + http.Redirect(w, r, r.URL.String(), http.StatusFound) + }) + } + + s.WebSocketRouter = &WebSocketRouter{ + server: s, + handlers: make(map[string]webSocketHandler), + } + + if err := mailservice.TestConnection(s.Config()); err != nil { + mlog.Error("Mail server connection test is failed: " + err.Message) + } + + if _, err := url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil { + mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: http://about.mattermost.com/default-site-url") + } + + backend, appErr := s.FileBackend() + if appErr == nil { + appErr = backend.TestConnection() + } + if appErr != nil { + mlog.Error("Problem with file storage settings", mlog.Err(appErr)) + } + model.AppErrorInit(utils.T) s.timezones = timezones.New() diff --git a/app/server_app_adapters.go b/app/server_app_adapters.go deleted file mode 100644 index 049716e840..0000000000 --- a/app/server_app_adapters.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package app - -import ( - "net/http" - "net/url" - "path" - - "github.com/mattermost/mattermost-server/v5/mlog" - "github.com/mattermost/mattermost-server/v5/model" - "github.com/mattermost/mattermost-server/v5/services/mailservice" - "github.com/mattermost/mattermost-server/v5/store" - "github.com/mattermost/mattermost-server/v5/store/localcachelayer" - "github.com/mattermost/mattermost-server/v5/store/searchlayer" - "github.com/mattermost/mattermost-server/v5/store/sqlstore" - "github.com/mattermost/mattermost-server/v5/utils" - "github.com/pkg/errors" -) - -// This is a bridge between the old and new initialization for the context refactor. -// It calls app layer initialization code that then turns around and acts on the server. -// Don't add anything new here, new initialization should be done in the server and -// performed in the NewServer function. -func (s *Server) RunOldAppInitialization() error { - s.createPushNotificationsHub() - - if err := utils.InitTranslations(s.Config().LocalizationSettings); err != nil { - return errors.Wrapf(err, "unable to load Mattermost translation files") - } - - s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) { - s.configOrLicenseListener() - - message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil) - - message.Add("config", s.ClientConfigWithComputed()) - s.Go(func() { - s.Publish(message) - }) - }) - s.licenseListenerId = s.AddLicenseListener(func(oldLicense, newLicense *model.License) { - s.configOrLicenseListener() - - message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil) - message.Add("license", s.GetSanitizedClientLicense()) - s.Go(func() { - s.Publish(message) - }) - - }) - - if err := s.setupInviteEmailRateLimiting(); err != nil { - return err - } - - mlog.Info("Server is initializing...") - - s.initEnterprise() - - if s.newStore == nil { - s.newStore = func() store.Store { - s.sqlStore = sqlstore.NewSqlSupplier(s.Config().SqlSettings, s.Metrics) - searchStore := searchlayer.NewSearchLayer( - localcachelayer.NewLocalCacheLayer( - s.sqlStore, - s.Metrics, - s.Cluster, - s.CacheProvider, - ), - s.SearchEngine, - s.Config(), - ) - - s.AddConfigListener(func(prevCfg, cfg *model.Config) { - searchStore.UpdateConfig(cfg) - }) - - s.sqlStore.UpdateLicense(s.License()) - s.AddLicenseListener(func(oldLicense, newLicense *model.License) { - s.sqlStore.UpdateLicense(newLicense) - }) - - return store.NewTimerLayer( - searchStore, - s.Metrics, - ) - } - } - - if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil { - mlog.Error("Failed to parse server templates", mlog.Err(err)) - } else { - s.htmlTemplateWatcher = htmlTemplateWatcher - } - - s.Store = s.newStore() - - if model.BuildEnterpriseReady == "true" { - s.LoadLicense() - } - - s.initJobs() - - if s.joinCluster && s.Cluster != nil { - s.Cluster.StartInterNodeCommunication() - } - - if err := s.ensureAsymmetricSigningKey(); err != nil { - return errors.Wrapf(err, "unable to ensure asymmetric signing key") - } - - if err := s.ensurePostActionCookieSecret(); err != nil { - return errors.Wrapf(err, "unable to ensure PostAction cookie secret") - } - - if err := s.ensureInstallationDate(); err != nil { - return errors.Wrapf(err, "unable to ensure installation date") - } - - if err := s.ensureFirstServerRunTimestamp(); err != nil { - return errors.Wrapf(err, "unable to ensure first run timestamp") - } - - s.ensureDiagnosticId() - s.regenerateClientConfig() - - s.clusterLeaderListenerId = s.AddClusterLeaderChangedListener(func() { - mlog.Info("Cluster leader changed. Determining if job schedulers should be running:", mlog.Bool("isLeader", s.IsLeader())) - if s.Jobs != nil && s.Jobs.Schedulers != nil { - s.Jobs.Schedulers.HandleClusterLeaderChange(s.IsLeader()) - } - }) - - subpath, err := utils.GetSubpathFromConfig(s.Config()) - if err != nil { - return errors.Wrap(err, "failed to parse SiteURL subpath") - } - s.Router = s.RootRouter.PathPrefix(subpath).Subrouter() - - // FakeApp: remove this when we have the ServePluginRequest and ServePluginPublicRequest migrated in the server - fakeApp := New(ServerConnector(s)) - pluginsRoute := s.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter() - pluginsRoute.HandleFunc("", fakeApp.ServePluginRequest) - pluginsRoute.HandleFunc("/public/{public_file:.*}", fakeApp.ServePluginPublicRequest) - pluginsRoute.HandleFunc("/{anything:.*}", fakeApp.ServePluginRequest) - - // If configured with a subpath, redirect 404s at the root back into the subpath. - if subpath != "/" { - s.RootRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r.URL.Path = path.Join(subpath, r.URL.Path) - http.Redirect(w, r, r.URL.String(), http.StatusFound) - }) - } - - s.WebSocketRouter = &WebSocketRouter{ - server: s, - handlers: make(map[string]webSocketHandler), - } - - if err := mailservice.TestConnection(s.Config()); err != nil { - mlog.Error("Mail server connection test is failed: " + err.Message) - } - - if _, err := url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil { - mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: http://about.mattermost.com/default-site-url") - } - - backend, appErr := s.FileBackend() - if appErr == nil { - appErr = backend.TestConnection() - } - if appErr != nil { - mlog.Error("Problem with file storage settings", mlog.Err(appErr)) - } - - return nil -} diff --git a/app/web_hub.go b/app/web_hub.go index 38f5858b41..8f6f100e8a 100644 --- a/app/web_hub.go +++ b/app/web_hub.go @@ -355,12 +355,13 @@ func (h *Hub) IsRegistered(userId, sessionToken string) bool { // Broadcast broadcasts the message to all connections in the hub. func (h *Hub) Broadcast(message *model.WebSocketEvent) { - // XXX: The hub nil check is because of the way we setup our tests. We call `app.NewServer()` - // which returns a server, but only after that, we call `wsapi.Init()` through our FakeApp adapter - // to initialize the hub. But in the `NewServer` call itself, we call `RunOldAppInitialization` - // which directly proceeds to broadcast some messages happily. - // This needs to be fixed once the FakeApp adapter goes away. And possibly, we can look into - // doing hub initialization inside NewServer itself. + // XXX: The hub nil check is because of the way we setup our tests. We call + // `app.NewServer()` which returns a server, but only after that, we call + // `wsapi.Init()` to initialize the hub. But in the `NewServer` call + // itself proceeds to broadcast some messages happily. This needs to be + // fixed once the the wsapi cyclic dependency with server/app goes away. + // And possibly, we can look into doing the hub initialization inside + // NewServer itself. if h != nil && message != nil { if metrics := h.app.Metrics(); metrics != nil { metrics.IncrementWebSocketBroadcastBufferSize(strconv.Itoa(h.connectionIndex), 1)