Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
e8a20643
Commit
e8a20643
authored
Aug 09, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: store last seen date for users and present in stats and user lists, closes #9007
parent
26ad0257
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
248 additions
and
83 deletions
+248
-83
pkg/metrics/publish.go
+11
-9
pkg/middleware/middleware.go
+10
-1
pkg/models/org_user.go
+7
-5
pkg/models/stats.go
+17
-14
pkg/models/user.go
+20
-7
pkg/services/sqlstore/migrations/user_mig.go
+4
-0
pkg/services/sqlstore/org_users.go
+11
-3
pkg/services/sqlstore/sqlstore.go
+1
-1
pkg/services/sqlstore/stats.go
+35
-18
pkg/services/sqlstore/user.go
+56
-14
pkg/util/strings.go
+34
-0
pkg/util/strings_test.go
+13
-0
public/app/features/admin/partials/stats.html
+13
-9
public/app/features/admin/partials/users.html
+11
-2
public/app/features/org/partials/orgUsers.html
+5
-0
No files found.
pkg/metrics/publish.go
View file @
e8a20643
...
...
@@ -67,10 +67,10 @@ func updateTotalStats() {
return
}
M_StatTotal_Dashboards
.
Update
(
statsQuery
.
Result
.
Dashboard
Count
)
M_StatTotal_Users
.
Update
(
statsQuery
.
Result
.
User
Count
)
M_StatTotal_Playlists
.
Update
(
statsQuery
.
Result
.
Playlist
Count
)
M_StatTotal_Orgs
.
Update
(
statsQuery
.
Result
.
Org
Count
)
M_StatTotal_Dashboards
.
Update
(
statsQuery
.
Result
.
Dashboard
s
)
M_StatTotal_Users
.
Update
(
statsQuery
.
Result
.
User
s
)
M_StatTotal_Playlists
.
Update
(
statsQuery
.
Result
.
Playlist
s
)
M_StatTotal_Orgs
.
Update
(
statsQuery
.
Result
.
Org
s
)
}
}
...
...
@@ -97,14 +97,16 @@ func sendUsageStats() {
return
}
metrics
[
"stats.dashboards.count"
]
=
statsQuery
.
Result
.
Dashboard
Count
metrics
[
"stats.users.count"
]
=
statsQuery
.
Result
.
User
Count
metrics
[
"stats.orgs.count"
]
=
statsQuery
.
Result
.
Org
Count
metrics
[
"stats.playlist.count"
]
=
statsQuery
.
Result
.
Playlist
Count
metrics
[
"stats.dashboards.count"
]
=
statsQuery
.
Result
.
Dashboard
s
metrics
[
"stats.users.count"
]
=
statsQuery
.
Result
.
User
s
metrics
[
"stats.orgs.count"
]
=
statsQuery
.
Result
.
Org
s
metrics
[
"stats.playlist.count"
]
=
statsQuery
.
Result
.
Playlist
s
metrics
[
"stats.plugins.apps.count"
]
=
len
(
plugins
.
Apps
)
metrics
[
"stats.plugins.panels.count"
]
=
len
(
plugins
.
Panels
)
metrics
[
"stats.plugins.datasources.count"
]
=
len
(
plugins
.
DataSources
)
metrics
[
"stats.alerts.count"
]
=
statsQuery
.
Result
.
AlertCount
metrics
[
"stats.alerts.count"
]
=
statsQuery
.
Result
.
Alerts
metrics
[
"stats.active_users.count"
]
=
statsQuery
.
Result
.
ActiveUsers
metrics
[
"stats.datasources.count"
]
=
statsQuery
.
Result
.
Datasources
dsStats
:=
m
.
GetDataSourceStatsQuery
{}
if
err
:=
bus
.
Dispatch
(
&
dsStats
);
err
!=
nil
{
...
...
pkg/middleware/middleware.go
View file @
e8a20643
...
...
@@ -62,6 +62,15 @@ func GetContextHandler() macaron.Handler {
ctx
.
Data
[
"ctx"
]
=
ctx
c
.
Map
(
ctx
)
// update last seen at
// update last seen every 5min
if
ctx
.
ShouldUpdateLastSeenAt
()
{
ctx
.
Logger
.
Debug
(
"Updating last user_seen_at"
,
"user_id"
,
ctx
.
UserId
)
if
err
:=
bus
.
Dispatch
(
&
m
.
UpdateUserLastSeenAtCommand
{
UserId
:
ctx
.
UserId
});
err
!=
nil
{
ctx
.
Logger
.
Error
(
"Failed to update last_seen_at"
,
"error"
,
err
)
}
}
}
}
...
...
@@ -99,7 +108,7 @@ func initContextWithUserSessionCookie(ctx *Context, orgId int64) bool {
query
:=
m
.
GetSignedInUserQuery
{
UserId
:
userId
,
OrgId
:
orgId
}
if
err
:=
bus
.
Dispatch
(
&
query
);
err
!=
nil
{
ctx
.
Logger
.
Error
(
"Failed to get user with id"
,
"userId"
,
userId
)
ctx
.
Logger
.
Error
(
"Failed to get user with id"
,
"userId"
,
userId
,
"error"
,
err
)
return
false
}
...
...
pkg/models/org_user.go
View file @
e8a20643
...
...
@@ -103,9 +103,11 @@ type GetOrgUsersQuery struct {
// Projections and DTOs
type
OrgUserDTO
struct
{
OrgId
int64
`json:"orgId"`
UserId
int64
`json:"userId"`
Email
string
`json:"email"`
Login
string
`json:"login"`
Role
string
`json:"role"`
OrgId
int64
`json:"orgId"`
UserId
int64
`json:"userId"`
Email
string
`json:"email"`
Login
string
`json:"login"`
Role
string
`json:"role"`
LastSeenAt
time
.
Time
`json:"lastSeenAt"`
LastSeenAtAge
string
`json:"lastSeenAtAge"`
}
pkg/models/stats.go
View file @
e8a20643
package
models
type
SystemStats
struct
{
DashboardCount
int64
UserCount
int64
OrgCount
int64
PlaylistCount
int64
AlertCount
int64
Dashboards
int64
Datasources
int64
Users
int64
ActiveUsers
int64
Orgs
int64
Playlists
int64
Alerts
int64
}
type
DataSourceStats
struct
{
...
...
@@ -22,15 +24,16 @@ type GetDataSourceStatsQuery struct {
}
type
AdminStats
struct
{
UserCount
int
`json:"user_count"`
OrgCount
int
`json:"org_count"`
DashboardCount
int
`json:"dashboard_count"`
DbSnapshotCount
int
`json:"db_snapshot_count"`
DbTagCount
int
`json:"db_tag_count"`
DataSourceCount
int
`json:"data_source_count"`
PlaylistCount
int
`json:"playlist_count"`
StarredDbCount
int
`json:"starred_db_count"`
AlertCount
int
`json:"alert_count"`
Users
int
`json:"users"`
Orgs
int
`json:"orgs"`
Dashboards
int
`json:"dashboards"`
Snapshots
int
`json:"snapshots"`
Tags
int
`json:"tags"`
Datasources
int
`json:"datasources"`
Playlists
int
`json:"playlists"`
Stars
int
`json:"stars"`
Alerts
int
`json:"alerts"`
ActiveUsers
int
`json:"activeUsers"`
}
type
GetAdminStatsQuery
struct
{
...
...
pkg/models/user.go
View file @
e8a20643
...
...
@@ -33,8 +33,9 @@ type User struct {
IsAdmin
bool
OrgId
int64
Created
time
.
Time
Updated
time
.
Time
Created
time
.
Time
Updated
time
.
Time
LastSeenAt
time
.
Time
}
func
(
u
*
User
)
NameOrFallback
()
string
{
...
...
@@ -127,6 +128,7 @@ type GetUserProfileQuery struct {
}
type
SearchUsersQuery
struct
{
OrgId
int64
Query
string
Page
int
Limit
int
...
...
@@ -160,6 +162,15 @@ type SignedInUser struct {
ApiKeyId
int64
IsGrafanaAdmin
bool
HelpFlags1
HelpFlags1
LastSeenAt
time
.
Time
}
func
(
u
*
SignedInUser
)
ShouldUpdateLastSeenAt
()
bool
{
return
u
.
UserId
>
0
&&
time
.
Since
(
u
.
LastSeenAt
)
>
time
.
Minute
*
5
}
type
UpdateUserLastSeenAtCommand
struct
{
UserId
int64
}
type
UserProfileDTO
struct
{
...
...
@@ -173,11 +184,13 @@ type UserProfileDTO struct {
}
type
UserSearchHitDTO
struct
{
Id
int64
`json:"id"`
Name
string
`json:"name"`
Login
string
`json:"login"`
Email
string
`json:"email"`
IsAdmin
bool
`json:"isAdmin"`
Id
int64
`json:"id"`
Name
string
`json:"name"`
Login
string
`json:"login"`
Email
string
`json:"email"`
IsAdmin
bool
`json:"isAdmin"`
LastSeenAt
time
.
Time
`json:"lastSeenAt"`
LastSeenAtAge
string
`json:"lastSeenAtAge"`
}
type
UserIdDTO
struct
{
...
...
pkg/services/sqlstore/migrations/user_mig.go
View file @
e8a20643
...
...
@@ -103,4 +103,8 @@ func addUserMigrations(mg *Migrator) {
{
Name
:
"company"
,
Type
:
DB_NVarchar
,
Length
:
255
,
Nullable
:
true
},
{
Name
:
"theme"
,
Type
:
DB_NVarchar
,
Length
:
255
,
Nullable
:
true
},
}))
mg
.
AddMigration
(
"Add last_seen_at column to user"
,
NewAddColumnMigration
(
userV2
,
&
Column
{
Name
:
"last_seen_at"
,
Type
:
DB_DateTime
,
Nullable
:
true
,
}))
}
pkg/services/sqlstore/org_users.go
View file @
e8a20643
...
...
@@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus"
m
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
func
init
()
{
...
...
@@ -71,11 +72,18 @@ func GetOrgUsers(query *m.GetOrgUsersQuery) error {
sess
:=
x
.
Table
(
"org_user"
)
sess
.
Join
(
"INNER"
,
"user"
,
fmt
.
Sprintf
(
"org_user.user_id=%s.id"
,
x
.
Dialect
()
.
Quote
(
"user"
)))
sess
.
Where
(
"org_user.org_id=?"
,
query
.
OrgId
)
sess
.
Cols
(
"org_user.org_id"
,
"org_user.user_id"
,
"user.email"
,
"user.login"
,
"org_user.role"
)
sess
.
Cols
(
"org_user.org_id"
,
"org_user.user_id"
,
"user.email"
,
"user.login"
,
"org_user.role"
,
"user.last_seen_at"
)
sess
.
Asc
(
"user.email"
,
"user.login"
)
err
:=
sess
.
Find
(
&
query
.
Result
)
return
err
if
err
:=
sess
.
Find
(
&
query
.
Result
);
err
!=
nil
{
return
err
}
for
_
,
user
:=
range
query
.
Result
{
user
.
LastSeenAtAge
=
util
.
GetAgeString
(
user
.
LastSeenAt
)
}
return
nil
}
func
RemoveOrgUser
(
cmd
*
m
.
RemoveOrgUserCommand
)
error
{
...
...
pkg/services/sqlstore/sqlstore.go
View file @
e8a20643
...
...
@@ -53,7 +53,7 @@ func EnsureAdminUser() {
return
}
if
statsQuery
.
Result
.
User
Count
>
0
{
if
statsQuery
.
Result
.
User
s
>
0
{
return
}
...
...
pkg/services/sqlstore/stats.go
View file @
e8a20643
package
sqlstore
import
(
"time"
"github.com/grafana/grafana/pkg/bus"
m
"github.com/grafana/grafana/pkg/models"
)
...
...
@@ -11,6 +13,8 @@ func init() {
bus
.
AddHandler
(
"sql"
,
GetAdminStats
)
}
var
activeUserTimeLimit
time
.
Duration
=
time
.
Hour
*
24
*
14
func
GetDataSourceStats
(
query
*
m
.
GetDataSourceStatsQuery
)
error
{
var
rawSql
=
`SELECT COUNT(*) as count, type FROM data_source GROUP BY type`
query
.
Result
=
make
([]
*
m
.
DataSourceStats
,
0
)
...
...
@@ -27,27 +31,35 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error {
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"user"
)
+
`
) AS user
_count
,
) AS user
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"org"
)
+
`
) AS org
_count
,
) AS org
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"dashboard"
)
+
`
) AS dashboard_count,
) AS dashboards,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"data_source"
)
+
`
) AS datasources,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"playlist"
)
+
`
) AS playlist
_count
,
) AS playlist
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"alert"
)
+
`
) AS alert_count
) AS alerts,
(
SELECT COUNT(*) FROM `
+
dialect
.
Quote
(
"user"
)
+
` where last_seen_at > ?
) as active_users
`
activeUserDeadlineDate
:=
time
.
Now
()
.
Add
(
-
activeUserTimeLimit
)
var
stats
m
.
SystemStats
_
,
err
:=
x
.
Sql
(
rawSql
)
.
Get
(
&
stats
)
_
,
err
:=
x
.
Sql
(
rawSql
,
activeUserDeadlineDate
)
.
Get
(
&
stats
)
if
err
!=
nil
{
return
err
}
...
...
@@ -61,43 +73,48 @@ func GetAdminStats(query *m.GetAdminStatsQuery) error {
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"user"
)
+
`
) AS user
_count
,
) AS user
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"org"
)
+
`
) AS org
_count
,
) AS org
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"dashboard"
)
+
`
) AS dashboard
_count
,
) AS dashboard
s
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"dashboard_snapshot"
)
+
`
) AS
db_snapshot_count
,
) AS
snapshots
,
(
SELECT COUNT( DISTINCT ( `
+
dialect
.
Quote
(
"term"
)
+
` ))
FROM `
+
dialect
.
Quote
(
"dashboard_tag"
)
+
`
) AS
db_tag_count
,
) AS
tags
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"data_source"
)
+
`
) AS data
_source_count
,
) AS data
sources
,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"playlist"
)
+
`
) AS playlist
_count
,
) AS playlist
s
,
(
SELECT COUNT(DISTINCT `
+
dialect
.
Quote
(
"dashboard_id"
)
+
` )
FROM `
+
dialect
.
Quote
(
"star"
)
+
`
) AS starred_db_count,
SELECT COUNT(*) FROM `
+
dialect
.
Quote
(
"star"
)
+
`
) AS stars,
(
SELECT COUNT(*)
FROM `
+
dialect
.
Quote
(
"alert"
)
+
`
) AS alert_count
) AS alerts,
(
SELECT COUNT(*)
from `
+
dialect
.
Quote
(
"user"
)
+
` where last_seen_at > ?
) as active_users
`
activeUserDeadlineDate
:=
time
.
Now
()
.
Add
(
-
activeUserTimeLimit
)
var
stats
m
.
AdminStats
_
,
err
:=
x
.
Sql
(
rawSql
)
.
Get
(
&
stats
)
_
,
err
:=
x
.
Sql
(
rawSql
,
activeUserDeadlineDate
)
.
Get
(
&
stats
)
if
err
!=
nil
{
return
err
}
...
...
pkg/services/sqlstore/user.go
View file @
e8a20643
...
...
@@ -22,6 +22,7 @@ func init() {
bus
.
AddHandler
(
"sql"
,
GetUserByLogin
)
bus
.
AddHandler
(
"sql"
,
GetUserByEmail
)
bus
.
AddHandler
(
"sql"
,
SetUsingOrg
)
bus
.
AddHandler
(
"sql"
,
UpdateUserLastSeenAt
)
bus
.
AddHandler
(
"sql"
,
GetUserProfile
)
bus
.
AddHandler
(
"sql"
,
GetSignedInUser
)
bus
.
AddHandler
(
"sql"
,
SearchUsers
)
...
...
@@ -260,6 +261,24 @@ func ChangeUserPassword(cmd *m.ChangeUserPasswordCommand) error {
})
}
func
UpdateUserLastSeenAt
(
cmd
*
m
.
UpdateUserLastSeenAtCommand
)
error
{
return
inTransaction
(
func
(
sess
*
DBSession
)
error
{
if
cmd
.
UserId
<=
0
{
}
user
:=
m
.
User
{
Id
:
cmd
.
UserId
,
LastSeenAt
:
time
.
Now
(),
}
if
_
,
err
:=
sess
.
Id
(
cmd
.
UserId
)
.
Update
(
&
user
);
err
!=
nil
{
return
err
}
return
nil
})
}
func
SetUsingOrg
(
cmd
*
m
.
SetUsingOrgCommand
)
error
{
getOrgsForUserCmd
:=
&
m
.
GetUserOrgListQuery
{
UserId
:
cmd
.
UserId
}
GetUserOrgList
(
getOrgsForUserCmd
)
...
...
@@ -324,15 +343,16 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error {
}
var
rawSql
=
`SELECT
u.id as user_id,
u.is_admin as is_grafana_admin,
u.email as email,
u.login as login,
u.name as name,
u.help_flags1 as help_flags1,
org.name as org_name,
org_user.role as org_role,
org.id as org_id
u.id as user_id,
u.is_admin as is_grafana_admin,
u.email as email,
u.login as login,
u.name as name,
u.help_flags1 as help_flags1,
u.last_seen_at as last_seen_at,
org.name as org_name,
org_user.role as org_role,
org.id as org_id
FROM `
+
dialect
.
Quote
(
"user"
)
+
` as u
LEFT OUTER JOIN org_user on org_user.org_id = `
+
orgId
+
` and org_user.user_id = u.id
LEFT OUTER JOIN org on org.id = org_user.org_id `
...
...
@@ -367,27 +387,49 @@ func SearchUsers(query *m.SearchUsersQuery) error {
query
.
Result
=
m
.
SearchUserQueryResult
{
Users
:
make
([]
*
m
.
UserSearchHitDTO
,
0
),
}
queryWithWildcards
:=
"%"
+
query
.
Query
+
"%"
whereConditions
:=
make
([]
string
,
0
)
whereParams
:=
make
([]
interface
{},
0
)
sess
:=
x
.
Table
(
"user"
)
if
query
.
OrgId
>
0
{
whereConditions
=
append
(
whereConditions
,
"org_id = ?"
)
whereParams
=
append
(
whereParams
,
query
.
OrgId
)
}
if
query
.
Query
!=
""
{
sess
.
Where
(
"email LIKE ? OR name LIKE ? OR login like ?"
,
queryWithWildcards
,
queryWithWildcards
,
queryWithWildcards
)
whereConditions
=
append
(
whereConditions
,
"(email LIKE ? OR name LIKE ? OR login like ?)"
)
whereParams
=
append
(
whereParams
,
queryWithWildcards
,
queryWithWildcards
,
queryWithWildcards
)
}
if
len
(
whereConditions
)
>
0
{
sess
.
Where
(
strings
.
Join
(
whereConditions
,
" AND "
),
whereParams
...
)
}
offset
:=
query
.
Limit
*
(
query
.
Page
-
1
)
sess
.
Limit
(
query
.
Limit
,
offset
)
sess
.
Cols
(
"id"
,
"email"
,
"name"
,
"login"
,
"is_admin"
)
sess
.
Cols
(
"id"
,
"email"
,
"name"
,
"login"
,
"is_admin"
,
"last_seen_at"
)
if
err
:=
sess
.
Find
(
&
query
.
Result
.
Users
);
err
!=
nil
{
return
err
}
// get total
user
:=
m
.
User
{}
countSess
:=
x
.
Table
(
"user"
)
if
query
.
Query
!=
""
{
countSess
.
Where
(
"email LIKE ? OR name LIKE ? OR login like ?"
,
queryWithWildcards
,
queryWithWildcards
,
queryWithWildcards
)
if
len
(
whereConditions
)
>
0
{
countSess
.
Where
(
strings
.
Join
(
whereConditions
,
" AND "
),
whereParams
...
)
}
count
,
err
:=
countSess
.
Count
(
&
user
)
query
.
Result
.
TotalCount
=
count
for
_
,
user
:=
range
query
.
Result
.
Users
{
user
.
LastSeenAtAge
=
util
.
GetAgeString
(
user
.
LastSeenAt
)
}
return
err
}
...
...
pkg/util/strings.go
View file @
e8a20643
package
util
import
(
"fmt"
"math"
"regexp"
"time"
)
func
StringsFallback2
(
val1
string
,
val2
string
)
string
{
...
...
@@ -28,3 +31,34 @@ func SplitString(str string) []string {
return
regexp
.
MustCompile
(
"[, ]+"
)
.
Split
(
str
,
-
1
)
}
func
GetAgeString
(
t
time
.
Time
)
string
{
if
t
.
IsZero
()
{
return
"?"
}
sinceNow
:=
time
.
Since
(
t
)
minutes
:=
sinceNow
.
Minutes
()
years
:=
int
(
math
.
Floor
(
minutes
/
525600
))
months
:=
int
(
math
.
Floor
(
minutes
/
43800
))
days
:=
int
(
math
.
Floor
(
minutes
/
1440
))
hours
:=
int
(
math
.
Floor
(
minutes
/
60
))
if
years
>
0
{
return
fmt
.
Sprintf
(
"%dy"
,
years
)
}
if
months
>
0
{
return
fmt
.
Sprintf
(
"%dM"
,
months
)
}
if
days
>
0
{
return
fmt
.
Sprintf
(
"%dd"
,
days
)
}
if
hours
>
0
{
return
fmt
.
Sprintf
(
"%dh"
,
hours
)
}
if
int
(
minutes
)
>
0
{
return
fmt
.
Sprintf
(
"%dm"
,
int
(
minutes
))
}
return
"< 1m"
}
pkg/util/strings_test.go
View file @
e8a20643
...
...
@@ -2,6 +2,7 @@ package util
import
(
"testing"
"time"
.
"github.com/smartystreets/goconvey/convey"
)
...
...
@@ -24,3 +25,15 @@ func TestSplitString(t *testing.T) {
So
(
SplitString
(
"test1 , test2 test3"
),
ShouldResemble
,
[]
string
{
"test1"
,
"test2"
,
"test3"
})
})
}
func
TestDateAge
(
t
*
testing
.
T
)
{
Convey
(
"GetAgeString"
,
t
,
func
()
{
So
(
GetAgeString
(
time
.
Time
{}),
ShouldEqual
,
"?"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Second
*
2
)),
ShouldEqual
,
"< 1m"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Minute
*
2
)),
ShouldEqual
,
"2m"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Hour
*
2
)),
ShouldEqual
,
"2h"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Hour
*
24
*
3
)),
ShouldEqual
,
"3d"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Hour
*
24
*
67
)),
ShouldEqual
,
"2M"
)
So
(
GetAgeString
(
time
.
Now
()
.
Add
(
-
time
.
Hour
*
24
*
409
)),
ShouldEqual
,
"1y"
)
})
}
public/app/features/admin/partials/stats.html
View file @
e8a20643
...
...
@@ -15,39 +15,43 @@
<tbody>
<tr>
<td>
Total dashboards
</td>
<td>
{{ctrl.stats.dashboard
_count
}}
</td>
<td>
{{ctrl.stats.dashboard
s
}}
</td>
</tr>
<tr>
<td>
Total users
</td>
<td>
{{ctrl.stats.user_count}}
</td>
<td>
{{ctrl.stats.users}}
</td>
</tr>
<tr>
<td>
Active users (seen last 14 days)
</td>
<td>
{{ctrl.stats.activeUsers}}
</td>
</tr>
<tr>
<td>
Total organizations
</td>
<td>
{{ctrl.stats.org
_count
}}
</td>
<td>
{{ctrl.stats.org
s
}}
</td>
</tr>
<tr>
<td>
Total datasources
</td>
<td>
{{ctrl.stats.data
_source_count
}}
</td>
<td>
{{ctrl.stats.data
sources
}}
</td>
</tr>
<tr>
<td>
Total playlists
</td>
<td>
{{ctrl.stats.playlist
_count
}}
</td>
<td>
{{ctrl.stats.playlist
s
}}
</td>
</tr>
<tr>
<td>
Total snapshots
</td>
<td>
{{ctrl.stats.
db_snapshot_count
}}
</td>
<td>
{{ctrl.stats.
snapshots
}}
</td>
</tr>
<tr>
<td>
Total dashboard tags
</td>
<td>
{{ctrl.stats.
db_tag_count
}}
</td>
<td>
{{ctrl.stats.
tags
}}
</td>
</tr>
<tr>
<td>
Total starred dashboards
</td>
<td>
{{ctrl.stats.star
red_db_count
}}
</td>
<td>
{{ctrl.stats.star
s
}}
</td>
</tr>
<tr>
<td>
Total alerts
</td>
<td>
{{ctrl.stats.alert
_count
}}
</td>
<td>
{{ctrl.stats.alert
s
}}
</td>
</tr>
</tbody>
</table>
...
...
public/app/features/admin/partials/users.html
View file @
e8a20643
...
...
@@ -25,7 +25,11 @@
<th>
Name
</th>
<th>
Login
</th>
<th>
Email
</th>
<th
style=
"white-space: nowrap"
>
Grafana Admin
</th>
<th>
Seen
<tip>
Time since user was seen using Grafana
</tip>
</th>
<th></th>
<th></th>
</tr>
</thead>
...
...
@@ -35,7 +39,12 @@
<td>
{{user.name}}
</td>
<td>
{{user.login}}
</td>
<td>
{{user.email}}
</td>
<td>
{{user.isAdmin}}
</td>
<td>
{{user.lastSeenAtAge}}
</td>
<td>
<i
class=
"fa fa-shield"
ng-show=
"user.isAdmin"
bs-tooltip=
"'Grafana Admin'"
></i>
</td>
<td
class=
"text-right"
>
<a
href=
"admin/users/edit/{{user.id}}"
class=
"btn btn-inverse btn-small"
>
<i
class=
"fa fa-edit"
></i>
...
...
public/app/features/org/partials/orgUsers.html
View file @
e8a20643
...
...
@@ -41,6 +41,10 @@
<tr>
<th>
Login
</th>
<th>
Email
</th>
<th>
Seen
<tip>
Time since user was seen using Grafana
</tip>
</th>
<th>
Role
</th>
<th
style=
"width: 34px;"
></th>
</tr>
...
...
@@ -48,6 +52,7 @@
<tr
ng-repeat=
"user in ctrl.users"
>
<td>
{{user.login}}
</td>
<td><span
class=
"ellipsis"
>
{{user.email}}
</span></td>
<td>
{{user.lastSeenAtAge}}
</td>
<td>
<select
type=
"text"
ng-model=
"user.role"
class=
"input-medium"
ng-options=
"f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']"
ng-change=
"ctrl.updateOrgUser(user)"
>
</select>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment