mirror of https://github.com/grafana/grafana
FEMT: Add feature toggle and expose the service in regular grafana (#104428)
parent
29b3738bc8
commit
7b492d7e16
|
@ -0,0 +1,108 @@ |
||||
package frontend |
||||
|
||||
import ( |
||||
"context" |
||||
"embed" |
||||
"errors" |
||||
"fmt" |
||||
"html/template" |
||||
"net/http" |
||||
"syscall" |
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging" |
||||
"github.com/grafana/grafana/pkg/api/dtos" |
||||
"github.com/grafana/grafana/pkg/api/webassets" |
||||
"github.com/grafana/grafana/pkg/middleware" |
||||
"github.com/grafana/grafana/pkg/services/licensing" |
||||
"github.com/grafana/grafana/pkg/setting" |
||||
) |
||||
|
||||
type IndexProvider struct { |
||||
log logging.Logger |
||||
index *template.Template |
||||
data IndexViewData |
||||
} |
||||
|
||||
type IndexViewData struct { |
||||
CSPContent string |
||||
CSPEnabled bool |
||||
IsDevelopmentEnv bool |
||||
|
||||
AppSubUrl string |
||||
BuildVersion string |
||||
BuildCommit string |
||||
AppTitle string |
||||
|
||||
Assets *dtos.EntryPointAssets // Includes CDN info
|
||||
|
||||
// Nonce is a cryptographic identifier for use with Content Security Policy.
|
||||
Nonce string |
||||
} |
||||
|
||||
// Templates setup.
|
||||
var ( |
||||
//go:embed *.html
|
||||
templatesFS embed.FS |
||||
|
||||
// templates
|
||||
htmlTemplates = template.Must(template.New("html").Delims("[[", "]]").ParseFS(templatesFS, `*.html`)) |
||||
) |
||||
|
||||
func NewIndexProvider(cfg *setting.Cfg, license licensing.Licensing) (*IndexProvider, error) { |
||||
assets, err := webassets.GetWebAssets(context.Background(), cfg, license) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
t := htmlTemplates.Lookup("index.html") |
||||
if t == nil { |
||||
return nil, fmt.Errorf("missing index template") |
||||
} |
||||
|
||||
return &IndexProvider{ |
||||
log: logging.DefaultLogger.With("logger", "index-provider"), |
||||
index: t, |
||||
data: IndexViewData{ |
||||
AppTitle: "Grafana", |
||||
AppSubUrl: cfg.AppSubURL, // Based on the request?
|
||||
BuildVersion: cfg.BuildVersion, |
||||
BuildCommit: cfg.BuildCommit, |
||||
Assets: assets, |
||||
|
||||
CSPEnabled: cfg.CSPEnabled, |
||||
CSPContent: cfg.CSPTemplate, |
||||
|
||||
IsDevelopmentEnv: cfg.Env == setting.Dev, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
func (p *IndexProvider) HandleRequest(writer http.ResponseWriter, request *http.Request) { |
||||
if request.Method != "GET" { |
||||
writer.WriteHeader(http.StatusMethodNotAllowed) |
||||
return |
||||
} |
||||
|
||||
nonce, err := middleware.GenerateNonce() |
||||
if err != nil { |
||||
p.log.Error("error creating nonce", "err", err) |
||||
writer.WriteHeader(500) |
||||
return |
||||
} |
||||
|
||||
// TODO -- restructure so the static stuff is under one variable and the rest is dynamic
|
||||
data := p.data // copy everything
|
||||
data.Nonce = nonce |
||||
|
||||
if data.CSPEnabled { |
||||
data.CSPContent = middleware.ReplacePolicyVariables(p.data.CSPContent, p.data.AppSubUrl, data.Nonce) |
||||
} |
||||
|
||||
writer.Header().Set("Content-Type", "text/html; charset=UTF-8") |
||||
writer.WriteHeader(200) |
||||
if err := p.index.Execute(writer, &data); err != nil { |
||||
if errors.Is(err, syscall.EPIPE) { // Client has stopped listening.
|
||||
return |
||||
} |
||||
panic(fmt.Sprintf("Error rendering index\n %s", err.Error())) |
||||
} |
||||
} |
@ -0,0 +1,33 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
[[ if and .CSPEnabled .IsDevelopmentEnv ]] |
||||
<!-- Cypress overwrites CSP headers in HTTP requests, so this is required for e2e tests--> |
||||
<meta http-equiv="Content-Security-Policy" content="[[.CSPContent]]"/> |
||||
[[ end ]] |
||||
<meta charset="utf-8" /> |
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> |
||||
<meta name="viewport" content="width=device-width" /> |
||||
<meta name="theme-color" content="#000" /> |
||||
|
||||
<title>[[.AppTitle]]</title> |
||||
|
||||
<base href="[[.AppSubUrl]]/" /> |
||||
|
||||
<link rel="mask-icon" href="[[.Assets.ContentDeliveryURL]]public/img/grafana_mask_icon.svg" color="#F05A28" /> |
||||
|
||||
[[range $asset := .Assets.CSSFiles]] |
||||
<link rel="stylesheet" href="[[$asset.FilePath]]" /> |
||||
[[end]] |
||||
|
||||
<script nonce="[[.Nonce]]"> |
||||
performance.mark('frontend_boot_css_time_seconds'); |
||||
</script> |
||||
|
||||
</head> |
||||
|
||||
<body> |
||||
<h1>Grafana Frontend Server ([[.BuildVersion]])</h1> |
||||
<p>This is a simple static HTML page served by the Grafana frontend server module.</p> |
||||
</body> |
||||
</html> |
Loading…
Reference in new issue