github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+incompatible/app/admin.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "io" 8 "os" 9 "strings" 10 "time" 11 12 "runtime/debug" 13 14 "net/http" 15 16 "github.com/mattermost/mattermost-server/mlog" 17 "github.com/mattermost/mattermost-server/model" 18 "github.com/mattermost/mattermost-server/services/mailservice" 19 "github.com/mattermost/mattermost-server/utils" 20 ) 21 22 func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) { 23 var lines []string 24 if a.Cluster != nil && *a.Config().ClusterSettings.Enable { 25 lines = append(lines, "-----------------------------------------------------------------------------------------------------------") 26 lines = append(lines, "-----------------------------------------------------------------------------------------------------------") 27 lines = append(lines, a.Cluster.GetMyClusterInfo().Hostname) 28 lines = append(lines, "-----------------------------------------------------------------------------------------------------------") 29 lines = append(lines, "-----------------------------------------------------------------------------------------------------------") 30 } 31 32 melines, err := a.GetLogsSkipSend(page, perPage) 33 if err != nil { 34 return nil, err 35 } 36 37 lines = append(lines, melines...) 38 39 if a.Cluster != nil && *a.Config().ClusterSettings.Enable { 40 clines, err := a.Cluster.GetLogs(page, perPage) 41 if err != nil { 42 return nil, err 43 } 44 45 lines = append(lines, clines...) 46 } 47 48 return lines, nil 49 } 50 51 func (a *App) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) { 52 var lines []string 53 54 if a.Config().LogSettings.EnableFile { 55 file, err := os.Open(utils.GetLogFileLocation(a.Config().LogSettings.FileLocation)) 56 if err != nil { 57 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) 58 } 59 60 defer file.Close() 61 62 var newLine = []byte{'\n'} 63 var lineCount int 64 const searchPos = -1 65 lineEndPos, err := file.Seek(0, io.SeekEnd) 66 if err != nil { 67 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) 68 } 69 for { 70 pos, err := file.Seek(searchPos, io.SeekCurrent) 71 if err != nil { 72 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) 73 } 74 75 b := make([]byte, 1) 76 _, err = file.ReadAt(b, pos) 77 if err != nil { 78 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) 79 } 80 81 if b[0] == newLine[0] || pos == 0 { 82 lineCount++ 83 if lineCount > page*perPage { 84 line := make([]byte, lineEndPos-pos) 85 _, err := file.ReadAt(line, pos) 86 if err != nil { 87 return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError) 88 } 89 lines = append(lines, string(line)) 90 } 91 if pos == 0 { 92 break 93 } 94 lineEndPos = pos 95 } 96 97 if len(lines) == perPage { 98 break 99 } 100 } 101 102 for i, j := 0, len(lines)-1; i < j; i, j = i+1, j-1 { 103 lines[i], lines[j] = lines[j], lines[i] 104 } 105 } else { 106 lines = append(lines, "") 107 } 108 109 return lines, nil 110 } 111 112 func (a *App) GetClusterStatus() []*model.ClusterInfo { 113 infos := make([]*model.ClusterInfo, 0) 114 115 if a.Cluster != nil { 116 infos = a.Cluster.GetClusterInfos() 117 } 118 119 return infos 120 } 121 122 func (a *App) InvalidateAllCaches() *model.AppError { 123 debug.FreeOSMemory() 124 a.InvalidateAllCachesSkipSend() 125 126 if a.Cluster != nil { 127 128 msg := &model.ClusterMessage{ 129 Event: model.CLUSTER_EVENT_INVALIDATE_ALL_CACHES, 130 SendType: model.CLUSTER_SEND_RELIABLE, 131 WaitForAllToSend: true, 132 } 133 134 a.Cluster.SendClusterMessage(msg) 135 } 136 137 return nil 138 } 139 140 func (a *App) InvalidateAllCachesSkipSend() { 141 mlog.Info("Purging all caches") 142 a.sessionCache.Purge() 143 ClearStatusCache() 144 a.Srv.Store.Channel().ClearCaches() 145 a.Srv.Store.User().ClearCaches() 146 a.Srv.Store.Post().ClearCaches() 147 a.Srv.Store.FileInfo().ClearCaches() 148 a.Srv.Store.Webhook().ClearCaches() 149 a.LoadLicense() 150 } 151 152 func (a *App) GetConfig() *model.Config { 153 json := a.Config().ToJson() 154 cfg := model.ConfigFromJson(strings.NewReader(json)) 155 cfg.Sanitize() 156 157 return cfg 158 } 159 160 func (a *App) GetEnvironmentConfig() map[string]interface{} { 161 return a.EnvironmentConfig() 162 } 163 164 func (a *App) SaveConfig(cfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError { 165 oldCfg := a.Config() 166 cfg.SetDefaults() 167 a.Desanitize(cfg) 168 169 if err := cfg.IsValid(); err != nil { 170 return err 171 } 172 173 if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil { 174 return err 175 } 176 177 if *a.Config().ClusterSettings.Enable && *a.Config().ClusterSettings.ReadOnlyConfig { 178 return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, "", http.StatusForbidden) 179 } 180 181 a.DisableConfigWatch() 182 183 a.UpdateConfig(func(update *model.Config) { 184 *update = *cfg 185 }) 186 a.PersistConfig() 187 a.ReloadConfig() 188 a.EnableConfigWatch() 189 190 if a.Metrics != nil { 191 if *a.Config().MetricsSettings.Enable { 192 a.Metrics.StartServer() 193 } else { 194 a.Metrics.StopServer() 195 } 196 } 197 198 if a.Cluster != nil { 199 err := a.Cluster.ConfigChanged(cfg, oldCfg, sendConfigChangeClusterMessage) 200 if err != nil { 201 return err 202 } 203 } 204 205 // start/restart email batching job if necessary 206 a.InitEmailBatching() 207 208 return nil 209 } 210 211 func (a *App) RecycleDatabaseConnection() { 212 oldStore := a.Srv.Store 213 214 mlog.Warn("Attempting to recycle the database connection.") 215 a.Srv.Store = a.newStore() 216 a.Jobs.Store = a.Srv.Store 217 218 if a.Srv.Store != oldStore { 219 time.Sleep(20 * time.Second) 220 oldStore.Close() 221 } 222 223 mlog.Warn("Finished recycling the database connection.") 224 } 225 226 func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError { 227 if len(cfg.EmailSettings.SMTPServer) == 0 { 228 return model.NewAppError("testEmail", "api.admin.test_email.missing_server", nil, utils.T("api.context.invalid_param.app_error", map[string]interface{}{"Name": "SMTPServer"}), http.StatusBadRequest) 229 } 230 231 // if the user hasn't changed their email settings, fill in the actual SMTP password so that 232 // the user can verify an existing SMTP connection 233 if cfg.EmailSettings.SMTPPassword == model.FAKE_SETTING { 234 if cfg.EmailSettings.SMTPServer == a.Config().EmailSettings.SMTPServer && 235 cfg.EmailSettings.SMTPPort == a.Config().EmailSettings.SMTPPort && 236 cfg.EmailSettings.SMTPUsername == a.Config().EmailSettings.SMTPUsername { 237 cfg.EmailSettings.SMTPPassword = a.Config().EmailSettings.SMTPPassword 238 } else { 239 return model.NewAppError("testEmail", "api.admin.test_email.reenter_password", nil, "", http.StatusBadRequest) 240 } 241 } 242 user, err := a.GetUser(userId) 243 if err != nil { 244 return err 245 } 246 247 T := utils.GetUserTranslations(user.Locale) 248 license := a.License() 249 if err := mailservice.SendMailUsingConfig(user.Email, T("api.admin.test_email.subject"), T("api.admin.test_email.body"), cfg, license != nil && *license.Features.Compliance); err != nil { 250 return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError) 251 } 252 253 return nil 254 }