diff --git a/pkg/api/dtos/frontend_settings.go b/pkg/api/dtos/frontend_settings.go new file mode 100644 index 00000000000..778ab9683d3 --- /dev/null +++ b/pkg/api/dtos/frontend_settings.go @@ -0,0 +1,227 @@ +package dtos + +import ( + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/setting" +) + +type FrontendSettingsAuthDTO struct { + OAuthSkipOrgRoleUpdateSync bool `json:"OAuthSkipOrgRoleUpdateSync"` + SAMLSkipOrgRoleSync bool `json:"SAMLSkipOrgRoleSync"` + LDAPSkipOrgRoleSync bool `json:"LDAPSkipOrgRoleSync"` + GoogleSkipOrgRoleSync bool `json:"GoogleSkipOrgRoleSync"` + JWTAuthSkipOrgRoleSync bool `json:"JWTAuthSkipOrgRoleSync"` + GrafanaComSkipOrgRoleSync bool `json:"GrafanaComSkipOrgRoleSync"` + AzureADSkipOrgRoleSync bool `json:"AzureADSkipOrgRoleSync"` + GithubSkipOrgRoleSync bool `json:"GithubSkipOrgRoleSync"` + GitLabSkipOrgRoleSync bool `json:"GitLabSkipOrgRoleSync"` + OktaSkipOrgRoleSync bool `json:"OktaSkipOrgRoleSync"` + DisableSyncLock bool `json:"DisableSyncLock"` +} + +type FrontendSettingsBuildInfoDTO struct { + HideVersion bool `json:"hideVersion"` + Version string `json:"version"` + Commit string `json:"commit"` + Buildstamp int64 `json:"buildstamp"` + Edition string `json:"edition"` + LatestVersion string `json:"latestVersion"` + HasUpdate bool `json:"hasUpdate"` + Env string `json:"env"` +} + +type FrontendSettingsLicenseInfoDTO struct { + Expiry int64 `json:"expiry"` + StateInfo string `json:"stateInfo"` + LicenseUrl string `json:"licenseUrl"` + Edition string `json:"edition"` + EnabledFeatures map[string]bool `json:"enabledFeatures"` + + // Enterprise-only + TrialExpiry *int64 `json:"trialExpiry,omitempty"` + AppUrl *string `json:"appUrl,omitempty"` +} + +type FrontendSettingsAzureDTO struct { + Cloud string `json:"cloud"` + ManagedIdentityEnabled bool `json:"managedIdentityEnabled"` +} + +type FrontendSettingsCachingDTO struct { + Enabled bool `json:"enabled"` +} + +type FrontendSettingsRecordedQueriesDTO struct { + Enabled bool `json:"enabled"` +} + +type FrontendSettingsReportingDTO struct { + Enabled bool `json:"enabled"` +} + +type FrontendSettingsUnifiedAlertingDTO struct { + MinInterval string `json:"minInterval"` +} + +type DashboardPreviewsSystemRequirements struct { + Met bool `json:"met"` + RequiredImageRendererPluginVersion string `json:"requiredImageRendererPluginVersion"` +} + +type DashboardPreviewsSetupConfig struct { + SystemRequirements DashboardPreviewsSystemRequirements `json:"systemRequirements"` + ThumbnailsExist bool `json:"thumbnailsExist"` +} + +// Enterprise-only +type FrontendSettingsLicensingDTO struct { + Slug *string `json:"slug,omitempty"` + LimitBy *string `json:"limitBy,omitempty"` + IncludedUsers *int64 `json:"includedUsers,omitempty"` + LicenseExpiry *int64 `json:"licenseExpiry,omitempty"` + LicenseExpiryWarnDays *int64 `json:"licenseExpiryWarnDays,omitempty"` + TokenExpiry *int64 `json:"tokenExpiry,omitempty"` + IsTrial *bool `json:"isTrial,omitempty"` + TokenExpiryWarnDays *int64 `json:"tokenExpiryWarnDays,omitempty"` + UsageBilling *bool `json:"usageBilling,omitempty"` + ActiveAdminsAndEditors *int64 `json:"activeAdminsAndEditors,omitempty"` + ActiveViewers *int64 `json:"activeViewers,omitempty"` + ActiveUsers *int64 `json:"ActiveUsers,omitempty"` +} + +// Enterprise-only +type FrontendSettingsFooterConfigItemDTO struct { + Text string `json:"text"` + Url string `json:"url"` + Icon string `json:"icon"` + Target string `json:"blank"` +} + +// Enterprise-only +type FrontendSettingsPublicDashboardFooterConfigDTO struct { + Hide bool `json:"hide"` + Text string `json:"text"` + Logo string `json:"logo"` + Link string `json:"link"` +} + +// Enterprise-only +type FrontendSettingsWhitelabelingDTO struct { + Links []FrontendSettingsFooterConfigItemDTO `json:"links"` + LoginTitle string `json:"loginTitle"` + + AppTitle *string `json:"appTitle,omitempty"` + LoginLogo *string `json:"loginLogo,omitempty"` + MenuLogo *string `json:"menuLogo,omitempty"` + LoginBackground *string `json:"loginBackground,omitempty"` + LoginSubtitle *string `json:"loginSubtitle,omitempty"` + LoginBoxBackground *string `json:"loginBoxBackground,omitempty"` + LoadingLogo *string `json:"loadingLogo,omitempty"` + PublicDashboardFooter *FrontendSettingsPublicDashboardFooterConfigDTO `json:"publicDashboardFooter,omitempty"` // PR TODO: type this properly +} + +type FrontendSettingsDTO struct { + DefaultDatasource string `json:"defaultDatasource"` + Datasources map[string]plugins.DataSourceDTO `json:"datasources"` + MinRefreshInterval string `json:"minRefreshInterval"` + Panels map[string]plugins.PanelDTO `json:"panels"` + AppUrl string `json:"appUrl"` + AppSubUrl string `json:"appSubUrl"` + AllowOrgCreate bool `json:"allowOrgCreate"` + AuthProxyEnabled bool `json:"authProxyEnabled"` + LdapEnabled bool `json:"ldapEnabled"` + JwtHeaderName string `json:"jwtHeaderName"` + JwtUrlLogin bool `json:"jwtUrlLogin"` + AlertingEnabled bool `json:"alertingEnabled"` + AlertingErrorOrTimeout string `json:"alertingErrorOrTimeout"` + AlertingNoDataOrNullValues string `json:"alertingNoDataOrNullValues"` + AlertingMinInterval int64 `json:"alertingMinInterval"` + LiveEnabled bool `json:"liveEnabled"` + AutoAssignOrg bool `json:"autoAssignOrg"` + + VerifyEmailEnabled bool `json:"verifyEmailEnabled"` + SigV4AuthEnabled bool `json:"sigV4AuthEnabled"` + AzureAuthEnabled bool `json:"azureAuthEnabled"` + RbacEnabled bool `json:"rbacEnabled"` + ExploreEnabled bool `json:"exploreEnabled"` + HelpEnabled bool `json:"helpEnabled"` + ProfileEnabled bool `json:"profileEnabled"` + QueryHistoryEnabled bool `json:"queryHistoryEnabled"` + + GoogleAnalyticsId string `json:"googleAnalyticsId"` + GoogleAnalytics4Id string `json:"googleAnalytics4Id"` + GoogleAnalytics4SendManualPageViews bool `json:"GoogleAnalytics4SendManualPageViews"` + + RudderstackWriteKey string `json:"rudderstackWriteKey"` + RudderstackDataPlaneUrl string `json:"rudderstackDataPlaneUrl"` + RudderstackSdkUrl string `json:"rudderstackSdkUrl"` + RudderstackConfigUrl string `json:"rudderstackConfigUrl"` + + FeedbackLinksEnabled bool `json:"feedbackLinksEnabled"` + ApplicationInsightsConnectionString string `json:"applicationInsightsConnectionString"` + ApplicationInsightsEndpointUrl string `json:"applicationInsightsEndpointUrl"` + DisableLoginForm bool `json:"disableLoginForm"` + DisableUserSignUp bool `json:"disableUserSignUp"` + LoginHint string `json:"loginHint"` + PasswordHint string `json:"passwordHint"` + ExternalUserMngInfo string `json:"externalUserMngInfo"` + ExternalUserMngLinkUrl string `json:"externalUserMngLinkUrl"` + ExternalUserMngLinkName string `json:"externalUserMngLinkName"` + ViewersCanEdit bool `json:"viewersCanEdit"` + AngularSupportEnabled bool `json:"angularSupportEnabled"` + EditorsCanAdmin bool `json:"editorsCanAdmin"` + DisableSanitizeHtml bool `json:"disableSanitizeHtml"` + PluginsToPreload []*plugins.PreloadPlugin `json:"pluginsToPreload"` + + Auth FrontendSettingsAuthDTO `json:"auth"` + + BuildInfo FrontendSettingsBuildInfoDTO `json:"buildInfo"` + + LicenseInfo FrontendSettingsLicenseInfoDTO `json:"licenseInfo"` + + FeatureToggles map[string]bool `json:"featureToggles"` + RendererAvailable bool `json:"rendererAvailable"` + RendererVersion string `json:"rendererVersion"` + SecretsManagerPluginEnabled bool `json:"secretsManagerPluginEnabled"` + Http2Enabled bool `json:"http2Enabled"` + Sentry setting.Sentry `json:"sentry"` + GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"` + PluginCatalogURL string `json:"pluginCatalogURL"` + PluginAdminEnabled bool `json:"pluginAdminEnabled"` + PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"` + PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"` + ExpressionsEnabled bool `json:"expressionsEnabled"` + AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"` + AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"` + SupportBundlesEnabled bool `json:"supportBundlesEnabled"` + SnapshotEnabled bool `json:"snapshotEnabled"` + + Azure FrontendSettingsAzureDTO `json:"azure"` + + Caching FrontendSettingsCachingDTO `json:"caching"` + RecordedQueries FrontendSettingsRecordedQueriesDTO `json:"recordedQueries"` + Reporting FrontendSettingsReportingDTO `json:"reporting"` + UnifiedAlertingEnabled bool `json:"unifiedAlertingEnabled"` + UnifiedAlerting FrontendSettingsUnifiedAlertingDTO `json:"unifiedAlerting"` + Oauth map[string]interface{} `json:"oauth"` + SamlEnabled bool `json:"samlEnabled"` + SamlName string `json:"samlName"` + TokenExpirationDayLimit int `json:"tokenExpirationDayLimit"` + + DashboardPreviews DashboardPreviewsSetupConfig `json:"dashboardPreviews,omitempty"` + + GeomapDefaultBaseLayerConfig *map[string]interface{} `json:"geomapDefaultBaseLayerConfig,omitempty"` + GeomapDisableCustomBaseLayer bool `json:"geomapDisableCustomBaseLayer"` + + IsPublicDashboardView bool `json:"isPublicDashboardView"` + + DateFormats setting.DateFormats `json:"dateFormats,omitempty"` + + LoginError string `json:"loginError,omitempty"` + + PluginsCDNBaseURL string `json:"pluginsCDNBaseURL,omitempty"` + + // Enterprise + Licensing *FrontendSettingsLicensingDTO `json:"licensing,omitempty"` + Whitelabeling *FrontendSettingsWhitelabelingDTO `json:"whitelabeling,omitempty"` +} diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index 049a13f0425..8e5face1a97 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -9,7 +9,7 @@ import ( type IndexViewData struct { User *CurrentUser - Settings map[string]interface{} + Settings *FrontendSettingsDTO AppUrl string AppSubUrl string GoogleAnalyticsId string diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 130b8d34a91..ebdb2c50308 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/accesscontrol" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" @@ -20,7 +21,7 @@ import ( ) func (hs *HTTPServer) GetFrontendSettings(c *contextmodel.ReqContext) { - settings, err := hs.getFrontendSettingsMap(c) + settings, err := hs.getFrontendSettings(c) if err != nil { c.JsonApiErr(400, "Failed to get frontend settings", err) return @@ -29,8 +30,8 @@ func (hs *HTTPServer) GetFrontendSettings(c *contextmodel.ReqContext) { c.JSON(http.StatusOK, settings) } -// getFrontendSettingsMap returns a json object with all the settings needed for front end initialisation. -func (hs *HTTPServer) getFrontendSettingsMap(c *contextmodel.ReqContext) (map[string]interface{}, error) { +// getFrontendSettings returns a json object with all the settings needed for front end initialisation. +func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.FrontendSettingsDTO, error) { enabledPlugins, err := hs.enabledPlugins(c.Req.Context(), c.OrgID) if err != nil { return nil, err @@ -97,121 +98,137 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *contextmodel.ReqContext) (map[st hasAccess := accesscontrol.HasAccess(hs.AccessControl, c) secretsManagerPluginEnabled := kvstore.EvaluateRemoteSecretsPlugin(c.Req.Context(), hs.secretsPluginManager, hs.Cfg) == nil - jsonObj := map[string]interface{}{ - "defaultDatasource": defaultDS, - "datasources": dataSources, - "minRefreshInterval": setting.MinRefreshInterval, - "panels": panels, - "appUrl": hs.Cfg.AppURL, - "appSubUrl": hs.Cfg.AppSubURL, - "allowOrgCreate": (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, - "authProxyEnabled": setting.AuthProxyEnabled, - "ldapEnabled": hs.Cfg.LDAPEnabled, - "jwtHeaderName": hs.Cfg.JWTAuthHeaderName, - "jwtUrlLogin": hs.Cfg.JWTAuthURLLogin, - "alertingEnabled": setting.AlertingEnabled, - "alertingErrorOrTimeout": setting.AlertingErrorOrTimeout, - "alertingNoDataOrNullValues": setting.AlertingNoDataOrNullValues, - "alertingMinInterval": setting.AlertingMinInterval, - "liveEnabled": hs.Cfg.LiveMaxConnections != 0, - "autoAssignOrg": setting.AutoAssignOrg, - "verifyEmailEnabled": setting.VerifyEmailEnabled, - "sigV4AuthEnabled": setting.SigV4AuthEnabled, - "azureAuthEnabled": setting.AzureAuthEnabled, - "rbacEnabled": hs.Cfg.RBACEnabled, - "exploreEnabled": setting.ExploreEnabled, - "helpEnabled": setting.HelpEnabled, - "profileEnabled": setting.ProfileEnabled, - "queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled, - "googleAnalyticsId": setting.GoogleAnalyticsId, - "googleAnalytics4Id": setting.GoogleAnalytics4Id, - "GoogleAnalytics4SendManualPageViews": setting.GoogleAnalytics4SendManualPageViews, - "rudderstackWriteKey": setting.RudderstackWriteKey, - "rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl, - "rudderstackSdkUrl": setting.RudderstackSdkUrl, - "rudderstackConfigUrl": setting.RudderstackConfigUrl, - "feedbackLinksEnabled": hs.Cfg.FeedbackLinksEnabled, - "applicationInsightsConnectionString": hs.Cfg.ApplicationInsightsConnectionString, - "applicationInsightsEndpointUrl": hs.Cfg.ApplicationInsightsEndpointUrl, - "disableLoginForm": setting.DisableLoginForm, - "disableUserSignUp": !setting.AllowUserSignUp, - "loginHint": setting.LoginHint, - "passwordHint": setting.PasswordHint, - "externalUserMngInfo": setting.ExternalUserMngInfo, - "externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl, - "externalUserMngLinkName": setting.ExternalUserMngLinkName, - "viewersCanEdit": setting.ViewersCanEdit, - "angularSupportEnabled": hs.Cfg.AngularSupportEnabled, - "editorsCanAdmin": hs.Cfg.EditorsCanAdmin, - "disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml, - "pluginsToPreload": pluginsToPreload, - "auth": map[string]interface{}{ - "OAuthSkipOrgRoleUpdateSync": hs.Cfg.OAuthSkipOrgRoleUpdateSync, - "SAMLSkipOrgRoleSync": hs.Cfg.SectionWithEnvOverrides("auth.saml").Key("skip_org_role_sync").MustBool(false), - "LDAPSkipOrgRoleSync": hs.Cfg.LDAPSkipOrgRoleSync, - "GithubSkipOrgRoleSync": hs.Cfg.GithubSkipOrgRoleSync, - "GoogleSkipOrgRoleSync": hs.Cfg.GoogleSkipOrgRoleSync, - "JWTAuthSkipOrgRoleSync": hs.Cfg.JWTAuthSkipOrgRoleSync, - "GrafanaComSkipOrgRoleSync": hs.Cfg.GrafanaComSkipOrgRoleSync, - "GitLabSkipOrgRoleSync": hs.Cfg.GitLabSkipOrgRoleSync, - "AzureADSkipOrgRoleSync": hs.Cfg.AzureADSkipOrgRoleSync, - "OktaSkipOrgRoleSync": hs.Cfg.OktaSkipOrgRoleSync, - "DisableSyncLock": hs.Cfg.DisableSyncLock, + frontendSettings := &dtos.FrontendSettingsDTO{ + DefaultDatasource: defaultDS, + Datasources: dataSources, + MinRefreshInterval: setting.MinRefreshInterval, + Panels: panels, + AppUrl: hs.Cfg.AppURL, + AppSubUrl: hs.Cfg.AppSubURL, + AllowOrgCreate: (setting.AllowUserOrgCreate && c.IsSignedIn) || c.IsGrafanaAdmin, + AuthProxyEnabled: setting.AuthProxyEnabled, + LdapEnabled: hs.Cfg.LDAPEnabled, + JwtHeaderName: hs.Cfg.JWTAuthHeaderName, + JwtUrlLogin: hs.Cfg.JWTAuthURLLogin, + AlertingErrorOrTimeout: setting.AlertingErrorOrTimeout, + AlertingNoDataOrNullValues: setting.AlertingNoDataOrNullValues, + AlertingMinInterval: setting.AlertingMinInterval, + LiveEnabled: hs.Cfg.LiveMaxConnections != 0, + AutoAssignOrg: setting.AutoAssignOrg, + VerifyEmailEnabled: setting.VerifyEmailEnabled, + SigV4AuthEnabled: setting.SigV4AuthEnabled, + AzureAuthEnabled: setting.AzureAuthEnabled, + RbacEnabled: hs.Cfg.RBACEnabled, + ExploreEnabled: setting.ExploreEnabled, + HelpEnabled: setting.HelpEnabled, + ProfileEnabled: setting.ProfileEnabled, + QueryHistoryEnabled: hs.Cfg.QueryHistoryEnabled, + GoogleAnalyticsId: setting.GoogleAnalyticsId, + GoogleAnalytics4Id: setting.GoogleAnalytics4Id, + GoogleAnalytics4SendManualPageViews: setting.GoogleAnalytics4SendManualPageViews, + RudderstackWriteKey: setting.RudderstackWriteKey, + RudderstackDataPlaneUrl: setting.RudderstackDataPlaneUrl, + RudderstackSdkUrl: setting.RudderstackSdkUrl, + RudderstackConfigUrl: setting.RudderstackConfigUrl, + FeedbackLinksEnabled: hs.Cfg.FeedbackLinksEnabled, + ApplicationInsightsConnectionString: hs.Cfg.ApplicationInsightsConnectionString, + ApplicationInsightsEndpointUrl: hs.Cfg.ApplicationInsightsEndpointUrl, + DisableLoginForm: setting.DisableLoginForm, + DisableUserSignUp: !setting.AllowUserSignUp, + LoginHint: setting.LoginHint, + PasswordHint: setting.PasswordHint, + ExternalUserMngInfo: setting.ExternalUserMngInfo, + ExternalUserMngLinkUrl: setting.ExternalUserMngLinkUrl, + ExternalUserMngLinkName: setting.ExternalUserMngLinkName, + ViewersCanEdit: setting.ViewersCanEdit, + AngularSupportEnabled: hs.Cfg.AngularSupportEnabled, + EditorsCanAdmin: hs.Cfg.EditorsCanAdmin, + DisableSanitizeHtml: hs.Cfg.DisableSanitizeHtml, + PluginsToPreload: pluginsToPreload, + DateFormats: hs.Cfg.DateFormats, + + Auth: dtos.FrontendSettingsAuthDTO{ + OAuthSkipOrgRoleUpdateSync: hs.Cfg.OAuthSkipOrgRoleUpdateSync, + SAMLSkipOrgRoleSync: hs.Cfg.SectionWithEnvOverrides("auth.saml").Key("skip_org_role_sync").MustBool(false), + LDAPSkipOrgRoleSync: hs.Cfg.LDAPSkipOrgRoleSync, + GoogleSkipOrgRoleSync: hs.Cfg.GoogleSkipOrgRoleSync, + JWTAuthSkipOrgRoleSync: hs.Cfg.JWTAuthSkipOrgRoleSync, + GrafanaComSkipOrgRoleSync: hs.Cfg.GrafanaComSkipOrgRoleSync, + AzureADSkipOrgRoleSync: hs.Cfg.AzureADSkipOrgRoleSync, + GithubSkipOrgRoleSync: hs.Cfg.GithubSkipOrgRoleSync, + GitLabSkipOrgRoleSync: hs.Cfg.GitLabSkipOrgRoleSync, + OktaSkipOrgRoleSync: hs.Cfg.OktaSkipOrgRoleSync, + DisableSyncLock: hs.Cfg.DisableSyncLock, }, - "buildInfo": map[string]interface{}{ - "hideVersion": hideVersion, - "version": version, - "commit": commit, - "buildstamp": buildstamp, - "edition": hs.License.Edition(), - "latestVersion": hs.grafanaUpdateChecker.LatestVersion(), - "hasUpdate": hs.grafanaUpdateChecker.UpdateAvailable(), - "env": setting.Env, + + BuildInfo: dtos.FrontendSettingsBuildInfoDTO{ + HideVersion: hideVersion, + Version: version, + Commit: commit, + Buildstamp: buildstamp, + Edition: hs.License.Edition(), + LatestVersion: hs.grafanaUpdateChecker.LatestVersion(), + HasUpdate: hs.grafanaUpdateChecker.UpdateAvailable(), + Env: setting.Env, }, - "licenseInfo": map[string]interface{}{ - "expiry": hs.License.Expiry(), - "stateInfo": hs.License.StateInfo(), - "licenseUrl": hs.License.LicenseURL(hasAccess(accesscontrol.ReqGrafanaAdmin, licensing.PageAccess)), - "edition": hs.License.Edition(), - "enabledFeatures": hs.License.EnabledFeatures(), + + LicenseInfo: dtos.FrontendSettingsLicenseInfoDTO{ + Expiry: hs.License.Expiry(), + StateInfo: hs.License.StateInfo(), + LicenseUrl: hs.License.LicenseURL(hasAccess(accesscontrol.ReqGrafanaAdmin, licensing.PageAccess)), + Edition: hs.License.Edition(), + EnabledFeatures: hs.License.EnabledFeatures(), }, - "featureToggles": hs.Features.GetEnabled(c.Req.Context()), - "rendererAvailable": hs.RenderService.IsAvailable(c.Req.Context()), - "rendererVersion": hs.RenderService.Version(), - "secretsManagerPluginEnabled": secretsManagerPluginEnabled, - "http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme, - "sentry": hs.Cfg.Sentry, - "grafanaJavascriptAgent": hs.Cfg.GrafanaJavascriptAgent, - "pluginCatalogURL": hs.Cfg.PluginCatalogURL, - "pluginAdminEnabled": hs.Cfg.PluginAdminEnabled, - "pluginAdminExternalManageEnabled": hs.Cfg.PluginAdminEnabled && hs.Cfg.PluginAdminExternalManageEnabled, - "pluginCatalogHiddenPlugins": hs.Cfg.PluginCatalogHiddenPlugins, - "expressionsEnabled": hs.Cfg.ExpressionsEnabled, - "awsAllowedAuthProviders": hs.Cfg.AWSAllowedAuthProviders, - "awsAssumeRoleEnabled": hs.Cfg.AWSAssumeRoleEnabled, - "supportBundlesEnabled": isSupportBundlesEnabled(hs), - "azure": map[string]interface{}{ - "cloud": hs.Cfg.Azure.Cloud, - "managedIdentityEnabled": hs.Cfg.Azure.ManagedIdentityEnabled, + + FeatureToggles: hs.Features.GetEnabled(c.Req.Context()), + RendererAvailable: hs.RenderService.IsAvailable(c.Req.Context()), + RendererVersion: hs.RenderService.Version(), + SecretsManagerPluginEnabled: secretsManagerPluginEnabled, + Http2Enabled: hs.Cfg.Protocol == setting.HTTP2Scheme, + Sentry: hs.Cfg.Sentry, + GrafanaJavascriptAgent: hs.Cfg.GrafanaJavascriptAgent, + PluginCatalogURL: hs.Cfg.PluginCatalogURL, + PluginAdminEnabled: hs.Cfg.PluginAdminEnabled, + PluginAdminExternalManageEnabled: hs.Cfg.PluginAdminEnabled && hs.Cfg.PluginAdminExternalManageEnabled, + PluginCatalogHiddenPlugins: hs.Cfg.PluginCatalogHiddenPlugins, + ExpressionsEnabled: hs.Cfg.ExpressionsEnabled, + AwsAllowedAuthProviders: hs.Cfg.AWSAllowedAuthProviders, + AwsAssumeRoleEnabled: hs.Cfg.AWSAssumeRoleEnabled, + SupportBundlesEnabled: isSupportBundlesEnabled(hs), + + Azure: dtos.FrontendSettingsAzureDTO{ + Cloud: hs.Cfg.Azure.Cloud, + ManagedIdentityEnabled: hs.Cfg.Azure.ManagedIdentityEnabled, }, - "caching": map[string]bool{ - "enabled": hs.Cfg.SectionWithEnvOverrides("caching").Key("enabled").MustBool(true), + + Caching: dtos.FrontendSettingsCachingDTO{ + Enabled: hs.Cfg.SectionWithEnvOverrides("caching").Key("enabled").MustBool(true), }, - "recordedQueries": map[string]bool{ - "enabled": hs.Cfg.SectionWithEnvOverrides("recorded_queries").Key("enabled").MustBool(true), + RecordedQueries: dtos.FrontendSettingsRecordedQueriesDTO{ + Enabled: hs.Cfg.SectionWithEnvOverrides("recorded_queries").Key("enabled").MustBool(true), }, - "reporting": map[string]bool{ - "enabled": hs.Cfg.SectionWithEnvOverrides("reporting").Key("enabled").MustBool(true), + Reporting: dtos.FrontendSettingsReportingDTO{ + Enabled: hs.Cfg.SectionWithEnvOverrides("reporting").Key("enabled").MustBool(true), }, - "unifiedAlertingEnabled": hs.Cfg.UnifiedAlerting.Enabled, - "unifiedAlerting": map[string]interface{}{ - "minInterval": hs.Cfg.UnifiedAlerting.MinInterval.String(), + + UnifiedAlerting: dtos.FrontendSettingsUnifiedAlertingDTO{ + MinInterval: hs.Cfg.UnifiedAlerting.MinInterval.String(), }, - "oauth": hs.getEnabledOAuthProviders(), - "samlEnabled": hs.samlEnabled(), - "samlName": hs.samlName(), - "tokenExpirationDayLimit": hs.Cfg.SATokenExpirationDayLimit, - "snapshotEnabled": hs.Cfg.SnapshotEnabled, + + Oauth: hs.getEnabledOAuthProviders(), + SamlEnabled: hs.samlEnabled(), + SamlName: hs.samlName(), + TokenExpirationDayLimit: hs.Cfg.SATokenExpirationDayLimit, + + SnapshotEnabled: hs.Cfg.SnapshotEnabled, + } + + if hs.Cfg.UnifiedAlerting.Enabled != nil { + frontendSettings.UnifiedAlertingEnabled = *hs.Cfg.UnifiedAlerting.Enabled + } + + if setting.AlertingEnabled != nil { + frontendSettings.AlertingEnabled = *setting.AlertingEnabled } if hs.pluginsCDNService != nil && hs.pluginsCDNService.IsEnabled() { @@ -219,21 +236,22 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *contextmodel.ReqContext) (map[st if err != nil { return nil, fmt.Errorf("plugins cdn base url: %w", err) } - jsonObj["pluginsCDNBaseURL"] = cdnBaseURL + frontendSettings.PluginsCDNBaseURL = cdnBaseURL } if hs.ThumbService != nil { - jsonObj["dashboardPreviews"] = hs.ThumbService.GetDashboardPreviewsSetupSettings(c) + frontendSettings.DashboardPreviews = hs.ThumbService.GetDashboardPreviewsSetupSettings(c) } if hs.Cfg.GeomapDefaultBaseLayerConfig != nil { - jsonObj["geomapDefaultBaseLayerConfig"] = hs.Cfg.GeomapDefaultBaseLayerConfig - } - if !hs.Cfg.GeomapEnableCustomBaseLayers { - jsonObj["geomapDisableCustomBaseLayer"] = true + frontendSettings.GeomapDefaultBaseLayerConfig = &hs.Cfg.GeomapDefaultBaseLayerConfig } - return jsonObj, nil + if !hs.Cfg.GeomapEnableCustomBaseLayers { + frontendSettings.GeomapDisableCustomBaseLayer = true + } + + return frontendSettings, nil } func isSupportBundlesEnabled(hs *HTTPServer) bool { diff --git a/pkg/api/index.go b/pkg/api/index.go index 2aee274b792..7395929194e 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -35,12 +35,12 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV hasAccess := ac.HasAccess(hs.AccessControl, c) hasEditPerm := hasAccess(hs.editorInAnyFolder, ac.EvalAny(ac.EvalPermission(dashboards.ActionDashboardsCreate), ac.EvalPermission(dashboards.ActionFoldersCreate))) - settings, err := hs.getFrontendSettingsMap(c) + settings, err := hs.getFrontendSettings(c) if err != nil { return nil, err } - settings["dateFormats"] = hs.Cfg.DateFormats + settings.IsPublicDashboardView = c.IsPublicDashboardView prefsQuery := pref.GetPreferenceWithDefaultsQuery{UserID: c.UserID, OrgID: c.OrgID, Teams: c.Teams} prefs, err := hs.preferenceService.GetWithDefaults(c.Req.Context(), &prefsQuery) @@ -70,7 +70,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV if c.IsRenderCall && !hs.Cfg.ServeFromSubPath { appURL = fmt.Sprintf("%s://localhost:%s", hs.Cfg.Protocol, hs.Cfg.HTTPPort) appSubURL = "" - settings["appSubUrl"] = "" + settings.AppSubUrl = "" } navTree, err := hs.navTreeService.GetNavTree(c, hasEditPerm, prefs) @@ -78,10 +78,6 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV return nil, err } - if c.IsPublicDashboardView { - settings["isPublicDashboardView"] = true - } - weekStart := "" if prefs.WeekStart != nil { weekStart = *prefs.WeekStart diff --git a/pkg/api/login.go b/pkg/api/login.go index 8d041941ff4..d4cf320bc16 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -100,7 +100,7 @@ func (hs *HTTPServer) LoginView(c *contextmodel.ReqContext) { // and the view should return immediately before attempting // to login again via OAuth and enter to a redirect loop cookies.DeleteCookie(c.Resp, loginErrorCookieName, hs.CookieOptionsFromCfg) - viewData.Settings["loginError"] = loginError + viewData.Settings.LoginError = loginError c.HTML(http.StatusOK, getViewIndex(), viewData) return } diff --git a/pkg/api/login_test.go b/pkg/api/login_test.go index 402d9d77b7e..95c1b8df329 100644 --- a/pkg/api/login_test.go +++ b/pkg/api/login_test.go @@ -44,7 +44,7 @@ func fakeSetIndexViewData(t *testing.T) { setIndexViewData = func(*HTTPServer, *contextmodel.ReqContext) (*dtos.IndexViewData, error) { data := &dtos.IndexViewData{ User: &dtos.CurrentUser{}, - Settings: map[string]interface{}{}, + Settings: &dtos.FrontendSettingsDTO{}, NavTree: &navtree.NavTreeRoot{}, } return data, nil diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index ac6d530749d..eaabf0085d1 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -167,7 +167,7 @@ func TestMiddlewareContext(t *testing.T) { t.Log("Handler called") data := &dtos.IndexViewData{ User: &dtos.CurrentUser{}, - Settings: map[string]interface{}{}, + Settings: &dtos.FrontendSettingsDTO{}, NavTree: &navtree.NavTreeRoot{}, } t.Log("Calling HTML", "data", data) diff --git a/pkg/services/thumbs/dummy.go b/pkg/services/thumbs/dummy.go index 9f77fe37891..6afeea7e869 100644 --- a/pkg/services/thumbs/dummy.go +++ b/pkg/services/thumbs/dummy.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" ) @@ -31,9 +32,9 @@ func (ds *dummyService) Enabled() bool { return false } -func (ds *dummyService) GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dashboardPreviewsSetupConfig { - return dashboardPreviewsSetupConfig{ - SystemRequirements: dashboardPreviewsSystemRequirements{ +func (ds *dummyService) GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dtos.DashboardPreviewsSetupConfig { + return dtos.DashboardPreviewsSetupConfig{ + SystemRequirements: dtos.DashboardPreviewsSystemRequirements{ Met: false, RequiredImageRendererPluginVersion: "", }, diff --git a/pkg/services/thumbs/models.go b/pkg/services/thumbs/models.go index dfc6fc7d887..ed40b90f33a 100644 --- a/pkg/services/thumbs/models.go +++ b/pkg/services/thumbs/models.go @@ -55,16 +55,6 @@ type crawlStatus struct { Last time.Time `json:"last,omitempty"` } -type dashboardPreviewsSystemRequirements struct { - Met bool `json:"met"` - RequiredImageRendererPluginVersion string `json:"requiredImageRendererPluginVersion"` -} - -type dashboardPreviewsSetupConfig struct { - SystemRequirements dashboardPreviewsSystemRequirements `json:"systemRequirements"` - ThumbnailsExist bool `json:"thumbnailsExist"` -} - type ThumbnailKind string type ThumbnailState string diff --git a/pkg/services/thumbs/service.go b/pkg/services/thumbs/service.go index f34eb8af566..aa15717b207 100644 --- a/pkg/services/thumbs/service.go +++ b/pkg/services/thumbs/service.go @@ -10,6 +10,7 @@ import ( "github.com/segmentio/encoding/json" + "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" @@ -34,7 +35,7 @@ type Service interface { Run(ctx context.Context) error Enabled() bool GetImage(c *contextmodel.ReqContext) - GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dashboardPreviewsSetupConfig + GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dtos.DashboardPreviewsSetupConfig // from dashboard page SetImage(c *contextmodel.ReqContext) // form post @@ -319,44 +320,44 @@ func (hs *thumbService) hasAccessToPreview(c *contextmodel.ReqContext, res *Dash return true } -func (hs *thumbService) GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dashboardPreviewsSetupConfig { +func (hs *thumbService) GetDashboardPreviewsSetupSettings(c *contextmodel.ReqContext) dtos.DashboardPreviewsSetupConfig { return hs.getDashboardPreviewsSetupSettings(c.Req.Context()) } -func (hs *thumbService) getDashboardPreviewsSetupSettings(ctx context.Context) dashboardPreviewsSetupConfig { +func (hs *thumbService) getDashboardPreviewsSetupSettings(ctx context.Context) dtos.DashboardPreviewsSetupConfig { systemRequirements := hs.getSystemRequirements(ctx) thumbnailsExist, err := hs.thumbnailRepo.doThumbnailsExist(ctx) if err != nil { - return dashboardPreviewsSetupConfig{ + return dtos.DashboardPreviewsSetupConfig{ SystemRequirements: systemRequirements, ThumbnailsExist: false, } } - return dashboardPreviewsSetupConfig{ + return dtos.DashboardPreviewsSetupConfig{ SystemRequirements: systemRequirements, ThumbnailsExist: thumbnailsExist, } } -func (hs *thumbService) getSystemRequirements(ctx context.Context) dashboardPreviewsSystemRequirements { +func (hs *thumbService) getSystemRequirements(ctx context.Context) dtos.DashboardPreviewsSystemRequirements { res, err := hs.renderingService.HasCapability(ctx, rendering.ScalingDownImages) if err != nil { hs.log.Error("Error when verifying dashboard previews system requirements thumbnail", "err", err.Error()) - return dashboardPreviewsSystemRequirements{ + return dtos.DashboardPreviewsSystemRequirements{ Met: false, } } if !res.IsSupported { - return dashboardPreviewsSystemRequirements{ + return dtos.DashboardPreviewsSystemRequirements{ Met: false, RequiredImageRendererPluginVersion: res.SemverConstraint, } } - return dashboardPreviewsSystemRequirements{ + return dtos.DashboardPreviewsSystemRequirements{ Met: true, } }