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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
56fb31f06f
Коммит
549e5b57cd
252
app/app.go
252
app/app.go
@@ -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
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user