mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Added post limit warning (#26793)
* Renamed user limit API to app limit API * Added post warning limit * Added tests * Fixed types * Renamed AppLimits to ServerLimits * Fixed tests and review fixes * Updated generated code * Updated server i18n * Fixed TestCreateUserOrGuest test * Exclude deleted posts from post count for liims * Reduced limits for ease of testing * Restored original limts
This commit is contained in:
@@ -3637,7 +3637,7 @@ components:
|
|||||||
state:
|
state:
|
||||||
description: The current state of the installation
|
description: The current state of the installation
|
||||||
type: string
|
type: string
|
||||||
UserLimits:
|
ServerLimits:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
maxUsersLimit:
|
maxUsersLimit:
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
/api/v4/limits/users:
|
/api/v4/limits/server:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- users
|
- users
|
||||||
summary: Gets the user limits for the server
|
summary: Gets the server limits for the server
|
||||||
description: >
|
description: >
|
||||||
Gets the user limits for the server
|
Gets the server limits for the server
|
||||||
|
|
||||||
##### Permissions
|
##### Permissions
|
||||||
|
|
||||||
Requires `sysconsole_read_user_management_users`.
|
Requires `sysconsole_read_user_management_users`.
|
||||||
|
|
||||||
operationId: getUserLimits
|
operationId: GetServerLimits
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: User limits for server
|
description: App limits for server
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/UserLimits"
|
$ref: "#/components/schemas/ServerLimits"
|
||||||
"400":
|
"400":
|
||||||
$ref: "#/components/responses/BadRequest"
|
$ref: "#/components/responses/BadRequest"
|
||||||
"401":
|
"401":
|
||||||
|
|||||||
@@ -13,22 +13,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) InitLimits() {
|
func (api *API) InitLimits() {
|
||||||
api.BaseRoutes.Limits.Handle("/users", api.APISessionRequired(getUserLimits)).Methods("GET")
|
api.BaseRoutes.Limits.Handle("/server", api.APISessionRequired(getServerLimits)).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserLimits(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getServerLimits(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if !(c.IsSystemAdmin() && c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementUsers)) {
|
if !(c.IsSystemAdmin() && c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementUsers)) {
|
||||||
c.SetPermissionError(model.PermissionSysconsoleReadUserManagementUsers)
|
c.SetPermissionError(model.PermissionSysconsoleReadUserManagementUsers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userLimits, err := c.App.GetUserLimits()
|
serverLimits, err := c.App.GetServerLimits()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(userLimits); err != nil {
|
if err := json.NewEncoder(w).Encode(serverLimits); err != nil {
|
||||||
c.Logger.Error("Error writing user limits response", mlog.Err(err))
|
c.Logger.Error("Error writing server limits response", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -806,6 +806,7 @@ type AppIface interface {
|
|||||||
GetSchemeRolesForTeam(teamID string) (string, string, string, *model.AppError)
|
GetSchemeRolesForTeam(teamID string) (string, string, string, *model.AppError)
|
||||||
GetSchemes(scope string, offset int, limit int) ([]*model.Scheme, *model.AppError)
|
GetSchemes(scope string, offset int, limit int) ([]*model.Scheme, *model.AppError)
|
||||||
GetSchemesPage(scope string, page int, perPage int) ([]*model.Scheme, *model.AppError)
|
GetSchemesPage(scope string, page int, perPage int) ([]*model.Scheme, *model.AppError)
|
||||||
|
GetServerLimits() (*model.ServerLimits, *model.AppError)
|
||||||
GetSession(token string) (*model.Session, *model.AppError)
|
GetSession(token string) (*model.Session, *model.AppError)
|
||||||
GetSessionById(c request.CTX, sessionID string) (*model.Session, *model.AppError)
|
GetSessionById(c request.CTX, sessionID string) (*model.Session, *model.AppError)
|
||||||
GetSessions(c request.CTX, userID string) ([]*model.Session, *model.AppError)
|
GetSessions(c request.CTX, userID string) ([]*model.Session, *model.AppError)
|
||||||
@@ -864,7 +865,6 @@ type AppIface interface {
|
|||||||
GetUserByUsername(username string) (*model.User, *model.AppError)
|
GetUserByUsername(username string) (*model.User, *model.AppError)
|
||||||
GetUserCountForReport(filter *model.UserReportOptions) (*int64, *model.AppError)
|
GetUserCountForReport(filter *model.UserReportOptions) (*int64, *model.AppError)
|
||||||
GetUserForLogin(c request.CTX, id, loginId string) (*model.User, *model.AppError)
|
GetUserForLogin(c request.CTX, id, loginId string) (*model.User, *model.AppError)
|
||||||
GetUserLimits() (*model.UserLimits, *model.AppError)
|
|
||||||
GetUserTermsOfService(userID string) (*model.UserTermsOfService, *model.AppError)
|
GetUserTermsOfService(userID string) (*model.UserTermsOfService, *model.AppError)
|
||||||
GetUsers(userIDs []string) ([]*model.User, *model.AppError)
|
GetUsers(userIDs []string) ([]*model.User, *model.AppError)
|
||||||
GetUsersByGroupChannelIds(c request.CTX, channelIDs []string, asAdmin bool) (map[string][]*model.User, *model.AppError)
|
GetUsersByGroupChannelIds(c request.CTX, channelIDs []string, asAdmin bool) (map[string][]*model.User, *model.AppError)
|
||||||
|
|||||||
@@ -12,26 +12,39 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxUsersLimit = 10000
|
maxUsersLimit = 10_000
|
||||||
maxUsersHardLimit = 11000
|
maxUsersHardLimit = 11_000
|
||||||
|
|
||||||
|
maxPostLimit = 5_000_000
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) GetUserLimits() (*model.UserLimits, *model.AppError) {
|
func (a *App) GetServerLimits() (*model.ServerLimits, *model.AppError) {
|
||||||
if !a.shouldShowUserLimits() {
|
var limits = &model.ServerLimits{}
|
||||||
return &model.UserLimits{}, nil
|
|
||||||
|
if a.shouldShowUserLimits() {
|
||||||
|
activeUserCount, appErr := a.Srv().Store().User().Count(model.UserCountOptions{})
|
||||||
|
if appErr != nil {
|
||||||
|
mlog.Error("Failed to get active user count from database", mlog.String("error", appErr.Error()))
|
||||||
|
return nil, model.NewAppError("GetServerLimits", "app.limits.get_app_limits.user_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
limits.ActiveUserCount = activeUserCount
|
||||||
|
limits.MaxUsersLimit = maxUsersLimit
|
||||||
|
limits.MaxUsersHardLimit = maxUsersHardLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
activeUserCount, appErr := a.Srv().Store().User().Count(model.UserCountOptions{})
|
if a.shouldShowPostLimits() {
|
||||||
if appErr != nil {
|
postCount, appErr := a.Srv().Store().Post().AnalyticsPostCount(&model.PostCountOptions{ExcludeDeleted: true})
|
||||||
mlog.Error("Failed to get active user count from database", mlog.String("error", appErr.Error()))
|
if appErr != nil {
|
||||||
return nil, model.NewAppError("GetUsersLimits", "app.limits.get_user_limits.user_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
mlog.Error("Failed to get post count from database", mlog.String("error", appErr.Error()))
|
||||||
|
return nil, model.NewAppError("GetServerLimits", "app.limits.get_server_limits.post_count.store_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
limits.MaxPostLimit = maxPostLimit
|
||||||
|
limits.PostCount = postCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return &model.UserLimits{
|
return limits, nil
|
||||||
ActiveUserCount: activeUserCount,
|
|
||||||
MaxUsersLimit: maxUsersLimit,
|
|
||||||
MaxUsersHardLimit: maxUsersHardLimit,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) shouldShowUserLimits() bool {
|
func (a *App) shouldShowUserLimits() bool {
|
||||||
@@ -42,8 +55,16 @@ func (a *App) shouldShowUserLimits() bool {
|
|||||||
return a.License() == nil
|
return a.License() == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) shouldShowPostLimits() bool {
|
||||||
|
if maxPostLimit == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.License() == nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) isHardUserLimitExceeded() (bool, *model.AppError) {
|
func (a *App) isHardUserLimitExceeded() (bool, *model.AppError) {
|
||||||
userLimits, appErr := a.GetUserLimits()
|
userLimits, appErr := a.GetServerLimits()
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return false, appErr
|
return false, appErr
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,127 +6,177 @@ package app
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetUserLimits(t *testing.T) {
|
func TestGetServerLimits(t *testing.T) {
|
||||||
th := Setup(t).InitBasic()
|
|
||||||
defer th.TearDown()
|
|
||||||
|
|
||||||
t.Run("base case", func(t *testing.T) {
|
t.Run("base case", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
// InitBasic creates 3 users by default
|
// InitBasic creates 3 users by default
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
require.Equal(t, int64(10000), userLimits.MaxUsersLimit)
|
require.Equal(t, int64(10000), serverLimits.MaxUsersLimit)
|
||||||
|
|
||||||
|
// 5 posts are created by default
|
||||||
|
require.Equal(t, int64(5), serverLimits.PostCount)
|
||||||
|
require.Equal(t, int64(5_000_000), serverLimits.MaxPostLimit)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user count should increase on creating new user and decrease on permanently deleting", func(t *testing.T) {
|
t.Run("user count should increase on creating new user and decrease on permanently deleting", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we create a new user
|
// now we create a new user
|
||||||
newUser := th.CreateUser()
|
newUser := th.CreateUser()
|
||||||
|
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(4), userLimits.ActiveUserCount)
|
require.Equal(t, int64(4), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we'll delete the user
|
// now we'll delete the user
|
||||||
_ = th.App.PermanentDeleteUser(th.Context, newUser)
|
_ = th.App.PermanentDeleteUser(th.Context, newUser)
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user count should increase on creating new guest user and decrease on permanently deleting", func(t *testing.T) {
|
t.Run("user count should increase on creating new guest user and decrease on permanently deleting", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we create a new user
|
// now we create a new user
|
||||||
newGuestUser := th.CreateGuest()
|
newGuestUser := th.CreateGuest()
|
||||||
|
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(4), userLimits.ActiveUserCount)
|
require.Equal(t, int64(4), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we'll delete the user
|
// now we'll delete the user
|
||||||
_ = th.App.PermanentDeleteUser(th.Context, newGuestUser)
|
_ = th.App.PermanentDeleteUser(th.Context, newGuestUser)
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user count should increase on creating new user and decrease on soft deleting", func(t *testing.T) {
|
t.Run("user count should increase on creating new user and decrease on soft deleting", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we create a new user
|
// now we create a new user
|
||||||
newUser := th.CreateUser()
|
newUser := th.CreateUser()
|
||||||
|
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(4), userLimits.ActiveUserCount)
|
require.Equal(t, int64(4), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we'll delete the user
|
// now we'll delete the user
|
||||||
_, appErr = th.App.UpdateActive(th.Context, newUser, false)
|
_, appErr = th.App.UpdateActive(th.Context, newUser, false)
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user count should increase on creating new guest user and decrease on soft deleting", func(t *testing.T) {
|
t.Run("user count should increase on creating new guest user and decrease on soft deleting", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we create a new user
|
// now we create a new user
|
||||||
newGuestUser := th.CreateGuest()
|
newGuestUser := th.CreateGuest()
|
||||||
|
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(4), userLimits.ActiveUserCount)
|
require.Equal(t, int64(4), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we'll delete the user
|
// now we'll delete the user
|
||||||
_, appErr = th.App.UpdateActive(th.Context, newGuestUser, false)
|
_, appErr = th.App.UpdateActive(th.Context, newGuestUser, false)
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("user count should not change on creating or deleting bots", func(t *testing.T) {
|
t.Run("user count should not change on creating or deleting bots", func(t *testing.T) {
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we create a new bot
|
// now we create a new bot
|
||||||
newBot := th.CreateBot()
|
newBot := th.CreateBot()
|
||||||
|
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
|
|
||||||
// now we'll delete the bot
|
// now we'll delete the bot
|
||||||
_ = th.App.PermanentDeleteBot(th.Context, newBot.UserId)
|
_ = th.App.PermanentDeleteBot(th.Context, newBot.UserId)
|
||||||
userLimits, appErr = th.App.GetUserLimits()
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
require.Equal(t, int64(3), userLimits.ActiveUserCount)
|
require.Equal(t, int64(3), serverLimits.ActiveUserCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("limits should be empty when there is a license", func(t *testing.T) {
|
t.Run("limits should be empty when there is a license", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
th.App.Srv().SetLicense(model.NewTestLicense())
|
th.App.Srv().SetLicense(model.NewTestLicense())
|
||||||
|
|
||||||
userLimits, appErr := th.App.GetUserLimits()
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
require.Nil(t, appErr)
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
require.Equal(t, int64(0), userLimits.ActiveUserCount)
|
require.Equal(t, int64(0), serverLimits.ActiveUserCount)
|
||||||
require.Equal(t, int64(0), userLimits.MaxUsersLimit)
|
require.Equal(t, int64(0), serverLimits.MaxUsersLimit)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("post count should increase on creating new post and should decrease on deleting post", func(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
serverLimits, appErr := th.App.GetServerLimits()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, int64(5), serverLimits.PostCount)
|
||||||
|
|
||||||
|
// now we create a new post
|
||||||
|
team := th.CreateTeam()
|
||||||
|
channel := th.CreateChannel(request.TestContext(t), team)
|
||||||
|
post := th.CreatePost(channel)
|
||||||
|
|
||||||
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, int64(6), serverLimits.PostCount)
|
||||||
|
|
||||||
|
// now we'll delete the post
|
||||||
|
_, appErr = th.App.DeletePost(request.TestContext(t), post.Id, "")
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
|
||||||
|
serverLimits, appErr = th.App.GetServerLimits()
|
||||||
|
require.Nil(t, appErr)
|
||||||
|
require.Equal(t, int64(5), serverLimits.PostCount)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9463,6 +9463,28 @@ func (a *OpenTracingAppLayer) GetSchemesPage(scope string, page int, perPage int
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *OpenTracingAppLayer) GetServerLimits() (*model.ServerLimits, *model.AppError) {
|
||||||
|
origCtx := a.ctx
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetServerLimits")
|
||||||
|
|
||||||
|
a.ctx = newCtx
|
||||||
|
a.app.Srv().Store().SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
a.app.Srv().Store().SetContext(origCtx)
|
||||||
|
a.ctx = origCtx
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
resultVar0, resultVar1 := a.app.GetServerLimits()
|
||||||
|
|
||||||
|
if resultVar1 != nil {
|
||||||
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
|
ext.Error.Set(span, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) GetSession(token string) (*model.Session, *model.AppError) {
|
func (a *OpenTracingAppLayer) GetSession(token string) (*model.Session, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSession")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSession")
|
||||||
@@ -10851,28 +10873,6 @@ func (a *OpenTracingAppLayer) GetUserForLogin(c request.CTX, id string, loginId
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) GetUserLimits() (*model.UserLimits, *model.AppError) {
|
|
||||||
origCtx := a.ctx
|
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUserLimits")
|
|
||||||
|
|
||||||
a.ctx = newCtx
|
|
||||||
a.app.Srv().Store().SetContext(newCtx)
|
|
||||||
defer func() {
|
|
||||||
a.app.Srv().Store().SetContext(origCtx)
|
|
||||||
a.ctx = origCtx
|
|
||||||
}()
|
|
||||||
|
|
||||||
defer span.Finish()
|
|
||||||
resultVar0, resultVar1 := a.app.GetUserLimits()
|
|
||||||
|
|
||||||
if resultVar1 != nil {
|
|
||||||
span.LogFields(spanlog.Error(resultVar1))
|
|
||||||
ext.Error.Set(span, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultVar0, resultVar1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) GetUserStatusesByIds(userIDs []string) ([]*model.Status, *model.AppError) {
|
func (a *OpenTracingAppLayer) GetUserStatusesByIds(userIDs []string) ([]*model.Status, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUserStatusesByIds")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUserStatusesByIds")
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
|
|||||||
}(ruser.Id)
|
}(ruser.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
userLimits, limitErr := a.GetUserLimits()
|
userLimits, limitErr := a.GetServerLimits()
|
||||||
if limitErr != nil {
|
if limitErr != nil {
|
||||||
// we don't want to break the create user flow just because of this.
|
// we don't want to break the create user flow just because of this.
|
||||||
// So, we log the error, not return
|
// So, we log the error, not return
|
||||||
@@ -1070,7 +1070,7 @@ func (a *App) UpdateActive(c request.CTX, user *model.User, active bool) (*model
|
|||||||
}
|
}
|
||||||
|
|
||||||
if active {
|
if active {
|
||||||
userLimits, appErr := a.GetUserLimits()
|
userLimits, appErr := a.GetServerLimits()
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
mlog.Error("Error fetching user limits in UpdateActive", mlog.Err(appErr))
|
mlog.Error("Error fetching user limits in UpdateActive", mlog.Err(appErr))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2033,8 +2033,12 @@ func TestCreateUserOrGuest(t *testing.T) {
|
|||||||
mockUserStore := storemocks.UserStore{}
|
mockUserStore := storemocks.UserStore{}
|
||||||
mockUserStore.On("Count", mock.Anything).Return(int64(12000), nil)
|
mockUserStore.On("Count", mock.Anything).Return(int64(12000), nil)
|
||||||
|
|
||||||
|
mockPostStore := storemocks.PostStore{}
|
||||||
|
mockPostStore.On("AnalyticsPostCount", mock.Anything).Return(int64(1000), nil)
|
||||||
|
|
||||||
mockStore := th.App.Srv().Store().(*storemocks.Store)
|
mockStore := th.App.Srv().Store().(*storemocks.Store)
|
||||||
mockStore.On("User").Return(&mockUserStore)
|
mockStore.On("User").Return(&mockUserStore)
|
||||||
|
mockStore.On("Post").Return(&mockPostStore)
|
||||||
|
|
||||||
user := &model.User{
|
user := &model.User{
|
||||||
Email: "TestCreateUserOrGuest@example.com",
|
Email: "TestCreateUserOrGuest@example.com",
|
||||||
@@ -2147,8 +2151,12 @@ func userCreationMocks(t *testing.T, th *TestHelper, userID string, activeUserCo
|
|||||||
mockProductNoticeStore := storemocks.ProductNoticesStore{}
|
mockProductNoticeStore := storemocks.ProductNoticesStore{}
|
||||||
mockProductNoticeStore.On("View", userID, mock.Anything).Return(nil)
|
mockProductNoticeStore.On("View", userID, mock.Anything).Return(nil)
|
||||||
|
|
||||||
|
mockPostStore := storemocks.PostStore{}
|
||||||
|
mockPostStore.On("AnalyticsPostCount", mock.Anything).Return(int64(1000), nil)
|
||||||
|
|
||||||
mockStore := th.App.Srv().Store().(*storemocks.Store)
|
mockStore := th.App.Srv().Store().(*storemocks.Store)
|
||||||
mockStore.On("User").Return(&mockUserStore)
|
mockStore.On("User").Return(&mockUserStore)
|
||||||
|
mockStore.On("Post").Return(&mockPostStore)
|
||||||
mockStore.On("Group").Return(&mockGroupStore)
|
mockStore.On("Group").Return(&mockGroupStore)
|
||||||
mockStore.On("Channel").Return(&mockChannelStore)
|
mockStore.On("Channel").Return(&mockChannelStore)
|
||||||
mockStore.On("Preference").Return(&mockPreferencesStore)
|
mockStore.On("Preference").Return(&mockPreferencesStore)
|
||||||
|
|||||||
@@ -6039,9 +6039,13 @@
|
|||||||
"translation": "No license present"
|
"translation": "No license present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "app.limits.get_user_limits.user_count.store_error",
|
"id": "app.limits.get_app_limits.user_count.store_error",
|
||||||
"translation": "Failed to get user count"
|
"translation": "Failed to get user count"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.limits.get_server_limits.post_count.store_error",
|
||||||
|
"translation": "Failed to get post count"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.login.doLogin.updateLastLogin.error",
|
"id": "app.login.doLogin.updateLastLogin.error",
|
||||||
"translation": "Could not update last login timestamp"
|
"translation": "Could not update last login timestamp"
|
||||||
|
|||||||
@@ -8953,20 +8953,20 @@ func (c *Client4) SubmitTrueUpReview(ctx context.Context, req map[string]any) (*
|
|||||||
return BuildResponse(r), nil
|
return BuildResponse(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client4) GetUserLimits(ctx context.Context) (*UserLimits, *Response, error) {
|
func (c *Client4) GetServerLimits(ctx context.Context) (*ServerLimits, *Response, error) {
|
||||||
r, err := c.DoAPIGet(ctx, c.limitsRoute()+"/users", "")
|
r, err := c.DoAPIGet(ctx, c.limitsRoute()+"/users", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, BuildResponse(r), err
|
return nil, BuildResponse(r), err
|
||||||
}
|
}
|
||||||
defer closeBody(r)
|
defer closeBody(r)
|
||||||
var userLimits UserLimits
|
var serverLimits ServerLimits
|
||||||
if r.StatusCode == http.StatusNotModified {
|
if r.StatusCode == http.StatusNotModified {
|
||||||
return &userLimits, BuildResponse(r), nil
|
return &serverLimits, BuildResponse(r), nil
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&userLimits); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&serverLimits); err != nil {
|
||||||
return nil, nil, NewAppError("GetUserLimits", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
return nil, nil, NewAppError("GetServerLimits", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
}
|
}
|
||||||
return &userLimits, BuildResponse(r), nil
|
return &serverLimits, BuildResponse(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateChannelBookmark creates a channel bookmark based on the provided struct.
|
// CreateChannelBookmark creates a channel bookmark based on the provided struct.
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
type UserLimits struct {
|
type ServerLimits struct {
|
||||||
MaxUsersLimit int64 `json:"maxUsersLimit"` // soft limit for max number of users.
|
MaxUsersLimit int64 `json:"maxUsersLimit"` // soft limit for max number of users.
|
||||||
MaxUsersHardLimit int64 `json:"maxUsersHardLimit"` // hard limit for max number of active users.
|
MaxUsersHardLimit int64 `json:"maxUsersHardLimit"` // hard limit for max number of active users.
|
||||||
ActiveUserCount int64 `json:"activeUserCount"` // actual number of active users on server. Active = non deleted
|
ActiveUserCount int64 `json:"activeUserCount"` // actual number of active users on server. Active = non deleted
|
||||||
|
|
||||||
|
MaxPostLimit int64 `json:"maxPostLimit"` // soft limit for max number of posts
|
||||||
|
PostCount int64 `json:"postCount"` // actual number of posts in system.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {Dispatch} from 'redux';
|
|||||||
|
|
||||||
import {uploadLicense, removeLicense, getPrevTrialLicense} from 'mattermost-redux/actions/admin';
|
import {uploadLicense, removeLicense, getPrevTrialLicense} from 'mattermost-redux/actions/admin';
|
||||||
import {getLicenseConfig} from 'mattermost-redux/actions/general';
|
import {getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||||
import {getUsersLimits} from 'mattermost-redux/actions/limits';
|
import {getServerLimits} from 'mattermost-redux/actions/limits';
|
||||||
import {getFilteredUsersStats} from 'mattermost-redux/actions/users';
|
import {getFilteredUsersStats} from 'mattermost-redux/actions/users';
|
||||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||||
import {getFilteredUsersStats as selectFilteredUserStats} from 'mattermost-redux/selectors/entities/users';
|
import {getFilteredUsersStats as selectFilteredUserStats} from 'mattermost-redux/selectors/entities/users';
|
||||||
@@ -43,7 +43,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
|||||||
requestTrialLicense,
|
requestTrialLicense,
|
||||||
openModal,
|
openModal,
|
||||||
getFilteredUsersStats,
|
getFilteredUsersStats,
|
||||||
getUsersLimits,
|
getServerLimits,
|
||||||
}, dispatch),
|
}, dispatch),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ describe('components/admin_console/license_settings/LicenseSettings', () => {
|
|||||||
upgradeToE0Status: jest.fn().mockImplementation(() => Promise.resolve({percentage: 0, error: null})),
|
upgradeToE0Status: jest.fn().mockImplementation(() => Promise.resolve({percentage: 0, error: null})),
|
||||||
openModal: jest.fn(),
|
openModal: jest.fn(),
|
||||||
getFilteredUsersStats: jest.fn(),
|
getFilteredUsersStats: jest.fn(),
|
||||||
getUsersLimits: jest.fn(),
|
getServerLimits: jest.fn(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {FormattedMessage, defineMessages} from 'react-intl';
|
|||||||
import type {StatusOK} from '@mattermost/types/client4';
|
import type {StatusOK} from '@mattermost/types/client4';
|
||||||
import type {ClientLicense} from '@mattermost/types/config';
|
import type {ClientLicense} from '@mattermost/types/config';
|
||||||
import type {ServerError} from '@mattermost/types/errors';
|
import type {ServerError} from '@mattermost/types/errors';
|
||||||
import type {UsersLimits} from '@mattermost/types/limits';
|
import type {ServerLimits} from '@mattermost/types/limits';
|
||||||
import type {GetFilteredUsersStatsOpts, UsersStats} from '@mattermost/types/users';
|
import type {GetFilteredUsersStatsOpts, UsersStats} from '@mattermost/types/users';
|
||||||
|
|
||||||
import type {ActionResult} from 'mattermost-redux/types/actions';
|
import type {ActionResult} from 'mattermost-redux/types/actions';
|
||||||
@@ -55,7 +55,7 @@ type Props = {
|
|||||||
ping: () => Promise<{status: string}>;
|
ping: () => Promise<{status: string}>;
|
||||||
requestTrialLicense: (users: number, termsAccepted: boolean, receiveEmailsAccepted: boolean, featureName: string) => Promise<ActionResult>;
|
requestTrialLicense: (users: number, termsAccepted: boolean, receiveEmailsAccepted: boolean, featureName: string) => Promise<ActionResult>;
|
||||||
openModal: <P>(modalData: ModalData<P>) => void;
|
openModal: <P>(modalData: ModalData<P>) => void;
|
||||||
getUsersLimits: () => Promise<ActionResult<UsersLimits, ServerError>>;
|
getServerLimits: () => Promise<ActionResult<ServerLimits, ServerError>>;
|
||||||
getFilteredUsersStats: (filters: GetFilteredUsersStatsOpts) => Promise<{
|
getFilteredUsersStats: (filters: GetFilteredUsersStatsOpts) => Promise<{
|
||||||
data?: UsersStats;
|
data?: UsersStats;
|
||||||
error?: ServerError;
|
error?: ServerError;
|
||||||
@@ -196,7 +196,7 @@ export default class LicenseSettings extends React.PureComponent<Props, State> {
|
|||||||
this.props.actions.getLicenseConfig(),
|
this.props.actions.getLicenseConfig(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await this.props.actions.getUsersLimits();
|
await this.props.actions.getServerLimits();
|
||||||
|
|
||||||
this.setState({serverError: null, removing: false});
|
this.setState({serverError: null, removing: false});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import React from 'react';
|
|||||||
import type {ClientLicense, ClientConfig, WarnMetricStatus} from '@mattermost/types/config';
|
import type {ClientLicense, ClientConfig, WarnMetricStatus} from '@mattermost/types/config';
|
||||||
|
|
||||||
import {ToPaidPlanBannerDismissable} from 'components/admin_console/billing/billing_subscriptions/to_paid_plan_nudge_banner';
|
import {ToPaidPlanBannerDismissable} from 'components/admin_console/billing/billing_subscriptions/to_paid_plan_nudge_banner';
|
||||||
|
import PostLimitsAnnouncementBar from 'components/announcement_bar/post_limits_announcement_bar';
|
||||||
import withGetCloudSubscription from 'components/common/hocs/cloud/with_get_cloud_subscription';
|
import withGetCloudSubscription from 'components/common/hocs/cloud/with_get_cloud_subscription';
|
||||||
|
|
||||||
import CloudAnnualRenewalAnnouncementBar from './cloud_annual_renewal';
|
import CloudAnnualRenewalAnnouncementBar from './cloud_annual_renewal';
|
||||||
@@ -98,10 +99,22 @@ class AnnouncementBarController extends React.PureComponent<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The component specified further down takes priority over the component above it.
|
||||||
|
// For example, consider this-
|
||||||
|
// {
|
||||||
|
// Foo
|
||||||
|
// Bar
|
||||||
|
// Baz
|
||||||
|
// }
|
||||||
|
// Even if all Foo, Bar and Baz render, only Baz is visible as it's further down.
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{adminConfiguredAnnouncementBar}
|
{adminConfiguredAnnouncementBar}
|
||||||
{errorBar}
|
{errorBar}
|
||||||
|
<PostLimitsAnnouncementBar
|
||||||
|
license={this.props.license}
|
||||||
|
userIsAdmin={this.props.userIsAdmin}
|
||||||
|
/>
|
||||||
<UsersLimitsAnnouncementBar
|
<UsersLimitsAnnouncementBar
|
||||||
license={this.props.license}
|
license={this.props.license}
|
||||||
userIsAdmin={this.props.userIsAdmin}
|
userIsAdmin={this.props.userIsAdmin}
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ShouldShowingPostLimitsAnnouncementBarProps} from 'components/announcement_bar/post_limits_announcement_bar/index';
|
||||||
|
import {shouldShowPostLimitsAnnouncementBar,
|
||||||
|
} from 'components/announcement_bar/post_limits_announcement_bar/index';
|
||||||
|
|
||||||
|
describe('shouldShowPostLimitsAnnouncementBar', () => {
|
||||||
|
const defaultProps: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
userIsAdmin: true,
|
||||||
|
isLicensed: false,
|
||||||
|
maxPostLimit: 10,
|
||||||
|
postCount: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
test('should not show when user is not admin', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
userIsAdmin: false,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show when post count is 0', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
postCount: 0,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show when max post limit is 0', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
maxPostLimit: 0,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show when post count is less than max users limit', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
maxPostLimit: 10,
|
||||||
|
postCount: 5,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show when post count is equal to max post limit', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
maxPostLimit: 10,
|
||||||
|
postCount: 10,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show for non licensed servers with post count is greater than max post limit', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
isLicensed: false,
|
||||||
|
maxPostLimit: 5,
|
||||||
|
postCount: 10,
|
||||||
|
};
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show for licensed server', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
isLicensed: true,
|
||||||
|
maxPostLimit: 0,
|
||||||
|
postCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not show for licensed server even if post count is greater than max post limit', () => {
|
||||||
|
const props: ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
...defaultProps,
|
||||||
|
isLicensed: true,
|
||||||
|
maxPostLimit: 10,
|
||||||
|
postCount: 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(shouldShowPostLimitsAnnouncementBar(props)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import React, {useCallback} from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
import {useSelector} from 'react-redux';
|
||||||
|
|
||||||
|
import {AlertOutlineIcon} from '@mattermost/compass-icons/components';
|
||||||
|
import type {ClientLicense} from '@mattermost/types/config';
|
||||||
|
|
||||||
|
import {getServerLimits} from 'mattermost-redux/selectors/entities/limits';
|
||||||
|
|
||||||
|
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
|
||||||
|
|
||||||
|
import {AnnouncementBarTypes} from 'utils/constants';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
license?: ClientLicense;
|
||||||
|
userIsAdmin: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const learnMoreExternalLink = 'https://mattermost.com/pl/error-code-error-safety-limits-exceeded';
|
||||||
|
|
||||||
|
function PostLimitsAnnouncementBar(props: Props) {
|
||||||
|
const serverLimits = useSelector(getServerLimits);
|
||||||
|
|
||||||
|
const handleCTAClick = useCallback(() => {
|
||||||
|
window.open(learnMoreExternalLink, '_blank');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const isLicensed = props?.license?.IsLicensed === 'true';
|
||||||
|
const maxPostLimit = serverLimits?.maxPostLimit ?? 0;
|
||||||
|
const postCount = serverLimits?.postCount ?? 0;
|
||||||
|
|
||||||
|
if (!shouldShowPostLimitsAnnouncementBar({userIsAdmin: props.userIsAdmin, isLicensed, maxPostLimit, postCount})) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnnouncementBar
|
||||||
|
id='post_limits_announcement_bar'
|
||||||
|
showCloseButton={false}
|
||||||
|
message={
|
||||||
|
<FormattedMessage
|
||||||
|
id='post_limits_announcement_bar.copyText'
|
||||||
|
defaultMessage='Message limits exceeded. Contact administrator with: {ErrorCode}'
|
||||||
|
values={{
|
||||||
|
ErrorCode: 'ERROR_SAFETY_LIMITS_EXCEEDED',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
type={AnnouncementBarTypes.CRITICAL}
|
||||||
|
icon={<AlertOutlineIcon size={16}/>}
|
||||||
|
showCTA={true}
|
||||||
|
showLinkAsButton={true}
|
||||||
|
ctaText={
|
||||||
|
<FormattedMessage
|
||||||
|
id='users_limits_announcement_bar.ctaText'
|
||||||
|
defaultMessage='Learn More'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onButtonClick={handleCTAClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ShouldShowingPostLimitsAnnouncementBarProps = {
|
||||||
|
userIsAdmin: boolean;
|
||||||
|
isLicensed: boolean;
|
||||||
|
maxPostLimit: number;
|
||||||
|
postCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function shouldShowPostLimitsAnnouncementBar({userIsAdmin, isLicensed, maxPostLimit, postCount}: ShouldShowingPostLimitsAnnouncementBarProps) {
|
||||||
|
if (!userIsAdmin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxPostLimit === 0 || postCount === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isLicensed && postCount >= maxPostLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PostLimitsAnnouncementBar;
|
||||||
@@ -8,7 +8,7 @@ import {useSelector} from 'react-redux';
|
|||||||
import {AlertOutlineIcon} from '@mattermost/compass-icons/components';
|
import {AlertOutlineIcon} from '@mattermost/compass-icons/components';
|
||||||
import type {ClientLicense} from '@mattermost/types/config';
|
import type {ClientLicense} from '@mattermost/types/config';
|
||||||
|
|
||||||
import {getUsersLimits} from 'mattermost-redux/selectors/entities/limits';
|
import {getServerLimits} from 'mattermost-redux/selectors/entities/limits';
|
||||||
|
|
||||||
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
|
import AnnouncementBar from 'components/announcement_bar/default_announcement_bar';
|
||||||
|
|
||||||
@@ -22,15 +22,15 @@ type Props = {
|
|||||||
const learnMoreExternalLink = 'https://mattermost.com/pl/error-code-error-safety-limits-exceeded';
|
const learnMoreExternalLink = 'https://mattermost.com/pl/error-code-error-safety-limits-exceeded';
|
||||||
|
|
||||||
function UsersLimitsAnnouncementBar(props: Props) {
|
function UsersLimitsAnnouncementBar(props: Props) {
|
||||||
const usersLimits = useSelector(getUsersLimits);
|
const serverLimits = useSelector(getServerLimits);
|
||||||
|
|
||||||
const handleCTAClick = useCallback(() => {
|
const handleCTAClick = useCallback(() => {
|
||||||
window.open(learnMoreExternalLink, '_blank');
|
window.open(learnMoreExternalLink, '_blank');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isLicensed = props?.license?.IsLicensed === 'true';
|
const isLicensed = props?.license?.IsLicensed === 'true';
|
||||||
const maxUsersLimit = usersLimits?.maxUsersLimit ?? 0;
|
const maxUsersLimit = serverLimits?.maxUsersLimit ?? 0;
|
||||||
const activeUserCount = usersLimits?.activeUserCount ?? 0;
|
const activeUserCount = serverLimits?.activeUserCount ?? 0;
|
||||||
|
|
||||||
if (!shouldShowUserLimitsAnnouncementBar({userIsAdmin: props.userIsAdmin, isLicensed, maxUsersLimit, activeUserCount})) {
|
if (!shouldShowUserLimitsAnnouncementBar({userIsAdmin: props.userIsAdmin, isLicensed, maxUsersLimit, activeUserCount})) {
|
||||||
return null;
|
return null;
|
||||||
@@ -43,7 +43,10 @@ function UsersLimitsAnnouncementBar(props: Props) {
|
|||||||
message={
|
message={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='users_limits_announcement_bar.copyText'
|
id='users_limits_announcement_bar.copyText'
|
||||||
defaultMessage='User limits exceeded. Contact administrator with: ERROR_SAFETY_LIMITS_EXCEEDED'
|
defaultMessage='User limits exceeded. Contact administrator with: {ErrorCode}'
|
||||||
|
values={{
|
||||||
|
ErrorCode: 'ERROR_SAFETY_LIMITS_EXCEEDED',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
type={AnnouncementBarTypes.CRITICAL}
|
type={AnnouncementBarTypes.CRITICAL}
|
||||||
|
|||||||
@@ -4584,6 +4584,7 @@
|
|||||||
"post_info.tooltip.add_reactions": "Add Reaction",
|
"post_info.tooltip.add_reactions": "Add Reaction",
|
||||||
"post_info.unpin": "Unpin from Channel",
|
"post_info.unpin": "Unpin from Channel",
|
||||||
"post_info.unread": "Mark as Unread",
|
"post_info.unread": "Mark as Unread",
|
||||||
|
"post_limits_announcement_bar.copyText": "Message limits exceeded. Contact administrator with: {ErrorCode}",
|
||||||
"post_message_preview.channel": "Only visible to users in ~{channel}",
|
"post_message_preview.channel": "Only visible to users in ~{channel}",
|
||||||
"post_message_view.edited": "Edited",
|
"post_message_view.edited": "Edited",
|
||||||
"post_message_view.view_post_edit_history": "Click to view history",
|
"post_message_view.view_post_edit_history": "Click to view history",
|
||||||
@@ -5850,7 +5851,7 @@
|
|||||||
"userGuideHelp.mattermostUserGuide": "Mattermost user guide",
|
"userGuideHelp.mattermostUserGuide": "Mattermost user guide",
|
||||||
"userGuideHelp.reportAProblem": "Report a problem",
|
"userGuideHelp.reportAProblem": "Report a problem",
|
||||||
"userGuideHelp.trainingResources": "Training resources",
|
"userGuideHelp.trainingResources": "Training resources",
|
||||||
"users_limits_announcement_bar.copyText": "User limits exceeded. Contact administrator with: ERROR_SAFETY_LIMITS_EXCEEDED",
|
"users_limits_announcement_bar.copyText": "User limits exceeded. Contact administrator with: {ErrorCode}",
|
||||||
"users_limits_announcement_bar.ctaText": "Learn More",
|
"users_limits_announcement_bar.ctaText": "Learn More",
|
||||||
"userSettingsModal.pluginPreferences.header": "PLUGIN PREFERENCES",
|
"userSettingsModal.pluginPreferences.header": "PLUGIN PREFERENCES",
|
||||||
"version_bar.new": "A new version of Mattermost is available.",
|
"version_bar.new": "A new version of Mattermost is available.",
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
import keyMirror from 'mattermost-redux/utils/key_mirror';
|
import keyMirror from 'mattermost-redux/utils/key_mirror';
|
||||||
|
|
||||||
export default keyMirror({
|
export default keyMirror({
|
||||||
RECIEVED_USERS_LIMITS: null,
|
RECIEVED_APP_LIMITS: null,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default keyMirror({
|
|||||||
DISABLED_USER_ACCESS_TOKEN: null,
|
DISABLED_USER_ACCESS_TOKEN: null,
|
||||||
ENABLED_USER_ACCESS_TOKEN: null,
|
ENABLED_USER_ACCESS_TOKEN: null,
|
||||||
RECEIVED_USER_STATS: null,
|
RECEIVED_USER_STATS: null,
|
||||||
RECIEVED_USERS_LIMITS: null,
|
RECIEVED_APP_LIMITS: null,
|
||||||
RECEIVED_FILTERED_USER_STATS: null,
|
RECEIVED_FILTERED_USER_STATS: null,
|
||||||
PROFILE_NO_LONGER_VISIBLE: null,
|
PROFILE_NO_LONGER_VISIBLE: null,
|
||||||
LOGIN: null,
|
LOGIN: null,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import type {
|
|||||||
import type {DeepPartial} from '@mattermost/types/utilities';
|
import type {DeepPartial} from '@mattermost/types/utilities';
|
||||||
|
|
||||||
import {AdminTypes} from 'mattermost-redux/action_types';
|
import {AdminTypes} from 'mattermost-redux/action_types';
|
||||||
import {getUsersLimits} from 'mattermost-redux/actions/limits';
|
import {getServerLimits} from 'mattermost-redux/actions/limits';
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
import type {ActionFuncAsync} from 'mattermost-redux/types/actions';
|
import type {ActionFuncAsync} from 'mattermost-redux/types/actions';
|
||||||
|
|
||||||
@@ -386,7 +386,7 @@ export function removeLicense(): ActionFuncAsync<boolean> {
|
|||||||
return {error: error as ServerError};
|
return {error: error as ServerError};
|
||||||
}
|
}
|
||||||
|
|
||||||
await dispatch(getUsersLimits());
|
await dispatch(getServerLimits());
|
||||||
|
|
||||||
return {data: true};
|
return {data: true};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,18 +3,22 @@
|
|||||||
|
|
||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
|
||||||
|
import type {ServerLimits} from '@mattermost/types/limits';
|
||||||
|
|
||||||
import * as Actions from 'mattermost-redux/actions/limits';
|
import * as Actions from 'mattermost-redux/actions/limits';
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
|
|
||||||
import TestHelper from '../../test/test_helper';
|
import TestHelper from '../../test/test_helper';
|
||||||
import configureStore from '../../test/test_store';
|
import configureStore from '../../test/test_store';
|
||||||
|
|
||||||
describe('getUsersLimits', () => {
|
describe('getServerLimits', () => {
|
||||||
const URL_USERS_LIMITS = '/limits/users';
|
const URL_USERS_LIMITS = '/limits/server';
|
||||||
|
|
||||||
const defaultUserLimitsState = {
|
const defaultServerLimitsState: ServerLimits = {
|
||||||
activeUserCount: 0,
|
activeUserCount: 0,
|
||||||
maxUsersLimit: 0,
|
maxUsersLimit: 0,
|
||||||
|
maxPostLimit: 0,
|
||||||
|
postCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let store = configureStore();
|
let store = configureStore();
|
||||||
@@ -62,26 +66,28 @@ describe('getUsersLimits', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const {data} = await store.dispatch(Actions.getUsersLimits());
|
const {data} = await store.dispatch(Actions.getServerLimits());
|
||||||
expect(data).toEqual(defaultUserLimitsState);
|
expect(data).toEqual(defaultServerLimitsState);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not return default state for non admin users', async () => {
|
test('should not return default state for non admin users', async () => {
|
||||||
const {data} = await store.dispatch(Actions.getUsersLimits());
|
const {data} = await store.dispatch(Actions.getServerLimits());
|
||||||
expect(data).not.toEqual(defaultUserLimitsState);
|
expect(data).not.toEqual(defaultServerLimitsState);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return data if user is admin', async () => {
|
test('should return data if user is admin', async () => {
|
||||||
const userLimits = {
|
const userLimits: ServerLimits = {
|
||||||
activeUserCount: 600,
|
activeUserCount: 600,
|
||||||
maxUsersLimit: 10000,
|
maxUsersLimit: 10_000,
|
||||||
|
maxPostLimit: 5_000_000,
|
||||||
|
postCount: 10_000,
|
||||||
};
|
};
|
||||||
|
|
||||||
nock(Client4.getBaseRoute()).
|
nock(Client4.getBaseRoute()).
|
||||||
get(URL_USERS_LIMITS).
|
get(URL_USERS_LIMITS).
|
||||||
reply(200, userLimits);
|
reply(200, userLimits);
|
||||||
|
|
||||||
const {data} = await store.dispatch(Actions.getUsersLimits());
|
const {data} = await store.dispatch(Actions.getServerLimits());
|
||||||
expect(data).toEqual(userLimits);
|
expect(data).toEqual(userLimits);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,7 +97,7 @@ describe('getUsersLimits', () => {
|
|||||||
get(URL_USERS_LIMITS).
|
get(URL_USERS_LIMITS).
|
||||||
reply(400, {message: errorMessage});
|
reply(400, {message: errorMessage});
|
||||||
|
|
||||||
const {error} = await store.dispatch(Actions.getUsersLimits());
|
const {error} = await store.dispatch(Actions.getServerLimits());
|
||||||
console.log(error);
|
console.log(error);
|
||||||
expect(error.message).toEqual(errorMessage);
|
expect(error.message).toEqual(errorMessage);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import type {ServerError} from '@mattermost/types/errors';
|
import type {ServerError} from '@mattermost/types/errors';
|
||||||
import type {UsersLimits} from '@mattermost/types/limits';
|
import type {ServerLimits} from '@mattermost/types/limits';
|
||||||
|
|
||||||
import {LimitsTypes} from 'mattermost-redux/action_types';
|
import {LimitsTypes} from 'mattermost-redux/action_types';
|
||||||
import {logError} from 'mattermost-redux/actions/errors';
|
import {logError} from 'mattermost-redux/actions/errors';
|
||||||
@@ -12,7 +12,7 @@ import {getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
|
|||||||
import type {ActionFuncAsync} from 'mattermost-redux/types/actions';
|
import type {ActionFuncAsync} from 'mattermost-redux/types/actions';
|
||||||
import {isAdmin} from 'mattermost-redux/utils/user_utils';
|
import {isAdmin} from 'mattermost-redux/utils/user_utils';
|
||||||
|
|
||||||
export function getUsersLimits(): ActionFuncAsync<UsersLimits> {
|
export function getServerLimits(): ActionFuncAsync<ServerLimits> {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const roles = getCurrentUserRoles(getState());
|
const roles = getCurrentUserRoles(getState());
|
||||||
const amIAdmin = isAdmin(roles);
|
const amIAdmin = isAdmin(roles);
|
||||||
@@ -21,26 +21,31 @@ export function getUsersLimits(): ActionFuncAsync<UsersLimits> {
|
|||||||
data: {
|
data: {
|
||||||
activeUserCount: 0,
|
activeUserCount: 0,
|
||||||
maxUsersLimit: 0,
|
maxUsersLimit: 0,
|
||||||
|
postCount: 0,
|
||||||
|
maxPostLimit: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await Client4.getUsersLimits();
|
response = await Client4.getServerLimits();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
forceLogoutIfNecessary(err, dispatch, getState);
|
forceLogoutIfNecessary(err, dispatch, getState);
|
||||||
dispatch(logError(err));
|
dispatch(logError(err));
|
||||||
return {error: err as ServerError};
|
return {error: err as ServerError};
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: UsersLimits = {
|
const data: ServerLimits = {
|
||||||
activeUserCount: response?.data?.activeUserCount ?? 0,
|
activeUserCount: response?.data?.activeUserCount ?? 0,
|
||||||
maxUsersLimit: response?.data?.maxUsersLimit ?? 0,
|
maxUsersLimit: response?.data?.maxUsersLimit ?? 0,
|
||||||
|
postCount: response?.data?.postCount ?? 0,
|
||||||
|
maxPostLimit: response?.data?.maxPostLimit ?? 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatch({type: LimitsTypes.RECIEVED_USERS_LIMITS, data});
|
dispatch({type: LimitsTypes.RECIEVED_APP_LIMITS, data});
|
||||||
|
|
||||||
return {data};
|
return {data};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {UserTypes, AdminTypes} from 'mattermost-redux/action_types';
|
|||||||
import {logError} from 'mattermost-redux/actions/errors';
|
import {logError} from 'mattermost-redux/actions/errors';
|
||||||
import {setServerVersion, getClientConfig, getLicenseConfig} from 'mattermost-redux/actions/general';
|
import {setServerVersion, getClientConfig, getLicenseConfig} from 'mattermost-redux/actions/general';
|
||||||
import {bindClientFunc, forceLogoutIfNecessary, debounce} from 'mattermost-redux/actions/helpers';
|
import {bindClientFunc, forceLogoutIfNecessary, debounce} from 'mattermost-redux/actions/helpers';
|
||||||
import {getUsersLimits} from 'mattermost-redux/actions/limits';
|
import {getServerLimits} from 'mattermost-redux/actions/limits';
|
||||||
import {getMyPreferences} from 'mattermost-redux/actions/preferences';
|
import {getMyPreferences} from 'mattermost-redux/actions/preferences';
|
||||||
import {loadRolesIfNeeded} from 'mattermost-redux/actions/roles';
|
import {loadRolesIfNeeded} from 'mattermost-redux/actions/roles';
|
||||||
import {getMyTeams, getMyTeamMembers, getMyTeamUnreads} from 'mattermost-redux/actions/teams';
|
import {getMyTeams, getMyTeamMembers, getMyTeamUnreads} from 'mattermost-redux/actions/teams';
|
||||||
@@ -76,7 +76,7 @@ export function loadMe(): ActionFuncAsync<boolean> {
|
|||||||
const isCollapsedThreads = isCollapsedThreadsEnabled(getState());
|
const isCollapsedThreads = isCollapsedThreadsEnabled(getState());
|
||||||
await dispatch(getMyTeamUnreads(isCollapsedThreads));
|
await dispatch(getMyTeamUnreads(isCollapsedThreads));
|
||||||
|
|
||||||
await dispatch(getUsersLimits());
|
await dispatch(getServerLimits());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(logError(error as ServerError));
|
dispatch(logError(error as ServerError));
|
||||||
return {error: error as ServerError};
|
return {error: error as ServerError};
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import {combineReducers} from 'redux';
|
|||||||
|
|
||||||
import {LimitsTypes} from 'mattermost-redux/action_types';
|
import {LimitsTypes} from 'mattermost-redux/action_types';
|
||||||
|
|
||||||
function usersLimits(state = {}, action: AnyAction) {
|
function serverLimits(state = {}, action: AnyAction) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case LimitsTypes.RECIEVED_USERS_LIMITS: {
|
case LimitsTypes.RECIEVED_APP_LIMITS: {
|
||||||
const usersLimits = action.data;
|
const serverLimits = action.data;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...usersLimits,
|
...serverLimits,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -21,5 +21,5 @@ function usersLimits(state = {}, action: AnyAction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
usersLimits,
|
serverLimits,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import type {UsersLimits} from '@mattermost/types/limits';
|
import type {ServerLimits} from '@mattermost/types/limits';
|
||||||
import type {GlobalState} from '@mattermost/types/store';
|
import type {GlobalState} from '@mattermost/types/store';
|
||||||
|
|
||||||
export function getUsersLimits(state: GlobalState): UsersLimits {
|
export function getServerLimits(state: GlobalState): ServerLimits {
|
||||||
return state.entities.limits.usersLimits;
|
return state.entities.limits.serverLimits;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ const state: GlobalState = {
|
|||||||
dndEndTimes: {},
|
dndEndTimes: {},
|
||||||
},
|
},
|
||||||
limits: {
|
limits: {
|
||||||
usersLimits: {
|
serverLimits: {
|
||||||
activeUserCount: 0,
|
activeUserCount: 0,
|
||||||
maxUsersLimit: 0,
|
maxUsersLimit: 0,
|
||||||
|
postCount: 0,
|
||||||
|
maxPostLimit: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
teams: {
|
teams: {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ import type {
|
|||||||
SubmitDialogResponse,
|
SubmitDialogResponse,
|
||||||
} from '@mattermost/types/integrations';
|
} from '@mattermost/types/integrations';
|
||||||
import type {Job, JobTypeBase} from '@mattermost/types/jobs';
|
import type {Job, JobTypeBase} from '@mattermost/types/jobs';
|
||||||
import type {UsersLimits} from '@mattermost/types/limits';
|
import type {ServerLimits} from '@mattermost/types/limits';
|
||||||
import type {
|
import type {
|
||||||
MarketplaceApp,
|
MarketplaceApp,
|
||||||
MarketplacePlugin,
|
MarketplacePlugin,
|
||||||
@@ -498,8 +498,8 @@ export default class Client4 {
|
|||||||
return `${this.getBaseRoute()}/limits`;
|
return `${this.getBaseRoute()}/limits`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUsersLimitsRoute() {
|
getServerLimitsRoute() {
|
||||||
return `${this.getLimitsRoute()}/users`;
|
return `${this.getLimitsRoute()}/server`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCSRFFromCookie() {
|
getCSRFFromCookie() {
|
||||||
@@ -1220,9 +1220,9 @@ export default class Client4 {
|
|||||||
|
|
||||||
// Limits Routes
|
// Limits Routes
|
||||||
|
|
||||||
getUsersLimits = () => {
|
getServerLimits = () => {
|
||||||
return this.doFetchWithResponse<UsersLimits>(
|
return this.doFetchWithResponse<ServerLimits>(
|
||||||
`${this.getUsersLimitsRoute()}`,
|
`${this.getServerLimitsRoute()}`,
|
||||||
{
|
{
|
||||||
method: 'get',
|
method: 'get',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,10 +2,13 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
export type LimitsState = {
|
export type LimitsState = {
|
||||||
usersLimits: UsersLimits;
|
serverLimits: ServerLimits;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UsersLimits = {
|
export type ServerLimits = {
|
||||||
activeUserCount: number;
|
activeUserCount: number;
|
||||||
maxUsersLimit: number;
|
maxUsersLimit: number;
|
||||||
|
|
||||||
|
maxPostLimit: number;
|
||||||
|
postCount: number;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user