"Fossies" - the Fresh Open Source Software Archive

Member "mattermost-server-6.0.1/app/admin.go" (18 Oct 2021, 7768 Bytes) of package /linux/www/mattermost-server-6.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "admin.go": 5.39.0_vs_6.0.0.

    1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
    2 // See LICENSE.txt for license information.
    3 
    4 package app
    5 
    6 import (
    7     "fmt"
    8     "io"
    9     "io/ioutil"
   10     "net/http"
   11     "os"
   12     "runtime/debug"
   13     "time"
   14 
   15     "github.com/mattermost/mattermost-server/v6/config"
   16     "github.com/mattermost/mattermost-server/v6/model"
   17     "github.com/mattermost/mattermost-server/v6/shared/i18n"
   18     "github.com/mattermost/mattermost-server/v6/shared/mail"
   19     "github.com/mattermost/mattermost-server/v6/shared/mlog"
   20 )
   21 
   22 func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
   23     var lines []string
   24 
   25     license := s.License()
   26     if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.Config().ClusterSettings.Enable {
   27         if info := s.Cluster.GetMyClusterInfo(); info != nil {
   28             lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
   29             lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
   30             lines = append(lines, info.Hostname)
   31             lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
   32             lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
   33         } else {
   34             mlog.Error("Could not get cluster info")
   35         }
   36     }
   37 
   38     melines, err := s.GetLogsSkipSend(page, perPage)
   39     if err != nil {
   40         return nil, err
   41     }
   42 
   43     lines = append(lines, melines...)
   44 
   45     if s.Cluster != nil && *s.Config().ClusterSettings.Enable {
   46         clines, err := s.Cluster.GetLogs(page, perPage)
   47         if err != nil {
   48             return nil, err
   49         }
   50 
   51         lines = append(lines, clines...)
   52     }
   53 
   54     return lines, nil
   55 }
   56 
   57 func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
   58     return a.Srv().GetLogs(page, perPage)
   59 }
   60 
   61 func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
   62     var lines []string
   63 
   64     if *s.Config().LogSettings.EnableFile {
   65         s.Log.Flush()
   66         logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
   67         file, err := os.Open(logFile)
   68         if err != nil {
   69             return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
   70         }
   71 
   72         defer file.Close()
   73 
   74         var newLine = []byte{'\n'}
   75         var lineCount int
   76         const searchPos = -1
   77         b := make([]byte, 1)
   78         var endOffset int64 = 0
   79 
   80         // if the file exists and it's last byte is '\n' - skip it
   81         var stat os.FileInfo
   82         if stat, err = os.Stat(logFile); err == nil {
   83             if _, err = file.ReadAt(b, stat.Size()-1); err == nil && b[0] == newLine[0] {
   84                 endOffset = -1
   85             }
   86         }
   87         lineEndPos, err := file.Seek(endOffset, io.SeekEnd)
   88         if err != nil {
   89             return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
   90         }
   91         for {
   92             pos, err := file.Seek(searchPos, io.SeekCurrent)
   93             if err != nil {
   94                 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
   95             }
   96 
   97             _, err = file.ReadAt(b, pos)
   98             if err != nil {
   99                 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
  100             }
  101 
  102             if b[0] == newLine[0] || pos == 0 {
  103                 lineCount++
  104                 if lineCount > page*perPage {
  105                     line := make([]byte, lineEndPos-pos)
  106                     _, err := file.ReadAt(line, pos)
  107                     if err != nil {
  108                         return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
  109                     }
  110                     lines = append(lines, string(line))
  111                 }
  112                 if pos == 0 {
  113                     break
  114                 }
  115                 lineEndPos = pos
  116             }
  117 
  118             if len(lines) == perPage {
  119                 break
  120             }
  121         }
  122 
  123         for i, j := 0, len(lines)-1; i < j; i, j = i+1, j-1 {
  124             lines[i], lines[j] = lines[j], lines[i]
  125         }
  126     } else {
  127         lines = append(lines, "")
  128     }
  129 
  130     return lines, nil
  131 }
  132 
  133 func (a *App) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
  134     return a.Srv().GetLogsSkipSend(page, perPage)
  135 }
  136 
  137 func (a *App) GetClusterStatus() []*model.ClusterInfo {
  138     infos := make([]*model.ClusterInfo, 0)
  139 
  140     if a.Cluster() != nil {
  141         infos = a.Cluster().GetClusterInfos()
  142     }
  143 
  144     return infos
  145 }
  146 
  147 func (s *Server) InvalidateAllCaches() *model.AppError {
  148     debug.FreeOSMemory()
  149     s.InvalidateAllCachesSkipSend()
  150 
  151     if s.Cluster != nil {
  152 
  153         msg := &model.ClusterMessage{
  154             Event:            model.ClusterEventInvalidateAllCaches,
  155             SendType:         model.ClusterSendReliable,
  156             WaitForAllToSend: true,
  157         }
  158 
  159         s.Cluster.SendClusterMessage(msg)
  160     }
  161 
  162     return nil
  163 }
  164 
  165 func (s *Server) InvalidateAllCachesSkipSend() {
  166     mlog.Info("Purging all caches")
  167     s.userService.ClearAllUsersSessionCacheLocal()
  168     s.statusCache.Purge()
  169     s.Store.Team().ClearCaches()
  170     s.Store.Channel().ClearCaches()
  171     s.Store.User().ClearCaches()
  172     s.Store.Post().ClearCaches()
  173     s.Store.FileInfo().ClearCaches()
  174     s.Store.Webhook().ClearCaches()
  175     linkCache.Purge()
  176     s.LoadLicense()
  177 }
  178 
  179 func (a *App) RecycleDatabaseConnection() {
  180     mlog.Info("Attempting to recycle database connections.")
  181 
  182     // This works by setting 10 seconds as the max conn lifetime for all DB connections.
  183     // This allows in gradually closing connections as they expire. In future, we can think
  184     // of exposing this as a param from the REST api.
  185     a.Srv().Store.RecycleDBConnections(10 * time.Second)
  186 
  187     mlog.Info("Finished recycling database connections.")
  188 }
  189 
  190 func (a *App) TestSiteURL(siteURL string) *model.AppError {
  191     url := fmt.Sprintf("%s/api/v4/system/ping", siteURL)
  192     res, err := http.Get(url)
  193     if err != nil || res.StatusCode != 200 {
  194         return model.NewAppError("testSiteURL", "app.admin.test_site_url.failure", nil, "", http.StatusBadRequest)
  195     }
  196     defer func() {
  197         _, _ = io.Copy(ioutil.Discard, res.Body)
  198         _ = res.Body.Close()
  199     }()
  200 
  201     return nil
  202 }
  203 
  204 func (a *App) TestEmail(userID string, cfg *model.Config) *model.AppError {
  205     if *cfg.EmailSettings.SMTPServer == "" {
  206         return model.NewAppError("testEmail", "api.admin.test_email.missing_server", nil, i18n.T("api.context.invalid_param.app_error", map[string]interface{}{"Name": "SMTPServer"}), http.StatusBadRequest)
  207     }
  208 
  209     // if the user hasn't changed their email settings, fill in the actual SMTP password so that
  210     // the user can verify an existing SMTP connection
  211     if *cfg.EmailSettings.SMTPPassword == model.FakeSetting {
  212         if *cfg.EmailSettings.SMTPServer == *a.Config().EmailSettings.SMTPServer &&
  213             *cfg.EmailSettings.SMTPPort == *a.Config().EmailSettings.SMTPPort &&
  214             *cfg.EmailSettings.SMTPUsername == *a.Config().EmailSettings.SMTPUsername {
  215             *cfg.EmailSettings.SMTPPassword = *a.Config().EmailSettings.SMTPPassword
  216         } else {
  217             return model.NewAppError("testEmail", "api.admin.test_email.reenter_password", nil, "", http.StatusBadRequest)
  218         }
  219     }
  220     user, err := a.GetUser(userID)
  221     if err != nil {
  222         return err
  223     }
  224 
  225     T := i18n.GetUserTranslations(user.Locale)
  226     license := a.Srv().License()
  227     mailConfig := a.Srv().MailServiceConfig()
  228     if err := mail.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), mailConfig, license != nil && *license.Features.Compliance, ""); err != nil {
  229         return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
  230     }
  231 
  232     return nil
  233 }
  234 
  235 // serverBusyStateChanged is called when a CLUSTER_EVENT_BUSY_STATE_CHANGED is received.
  236 func (s *Server) serverBusyStateChanged(sbs *model.ServerBusyState) {
  237     s.Busy.ClusterEventChanged(sbs)
  238     if sbs.Busy {
  239         mlog.Warn("server busy state activitated via cluster event - non-critical services disabled", mlog.Int64("expires_sec", sbs.Expires))
  240     } else {
  241         mlog.Info("server busy state cleared via cluster event - non-critical services enabled")
  242     }
  243 }