Add metric warning support (announcement bar and DM) (#14483)

* Admin. Advisory: Add warning for number of active users metric status

Co-authored-by: Catalin Tomai <catalin.tomai@mattermost.com>
Этот коммит содержится в:
catalintomai
2020-07-22 20:32:21 -07:00
коммит произвёл GitHub
родитель 56fb31f06f
Коммит 549e5b57cd
21 изменённых файлов: 979 добавлений и 28 удалений

Просмотреть файл

@@ -221,7 +221,7 @@ func (a *App) TestEmail(userId string, cfg *model.Config) *model.AppError {
T := utils.GetUserTranslations(user.Locale)
license := a.Srv().License()
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 {
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 {
return model.NewAppError("testEmail", "app.admin.test_email.failure", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
}

Просмотреть файл

@@ -5,16 +5,20 @@ package app
import (
"context"
"fmt"
"html/template"
"net/http"
"strconv"
"strings"
"github.com/mattermost/go-i18n/i18n"
goi18n "github.com/mattermost/go-i18n/i18n"
"github.com/mattermost/mattermost-server/v5/einterfaces"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/httpservice"
"github.com/mattermost/mattermost-server/v5/services/imageproxy"
"github.com/mattermost/mattermost-server/v5/services/mailservice"
"github.com/mattermost/mattermost-server/v5/services/searchengine"
"github.com/mattermost/mattermost-server/v5/services/timezones"
"github.com/mattermost/mattermost-server/v5/utils"
@@ -107,6 +111,7 @@ func (a *App) InitServer() {
if a.Srv().runjobs {
a.Srv().Go(func() {
runLicenseExpirationCheckJob(a)
runCheckNumberOfActiveUsersWarnMetricStatusJob(a)
})
}
a.srv.RunJobs()
@@ -184,6 +189,253 @@ func (s *Server) getFirstServerRunTimestamp() (int64, *model.AppError) {
return value, nil
}
func (a *App) GetWarnMetricsStatus() (map[string]*model.WarnMetricStatus, *model.AppError) {
systemDataList, appErr := a.Srv().Store.System().Get()
if appErr != nil {
return nil, appErr
}
result := map[string]*model.WarnMetricStatus{}
for key, value := range systemDataList {
if strings.HasPrefix(key, model.WARN_METRIC_STATUS_STORE_PREFIX) {
if warnMetric, ok := model.WarnMetricsTable[key]; ok {
if !warnMetric.IsBotOnly && value == model.WARN_METRIC_STATUS_LIMIT_REACHED {
result[key], _ = a.getWarnMetricStatusAndDisplayTextsForId(key, nil)
}
}
}
}
return result, nil
}
func (a *App) getWarnMetricStatusAndDisplayTextsForId(warnMetricId string, T i18n.TranslateFunc) (*model.WarnMetricStatus, *model.WarnMetricDisplayTexts) {
var warnMetricStatus *model.WarnMetricStatus
var warnMetricDisplayTexts = &model.WarnMetricDisplayTexts{}
if warnMetric, ok := model.WarnMetricsTable[warnMetricId]; ok {
warnMetricStatus = &model.WarnMetricStatus{
Id: warnMetric.Id,
Limit: warnMetric.Limit,
Acked: false,
}
if T == nil {
mlog.Debug("No translation function")
return warnMetricStatus, nil
}
warnMetricDisplayTexts.BotMailToBody = T("api.server.warn_metric.bot_response.number_of_users.mailto_body", map[string]interface{}{"Limit": warnMetric.Limit})
warnMetricDisplayTexts.EmailBody = T("api.templates.warn_metric_ack.number_of_active_users.body", map[string]interface{}{"Limit": warnMetric.Limit})
switch warnMetricId {
case model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_200.notification_title")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_200.notification_body")
case model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_400:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_400.notification_title")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_400.notification_body")
case model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_500.notification_title")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_500.notification_body")
default:
mlog.Error("Invalid metric id", mlog.String("id", warnMetricId))
return nil, nil
}
return warnMetricStatus, warnMetricDisplayTexts
}
return nil, nil
}
func (a *App) notifyAdminsOfWarnMetricStatus(warnMetricId string) *model.AppError {
perPage := 25
userOptions := &model.UserGetOptions{
Page: 0,
PerPage: perPage,
Role: model.SYSTEM_ADMIN_ROLE_ID,
Inactive: false,
}
// get sysadmins
var sysAdmins []*model.User
for {
sysAdminsList, err := a.GetUsers(userOptions)
if err != nil {
return err
}
if len(sysAdminsList) == 0 {
return model.NewAppError("NotifyAdminsOfWarnMetricStatus", "app.system.warn_metric.notification.empty_admin_list.app_error", nil, err.Error(), http.StatusInternalServerError)
}
sysAdmins = append(sysAdmins, sysAdminsList...)
if len(sysAdminsList) < perPage {
mlog.Debug("Number of system admins is less than page limit", mlog.Int("count", len(sysAdminsList)))
break
}
}
T := utils.GetUserTranslations(sysAdmins[0].Locale)
warnMetricsBot := &model.Bot{
Username: model.BOT_WARN_METRIC_BOT_USERNAME,
DisplayName: T("app.system.warn_metric.bot_displayname"),
Description: "",
OwnerId: sysAdmins[0].Id,
}
bot, err := a.getOrCreateWarnMetricsBot(warnMetricsBot)
if err != nil {
return err
}
for _, sysAdmin := range sysAdmins {
T := utils.GetUserTranslations(sysAdmin.Locale)
bot.DisplayName = T("app.system.warn_metric.bot_displayname")
bot.Description = T("app.system.warn_metric.bot_description")
channel, appErr := a.GetOrCreateDirectChannel(bot.UserId, sysAdmin.Id)
if appErr != nil {
mlog.Error("Cannot create channel for system bot notification!", mlog.String("Admin Id", sysAdmin.Id))
return appErr
}
warnMetricStatus, warnMetricDisplayTexts := a.getWarnMetricStatusAndDisplayTextsForId(warnMetricId, T)
if warnMetricStatus == nil {
return model.NewAppError("NotifyAdminsOfWarnMetricStatus", "app.system.warn_metric.notification.invalid_metric.app_error", nil, "", http.StatusInternalServerError)
}
botPost := &model.Post{
UserId: bot.UserId,
ChannelId: channel.Id,
Type: model.POST_SYSTEM_WARN_METRIC_STATUS,
Message: "",
}
actions := []*model.PostAction{}
actions = append(actions,
&model.PostAction{
Id: "contactUs",
Name: T("api.server.warn_metric.contact_us"),
Type: model.POST_ACTION_TYPE_BUTTON,
Options: []*model.PostActionOptions{
{
Text: "TrackEventId",
Value: warnMetricId,
},
{
Text: "ActionExecutingMessage",
Value: T("api.server.warn_metric.contacting_us"),
},
},
Integration: &model.PostActionIntegration{
Context: model.StringInterface{
"bot_user_id": bot.UserId,
"force_ack": false,
},
URL: fmt.Sprintf("/warn_metrics/ack/%s", warnMetricId),
},
},
)
attachments := []*model.SlackAttachment{{
AuthorName: "",
Title: warnMetricDisplayTexts.BotTitle,
Text: warnMetricDisplayTexts.BotMessageBody,
Actions: actions,
}}
model.ParseSlackAttachment(botPost, attachments)
mlog.Debug("Send admin advisory for metric", mlog.String("warnMetricId", warnMetricId), mlog.String("userid", botPost.UserId))
if _, err := a.CreatePostAsUser(botPost, a.Session().Id, true); err != nil {
return err
}
}
return nil
}
func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError {
if warnMetric, ok := model.WarnMetricsTable[warnMetricId]; ok {
data, err := a.Srv().Store.System().GetByName(warnMetric.Id)
if err == nil && data != nil && data.Value == model.WARN_METRIC_STATUS_ACK {
mlog.Debug("This metric warning has already been acknowledged")
return nil
}
if !forceAck {
if len(*a.Config().EmailSettings.SMTPServer) == 0 {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.missing_server.app_error", nil, utils.T("api.context.invalid_param.app_error", map[string]interface{}{"Name": "SMTPServer"}), http.StatusInternalServerError)
}
T := utils.GetUserTranslations(sender.Locale)
bodyPage := a.Srv().EmailService.newEmailTemplate("warn_metric_ack", sender.Locale)
bodyPage.Props["ContactNameHeader"] = T("api.templates.warn_metric_ack.body.contact_name_header")
bodyPage.Props["ContactNameValue"] = sender.GetFullName()
bodyPage.Props["ContactEmailHeader"] = T("api.templates.warn_metric_ack.body.contact_email_header")
bodyPage.Props["ContactEmailValue"] = sender.Email
//same definition as the active users count metric displayed in the SystemConsole Analytics section
registeredUsersCount, cerr := a.Srv().Store.User().Count(model.UserCountOptions{})
if cerr != nil {
mlog.Error("Error retrieving the number of registered users", mlog.Err(cerr))
} else {
bodyPage.Props["RegisteredUsersHeader"] = T("api.templates.warn_metric_ack.body.registered_users_header")
bodyPage.Props["RegisteredUsersValue"] = registeredUsersCount
}
bodyPage.Props["SiteURLHeader"] = T("api.templates.warn_metric_ack.body.site_url_header")
bodyPage.Props["SiteURL"] = a.GetSiteURL()
bodyPage.Props["DiagnosticIdHeader"] = T("api.templates.warn_metric_ack.body.diagnostic_id_header")
bodyPage.Props["DiagnosticIdValue"] = a.DiagnosticId()
bodyPage.Props["Footer"] = T("api.templates.warn_metric_ack.footer")
warnMetricStatus, warnMetricDisplayTexts := a.getWarnMetricStatusAndDisplayTextsForId(warnMetricId, T)
if warnMetricStatus == nil {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.invalid_warn_metric.app_error", nil, "", http.StatusInternalServerError)
}
subject := T("api.templates.warn_metric_ack.subject")
bodyPage.Props["Title"] = warnMetricDisplayTexts.EmailBody
if err = mailservice.SendMailUsingConfig(model.MM_SUPPORT_ADDRESS, subject, bodyPage.Render(), a.Config(), false, sender.Email); err != nil {
mlog.Error("Error while sending email", mlog.String("destination email", model.MM_SUPPORT_ADDRESS), mlog.Err(err))
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
}
}
mlog.Debug("Disable the monitoring of all warn metrics")
err = a.setWarnMetricsStatus(model.WARN_METRIC_STATUS_ACK)
if err != nil {
return err
}
if !warnMetric.IsBotOnly && !isBot {
message := model.NewWebSocketEvent(model.WEBSOCKET_WARN_METRIC_STATUS_REMOVED, "", "", "", nil)
message.Add("warnMetricId", warnMetric.Id)
a.Publish(message)
}
}
return nil
}
func (a *App) setWarnMetricsStatus(status string) *model.AppError {
for _, warnMetric := range model.WarnMetricsTable {
a.setWarnMetricsStatusForId(warnMetric.Id, status)
}
return nil
}
func (a *App) setWarnMetricsStatusForId(warnMetricId string, status string) *model.AppError {
mlog.Info("Storing user acknowledgement for warn metric", mlog.String("warnMetricId", warnMetricId))
if err := a.Srv().Store.System().SaveOrUpdate(&model.System{
Name: warnMetricId,
Value: status,
}); err != nil {
mlog.Error("Unable to write to database.", mlog.Err(err))
return model.NewAppError("setWarnMetricsStatusForId", "app.system.warn_metric.store.app_error", map[string]interface{}{"WarnMetricName": warnMetricId}, "", http.StatusInternalServerError)
}
return nil
}
func (a *App) Srv() *Server {
return a.srv
}

Просмотреть файл

@@ -692,6 +692,7 @@ type AppIface interface {
GetUsersWithoutTeamPage(options *model.UserGetOptions, asAdmin bool) ([]*model.User, *model.AppError)
GetVerifyEmailToken(token string) (*model.Token, *model.AppError)
GetViewUsersRestrictions(userId string) (*model.ViewUsersRestrictions, *model.AppError)
GetWarnMetricsStatus() (map[string]*model.WarnMetricStatus, *model.AppError)
HTTPService() httpservice.HTTPService
Handle404(w http.ResponseWriter, r *http.Request)
HandleCommandResponse(command *model.Command, args *model.CommandArgs, response *model.CommandResponse, builtIn bool) (*model.CommandResponse, *model.AppError)
@@ -753,6 +754,7 @@ type AppIface interface {
NewPluginAPI(manifest *model.Manifest) plugin.API
Notification() einterfaces.NotificationInterface
NotificationsLog() *mlog.Logger
NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError
OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError
OriginChecker() func(*http.Request) bool
PatchChannel(channel *model.Channel, patch *model.ChannelPatch, userId string) (*model.Channel, *model.AppError)

Просмотреть файл

@@ -65,6 +65,51 @@ func (a *App) CreateBot(bot *model.Bot) (*model.Bot, *model.AppError) {
return savedBot, nil
}
func (a *App) getOrCreateWarnMetricsBot(botDef *model.Bot) (*model.Bot, *model.AppError) {
botUser, appErr := a.GetUserByUsername(botDef.Username)
if appErr != nil {
if appErr.StatusCode != http.StatusNotFound {
mlog.Error(appErr.Error())
return nil, appErr
}
// cannot find this bot user, save the user
user, err := a.Srv().Store.User().Save(model.UserFromBot(botDef))
if err != nil {
mlog.Error(err.Error())
return nil, err
}
botDef.UserId = user.Id
//save the bot
savedBot, nErr := a.Srv().Store.Bot().Save(botDef)
if nErr != nil {
a.Srv().Store.User().PermanentDelete(savedBot.UserId)
var nAppErr *model.AppError
switch {
case errors.As(nErr, &nAppErr): // in case we haven't converted to plain error.
return nil, nAppErr
default: // last fallback in case it doesn't map to an existing app error.
return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
}
}
return savedBot, nil
}
if botUser == nil {
return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, "", http.StatusInternalServerError)
}
//return the bot for this user
savedBot, appErr := a.GetBot(botUser.Id, false)
if appErr != nil {
mlog.Error(appErr.Error())
return nil, appErr
}
return savedBot, nil
}
// PatchBot applies the given patch to the bot and corresponding user.
func (a *App) PatchBot(botUserId string, botPatch *model.BotPatch) (*model.Bot, *model.AppError) {
bot, err := a.GetBot(botUserId, true)

Просмотреть файл

@@ -57,6 +57,7 @@ const (
TRACK_ELASTICSEARCH = "elasticsearch"
TRACK_GROUPS = "groups"
TRACK_CHANNEL_MODERATION = "channel_moderation"
TRACK_WARN_METRICS = "warn_metrics"
TRACK_ACTIVITY = "activity"
TRACK_LICENSE = "license"
@@ -83,6 +84,7 @@ func (s *Server) sendDailyDiagnostics(override bool) {
s.trackElasticsearch()
s.trackGroups()
s.trackChannelModeration()
s.trackWarnMetrics()
}
}
@@ -1088,3 +1090,19 @@ func (s *Server) trackChannelModeration() {
"use_channel_mentions_guest_disabled_count": useChannelMentionsGuest,
})
}
func (s *Server) trackWarnMetrics() {
systemDataList, appErr := s.Store.System().Get()
if appErr != nil {
return
}
for key, value := range systemDataList {
if strings.HasPrefix(key, model.WARN_METRIC_STATUS_STORE_PREFIX) {
if _, ok := model.WarnMetricsTable[key]; ok {
s.SendDiagnostic(TRACK_WARN_METRICS, map[string]interface{}{
key: value != "false",
})
}
}
}
}

Просмотреть файл

@@ -550,15 +550,19 @@ func (es *EmailService) sendNotificationMail(to, subject, htmlBody string) *mode
}
func (es *EmailService) sendMail(to, subject, htmlBody string) *model.AppError {
return es.sendMailWithCC(to, subject, htmlBody, "")
}
func (es *EmailService) sendMailWithCC(to, subject, htmlBody string, ccMail string) *model.AppError {
license := es.srv.License()
return mailservice.SendMailUsingConfig(to, subject, htmlBody, es.srv.Config(), license != nil && *license.Features.Compliance)
return mailservice.SendMailUsingConfig(to, subject, htmlBody, es.srv.Config(), license != nil && *license.Features.Compliance, ccMail)
}
func (es *EmailService) sendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) *model.AppError {
license := es.srv.License()
config := es.srv.Config()
return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, config, license != nil && *license.Features.Compliance)
return mailservice.SendMailWithEmbeddedFilesUsingConfig(to, subject, htmlBody, embeddedFiles, config, license != nil && *license.Features.Compliance, "")
}
func (es *EmailService) CreateVerifyEmailToken(userId string, newEmail string) (*model.Token, *model.AppError) {

Просмотреть файл

@@ -26,10 +26,12 @@ import (
"net/http"
"net/url"
"path"
"path/filepath"
"strings"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/store"
"github.com/mattermost/mattermost-server/v5/utils"
@@ -211,11 +213,20 @@ func (a *App) DoPostActionWithCookie(postId, actionId, userId, selectedOption st
return "", appErr
}
resp, appErr := a.DoActionRequest(upstreamURL, upstreamRequest.ToJson())
if appErr != nil {
return "", appErr
var resp *http.Response
if strings.HasPrefix(upstreamURL, "/warn_metrics/") {
appErr = a.doLocalWarnMetricsRequest(upstreamURL, upstreamRequest)
if appErr != nil {
return "", appErr
}
return "", nil
} else {
resp, appErr = a.DoActionRequest(upstreamURL, upstreamRequest.ToJson())
if appErr != nil {
return "", appErr
}
defer resp.Body.Close()
}
defer resp.Body.Close()
var response model.PostActionIntegrationResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
@@ -398,6 +409,129 @@ func (a *App) doPluginRequest(method, rawURL string, values url.Values, body []b
return resp, nil
}
func (a *App) doLocalWarnMetricsRequest(rawURL string, upstreamRequest *model.PostActionIntegrationRequest) *model.AppError {
_, err := url.Parse(rawURL)
if err != nil {
return model.NewAppError("doLocalWarnMetricsRequest", "api.post.do_action.action_integration.app_error", nil, err.Error(), http.StatusBadRequest)
}
warnMetricId := filepath.Base(rawURL)
if warnMetricId == "" {
return model.NewAppError("doLocalWarnMetricsRequest", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest)
}
user, appErr := a.GetUser(a.Session().UserId)
if appErr != nil {
return appErr
}
botPost := &model.Post{
UserId: upstreamRequest.Context["bot_user_id"].(string),
ChannelId: upstreamRequest.ChannelId,
HasReactions: true,
}
forceAck := upstreamRequest.Context["force_ack"].(bool)
if appErr = a.NotifyAndSetWarnMetricAck(warnMetricId, user, forceAck, true); appErr != nil {
if forceAck {
return appErr
}
mailtoLinkText := a.buildWarnMetricMailtoLink(warnMetricId, user)
botPost.Message = ":warning: " + utils.T("api.server.warn_metric.bot_response.notification_failure.message")
actions := []*model.PostAction{}
actions = append(actions,
&model.PostAction{
Id: "emailUs",
Name: utils.T("api.server.warn_metric.email_us"),
Type: model.POST_ACTION_TYPE_BUTTON,
Options: []*model.PostActionOptions{
{
Text: "WarnMetricMailtoUrl",
Value: mailtoLinkText,
},
{
Text: "TrackEventId",
Value: warnMetricId,
},
},
Integration: &model.PostActionIntegration{
Context: model.StringInterface{
"bot_user_id": botPost.UserId,
"force_ack": true,
},
URL: fmt.Sprintf("/warn_metrics/ack/%s", model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500),
},
},
)
attachements := []*model.SlackAttachment{{
AuthorName: "",
Title: "",
Actions: actions,
Text: utils.T("api.server.warn_metric.bot_response.notification_failure.body"),
}}
model.ParseSlackAttachment(botPost, attachements)
} else {
botPost.Message = ":white_check_mark: " + utils.T("api.server.warn_metric.bot_response.notification_success.message")
}
if _, err := a.CreatePostAsUser(botPost, a.Session().Id, true); err != nil {
return err
}
return nil
}
type MailToLinkContent struct {
MetricId string `json:"metric_id"`
MailRecipient string `json:"mail_recipient"`
MailCC string `json:"mail_cc"`
MailSubject string `json:"mail_subject"`
MailBody string `json:"mail_body"`
}
func (mlc *MailToLinkContent) ToJson() string {
b, _ := json.Marshal(mlc)
return string(b)
}
func (a *App) buildWarnMetricMailtoLink(warnMetricId string, user *model.User) string {
T := utils.GetUserTranslations(user.Locale)
_, warnMetricDisplayTexts := a.getWarnMetricStatusAndDisplayTextsForId(warnMetricId, T)
mailBody := warnMetricDisplayTexts.BotMailToBody
mailBody += T("api.server.warn_metric.bot_response.mailto_contact_header", map[string]interface{}{"Contact": user.GetFullName()})
mailBody += "\r\n"
mailBody += T("api.server.warn_metric.bot_response.mailto_email_header", map[string]interface{}{"Email": user.Email})
mailBody += "\r\n"
registeredUsersCount, err := a.Srv().Store.User().Count(model.UserCountOptions{})
if err != nil {
mlog.Error("Error retrieving the number of registered users", mlog.Err(err))
} else {
mailBody += utils.T("api.server.warn_metric.bot_response.mailto_registered_users_header", map[string]interface{}{"NoRegisteredUsers": registeredUsersCount})
mailBody += "\r\n"
}
mailBody += T("api.server.warn_metric.bot_response.mailto_site_url_header", map[string]interface{}{"SiteUrl": a.GetSiteURL()})
mailBody += "\r\n"
mailBody += T("api.server.warn_metric.bot_response.mailto_diagnostic_id_header", map[string]interface{}{"DiagnosticId": a.DiagnosticId()})
mailBody += "\r\n"
mailBody += T("api.server.warn_metric.bot_response.mailto_footer")
mailToLinkContent := &MailToLinkContent{
MetricId: warnMetricId,
MailRecipient: "support@mattermost.com",
MailCC: user.Email,
MailSubject: T("api.server.warn_metric.bot_response.mailto_subject"),
MailBody: mailBody,
}
return mailToLinkContent.ToJson()
}
func (a *App) DoLocalRequest(rawURL string, body []byte) (*http.Response, *model.AppError) {
return a.doPluginRequest("POST", rawURL, nil, body)
}

Просмотреть файл

@@ -8940,6 +8940,28 @@ func (a *OpenTracingAppLayer) GetViewUsersRestrictions(userId string) (*model.Vi
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetWarnMetricsStatus() (map[string]*model.WarnMetricStatus, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetWarnMetricsStatus")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetWarnMetricsStatus()
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) Handle404(w http.ResponseWriter, r *http.Request) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.Handle404")
@@ -10255,6 +10277,28 @@ func (a *OpenTracingAppLayer) NewWebHub() *app.Hub {
return resultVar0
}
func (a *OpenTracingAppLayer) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NotifyAndSetWarnMetricAck")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.NotifyAndSetWarnMetricAck(warnMetricId, sender, forceAck, isBot)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) NotifySessionsExpired() *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NotifySessionsExpired")

Просмотреть файл

@@ -115,7 +115,7 @@ func (s *Server) DoSecurityUpdateCheck() {
for _, user := range users {
mlog.Info("Sending security bulletin", mlog.String("bulletin_id", bulletin.Id), mlog.String("user_email", user.Email))
license := s.License()
mailservice.SendMailUsingConfig(user.Email, utils.T("mattermost.bulletin.subject"), string(body), s.Config(), license != nil && *license.Features.Compliance)
mailservice.SendMailUsingConfig(user.Email, utils.T("mattermost.bulletin.subject"), string(body), s.Config(), license != nil && *license.Features.Compliance, "")
}
bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id}

Просмотреть файл

@@ -1090,6 +1090,13 @@ func runLicenseExpirationCheckJob(a *App) {
}, time.Hour*24)
}
func runCheckNumberOfActiveUsersWarnMetricStatusJob(a *App) {
doCheckNumberOfActiveUsersWarnMetricStatus(a)
model.CreateRecurringTask("Check Number Of Active Users Warn Metric Status", func() {
doCheckNumberOfActiveUsersWarnMetricStatus(a)
}, time.Hour*24)
}
func doSecurity(s *Server) {
s.DoSecurityUpdateCheck()
}
@@ -1117,6 +1124,58 @@ func doSessionCleanup(s *Server) {
s.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE)
}
func doCheckNumberOfActiveUsersWarnMetricStatus(a *App) {
license := a.Srv().License()
if license != nil {
mlog.Debug("License is present, skip this check")
return
}
numberOfActiveUsers, err := a.Srv().Store.User().Count(model.UserCountOptions{})
if err != nil {
mlog.Error("Error to get active registered users.", mlog.Err(err))
}
warnMetrics := []model.WarnMetric{}
if numberOfActiveUsers < model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200].Limit {
return
} else if numberOfActiveUsers >= model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200].Limit && numberOfActiveUsers < model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_400].Limit {
warnMetrics = append(warnMetrics, model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200])
} else if numberOfActiveUsers >= model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_400].Limit && numberOfActiveUsers < model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500].Limit {
warnMetrics = append(warnMetrics, model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_400])
} else {
warnMetrics = append(warnMetrics, model.WarnMetricsTable[model.SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500])
}
for _, warnMetric := range warnMetrics {
data, err := a.Srv().Store.System().GetByName(warnMetric.Id)
if err == nil && data != nil && (data.Value == model.WARN_METRIC_STATUS_ACK || data.Value == model.WARN_METRIC_STATUS_RUNONCE) {
mlog.Debug("This metric warning has already been acked or should only run once")
continue
}
if err = a.Srv().Store.System().SaveOrUpdate(&model.System{Name: warnMetric.Id, Value: model.WARN_METRIC_STATUS_LIMIT_REACHED}); err != nil {
mlog.Error("Unable to write to database.", mlog.String("id", warnMetric.Id), mlog.Err(err))
continue
}
warnMetricStatus, _ := a.getWarnMetricStatusAndDisplayTextsForId(warnMetric.Id, nil)
if !warnMetric.IsBotOnly {
message := model.NewWebSocketEvent(model.WEBSOCKET_WARN_METRIC_STATUS_RECEIVED, "", "", "", nil)
message.Add("warnMetricStatus", warnMetricStatus.ToJson())
a.Publish(message)
}
if err = a.notifyAdminsOfWarnMetricStatus(warnMetric.Id); err != nil {
mlog.Error("Failed to send notifications to admin users.", mlog.Err(err))
}
if warnMetric.IsRunOnce {
a.setWarnMetricsStatusForId(warnMetric.Id, model.WARN_METRIC_STATUS_RUNONCE)
}
}
}
func doLicenseExpirationCheck(a *App) {
a.Srv().LoadLicense()
license := a.Srv().License()