Merge branch 'master' of github.com:mattermost/mattermost-server into top-dms-clean
Этот коммит содержится в:
11
app/admin.go
11
app/admin.go
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
@@ -29,7 +28,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
||||
var lines []string
|
||||
|
||||
license := s.License()
|
||||
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.Config().ClusterSettings.Enable {
|
||||
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
|
||||
if info := s.Cluster.GetMyClusterInfo(); info != nil {
|
||||
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
||||
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
||||
@@ -48,7 +47,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
||||
|
||||
lines = append(lines, melines...)
|
||||
|
||||
if s.Cluster != nil && *s.Config().ClusterSettings.Enable {
|
||||
if s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
|
||||
clines, err := s.Cluster.GetLogs(page, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,9 +66,9 @@ func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
||||
func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
|
||||
var lines []string
|
||||
|
||||
if *s.Config().LogSettings.EnableFile {
|
||||
if *s.platform.Config().LogSettings.EnableFile {
|
||||
s.Log.Flush()
|
||||
logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
|
||||
logFile := config.GetLogFileLocation(*s.platform.Config().LogSettings.FileLocation)
|
||||
file, err := os.Open(logFile)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
@@ -261,7 +260,7 @@ func (a *App) GetLatestVersion(latestVersionUrl string) (*model.GithubReleaseInf
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
responseData, err := ioutil.ReadAll(res.Body)
|
||||
responseData, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetLatestVersion", "app.admin.latest_version_read_all.failure", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -283,6 +283,8 @@ type AppIface interface {
|
||||
// PromoteGuestToUser Convert user's roles and all his membership's roles from
|
||||
// guest roles to regular user roles.
|
||||
PromoteGuestToUser(c *request.Context, user *model.User, requestorId string) *model.AppError
|
||||
// Removes a listener function by the unique ID returned when AddConfigListener was called
|
||||
RemoveConfigListener(id string)
|
||||
// RenameChannel is used to rename the channel Name and the DisplayName fields
|
||||
RenameChannel(c request.CTX, channel *model.Channel, newChannelName string, newDisplayName string) (*model.Channel, *model.AppError)
|
||||
// RenameTeam is used to rename the team Name and the DisplayName fields
|
||||
@@ -942,7 +944,6 @@ type AppIface interface {
|
||||
ReloadConfig() error
|
||||
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
|
||||
RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *model.AppError
|
||||
RemoveConfigListener(id string)
|
||||
RemoveCustomStatus(c request.CTX, userID string) *model.AppError
|
||||
RemoveDirectory(path string) *model.AppError
|
||||
RemoveFile(path string) *model.AppError
|
||||
|
||||
@@ -109,10 +109,10 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
|
||||
adt.OnError = s.onAuditError
|
||||
|
||||
var logConfigSrc config.LogConfigSrc
|
||||
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
||||
dsn := *s.platform.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
||||
if bAllowAdvancedLogging && dsn != "" {
|
||||
var err error
|
||||
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore.Store)
|
||||
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.platform.GetConfigStore())
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config source for audit, %w", err)
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
|
||||
}
|
||||
|
||||
// ExperimentalAuditSettings provides basic file audit (E0, E10); logConfigSrc provides advanced config (E20).
|
||||
cfg, err := config.MloggerConfigFromAuditConfig(s.Config().ExperimentalAuditSettings, logConfigSrc)
|
||||
cfg, err := config.MloggerConfigFromAuditConfig(s.platform.Config().ExperimentalAuditSettings, logConfigSrc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config for audit, %w", err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -133,7 +133,7 @@ func TestSessionHasPermissionToGroup(t *testing.T) {
|
||||
require.NoError(t, e)
|
||||
defer file.Close()
|
||||
|
||||
b, e := ioutil.ReadAll(file)
|
||||
b, e := io.ReadAll(file)
|
||||
require.NoError(t, e)
|
||||
|
||||
r := csv.NewReader(strings.NewReader(string(b)))
|
||||
|
||||
116
app/channel.go
116
app/channel.go
@@ -641,11 +641,11 @@ func (a *App) UpdateChannel(c request.CTX, channel *model.Channel) (*model.Chann
|
||||
var invErr *store.ErrInvalidInput
|
||||
switch {
|
||||
case errors.As(err, &invErr):
|
||||
return nil, model.NewAppError("UpdateChannel", "app.channel.update.bad_id", nil, invErr.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("UpdateChannel", "app.channel.update.bad_id", nil, "", http.StatusBadRequest).Wrap(invErr)
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
default:
|
||||
return nil, model.NewAppError("UpdateChannel", "app.channel.update_channel.internal_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateChannel", "app.channel.update_channel.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,9 +1267,9 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("updateMemberNotifyProps", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
|
||||
return nil, model.NewAppError("updateMemberNotifyProps", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return nil, model.NewAppError("updateMemberNotifyProps", "app.channel.get_member.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("updateMemberNotifyProps", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1289,17 +1289,17 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
|
||||
}
|
||||
|
||||
func (a *App) updateChannelMember(c request.CTX, member *model.ChannelMember) (*model.ChannelMember, *model.AppError) {
|
||||
member, nErr := a.Srv().Store.Channel().UpdateMember(member)
|
||||
if nErr != nil {
|
||||
member, err := a.Srv().Store.Channel().UpdateMember(member)
|
||||
if err != nil {
|
||||
var appErr *model.AppError
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(nErr, &appErr):
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(nErr, &nfErr):
|
||||
return nil, model.NewAppError("updateChannelMember", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("updateChannelMember", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return nil, model.NewAppError("updateChannelMember", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("updateChannelMember", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2604,14 +2604,14 @@ func (a *App) MarkChannelAsUnreadFromPost(c request.CTX, postID string, userID s
|
||||
}
|
||||
|
||||
func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID string, userID string) (*model.ChannelUnreadAt, *model.AppError) {
|
||||
post, err := a.GetSinglePost(postID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
post, appErr := a.GetSinglePost(postID, false)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
user, err := a.GetUser(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
user, appErr := a.GetUser(userID)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
threadId := post.RootId
|
||||
@@ -2619,18 +2619,18 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
threadId = post.Id
|
||||
}
|
||||
|
||||
unreadMentions, unreadMentionsRoot, err := a.countMentionsFromPost(c, user, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
unreadMentions, unreadMentionsRoot, appErr := a.countMentionsFromPost(c, user, post)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
// if root post,
|
||||
// In CRT Supported Client: badge on channel only sums mentions in root posts including and below the post that was marked.
|
||||
// In CRT Unsupported Client: badge on channel sums mentions in all posts (root & replies) including and below the post that was marked unread.
|
||||
if post.RootId == "" {
|
||||
channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
|
||||
if nErr != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
channelUnread, err := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
|
||||
if err != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
a.sendWebSocketPostUnreadEvent(c, channelUnread, postID, true)
|
||||
@@ -2643,21 +2643,21 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
// If there are replies with mentions below the marked reply in the thread, then sum the mentions for the threads mention badge.
|
||||
// In CRT Unsupported Client: Channel is marked as unread and new messages line inserted above the marked post.
|
||||
// Badge on channel sums mentions in all posts (root & replies) including and below the post that was marked unread.
|
||||
rootPost, err := a.GetSinglePost(post.RootId, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
rootPost, appErr := a.GetSinglePost(post.RootId, false)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
channel, nErr := a.Srv().Store.Channel().Get(post.ChannelId, true)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
channel, err := a.Srv().Store.Channel().Get(post.ChannelId, true)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
if *a.Config().ServiceSettings.ThreadAutoFollow {
|
||||
threadMembership, sErr := a.Srv().Store.Thread().GetMembershipForUser(user.Id, threadId)
|
||||
threadMembership, mErr := a.Srv().Store.Thread().GetMembershipForUser(user.Id, threadId)
|
||||
var errNotFound *store.ErrNotFound
|
||||
if sErr != nil && !errors.As(sErr, &errNotFound) {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
|
||||
if mErr != nil && !errors.As(mErr, &errNotFound) {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
// Follow thread if we're not already following it
|
||||
if threadMembership == nil {
|
||||
@@ -2668,25 +2668,25 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
UpdateViewedTimestamp: false,
|
||||
UpdateParticipants: false,
|
||||
}
|
||||
threadMembership, sErr = a.Srv().Store.Thread().MaintainMembership(user.Id, threadId, opts)
|
||||
if sErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
|
||||
threadMembership, mErr = a.Srv().Store.Thread().MaintainMembership(user.Id, threadId, opts)
|
||||
if mErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
}
|
||||
// If threadmembership already exists but user had previously unfollowed the thread, then follow the thread again.
|
||||
threadMembership.Following = true
|
||||
threadMembership.LastViewed = post.CreateAt - 1
|
||||
threadMembership.UnreadMentions, err = a.countThreadMentions(c, user, rootPost, channel.TeamId, post.CreateAt-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
threadMembership.UnreadMentions, appErr = a.countThreadMentions(c, user, rootPost, channel.TeamId, post.CreateAt-1)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
threadMembership, sErr = a.Srv().Store.Thread().UpdateMembership(threadMembership)
|
||||
if sErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
|
||||
threadMembership, mErr = a.Srv().Store.Thread().UpdateMembership(threadMembership)
|
||||
if mErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
thread, sErr := a.Srv().Store.Thread().GetThreadForUser(channel.TeamId, threadMembership, true)
|
||||
if sErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
|
||||
thread, mErr := a.Srv().Store.Thread().GetThreadForUser(channel.TeamId, threadMembership, true)
|
||||
if mErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
|
||||
}
|
||||
a.sanitizeProfiles(thread.Participants, false)
|
||||
thread.Post.SanitizeProps()
|
||||
@@ -2702,9 +2702,9 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
|
||||
}
|
||||
}
|
||||
|
||||
channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, false)
|
||||
if nErr != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
channelUnread, err := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, false)
|
||||
if err != nil {
|
||||
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
a.sendWebSocketPostUnreadEvent(c, channelUnread, postID, false)
|
||||
a.UpdateMobileAppBadge(userID)
|
||||
@@ -3213,14 +3213,14 @@ func (a *App) ToggleMuteChannel(c request.CTX, channelID, userID string) (*model
|
||||
}
|
||||
|
||||
func (a *App) setChannelsMuted(c request.CTX, channelIDs []string, userID string, muted bool) ([]*model.ChannelMember, *model.AppError) {
|
||||
members, nErr := a.Srv().Store.Channel().GetMembersByChannelIds(channelIDs, userID)
|
||||
if nErr != nil {
|
||||
members, err := a.Srv().Store.Channel().GetMembersByChannelIds(channelIDs, userID)
|
||||
if err != nil {
|
||||
var appErr *model.AppError
|
||||
switch {
|
||||
case errors.As(nErr, &appErr):
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
default:
|
||||
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3240,17 +3240,17 @@ func (a *App) setChannelsMuted(c request.CTX, channelIDs []string, userID string
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
updated, nErr := a.Srv().Store.Channel().UpdateMultipleMembers(membersToUpdate)
|
||||
if nErr != nil {
|
||||
updated, err := a.Srv().Store.Channel().UpdateMultipleMembers(membersToUpdate)
|
||||
if err != nil {
|
||||
var appErr *model.AppError
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(nErr, &appErr):
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(nErr, &nfErr):
|
||||
return nil, model.NewAppError("setChannelsMuted", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("setChannelsMuted", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3375,7 +3375,7 @@ func (a *App) ClearChannelMembersCache(c request.CTX, channelID string) error {
|
||||
return nil
|
||||
}
|
||||
if err := a.forEachChannelMember(c, channelID, clearSessionCache); err != nil {
|
||||
return fmt.Errorf("error clearing cache for channel members: channel_id: %s, error: %v", channelID, err)
|
||||
return fmt.Errorf("error clearing cache for channel members: channel_id: %s, error: %w", channelID, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3383,7 +3383,7 @@ func (a *App) ClearChannelMembersCache(c request.CTX, channelID string) error {
|
||||
func (a *App) GetMemberCountsByGroup(ctx context.Context, channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError) {
|
||||
channelMemberCounts, err := a.Srv().Store.Channel().GetMemberCountsByGroup(ctx, channelID, includeTimezones)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetMemberCountsByGroup", "app.channel.get_member_count.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("GetMemberCountsByGroup", "app.channel.get_member_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return channelMemberCounts, nil
|
||||
|
||||
@@ -144,7 +144,7 @@ func (a *App) UpdateSidebarCategoryOrder(c request.CTX, userID, teamID string, c
|
||||
func (a *App) UpdateSidebarCategories(c request.CTX, userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
|
||||
updatedCategories, originalCategories, err := a.Srv().Store.Channel().UpdateSidebarCategories(userID, teamID, categories)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, teamID, "", userID, nil)
|
||||
|
||||
@@ -2056,7 +2056,7 @@ func TestMarkChannelsAsViewedPanic(t *testing.T) {
|
||||
UserStore: &mockUserStore,
|
||||
SessionStore: &mockSessionStore,
|
||||
OAuthStore: &mockOAuthStore,
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -31,12 +31,6 @@ type licenseSvc interface {
|
||||
RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError
|
||||
}
|
||||
|
||||
// namer is an interface which enforces that
|
||||
// all services can return their names.
|
||||
type namer interface {
|
||||
Name() ServiceKey
|
||||
}
|
||||
|
||||
// Channels contains all channels related state.
|
||||
type Channels struct {
|
||||
srv *Server
|
||||
@@ -107,7 +101,7 @@ func init() {
|
||||
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
||||
ch := &Channels{
|
||||
srv: s,
|
||||
imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log),
|
||||
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log),
|
||||
uploadLockMap: map[string]bool{},
|
||||
}
|
||||
|
||||
@@ -133,10 +127,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
||||
if !ok {
|
||||
return nil, errors.New("Config service did not satisfy ConfigSvc interface")
|
||||
}
|
||||
_, ok = svc.(namer)
|
||||
if !ok {
|
||||
return nil, errors.New("Config service does not contain Name method")
|
||||
}
|
||||
ch.cfgSvc = cfgSvc
|
||||
case FilestoreKey:
|
||||
filestore, ok := svc.(filestore.FileBackend)
|
||||
@@ -149,10 +139,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
||||
if !ok {
|
||||
return nil, errors.New("License service did not satisfy licenseSvc interface")
|
||||
}
|
||||
_, ok = svc.(namer)
|
||||
if !ok {
|
||||
return nil, errors.New("License service does not contain Name method")
|
||||
}
|
||||
ch.licenseSvc = svc
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (cds *ClusterDiscoveryService) Stop() {
|
||||
}
|
||||
|
||||
func (s *Server) IsLeader() bool {
|
||||
if s.License() != nil && *s.Config().ClusterSettings.Enable && s.Cluster != nil {
|
||||
if s.License() != nil && *s.platform.Config().ClusterSettings.Enable && s.Cluster != nil {
|
||||
return s.Cluster.IsLeader()
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -521,7 +520,7 @@ func (a *App) DoCommandRequest(cmd *model.Command, p url.Values) (*model.Command
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil
|
||||
bodyBytes, _ := ioutil.ReadAll(body)
|
||||
bodyBytes, _ := io.ReadAll(body)
|
||||
|
||||
return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]any{"Trigger": cmd.Trigger, "Status": resp.Status}, string(bodyBytes), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ func (a *App) getDynamicListArgument(c *request.Context, commandArgs *model.Comm
|
||||
|
||||
var listItems []model.AutocompleteListItem
|
||||
if jsonErr := json.NewDecoder(resp.Body).Decode(&listItems); jsonErr != nil {
|
||||
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr))
|
||||
c.Logger().Warn("Failed to decode from JSON", mlog.Err(jsonErr))
|
||||
}
|
||||
|
||||
return parseListItems(listItems, parsed, toBeParsed)
|
||||
|
||||
@@ -5,8 +5,8 @@ package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
@@ -75,7 +75,7 @@ func (a *App) GetComplianceReport(reportId string) (*model.Compliance, *model.Ap
|
||||
}
|
||||
|
||||
func (a *App) GetComplianceFile(job *model.Compliance) ([]byte, *model.AppError) {
|
||||
f, err := ioutil.ReadFile(*a.Config().ComplianceSettings.Directory + "compliance/" + job.JobName() + ".zip")
|
||||
f, err := os.ReadFile(*a.Config().ComplianceSettings.Directory + "compliance/" + job.JobName() + ".zip")
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("readFile", "api.file.read_file.reading_local.app_error", nil, err.Error(), http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
128
app/config.go
128
app/config.go
@@ -12,7 +12,6 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/product"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mail"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v6/utils"
|
||||
@@ -32,113 +30,24 @@ const (
|
||||
ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error"
|
||||
)
|
||||
|
||||
// ensure the config wrapper implements `product.ConfigService`
|
||||
var _ product.ConfigService = (*configWrapper)(nil)
|
||||
|
||||
// configWrapper is an adapter struct that only exposes the
|
||||
// config related functionality to be passed down to other products.
|
||||
type configWrapper struct {
|
||||
srv *Server
|
||||
*config.Store
|
||||
}
|
||||
|
||||
func (w *configWrapper) Name() ServiceKey {
|
||||
return ConfigKey
|
||||
}
|
||||
|
||||
func (w *configWrapper) Config() *model.Config {
|
||||
return w.Store.Get()
|
||||
}
|
||||
|
||||
func (w *configWrapper) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
return w.Store.AddListener(listener)
|
||||
}
|
||||
|
||||
func (w *configWrapper) RemoveConfigListener(id string) {
|
||||
w.Store.RemoveListener(id)
|
||||
}
|
||||
|
||||
func (w *configWrapper) UpdateConfig(f func(*model.Config)) {
|
||||
if w.Store.IsReadOnly() {
|
||||
return
|
||||
}
|
||||
old := w.Config()
|
||||
updated := old.Clone()
|
||||
f(updated)
|
||||
if _, _, err := w.Store.Set(updated); err != nil {
|
||||
mlog.Error("Failed to update config", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *configWrapper) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
||||
oldCfg, newCfg, err := w.Store.Set(newCfg)
|
||||
if errors.Cause(err) == config.ErrReadOnlyConfiguration {
|
||||
return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
|
||||
} else if err != nil {
|
||||
return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if w.srv.startMetrics && *w.Config().MetricsSettings.Enable {
|
||||
if w.srv.GetMetrics() != nil {
|
||||
w.srv.GetMetrics().Register()
|
||||
}
|
||||
w.srv.platform.RestartMetrics() // TODO: remove when this moved to the platform service
|
||||
} else {
|
||||
w.srv.platform.ShutdownMetrics() // TODO: remove when this moved to the platform service
|
||||
}
|
||||
|
||||
if w.srv.Cluster != nil {
|
||||
err := w.srv.Cluster.ConfigChanged(w.Store.RemoveEnvironmentOverrides(oldCfg),
|
||||
w.Store.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return oldCfg, newCfg, nil
|
||||
}
|
||||
|
||||
func (w *configWrapper) ReloadConfig() error {
|
||||
if err := w.Store.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Config() *model.Config {
|
||||
return s.configStore.Config()
|
||||
}
|
||||
|
||||
func (s *Server) ConfigStore() *configWrapper {
|
||||
return s.configStore
|
||||
return s.platform.Config()
|
||||
}
|
||||
|
||||
func (a *App) Config() *model.Config {
|
||||
return a.ch.cfgSvc.Config()
|
||||
}
|
||||
|
||||
func (s *Server) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
|
||||
return s.configStore.GetEnvironmentOverridesWithFilter(filter)
|
||||
}
|
||||
|
||||
func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
|
||||
return a.Srv().EnvironmentConfig(filter)
|
||||
}
|
||||
|
||||
func (s *Server) UpdateConfig(f func(*model.Config)) {
|
||||
s.configStore.UpdateConfig(f)
|
||||
return a.Srv().platform.GetEnvironmentOverridesWithFilter(filter)
|
||||
}
|
||||
|
||||
func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||
a.Srv().UpdateConfig(f)
|
||||
}
|
||||
|
||||
func (s *Server) ReloadConfig() error {
|
||||
return s.configStore.ReloadConfig()
|
||||
a.Srv().platform.UpdateConfig(f)
|
||||
}
|
||||
|
||||
func (a *App) ReloadConfig() error {
|
||||
return a.Srv().ReloadConfig()
|
||||
return a.Srv().platform.ReloadConfig()
|
||||
}
|
||||
|
||||
func (a *App) ClientConfig() map[string]string {
|
||||
@@ -153,24 +62,13 @@ func (a *App) LimitedClientConfig() map[string]string {
|
||||
return a.ch.limitedClientConfig.Load().(map[string]string)
|
||||
}
|
||||
|
||||
// Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
|
||||
// will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
|
||||
// for the listener that can later be used to remove it.
|
||||
func (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
return s.configStore.AddConfigListener(listener)
|
||||
}
|
||||
|
||||
func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
return a.Srv().AddConfigListener(listener)
|
||||
return a.Srv().platform.AddConfigListener(listener)
|
||||
}
|
||||
|
||||
// Removes a listener function by the unique ID returned when AddConfigListener was called
|
||||
func (s *Server) RemoveConfigListener(id string) {
|
||||
s.configStore.RemoveConfigListener(id)
|
||||
}
|
||||
|
||||
func (a *App) RemoveConfigListener(id string) {
|
||||
a.Srv().RemoveConfigListener(id)
|
||||
a.Srv().platform.RemoveConfigListener(id)
|
||||
}
|
||||
|
||||
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
|
||||
@@ -449,7 +347,7 @@ func (a *App) LimitedClientConfigWithComputed() map[string]string {
|
||||
|
||||
// GetConfigFile proxies access to the given configuration file to the underlying config store.
|
||||
func (a *App) GetConfigFile(name string) ([]byte, error) {
|
||||
data, err := a.Srv().configStore.GetFile(name)
|
||||
data, err := a.Srv().platform.GetConfigFile(name)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get config file %s", name)
|
||||
}
|
||||
@@ -471,15 +369,9 @@ func (a *App) GetEnvironmentConfig(filter func(reflect.StructField) bool) map[st
|
||||
return a.EnvironmentConfig(filter)
|
||||
}
|
||||
|
||||
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||
// It returns both the previous and current configs.
|
||||
func (s *Server) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
||||
return s.configStore.SaveConfig(newCfg, sendConfigChangeClusterMessage)
|
||||
}
|
||||
|
||||
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
||||
return a.Srv().SaveConfig(newCfg, sendConfigChangeClusterMessage)
|
||||
return a.Srv().platform.SaveConfig(newCfg, sendConfigChangeClusterMessage)
|
||||
}
|
||||
|
||||
func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
|
||||
@@ -499,8 +391,8 @@ func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config)
|
||||
}
|
||||
|
||||
func (s *Server) MailServiceConfig() *mail.SMTPConfig {
|
||||
emailSettings := s.Config().EmailSettings
|
||||
hostname := utils.GetHostnameFromSiteURL(*s.Config().ServiceSettings.SiteURL)
|
||||
emailSettings := s.platform.Config().EmailSettings
|
||||
hostname := utils.GetHostnameFromSiteURL(*s.platform.Config().ServiceSettings.SiteURL)
|
||||
cfg := mail.SMTPConfig{
|
||||
Hostname: hostname,
|
||||
ConnectionSecurity: *emailSettings.ConnectionSecurity,
|
||||
|
||||
@@ -16,41 +16,6 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/utils"
|
||||
)
|
||||
|
||||
func TestConfigListener(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
originalSiteName := th.App.Config().TeamSettings.SiteName
|
||||
|
||||
listenerCalled := false
|
||||
listener := func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
assert.False(t, listenerCalled, "listener called twice")
|
||||
|
||||
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
|
||||
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
|
||||
|
||||
listenerCalled = true
|
||||
}
|
||||
listenerId := th.App.AddConfigListener(listener)
|
||||
defer th.App.RemoveConfigListener(listenerId)
|
||||
|
||||
listener2Called := false
|
||||
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
assert.False(t, listener2Called, "listener2 called twice")
|
||||
|
||||
listener2Called = true
|
||||
}
|
||||
listener2Id := th.App.AddConfigListener(listener2)
|
||||
defer th.App.RemoveConfigListener(listener2Id)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.SiteName = "test123"
|
||||
})
|
||||
|
||||
assert.True(t, listenerCalled, "listener should've been called")
|
||||
assert.True(t, listener2Called, "listener 2 should've been called")
|
||||
}
|
||||
|
||||
func TestAsymmetricSigningKey(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -5,7 +5,6 @@ package app
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -35,7 +34,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to parse url %s", downloadURL)
|
||||
}
|
||||
if !*s.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
|
||||
if !*s.platform.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
|
||||
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
|
||||
}
|
||||
|
||||
@@ -64,5 +63,5 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ package email
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -64,7 +63,7 @@ func SetupWithStoreMock(tb testing.TB) *TestHelper {
|
||||
}
|
||||
|
||||
func setupTestHelper(s store.Store, tb testing.TB) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "userservicetest")
|
||||
tempWorkspace, err := os.MkdirTemp("", "userservicetest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
35
app/emoji.go
35
app/emoji.go
@@ -39,11 +39,11 @@ const (
|
||||
|
||||
func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return nil, model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
if *a.Config().FileSettings.DriverName == "" {
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
// wipe the emoji id so that existing emojis can't get overwritten
|
||||
@@ -52,8 +52,8 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
|
||||
// do our best to validate the emoji before committing anything to the DB so that we don't have to clean up
|
||||
// orphaned files left over when validation fails later on
|
||||
emoji.PreSave()
|
||||
if err := emoji.IsValid(); err != nil {
|
||||
return nil, err
|
||||
if appErr := emoji.IsValid(); appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if emoji.CreatorId != sessionUserId {
|
||||
@@ -61,22 +61,21 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
|
||||
}
|
||||
|
||||
if existingEmoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emoji.Name, true); err == nil && existingEmoji != nil {
|
||||
return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest)
|
||||
return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
imageData := multiPartImageData.File["image"]
|
||||
if len(imageData) == 0 {
|
||||
err := model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]any{"Name": "createEmoji"}, "", http.StatusBadRequest)
|
||||
return nil, err
|
||||
return nil, model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]any{"Name": "createEmoji"}, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if err := a.UploadEmojiImage(emoji.Id, imageData[0]); err != nil {
|
||||
return nil, err
|
||||
if appErr := a.UploadEmojiImage(emoji.Id, imageData[0]); appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
emoji, err := a.Srv().Store.Emoji().Save(emoji)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("CreateEmoji", "app.emoji.create.internal_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("CreateEmoji", "app.emoji.create.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventEmojiAdded, "", "", "", nil)
|
||||
@@ -100,11 +99,11 @@ func (a *App) GetEmojiList(page, perPage int, sort string) ([]*model.Emoji, *mod
|
||||
|
||||
func (a *App) UploadEmojiImage(id string, imageData *multipart.FileHeader) *model.AppError {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
if *a.Config().FileSettings.DriverName == "" {
|
||||
return model.NewAppError("UploadEmojiImage", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return model.NewAppError("UploadEmojiImage", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
file, err := imageData.Open()
|
||||
@@ -185,11 +184,11 @@ func (a *App) DeleteEmoji(emoji *model.Emoji) *model.AppError {
|
||||
|
||||
func (a *App) GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
if *a.Config().FileSettings.DriverName == "" {
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
emoji, err := a.Srv().Store.Emoji().Get(context.Background(), emojiId, true)
|
||||
@@ -208,11 +207,11 @@ func (a *App) GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
|
||||
|
||||
func (a *App) GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return nil, model.NewAppError("GetEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
if *a.Config().FileSettings.DriverName == "" {
|
||||
return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
emoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emojiName, true)
|
||||
@@ -231,7 +230,7 @@ func (a *App) GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) {
|
||||
|
||||
func (a *App) GetMultipleEmojiByName(names []string) ([]*model.Emoji, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return nil, model.NewAppError("GetMultipleEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("GetMultipleEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
emoji, err := a.Srv().Store.Emoji().GetMultipleByName(names)
|
||||
@@ -269,7 +268,7 @@ func (a *App) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) {
|
||||
|
||||
func (a *App) SearchEmoji(name string, prefixOnly bool, limit int) ([]*model.Emoji, *model.AppError) {
|
||||
if !*a.Config().ServiceSettings.EnableCustomEmoji {
|
||||
return nil, model.NewAppError("SearchEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
return nil, model.NewAppError("SearchEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
list, err := a.Srv().Store.Emoji().Search(name, prefixOnly, limit)
|
||||
|
||||
@@ -87,9 +87,9 @@ func RegisterCloudInterface(f func(*Server) einterfaces.CloudInterface) {
|
||||
cloudInterface = f
|
||||
}
|
||||
|
||||
var metricsInterface func(*Server) einterfaces.MetricsInterface
|
||||
var metricsInterface func(*Server, string, string) einterfaces.MetricsInterface
|
||||
|
||||
func RegisterMetricsInterface(f func(*Server) einterfaces.MetricsInterface) {
|
||||
func RegisterMetricsInterface(f func(*Server, string, string) einterfaces.MetricsInterface) {
|
||||
metricsInterface = f
|
||||
}
|
||||
|
||||
|
||||
@@ -141,14 +141,14 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, opts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) exportWriteLine(writer io.Writer, line *LineImportData) *model.AppError {
|
||||
func (a *App) exportWriteLine(w io.Writer, line *LineImportData) *model.AppError {
|
||||
b, err := json.Marshal(line)
|
||||
if err != nil {
|
||||
return model.NewAppError("BulkExport", "app.export.export_write_line.json_marshall.error", nil, "err="+err.Error(), http.StatusBadRequest)
|
||||
return model.NewAppError("BulkExport", "app.export.export_write_line.json_marshall.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
if _, err := writer.Write(append(b, '\n')); err != nil {
|
||||
return model.NewAppError("BulkExport", "app.export.export_write_line.io_writer.error", nil, "err="+err.Error(), http.StatusBadRequest)
|
||||
if _, err := w.Write(append(b, '\n')); err != nil {
|
||||
return model.NewAppError("BulkExport", "app.export.export_write_line.io_writer.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -590,7 +589,7 @@ func TestBulkExport(t *testing.T) {
|
||||
th := Setup(t)
|
||||
testsDir, _ := fileutils.FindDir("tests")
|
||||
|
||||
dir, err := ioutil.TempDir("", "import_test")
|
||||
dir, err := os.MkdirTemp("", "import_test")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -81,7 +80,7 @@ func TestExtractTarGz(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
dst, err := ioutil.TempDir("", "TestExtractTarGz")
|
||||
dst, err := os.MkdirTemp("", "TestExtractTarGz")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dst)
|
||||
|
||||
@@ -175,7 +174,7 @@ func TestExtractTarGz(t *testing.T) {
|
||||
|
||||
for i, testCase := range testCases {
|
||||
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
|
||||
dst, err := ioutil.TempDir("", "TestExtractTarGz")
|
||||
dst, err := os.MkdirTemp("", "TestExtractTarGz")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dst)
|
||||
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/featureflag"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
// setupFeatureFlags called on startup and when the cluster leader changes.
|
||||
// Starts or stops the synchronization of feature flags from upstream management.
|
||||
func (s *Server) setupFeatureFlags() {
|
||||
s.featureFlagSynchronizerMutex.Lock()
|
||||
defer s.featureFlagSynchronizerMutex.Unlock()
|
||||
splitKey := *s.Config().ServiceSettings.SplitKey
|
||||
splitConfigured := splitKey != ""
|
||||
syncFeatureFlags := splitConfigured && s.IsLeader()
|
||||
|
||||
s.configStore.SetReadOnlyFF(!splitConfigured)
|
||||
|
||||
if syncFeatureFlags {
|
||||
if err := s.startFeatureFlagUpdateJob(); err != nil {
|
||||
s.Log.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
|
||||
}
|
||||
} else {
|
||||
s.stopFeatureFlagUpdateJob()
|
||||
}
|
||||
|
||||
if err := s.configStore.Load(); err != nil {
|
||||
s.Log.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) updateFeatureFlagValuesFromManagement() {
|
||||
newCfg := s.configStore.GetNoEnv().Clone()
|
||||
oldFlags := *newCfg.FeatureFlags
|
||||
newFlags := s.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
|
||||
oldFlagsBytes, _ := json.Marshal(oldFlags)
|
||||
newFlagsBytes, _ := json.Marshal(newFlags)
|
||||
s.Log.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
|
||||
if oldFlags != newFlags {
|
||||
s.Log.Debug("Feature flag change detected, updating config")
|
||||
*newCfg.FeatureFlags = newFlags
|
||||
s.SaveConfig(newCfg, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) startFeatureFlagUpdateJob() error {
|
||||
// Can be run multiple times
|
||||
if s.featureFlagSynchronizer != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var log *mlog.Logger
|
||||
if *s.Config().ServiceSettings.DebugSplit {
|
||||
log = s.Log
|
||||
}
|
||||
|
||||
attributes := map[string]any{}
|
||||
|
||||
// if we are part of a cloud installation, add its installation and group id
|
||||
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
|
||||
attributes["installation_id"] = installationId
|
||||
}
|
||||
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
|
||||
attributes["group_id"] = groupId
|
||||
}
|
||||
|
||||
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
|
||||
ServerID: s.TelemetryId(),
|
||||
SplitKey: *s.Config().ServiceSettings.SplitKey,
|
||||
Log: log,
|
||||
Attributes: attributes,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.featureFlagStop = make(chan struct{})
|
||||
s.featureFlagStopped = make(chan struct{})
|
||||
s.featureFlagSynchronizer = synchronizer
|
||||
syncInterval := *s.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
defer close(s.featureFlagStopped)
|
||||
if err := synchronizer.EnsureReady(); err != nil {
|
||||
s.Log.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
s.updateFeatureFlagValuesFromManagement()
|
||||
for {
|
||||
select {
|
||||
case <-s.featureFlagStop:
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.updateFeatureFlagValuesFromManagement()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) stopFeatureFlagUpdateJob() {
|
||||
if s.featureFlagSynchronizer != nil {
|
||||
close(s.featureFlagStop)
|
||||
<-s.featureFlagStopped
|
||||
s.featureFlagSynchronizer.Close()
|
||||
s.featureFlagSynchronizer = nil
|
||||
}
|
||||
}
|
||||
61
app/group.go
61
app/group.go
@@ -122,9 +122,9 @@ func (a *App) isUniqueToUsernames(val string) *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Group, *model.AppError) {
|
||||
if err := a.isUniqueToUsernames(group.GetName()); err != nil {
|
||||
err.Where = "CreateGroupWithUserIds"
|
||||
return nil, err
|
||||
if appErr := a.isUniqueToUsernames(group.GetName()); appErr != nil {
|
||||
appErr.Where = "CreateGroupWithUserIds"
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
newGroup, err := a.Srv().Store.Group().CreateWithUserIds(group)
|
||||
@@ -136,18 +136,18 @@ func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Grou
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(err, &invErr):
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, invErr.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(invErr)
|
||||
case errors.As(err, &dupKey):
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.custom_group.unique_name", nil, dupKey.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.custom_group.unique_name", nil, "", http.StatusBadRequest).Wrap(dupKey)
|
||||
default:
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.insert_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.insert_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
|
||||
count, err := a.Srv().Store.Group().GetMemberCount(newGroup.Id)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
group.MemberCount = model.NewInt(int(count))
|
||||
groupJSON, jsonErr := json.Marshal(newGroup)
|
||||
@@ -161,28 +161,12 @@ func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Grou
|
||||
}
|
||||
|
||||
func (a *App) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) {
|
||||
if err := a.isUniqueToUsernames(group.GetName()); err != nil {
|
||||
err.Where = "UpdateGroup"
|
||||
return nil, err
|
||||
if appErr := a.isUniqueToUsernames(group.GetName()); appErr != nil {
|
||||
appErr.Where = "UpdateGroup"
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
updatedGroup, err := a.Srv().Store.Group().Update(group)
|
||||
|
||||
if err == nil {
|
||||
count, countErr := a.Srv().Store.Group().GetMemberCount(updatedGroup.Id)
|
||||
if countErr != nil {
|
||||
return nil, model.NewAppError("UpdateGroup", "app.group.id.app_error", nil, countErr.Error(), http.StatusBadRequest)
|
||||
}
|
||||
updatedGroup.MemberCount = model.NewInt(int(count))
|
||||
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
|
||||
groupJSON, jsonErr := json.Marshal(updatedGroup)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("UpdateGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
messageWs.Add("group", string(groupJSON))
|
||||
a.Publish(messageWs)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
var appErr *model.AppError
|
||||
@@ -191,14 +175,29 @@ func (a *App) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) {
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("UpdateGroup", "app.group.no_rows", nil, nfErr.Error(), http.StatusNotFound)
|
||||
return nil, model.NewAppError("UpdateGroup", "app.group.no_rows", nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
case errors.As(err, &dupKey):
|
||||
return nil, model.NewAppError("CreateGroup", "app.custom_group.unique_name", nil, dupKey.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("CreateGroup", "app.custom_group.unique_name", nil, "", http.StatusBadRequest).Wrap(dupKey)
|
||||
default:
|
||||
return nil, model.NewAppError("UpdateGroup", "app.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateGroup", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
count, err := a.Srv().Store.Group().GetMemberCount(updatedGroup.Id)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("UpdateGroup", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
updatedGroup.MemberCount = model.NewInt(int(count))
|
||||
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
|
||||
|
||||
groupJSON, err := json.Marshal(updatedGroup)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("UpdateGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
messageWs.Add("group", string(groupJSON))
|
||||
a.Publish(messageWs)
|
||||
|
||||
return updatedGroup, nil
|
||||
}
|
||||
|
||||
@@ -763,9 +762,9 @@ func (a *App) DeleteGroupMembers(groupID string, userIDs []string) ([]*model.Gro
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
case errors.As(err, &invErr):
|
||||
return nil, model.NewAppError("DeleteGroupMember", "app.group.uniqueness_error", nil, invErr.Error(), http.StatusBadRequest)
|
||||
return nil, model.NewAppError("DeleteGroupMember", "app.group.uniqueness_error", nil, "", http.StatusBadRequest).Wrap(invErr)
|
||||
default:
|
||||
return nil, model.NewAppError("DeleteGroupMember", "app.update_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("DeleteGroupMember", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -48,7 +47,7 @@ type TestHelper struct {
|
||||
}
|
||||
|
||||
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, options []Option, tb testing.TB) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "apptest")
|
||||
tempWorkspace, err := os.MkdirTemp("", "apptest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package imaging
|
||||
import (
|
||||
"bytes"
|
||||
"image/color"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -77,7 +76,7 @@ func TestFillImageTransparency(t *testing.T) {
|
||||
require.NotNil(t, inputImg)
|
||||
require.Equal(t, "png", format)
|
||||
|
||||
expectedBytes, err := ioutil.ReadFile(imgDir + "/" + tc.outputName)
|
||||
expectedBytes, err := os.ReadFile(imgDir + "/" + tc.outputName)
|
||||
require.NoError(t, err)
|
||||
|
||||
FillImageTransparency(inputImg, tc.fillColor)
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -1217,7 +1216,7 @@ func (a *App) importAttachment(c *request.Context, data *AttachmentImportData, p
|
||||
|
||||
timestamp := utils.TimeFromMillis(post.CreateAt)
|
||||
|
||||
fileData, err := ioutil.ReadAll(file)
|
||||
fileData, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("BulkImport", "app.import.attachment.read_file_data.error", map[string]any{"FilePath": *data.Path}, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"archive/zip"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -3100,7 +3099,6 @@ func TestImportImportPost(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Reply CreateAt before parent post CreateAt", func(t *testing.T) {
|
||||
t.Skip("MM-44922")
|
||||
now := model.GetMillis()
|
||||
before := now - 10
|
||||
data := LineImportWorkerData{
|
||||
@@ -3128,6 +3126,7 @@ func TestImportImportPost(t *testing.T) {
|
||||
posts, nErr := th.App.Srv().Store.Post().GetPostsCreatedAt(channel.Id, now)
|
||||
require.NoError(t, nErr)
|
||||
require.Len(t, posts, 2, "Unexpected number of posts found.")
|
||||
require.NoError(t, th.TestLogger.Flush())
|
||||
testlib.AssertLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Reply CreateAt is before parent post CreateAt, setting it to parent post CreateAt")
|
||||
|
||||
rootPost := posts[0]
|
||||
@@ -4378,11 +4377,11 @@ func TestImportDirectPostWithAttachments(t *testing.T) {
|
||||
testImage := filepath.Join(testsDir, "test.png")
|
||||
testImage2 := filepath.Join(testsDir, "test.svg")
|
||||
// create a temp file with same name as original but with a different first byte
|
||||
tmpFolder, _ := ioutil.TempDir("", "imgFake")
|
||||
tmpFolder, _ := os.MkdirTemp("", "imgFake")
|
||||
testImageFake := filepath.Join(tmpFolder, "test.png")
|
||||
fakeFileData, _ := ioutil.ReadFile(testImage)
|
||||
fakeFileData, _ := os.ReadFile(testImage)
|
||||
fakeFileData[0] = 0
|
||||
_ = ioutil.WriteFile(testImageFake, fakeFileData, 0644)
|
||||
_ = os.WriteFile(testImageFake, fakeFileData, 0644)
|
||||
defer os.RemoveAll(tmpFolder)
|
||||
|
||||
// Create a user.
|
||||
|
||||
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -439,7 +438,7 @@ func BenchmarkBulkImport(b *testing.B) {
|
||||
info, err := importFile.Stat()
|
||||
require.NoError(b, err)
|
||||
|
||||
dir, err := ioutil.TempDir("", "testimport")
|
||||
dir, err := os.MkdirTemp("", "testimport")
|
||||
require.NoError(b, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
|
||||
@@ -332,10 +332,10 @@ func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError {
|
||||
}
|
||||
}
|
||||
|
||||
if tdata.Theme != nil && 0 < len(strings.Trim(*tdata.Theme, " \t\r")) {
|
||||
if tdata.Theme != nil && strings.Trim(*tdata.Theme, " \t\r") != "" {
|
||||
var unused map[string]string
|
||||
if err := json.NewDecoder(strings.NewReader(*tdata.Theme)).Decode(&unused); err != nil {
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_team_theme.error", nil, err.Error(), http.StatusBadRequest)
|
||||
return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_team_theme.error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
@@ -98,9 +98,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(result.NErr, &nfErr):
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, nfErr.Error(), http.StatusNotFound)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
|
||||
}
|
||||
}
|
||||
if cookie.Integration == nil {
|
||||
@@ -116,9 +116,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", nil, nfErr.Error(), http.StatusNotFound)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
post := result.Data.(*model.Post)
|
||||
result = <-cchan
|
||||
if result.NErr != nil {
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get_for_post.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get_for_post.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
|
||||
}
|
||||
channel := result.Data.(*model.Channel)
|
||||
|
||||
@@ -195,9 +195,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(ur.NErr, &nfErr):
|
||||
return "", model.NewAppError("DoPostActionWithCookie", MissingAccountError, nil, nfErr.Error(), http.StatusNotFound)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", MissingAccountError, nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.user.get.app_error", nil, ur.NErr.Error(), http.StatusInternalServerError)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(ur.NErr)
|
||||
}
|
||||
}
|
||||
user := ur.Data.(*model.User)
|
||||
@@ -209,9 +209,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(tr.NErr, &nfErr):
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.find.app_error", nil, nfErr.Error(), http.StatusNotFound)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.find.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.finding.app_error", nil, tr.NErr.Error(), http.StatusInternalServerError)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.finding.app_error", nil, "", http.StatusInternalServerError).Wrap(tr.NErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,6 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
return "", appErr
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
if strings.HasPrefix(upstreamURL, "/warn_metrics/") {
|
||||
appErr = a.doLocalWarnMetricsRequest(c, upstreamURL, upstreamRequest)
|
||||
if appErr != nil {
|
||||
@@ -242,25 +241,26 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
requestJSON, jsonErr := json.Marshal(upstreamRequest)
|
||||
if jsonErr != nil {
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
|
||||
requestJSON, err := json.Marshal(upstreamRequest)
|
||||
if err != nil {
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
resp, appErr = a.DoActionRequest(c, upstreamURL, requestJSON)
|
||||
resp, appErr := a.DoActionRequest(c, upstreamURL, requestJSON)
|
||||
if appErr != nil {
|
||||
return "", appErr
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var response model.PostActionIntegrationResponse
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
if len(respBytes) > 0 {
|
||||
if err = json.Unmarshal(respBytes, &response); err != nil {
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest)
|
||||
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, v
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: w.headers,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(w.data)),
|
||||
Body: io.NopCloser(bytes.NewReader(w.data)),
|
||||
}
|
||||
if resp.StatusCode == 0 {
|
||||
resp.StatusCode = http.StatusOK
|
||||
@@ -585,14 +585,17 @@ func (a *App) DoLocalRequest(c *request.Context, rawURL string, body []byte) (*h
|
||||
}
|
||||
|
||||
func (a *App) OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError {
|
||||
clientTriggerId, userID, err := request.DecodeAndVerifyTriggerId(a.AsymmetricSigningKey())
|
||||
if err != nil {
|
||||
return err
|
||||
clientTriggerId, userID, appErr := request.DecodeAndVerifyTriggerId(a.AsymmetricSigningKey())
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
request.TriggerId = clientTriggerId
|
||||
|
||||
jsonRequest, _ := json.Marshal(request)
|
||||
jsonRequest, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
a.ch.srv.GetLogger().Warn("Error encoding request", mlog.Err(err))
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventOpenDialog, "", "", userID, nil)
|
||||
message.Add("dialog", string(jsonRequest))
|
||||
@@ -606,23 +609,19 @@ func (a *App) SubmitInteractiveDialog(c *request.Context, request model.SubmitDi
|
||||
request.URL = ""
|
||||
request.Type = "dialog_submission"
|
||||
|
||||
b, jsonErr := json.Marshal(request)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("SubmitInteractiveDialog", "app.submit_interactive_dialog.json_error", nil, jsonErr.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
resp, err := a.DoActionRequest(c, url, b)
|
||||
b, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, model.NewAppError("SubmitInteractiveDialog", "app.submit_interactive_dialog.json_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
resp, appErr := a.DoActionRequest(c, url, b)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var response model.SubmitDialogResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||
// Don't fail, an empty response is acceptable
|
||||
return &response, nil
|
||||
}
|
||||
json.NewDecoder(resp.Body).Decode(&response) // Don't fail, an empty response is acceptable
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package app
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -1079,47 +1079,47 @@ func TestDoPluginRequest(t *testing.T) {
|
||||
resp, err := th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "could not find param abc=xyz", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "param multiple should have 3 values", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin",
|
||||
url.Values{"abc": []string{"xyz"}, "multiple": []string{"1 first", "2 second", "3 third"}}, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "OK", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz&multiple=1%20first",
|
||||
url.Values{"multiple": []string{"2 second", "3 third"}}, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "OK", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz&multiple=1%20first&multiple=3%20third",
|
||||
url.Values{"multiple": []string{"2 second"}}, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "OK", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?multiple=1%20first&multiple=3%20third",
|
||||
url.Values{"multiple": []string{"2 second"}, "abc": []string{"xyz"}}, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "OK", string(body))
|
||||
|
||||
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?multiple=1%20first&multiple=3%20third",
|
||||
url.Values{"multiple": []string{"4 fourth"}, "abc": []string{"xyz"}}, nil)
|
||||
assert.Nil(t, err)
|
||||
require.NotNil(t, resp)
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, "param multiple not correct", string(body))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
@@ -58,7 +58,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputFile, formattedCode, 0644)
|
||||
err = os.WriteFile(outputFile, formattedCode, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func extractStoreMetadata() (*storeMetadata, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open %s file: %w", inputFile, err)
|
||||
}
|
||||
src, err := ioutil.ReadAll(file)
|
||||
src, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
|
||||
@@ -186,12 +186,12 @@ func (a *App) writeLdapFile(filename string, fileData *multipart.FileHeader) *mo
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
err = a.Srv().configStore.SetFile(filename, data)
|
||||
err = a.Srv().platform.SetConfigFile(filename, data)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (a *App) AddLdapPrivateCertificate(fileData *multipart.FileHeader) *model.A
|
||||
}
|
||||
|
||||
func (a *App) removeLdapFile(filename string) *model.AppError {
|
||||
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
|
||||
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
|
||||
return model.NewAppError("RemoveLdapFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -48,7 +48,7 @@ func (w *licenseWrapper) GetLicense() *model.License {
|
||||
}
|
||||
|
||||
func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError {
|
||||
if *w.srv.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||
if *w.srv.platform.Config().ExperimentalSettings.RestrictSystemAdmin {
|
||||
return model.NewAppError("RequestTrialLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
|
||||
}
|
||||
|
||||
@@ -75,8 +75,8 @@ func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, term
|
||||
ServerID: w.srv.TelemetryId(),
|
||||
Name: requester.GetDisplayName(model.ShowFullName),
|
||||
Email: requester.Email,
|
||||
SiteName: *w.srv.Config().TeamSettings.SiteName,
|
||||
SiteURL: *w.srv.Config().ServiceSettings.SiteURL,
|
||||
SiteName: *w.srv.platform.Config().TeamSettings.SiteName,
|
||||
SiteURL: *w.srv.platform.Config().ServiceSettings.SiteURL,
|
||||
Users: users,
|
||||
TermsAccepted: termsAccepted,
|
||||
ReceiveEmailsAccepted: receiveEmailsAccepted,
|
||||
@@ -93,6 +93,11 @@ type JWTClaims struct {
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
func (s *Server) License() *model.License {
|
||||
license, _ := s.licenseValue.Load().(*model.License)
|
||||
return license
|
||||
}
|
||||
|
||||
func (s *Server) LoadLicense() {
|
||||
// ENV var overrides all other sources of license.
|
||||
licenseStr := os.Getenv(LicenseEnv)
|
||||
@@ -131,7 +136,7 @@ func (s *Server) LoadLicense() {
|
||||
|
||||
if !model.IsValidId(licenseId) {
|
||||
// Lets attempt to load the file from disk since it was missing from the DB
|
||||
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.Config().ServiceSettings.LicenseFileLocation)
|
||||
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.platform.Config().ServiceSettings.LicenseFileLocation)
|
||||
|
||||
if license != nil {
|
||||
if _, err := s.SaveLicense(licenseBytes); err != nil {
|
||||
@@ -177,13 +182,13 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
|
||||
return nil, model.NewAppError("addLicense", model.ExpiredLicenseError, nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
|
||||
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if err := s.Jobs.StopSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersNotRunning) {
|
||||
mlog.Error("Stopping job server schedulers failed", mlog.Err(err))
|
||||
}
|
||||
@@ -193,12 +198,12 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
|
||||
// restart job server workers - this handles the edge case where a license file is uploaded, but the job server
|
||||
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
|
||||
// functioning as expected
|
||||
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if err := s.Jobs.StartWorkers(); err != nil {
|
||||
mlog.Error("Starting job server workers failed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
|
||||
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
|
||||
}
|
||||
@@ -233,7 +238,7 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
|
||||
return nil, model.NewAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
s.ReloadConfig()
|
||||
s.platform.ReloadConfig()
|
||||
s.InvalidateAllCaches()
|
||||
|
||||
return &license, nil
|
||||
@@ -256,12 +261,20 @@ func (s *Server) SetLicense(license *model.License) bool {
|
||||
license.Features.SetDefaults()
|
||||
|
||||
s.licenseValue.Store(license)
|
||||
if s.platform != nil {
|
||||
s.platform.SetLicense(license)
|
||||
}
|
||||
|
||||
s.clientLicenseValue.Store(utils.GetClientLicense(license))
|
||||
return true
|
||||
}
|
||||
|
||||
s.licenseValue.Store((*model.License)(nil))
|
||||
s.clientLicenseValue.Store(map[string]string(nil))
|
||||
if s.platform != nil {
|
||||
s.platform.SetLicense((*model.License)(nil))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -307,7 +320,7 @@ func (s *Server) RemoveLicense() *model.AppError {
|
||||
}
|
||||
|
||||
s.SetLicense(nil)
|
||||
s.ReloadConfig()
|
||||
s.platform.ReloadConfig()
|
||||
s.InvalidateAllCaches()
|
||||
|
||||
return nil
|
||||
@@ -329,14 +342,14 @@ func (s *Server) GetSanitizedClientLicense() map[string]string {
|
||||
|
||||
// RequestTrialLicense request a trial license from the mattermost official license server
|
||||
func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *model.AppError {
|
||||
trialRequestJSON, jsonErr := json.Marshal(trialRequest)
|
||||
if jsonErr != nil {
|
||||
return model.NewAppError("RequestTrialLicense", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
trialRequestJSON, err := json.Marshal(trialRequest)
|
||||
if err != nil {
|
||||
return model.NewAppError("RequestTrialLicense", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
resp, err := http.Post(RequestTrialURL, "application/json", bytes.NewBuffer(trialRequestJSON))
|
||||
if err != nil {
|
||||
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
@@ -350,7 +363,11 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
|
||||
fmt.Sprintf("Unexpected HTTP status code %q returned by server", resp.Status), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
licenseResponse := model.MapFromJSON(resp.Body)
|
||||
var licenseResponse map[string]string
|
||||
err = json.NewDecoder(resp.Body).Decode(&licenseResponse)
|
||||
if err != nil {
|
||||
s.GetLogger().Warn("Error decoding license response", mlog.Err(err))
|
||||
}
|
||||
|
||||
if _, ok := licenseResponse["license"]; !ok {
|
||||
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, licenseResponse["message"], http.StatusBadRequest)
|
||||
@@ -360,7 +377,7 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
|
||||
return err
|
||||
}
|
||||
|
||||
s.ReloadConfig()
|
||||
s.platform.ReloadConfig()
|
||||
s.InvalidateAllCaches()
|
||||
|
||||
return nil
|
||||
|
||||
@@ -69,9 +69,9 @@ func (s *Server) doAdvancedPermissionsMigration() {
|
||||
return
|
||||
}
|
||||
|
||||
config := s.Config()
|
||||
config := s.platform.Config()
|
||||
*config.ServiceSettings.PostEditTimeLimit = -1
|
||||
if _, _, err := s.SaveConfig(config, true); err != nil {
|
||||
if _, _, err := s.platform.SaveConfig(config, true); err != nil {
|
||||
mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.Err(err))
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ func (s *Server) doContentExtractionConfigDefaultTrueMigration() {
|
||||
return
|
||||
}
|
||||
|
||||
s.UpdateConfig(func(config *model.Config) {
|
||||
s.platform.UpdateConfig(func(config *model.Config) {
|
||||
config.FileSettings.ExtractContent = model.NewBool(true)
|
||||
})
|
||||
|
||||
@@ -474,7 +474,7 @@ const existingInstallationPostsThreshold = 10
|
||||
func (s *Server) doFirstAdminSetupCompleteMigration() {
|
||||
// Don't run the migration until the flag is turned on.
|
||||
|
||||
if !s.Config().FeatureFlags.UseCaseOnboarding {
|
||||
if !s.platform.Config().FeatureFlags.UseCaseOnboarding {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/i18n"
|
||||
@@ -290,7 +290,7 @@ func (a *App) UpdateMobileAppBadge(userID string) {
|
||||
}
|
||||
|
||||
func (s *Server) createPushNotificationsHub(c request.CTX) {
|
||||
buffer := *s.Config().EmailSettings.PushNotificationBuffer
|
||||
buffer := *s.platform.Config().EmailSettings.PushNotificationBuffer
|
||||
hub := PushNotificationsHub{
|
||||
notificationsChan: make(chan PushNotification, buffer),
|
||||
app: New(ServerConnector(s.Channels())),
|
||||
@@ -382,9 +382,9 @@ func (s *Server) StopPushNotificationsHubWorkers() {
|
||||
}
|
||||
|
||||
func (a *App) rawSendToPushProxy(msg *model.PushNotification) (model.PushResponse, error) {
|
||||
msgJSON, jsonErr := json.Marshal(msg)
|
||||
if jsonErr != nil {
|
||||
return nil, errors.Wrap(jsonErr, "failed to encode to JSON")
|
||||
msgJSON, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode to JSON: %w", err)
|
||||
}
|
||||
|
||||
url := strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/") + model.APIURLSuffixV1 + "/send_push"
|
||||
@@ -400,8 +400,8 @@ func (a *App) rawSendToPushProxy(msg *model.PushNotification) (model.PushRespons
|
||||
defer resp.Body.Close()
|
||||
|
||||
var pushResponse model.PushResponse
|
||||
if jsonErr := json.NewDecoder(resp.Body).Decode(&pushResponse); jsonErr != nil {
|
||||
return nil, errors.Wrap(jsonErr, "failed to decode from JSON")
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pushResponse); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode from JSON: %w", err)
|
||||
}
|
||||
|
||||
return pushResponse, nil
|
||||
@@ -427,7 +427,7 @@ func (a *App) sendToPushProxy(msg *model.PushNotification, session *model.Sessio
|
||||
case model.PushStatusRemove:
|
||||
a.AttachDeviceId(session.Id, "", session.ExpiresAt)
|
||||
a.ClearSessionCacheForUser(session.UserId)
|
||||
return errors.New("Device was reported as removed")
|
||||
return errors.New("device was reported as removed")
|
||||
case model.PushStatusFail:
|
||||
return errors.New(pushResponse[model.PushStatusErrorMsg])
|
||||
}
|
||||
@@ -447,9 +447,9 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
|
||||
mlog.String("status", model.PushReceived),
|
||||
)
|
||||
|
||||
ackJSON, jsonErr := json.Marshal(ack)
|
||||
if jsonErr != nil {
|
||||
return errors.Wrap(jsonErr, "failed to encode to JSON")
|
||||
ackJSON, err := json.Marshal(ack)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode to JSON: %w", err)
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(
|
||||
@@ -457,7 +457,6 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
|
||||
strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/")+model.APIURLSuffixV1+"/ack",
|
||||
bytes.NewReader(ackJSON),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -467,19 +466,16 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Reading the body to completion.
|
||||
_, err = io.Copy(io.Discard, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *App) getMobileAppSessions(userID string) ([]*model.Session, *model.AppError) {
|
||||
sessions, err := a.Srv().Store.Session().GetSessionsWithActiveDeviceIds(userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("getMobileAppSessions", "app.session.get_sessions.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("getMobileAppSessions", "app.session.get_sessions.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return sessions, nil
|
||||
@@ -572,7 +568,7 @@ func (a *App) BuildPushNotificationMessage(c request.CTX, contentsConfig string,
|
||||
|
||||
unreadCount, err := a.Srv().Store.User().GetUnreadCount(user.Id)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("BuildPushNotificationMessage", "app.user.get_unread_count.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("BuildPushNotificationMessage", "app.user.get_unread_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
msg.Badge = int(unreadCount)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
|
||||
@@ -1443,9 +1444,13 @@ func TestPushNotificationRace(t *testing.T) {
|
||||
Router: mux.NewRouter(),
|
||||
filestore: &fmocks.FileBackend{},
|
||||
}
|
||||
s.configStore = &configWrapper{srv: s, Store: memoryStore}
|
||||
var err error
|
||||
s.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: memoryStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
serviceMap := map[ServiceKey]any{
|
||||
ConfigKey: s.configStore,
|
||||
ConfigKey: s.platform,
|
||||
LicenseKey: &licenseWrapper{s},
|
||||
FilestoreKey: s.filestore,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -853,7 +852,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
|
||||
var ar *model.AccessResponse
|
||||
err = json.NewDecoder(tee).Decode(&ar)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d, error=%v", buf.String(), resp.StatusCode, err), http.StatusInternalServerError)
|
||||
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d, error=%v", buf.String(), resp.StatusCode, err), http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
if strings.ToLower(ar.TokenType) != model.AccessTokenType {
|
||||
@@ -891,7 +890,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil
|
||||
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
mlog.Error("Error getting OAuth user", mlog.Int("response", resp.StatusCode), mlog.String("body_string", bodyString))
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -517,7 +517,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
|
||||
body, receivedTeamId, receivedStateProps, _, err := th.App.AuthorizeOAuthUser(&recorder, request, model.ServiceGitlab, "", state, "")
|
||||
|
||||
require.NotNil(t, body)
|
||||
bodyBytes, bodyErr := ioutil.ReadAll(body)
|
||||
bodyBytes, bodyErr := io.ReadAll(body)
|
||||
require.NoError(t, bodyErr)
|
||||
assert.Equal(t, userData, string(bodyBytes))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package app
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
@@ -52,7 +53,22 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option {
|
||||
return errors.Wrap(err, "failed to apply Config option")
|
||||
}
|
||||
|
||||
s.configStore = &configWrapper{srv: s, Store: configStore}
|
||||
platformCfg := platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
Logger: s.Log,
|
||||
StartMetrics: s.startMetrics,
|
||||
Cluster: s.Cluster,
|
||||
}
|
||||
if metricsInterface != nil {
|
||||
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
|
||||
}
|
||||
|
||||
ps, sErr := platform.New(platformCfg)
|
||||
if sErr != nil {
|
||||
return errors.Wrap(sErr, "failed to initialize platform")
|
||||
}
|
||||
s.platform = ps
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -60,7 +76,21 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option {
|
||||
// ConfigStore applies the given config store, typically to replace the traditional sources with a memory store for testing.
|
||||
func ConfigStore(configStore *config.Store) Option {
|
||||
return func(s *Server) error {
|
||||
s.configStore = &configWrapper{srv: s, Store: configStore}
|
||||
platformCfg := platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
Logger: s.Log,
|
||||
StartMetrics: s.startMetrics,
|
||||
Cluster: s.Cluster,
|
||||
}
|
||||
if metricsInterface != nil {
|
||||
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
|
||||
}
|
||||
|
||||
ps, sErr := platform.New(platformCfg)
|
||||
if sErr != nil {
|
||||
return errors.Wrap(sErr, "failed to initialize platform")
|
||||
}
|
||||
s.platform = ps
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
12
app/platform/cluster.go
Обычный файл
12
app/platform/cluster.go
Обычный файл
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
func (ps *PlatformService) IsLeader() bool {
|
||||
if ps.License() != nil && *ps.Config().ClusterSettings.Enable && ps.cluster != nil {
|
||||
return ps.cluster.IsLeader()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -5,9 +5,14 @@ package platform
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/product"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
@@ -30,7 +35,145 @@ func (c *ServiceConfig) validate() error {
|
||||
}
|
||||
|
||||
if c.Logger == nil {
|
||||
return errors.New("Logger is required")
|
||||
var err error
|
||||
// If Logger is not set, use a default logger temporarily.
|
||||
// this should be removed once the logger is properly configured with the service config.
|
||||
// MM-45841
|
||||
c.Logger, err = mlog.NewLogger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensure the config wrapper implements `product.ConfigService`
|
||||
var _ product.ConfigService = (*PlatformService)(nil)
|
||||
|
||||
func (ps *PlatformService) Config() *model.Config {
|
||||
return ps.configStore.Get()
|
||||
}
|
||||
|
||||
// Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
|
||||
// will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
|
||||
// for the listener that can later be used to remove it.
|
||||
func (ps *PlatformService) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
return ps.configStore.AddListener(listener)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) RemoveConfigListener(id string) {
|
||||
ps.configStore.RemoveListener(id)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) UpdateConfig(f func(*model.Config)) {
|
||||
if ps.configStore.IsReadOnly() {
|
||||
return
|
||||
}
|
||||
old := ps.Config()
|
||||
updated := old.Clone()
|
||||
f(updated)
|
||||
if _, _, err := ps.configStore.Set(updated); err != nil {
|
||||
ps.logger.Error("Failed to update config", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||
// It returns both the previous and current configs.
|
||||
func (ps *PlatformService) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
||||
oldCfg, newCfg, err := ps.configStore.Set(newCfg)
|
||||
if errors.Is(err, config.ErrReadOnlyConfiguration) {
|
||||
return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
|
||||
} else if err != nil {
|
||||
return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if ps.serviceConfig.StartMetrics && *ps.Config().MetricsSettings.Enable {
|
||||
ps.RestartMetrics()
|
||||
} else {
|
||||
ps.ShutdownMetrics()
|
||||
}
|
||||
|
||||
if ps.cluster != nil {
|
||||
err := ps.cluster.ConfigChanged(ps.configStore.RemoveEnvironmentOverrides(oldCfg),
|
||||
ps.configStore.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return oldCfg, newCfg, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) ReloadConfig() error {
|
||||
if err := ps.configStore.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetEnvironmentOverridesWithFilter(filter func(reflect.StructField) bool) map[string]interface{} {
|
||||
return ps.configStore.GetEnvironmentOverridesWithFilter(filter)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetEnvironmentOverrides() map[string]interface{} {
|
||||
return ps.configStore.GetEnvironmentOverrides()
|
||||
}
|
||||
|
||||
func (ps *PlatformService) DescribeConfig() string {
|
||||
return ps.configStore.String()
|
||||
}
|
||||
|
||||
func (ps *PlatformService) CleanUpConfig() error {
|
||||
return ps.configStore.CleanUp()
|
||||
}
|
||||
|
||||
// ConfigureLogger applies the specified configuration to a logger.
|
||||
func (ps *PlatformService) ConfigureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, getPath func(string) string) error {
|
||||
// Advanced logging is E20 only, however logging must be initialized before the license
|
||||
// file is loaded. If no valid E20 license exists then advanced logging will be
|
||||
// shutdown once license is loaded/checked.
|
||||
var err error
|
||||
dsn := *logSettings.AdvancedLoggingConfig
|
||||
var logConfigSrc config.LogConfigSrc
|
||||
if dsn != "" {
|
||||
logConfigSrc, err = config.NewLogConfigSrc(dsn, ps.configStore)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config source for %s, %w", name, err)
|
||||
}
|
||||
ps.logger.Info("Loaded configuration for "+name, mlog.String("source", dsn))
|
||||
}
|
||||
|
||||
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config source for %s, %w", name, err)
|
||||
}
|
||||
|
||||
if err := logger.ConfigureTargets(cfg, nil); err != nil {
|
||||
return fmt.Errorf("invalid config for %s, %w", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetConfigStore() *config.Store {
|
||||
return ps.configStore
|
||||
}
|
||||
|
||||
func (ps *PlatformService) GetConfigFile(name string) ([]byte, error) {
|
||||
return ps.configStore.GetFile(name)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetConfigFile(name string, data []byte) error {
|
||||
return ps.configStore.SetFile(name, data)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) RemoveConfigFile(name string) error {
|
||||
return ps.configStore.RemoveFile(name)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) HasConfigFile(name string) (bool, error) {
|
||||
return ps.configStore.HasFile(name)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetConfigReadOnlyFF(readOnly bool) {
|
||||
ps.configStore.SetReadOnlyFF(readOnly)
|
||||
}
|
||||
|
||||
47
app/platform/config_test.go
Обычный файл
47
app/platform/config_test.go
Обычный файл
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func TestConfigListener(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
originalSiteName := th.Service.Config().TeamSettings.SiteName
|
||||
|
||||
listenerCalled := false
|
||||
listener := func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
assert.False(t, listenerCalled, "listener called twice")
|
||||
|
||||
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
|
||||
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
|
||||
|
||||
listenerCalled = true
|
||||
}
|
||||
listenerId := th.Service.AddConfigListener(listener)
|
||||
defer th.Service.RemoveConfigListener(listenerId)
|
||||
|
||||
listener2Called := false
|
||||
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
assert.False(t, listener2Called, "listener2 called twice")
|
||||
|
||||
listener2Called = true
|
||||
}
|
||||
listener2Id := th.Service.AddConfigListener(listener2)
|
||||
defer th.Service.RemoveConfigListener(listener2Id)
|
||||
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.SiteName = "test123"
|
||||
})
|
||||
|
||||
assert.True(t, listenerCalled, "listener should've been called")
|
||||
assert.True(t, listener2Called, "listener 2 should've been called")
|
||||
}
|
||||
118
app/platform/feature_flags.go
Обычный файл
118
app/platform/feature_flags.go
Обычный файл
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/featureflag"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
// SetupFeatureFlags called on startup and when the cluster leader changes.
|
||||
// Starts or stops the synchronization of feature flags from upstream management.
|
||||
func (ps *PlatformService) SetupFeatureFlags() {
|
||||
ps.featureFlagSynchronizerMutex.Lock()
|
||||
defer ps.featureFlagSynchronizerMutex.Unlock()
|
||||
splitKey := *ps.Config().ServiceSettings.SplitKey
|
||||
splitConfigured := splitKey != ""
|
||||
syncFeatureFlags := splitConfigured && ps.IsLeader()
|
||||
|
||||
ps.configStore.SetReadOnlyFF(!splitConfigured)
|
||||
|
||||
if syncFeatureFlags {
|
||||
if err := ps.startFeatureFlagUpdateJob(); err != nil {
|
||||
ps.logger.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
|
||||
}
|
||||
} else {
|
||||
ps.StopFeatureFlagUpdateJob()
|
||||
}
|
||||
|
||||
if err := ps.configStore.Load(); err != nil {
|
||||
ps.logger.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PlatformService) updateFeatureFlagValuesFromManagement() {
|
||||
newCfg := ps.configStore.GetNoEnv().Clone()
|
||||
oldFlags := *newCfg.FeatureFlags
|
||||
newFlags := ps.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
|
||||
oldFlagsBytes, _ := json.Marshal(oldFlags)
|
||||
newFlagsBytes, _ := json.Marshal(newFlags)
|
||||
ps.logger.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
|
||||
if oldFlags != newFlags {
|
||||
ps.logger.Debug("Feature flag change detected, updating config")
|
||||
*newCfg.FeatureFlags = newFlags
|
||||
ps.SaveConfig(newCfg, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PlatformService) startFeatureFlagUpdateJob() error {
|
||||
// Can be run multiple times
|
||||
if ps.featureFlagSynchronizer != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var log *mlog.Logger
|
||||
if *ps.Config().ServiceSettings.DebugSplit {
|
||||
log = ps.logger
|
||||
}
|
||||
|
||||
attributes := map[string]any{}
|
||||
|
||||
// if we are part of a cloud installation, add its installation and group id
|
||||
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
|
||||
attributes["installation_id"] = installationId
|
||||
}
|
||||
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
|
||||
attributes["group_id"] = groupId
|
||||
}
|
||||
|
||||
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
|
||||
ServerID: ps.telemetryId,
|
||||
SplitKey: *ps.Config().ServiceSettings.SplitKey,
|
||||
Log: log,
|
||||
Attributes: attributes,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ps.featureFlagStop = make(chan struct{})
|
||||
ps.featureFlagStopped = make(chan struct{})
|
||||
ps.featureFlagSynchronizer = synchronizer
|
||||
syncInterval := *ps.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
defer close(ps.featureFlagStopped)
|
||||
if err := synchronizer.EnsureReady(); err != nil {
|
||||
ps.logger.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
ps.updateFeatureFlagValuesFromManagement()
|
||||
for {
|
||||
select {
|
||||
case <-ps.featureFlagStop:
|
||||
return
|
||||
case <-ticker.C:
|
||||
ps.updateFeatureFlagValuesFromManagement()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) StopFeatureFlagUpdateJob() {
|
||||
if ps.featureFlagSynchronizer != nil {
|
||||
close(ps.featureFlagStop)
|
||||
<-ps.featureFlagStopped
|
||||
ps.featureFlagSynchronizer.Close()
|
||||
ps.featureFlagSynchronizer = nil
|
||||
}
|
||||
}
|
||||
87
app/platform/helper_test.go
Обычный файл
87
app/platform/helper_test.go
Обычный файл
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/store"
|
||||
)
|
||||
|
||||
type TestHelper struct {
|
||||
Service *PlatformService
|
||||
}
|
||||
|
||||
func Setup(tb testing.TB) *TestHelper {
|
||||
if testing.Short() {
|
||||
tb.SkipNow()
|
||||
}
|
||||
dbStore := mainHelper.GetStore()
|
||||
dbStore.DropAllTables()
|
||||
dbStore.MarkSystemRanUnitTests()
|
||||
mainHelper.PreloadMigrations()
|
||||
|
||||
return setupTestHelper(dbStore, false, true, tb)
|
||||
}
|
||||
|
||||
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, tb testing.TB) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "apptest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
configStore := config.NewTestMemoryStore()
|
||||
|
||||
memoryConfig := configStore.Get()
|
||||
*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
|
||||
*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
|
||||
*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
|
||||
*memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests
|
||||
*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
|
||||
*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
|
||||
configStore.Set(memoryConfig)
|
||||
|
||||
ps, err := New(ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
th := &TestHelper{
|
||||
Service: ps,
|
||||
}
|
||||
|
||||
// Share same configuration with app.TestHelper
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.MaxUsersPerTeam = 50
|
||||
*cfg.RateLimitSettings.Enable = false
|
||||
*cfg.TeamSettings.EnableOpenServer = true
|
||||
})
|
||||
|
||||
// Disable strict password requirements for test
|
||||
th.Service.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PasswordSettings.MinimumLength = 5
|
||||
*cfg.PasswordSettings.Lowercase = false
|
||||
*cfg.PasswordSettings.Uppercase = false
|
||||
*cfg.PasswordSettings.Symbol = false
|
||||
*cfg.PasswordSettings.Number = false
|
||||
})
|
||||
|
||||
if enterprise {
|
||||
th.Service.SetLicense(model.NewTestLicense())
|
||||
} else {
|
||||
th.Service.SetLicense(nil)
|
||||
}
|
||||
|
||||
return th
|
||||
}
|
||||
|
||||
func (th *TestHelper) TearDown() {
|
||||
// Add cleaning code here
|
||||
}
|
||||
32
app/platform/main_test.go
Обычный файл
32
app/platform/main_test.go
Обычный файл
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/testlib"
|
||||
)
|
||||
|
||||
var mainHelper *testlib.MainHelper
|
||||
var replicaFlag bool
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if f := flag.Lookup("mysql-replica"); f == nil {
|
||||
flag.BoolVar(&replicaFlag, "mysql-replica", false, "")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
var options = testlib.HelperOptions{
|
||||
EnableStore: true,
|
||||
EnableResources: true,
|
||||
WithReadReplica: replicaFlag,
|
||||
}
|
||||
|
||||
mainHelper = testlib.NewMainHelperWithOptions(&options)
|
||||
defer mainHelper.Close()
|
||||
|
||||
mainHelper.Main(m)
|
||||
}
|
||||
19
app/platform/server_license.go
Обычный файл
19
app/platform/server_license.go
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
// License returns the license stored in the server struct.
|
||||
// This should be removed with MM-45839
|
||||
func (ps *PlatformService) License() *model.License {
|
||||
license, _ := ps.licenseValue.Load().(*model.License)
|
||||
return license
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetLicense(license *model.License) {
|
||||
ps.licenseValue.Store(license)
|
||||
}
|
||||
@@ -4,6 +4,11 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/featureflag"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
@@ -19,6 +24,14 @@ type PlatformService struct {
|
||||
|
||||
metrics *platformMetrics
|
||||
|
||||
featureFlagSynchronizerMutex sync.Mutex
|
||||
featureFlagSynchronizer *featureflag.Synchronizer
|
||||
featureFlagStop chan struct{}
|
||||
featureFlagStopped chan struct{}
|
||||
|
||||
licenseValue atomic.Value
|
||||
telemetryId string
|
||||
|
||||
cluster einterfaces.ClusterInterface
|
||||
}
|
||||
|
||||
@@ -49,3 +62,18 @@ func (ps *PlatformService) ShutdownMetrics() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) ShutdownConfig() error {
|
||||
if ps.configStore != nil {
|
||||
err := ps.configStore.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to close config store: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetTelemetryId(id string) {
|
||||
ps.telemetryId = id
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -39,7 +38,7 @@ type pluginSignaturePath struct {
|
||||
signaturePath string
|
||||
}
|
||||
|
||||
//Ensure routerService implements `product.RouterService`
|
||||
// Ensure routerService implements `product.RouterService`
|
||||
var _ product.RouterService = (*routerService)(nil)
|
||||
|
||||
type routerService struct {
|
||||
@@ -976,7 +975,7 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "plugintmp")
|
||||
tmpDir, err := os.MkdirTemp("", "plugintmp")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to create temp dir plugintmp")
|
||||
}
|
||||
@@ -1086,7 +1085,7 @@ func getPrepackagedPlugin(pluginPath *pluginSignaturePath, pluginFile io.ReadSee
|
||||
if sigErr != nil {
|
||||
return nil, "", errors.Wrapf(sigErr, "Failed to open prepackaged plugin signature %s", sig)
|
||||
}
|
||||
bytes, sigErr := ioutil.ReadAll(sigReader)
|
||||
bytes, sigErr := io.ReadAll(sigReader)
|
||||
if sigErr != nil {
|
||||
return nil, "", errors.Wrapf(sigErr, "Failed to read prepackaged plugin signature %s", sig)
|
||||
}
|
||||
@@ -1105,7 +1104,7 @@ func getPrepackagedPlugin(pluginPath *pluginSignaturePath, pluginFile io.ReadSee
|
||||
}
|
||||
|
||||
func getIcon(iconPath string) (string, error) {
|
||||
icon, err := ioutil.ReadFile(iconPath)
|
||||
icon, err := os.ReadFile(iconPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to open icon at path %s", iconPath)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
@@ -897,7 +896,7 @@ func (api *PluginAPI) InstallPlugin(file io.Reader, replace bool) (*model.Manife
|
||||
return nil, model.NewAppError("installPlugin", "app.plugin.upload_disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
fileBuffer, err := ioutil.ReadAll(file)
|
||||
fileBuffer, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("InstallPlugin", "api.plugin.upload.file.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
@@ -1029,7 +1028,7 @@ func (api *PluginAPI) PluginHTTP(request *http.Request) *http.Response {
|
||||
if len(split) != 3 {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString("Not enough URL. Form of URL should be /<pluginid>/*")),
|
||||
Body: io.NopCloser(bytes.NewBufferString("Not enough URL. Form of URL should be /<pluginid>/*")),
|
||||
}
|
||||
}
|
||||
destinationPluginId := split[1]
|
||||
@@ -1043,7 +1042,7 @@ func (api *PluginAPI) PluginHTTP(request *http.Request) *http.Response {
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Body: ioutil.NopCloser(bytes.NewBufferString(message)),
|
||||
Body: io.NopCloser(bytes.NewBufferString(message)),
|
||||
}
|
||||
}
|
||||
responseTransfer := &PluginResponseWriter{}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -70,7 +70,7 @@ func setDefaultPluginConfig(th *TestHelper, pluginID string) {
|
||||
}
|
||||
|
||||
func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests []string, pluginIDs []string, asMain bool, app *App, c *request.Context) string {
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err = os.RemoveAll(pluginDir)
|
||||
@@ -79,7 +79,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
|
||||
}
|
||||
})
|
||||
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
err = os.RemoveAll(webappPluginDir)
|
||||
@@ -106,7 +106,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
|
||||
utils.CompileGoTest(t, pluginCodes[i], backend)
|
||||
}
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifests[i]), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifests[i]), 0600)
|
||||
manifest, activated, reterr := env.Activate(pluginID)
|
||||
require.NoError(t, reterr)
|
||||
require.NotNil(t, manifest)
|
||||
@@ -841,9 +841,9 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
}
|
||||
`
|
||||
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
@@ -857,7 +857,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
utils.CompileGo(t, pluginCode, backend)
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
||||
manifest, activated, reterr := env.Activate(pluginID)
|
||||
|
||||
require.NoError(t, reterr)
|
||||
@@ -884,7 +884,7 @@ func TestPluginAPIInstallPlugin(t *testing.T) {
|
||||
api := th.SetupPluginAPI()
|
||||
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
tarData, err := ioutil.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
|
||||
tarData, err := os.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
|
||||
_, appErr := api.InstallPlugin(bytes.NewReader(tarData), true)
|
||||
@@ -922,9 +922,9 @@ func TestInstallPlugin(t *testing.T) {
|
||||
// since it removes plugin dirs right after it returns, does not update App configs with the plugin
|
||||
// dirs and this behavior tends to break this test as a result.
|
||||
setupTest := func(t *testing.T, pluginCode string, pluginManifest string, pluginID string, app *App, c *request.Context) (func(), string) {
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
app.UpdateConfig(func(cfg *model.Config) {
|
||||
@@ -944,7 +944,7 @@ func TestInstallPlugin(t *testing.T) {
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
utils.CompileGo(t, pluginCode, backend)
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
|
||||
manifest, activated, reterr := env.Activate(pluginID)
|
||||
require.NoError(t, reterr)
|
||||
require.NotNil(t, manifest)
|
||||
@@ -1126,7 +1126,7 @@ func TestPluginAPIRemoveTeamIcon(t *testing.T) {
|
||||
}
|
||||
|
||||
func pluginAPIHookTest(t *testing.T, th *TestHelper, fileName string, id string, settingsSchema string) error {
|
||||
data, err := ioutil.ReadFile(fileName)
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1161,7 +1161,7 @@ func TestBasicAPIPlugins(t *testing.T) {
|
||||
defaultSchema := getDefaultPluginSettingsSchema()
|
||||
testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
|
||||
require.True(t, found, "Cannot read find app folder")
|
||||
dirs, err := ioutil.ReadDir(testFolder)
|
||||
dirs, err := os.ReadDir(testFolder)
|
||||
require.NoError(t, err, "Cannot read test folder %v", testFolder)
|
||||
for _, dir := range dirs {
|
||||
d := dir.Name()
|
||||
@@ -1523,7 +1523,7 @@ func TestInterpluginPluginHTTP(t *testing.T) {
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"bytes"
|
||||
"net/http"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
)
|
||||
|
||||
type MyPlugin struct {
|
||||
@@ -1545,7 +1545,7 @@ func TestInterpluginPluginHTTP(t *testing.T) {
|
||||
if resp.Body == nil {
|
||||
return nil, "Nil body"
|
||||
}
|
||||
respbody, err := ioutil.ReadAll(resp.Body)
|
||||
respbody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err.Error()
|
||||
}
|
||||
@@ -1605,9 +1605,9 @@ func TestAPIMetrics(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
metricsMock := &mocks.MetricsInterface{}
|
||||
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
@@ -1642,7 +1642,7 @@ func TestAPIMetrics(t *testing.T) {
|
||||
}
|
||||
`
|
||||
utils.CompileGo(t, code, backend)
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
|
||||
// Don't care about these mocks
|
||||
metricsMock.On("ObservePluginHookDuration", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
|
||||
@@ -1730,7 +1730,7 @@ func TestPluginHTTPConnHijack(t *testing.T) {
|
||||
require.True(t, found, "Cannot find tests folder")
|
||||
fullPath := path.Join(testFolder, "manual.test_http_hijack_plugin", "main.go")
|
||||
|
||||
pluginCode, err := ioutil.ReadFile(fullPath)
|
||||
pluginCode, err := os.ReadFile(fullPath)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pluginCode)
|
||||
|
||||
@@ -1752,7 +1752,7 @@ func TestPluginHTTPConnHijack(t *testing.T) {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "OK", string(body))
|
||||
}
|
||||
@@ -1765,7 +1765,7 @@ func TestPluginHTTPUpgradeWebSocket(t *testing.T) {
|
||||
require.True(t, found, "Cannot find tests folder")
|
||||
fullPath := path.Join(testFolder, "manual.test_http_upgrade_websocket_plugin", "main.go")
|
||||
|
||||
pluginCode, err := ioutil.ReadFile(fullPath)
|
||||
pluginCode, err := os.ReadFile(fullPath)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pluginCode)
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -29,9 +28,9 @@ import (
|
||||
)
|
||||
|
||||
func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, apiFunc func(*model.Manifest) plugin.API) (func(), []string, []error) {
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil)
|
||||
@@ -45,7 +44,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
utils.CompileGo(t, code, backend)
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
_, _, activationErr := env.Activate(pluginID)
|
||||
pluginIDs = append(pluginIDs, pluginID)
|
||||
activationErrors = append(activationErrors, activationErr)
|
||||
@@ -1024,9 +1023,9 @@ func TestHookMetrics(t *testing.T) {
|
||||
t.Run("", func(t *testing.T) {
|
||||
metricsMock := &mocks.MetricsInterface{}
|
||||
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
pluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
@@ -1069,7 +1068,7 @@ func TestHookMetrics(t *testing.T) {
|
||||
}
|
||||
`
|
||||
utils.CompileGo(t, code, backend)
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
|
||||
|
||||
// Setup mocks before activating
|
||||
metricsMock.On("ObservePluginHookDuration", pluginID, "Implemented", true, mock.Anything).Return()
|
||||
|
||||
@@ -33,14 +33,12 @@
|
||||
// Prepackaged plugins are included with the server. They otherwise follow the above flow, except do not get uploaded
|
||||
// to the filestore. Prepackaged plugins override all other plugins with the same plugin id, but only when the prepackaged
|
||||
// plugin is newer. Managed plugins unconditionally override unmanaged plugins with the same plugin id.
|
||||
//
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -280,7 +278,7 @@ func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, in
|
||||
}
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "plugintmp")
|
||||
tmpDir, err := os.MkdirTemp("", "plugintmp")
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("installPluginLocally", "app.plugin.filesystem.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -305,7 +303,7 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest
|
||||
return nil, "", model.NewAppError("extractPlugin", "app.plugin.extract.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
dir, err := ioutil.ReadDir(extractDir)
|
||||
dir, err := os.ReadDir(extractDir)
|
||||
if err != nil {
|
||||
return nil, "", model.NewAppError("extractPlugin", "app.plugin.filesystem.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -157,11 +157,11 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
|
||||
sentToken := ""
|
||||
|
||||
if r.Header.Get(model.HeaderCsrfToken) == "" {
|
||||
bodyBytes, _ := ioutil.ReadAll(r.Body)
|
||||
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
bodyBytes, _ := io.ReadAll(r.Body)
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
r.ParseForm()
|
||||
sentToken = r.FormValue("csrf")
|
||||
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
} else {
|
||||
sentToken = r.Header.Get(model.HeaderCsrfToken)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
@@ -25,7 +24,7 @@ func (a *App) GetPublicKey(name string) ([]byte, *model.AppError) {
|
||||
}
|
||||
|
||||
func (s *Server) getPublicKey(name string) ([]byte, *model.AppError) {
|
||||
data, err := s.configStore.GetFile(name)
|
||||
data, err := s.platform.GetConfigFile(name)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPublicKey", "app.plugin.get_public_key.get_file.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -37,11 +36,11 @@ func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError {
|
||||
if isSamlFile(&a.Config().SamlSettings, name) {
|
||||
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
data, err := ioutil.ReadAll(key)
|
||||
data, err := io.ReadAll(key)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddPublicKey", "app.plugin.write_file.read.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
err = a.Srv().configStore.SetFile(name, data)
|
||||
err = a.Srv().platform.SetConfigFile(name, data)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddPublicKey", "app.plugin.write_file.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -61,7 +60,7 @@ func (a *App) DeletePublicKey(name string) *model.AppError {
|
||||
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
filename := filepath.Base(name)
|
||||
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
|
||||
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
|
||||
return model.NewAppError("DeletePublicKey", "app.plugin.delete_public_key.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -122,7 +121,7 @@ func verifyBinarySignature(publicKey, signedFile, signature io.Reader) error {
|
||||
}
|
||||
|
||||
func decodeIfArmored(reader io.Reader) (io.Reader, error) {
|
||||
readBytes, err := ioutil.ReadAll(reader)
|
||||
readBytes, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read the file")
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -38,7 +37,7 @@ func TestPluginPublicKeys(t *testing.T) {
|
||||
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
publicKeyFilename := "test-public-key.plugin.gpg"
|
||||
publicKey, err := ioutil.ReadFile(filepath.Join(path, publicKeyFilename))
|
||||
publicKey, err := os.ReadFile(filepath.Join(path, publicKeyFilename))
|
||||
require.NoError(t, err)
|
||||
fileReader, err := os.Open(filepath.Join(path, publicKeyFilename))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -384,7 +384,7 @@ func TestPrivateServePluginRequest(t *testing.T) {
|
||||
handler := func(context *plugin.Context, w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, testCase.ExpectedURL, r.URL.Path)
|
||||
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
assert.Equal(t, expectedBody, body)
|
||||
}
|
||||
|
||||
@@ -827,7 +827,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
|
||||
t.Run("automatic, enabled plugin, no signature", func(t *testing.T) {
|
||||
// Install the plugin and enable
|
||||
pluginBytes, err := ioutil.ReadFile(testPluginPath)
|
||||
pluginBytes, err := os.ReadFile(testPluginPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
@@ -935,7 +935,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Install first plugin and enable
|
||||
pluginBytes, err := ioutil.ReadFile(testPluginPath)
|
||||
pluginBytes, err := os.ReadFile(testPluginPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
|
||||
71
app/post.go
71
app/post.go
@@ -251,7 +251,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
post.AddProp("attachments", attachmentsInterface)
|
||||
}
|
||||
if err != nil {
|
||||
mlog.Warn("Could not convert post attachments to map interface.", mlog.Err(err))
|
||||
c.Logger().Warn("Could not convert post attachments to map interface.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
|
||||
if len(post.FileIds) > 0 {
|
||||
if err = a.attachFilesToPost(post); err != nil {
|
||||
mlog.Warn("Encountered error attaching files to post", mlog.String("post_id", post.Id), mlog.Any("file_ids", post.FileIds), mlog.Err(err))
|
||||
c.Logger().Warn("Encountered error attaching files to post", mlog.String("post_id", post.Id), mlog.Any("file_ids", post.FileIds), mlog.Err(err))
|
||||
}
|
||||
|
||||
if a.Metrics() != nil {
|
||||
@@ -348,12 +348,12 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
UpdateFollowing: true,
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Warn("Failed to update thread membership", mlog.Err(err))
|
||||
c.Logger().Warn("Failed to update thread membership", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.handlePostEvents(c, rpost, user, channel, triggerWebhooks, parentPostList, setOnline); err != nil {
|
||||
mlog.Warn("Failed to handle post events", mlog.Err(err))
|
||||
c.Logger().Warn("Failed to handle post events", mlog.Err(err))
|
||||
}
|
||||
|
||||
// Send any ephemeral posts after the post is created to ensure it shows up after the latest post created
|
||||
@@ -1224,34 +1224,35 @@ func (a *App) GetPostsForChannelAroundLastUnread(c request.CTX, channelID, userI
|
||||
}
|
||||
|
||||
func (a *App) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post, *model.AppError) {
|
||||
post, nErr := a.Srv().Store.Post().GetSingle(postID, false)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("DeletePost", "app.post.get.app_error", nil, nErr.Error(), http.StatusBadRequest)
|
||||
post, err := a.Srv().Store.Post().GetSingle(postID, false)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("DeletePost", "app.post.get.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
channel, err := a.GetChannel(c, post.ChannelId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
channel, appErr := a.GetChannel(c, post.ChannelId)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if channel.DeleteAt != 0 {
|
||||
err := model.NewAppError("DeletePost", "api.post.delete_post.can_not_delete_post_in_deleted.error", nil, "", http.StatusBadRequest)
|
||||
return nil, err
|
||||
appErr := model.NewAppError("DeletePost", "api.post.delete_post.can_not_delete_post_in_deleted.error", nil, "", http.StatusBadRequest)
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if err := a.Srv().Store.Post().Delete(postID, model.GetMillis(), deleteByID); err != nil {
|
||||
err = a.Srv().Store.Post().Delete(postID, model.GetMillis(), deleteByID)
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, nfErr.Error(), http.StatusNotFound)
|
||||
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
|
||||
default:
|
||||
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
postJSON, jsonErr := json.Marshal(post)
|
||||
if jsonErr != nil {
|
||||
return nil, model.NewAppError("DeletePost", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
postJSON, err := json.Marshal(post)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("DeletePost", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
userMessage := model.NewWebSocketEvent(model.WebsocketEventPostDeleted, "", post.ChannelId, "", nil)
|
||||
@@ -1283,14 +1284,14 @@ func (a *App) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post,
|
||||
|
||||
func (a *App) deleteFlaggedPosts(postID string) {
|
||||
if err := a.Srv().Store.Preference().DeleteCategoryAndName(model.PreferenceCategoryFlaggedPost, postID); err != nil {
|
||||
mlog.Warn("Unable to delete flagged post preference when deleting post.", mlog.Err(err))
|
||||
a.Log().Warn("Unable to delete flagged post preference when deleting post.", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) deletePostFiles(postID string) {
|
||||
if _, err := a.Srv().Store.FileInfo().DeleteForPost(postID); err != nil {
|
||||
mlog.Warn("Encountered error when deleting files for post", mlog.String("post_id", postID), mlog.Err(err))
|
||||
a.Log().Warn("Encountered error when deleting files for post", mlog.String("post_id", postID), mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1358,7 +1359,7 @@ func (a *App) searchPostsInTeam(teamID string, userID string, paramsList []*mode
|
||||
|
||||
for result := range pchan {
|
||||
if result.NErr != nil {
|
||||
return nil, model.NewAppError("searchPostsInTeam", "app.post.search.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("searchPostsInTeam", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
|
||||
}
|
||||
data := result.Data.(*model.PostList)
|
||||
posts.Extend(data)
|
||||
@@ -1375,7 +1376,7 @@ func (a *App) convertChannelNamesToChannelIds(c *request.Context, channels []str
|
||||
for idx, channelName := range channels {
|
||||
channel, err := a.parseAndFetchChannelIdByNameFromInFilter(c, channelName, userID, teamID, includeDeletedChannels)
|
||||
if err != nil {
|
||||
mlog.Warn("error getting channel id by name from in filter", mlog.Err(err))
|
||||
a.Log().Warn("error getting channel id by name from in filter", mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
channels[idx] = channel.Id
|
||||
@@ -1387,7 +1388,7 @@ func (a *App) convertUserNameToUserIds(usernames []string) []string {
|
||||
for idx, username := range usernames {
|
||||
user, err := a.GetUserByUsername(username)
|
||||
if err != nil {
|
||||
mlog.Warn("error getting user by username", mlog.String("user_name", username), mlog.Err(err))
|
||||
a.Log().Warn("error getting user by username", mlog.String("user_name", username), mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
usernames[idx] = user.Id
|
||||
@@ -1410,13 +1411,13 @@ func (a *App) GetLastAccessiblePostTime() (int64, *model.AppError) {
|
||||
// All posts are accessible
|
||||
return 0, nil
|
||||
default:
|
||||
return 0, model.NewAppError("GetLastAccessiblePostTime", "app.system.get_by_name.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return 0, model.NewAppError("GetLastAccessiblePostTime", "app.system.get_by_name.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
lastAccessiblePostTime, err := strconv.ParseInt(system.Value, 10, 64)
|
||||
if err != nil {
|
||||
return 0, model.NewAppError("GetLastAccessiblePostTime", "common.parse_error_int64", map[string]interface{}{"Value": system.Value}, err.Error(), http.StatusInternalServerError)
|
||||
return 0, model.NewAppError("GetLastAccessiblePostTime", "common.parse_error_int64", map[string]interface{}{"Value": system.Value}, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return lastAccessiblePostTime, nil
|
||||
@@ -1434,7 +1435,7 @@ func (a *App) ComputeLastAccessiblePostTime() error {
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
if !errors.As(err, &nfErr) {
|
||||
return model.NewAppError("ComputeLastAccessiblePostTime", "app.last_accessible_post.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return model.NewAppError("ComputeLastAccessiblePostTime", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1444,7 +1445,7 @@ func (a *App) ComputeLastAccessiblePostTime() error {
|
||||
Value: strconv.FormatInt(createdAt, 10),
|
||||
})
|
||||
if err != nil {
|
||||
return model.NewAppError("ComputeLastAccessiblePostTime", "app.system.save.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return model.NewAppError("ComputeLastAccessiblePostTime", "app.system.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1458,7 +1459,7 @@ func (a *App) getCloudMessagesHistoryLimit() (int64, *model.AppError) {
|
||||
|
||||
limits, err := a.Cloud().GetCloudLimits("")
|
||||
if err != nil {
|
||||
return 0, model.NewAppError("getCloudMessagesHistoryLimit", "api.cloud.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return 0, model.NewAppError("getCloudMessagesHistoryLimit", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
if limits == nil || limits.Messages == nil || limits.Messages.History == nil {
|
||||
@@ -1516,14 +1517,14 @@ func (a *App) SearchPostsForUser(c *request.Context, terms string, userID string
|
||||
return model.MakePostSearchResults(model.NewPostList(), nil), nil
|
||||
}
|
||||
|
||||
postSearchResults, nErr := a.Srv().Store.Post().SearchPostsForUser(finalParamsList, userID, teamID, page, perPage)
|
||||
if nErr != nil {
|
||||
postSearchResults, err := a.Srv().Store.Post().SearchPostsForUser(finalParamsList, userID, teamID, page, perPage)
|
||||
if err != nil {
|
||||
var appErr *model.AppError
|
||||
switch {
|
||||
case errors.As(nErr, &appErr):
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
default:
|
||||
return nil, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1535,9 +1536,9 @@ func (a *App) SearchPostsForUser(c *request.Context, terms string, userID string
|
||||
}
|
||||
|
||||
func (a *App) GetRecentSearchesForUser(userID string) ([]*model.SearchParams, *model.AppError) {
|
||||
searchParams, nErr := a.Srv().Store.Post().GetRecentSearchesForUser(userID)
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("GetRecentSearchesForUser", "app.recent_searches.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
searchParams, err := a.Srv().Store.Post().GetRecentSearchesForUser(userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetRecentSearchesForUser", "app.recent_searches.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
return searchParams, nil
|
||||
|
||||
@@ -41,7 +41,7 @@ var linkCache = cache.NewLRU(cache.LRUOptions{
|
||||
|
||||
func (s *Server) initPostMetadata() {
|
||||
// Dump any cached links if the proxy settings have changed so image URLs can be updated
|
||||
s.AddConfigListener(func(before, after *model.Config) {
|
||||
s.platform.AddConfigListener(func(before, after *model.Config) {
|
||||
if (before.ImageProxySettings.Enable != after.ImageProxySettings.Enable) ||
|
||||
(before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) ||
|
||||
(before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) ||
|
||||
|
||||
@@ -796,7 +796,7 @@ func TestPreparePostForClientWithImageProxy(t *testing.T) {
|
||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
||||
})
|
||||
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
|
||||
|
||||
return th
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ func TestImageProxy(t *testing.T) {
|
||||
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
|
||||
})
|
||||
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
|
||||
|
||||
for name, tc := range map[string]struct {
|
||||
ProxyType string
|
||||
@@ -686,7 +686,7 @@ func TestCreatePost(t *testing.T) {
|
||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
||||
})
|
||||
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
|
||||
|
||||
imageURL := "http://mydomain.com/myimage"
|
||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
||||
@@ -956,7 +956,7 @@ func TestPatchPost(t *testing.T) {
|
||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
||||
})
|
||||
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
|
||||
|
||||
imageURL := "http://mydomain.com/myimage"
|
||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
||||
@@ -1252,7 +1252,7 @@ func TestUpdatePost(t *testing.T) {
|
||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
||||
})
|
||||
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
|
||||
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
|
||||
|
||||
imageURL := "http://mydomain.com/myimage"
|
||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
||||
|
||||
@@ -53,12 +53,12 @@ func (a *App) UpdatePreferences(userID string, preferences model.Preferences) *m
|
||||
case errors.As(err, &appErr):
|
||||
return appErr
|
||||
default:
|
||||
return model.NewAppError("UpdatePreferences", "app.preference.save.updating.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return model.NewAppError("UpdatePreferences", "app.preference.save.updating.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Srv().Store.Channel().UpdateSidebarChannelsByPreferences(preferences); err != nil {
|
||||
return model.NewAppError("UpdatePreferences", "api.preference.update_preferences.update_sidebar.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return model.NewAppError("UpdatePreferences", "api.preference.update_preferences.update_sidebar.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, "", "", userID, nil)
|
||||
@@ -87,12 +87,12 @@ func (a *App) DeletePreferences(userID string, preferences model.Preferences) *m
|
||||
|
||||
for _, preference := range preferences {
|
||||
if err := a.Srv().Store.Preference().Delete(userID, preference.Category, preference.Name); err != nil {
|
||||
return model.NewAppError("DeletePreferences", "app.preference.delete.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
return model.NewAppError("DeletePreferences", "app.preference.delete.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Srv().Store.Channel().DeleteSidebarChannelsByPreferences(preferences); err != nil {
|
||||
return model.NewAppError("DeletePreferences", "api.preference.delete_preferences.update_sidebar.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return model.NewAppError("DeletePreferences", "api.preference.delete_preferences.update_sidebar.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, "", "", userID, nil)
|
||||
|
||||
@@ -162,9 +162,9 @@ func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction
|
||||
func (a *App) sendReactionEvent(event string, reaction *model.Reaction, post *model.Post) {
|
||||
// send out that a reaction has been added/removed
|
||||
message := model.NewWebSocketEvent(event, "", post.ChannelId, "", nil)
|
||||
reactionJSON, jsonErr := json.Marshal(reaction)
|
||||
if jsonErr != nil {
|
||||
mlog.Warn("Failed to encode reaction to JSON")
|
||||
reactionJSON, err := json.Marshal(reaction)
|
||||
if err != nil {
|
||||
a.Log().Warn("Failed to encode reaction to JSON", mlog.Err(err))
|
||||
}
|
||||
message.Add("reaction", string(reactionJSON))
|
||||
a.Publish(message)
|
||||
|
||||
@@ -90,8 +90,8 @@ func TestGetTopReactionsForTeamSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Server.configStore.SetReadOnlyFF(false)
|
||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
user2Id := th.BasicUser2.Id
|
||||
@@ -261,8 +261,8 @@ func TestGetTopReactionsForUserSince(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Server.configStore.SetReadOnlyFF(false)
|
||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
|
||||
userId := th.BasicUser.Id
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -59,7 +59,7 @@ func (rt *PluginResponseWriter) GenerateResponse() *http.Response {
|
||||
res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode))
|
||||
|
||||
if rt.Len() > 0 {
|
||||
res.Body = ioutil.NopCloser(rt)
|
||||
res.Body = io.NopCloser(rt)
|
||||
} else {
|
||||
res.Body = http.NoBody
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -130,7 +130,7 @@ func testPermissionInheritance(t *testing.T, testCallback func(t *testing.T, th
|
||||
require.NoError(t, e)
|
||||
defer file.Close()
|
||||
|
||||
b, e := ioutil.ReadAll(file)
|
||||
b, e := io.ReadAll(file)
|
||||
require.NoError(t, e)
|
||||
|
||||
r := csv.NewReader(strings.NewReader(string(b)))
|
||||
|
||||
18
app/saml.go
18
app/saml.go
@@ -8,7 +8,7 @@ import (
|
||||
"encoding/pem"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -42,12 +42,12 @@ func (a *App) writeSamlFile(filename string, fileData *multipart.FileHeader) *mo
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
err = a.Srv().configStore.SetFile(filename, data)
|
||||
err = a.Srv().platform.SetConfigFile(filename, data)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func (a *App) AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppEr
|
||||
}
|
||||
|
||||
func (a *App) removeSamlFile(filename string) *model.AppError {
|
||||
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
|
||||
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
|
||||
return model.NewAppError("RemoveSamlFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -171,9 +171,9 @@ func (a *App) RemoveSamlIdpCertificate() *model.AppError {
|
||||
func (a *App) GetSamlCertificateStatus() *model.SamlCertificateStatus {
|
||||
status := &model.SamlCertificateStatus{}
|
||||
|
||||
status.IdpCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.IdpCertificateFile)
|
||||
status.PrivateKeyFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PrivateKeyFile)
|
||||
status.PublicCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PublicCertificateFile)
|
||||
status.IdpCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.IdpCertificateFile)
|
||||
status.PrivateKeyFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PrivateKeyFile)
|
||||
status.PublicCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PublicCertificateFile)
|
||||
|
||||
return status
|
||||
}
|
||||
@@ -212,7 +212,7 @@ func (a *App) FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyXML, err := ioutil.ReadAll(resp.Body)
|
||||
bodyXML, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("FetchSamlMetadataFromIdp", "app.admin.saml.failure_read_response_body_from_idp.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func (a *App) SetSamlIdpCertificateFromMetadata(data []byte) *model.AppError {
|
||||
Bytes: block.Bytes,
|
||||
})
|
||||
|
||||
if err := a.Srv().configStore.SetFile(SamlIdpCertificateName, data); err != nil {
|
||||
if err := a.Srv().platform.SetConfigFile(SamlIdpCertificateName, data); err != nil {
|
||||
return model.NewAppError("SetSamlIdpCertificateFromMetadata", "api.admin.saml.failure_save_idp_certificate_file.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
@@ -33,7 +33,7 @@ const (
|
||||
)
|
||||
|
||||
func (s *Server) DoSecurityUpdateCheck() {
|
||||
if !*s.Config().ServiceSettings.EnableSecurityFixAlert {
|
||||
if !*s.platform.Config().ServiceSettings.EnableSecurityFixAlert {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *Server) DoSecurityUpdateCheck() {
|
||||
v.Set(PropSecurityID, s.TelemetryId())
|
||||
v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber)
|
||||
v.Set(PropSecurityEnterpriseReady, model.BuildEnterpriseReady)
|
||||
v.Set(PropSecurityDatabase, *s.Config().SqlSettings.DriverName)
|
||||
v.Set(PropSecurityDatabase, *s.platform.Config().SqlSettings.DriverName)
|
||||
v.Set(PropSecurityOS, runtime.GOOS)
|
||||
|
||||
if props[model.SystemRanUnitTests] != "" {
|
||||
@@ -91,7 +91,7 @@ func (s *Server) DoSecurityUpdateCheck() {
|
||||
|
||||
var bulletins model.SecurityBulletins
|
||||
if jsonErr := json.NewDecoder(res.Body).Decode(&bulletins); jsonErr != nil {
|
||||
mlog.Error("Failed to decode JSON", mlog.Err(jsonErr))
|
||||
s.Log.Error("Failed to decode JSON", mlog.Err(jsonErr))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func (s *Server) DoSecurityUpdateCheck() {
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resBody.Body)
|
||||
body, err := io.ReadAll(resBody.Body)
|
||||
resBody.Body.Close()
|
||||
if err != nil || resBody.StatusCode != 200 {
|
||||
mlog.Error("Failed to read security bulletin details")
|
||||
|
||||
236
app/server.go
236
app/server.go
@@ -31,7 +31,6 @@ import (
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/email"
|
||||
"github.com/mattermost/mattermost-server/v6/app/featureflag"
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/app/teams"
|
||||
@@ -168,7 +167,6 @@ type Server struct {
|
||||
searchConfigListenerId string
|
||||
searchLicenseListenerId string
|
||||
loggerLicenseListenerId string
|
||||
configStore *configWrapper
|
||||
filestore filestore.FileBackend
|
||||
|
||||
platform *platform.PlatformService
|
||||
@@ -201,11 +199,6 @@ type Server struct {
|
||||
|
||||
tracer *tracing.Tracer
|
||||
|
||||
featureFlagSynchronizer *featureflag.Synchronizer
|
||||
featureFlagStop chan struct{}
|
||||
featureFlagStopped chan struct{}
|
||||
featureFlagSynchronizerMutex sync.Mutex
|
||||
|
||||
products map[string]Product
|
||||
}
|
||||
|
||||
@@ -237,7 +230,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// and has dependency requirements with the previous step.
|
||||
//
|
||||
// Step 1: Config.
|
||||
if s.configStore == nil {
|
||||
if s.platform == nil {
|
||||
innerStore, err := config.NewFileStore("config.json", true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to load config")
|
||||
@@ -247,7 +240,25 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
return nil, errors.Wrap(err, "failed to load config")
|
||||
}
|
||||
|
||||
s.configStore = &configWrapper{srv: s, Store: configStore}
|
||||
platformCfg := platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
Logger: s.Log,
|
||||
StartMetrics: s.startMetrics,
|
||||
Cluster: s.Cluster,
|
||||
}
|
||||
if metricsInterface != nil {
|
||||
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
|
||||
}
|
||||
|
||||
ps, sErr := platform.New(platformCfg)
|
||||
if sErr != nil {
|
||||
return nil, errors.Wrap(sErr, "failed to initialize platform")
|
||||
}
|
||||
s.platform = ps
|
||||
|
||||
if s.licenseValue.Load() != nil {
|
||||
ps.SetLicense(s.licenseValue.Load().(*model.License)) // in case license is set in server options
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Logging
|
||||
@@ -255,7 +266,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
mlog.Error("Could not initiate logging", mlog.Err(err))
|
||||
}
|
||||
|
||||
subpath, err := utils.GetSubpathFromConfig(s.Config())
|
||||
subpath, err := utils.GetSubpathFromConfig(s.platform.Config())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
|
||||
}
|
||||
@@ -264,12 +275,12 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// This is called after initLogging() to avoid a race condition.
|
||||
mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version()))
|
||||
|
||||
s.httpService = httpservice.MakeHTTPService(s)
|
||||
s.httpService = httpservice.MakeHTTPService(s.platform)
|
||||
|
||||
// Step 3: Search Engine
|
||||
// Depends on Step 1 (config).
|
||||
searchEngine := searchengine.NewBroker(s.Config())
|
||||
bleveEngine := bleveengine.NewBleveEngine(s.Config())
|
||||
searchEngine := searchengine.NewBroker(s.platform.Config())
|
||||
bleveEngine := bleveengine.NewBleveEngine(s.platform.Config())
|
||||
if err := bleveEngine.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -280,22 +291,6 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// Depends on step 3 (s.SearchEngine must be non-nil)
|
||||
s.initEnterprise()
|
||||
|
||||
platformCfg := platform.ServiceConfig{
|
||||
ConfigStore: s.configStore.Store,
|
||||
Logger: s.Log,
|
||||
StartMetrics: s.startMetrics,
|
||||
Cluster: s.Cluster,
|
||||
}
|
||||
if metricsInterface != nil {
|
||||
platformCfg.Metrics = metricsInterface(s)
|
||||
}
|
||||
|
||||
ps, sErr := platform.New(platformCfg)
|
||||
if sErr != nil {
|
||||
return nil, errors.Wrap(sErr, "failed to initialize platform")
|
||||
}
|
||||
s.platform = ps
|
||||
|
||||
// Step 5: Cache provider.
|
||||
// At the moment we only have this implementation
|
||||
// in the future the cache provider will be built based on the loaded config
|
||||
@@ -308,7 +303,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// Depends on Step 1 (config), 4 (metrics, cluster) and 5 (cacheProvider).
|
||||
if s.newStore == nil {
|
||||
s.newStore = func() (store.Store, error) {
|
||||
s.sqlStore = sqlstore.New(s.Config().SqlSettings, s.GetMetrics())
|
||||
s.sqlStore = sqlstore.New(s.platform.Config().SqlSettings, s.GetMetrics())
|
||||
|
||||
lcl, err2 := localcachelayer.NewLocalCacheLayer(
|
||||
retrylayer.New(s.sqlStore),
|
||||
@@ -323,10 +318,10 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
searchStore := searchlayer.NewSearchLayer(
|
||||
lcl,
|
||||
s.SearchEngine,
|
||||
s.Config(),
|
||||
s.platform.Config(),
|
||||
)
|
||||
|
||||
s.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
||||
s.platform.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
||||
searchStore.UpdateConfig(cfg)
|
||||
})
|
||||
|
||||
@@ -352,7 +347,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
UserStore: s.Store.User(),
|
||||
SessionStore: s.Store.Session(),
|
||||
OAuthStore: s.Store.OAuth(),
|
||||
ConfigFn: s.Config,
|
||||
ConfigFn: s.platform.Config,
|
||||
Metrics: s.GetMetrics(),
|
||||
Cluster: s.Cluster,
|
||||
LicenseFn: s.License,
|
||||
@@ -376,9 +371,9 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
}
|
||||
|
||||
license := s.License()
|
||||
insecure := s.Config().ServiceSettings.EnableInsecureOutgoingConnections
|
||||
insecure := s.platform.Config().ServiceSettings.EnableInsecureOutgoingConnections
|
||||
// Step 7: Initialize filestore
|
||||
backend, err := filestore.NewFileBackend(s.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
|
||||
backend, err := filestore.NewFileBackend(s.platform.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to initialize filebackend")
|
||||
}
|
||||
@@ -398,7 +393,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
GroupStore: s.Store.Group(),
|
||||
Users: s.userService,
|
||||
WebHub: s,
|
||||
ConfigFn: s.Config,
|
||||
ConfigFn: s.platform.Config,
|
||||
LicenseFn: s.License,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -410,7 +405,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
serviceMap := map[ServiceKey]any{
|
||||
ChannelKey: &channelsWrapper{srv: s},
|
||||
ConfigKey: s.configStore,
|
||||
ConfigKey: s.platform,
|
||||
LicenseKey: s.licenseWrapper,
|
||||
FilestoreKey: s.filestore,
|
||||
FileInfoStoreKey: &fileInfoWrapper{srv: s},
|
||||
@@ -441,7 +436,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// below this. Otherwise, please add it to Channels struct in app/channels.go.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry {
|
||||
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry {
|
||||
if strings.Contains(SentryDSN, "placeholder") {
|
||||
mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.")
|
||||
} else {
|
||||
@@ -468,7 +463,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if *s.Config().ServiceSettings.EnableOpenTracing {
|
||||
if *s.platform.Config().ServiceSettings.EnableOpenTracing {
|
||||
tracer, err2 := tracing.New()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
@@ -496,7 +491,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
s.createPushNotificationsHub(request.EmptyContext(s.GetLogger()))
|
||||
|
||||
if err2 := i18n.InitTranslations(*s.Config().LocalizationSettings.DefaultServerLocale, *s.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
|
||||
if err2 := i18n.InitTranslations(*s.platform.Config().LocalizationSettings.DefaultServerLocale, *s.platform.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
|
||||
return nil, errors.Wrapf(err2, "unable to load Mattermost translation files")
|
||||
}
|
||||
|
||||
@@ -515,7 +510,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
})
|
||||
s.htmlTemplateWatcher = htmlTemplateWatcher
|
||||
|
||||
s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) {
|
||||
s.configListenerId = s.platform.AddConfigListener(func(_, _ *model.Config) {
|
||||
ch := s.Channels()
|
||||
ch.regenerateClientConfig()
|
||||
|
||||
@@ -544,9 +539,10 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
})
|
||||
|
||||
s.telemetryService = telemetry.New(New(ServerConnector(s.Channels())), s.Store, s.SearchEngine, s.Log)
|
||||
s.platform.SetTelemetryId(s.TelemetryId()) // TODO: move this into platform once telemetry service moved to platform.
|
||||
|
||||
emailService, err := email.NewService(email.ServiceConfig{
|
||||
ConfigFn: s.Config,
|
||||
ConfigFn: s.platform.Config,
|
||||
LicenseFn: s.License,
|
||||
GoFn: s.Go,
|
||||
TemplatesContainer: s.TemplatesContainer(),
|
||||
@@ -558,7 +554,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
}
|
||||
s.EmailService = emailService
|
||||
|
||||
s.setupFeatureFlags()
|
||||
s.platform.SetupFeatureFlags()
|
||||
|
||||
s.initJobs()
|
||||
|
||||
@@ -567,7 +563,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
if s.Jobs != nil {
|
||||
s.Jobs.HandleClusterLeaderChange(s.IsLeader())
|
||||
}
|
||||
s.setupFeatureFlags()
|
||||
s.platform.SetupFeatureFlags()
|
||||
})
|
||||
|
||||
// If configured with a subpath, redirect 404s at the root back into the subpath.
|
||||
@@ -578,12 +574,12 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
})
|
||||
}
|
||||
|
||||
if _, err = url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil {
|
||||
if _, err = url.ParseRequestURI(*s.platform.Config().ServiceSettings.SiteURL); err != nil {
|
||||
mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: https://docs.mattermost.com/configure/configuration-settings.html#site-url")
|
||||
}
|
||||
|
||||
// Start email batching because it's not like the other jobs
|
||||
s.AddConfigListener(func(_, _ *model.Config) {
|
||||
s.platform.AddConfigListener(func(_, _ *model.Config) {
|
||||
s.EmailService.InitEmailBatching()
|
||||
})
|
||||
|
||||
@@ -604,7 +600,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
pwd, _ := os.Getwd()
|
||||
mlog.Info("Printing current working", mlog.String("directory", pwd))
|
||||
mlog.Info("Loaded config", mlog.String("source", s.configStore.String()))
|
||||
mlog.Info("Loaded config", mlog.String("source", s.platform.DescribeConfig()))
|
||||
|
||||
allowAdvancedLogging := license != nil && *license.Features.AdvancedLogging
|
||||
|
||||
@@ -626,7 +622,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
|
||||
// Enable developer settings if this is a "dev" build
|
||||
if model.BuildNumber == "dev" {
|
||||
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
|
||||
}
|
||||
|
||||
if s.startMetrics {
|
||||
@@ -649,13 +645,13 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
}
|
||||
})
|
||||
|
||||
s.SearchEngine.UpdateConfig(s.Config())
|
||||
s.SearchEngine.UpdateConfig(s.platform.Config())
|
||||
searchConfigListenerId, searchLicenseListenerId := s.StartSearchEngine()
|
||||
s.searchConfigListenerId = searchConfigListenerId
|
||||
s.searchLicenseListenerId = searchLicenseListenerId
|
||||
|
||||
// if enabled - perform initial product notices fetch
|
||||
if *s.Config().AnnouncementSettings.AdminNoticesEnabled || *s.Config().AnnouncementSettings.UserNoticesEnabled {
|
||||
if *s.platform.Config().AnnouncementSettings.AdminNoticesEnabled || *s.platform.Config().AnnouncementSettings.UserNoticesEnabled {
|
||||
go func() {
|
||||
appInstance := New(ServerConnector(s.Channels()))
|
||||
if err := appInstance.UpdateProductNotices(); err != nil {
|
||||
@@ -668,7 +664,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
s.AddConfigListener(func(old, new *model.Config) {
|
||||
s.platform.AddConfigListener(func(old, new *model.Config) {
|
||||
appInstance := New(ServerConnector(s.Channels()))
|
||||
if *old.GuestAccountsSettings.Enable && !*new.GuestAccountsSettings.Enable {
|
||||
c := request.EmptyContext(s.GetLogger())
|
||||
@@ -679,7 +675,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
})
|
||||
|
||||
// Disable active guest accounts on first run if guest accounts are disabled
|
||||
if !*s.Config().GuestAccountsSettings.Enable {
|
||||
if !*s.platform.Config().GuestAccountsSettings.Enable {
|
||||
appInstance := New(ServerConnector(s.Channels()))
|
||||
c := request.EmptyContext(s.GetLogger())
|
||||
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
|
||||
@@ -703,7 +699,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
s.initPostMetadata()
|
||||
|
||||
// Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy)
|
||||
s.AddConfigListener(func(oldCfg, newCfg *model.Config) {
|
||||
s.platform.AddConfigListener(func(oldCfg, newCfg *model.Config) {
|
||||
if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) ||
|
||||
(oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) ||
|
||||
(oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) ||
|
||||
@@ -755,18 +751,18 @@ func (s *Server) runJobs() {
|
||||
complianceI.StartComplianceDailyJob()
|
||||
}
|
||||
|
||||
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
|
||||
if err := s.Jobs.StartWorkers(); err != nil {
|
||||
mlog.Error("Failed to start job server workers", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
|
||||
if err := s.Jobs.StartSchedulers(); err != nil {
|
||||
mlog.Error("Failed to start job server schedulers", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
if *s.Config().ServiceSettings.EnableAWSMetering {
|
||||
if *s.platform.Config().ServiceSettings.EnableAWSMetering {
|
||||
runReportToAWSMeterJob(s)
|
||||
}
|
||||
}
|
||||
@@ -786,7 +782,7 @@ func (s *Server) Channels() *Channels {
|
||||
// Return Database type (postgres or mysql) and current version of the schema
|
||||
func (s *Server) DatabaseTypeAndSchemaVersion() (string, string) {
|
||||
schemaVersion, _ := s.Store.GetDBSchemaVersion()
|
||||
return *s.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
|
||||
return *s.platform.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
|
||||
}
|
||||
|
||||
// initLogging initializes and configures the logger(s). This may be called more than once.
|
||||
@@ -809,7 +805,7 @@ func (s *Server) initLogging() error {
|
||||
s.NotificationsLog = l.With(mlog.String("logSource", "notifications"))
|
||||
}
|
||||
|
||||
if err := s.configureLogger("logging", s.Log, &s.Config().LogSettings, s.configStore.Store, config.GetLogFileLocation); err != nil {
|
||||
if err := s.platform.ConfigureLogger("logging", s.Log, &s.platform.Config().LogSettings, config.GetLogFileLocation); err != nil {
|
||||
// if the config is locked then a unit test has already configured and locked the logger; not an error.
|
||||
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
||||
// revert to default logger if the config is invalid
|
||||
@@ -824,8 +820,8 @@ func (s *Server) initLogging() error {
|
||||
// Use the app logger as the global logger (eventually remove all instances of global logging).
|
||||
mlog.InitGlobalLogger(s.Log)
|
||||
|
||||
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings)
|
||||
if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore.Store, config.GetNotificationsLogFileLocation); err != nil {
|
||||
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.platform.Config().NotificationLogSettings)
|
||||
if err := s.platform.ConfigureLogger("notification logging", s.NotificationsLog, notificationLogSettings, config.GetNotificationsLogFileLocation); err != nil {
|
||||
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
||||
mlog.Error("Error configuring notification logger", mlog.Err(err))
|
||||
return err
|
||||
@@ -834,33 +830,6 @@ func (s *Server) initLogging() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureLogger applies the specified configuration to a logger.
|
||||
func (s *Server) configureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, configStore *config.Store, getPath func(string) string) error {
|
||||
// Advanced logging is E20 only, however logging must be initialized before the license
|
||||
// file is loaded. If no valid E20 license exists then advanced logging will be
|
||||
// shutdown once license is loaded/checked.
|
||||
var err error
|
||||
dsn := *logSettings.AdvancedLoggingConfig
|
||||
var logConfigSrc config.LogConfigSrc
|
||||
if dsn != "" {
|
||||
logConfigSrc, err = config.NewLogConfigSrc(dsn, configStore)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config source for %s, %w", name, err)
|
||||
}
|
||||
mlog.Info("Loaded configuration for "+name, mlog.String("source", dsn))
|
||||
}
|
||||
|
||||
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid config source for %s, %w", name, err)
|
||||
}
|
||||
|
||||
if err := logger.ConfigureTargets(cfg, nil); err != nil {
|
||||
return fmt.Errorf("invalid config for %s, %w", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeUnlicensedLogTargets removes any unlicensed log target types.
|
||||
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
|
||||
if license != nil && *license.Features.AdvancedLogging {
|
||||
@@ -895,7 +864,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
|
||||
}
|
||||
|
||||
// Config check
|
||||
if !*s.Config().ExperimentalSettings.EnableRemoteClusterService {
|
||||
if !*s.platform.Config().ExperimentalSettings.EnableRemoteClusterService {
|
||||
mlog.Debug("Remote Cluster Service disabled via config")
|
||||
return nil
|
||||
}
|
||||
@@ -924,7 +893,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
|
||||
}
|
||||
|
||||
// Config check
|
||||
if !*s.Config().ExperimentalSettings.EnableSharedChannels {
|
||||
if !*s.platform.Config().ExperimentalSettings.EnableSharedChannels {
|
||||
mlog.Debug("Shared Channels Service disabled via config")
|
||||
return nil
|
||||
}
|
||||
@@ -1029,14 +998,16 @@ func (s *Server) Shutdown() {
|
||||
|
||||
s.WaitForGoroutines()
|
||||
|
||||
s.RemoveConfigListener(s.configListenerId)
|
||||
s.platform.RemoveConfigListener(s.configListenerId)
|
||||
s.stopSearchEngine()
|
||||
|
||||
s.Audit.Shutdown()
|
||||
|
||||
s.stopFeatureFlagUpdateJob()
|
||||
s.platform.StopFeatureFlagUpdateJob()
|
||||
|
||||
s.configStore.Close()
|
||||
if err = s.platform.ShutdownConfig(); err != nil {
|
||||
s.Log.Warn("Failed to shut down config store", mlog.Err(err))
|
||||
}
|
||||
|
||||
if s.Cluster != nil {
|
||||
s.Cluster.StopInterNodeCommunication()
|
||||
@@ -1239,23 +1210,23 @@ func (s *Server) Start() error {
|
||||
|
||||
s.checkPushNotificationServerURL()
|
||||
|
||||
s.ReloadConfig()
|
||||
s.platform.ReloadConfig()
|
||||
|
||||
mlog.Info("Starting Server...")
|
||||
|
||||
var handler http.Handler = s.RootRouter
|
||||
|
||||
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
|
||||
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
|
||||
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||
Repanic: true,
|
||||
})
|
||||
handler = sentryHandler.Handle(handler)
|
||||
}
|
||||
|
||||
if allowedOrigins := *s.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
|
||||
exposedCorsHeaders := *s.Config().ServiceSettings.CorsExposedHeaders
|
||||
allowCredentials := *s.Config().ServiceSettings.CorsAllowCredentials
|
||||
debug := *s.Config().ServiceSettings.CorsDebug
|
||||
if allowedOrigins := *s.platform.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
|
||||
exposedCorsHeaders := *s.platform.Config().ServiceSettings.CorsExposedHeaders
|
||||
allowCredentials := *s.platform.Config().ServiceSettings.CorsAllowCredentials
|
||||
debug := *s.platform.Config().ServiceSettings.CorsDebug
|
||||
corsWrapper := cors.New(cors.Options{
|
||||
AllowedOrigins: strings.Fields(allowedOrigins),
|
||||
AllowedMethods: corsAllowedMethods,
|
||||
@@ -1274,10 +1245,10 @@ func (s *Server) Start() error {
|
||||
handler = corsWrapper.Handler(handler)
|
||||
}
|
||||
|
||||
if *s.Config().RateLimitSettings.Enable {
|
||||
if *s.platform.Config().RateLimitSettings.Enable {
|
||||
mlog.Info("RateLimiter is enabled")
|
||||
|
||||
rateLimiter, err2 := NewRateLimiter(&s.Config().RateLimitSettings, s.Config().ServiceSettings.TrustedProxyIPHeader)
|
||||
rateLimiter, err2 := NewRateLimiter(&s.platform.Config().RateLimitSettings, s.platform.Config().ServiceSettings.TrustedProxyIPHeader)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
@@ -1292,15 +1263,15 @@ func (s *Server) Start() error {
|
||||
|
||||
s.Server = &http.Server{
|
||||
Handler: handler,
|
||||
ReadTimeout: time.Duration(*s.Config().ServiceSettings.ReadTimeout) * time.Second,
|
||||
WriteTimeout: time.Duration(*s.Config().ServiceSettings.WriteTimeout) * time.Second,
|
||||
IdleTimeout: time.Duration(*s.Config().ServiceSettings.IdleTimeout) * time.Second,
|
||||
ReadTimeout: time.Duration(*s.platform.Config().ServiceSettings.ReadTimeout) * time.Second,
|
||||
WriteTimeout: time.Duration(*s.platform.Config().ServiceSettings.WriteTimeout) * time.Second,
|
||||
IdleTimeout: time.Duration(*s.platform.Config().ServiceSettings.IdleTimeout) * time.Second,
|
||||
ErrorLog: errStdLog,
|
||||
}
|
||||
|
||||
addr := *s.Config().ServiceSettings.ListenAddress
|
||||
addr := *s.platform.Config().ServiceSettings.ListenAddress
|
||||
if addr == "" {
|
||||
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||
addr = ":https"
|
||||
} else {
|
||||
addr = ":http"
|
||||
@@ -1317,11 +1288,11 @@ func (s *Server) Start() error {
|
||||
mlog.Info(logListeningPort, mlog.String("address", listener.Addr().String()))
|
||||
|
||||
m := &autocert.Manager{
|
||||
Cache: autocert.DirCache(*s.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
|
||||
Cache: autocert.DirCache(*s.platform.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
|
||||
Prompt: autocert.AcceptTOS,
|
||||
}
|
||||
|
||||
if *s.Config().ServiceSettings.Forward80To443 {
|
||||
if *s.platform.Config().ServiceSettings.Forward80To443 {
|
||||
if host, port, err := net.SplitHostPort(addr); err != nil {
|
||||
mlog.Error("Unable to setup forwarding", mlog.Err(err))
|
||||
} else if port != "443" {
|
||||
@@ -1329,7 +1300,7 @@ func (s *Server) Start() error {
|
||||
} else {
|
||||
httpListenAddress := net.JoinHostPort(host, "http")
|
||||
|
||||
if *s.Config().ServiceSettings.UseLetsEncrypt {
|
||||
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
|
||||
server := &http.Server{
|
||||
Addr: httpListenAddress,
|
||||
Handler: m.HTTPHandler(nil),
|
||||
@@ -1353,21 +1324,21 @@ func (s *Server) Start() error {
|
||||
}()
|
||||
}
|
||||
}
|
||||
} else if *s.Config().ServiceSettings.UseLetsEncrypt {
|
||||
} else if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
|
||||
return errors.New(i18n.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt"))
|
||||
}
|
||||
|
||||
s.didFinishListen = make(chan struct{})
|
||||
go func() {
|
||||
var err error
|
||||
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
PreferServerCipherSuites: true,
|
||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||
}
|
||||
|
||||
switch *s.Config().ServiceSettings.TLSMinVer {
|
||||
switch *s.platform.Config().ServiceSettings.TLSMinVer {
|
||||
case "1.0":
|
||||
tlsConfig.MinVersion = tls.VersionTLS10
|
||||
case "1.1":
|
||||
@@ -1385,11 +1356,11 @@ func (s *Server) Start() error {
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
}
|
||||
|
||||
if len(s.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
|
||||
if len(s.platform.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
|
||||
tlsConfig.CipherSuites = defaultCiphers
|
||||
} else {
|
||||
var cipherSuites []uint16
|
||||
for _, cipher := range s.Config().ServiceSettings.TLSOverwriteCiphers {
|
||||
for _, cipher := range s.platform.Config().ServiceSettings.TLSOverwriteCiphers {
|
||||
value, ok := model.ServerTLSSupportedCiphers[cipher]
|
||||
|
||||
if !ok {
|
||||
@@ -1411,12 +1382,12 @@ func (s *Server) Start() error {
|
||||
certFile := ""
|
||||
keyFile := ""
|
||||
|
||||
if *s.Config().ServiceSettings.UseLetsEncrypt {
|
||||
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
|
||||
tlsConfig.GetCertificate = m.GetCertificate
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
} else {
|
||||
certFile = *s.Config().ServiceSettings.TLSCertFile
|
||||
keyFile = *s.Config().ServiceSettings.TLSKeyFile
|
||||
certFile = *s.platform.Config().ServiceSettings.TLSCertFile
|
||||
keyFile = *s.platform.Config().ServiceSettings.TLSKeyFile
|
||||
}
|
||||
|
||||
s.Server.TLSConfig = tlsConfig
|
||||
@@ -1433,7 +1404,7 @@ func (s *Server) Start() error {
|
||||
close(s.didFinishListen)
|
||||
}()
|
||||
|
||||
if *s.Config().ServiceSettings.EnableLocalMode {
|
||||
if *s.platform.Config().ServiceSettings.EnableLocalMode {
|
||||
if err := s.startLocalModeServer(); err != nil {
|
||||
mlog.Critical(err.Error())
|
||||
}
|
||||
@@ -1451,7 +1422,7 @@ func (s *Server) startLocalModeServer() error {
|
||||
Handler: s.LocalRouter,
|
||||
}
|
||||
|
||||
socket := *s.configStore.Get().ServiceSettings.LocalModeSocketLocation
|
||||
socket := *s.platform.Config().ServiceSettings.LocalModeSocketLocation
|
||||
if err := os.RemoveAll(socket); err != nil {
|
||||
return errors.Wrapf(err, i18n.T("api.server.start_server.starting.critical"), err)
|
||||
}
|
||||
@@ -1495,7 +1466,7 @@ func (a *App) OriginChecker() func(*http.Request) bool {
|
||||
}
|
||||
|
||||
func (s *Server) checkPushNotificationServerURL() {
|
||||
notificationServer := *s.Config().EmailSettings.PushNotificationServer
|
||||
notificationServer := *s.platform.Config().EmailSettings.PushNotificationServer
|
||||
if strings.HasPrefix(notificationServer, "http://") {
|
||||
mlog.Warn("Your push notification server is configured with HTTP. For improved security, update to HTTPS in your configuration.")
|
||||
}
|
||||
@@ -1563,7 +1534,7 @@ func runReportToAWSMeterJob(s *Server) {
|
||||
}
|
||||
|
||||
func doReportUsageToAWSMeteringService(s *Server) {
|
||||
awsMeter := awsmeter.New(s.Store, s.Config())
|
||||
awsMeter := awsmeter.New(s.Store, s.platform.Config())
|
||||
if awsMeter == nil {
|
||||
mlog.Error("Cannot obtain instance of AWS Metering Service.")
|
||||
return
|
||||
@@ -1604,12 +1575,12 @@ func doSessionCleanup(s *Server) {
|
||||
}
|
||||
|
||||
func doJobsCleanup(s *Server) {
|
||||
if *s.Config().JobSettings.CleanupJobsThresholdDays < 0 {
|
||||
if *s.platform.Config().JobSettings.CleanupJobsThresholdDays < 0 {
|
||||
return
|
||||
}
|
||||
mlog.Debug("Cleaning up jobs store.")
|
||||
|
||||
dur := time.Duration(*s.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
|
||||
dur := time.Duration(*s.platform.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
|
||||
expiry := model.GetMillisForTime(time.Now().Add(-dur))
|
||||
err := s.Store.Job().Cleanup(expiry, jobsCleanupBatchSize)
|
||||
if err != nil {
|
||||
@@ -1618,12 +1589,12 @@ func doJobsCleanup(s *Server) {
|
||||
}
|
||||
|
||||
func doConfigCleanup(s *Server) {
|
||||
if *s.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.ConfigStore().Store.String()) {
|
||||
if *s.platform.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.platform.DescribeConfig()) {
|
||||
return
|
||||
}
|
||||
mlog.Info("Cleaning up configuration store.")
|
||||
|
||||
if err := s.ConfigStore().Store.CleanUp(); err != nil {
|
||||
if err := s.platform.CleanUpConfig(); err != nil {
|
||||
mlog.Warn("Error while cleaning up configurations", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
@@ -1654,7 +1625,7 @@ func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, lice
|
||||
if name == "" {
|
||||
name = user.Username
|
||||
}
|
||||
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
|
||||
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.platform.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
|
||||
mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email), mlog.Err(err))
|
||||
countNotOks++
|
||||
}
|
||||
@@ -1735,7 +1706,7 @@ func (s *Server) doLicenseExpirationCheck() {
|
||||
|
||||
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
|
||||
s.Go(func() {
|
||||
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.Config().ServiceSettings.SiteURL); err != nil {
|
||||
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.platform.Config().ServiceSettings.SiteURL); err != nil {
|
||||
mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err))
|
||||
}
|
||||
})
|
||||
@@ -1765,7 +1736,7 @@ func (s *Server) StartSearchEngine() (string, string) {
|
||||
})
|
||||
}
|
||||
|
||||
configListenerId := s.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
configListenerId := s.platform.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
if s.SearchEngine == nil {
|
||||
return
|
||||
}
|
||||
@@ -1824,7 +1795,7 @@ func (s *Server) StartSearchEngine() (string, string) {
|
||||
}
|
||||
|
||||
func (s *Server) stopSearchEngine() {
|
||||
s.RemoveConfigListener(s.searchConfigListenerId)
|
||||
s.platform.RemoveConfigListener(s.searchConfigListenerId)
|
||||
s.RemoveLicenseListener(s.searchLicenseListenerId)
|
||||
if s.SearchEngine != nil && s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
|
||||
s.SearchEngine.ElasticsearchEngine.Stop()
|
||||
@@ -1858,7 +1829,7 @@ func (ch *Channels) ClientConfigHash() string {
|
||||
}
|
||||
|
||||
func (s *Server) initJobs() {
|
||||
s.Jobs = jobs.NewJobServer(s, s.Store, s.GetMetrics())
|
||||
s.Jobs = jobs.NewJobServer(s.platform, s.Store, s.GetMetrics())
|
||||
|
||||
if jobsDataRetentionJobInterface != nil {
|
||||
builder := jobsDataRetentionJobInterface(s)
|
||||
@@ -2031,7 +2002,7 @@ func (s *Server) SetSharedChannelSyncService(sharedChannelService SharedChannelS
|
||||
}
|
||||
|
||||
func (s *Server) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
|
||||
if *s.Config().FileSettings.DriverName == "" {
|
||||
if *s.platform.Config().FileSettings.DriverName == "" {
|
||||
img, appErr := s.GetDefaultProfileImage(user)
|
||||
if appErr != nil {
|
||||
return nil, false, appErr
|
||||
@@ -2141,3 +2112,8 @@ func (a *App) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.App
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// Expose platform service from server, this should be replaced with server itself in time.
|
||||
func (s *Server) Platform() *platform.PlatformService {
|
||||
return s.platform
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ const inactivityEmailSent = "INACTIVITY"
|
||||
|
||||
func (s *Server) doInactivityCheck() {
|
||||
|
||||
if *s.Config().ServiceSettings.EnableDeveloper {
|
||||
if *s.platform.Config().ServiceSettings.EnableDeveloper {
|
||||
mlog.Info("No activity check because developer mode is enabled")
|
||||
return
|
||||
}
|
||||
|
||||
if !*s.Config().EmailSettings.EnableInactivityEmail {
|
||||
if !*s.platform.Config().EmailSettings.EnableInactivityEmail {
|
||||
mlog.Info("No activity check because EnableInactivityEmail is false")
|
||||
return
|
||||
}
|
||||
|
||||
if !s.Config().FeatureFlags.EnableInactivityCheckJob {
|
||||
if !s.platform.Config().FeatureFlags.EnableInactivityCheckJob {
|
||||
mlog.Info("No activity check because EnableInactivityCheckJob feature flag is disabled")
|
||||
return
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (s *Server) doInactivityCheck() {
|
||||
}
|
||||
|
||||
func (s *Server) takeInactivityAction() {
|
||||
siteURL := *s.Config().ServiceSettings.SiteURL
|
||||
siteURL := *s.platform.Config().ServiceSettings.SiteURL
|
||||
if siteURL == "" {
|
||||
mlog.Warn("No SiteURL configured")
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (s *Server) License() *model.License {
|
||||
license, _ := s.licenseValue.Load().(*model.License)
|
||||
return license
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
||||
@@ -83,54 +84,70 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) {
|
||||
s, err := NewServer(func(server *Server) error {
|
||||
configStore := config.NewTestMemoryStore()
|
||||
configStore.Set(&cfg)
|
||||
server.configStore = &configWrapper{srv: server, Store: configStore}
|
||||
var err error
|
||||
server.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer s.Shutdown()
|
||||
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
|
||||
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
|
||||
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
|
||||
})
|
||||
|
||||
t.Run("Read Replicas With License", func(t *testing.T) {
|
||||
s, err := NewServer(func(server *Server) error {
|
||||
configStore := config.NewTestMemoryStore()
|
||||
configStore.Set(&cfg)
|
||||
server.configStore = &configWrapper{srv: server, Store: configStore}
|
||||
var err error
|
||||
server.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
server.licenseValue.Store(model.NewTestLicense())
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer s.Shutdown()
|
||||
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
|
||||
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
|
||||
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
|
||||
})
|
||||
|
||||
t.Run("Search Replicas with no License", func(t *testing.T) {
|
||||
s, err := NewServer(func(server *Server) error {
|
||||
configStore := config.NewTestMemoryStore()
|
||||
configStore.Set(&cfg)
|
||||
server.configStore = &configWrapper{srv: server, Store: configStore}
|
||||
var err error
|
||||
server.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer s.Shutdown()
|
||||
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
|
||||
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
|
||||
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
|
||||
})
|
||||
|
||||
t.Run("Search Replicas With License", func(t *testing.T) {
|
||||
s, err := NewServer(func(server *Server) error {
|
||||
configStore := config.NewTestMemoryStore()
|
||||
configStore.Set(&cfg)
|
||||
server.configStore = &configWrapper{srv: server, Store: configStore}
|
||||
var err error
|
||||
server.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: configStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
server.licenseValue.Store(model.NewTestLicense())
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer s.Shutdown()
|
||||
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
|
||||
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
|
||||
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -143,7 +160,7 @@ func TestStartServerPortUnavailable(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Attempt to listen on the port used above.
|
||||
s.UpdateConfig(func(cfg *model.Config) {
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.ListenAddress = listener.Addr().String()
|
||||
})
|
||||
|
||||
@@ -168,8 +185,12 @@ func TestStartServerNoS3Bucket(t *testing.T) {
|
||||
s, err := NewServer(func(server *Server) error {
|
||||
configStore, _ := config.NewFileStore("config.json", true)
|
||||
store, _ := config.NewStoreFromBacking(configStore, nil, false)
|
||||
server.configStore = &configWrapper{srv: server, Store: store}
|
||||
server.UpdateConfig(func(cfg *model.Config) {
|
||||
var err error
|
||||
server.platform, err = platform.New(platform.ServiceConfig{
|
||||
ConfigStore: store,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
server.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.FileSettings = model.FileSettings{
|
||||
DriverName: model.NewString(model.ImageDriverS3),
|
||||
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
|
||||
@@ -393,7 +414,7 @@ func TestPanicLog(t *testing.T) {
|
||||
})
|
||||
|
||||
testDir, _ := fileutils.FindDir("tests")
|
||||
s.UpdateConfig(func(cfg *model.Config) {
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.ServiceSettings.ListenAddress = ":0"
|
||||
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
|
||||
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
|
||||
|
||||
@@ -77,9 +77,9 @@ func setCollapsePreference(a *app.App, args *model.CommandArgs, isCollapse bool)
|
||||
|
||||
socketMessage := model.NewWebSocketEvent(model.WebsocketEventPreferenceChanged, "", "", args.UserId, nil)
|
||||
|
||||
prefJSON, jsonErr := json.Marshal(pref)
|
||||
if jsonErr != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.marshal_error") + jsonErr.Error(), ResponseType: model.CommandResponseTypeEphemeral}
|
||||
prefJSON, err := json.Marshal(pref)
|
||||
if err != nil {
|
||||
return &model.CommandResponse{Text: args.T("api.marshal_error") + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}
|
||||
}
|
||||
socketMessage.Add("preference", string(prefJSON))
|
||||
a.Publish(socketMessage)
|
||||
|
||||
@@ -570,7 +570,7 @@ func (*LoadTestProvider) JsonCommand(a *app.App, c request.CTX, args *model.Comm
|
||||
|
||||
var post model.Post
|
||||
if jsonErr := json.NewDecoder(r.Body).Decode(&post); jsonErr != nil {
|
||||
return &model.CommandResponse{Text: "Unable to decode post", ResponseType: model.CommandResponseTypeEphemeral}, errors.Errorf("could not decode post from json")
|
||||
return &model.CommandResponse{Text: "Unable to decode post", ResponseType: model.CommandResponseTypeEphemeral}, errors.Wrapf(jsonErr, "could not decode post from json")
|
||||
}
|
||||
post.ChannelId = args.ChannelId
|
||||
post.UserId = args.UserId
|
||||
|
||||
@@ -6,7 +6,6 @@ package slashcommands
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -42,7 +41,7 @@ type TestHelper struct {
|
||||
}
|
||||
|
||||
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, tb testing.TB, configSet func(*model.Config)) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "apptest")
|
||||
tempWorkspace, err := os.MkdirTemp("", "apptest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ func (a *App) AddStatusCache(status *model.Status) {
|
||||
a.AddStatusCacheSkipClusterSend(status)
|
||||
|
||||
if a.Cluster() != nil {
|
||||
statusJSON, jsonErr := json.Marshal(status)
|
||||
if jsonErr != nil {
|
||||
mlog.Warn("Failed to encode status to JSON")
|
||||
statusJSON, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
a.Log().Warn("Failed to encode status to JSON", mlog.Err(err))
|
||||
}
|
||||
msg := &model.ClusterMessage{
|
||||
Event: model.ClusterEventUpdateStatus,
|
||||
@@ -456,20 +456,20 @@ func (a *App) GetCustomStatus(userID string) (*model.CustomStatus, *model.AppErr
|
||||
func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
|
||||
var newRCS model.RecentCustomStatuses
|
||||
|
||||
pref, err := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if err != nil || pref.Value == "" {
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if appErr != nil || pref.Value == "" {
|
||||
newRCS = model.RecentCustomStatuses{*status}
|
||||
} else {
|
||||
var existingRCS model.RecentCustomStatuses
|
||||
if jsonErr := json.Unmarshal([]byte(pref.Value), &existingRCS); jsonErr != nil {
|
||||
return model.NewAppError("addRecentCustomStatus", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
|
||||
if err := json.Unmarshal([]byte(pref.Value), &existingRCS); err != nil {
|
||||
return model.NewAppError("addRecentCustomStatus", "api.unmarshal_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
newRCS = existingRCS.Add(status)
|
||||
}
|
||||
|
||||
newRCSJSON, jsonErr := json.Marshal(newRCS)
|
||||
if jsonErr != nil {
|
||||
return model.NewAppError("addRecentCustomStatus", "api.marshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
|
||||
newRCSJSON, err := json.Marshal(newRCS)
|
||||
if err != nil {
|
||||
return model.NewAppError("addRecentCustomStatus", "api.marshal_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
pref = &model.Preference{
|
||||
UserId: userID,
|
||||
@@ -477,17 +477,17 @@ func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *
|
||||
Name: model.PreferenceNameRecentCustomStatuses,
|
||||
Value: string(newRCSJSON),
|
||||
}
|
||||
if err := a.UpdatePreferences(userID, model.Preferences{*pref}); err != nil {
|
||||
return err
|
||||
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
|
||||
pref, err := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if err != nil {
|
||||
return err
|
||||
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
if pref.Value == "" {
|
||||
@@ -495,26 +495,26 @@ func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus
|
||||
}
|
||||
|
||||
var existingRCS model.RecentCustomStatuses
|
||||
if jsonErr := json.Unmarshal([]byte(pref.Value), &existingRCS); jsonErr != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
|
||||
if err := json.Unmarshal([]byte(pref.Value), &existingRCS); err != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.unmarshal_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
if ok, err := existingRCS.Contains(status); !ok || err != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
newRCS, removeErr := existingRCS.Remove(status)
|
||||
if removeErr != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, removeErr.Error(), http.StatusBadRequest)
|
||||
newRCS, err := existingRCS.Remove(status)
|
||||
if err != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
newRCSJSON, jsonErr := json.Marshal(newRCS)
|
||||
if jsonErr != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.marshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
|
||||
newRCSJSON, err := json.Marshal(newRCS)
|
||||
if err != nil {
|
||||
return model.NewAppError("RemoveRecentCustomStatus", "api.marshal_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
pref.Value = string(newRCSJSON)
|
||||
if err := a.UpdatePreferences(userID, model.Preferences{*pref}); err != nil {
|
||||
return err
|
||||
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -104,7 +104,7 @@ func TestCustomStatusErrors(t *testing.T) {
|
||||
UserStore: &mockUserStore,
|
||||
SessionStore: &mockSessionStore,
|
||||
OAuthStore: &mockOAuthStore,
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -6,7 +6,7 @@ package app
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
@@ -145,7 +145,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) {
|
||||
// notifications.log
|
||||
notificationsLog := config.GetNotificationsLogFileLocation(*a.Config().LogSettings.FileLocation)
|
||||
|
||||
notificationsLogFileData, notificationsLogFileDataErr := ioutil.ReadFile(notificationsLog)
|
||||
notificationsLogFileData, notificationsLogFileDataErr := os.ReadFile(notificationsLog)
|
||||
|
||||
if notificationsLogFileDataErr == nil {
|
||||
fileData := model.FileData{
|
||||
@@ -155,7 +155,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) {
|
||||
return &fileData, ""
|
||||
}
|
||||
|
||||
warning = fmt.Sprintf("ioutil.ReadFile(notificationsLog) Error: %s", notificationsLogFileDataErr.Error())
|
||||
warning = fmt.Sprintf("os.ReadFile(notificationsLog) Error: %s", notificationsLogFileDataErr.Error())
|
||||
|
||||
} else {
|
||||
warning = "Unable to retrieve notifications.log because LogSettings: EnableFile is false in config.json"
|
||||
@@ -172,7 +172,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) {
|
||||
// mattermost.log
|
||||
mattermostLog := config.GetLogFileLocation(*a.Config().LogSettings.FileLocation)
|
||||
|
||||
mattermostLogFileData, mattermostLogFileDataErr := ioutil.ReadFile(mattermostLog)
|
||||
mattermostLogFileData, mattermostLogFileDataErr := os.ReadFile(mattermostLog)
|
||||
|
||||
if mattermostLogFileDataErr == nil {
|
||||
fileData := model.FileData{
|
||||
@@ -181,7 +181,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) {
|
||||
}
|
||||
return &fileData, ""
|
||||
}
|
||||
warning = fmt.Sprintf("ioutil.ReadFile(mattermostLog) Error: %s", mattermostLogFileDataErr.Error())
|
||||
warning = fmt.Sprintf("os.ReadFile(mattermostLog) Error: %s", mattermostLogFileDataErr.Error())
|
||||
|
||||
} else {
|
||||
warning = "Unable to retrieve mattermost.log because LogSettings: EnableFile is false in config.json"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -61,9 +60,9 @@ func TestGenerateSupportPacket(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("mattermost.log", d1, 0777)
|
||||
err := os.WriteFile("mattermost.log", d1, 0777)
|
||||
require.NoError(t, err)
|
||||
err = ioutil.WriteFile("notifications.log", d1, 0777)
|
||||
err = os.WriteFile("notifications.log", d1, 0777)
|
||||
require.NoError(t, err)
|
||||
|
||||
fileDatas := th.App.GenerateSupportPacket()
|
||||
@@ -111,11 +110,11 @@ func TestGetNotificationsLog(t *testing.T) {
|
||||
|
||||
fileData, warning = th.App.getNotificationsLog()
|
||||
assert.Nil(t, fileData)
|
||||
assert.Contains(t, warning, "ioutil.ReadFile(notificationsLog) Error:")
|
||||
assert.Contains(t, warning, "os.ReadFile(notificationsLog) Error:")
|
||||
|
||||
// Happy path where we have file and no warning
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("notifications.log", d1, 0777)
|
||||
err := os.WriteFile("notifications.log", d1, 0777)
|
||||
defer os.Remove("notifications.log")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -149,11 +148,11 @@ func TestGetMattermostLog(t *testing.T) {
|
||||
|
||||
fileData, warning = th.App.getMattermostLog()
|
||||
assert.Nil(t, fileData)
|
||||
assert.Contains(t, warning, "ioutil.ReadFile(mattermostLog) Error:")
|
||||
assert.Contains(t, warning, "os.ReadFile(mattermostLog) Error:")
|
||||
|
||||
// Happy path where we get a log file and no warning
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("mattermost.log", d1, 0777)
|
||||
err := os.WriteFile("mattermost.log", d1, 0777)
|
||||
defer os.Remove("mattermost.log")
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -1044,7 +1044,7 @@ func TestLeaveTeamPanic(t *testing.T) {
|
||||
UserStore: &mockUserStore,
|
||||
SessionStore: &mocks.SessionStore{},
|
||||
OAuthStore: &mocks.OAuthStore{},
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -1088,7 +1088,7 @@ func TestLeaveTeamPanic(t *testing.T) {
|
||||
GroupStore: &mocks.GroupStore{},
|
||||
Users: th.App.ch.srv.userService,
|
||||
WebHub: th.App.ch.srv,
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -5,7 +5,6 @@ package teams
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -43,7 +42,7 @@ func Setup(tb testing.TB) *TestHelper {
|
||||
}
|
||||
|
||||
func setupTestHelper(s store.Store, includeCacheLayer bool, tb testing.TB) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "teamservicetest")
|
||||
tempWorkspace, err := os.MkdirTemp("", "teamservicetest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -213,7 +213,7 @@ func TestUploadData(t *testing.T) {
|
||||
|
||||
t.Run("image processing", func(t *testing.T) {
|
||||
testDir, _ := fileutils.FindDir("tests")
|
||||
data, err := ioutil.ReadFile(filepath.Join(testDir, "test.png"))
|
||||
data, err := os.ReadFile(filepath.Join(testDir, "test.png"))
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
|
||||
16
app/user.go
16
app/user.go
@@ -1241,7 +1241,7 @@ func (a *App) updateUserNotifyProps(userID string, props map[string]string) *mod
|
||||
case errors.As(err, &appErr):
|
||||
return appErr
|
||||
default:
|
||||
return model.NewAppError("UpdateUser", "app.user.update.finding.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return model.NewAppError("UpdateUser", "app.user.update.finding.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1417,7 +1417,7 @@ func (a *App) CreatePasswordRecoveryToken(userID, email string) (*model.Token, *
|
||||
}
|
||||
jsonData, err := json.Marshal(tokenExtra)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("CreatePasswordRecoveryToken", "api.user.create_password_token.error", nil, "", http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("CreatePasswordRecoveryToken", "api.user.create_password_token.error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
token := model.NewToken(TokenTypePasswordRecovery, string(jsonData))
|
||||
@@ -2184,9 +2184,9 @@ func (a *App) PromoteGuestToUser(c *request.Context, user *model.User, requestor
|
||||
for _, member := range teamMembers {
|
||||
a.sendUpdatedMemberRoleEvent(user.Id, member)
|
||||
|
||||
channelMembers, err := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||
if err != nil {
|
||||
c.Logger().Warn("Failed to get channel members for user on promote guest to user", mlog.Err(err))
|
||||
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||
if appErr != nil {
|
||||
c.Logger().Warn("Failed to get channel members for user on promote guest to user", mlog.Err(appErr))
|
||||
}
|
||||
|
||||
for _, member := range channelMembers {
|
||||
@@ -2228,9 +2228,9 @@ func (a *App) DemoteUserToGuest(c request.CTX, user *model.User) *model.AppError
|
||||
for _, member := range teamMembers {
|
||||
a.sendUpdatedMemberRoleEvent(user.Id, member)
|
||||
|
||||
channelMembers, err := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||
if err != nil {
|
||||
c.Logger().Warn("Failed to get channel members for users on demote user to guest", mlog.Err(err))
|
||||
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
|
||||
if appErr != nil {
|
||||
c.Logger().Warn("Failed to get channel members for users on demote user to guest", mlog.Err(appErr))
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -1712,7 +1712,7 @@ func TestUpdateThreadReadForUser(t *testing.T) {
|
||||
UserStore: &mockUserStore,
|
||||
SessionStore: &storemocks.SessionStore{},
|
||||
OAuthStore: &storemocks.OAuthStore{},
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -1749,8 +1749,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("successfully create a user with insights feature flag disabled", func(t *testing.T) {
|
||||
th.Server.configStore.SetReadOnlyFF(false)
|
||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
||||
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
testUser := th.CreateUser()
|
||||
@@ -1769,8 +1769,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("successfully create a guest user with initial tutorial, insights and recommended steps preferences", func(t *testing.T) {
|
||||
th.Server.configStore.SetReadOnlyFF(false)
|
||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
||||
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||
testUser := th.CreateGuest()
|
||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||
|
||||
@@ -5,7 +5,6 @@ package users
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -48,7 +47,7 @@ func Setup(tb testing.TB) *TestHelper {
|
||||
}
|
||||
|
||||
func setupTestHelper(s store.Store, includeCacheLayer bool, tb testing.TB) *TestHelper {
|
||||
tempWorkspace, err := ioutil.TempDir("", "userservicetest")
|
||||
tempWorkspace, err := os.MkdirTemp("", "userservicetest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -174,7 +174,7 @@ func getFont(initialFont string) (*truetype.Font, error) {
|
||||
}
|
||||
|
||||
fontDir, _ := fileutils.FindDir("fonts")
|
||||
fontBytes, err := ioutil.ReadFile(filepath.Join(fontDir, initialFont))
|
||||
fontBytes, err := os.ReadFile(filepath.Join(fontDir, initialFont))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func TestHubSessionRevokeRace(t *testing.T) {
|
||||
UserStore: &mockUserStore,
|
||||
SessionStore: &mockSessionStore,
|
||||
OAuthStore: &mockOAuthStore,
|
||||
ConfigFn: th.App.ch.srv.Config,
|
||||
ConfigFn: th.App.ch.srv.platform.Config,
|
||||
Metrics: th.App.Metrics(),
|
||||
Cluster: th.App.Cluster(),
|
||||
LicenseFn: th.App.ch.srv.License,
|
||||
|
||||
@@ -98,9 +98,9 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
|
||||
var body io.Reader
|
||||
var contentType string
|
||||
if hook.ContentType == "application/json" {
|
||||
js, jsonErr := json.Marshal(payload)
|
||||
if jsonErr != nil {
|
||||
mlog.Warn("Failed to encode to JSON", mlog.Err(jsonErr))
|
||||
js, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
c.Logger().Warn("Failed to encode to JSON", mlog.Err(err))
|
||||
}
|
||||
body = bytes.NewReader(js)
|
||||
contentType = "application/json"
|
||||
@@ -116,7 +116,7 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
|
||||
a.Srv().Go(func() {
|
||||
webhookResp, err := a.doOutgoingWebhookRequest(url, body, contentType)
|
||||
if err != nil {
|
||||
mlog.Error("Event POST failed.", mlog.Err(err))
|
||||
c.Logger().Error("Event POST failed.", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
|
||||
webhookResp.IconURL = hook.IconURL
|
||||
}
|
||||
if _, err := a.CreateWebhookPost(c, hook.CreatorId, channel, text, webhookResp.Username, webhookResp.IconURL, "", webhookResp.Props, webhookResp.Type, postRootId); err != nil {
|
||||
mlog.Error("Failed to create response post.", mlog.Err(err))
|
||||
c.Logger().Error("Failed to create response post.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -175,7 +175,7 @@ func (a *App) doOutgoingWebhookRequest(url string, body io.Reader, contentType s
|
||||
if jsonErr == io.EOF {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, model.NewAppError("doOutgoingWebhookRequest", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("doOutgoingWebhookRequest", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
|
||||
}
|
||||
|
||||
return &hookResp, nil
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -273,7 +272,7 @@ func generateInitialCorpus() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile("./workdir/corpus"+strconv.Itoa(i), data, 0644)
|
||||
err = os.WriteFile("./workdir/corpus"+strconv.Itoa(i), data, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user