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