github.com/coincircle/mattermost-server@v4.8.1-0.20180321182714-9d701c704416+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 l4g "github.com/alecthomas/log4go" 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 l4g.Info(utils.T("api.context.invalidate_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) SaveConfig(cfg *model.Config, sendConfigChangeClusterMessage bool) *model.AppError { 160 oldCfg := a.Config() 161 cfg.SetDefaults() 162 a.Desanitize(cfg) 163 164 if err := cfg.IsValid(); err != nil { 165 return err 166 } 167 168 if err := utils.ValidateLdapFilter(cfg, a.Ldap); err != nil { 169 return err 170 } 171 172 if *a.Config().ClusterSettings.Enable && *a.Config().ClusterSettings.ReadOnlyConfig { 173 return model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, "", http.StatusForbidden) 174 } 175 176 a.DisableConfigWatch() 177 a.UpdateConfig(func(update *model.Config) { 178 *update = *cfg 179 }) 180 a.PersistConfig() 181 a.ReloadConfig() 182 a.EnableConfigWatch() 183 184 if a.Metrics != nil { 185 if *a.Config().MetricsSettings.Enable { 186 a.Metrics.StartServer() 187 } else { 188 a.Metrics.StopServer() 189 } 190 } 191 192 if a.Cluster != nil { 193 err := a.Cluster.ConfigChanged(cfg, oldCfg, sendConfigChangeClusterMessage) 194 if err != nil { 195 return err 196 } 197 } 198 199 // start/restart email batching job if necessary 200 a.InitEmailBatching() 201 202 return nil 203 } 204 205 func (a *App) RecycleDatabaseConnection() { 206 oldStore := a.Srv.Store 207 208 l4g.Warn(utils.T("api.admin.recycle_db_start.warn")) 209 a.Srv.Store = a.newStore() 210 a.Jobs.Store = a.Srv.Store 211 212 if a.Srv.Store != oldStore { 213 time.Sleep(20 * time.Second) 214 oldStore.Close() 215 } 216 217 l4g.Warn(utils.T("api.admin.recycle_db_end.warn")) 218 } 219 220 func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError { 221 if len(cfg.EmailSettings.SMTPServer) == 0 { 222 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) 223 } 224 225 // if the user hasn't changed their email settings, fill in the actual SMTP password so that 226 // the user can verify an existing SMTP connection 227 if cfg.EmailSettings.SMTPPassword == model.FAKE_SETTING { 228 if cfg.EmailSettings.SMTPServer == a.Config().EmailSettings.SMTPServer && 229 cfg.EmailSettings.SMTPPort == a.Config().EmailSettings.SMTPPort && 230 cfg.EmailSettings.SMTPUsername == a.Config().EmailSettings.SMTPUsername { 231 cfg.EmailSettings.SMTPPassword = a.Config().EmailSettings.SMTPPassword 232 } else { 233 return model.NewAppError("testEmail", "api.admin.test_email.reenter_password", nil, "", http.StatusBadRequest) 234 } 235 } 236 if user, err := a.GetUser(userId); err != nil { 237 return err 238 } else { 239 T := utils.GetUserTranslations(user.Locale) 240 license := a.License() 241 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 { 242 return err 243 } 244 } 245 246 return nil 247 }