PLT-3921 Fix System Console Recent Active Users (#3856)
* PLT-3921 System Console Recent Active Users
Этот коммит содержится в:
35
api/admin.go
35
api/admin.go
@@ -48,6 +48,7 @@ func InitAdmin() {
|
|||||||
BaseRoutes.Admin.Handle("/remove_certificate", ApiAdminSystemRequired(removeCertificate)).Methods("POST")
|
BaseRoutes.Admin.Handle("/remove_certificate", ApiAdminSystemRequired(removeCertificate)).Methods("POST")
|
||||||
BaseRoutes.Admin.Handle("/saml_cert_status", ApiAdminSystemRequired(samlCertificateStatus)).Methods("GET")
|
BaseRoutes.Admin.Handle("/saml_cert_status", ApiAdminSystemRequired(samlCertificateStatus)).Methods("GET")
|
||||||
BaseRoutes.Admin.Handle("/cluster_status", ApiAdminSystemRequired(getClusterStatus)).Methods("GET")
|
BaseRoutes.Admin.Handle("/cluster_status", ApiAdminSystemRequired(getClusterStatus)).Methods("GET")
|
||||||
|
BaseRoutes.Admin.Handle("/recently_active_users/{team_id:[A-Za-z0-9]+}", ApiUserRequiredActivity(getRecentlyActiveUsers, false)).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -754,3 +755,37 @@ func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.Write([]byte(model.StringInterfaceToJson(status)))
|
w.Write([]byte(model.StringInterfaceToJson(status)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRecentlyActiveUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
statusMap := map[string]interface{}{}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Status().GetAllFromTeam(c.TeamId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
statuses := result.Data.([]*model.Status)
|
||||||
|
for _, s := range statuses {
|
||||||
|
statusMap[s.UserId] = s.LastActivityAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.User().GetProfiles(c.TeamId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
profiles := result.Data.(map[string]*model.User)
|
||||||
|
|
||||||
|
for k, p := range profiles {
|
||||||
|
p = sanitizeProfile(c, p)
|
||||||
|
|
||||||
|
if lastActivityAt, ok := statusMap[p.Id].(int64); ok {
|
||||||
|
p.LastActivityAt = lastActivityAt
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles[k] = p
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(model.UserMapToJson(profiles)))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -527,3 +527,18 @@ func TestAdminLdapSyncNow(t *testing.T) {
|
|||||||
t.Fatal("Returned Failure")
|
t.Fatal("Returned Failure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetRecentlyActiveUsers(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
|
||||||
|
user1Id := th.BasicUser.Id
|
||||||
|
user2Id := th.BasicUser2.Id
|
||||||
|
|
||||||
|
if userMap, err := th.BasicClient.GetRecentlyActiveUsers(th.BasicTeam.Id); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if len(userMap.Data.(map[string]*model.User)) != 2 {
|
||||||
|
t.Fatal("should have been 2")
|
||||||
|
} else if userMap.Data.(map[string]*model.User)[user1Id].Id != user1Id || userMap.Data.(map[string]*model.User)[user2Id].Id != user2Id {
|
||||||
|
t.Fatal("should have been valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4283,6 +4283,10 @@
|
|||||||
"id": "store.sql_status.get_online_away.app_error",
|
"id": "store.sql_status.get_online_away.app_error",
|
||||||
"translation": "Encountered an error retrieving all the online/away statuses"
|
"translation": "Encountered an error retrieving all the online/away statuses"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_status.get_team_statuses.app_error",
|
||||||
|
"translation": "Encountered an error retrieving all statuses from the team members"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "store.sql_status.get_total_active_users_count.app_error",
|
"id": "store.sql_status.get_total_active_users_count.app_error",
|
||||||
"translation": "We could not count the active users"
|
"translation": "We could not count the active users"
|
||||||
|
|||||||
@@ -820,6 +820,17 @@ func (c *Client) GetClusterStatus() ([]*ClusterInfo, *AppError) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRecentlyActiveUsers returns a map of users including lastActivityAt using user id as the key
|
||||||
|
func (c *Client) GetRecentlyActiveUsers(teamId string) (*Result, *AppError) {
|
||||||
|
if r, err := c.DoApiGet("/admin/recently_active_users/"+teamId, "", ""); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer closeBody(r)
|
||||||
|
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||||
|
r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) GetAllAudits() (*Result, *AppError) {
|
func (c *Client) GetAllAudits() (*Result, *AppError) {
|
||||||
if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
|
if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type User struct {
|
|||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
MfaActive bool `json:"mfa_active,omitempty"`
|
MfaActive bool `json:"mfa_active,omitempty"`
|
||||||
MfaSecret string `json:"mfa_secret,omitempty"`
|
MfaSecret string `json:"mfa_secret,omitempty"`
|
||||||
|
LastActivityAt int64 `db:"-" json:"last_activity_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid validates the user and returns an error if it isn't configured
|
// IsValid validates the user and returns an error if it isn't configured
|
||||||
|
|||||||
@@ -129,6 +129,28 @@ func (s SqlStatusStore) GetOnline() StoreChannel {
|
|||||||
return storeChannel
|
return storeChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s SqlStatusStore) GetAllFromTeam(teamId string) StoreChannel {
|
||||||
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
result := StoreResult{}
|
||||||
|
|
||||||
|
var statuses []*model.Status
|
||||||
|
if _, err := s.GetReplica().Select(&statuses,
|
||||||
|
`SELECT s.* FROM Status AS s INNER JOIN
|
||||||
|
TeamMembers AS tm ON tm.TeamId=:TeamId AND s.UserId=tm.UserId`, map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||||
|
result.Err = model.NewLocAppError("SqlStatusStore.GetAllFromTeam", "store.sql_status.get_team_statuses.app_error", nil, err.Error())
|
||||||
|
} else {
|
||||||
|
result.Data = statuses
|
||||||
|
}
|
||||||
|
|
||||||
|
storeChannel <- result
|
||||||
|
close(storeChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return storeChannel
|
||||||
|
}
|
||||||
|
|
||||||
func (s SqlStatusStore) ResetAll() StoreChannel {
|
func (s SqlStatusStore) ResetAll() StoreChannel {
|
||||||
storeChannel := make(StoreChannel)
|
storeChannel := make(StoreChannel)
|
||||||
|
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ type StatusStore interface {
|
|||||||
Get(userId string) StoreChannel
|
Get(userId string) StoreChannel
|
||||||
GetOnlineAway() StoreChannel
|
GetOnlineAway() StoreChannel
|
||||||
GetOnline() StoreChannel
|
GetOnline() StoreChannel
|
||||||
|
GetAllFromTeam(teamId string) StoreChannel
|
||||||
ResetAll() StoreChannel
|
ResetAll() StoreChannel
|
||||||
GetTotalActiveUsersCount() StoreChannel
|
GetTotalActiveUsersCount() StoreChannel
|
||||||
UpdateLastActivityAt(userId string, lastActivityAt int64) StoreChannel
|
UpdateLastActivityAt(userId string, lastActivityAt int64) StoreChannel
|
||||||
|
|||||||
@@ -958,6 +958,15 @@ export default class Client {
|
|||||||
end(this.handleResponse.bind(this, 'getAudits', success, error));
|
end(this.handleResponse.bind(this, 'getAudits', success, error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRecentlyActiveUsers(id, success, error) {
|
||||||
|
request.
|
||||||
|
get(`${this.getAdminRoute()}/recently_active_users/${id}`).
|
||||||
|
set(this.defaultHeaders).
|
||||||
|
type('application/json').
|
||||||
|
accept('application/json').
|
||||||
|
end(this.handleResponse.bind(this, 'getRecentlyActiveUsers', success, error));
|
||||||
|
}
|
||||||
|
|
||||||
getDirectProfiles(success, error) {
|
getDirectProfiles(success, error) {
|
||||||
request.
|
request.
|
||||||
get(`${this.getUsersRoute()}/direct_profiles`).
|
get(`${this.getUsersRoute()}/direct_profiles`).
|
||||||
|
|||||||
@@ -1138,7 +1138,7 @@ export function getRecentAndNewUsersAnalytics(teamId) {
|
|||||||
|
|
||||||
callTracker[callName] = utils.getTimestamp();
|
callTracker[callName] = utils.getTimestamp();
|
||||||
|
|
||||||
Client.getProfilesForTeam(
|
Client.getRecentlyActiveUsers(
|
||||||
teamId,
|
teamId,
|
||||||
(users) => {
|
(users) => {
|
||||||
const stats = {};
|
const stats = {};
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user