2021-08-24 19:13:48 +03:00
|
|
|
import { css } from '@emotion/css';
|
2022-04-22 14:33:13 +01:00
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
|
2021-08-24 19:13:48 +03:00
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
2023-12-18 09:32:57 +01:00
|
|
|
import { config, GrafanaBootConfig } from '@grafana/runtime';
|
2023-12-06 16:06:30 +00:00
|
|
|
import { LinkButton, useStyles2 } from '@grafana/ui';
|
2021-08-27 11:09:17 +03:00
|
|
|
import { AccessControlAction } from 'app/types';
|
2022-04-22 14:33:13 +01:00
|
|
|
|
2021-08-24 19:13:48 +03:00
|
|
|
import { contextSrv } from '../../core/services/context_srv';
|
2022-04-22 14:33:13 +01:00
|
|
|
|
2023-12-06 16:06:30 +00:00
|
|
|
import { ServerStatsCard } from './ServerStatsCard';
|
2022-04-22 14:33:13 +01:00
|
|
|
import { getServerStats, ServerStat } from './state/apis';
|
2018-08-31 09:49:32 -07:00
|
|
|
|
2021-08-27 11:09:17 +03:00
|
|
|
export const ServerStats = () => {
|
2021-08-24 19:13:48 +03:00
|
|
|
const [stats, setStats] = useState<ServerStat | null>(null);
|
2023-12-06 16:06:30 +00:00
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
2021-08-24 19:13:48 +03:00
|
|
|
const styles = useStyles2(getStyles);
|
2018-08-31 09:49:32 -07:00
|
|
|
|
2023-09-06 16:07:49 +01:00
|
|
|
const hasAccessToDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesRead);
|
|
|
|
|
const hasAccessToAdminUsers = contextSrv.hasPermission(AccessControlAction.UsersRead);
|
2022-01-13 10:42:09 -03:00
|
|
|
|
2021-08-24 19:13:48 +03:00
|
|
|
useEffect(() => {
|
2023-09-06 16:07:49 +01:00
|
|
|
if (contextSrv.hasPermission(AccessControlAction.ActionServerStatsRead)) {
|
2021-10-05 14:54:26 +01:00
|
|
|
getServerStats().then((stats) => {
|
|
|
|
|
setStats(stats);
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-08-27 11:09:17 +03:00
|
|
|
}, []);
|
2018-08-31 09:49:32 -07:00
|
|
|
|
2023-09-06 16:07:49 +01:00
|
|
|
if (!contextSrv.hasPermission(AccessControlAction.ActionServerStatsRead)) {
|
2021-08-24 19:13:48 +03:00
|
|
|
return null;
|
2018-08-31 09:49:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2021-08-24 19:13:48 +03:00
|
|
|
<>
|
|
|
|
|
<h2 className={styles.title}>Instance statistics</h2>
|
2023-12-06 16:06:30 +00:00
|
|
|
{!isLoading && !stats ? (
|
|
|
|
|
<p className={styles.notFound}>No stats found.</p>
|
|
|
|
|
) : (
|
2021-08-24 19:13:48 +03:00
|
|
|
<div className={styles.row}>
|
2023-12-06 16:06:30 +00:00
|
|
|
<ServerStatsCard
|
|
|
|
|
isLoading={isLoading}
|
2021-08-24 19:13:48 +03:00
|
|
|
content={[
|
2023-12-06 16:06:30 +00:00
|
|
|
{ name: 'Dashboards (starred)', value: `${stats?.dashboards} (${stats?.stars})` },
|
|
|
|
|
{ name: 'Tags', value: stats?.tags },
|
|
|
|
|
{ name: 'Playlists', value: stats?.playlists },
|
|
|
|
|
{ name: 'Snapshots', value: stats?.snapshots },
|
2021-08-24 19:13:48 +03:00
|
|
|
]}
|
|
|
|
|
footer={
|
|
|
|
|
<LinkButton href={'/dashboards'} variant={'secondary'}>
|
|
|
|
|
Manage dashboards
|
|
|
|
|
</LinkButton>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div className={styles.doubleRow}>
|
2023-12-06 16:06:30 +00:00
|
|
|
<ServerStatsCard
|
|
|
|
|
isLoading={isLoading}
|
|
|
|
|
content={[{ name: 'Data sources', value: stats?.datasources }]}
|
2021-08-24 19:13:48 +03:00
|
|
|
footer={
|
2022-01-13 10:42:09 -03:00
|
|
|
hasAccessToDataSources && (
|
|
|
|
|
<LinkButton href={'/datasources'} variant={'secondary'}>
|
|
|
|
|
Manage data sources
|
|
|
|
|
</LinkButton>
|
|
|
|
|
)
|
2021-08-24 19:13:48 +03:00
|
|
|
}
|
|
|
|
|
/>
|
2023-12-06 16:06:30 +00:00
|
|
|
<ServerStatsCard
|
|
|
|
|
isLoading={isLoading}
|
|
|
|
|
content={[{ name: 'Alerts', value: stats?.alerts }]}
|
2021-08-24 19:13:48 +03:00
|
|
|
footer={
|
|
|
|
|
<LinkButton href={'/alerting/list'} variant={'secondary'}>
|
2023-12-18 09:32:57 +01:00
|
|
|
Manage alerts
|
2021-08-24 19:13:48 +03:00
|
|
|
</LinkButton>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2023-12-06 16:06:30 +00:00
|
|
|
<ServerStatsCard
|
|
|
|
|
isLoading={isLoading}
|
2021-08-24 19:13:48 +03:00
|
|
|
content={[
|
2023-12-06 16:06:30 +00:00
|
|
|
{ name: 'Organisations', value: stats?.orgs },
|
|
|
|
|
{ name: 'Users total', value: stats?.users },
|
|
|
|
|
{ name: 'Active sessions', value: stats?.activeSessions },
|
2023-12-18 09:32:57 +01:00
|
|
|
{ name: 'Active users in last 30 days', value: stats?.activeUsers },
|
|
|
|
|
...getAnonymousStatsContent(stats, config),
|
2021-08-24 19:13:48 +03:00
|
|
|
]}
|
|
|
|
|
footer={
|
2022-01-13 10:42:09 -03:00
|
|
|
hasAccessToAdminUsers && (
|
|
|
|
|
<LinkButton href={'/admin/users'} variant={'secondary'}>
|
|
|
|
|
Manage users
|
|
|
|
|
</LinkButton>
|
|
|
|
|
)
|
2021-08-24 19:13:48 +03:00
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
2018-08-31 09:49:32 -07:00
|
|
|
);
|
2021-08-24 19:13:48 +03:00
|
|
|
};
|
|
|
|
|
|
2023-12-18 09:32:57 +01:00
|
|
|
const getAnonymousStatsContent = (stats: ServerStat | null, config: GrafanaBootConfig) => {
|
2024-02-23 16:53:37 +01:00
|
|
|
if (!config.anonymousEnabled || !stats?.activeDevices) {
|
2023-12-18 09:32:57 +01:00
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
if (!config.anonymousDeviceLimit) {
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
name: 'Active anonymous devices',
|
|
|
|
|
value: `${stats.activeDevices}`,
|
|
|
|
|
tooltip: 'Detected devices that are not logged in, in last 30 days.',
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
name: 'Active anonymous devices',
|
|
|
|
|
value: `${stats.activeDevices} / ${config.anonymousDeviceLimit}`,
|
|
|
|
|
tooltip: 'Detected devices that are not logged in, in last 30 days.',
|
|
|
|
|
highlight: stats.activeDevices > config.anonymousDeviceLimit,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-24 19:13:48 +03:00
|
|
|
const getStyles = (theme: GrafanaTheme2) => {
|
|
|
|
|
return {
|
2023-12-06 16:06:30 +00:00
|
|
|
title: css({
|
|
|
|
|
marginBottom: theme.spacing(4),
|
|
|
|
|
}),
|
|
|
|
|
row: css({
|
|
|
|
|
display: 'flex',
|
|
|
|
|
justifyContent: 'space-between',
|
|
|
|
|
width: '100%',
|
|
|
|
|
|
|
|
|
|
'& > div:not(:last-of-type)': {
|
|
|
|
|
marginRight: theme.spacing(2),
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
'& > div': {
|
|
|
|
|
width: '33.3%',
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
doubleRow: css({
|
|
|
|
|
display: 'flex',
|
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
|
|
|
|
|
'& > div:first-of-type': {
|
|
|
|
|
marginBottom: theme.spacing(2),
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
notFound: css({
|
|
|
|
|
fontSize: theme.typography.h6.fontSize,
|
|
|
|
|
textAlign: 'center',
|
|
|
|
|
height: '290px',
|
|
|
|
|
}),
|
2021-08-24 19:13:48 +03:00
|
|
|
};
|
|
|
|
|
};
|