Files
mostlymatter/app/admin_advisor.go
Kyriakos Z c11ad8995f Adds OmitConnection parameter to broadcast (#20723)
* Adds OmitConnection parameter to broadcast

Currently we have no means to omit sending a websocket event to a
specific connection id.
This is needed mainly so that the initiator won't receive an event for
the action it just initiated.
Will be used for the global drafts feature, so that we won't update
drafts through ws when a user is typing.

This commit adds OmitConnection to the Broadcast struct and to the
NewWebSocketEvent function signature.
shouldSendEvent should return false for that specific connection.

* Return early only if connection id matches the omitted

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2022-09-02 13:17:22 +03:00

274 строки
14 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"net/http"
"strings"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
"github.com/mattermost/mattermost-server/v6/shared/mail"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
func (a *App) GetWarnMetricsStatus() (map[string]*model.WarnMetricStatus, *model.AppError) {
systemDataList, nErr := a.Srv().Store.System().Get()
if nErr != nil {
return nil, model.NewAppError("GetWarnMetricsStatus", "app.system.get.app_error", nil, "", http.StatusInternalServerError).Wrap(nErr)
}
isE0Edition := model.BuildEnterpriseReady == "true" // license == nil was already validated upstream
result := map[string]*model.WarnMetricStatus{}
for key, value := range systemDataList {
if strings.HasPrefix(key, model.WarnMetricStatusStorePrefix) {
if warnMetric, ok := model.WarnMetricsTable[key]; ok {
if !warnMetric.IsBotOnly && (value == model.WarnMetricStatusRunonce || value == model.WarnMetricStatusLimitReached) {
result[key], _ = a.getWarnMetricStatusAndDisplayTextsForId(key, nil, isE0Edition)
}
}
}
}
return result, nil
}
func (a *App) getWarnMetricStatusAndDisplayTextsForId(warnMetricId string, T i18n.TranslateFunc, isE0Edition bool) (*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.BotSuccessMessage = T("api.server.warn_metric.bot_response.notification_success.message")
switch warnMetricId {
case model.SystemWarnMetricNumberOfTeams5:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_teams_5.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_teams_5.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_teams_5.start_trial_notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_teams_5.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_teams_5.notification_body")
}
case model.SystemWarnMetricMfa:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.mfa.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.mfa.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.mfa.start_trial_notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.mfa.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.mfa.notification_body")
}
case model.SystemWarnMetricEmailDomain:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.email_domain.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.email_domain.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.email_domain.start_trial_notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.email_domain.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.email_domain.notification_body")
}
case model.SystemWarnMetricNumberOfChannels50:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_channels_50.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_channels_50.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_channels_50.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_channels_50.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_channels_50.notification_body")
}
case model.SystemWarnMetricNumberOfActiveUsers100:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_100.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_100.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_active_users_100.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_active_users_100.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_100.notification_body")
}
case model.SystemWarnMetricNumberOfActiveUsers200:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_200.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_200.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_active_users_200.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_active_users_200.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_200.notification_body")
}
case model.SystemWarnMetricNumberOfActiveUsers300:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_300.start_trial.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_300.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_active_users_300.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_active_users_300.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_300.notification_body")
}
case model.SystemWarnMetricNumberOfActiveUsers500:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_active_users_500.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_500.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_active_users_500.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_active_users_500.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_active_users_500.notification_body")
}
case model.SystemWarnMetricNumberOfPosts2m:
warnMetricDisplayTexts.BotTitle = T("api.server.warn_metric.number_of_posts_2M.notification_title")
if isE0Edition {
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_posts_2M.start_trial.notification_body")
warnMetricDisplayTexts.BotSuccessMessage = T("api.server.warn_metric.number_of_posts_2M.start_trial.notification_success.message")
} else {
warnMetricDisplayTexts.EmailBody = T("api.server.warn_metric.number_of_posts_2M.contact_us.email_body")
warnMetricDisplayTexts.BotMessageBody = T("api.server.warn_metric.number_of_posts_2M.notification_body")
}
default:
mlog.Debug("Invalid metric id", mlog.String("id", warnMetricId))
return nil, nil
}
return warnMetricStatus, warnMetricDisplayTexts
}
return nil, nil
}
func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError {
if warnMetric, ok := model.WarnMetricsTable[warnMetricId]; ok {
data, nErr := a.Srv().Store.System().GetByName(warnMetric.Id)
if nErr == nil && data != nil && data.Value == model.WarnMetricStatusAck {
mlog.Debug("This metric warning has already been acknowledged", mlog.String("id", warnMetric.Id))
return nil
}
if !forceAck {
if *a.Config().EmailSettings.SMTPServer == "" {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.missing_server.app_error", nil, i18n.T("api.context.invalid_param.app_error", map[string]any{"Name": "SMTPServer"}), http.StatusInternalServerError)
}
T := i18n.GetUserTranslations(sender.Locale)
data := a.Srv().EmailService.NewEmailTemplateData(sender.Locale)
data.Props["ContactNameHeader"] = T("api.templates.warn_metric_ack.body.contact_name_header")
data.Props["ContactNameValue"] = sender.GetFullName()
data.Props["ContactEmailHeader"] = T("api.templates.warn_metric_ack.body.contact_email_header")
data.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.Warn("Error retrieving the number of registered users", mlog.Err(cerr))
} else {
data.Props["RegisteredUsersHeader"] = T("api.templates.warn_metric_ack.body.registered_users_header")
data.Props["RegisteredUsersValue"] = registeredUsersCount
}
data.Props["SiteURLHeader"] = T("api.templates.warn_metric_ack.body.site_url_header")
data.Props["SiteURL"] = a.GetSiteURL()
data.Props["TelemetryIdHeader"] = T("api.templates.warn_metric_ack.body.diagnostic_id_header")
data.Props["TelemetryIdValue"] = a.TelemetryId()
data.Props["Footer"] = T("api.templates.warn_metric_ack.footer")
warnMetricStatus, warnMetricDisplayTexts := a.getWarnMetricStatusAndDisplayTextsForId(warnMetricId, T, false)
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")
data.Props["Title"] = warnMetricDisplayTexts.EmailBody
mailConfig := a.Srv().MailServiceConfig()
body, err := a.Srv().TemplatesContainer().RenderToString("warn_metric_ack", data)
if err != nil {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError)
}
if err := mail.SendMailUsingConfig(model.MmSupportAdvisorAddress, subject, body, mailConfig, false, "", "", "", sender.Email); err != nil {
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]any{"Error": err.Error()}, "", http.StatusInternalServerError)
}
}
if err := a.setWarnMetricsStatusAndNotify(warnMetric.Id); err != nil {
return err
}
}
return nil
}
func (a *App) setWarnMetricsStatusAndNotify(warnMetricId string) *model.AppError {
// Ack all metric warnings on the server
if err := a.setWarnMetricsStatus(model.WarnMetricStatusAck); err != nil {
return err
}
// Inform client that this metric warning has been acked
message := model.NewWebSocketEvent(model.WebsocketWarnMetricStatusRemoved, "", "", "", nil, "")
message.Add("warnMetricId", warnMetricId)
a.Publish(message)
return nil
}
func (a *App) setWarnMetricsStatus(status string) *model.AppError {
mlog.Debug("Set monitoring status for all warn metrics", mlog.String("status", status))
for _, warnMetric := range model.WarnMetricsTable {
if err := a.setWarnMetricsStatusForId(warnMetric.Id, status); err != nil {
return err
}
}
return nil
}
func (a *App) setWarnMetricsStatusForId(warnMetricId string, status string) *model.AppError {
mlog.Debug("Store status for warn metric", mlog.String("warnMetricId", warnMetricId), mlog.String("status", status))
if err := a.Srv().Store.System().SaveOrUpdateWithWarnMetricHandling(&model.System{
Name: warnMetricId,
Value: status,
}); err != nil {
return model.NewAppError("setWarnMetricsStatusForId", "app.system.warn_metric.store.app_error", map[string]any{"WarnMetricName": warnMetricId}, "", http.StatusInternalServerError).Wrap(err)
}
return nil
}
func (a *App) RequestLicenseAndAckWarnMetric(c *request.Context, warnMetricId string, isBot bool) *model.AppError {
if *a.Config().ExperimentalSettings.RestrictSystemAdmin {
return model.NewAppError("RequestLicenseAndAckWarnMetric", "api.restricted_system_admin", nil, "", http.StatusForbidden)
}
currentUser, appErr := a.GetUser(c.Session().UserId)
if appErr != nil {
return appErr
}
registeredUsersCount, err := a.Srv().Store.User().Count(model.UserCountOptions{})
if err != nil {
return model.NewAppError("RequestLicenseAndAckWarnMetric", "api.license.request_trial_license.fail_get_user_count.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
if err := a.Channels().RequestTrialLicense(c.Session().UserId, int(registeredUsersCount), true, true); err != nil {
// turn off warn metric warning even in case of StartTrial failure
if nerr := a.setWarnMetricsStatusAndNotify(warnMetricId); nerr != nil {
return nerr
}
return err
}
if appErr = a.NotifyAndSetWarnMetricAck(warnMetricId, currentUser, true, isBot); appErr != nil {
return appErr
}
return nil
}