github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/app/admin.go (about)

     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  	"os"
    10  	"time"
    11  
    12  	"runtime/debug"
    13  
    14  	"net/http"
    15  
    16  	"github.com/vnforks/kid/v5/mlog"
    17  	"github.com/vnforks/kid/v5/model"
    18  	"github.com/vnforks/kid/v5/services/mailservice"
    19  	"github.com/vnforks/kid/v5/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  		logFile := utils.GetLogFileLocation(*a.Config().LogSettings.FileLocation)
    56  		file, err := os.Open(logFile)
    57  		if err != nil {
    58  			return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
    59  		}
    60  
    61  		defer file.Close()
    62  
    63  		var newLine = []byte{'\n'}
    64  		var lineCount int
    65  		const searchPos = -1
    66  		b := make([]byte, 1)
    67  		var endOffset int64 = 0
    68  
    69  		// if the file exists and it's last byte is '\n' - skip it
    70  		var stat os.FileInfo
    71  		if stat, err = os.Stat(logFile); err == nil {
    72  			if _, err = file.ReadAt(b, stat.Size()-1); err == nil && b[0] == newLine[0] {
    73  				endOffset = -1
    74  			}
    75  		}
    76  		lineEndPos, err := file.Seek(endOffset, io.SeekEnd)
    77  		if err != nil {
    78  			return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
    79  		}
    80  		for {
    81  			pos, err := file.Seek(searchPos, io.SeekCurrent)
    82  			if err != nil {
    83  				return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
    84  			}
    85  
    86  			_, err = file.ReadAt(b, pos)
    87  			if err != nil {
    88  				return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
    89  			}
    90  
    91  			if b[0] == newLine[0] || pos == 0 {
    92  				lineCount++
    93  				if lineCount > page*perPage {
    94  					line := make([]byte, lineEndPos-pos)
    95  					_, err := file.ReadAt(line, pos)
    96  					if err != nil {
    97  						return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
    98  					}
    99  					lines = append(lines, string(line))
   100  				}
   101  				if pos == 0 {
   102  					break
   103  				}
   104  				lineEndPos = pos
   105  			}
   106  
   107  			if len(lines) == perPage {
   108  				break
   109  			}
   110  		}
   111  
   112  		for i, j := 0, len(lines)-1; i < j; i, j = i+1, j-1 {
   113  			lines[i], lines[j] = lines[j], lines[i]
   114  		}
   115  	} else {
   116  		lines = append(lines, "")
   117  	}
   118  
   119  	return lines, nil
   120  }
   121  
   122  func (a *App) GetClusterStatus() []*model.ClusterInfo {
   123  	infos := make([]*model.ClusterInfo, 0)
   124  
   125  	if a.Cluster() != nil {
   126  		infos = a.Cluster().GetClusterInfos()
   127  	}
   128  
   129  	return infos
   130  }
   131  
   132  func (a *App) InvalidateAllCaches() *model.AppError {
   133  	debug.FreeOSMemory()
   134  	a.InvalidateAllCachesSkipSend()
   135  
   136  	if a.Cluster() != nil {
   137  
   138  		msg := &model.ClusterMessage{
   139  			Event:            model.CLUSTER_EVENT_INVALIDATE_ALL_CACHES,
   140  			SendType:         model.CLUSTER_SEND_RELIABLE,
   141  			WaitForAllToSend: true,
   142  		}
   143  
   144  		a.Cluster().SendClusterMessage(msg)
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  func (a *App) InvalidateAllCachesSkipSend() {
   151  	mlog.Info("Purging all caches")
   152  	a.Srv().sessionCache.Purge()
   153  	a.Srv().statusCache.Purge()
   154  	a.Srv().Store.Branch().ClearCaches()
   155  	a.Srv().Store.Class().ClearCaches()
   156  	a.Srv().Store.User().ClearCaches()
   157  	a.Srv().Store.FileInfo().ClearCaches()
   158  	a.Srv().Store.Webhook().ClearCaches()
   159  	a.LoadLicense()
   160  }
   161  
   162  func (a *App) RecycleDatabaseConnection() {
   163  	oldStore := a.Srv().Store
   164  
   165  	mlog.Warn("Attempting to recycle the database connection.")
   166  	a.Srv().Store = a.Srv().newStore()
   167  	a.Srv().Jobs.Store = a.Srv().Store
   168  
   169  	if a.Srv().Store != oldStore {
   170  		time.Sleep(20 * time.Second)
   171  		oldStore.Close()
   172  	}
   173  
   174  	mlog.Warn("Finished recycling the database connection.")
   175  }
   176  
   177  func (a *App) TestSiteURL(siteURL string) *model.AppError {
   178  	url := fmt.Sprintf("%s/api/v4/system/ping", siteURL)
   179  	res, err := http.Get(url)
   180  	if err != nil || res.StatusCode != 200 {
   181  		return model.NewAppError("testSiteURL", "app.admin.test_site_url.failure", nil, "", http.StatusBadRequest)
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError {
   188  	if len(*cfg.EmailSettings.SMTPServer) == 0 {
   189  		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)
   190  	}
   191  
   192  	if !*cfg.EmailSettings.SendEmailNotifications {
   193  		return nil
   194  	}
   195  
   196  	// if the user hasn't changed their email settings, fill in the actual SMTP password so that
   197  	// the user can verify an existing SMTP connection
   198  	if *cfg.EmailSettings.SMTPPassword == model.FAKE_SETTING {
   199  		if *cfg.EmailSettings.SMTPServer == *a.Config().EmailSettings.SMTPServer &&
   200  			*cfg.EmailSettings.SMTPPort == *a.Config().EmailSettings.SMTPPort &&
   201  			*cfg.EmailSettings.SMTPUsername == *a.Config().EmailSettings.SMTPUsername {
   202  			*cfg.EmailSettings.SMTPPassword = *a.Config().EmailSettings.SMTPPassword
   203  		} else {
   204  			return model.NewAppError("testEmail", "api.admin.test_email.reenter_password", nil, "", http.StatusBadRequest)
   205  		}
   206  	}
   207  	user, err := a.GetUser(userId)
   208  	if err != nil {
   209  		return err
   210  	}
   211  
   212  	T := utils.GetUserTranslations(user.Locale)
   213  	license := a.License()
   214  	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 {
   215  		return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
   216  	}
   217  
   218  	return nil
   219  }
   220  
   221  // ServerBusyStateChanged is called when a CLUSTER_EVENT_BUSY_STATE_CHANGED is received.
   222  func (a *App) ServerBusyStateChanged(sbs *model.ServerBusyState) {
   223  	a.Srv().Busy.ClusterEventChanged(sbs)
   224  	if sbs.Busy {
   225  		mlog.Warn("server busy state activitated via cluster event - non-critical services disabled", mlog.Int64("expires_sec", sbs.Expires))
   226  	} else {
   227  		mlog.Info("server busy state cleared via cluster event - non-critical services enabled")
   228  	}
   229  }