github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/system.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    13  	"path"
    14  	"runtime"
    15  	"strconv"
    16  	"time"
    17  
    18  	"github.com/pkg/errors"
    19  
    20  	"github.com/masterhung0112/hk_server/v5/audit"
    21  	"github.com/masterhung0112/hk_server/v5/model"
    22  	"github.com/masterhung0112/hk_server/v5/services/cache"
    23  	"github.com/masterhung0112/hk_server/v5/services/upgrader"
    24  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    25  )
    26  
    27  const (
    28  	RedirectLocationCacheSize = 10000
    29  	DefaultServerBusySeconds  = 3600
    30  	MaxServerBusySeconds      = 86400
    31  )
    32  
    33  var redirectLocationDataCache = cache.NewLRU(cache.LRUOptions{
    34  	Size: RedirectLocationCacheSize,
    35  })
    36  
    37  func (api *API) InitSystem() {
    38  	api.BaseRoutes.System.Handle("/ping", api.ApiHandler(getSystemPing)).Methods("GET")
    39  
    40  	api.BaseRoutes.System.Handle("/timezones", api.ApiSessionRequired(getSupportedTimezones)).Methods("GET")
    41  
    42  	api.BaseRoutes.ApiRoot.Handle("/audits", api.ApiSessionRequired(getAudits)).Methods("GET")
    43  	api.BaseRoutes.ApiRoot.Handle("/email/test", api.ApiSessionRequired(testEmail)).Methods("POST")
    44  	api.BaseRoutes.ApiRoot.Handle("/site_url/test", api.ApiSessionRequired(testSiteURL)).Methods("POST")
    45  	api.BaseRoutes.ApiRoot.Handle("/file/s3_test", api.ApiSessionRequired(testS3)).Methods("POST")
    46  	api.BaseRoutes.ApiRoot.Handle("/database/recycle", api.ApiSessionRequired(databaseRecycle)).Methods("POST")
    47  	api.BaseRoutes.ApiRoot.Handle("/caches/invalidate", api.ApiSessionRequired(invalidateCaches)).Methods("POST")
    48  
    49  	api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiSessionRequired(getLogs)).Methods("GET")
    50  	api.BaseRoutes.ApiRoot.Handle("/logs", api.ApiHandler(postLog)).Methods("POST")
    51  
    52  	api.BaseRoutes.ApiRoot.Handle("/analytics/old", api.ApiSessionRequired(getAnalytics)).Methods("GET")
    53  
    54  	api.BaseRoutes.ApiRoot.Handle("/redirect_location", api.ApiSessionRequiredTrustRequester(getRedirectLocation)).Methods("GET")
    55  
    56  	api.BaseRoutes.ApiRoot.Handle("/notifications/ack", api.ApiSessionRequired(pushNotificationAck)).Methods("POST")
    57  
    58  	api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(setServerBusy)).Methods("POST")
    59  	api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(getServerBusyExpires)).Methods("GET")
    60  	api.BaseRoutes.ApiRoot.Handle("/server_busy", api.ApiSessionRequired(clearServerBusy)).Methods("DELETE")
    61  	api.BaseRoutes.ApiRoot.Handle("/upgrade_to_enterprise", api.ApiSessionRequired(upgradeToEnterprise)).Methods("POST")
    62  	api.BaseRoutes.ApiRoot.Handle("/upgrade_to_enterprise/status", api.ApiSessionRequired(upgradeToEnterpriseStatus)).Methods("GET")
    63  	api.BaseRoutes.ApiRoot.Handle("/restart", api.ApiSessionRequired(restart)).Methods("POST")
    64  	api.BaseRoutes.ApiRoot.Handle("/warn_metrics/status", api.ApiSessionRequired(getWarnMetricsStatus)).Methods("GET")
    65  	api.BaseRoutes.ApiRoot.Handle("/warn_metrics/ack/{warn_metric_id:[A-Za-z0-9-_]+}", api.ApiHandler(sendWarnMetricAckEmail)).Methods("POST")
    66  	api.BaseRoutes.ApiRoot.Handle("/warn_metrics/trial-license-ack/{warn_metric_id:[A-Za-z0-9-_]+}", api.ApiHandler(requestTrialLicenseAndAckWarnMetric)).Methods("POST")
    67  	api.BaseRoutes.System.Handle("/notices/{team_id:[A-Za-z0-9]+}", api.ApiSessionRequired(getProductNotices)).Methods("GET")
    68  	api.BaseRoutes.System.Handle("/notices/view", api.ApiSessionRequired(updateViewedProductNotices)).Methods("PUT")
    69  
    70  	api.BaseRoutes.System.Handle("/support_packet", api.ApiSessionRequired(generateSupportPacket)).Methods("GET")
    71  }
    72  
    73  func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) {
    74  	const FileMime = "application/zip"
    75  	const OutputDirectory = "support_packet"
    76  
    77  	// Checking to see if the user is a admin of any sort or not
    78  	// If they are a admin, they should theoritcally have access to one or more of the system console read permissions
    79  	if !c.App.SessionHasPermissionToAny(*c.AppContext.Session(), model.SysconsoleReadPermissions) {
    80  		c.SetPermissionError(model.SysconsoleReadPermissions...)
    81  		return
    82  	}
    83  
    84  	// Checking to see if the server has a e10 or e20 license (this feature is only permitted for servers with licenses)
    85  	if c.App.Srv().License() == nil {
    86  		c.Err = model.NewAppError("Api4.generateSupportPacket", "api.no_license", nil, "", http.StatusForbidden)
    87  		return
    88  	}
    89  
    90  	fileDatas := c.App.GenerateSupportPacket()
    91  
    92  	// Constructing the ZIP file name as per spec (mattermost_support_packet_YYYY-MM-DD-HH-MM.zip)
    93  	now := time.Now()
    94  	outputZipFilename := fmt.Sprintf("mattermost_support_packet_%s.zip", now.Format("2006-01-02-03-04"))
    95  
    96  	fileStorageBackend, fileBackendErr := c.App.FileBackend()
    97  	if fileBackendErr != nil {
    98  		c.Err = fileBackendErr
    99  		return
   100  	}
   101  
   102  	// We do this incase we get concurrent requests, we will always have a unique directory.
   103  	// This is to avoid the situation where we try to write to the same directory while we are trying to delete it (further down)
   104  	outputDirectoryToUse := OutputDirectory + "_" + model.NewId()
   105  	err := c.App.CreateZipFileAndAddFiles(fileStorageBackend, fileDatas, outputZipFilename, outputDirectoryToUse)
   106  	if err != nil {
   107  		c.Err = model.NewAppError("Api4.generateSupportPacket", "api.unable_to_create_zip_file", nil, err.Error(), http.StatusForbidden)
   108  		return
   109  	}
   110  
   111  	fileBytes, err := fileStorageBackend.ReadFile(path.Join(outputDirectoryToUse, outputZipFilename))
   112  	defer fileStorageBackend.RemoveDirectory(outputDirectoryToUse)
   113  	if err != nil {
   114  		c.Err = model.NewAppError("Api4.generateSupportPacket", "api.unable_to_read_file_from_backend", nil, err.Error(), http.StatusForbidden)
   115  		return
   116  	}
   117  	fileBytesReader := bytes.NewReader(fileBytes)
   118  
   119  	// Send the zip file back to client
   120  	// We are able to pass 0 for content size due to the fact that Golang's serveContent (https://golang.org/src/net/http/fs.go)
   121  	// already sets that for us
   122  	writeFileResponse(outputZipFilename, FileMime, 0, now, *c.App.Config().ServiceSettings.WebserverMode, fileBytesReader, true, w, r)
   123  }
   124  
   125  func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
   126  	reqs := c.App.Config().ClientRequirements
   127  
   128  	s := make(map[string]string)
   129  	s[model.STATUS] = model.STATUS_OK
   130  	s["AndroidLatestVersion"] = reqs.AndroidLatestVersion
   131  	s["AndroidMinVersion"] = reqs.AndroidMinVersion
   132  	s["DesktopLatestVersion"] = reqs.DesktopLatestVersion
   133  	s["DesktopMinVersion"] = reqs.DesktopMinVersion
   134  	s["IosLatestVersion"] = reqs.IosLatestVersion
   135  	s["IosMinVersion"] = reqs.IosMinVersion
   136  
   137  	testflag := c.App.Config().FeatureFlags.TestFeature
   138  	if testflag != "off" {
   139  		s["TestFeatureFlag"] = testflag
   140  	}
   141  
   142  	actualGoroutines := runtime.NumGoroutine()
   143  	if *c.App.Config().ServiceSettings.GoroutineHealthThreshold > 0 && actualGoroutines >= *c.App.Config().ServiceSettings.GoroutineHealthThreshold {
   144  		mlog.Warn("The number of running goroutines is over the health threshold", mlog.Int("goroutines", actualGoroutines), mlog.Int("health_threshold", *c.App.Config().ServiceSettings.GoroutineHealthThreshold))
   145  		s[model.STATUS] = model.STATUS_UNHEALTHY
   146  	}
   147  
   148  	// Enhanced ping health check:
   149  	// If an extra form value is provided then perform extra health checks for
   150  	// database and file storage backends.
   151  	if r.FormValue("get_server_status") != "" {
   152  		dbStatusKey := "database_status"
   153  		s[dbStatusKey] = model.STATUS_OK
   154  
   155  		writeErr := c.App.DBHealthCheckWrite()
   156  		if writeErr != nil {
   157  			mlog.Warn("Unable to write to database.", mlog.Err(writeErr))
   158  			s[dbStatusKey] = model.STATUS_UNHEALTHY
   159  			s[model.STATUS] = model.STATUS_UNHEALTHY
   160  		}
   161  
   162  		writeErr = c.App.DBHealthCheckDelete()
   163  		if writeErr != nil {
   164  			mlog.Warn("Unable to remove ping health check value from database.", mlog.Err(writeErr))
   165  			s[dbStatusKey] = model.STATUS_UNHEALTHY
   166  			s[model.STATUS] = model.STATUS_UNHEALTHY
   167  		}
   168  
   169  		if s[dbStatusKey] == model.STATUS_OK {
   170  			mlog.Debug("Able to write to database.")
   171  		}
   172  
   173  		filestoreStatusKey := "filestore_status"
   174  		s[filestoreStatusKey] = model.STATUS_OK
   175  		appErr := c.App.TestFileStoreConnection()
   176  		if appErr != nil {
   177  			s[filestoreStatusKey] = model.STATUS_UNHEALTHY
   178  			s[model.STATUS] = model.STATUS_UNHEALTHY
   179  		}
   180  
   181  		w.Header().Set(model.STATUS, s[model.STATUS])
   182  		w.Header().Set(dbStatusKey, s[dbStatusKey])
   183  		w.Header().Set(filestoreStatusKey, s[filestoreStatusKey])
   184  	}
   185  
   186  	if s[model.STATUS] != model.STATUS_OK {
   187  		w.WriteHeader(http.StatusInternalServerError)
   188  	}
   189  	w.Write([]byte(model.MapToJson(s)))
   190  }
   191  
   192  func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
   193  	cfg := model.ConfigFromJson(r.Body)
   194  	if cfg == nil {
   195  		cfg = c.App.Config()
   196  	}
   197  
   198  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_TEST_EMAIL) {
   199  		c.SetPermissionError(model.PERMISSION_TEST_EMAIL)
   200  		return
   201  	}
   202  
   203  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   204  		c.Err = model.NewAppError("testEmail", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   205  		return
   206  	}
   207  
   208  	err := c.App.TestEmail(c.AppContext.Session().UserId, cfg)
   209  	if err != nil {
   210  		c.Err = err
   211  		return
   212  	}
   213  
   214  	ReturnStatusOK(w)
   215  }
   216  
   217  func testSiteURL(c *Context, w http.ResponseWriter, r *http.Request) {
   218  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_TEST_SITE_URL) {
   219  		c.SetPermissionError(model.PERMISSION_TEST_SITE_URL)
   220  		return
   221  	}
   222  
   223  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   224  		c.Err = model.NewAppError("testSiteURL", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   225  		return
   226  	}
   227  
   228  	props := model.MapFromJson(r.Body)
   229  	siteURL := props["site_url"]
   230  	if siteURL == "" {
   231  		c.SetInvalidParam("site_url")
   232  		return
   233  	}
   234  
   235  	err := c.App.TestSiteURL(siteURL)
   236  	if err != nil {
   237  		c.Err = err
   238  		return
   239  	}
   240  
   241  	ReturnStatusOK(w)
   242  }
   243  
   244  func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
   245  	auditRec := c.MakeAuditRecord("getAudits", audit.Fail)
   246  	defer c.LogAuditRec(auditRec)
   247  
   248  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_READ_AUDITS) {
   249  		c.SetPermissionError(model.PERMISSION_READ_AUDITS)
   250  		return
   251  	}
   252  
   253  	audits, err := c.App.GetAuditsPage("", c.Params.Page, c.Params.PerPage)
   254  	if err != nil {
   255  		c.Err = err
   256  		return
   257  	}
   258  
   259  	auditRec.Success()
   260  	auditRec.AddMeta("page", c.Params.Page)
   261  	auditRec.AddMeta("audits_per_page", c.Params.LogsPerPage)
   262  
   263  	w.Write([]byte(audits.ToJson()))
   264  }
   265  
   266  func databaseRecycle(c *Context, w http.ResponseWriter, r *http.Request) {
   267  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_RECYCLE_DATABASE_CONNECTIONS) {
   268  		c.SetPermissionError(model.PERMISSION_RECYCLE_DATABASE_CONNECTIONS)
   269  		return
   270  	}
   271  
   272  	auditRec := c.MakeAuditRecord("databaseRecycle", audit.Fail)
   273  	defer c.LogAuditRec(auditRec)
   274  
   275  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   276  		c.Err = model.NewAppError("databaseRecycle", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   277  		return
   278  	}
   279  
   280  	c.App.RecycleDatabaseConnection()
   281  
   282  	auditRec.Success()
   283  	ReturnStatusOK(w)
   284  }
   285  
   286  func invalidateCaches(c *Context, w http.ResponseWriter, r *http.Request) {
   287  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_INVALIDATE_CACHES) {
   288  		c.SetPermissionError(model.PERMISSION_INVALIDATE_CACHES)
   289  		return
   290  	}
   291  
   292  	auditRec := c.MakeAuditRecord("invalidateCaches", audit.Fail)
   293  	defer c.LogAuditRec(auditRec)
   294  
   295  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   296  		c.Err = model.NewAppError("invalidateCaches", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   297  		return
   298  	}
   299  
   300  	err := c.App.Srv().InvalidateAllCaches()
   301  	if err != nil {
   302  		c.Err = err
   303  		return
   304  	}
   305  
   306  	auditRec.Success()
   307  
   308  	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
   309  	ReturnStatusOK(w)
   310  }
   311  
   312  func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
   313  	auditRec := c.MakeAuditRecord("getLogs", audit.Fail)
   314  	defer c.LogAuditRec(auditRec)
   315  
   316  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   317  		c.Err = model.NewAppError("getLogs", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   318  		return
   319  	}
   320  
   321  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_GET_LOGS) {
   322  		c.SetPermissionError(model.PERMISSION_GET_LOGS)
   323  		return
   324  	}
   325  
   326  	lines, err := c.App.GetLogs(c.Params.Page, c.Params.LogsPerPage)
   327  	if err != nil {
   328  		c.Err = err
   329  		return
   330  	}
   331  
   332  	auditRec.AddMeta("page", c.Params.Page)
   333  	auditRec.AddMeta("logs_per_page", c.Params.LogsPerPage)
   334  
   335  	w.Write([]byte(model.ArrayToJson(lines)))
   336  }
   337  
   338  func postLog(c *Context, w http.ResponseWriter, r *http.Request) {
   339  	forceToDebug := false
   340  
   341  	if !*c.App.Config().ServiceSettings.EnableDeveloper {
   342  		if c.AppContext.Session().UserId == "" {
   343  			c.Err = model.NewAppError("postLog", "api.context.permissions.app_error", nil, "", http.StatusForbidden)
   344  			return
   345  		}
   346  
   347  		if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   348  			forceToDebug = true
   349  		}
   350  	}
   351  
   352  	m := model.MapFromJson(r.Body)
   353  	lvl := m["level"]
   354  	msg := m["message"]
   355  
   356  	if len(msg) > 400 {
   357  		msg = msg[0:399]
   358  	}
   359  
   360  	msg = "Client Logs API Endpoint Message: " + msg
   361  	fields := []mlog.Field{
   362  		mlog.String("type", "client_message"),
   363  		mlog.String("user_agent", c.AppContext.UserAgent()),
   364  	}
   365  
   366  	if !forceToDebug && lvl == "ERROR" {
   367  		mlog.Error(msg, fields...)
   368  	} else {
   369  		mlog.Debug(msg, fields...)
   370  	}
   371  
   372  	m["message"] = msg
   373  	w.Write([]byte(model.MapToJson(m)))
   374  }
   375  
   376  func getAnalytics(c *Context, w http.ResponseWriter, r *http.Request) {
   377  	name := r.URL.Query().Get("name")
   378  	teamId := r.URL.Query().Get("team_id")
   379  
   380  	if name == "" {
   381  		name = "standard"
   382  	}
   383  
   384  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_GET_ANALYTICS) {
   385  		c.SetPermissionError(model.PERMISSION_GET_ANALYTICS)
   386  		return
   387  	}
   388  
   389  	rows, err := c.App.GetAnalytics(name, teamId)
   390  	if err != nil {
   391  		c.Err = err
   392  		return
   393  	}
   394  
   395  	if rows == nil {
   396  		c.SetInvalidParam("name")
   397  		return
   398  	}
   399  
   400  	w.Write([]byte(rows.ToJson()))
   401  }
   402  
   403  func getSupportedTimezones(c *Context, w http.ResponseWriter, r *http.Request) {
   404  	supportedTimezones := c.App.Timezones().GetSupported()
   405  	if supportedTimezones == nil {
   406  		supportedTimezones = make([]string, 0)
   407  	}
   408  
   409  	b, err := json.Marshal(supportedTimezones)
   410  	if err != nil {
   411  		c.Logger.Warn("Unable to marshal JSON in timezones.", mlog.Err(err))
   412  		w.WriteHeader(http.StatusInternalServerError)
   413  	}
   414  
   415  	w.Write(b)
   416  }
   417  
   418  func testS3(c *Context, w http.ResponseWriter, r *http.Request) {
   419  	cfg := model.ConfigFromJson(r.Body)
   420  	if cfg == nil {
   421  		cfg = c.App.Config()
   422  	}
   423  
   424  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_TEST_S3) {
   425  		c.SetPermissionError(model.PERMISSION_TEST_S3)
   426  		return
   427  	}
   428  
   429  	if *c.App.Config().ExperimentalSettings.RestrictSystemAdmin {
   430  		c.Err = model.NewAppError("testS3", "api.restricted_system_admin", nil, "", http.StatusForbidden)
   431  		return
   432  	}
   433  
   434  	err := c.App.CheckMandatoryS3Fields(&cfg.FileSettings)
   435  	if err != nil {
   436  		c.Err = err
   437  		return
   438  	}
   439  
   440  	if *cfg.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING {
   441  		cfg.FileSettings.AmazonS3SecretAccessKey = c.App.Config().FileSettings.AmazonS3SecretAccessKey
   442  	}
   443  
   444  	appErr := c.App.TestFileStoreConnectionWithConfig(&cfg.FileSettings)
   445  	if appErr != nil {
   446  		c.Err = appErr
   447  		return
   448  	}
   449  
   450  	ReturnStatusOK(w)
   451  }
   452  
   453  func getRedirectLocation(c *Context, w http.ResponseWriter, r *http.Request) {
   454  	m := make(map[string]string)
   455  	m["location"] = ""
   456  
   457  	if !*c.App.Config().ServiceSettings.EnableLinkPreviews {
   458  		w.Write([]byte(model.MapToJson(m)))
   459  		return
   460  	}
   461  
   462  	url := r.URL.Query().Get("url")
   463  	if url == "" {
   464  		c.SetInvalidParam("url")
   465  		return
   466  	}
   467  
   468  	var location string
   469  	if err := redirectLocationDataCache.Get(url, &location); err == nil {
   470  		m["location"] = location
   471  		w.Write([]byte(model.MapToJson(m)))
   472  		return
   473  	}
   474  
   475  	client := c.App.HTTPService().MakeClient(false)
   476  	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
   477  		return http.ErrUseLastResponse
   478  	}
   479  
   480  	res, err := client.Head(url)
   481  	if err != nil {
   482  		// Cache failures to prevent retries.
   483  		redirectLocationDataCache.SetWithExpiry(url, "", 1*time.Hour)
   484  		// Always return a success status and a JSON string to limit information returned to client.
   485  		w.Write([]byte(model.MapToJson(m)))
   486  		return
   487  	}
   488  	defer func() {
   489  		io.Copy(ioutil.Discard, res.Body)
   490  		res.Body.Close()
   491  	}()
   492  
   493  	location = res.Header.Get("Location")
   494  	redirectLocationDataCache.SetWithExpiry(url, location, 1*time.Hour)
   495  	m["location"] = location
   496  
   497  	w.Write([]byte(model.MapToJson(m)))
   498  }
   499  
   500  func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) {
   501  	ack, err := model.PushNotificationAckFromJson(r.Body)
   502  	if err != nil {
   503  		c.Err = model.NewAppError("pushNotificationAck",
   504  			"api.push_notifications_ack.message.parse.app_error",
   505  			nil,
   506  			err.Error(),
   507  			http.StatusBadRequest,
   508  		)
   509  		return
   510  	}
   511  
   512  	if !*c.App.Config().EmailSettings.SendPushNotifications {
   513  		c.Err = model.NewAppError("pushNotificationAck", "api.push_notification.disabled.app_error", nil, "", http.StatusNotImplemented)
   514  		return
   515  	}
   516  
   517  	err = c.App.SendAckToPushProxy(ack)
   518  	if ack.IsIdLoaded {
   519  		if err != nil {
   520  			// Log the error only, then continue to fetch notification message
   521  			c.App.NotificationsLog().Error("Notification ack not sent to push proxy",
   522  				mlog.String("ackId", ack.Id),
   523  				mlog.String("type", ack.NotificationType),
   524  				mlog.String("postId", ack.PostId),
   525  				mlog.String("status", err.Error()),
   526  			)
   527  		}
   528  
   529  		notificationInterface := c.App.Notification()
   530  
   531  		if notificationInterface == nil {
   532  			c.Err = model.NewAppError("pushNotificationAck", "api.system.id_loaded.not_available.app_error", nil, "", http.StatusFound)
   533  			return
   534  		}
   535  
   536  		msg, appError := notificationInterface.GetNotificationMessage(ack, c.AppContext.Session().UserId)
   537  		if appError != nil {
   538  			c.Err = model.NewAppError("pushNotificationAck", "api.push_notification.id_loaded.fetch.app_error", nil, appError.Error(), http.StatusInternalServerError)
   539  			return
   540  		}
   541  
   542  		w.Write([]byte(msg.ToJson()))
   543  
   544  		return
   545  	} else if err != nil {
   546  		c.Err = model.NewAppError("pushNotificationAck", "api.push_notifications_ack.forward.app_error", nil, err.Error(), http.StatusInternalServerError)
   547  		return
   548  	}
   549  
   550  	ReturnStatusOK(w)
   551  }
   552  
   553  func setServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
   554  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   555  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   556  		return
   557  	}
   558  
   559  	// number of seconds to keep server marked busy
   560  	secs := r.URL.Query().Get("seconds")
   561  	if secs == "" {
   562  		secs = strconv.FormatInt(DefaultServerBusySeconds, 10)
   563  	}
   564  
   565  	i, err := strconv.ParseInt(secs, 10, 64)
   566  	if err != nil || i <= 0 || i > MaxServerBusySeconds {
   567  		c.SetInvalidUrlParam(fmt.Sprintf("seconds must be 1 - %d", MaxServerBusySeconds))
   568  		return
   569  	}
   570  
   571  	auditRec := c.MakeAuditRecord("setServerBusy", audit.Fail)
   572  	defer c.LogAuditRec(auditRec)
   573  	auditRec.AddMeta("seconds", i)
   574  
   575  	c.App.Srv().Busy.Set(time.Second * time.Duration(i))
   576  	mlog.Warn("server busy state activated - non-critical services disabled", mlog.Int64("seconds", i))
   577  
   578  	auditRec.Success()
   579  	ReturnStatusOK(w)
   580  }
   581  
   582  func clearServerBusy(c *Context, w http.ResponseWriter, r *http.Request) {
   583  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   584  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   585  		return
   586  	}
   587  
   588  	auditRec := c.MakeAuditRecord("clearServerBusy", audit.Fail)
   589  	defer c.LogAuditRec(auditRec)
   590  
   591  	c.App.Srv().Busy.Clear()
   592  	mlog.Info("server busy state cleared - non-critical services enabled")
   593  
   594  	auditRec.Success()
   595  	ReturnStatusOK(w)
   596  }
   597  
   598  func getServerBusyExpires(c *Context, w http.ResponseWriter, r *http.Request) {
   599  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   600  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   601  		return
   602  	}
   603  	w.Write([]byte(c.App.Srv().Busy.ToJson()))
   604  }
   605  
   606  func upgradeToEnterprise(c *Context, w http.ResponseWriter, r *http.Request) {
   607  	auditRec := c.MakeAuditRecord("upgradeToEnterprise", audit.Fail)
   608  	defer c.LogAuditRec(auditRec)
   609  
   610  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   611  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   612  		return
   613  	}
   614  
   615  	if model.BuildEnterpriseReady == "true" {
   616  		c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.already-enterprise.app_error", nil, "", http.StatusTooManyRequests)
   617  		return
   618  	}
   619  
   620  	percentage, _ := c.App.Srv().UpgradeToE0Status()
   621  
   622  	if percentage > 0 {
   623  		c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.app_error", nil, "", http.StatusTooManyRequests)
   624  		return
   625  	}
   626  	if percentage == 100 {
   627  		c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.already-done.app_error", nil, "", http.StatusTooManyRequests)
   628  		return
   629  	}
   630  
   631  	if err := c.App.Srv().CanIUpgradeToE0(); err != nil {
   632  		var ipErr *upgrader.InvalidPermissions
   633  		var iaErr *upgrader.InvalidArch
   634  		switch {
   635  		case errors.As(err, &ipErr):
   636  			params := map[string]interface{}{
   637  				"MattermostUsername": ipErr.MattermostUsername,
   638  				"FileUsername":       ipErr.FileUsername,
   639  				"Path":               ipErr.Path,
   640  			}
   641  			if ipErr.ErrType == "invalid-user-and-permission" {
   642  				c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-user-and-permission.app_error", params, err.Error(), http.StatusForbidden)
   643  			} else if ipErr.ErrType == "invalid-user" {
   644  				c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-user.app_error", params, err.Error(), http.StatusForbidden)
   645  			} else if ipErr.ErrType == "invalid-permission" {
   646  				c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.invalid-permission.app_error", params, err.Error(), http.StatusForbidden)
   647  			}
   648  		case errors.As(err, &iaErr):
   649  			c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.system_not_supported.app_error", nil, err.Error(), http.StatusForbidden)
   650  		default:
   651  			c.Err = model.NewAppError("upgradeToEnterprise", "api.upgrade_to_enterprise.generic_error.app_error", nil, err.Error(), http.StatusForbidden)
   652  		}
   653  		return
   654  	}
   655  
   656  	c.App.Srv().Go(func() {
   657  		c.App.Srv().UpgradeToE0()
   658  	})
   659  
   660  	auditRec.Success()
   661  	w.WriteHeader(http.StatusAccepted)
   662  	ReturnStatusOK(w)
   663  }
   664  
   665  func upgradeToEnterpriseStatus(c *Context, w http.ResponseWriter, r *http.Request) {
   666  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   667  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   668  		return
   669  	}
   670  
   671  	percentage, err := c.App.Srv().UpgradeToE0Status()
   672  	var s map[string]interface{}
   673  	if err != nil {
   674  		var isErr *upgrader.InvalidSignature
   675  		switch {
   676  		case errors.As(err, &isErr):
   677  			appErr := model.NewAppError("upgradeToEnterpriseStatus", "api.upgrade_to_enterprise_status.app_error", nil, err.Error(), http.StatusBadRequest)
   678  			s = map[string]interface{}{"percentage": 0, "error": appErr.Message}
   679  		default:
   680  			appErr := model.NewAppError("upgradeToEnterpriseStatus", "api.upgrade_to_enterprise_status.signature.app_error", nil, err.Error(), http.StatusBadRequest)
   681  			s = map[string]interface{}{"percentage": 0, "error": appErr.Message}
   682  		}
   683  	} else {
   684  		s = map[string]interface{}{"percentage": percentage, "error": nil}
   685  	}
   686  
   687  	w.Write([]byte(model.StringInterfaceToJson(s)))
   688  }
   689  
   690  func restart(c *Context, w http.ResponseWriter, r *http.Request) {
   691  	auditRec := c.MakeAuditRecord("restartServer", audit.Fail)
   692  	defer c.LogAuditRec(auditRec)
   693  
   694  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   695  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   696  		return
   697  	}
   698  
   699  	auditRec.Success()
   700  	ReturnStatusOK(w)
   701  	time.Sleep(1 * time.Second)
   702  
   703  	go func() {
   704  		c.App.Srv().Restart()
   705  	}()
   706  }
   707  
   708  func getWarnMetricsStatus(c *Context, w http.ResponseWriter, r *http.Request) {
   709  	if !c.App.SessionHasPermissionToAny(*c.AppContext.Session(), model.SysconsoleReadPermissions) {
   710  		c.SetPermissionError(model.SysconsoleReadPermissions...)
   711  		return
   712  	}
   713  
   714  	license := c.App.Srv().License()
   715  	if license != nil {
   716  		mlog.Debug("License is present, skip.")
   717  		return
   718  	}
   719  
   720  	status, err := c.App.GetWarnMetricsStatus()
   721  	if err != nil {
   722  		c.Err = err
   723  		return
   724  	}
   725  
   726  	w.Write([]byte(model.MapWarnMetricStatusToJson(status)))
   727  }
   728  
   729  func sendWarnMetricAckEmail(c *Context, w http.ResponseWriter, r *http.Request) {
   730  	auditRec := c.MakeAuditRecord("sendWarnMetricAckEmail", audit.Fail)
   731  	defer c.LogAuditRec(auditRec)
   732  	c.LogAudit("attempt")
   733  
   734  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   735  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   736  		return
   737  	}
   738  
   739  	license := c.App.Srv().License()
   740  	if license != nil {
   741  		mlog.Debug("License is present, skip.")
   742  		return
   743  	}
   744  
   745  	user, appErr := c.App.GetUser(c.AppContext.Session().UserId)
   746  	if appErr != nil {
   747  		c.Err = appErr
   748  		return
   749  	}
   750  
   751  	ack := model.SendWarnMetricAckFromJson(r.Body)
   752  	if ack == nil {
   753  		c.SetInvalidParam("ack")
   754  		return
   755  	}
   756  
   757  	appErr = c.App.NotifyAndSetWarnMetricAck(c.Params.WarnMetricId, user, ack.ForceAck, false)
   758  	if appErr != nil {
   759  		c.Err = appErr
   760  	}
   761  
   762  	auditRec.Success()
   763  	ReturnStatusOK(w)
   764  }
   765  
   766  func requestTrialLicenseAndAckWarnMetric(c *Context, w http.ResponseWriter, r *http.Request) {
   767  	auditRec := c.MakeAuditRecord("requestTrialLicenseAndAckWarnMetric", audit.Fail)
   768  	defer c.LogAuditRec(auditRec)
   769  	c.LogAudit("attempt")
   770  
   771  	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) {
   772  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   773  		return
   774  	}
   775  
   776  	if model.BuildEnterpriseReady != "true" {
   777  		mlog.Debug("Not Enterprise Edition, skip.")
   778  		return
   779  	}
   780  
   781  	license := c.App.Srv().License()
   782  	if license != nil {
   783  		mlog.Debug("License is present, skip.")
   784  		return
   785  	}
   786  
   787  	if err := c.App.RequestLicenseAndAckWarnMetric(c.AppContext, c.Params.WarnMetricId, false); err != nil {
   788  		c.Err = err
   789  		return
   790  	}
   791  
   792  	auditRec.Success()
   793  	ReturnStatusOK(w)
   794  }
   795  
   796  func getProductNotices(c *Context, w http.ResponseWriter, r *http.Request) {
   797  	c.RequireTeamId()
   798  	if c.Err != nil {
   799  		return
   800  	}
   801  
   802  	client, parseError := model.NoticeClientTypeFromString(r.URL.Query().Get("client"))
   803  	if parseError != nil {
   804  		c.SetInvalidParam("client")
   805  		return
   806  	}
   807  	clientVersion := r.URL.Query().Get("clientVersion")
   808  	locale := r.URL.Query().Get("locale")
   809  
   810  	notices, err := c.App.GetProductNotices(c.AppContext, c.AppContext.Session().UserId, c.Params.TeamId, client, clientVersion, locale)
   811  
   812  	if err != nil {
   813  		c.Err = err
   814  		return
   815  	}
   816  	result, _ := notices.Marshal()
   817  	_, _ = w.Write(result)
   818  }
   819  
   820  func updateViewedProductNotices(c *Context, w http.ResponseWriter, r *http.Request) {
   821  	auditRec := c.MakeAuditRecord("updateViewedProductNotices", audit.Fail)
   822  	defer c.LogAuditRec(auditRec)
   823  	c.LogAudit("attempt")
   824  
   825  	ids := model.ArrayFromJson(r.Body)
   826  	err := c.App.UpdateViewedProductNotices(c.AppContext.Session().UserId, ids)
   827  	if err != nil {
   828  		c.Err = err
   829  		return
   830  	}
   831  
   832  	auditRec.Success()
   833  	ReturnStatusOK(w)
   834  }