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("/saml_cert_status", ApiAdminSystemRequired(samlCertificateStatus)).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) {
|
||||
@@ -754,3 +755,37 @@ func samlCertificateStatus(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
"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",
|
||||
"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) {
|
||||
if r, err := c.DoApiGet("/admin/audits", "", ""); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -48,6 +48,7 @@ type User struct {
|
||||
Locale string `json:"locale"`
|
||||
MfaActive bool `json:"mfa_active,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
|
||||
|
||||
@@ -129,6 +129,28 @@ func (s SqlStatusStore) GetOnline() 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 {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
|
||||
@@ -275,6 +275,7 @@ type StatusStore interface {
|
||||
Get(userId string) StoreChannel
|
||||
GetOnlineAway() StoreChannel
|
||||
GetOnline() StoreChannel
|
||||
GetAllFromTeam(teamId string) StoreChannel
|
||||
ResetAll() StoreChannel
|
||||
GetTotalActiveUsersCount() StoreChannel
|
||||
UpdateLastActivityAt(userId string, lastActivityAt int64) StoreChannel
|
||||
|
||||
@@ -958,6 +958,15 @@ export default class Client {
|
||||
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) {
|
||||
request.
|
||||
get(`${this.getUsersRoute()}/direct_profiles`).
|
||||
|
||||
@@ -1138,7 +1138,7 @@ export function getRecentAndNewUsersAnalytics(teamId) {
|
||||
|
||||
callTracker[callName] = utils.getTimestamp();
|
||||
|
||||
Client.getProfilesForTeam(
|
||||
Client.getRecentlyActiveUsers(
|
||||
teamId,
|
||||
(users) => {
|
||||
const stats = {};
|
||||
|
||||
Ссылка в новой задаче
Block a user