index.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import React, { useState } from 'react';
  2. import { dateTime } from '@grafana/data';
  3. import { config } from '@grafana/runtime';
  4. import { addBodyRenderHook } from 'app/AppWrapper';
  5. import { contextSrv } from 'app/core/services/context_srv';
  6. import store from 'app/core/store';
  7. import { OrgRole } from 'app/types';
  8. import { AccessControlAction } from '../types';
  9. import { IsInvalid, HasExpired, ExpiresSoon, TokenExpiresSoon, MaxUsersReached } from './LicenseWarning';
  10. import {
  11. DISMISS_WARNING_FOR_DAYS,
  12. LICENSE_WARNING_DISMISS_UNTIL_KEY,
  13. WARNING_CLOSE_TIMEOUT_SEC,
  14. LIMIT_BY_USERS,
  15. } from './constants';
  16. import { refreshLicenseStats } from './state/api';
  17. import { ActiveUserStats } from './types';
  18. interface LicensingSettings {
  19. activeAdminsAndEditors?: number;
  20. activeViewers?: number;
  21. activeUsers?: number;
  22. limitBy?: string;
  23. includedAdmins?: number;
  24. includedViewers?: number;
  25. includedUsers?: number;
  26. slug?: string;
  27. licenseExpiry?: number;
  28. licenseExpiryWarnDays?: number;
  29. tokenExpiry?: number;
  30. tokenExpiryWarnDays?: number;
  31. usageBilling?: boolean;
  32. isTrial?: boolean;
  33. }
  34. export function initLicenseWarnings() {
  35. addBodyRenderHook(LicenseWarning);
  36. }
  37. export function LicenseWarning() {
  38. const [isClosed, setIsClosed] = useState(false);
  39. const [settings, updateSettings] = useState((config as any).licensing as LicensingSettings);
  40. const dismissUntil = store.get(LICENSE_WARNING_DISMISS_UNTIL_KEY);
  41. const hasDismissed = dismissUntil && dismissUntil > dateTime().valueOf();
  42. // true if the user has licensing:read permission if RBAC is enabled or is an organisation admin if RBAC is disabled
  43. const isLicensingReaderOrAdmin = contextSrv.hasAccess(
  44. AccessControlAction.LicensingRead,
  45. contextSrv.hasRole(OrgRole.Admin)
  46. );
  47. // true if the user has licensing:read permission if RBAC is enabled or is a Grafana server admin if RBAC is disabled
  48. const isLicensingReaderOrGrafanaAdmin = contextSrv.hasAccess(
  49. AccessControlAction.LicensingRead,
  50. contextSrv.isGrafanaAdmin
  51. );
  52. const showExpireSoon = isLicensingReaderOrAdmin && willExpireSoon() && !hasDismissed;
  53. const showTokenExpireSoon = isLicensingReaderOrAdmin && tokenWillExpireSoon() && !hasDismissed;
  54. const usageBillingDisabled = isUsageBillingDisabled();
  55. let showMaxUsersReached = false;
  56. let activeUsers = 0;
  57. let maxUsers = 0;
  58. if (settings.limitBy === LIMIT_BY_USERS) {
  59. const maxUsersReached = numberOfActiveUsersReached(settings.includedUsers, settings.activeUsers);
  60. showMaxUsersReached =
  61. usageBillingDisabled && maxUsersReached && (isLicensingReaderOrAdmin || isLicensingReaderOrGrafanaAdmin);
  62. activeUsers = settings.activeUsers!;
  63. maxUsers = settings.includedUsers!;
  64. }
  65. if (isRenderingPanel() || isLicenseAdminPage()) {
  66. return null;
  67. }
  68. const onCloseWarning = () => {
  69. const dismissTill = dateTime().add(DISMISS_WARNING_FOR_DAYS, 'd').valueOf();
  70. store.set(LICENSE_WARNING_DISMISS_UNTIL_KEY, dismissTill);
  71. setIsClosed(true);
  72. };
  73. const onRefreshWarning = async () => {
  74. const activeUserStats: ActiveUserStats | null = await refreshLicenseStats().catch((err) => null);
  75. if (activeUserStats) {
  76. const update = {
  77. ...settings,
  78. activeUsers: activeUserStats.active_users,
  79. };
  80. // update on config object as well
  81. (config as any).licensing = update;
  82. updateSettings(update);
  83. }
  84. };
  85. if (isClosed) {
  86. return null;
  87. }
  88. if (isInvalid()) {
  89. return <IsInvalid isLicensingReader={isLicensingReaderOrGrafanaAdmin} />;
  90. } else if (hasExpired()) {
  91. return <HasExpired isLicensingReader={isLicensingReaderOrGrafanaAdmin} />;
  92. } else if (showMaxUsersReached) {
  93. return (
  94. <MaxUsersReached
  95. activeUsers={activeUsers}
  96. maxUsers={maxUsers}
  97. slug={settings.slug}
  98. onRefreshWarning={isLicensingReaderOrAdmin ? onRefreshWarning : undefined}
  99. />
  100. );
  101. } else if (showExpireSoon) {
  102. const expiresIn = willExpireInDays();
  103. // auto hide expire warning in case it's a TV monitor with admin permissions
  104. setTimeout(onCloseWarning, 1000 * WARNING_CLOSE_TIMEOUT_SEC);
  105. return (
  106. <ExpiresSoon
  107. days={expiresIn}
  108. onCloseWarning={onCloseWarning}
  109. isLicensingReader={isLicensingReaderOrGrafanaAdmin}
  110. />
  111. );
  112. } else if (showTokenExpireSoon) {
  113. const expiresIn = tokenWillExpireInDays();
  114. // auto hide expire warning in case it's a TV monitor with admin permissions
  115. setTimeout(onCloseWarning, 1000 * WARNING_CLOSE_TIMEOUT_SEC);
  116. return (
  117. <TokenExpiresSoon
  118. days={expiresIn}
  119. onCloseWarning={onCloseWarning}
  120. isLicensingReader={isLicensingReaderOrGrafanaAdmin}
  121. />
  122. );
  123. }
  124. return null;
  125. }
  126. export function isInvalid(): boolean {
  127. const { expiry, hasLicense } = (config as any).licenseInfo;
  128. return hasLicense && !expiry;
  129. }
  130. export function willExpireSoon(): boolean {
  131. const { licenseExpiry, licenseExpiryWarnDays = 30 } = (config as any).licensing;
  132. return licenseExpiry > 0 && dateTime(licenseExpiry * 1000) < dateTime().add(licenseExpiryWarnDays, 'd');
  133. }
  134. export function willExpireInDays(): number {
  135. const { licenseExpiry } = (config as any).licensing;
  136. return Math.ceil((licenseExpiry - dateTime().unix()) / 3600 / 24);
  137. }
  138. export function hasExpired(): boolean {
  139. const { licenseExpiry } = (config as any).licensing;
  140. return licenseExpiry > 0 && dateTime(licenseExpiry * 1000) < dateTime();
  141. }
  142. function tokenWillExpireSoon(): boolean {
  143. const { tokenExpiry, tokenExpiryWarnDays = 3 } = (config as any).licensing;
  144. return tokenExpiry > 0 && dateTime(tokenExpiry * 1000) < dateTime().add(tokenExpiryWarnDays, 'd');
  145. }
  146. function tokenWillExpireInDays(): number {
  147. const { tokenExpiry } = (config as any).licensing;
  148. return Math.ceil((tokenExpiry - dateTime().unix()) / 3600 / 24);
  149. }
  150. export function numberOfActiveUsersReached(includedUsers?: number, activeUsers?: number): boolean {
  151. if (includedUsers === undefined || activeUsers === undefined) {
  152. return false;
  153. }
  154. return includedUsers !== -1 && activeUsers > includedUsers;
  155. }
  156. function isUsageBillingDisabled(): boolean {
  157. const settings = (config as any).licensing as LicensingSettings;
  158. return !settings.usageBilling;
  159. }
  160. function isSoloPanel(): boolean {
  161. const soloPanelPattern = /\/d-solo\//;
  162. const path = window.location.pathname;
  163. return soloPanelPattern.test(path);
  164. }
  165. function isRenderingPanel(): boolean {
  166. return isSoloPanel();
  167. }
  168. function isLicenseAdminPage(): boolean {
  169. const pattern = /\/admin\/licensing$/;
  170. const path = window.location.pathname;
  171. return pattern.test(path);
  172. }