satellite/console/consoleweb: remove templating for index.html

Previously, we evaluated index.html as a template in order to insert
frontend config values into meta tags. Now that the frontend fetches
its config through the satellite API, this is no longer necessary.

Resolves #5494

Change-Id: Ic98507c5e16cd80317bd9c31d4b55abda0dd7e34
This commit is contained in:
Jeremy Wharton 2023-04-05 17:35:01 -05:00
parent 6e866856c4
commit 45d5a93085
2 changed files with 37 additions and 172 deletions

View File

@ -10,6 +10,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"io/fs"
"mime" "mime"
"net" "net"
"net/http" "net/http"
@ -30,7 +31,6 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"storj.io/common/errs2" "storj.io/common/errs2"
"storj.io/common/memory"
"storj.io/common/storj" "storj.io/common/storj"
"storj.io/storj/private/web" "storj.io/storj/private/web"
"storj.io/storj/satellite/abtesting" "storj.io/storj/satellite/abtesting"
@ -134,12 +134,7 @@ type Server struct {
schema graphql.Schema schema graphql.Schema
templatesCache *templates errorTemplate *template.Template
}
type templates struct {
index *template.Template
error *template.Template
} }
// apiAuth exposes methods to control authentication process for each generated API endpoint. // apiAuth exposes methods to control authentication process for each generated API endpoint.
@ -382,9 +377,9 @@ func (server *Server) Run(ctx context.Context) (err error) {
return Error.Wrap(err) return Error.Wrap(err)
} }
_, err = server.loadTemplates() _, err = server.loadErrorTemplate()
if err != nil { if err != nil {
// TODO: should it return error if some template can not be initialized or just log about it? // TODO: should it return error if the template cannot be initialized or just log about it?
return Error.Wrap(err) return Error.Wrap(err)
} }
@ -439,99 +434,32 @@ func (server *Server) appHandler(w http.ResponseWriter, r *http.Request) {
header.Set("X-Content-Type-Options", "nosniff") header.Set("X-Content-Type-Options", "nosniff")
header.Set("Referrer-Policy", "same-origin") // Only expose the referring url when navigating around the satellite itself. header.Set("Referrer-Policy", "same-origin") // Only expose the referring url when navigating around the satellite itself.
var data struct { path := filepath.Join(server.config.StaticDir, "dist", "index.html")
ExternalAddress string file, err := os.Open(path)
SatelliteName string if err != nil {
SatelliteNodeURL string if errors.Is(err, fs.ErrNotExist) {
StripePublicKey string server.log.Error("index.html was not generated. run 'npm run build' in the "+server.config.StaticDir+" directory", zap.Error(err))
PartneredSatellites string } else {
DefaultProjectLimit int server.log.Error("error loading index.html", zap.String("path", path), zap.Error(err))
GeneralRequestURL string }
ProjectLimitsIncreaseRequestURL string server.serveError(w, http.StatusInternalServerError)
GatewayCredentialsRequestURL string
IsBetaSatellite bool
BetaSatelliteFeedbackURL string
BetaSatelliteSupportURL string
DocumentationURL string
CouponCodeBillingUIEnabled bool
CouponCodeSignupUIEnabled bool
FileBrowserFlowDisabled bool
LinksharingURL string
PathwayOverviewEnabled bool
RegistrationRecaptchaEnabled bool
RegistrationRecaptchaSiteKey string
RegistrationHcaptchaEnabled bool
RegistrationHcaptchaSiteKey string
LoginRecaptchaEnabled bool
LoginRecaptchaSiteKey string
LoginHcaptchaEnabled bool
LoginHcaptchaSiteKey string
AllProjectsDashboard bool
DefaultPaidStorageLimit memory.Size
DefaultPaidBandwidthLimit memory.Size
InactivityTimerEnabled bool
InactivityTimerDuration int
InactivityTimerViewerEnabled bool
OptionalSignupSuccessURL string
HomepageURL string
NativeTokenPaymentsEnabled bool
PasswordMinimumLength int
PasswordMaximumLength int
ABTestingEnabled bool
PricingPackagesEnabled bool
}
data.ExternalAddress = server.config.ExternalAddress
data.SatelliteName = server.config.SatelliteName
data.SatelliteNodeURL = server.nodeURL.String()
data.StripePublicKey = server.stripePublicKey
data.PartneredSatellites = server.config.PartneredSatellites.String()
data.DefaultProjectLimit = server.config.DefaultProjectLimit
data.GeneralRequestURL = server.config.GeneralRequestURL
data.ProjectLimitsIncreaseRequestURL = server.config.ProjectLimitsIncreaseRequestURL
data.GatewayCredentialsRequestURL = server.config.GatewayCredentialsRequestURL
data.IsBetaSatellite = server.config.IsBetaSatellite
data.BetaSatelliteFeedbackURL = server.config.BetaSatelliteFeedbackURL
data.BetaSatelliteSupportURL = server.config.BetaSatelliteSupportURL
data.DocumentationURL = server.config.DocumentationURL
data.CouponCodeBillingUIEnabled = server.config.CouponCodeBillingUIEnabled
data.CouponCodeSignupUIEnabled = server.config.CouponCodeSignupUIEnabled
data.FileBrowserFlowDisabled = server.config.FileBrowserFlowDisabled
data.LinksharingURL = server.config.LinksharingURL
data.PathwayOverviewEnabled = server.config.PathwayOverviewEnabled
data.DefaultPaidStorageLimit = server.config.UsageLimits.Storage.Paid
data.DefaultPaidBandwidthLimit = server.config.UsageLimits.Bandwidth.Paid
data.RegistrationRecaptchaEnabled = server.config.Captcha.Registration.Recaptcha.Enabled
data.RegistrationRecaptchaSiteKey = server.config.Captcha.Registration.Recaptcha.SiteKey
data.RegistrationHcaptchaEnabled = server.config.Captcha.Registration.Hcaptcha.Enabled
data.RegistrationHcaptchaSiteKey = server.config.Captcha.Registration.Hcaptcha.SiteKey
data.LoginRecaptchaEnabled = server.config.Captcha.Login.Recaptcha.Enabled
data.LoginRecaptchaSiteKey = server.config.Captcha.Login.Recaptcha.SiteKey
data.LoginHcaptchaEnabled = server.config.Captcha.Login.Hcaptcha.Enabled
data.LoginHcaptchaSiteKey = server.config.Captcha.Login.Hcaptcha.SiteKey
data.AllProjectsDashboard = server.config.AllProjectsDashboard
data.InactivityTimerEnabled = server.config.Session.InactivityTimerEnabled
data.InactivityTimerDuration = server.config.Session.InactivityTimerDuration
data.InactivityTimerViewerEnabled = server.config.Session.InactivityTimerViewerEnabled
data.OptionalSignupSuccessURL = server.config.OptionalSignupSuccessURL
data.HomepageURL = server.config.HomepageURL
data.NativeTokenPaymentsEnabled = server.config.NativeTokenPaymentsEnabled
data.PasswordMinimumLength = console.PasswordMinimumLength
data.PasswordMaximumLength = console.PasswordMaximumLength
data.ABTestingEnabled = server.config.ABTesting.Enabled
data.PricingPackagesEnabled = server.config.PricingPackagesEnabled
templates, err := server.loadTemplates()
if err != nil || templates.index == nil {
server.log.Error("unable to load templates", zap.Error(err))
fmt.Fprintf(w, "Unable to load templates. See whether satellite UI has been built.")
return return
} }
if err := templates.index.Execute(w, data); err != nil { defer func() {
server.log.Error("index template could not be executed", zap.Error(err)) if err := file.Close(); err != nil {
server.log.Error("error closing index.html", zap.String("path", path), zap.Error(err))
}
}()
info, err := file.Stat()
if err != nil {
server.log.Error("failed to retrieve file info", zap.Error(err))
server.serveError(w, http.StatusInternalServerError)
return return
} }
http.ServeContent(w, r, path, info.ModTime(), file)
} }
// withAuth performs initial authorization before every request. // withAuth performs initial authorization before every request.
@ -854,18 +782,18 @@ func (server *Server) graphqlHandler(w http.ResponseWriter, r *http.Request) {
server.log.Debug(fmt.Sprintf("%s", result)) server.log.Debug(fmt.Sprintf("%s", result))
} }
// serveError serves error static pages. // serveError serves a static error page.
func (server *Server) serveError(w http.ResponseWriter, status int) { func (server *Server) serveError(w http.ResponseWriter, status int) {
w.WriteHeader(status) w.WriteHeader(status)
templates, err := server.loadTemplates() template, err := server.loadErrorTemplate()
if err != nil { if err != nil {
server.log.Error("unable to load templates", zap.Error(err)) server.log.Error("unable to load error template", zap.Error(err))
return return
} }
data := struct{ StatusCode int }{StatusCode: status} data := struct{ StatusCode int }{StatusCode: status}
err = templates.error.Execute(w, data) err = template.Execute(w, data)
if err != nil { if err != nil {
server.log.Error("cannot parse error template", zap.Error(err)) server.log.Error("cannot parse error template", zap.Error(err))
} }
@ -916,40 +844,16 @@ func (server *Server) brotliMiddleware(fn http.Handler) http.Handler {
}) })
} }
// loadTemplates is used to initialize all templates. // loadTemplates is used to initialize the error page template.
func (server *Server) loadTemplates() (_ *templates, err error) { func (server *Server) loadErrorTemplate() (_ *template.Template, err error) {
if server.config.Watch { if server.errorTemplate == nil || server.config.Watch {
return server.parseTemplates() server.errorTemplate, err = template.ParseFiles(filepath.Join(server.config.StaticDir, "static", "errors", "error.html"))
if err != nil {
return nil, Error.Wrap(err)
}
} }
if server.templatesCache != nil { return server.errorTemplate, nil
return server.templatesCache, nil
}
templates, err := server.parseTemplates()
if err != nil {
return nil, Error.Wrap(err)
}
server.templatesCache = templates
return server.templatesCache, nil
}
func (server *Server) parseTemplates() (_ *templates, err error) {
var t templates
t.index, err = template.ParseFiles(filepath.Join(server.config.StaticDir, "dist", "index.html"))
if err != nil {
server.log.Error("dist folder is not generated. use 'npm run build' command", zap.Error(err))
// Loading index is optional.
}
t.error, err = template.ParseFiles(filepath.Join(server.config.StaticDir, "static", "errors", "error.html"))
if err != nil {
return &t, Error.Wrap(err)
}
return &t, nil
} }
// NewUserIDRateLimiter constructs a RateLimiter that limits based on user ID. // NewUserIDRateLimiter constructs a RateLimiter that limits based on user ID.

View File

@ -3,45 +3,6 @@
<head lang="en"> <head lang="en">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="external-address" content="{{ .ExternalAddress }}">
<meta name="satellite-name" content="{{ .SatelliteName }}">
<meta name="satellite-nodeurl" content="{{ .SatelliteNodeURL }}">
<meta name="stripe-public-key" content="{{ .StripePublicKey }}">
<meta name="partnered-satellites" content="{{ .PartneredSatellites }}">
<meta name="default-project-limit" content="{{ .DefaultProjectLimit }}">
<meta name="general-request-url" content="{{ .GeneralRequestURL }}">
<meta name="project-limits-increase-request-url" content="{{ .ProjectLimitsIncreaseRequestURL }}">
<meta name="gateway-credentials-request-url" content="{{ .GatewayCredentialsRequestURL }}">
<meta name="is-beta-satellite" content="{{ .IsBetaSatellite }}">
<meta name="beta-satellite-feedback-url" content="{{ .BetaSatelliteFeedbackURL }}">
<meta name="beta-satellite-support-url" content="{{ .BetaSatelliteSupportURL }}">
<meta name="documentation-url" content="{{ .DocumentationURL }}">
<meta name="coupon-code-billing-ui-enabled" content="{{ .CouponCodeBillingUIEnabled }}">
<meta name="coupon-code-signup-ui-enabled" content="{{ .CouponCodeSignupUIEnabled }}">
<meta name="file-browser-flow-disabled" content="{{ .FileBrowserFlowDisabled }}">
<meta name="linksharing-url" content="{{ .LinksharingURL }}">
<meta name="registration-recaptcha-enabled" content="{{ .RegistrationRecaptchaEnabled }}">
<meta name="registration-recaptcha-site-key" content="{{ .RegistrationRecaptchaSiteKey }}">
<meta name="registration-hcaptcha-enabled" content="{{ .RegistrationHcaptchaEnabled }}">
<meta name="registration-hcaptcha-site-key" content="{{ .RegistrationHcaptchaSiteKey }}">
<meta name="login-recaptcha-enabled" content="{{ .LoginRecaptchaEnabled }}">
<meta name="login-recaptcha-site-key" content="{{ .LoginRecaptchaSiteKey }}">
<meta name="login-hcaptcha-enabled" content="{{ .LoginHcaptchaEnabled }}">
<meta name="login-hcaptcha-site-key" content="{{ .LoginHcaptchaSiteKey }}">
<meta name="all-projects-dashboard" content="{{ .AllProjectsDashboard }}">
<meta name="default-paid-storage-limit" content="{{ .DefaultPaidStorageLimit }}">
<meta name="default-paid-bandwidth-limit" content="{{ .DefaultPaidBandwidthLimit }}">
<meta name="inactivity-timer-enabled" content="{{ .InactivityTimerEnabled }}">
<meta name="inactivity-timer-duration" content="{{ .InactivityTimerDuration }}">
<meta name="inactivity-timer-viewer-enabled" content="{{ .InactivityTimerViewerEnabled }}">
<meta name="optional-signup-success-url" content="{{ .OptionalSignupSuccessURL }}">
<meta name="homepage-url" content="{{ .HomepageURL }}">
<meta name="native-token-payments-enabled" content="{{ .NativeTokenPaymentsEnabled }}">
<meta name="password-minimum-length" content="{{ .PasswordMinimumLength }}">
<meta name="password-maximum-length" content="{{ .PasswordMaximumLength }}">
<meta name="ab-testing-enabled" content="{{ .ABTestingEnabled }}">
<meta name="pricing-packages-enabled" content="{{ .PricingPackagesEnabled }}">
<title>{{ .SatelliteName }}</title>
<link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACDVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////8nbP8obf8pbf8qbv8rb/8sb/8tcP8ucf8vcf8vcv8xc/8zdP81df81dv82dv83d/84eP85eP86ef87ev89e/8+fP8/fP9Aff9Bfv9Cfv9Df/9EgP9FgP9Ggf9Hgv9Jg/9LhP9Mhf9Nhv9Oh/9Ph/9RiP9Rif9Sif9Ui/9Vi/9WjP9Xjf9Yjf9aj/9dkf9ekf9ilP9jlf9llv9nl/9omP9rmv9sm/9tnP9vnf9wnv9yn/91of92ov93o/94o/98pv9/qP+Bqf+Cqv+Eq/+FrP+Hrf+Irv+Jr/+Kr/+Msf+Nsv+Stf+Ttf+Ttv+Utv+Vt/+WuP+XuP+Zuf+Zuv+hv/+kwf+lwv+mwv+nw/+oxP+pxP+pxf+qxf+rxv+yy/+0zP+1zf+3zv+4z/+60P+70f+90v+/0/+/1P/B1f/D1v/E1//F1//F2P/G2P/H2f/I2v/J2v/K2//L3P/P3v/Q3//R4P/S4P/V4v/V4//W4//X5P/Y5P/b5v/b5//c5//d6P/e6f/f6f/g6v/h6//j7P/k7f/l7f/m7v/q8f/r8f/u8//w9f/x9f/x9v/z9//0+P/2+f/3+f/3+v/4+v/5+//6/P/8/f/9/v/+/v////9uCbVDAAAAFXRSTlMABAU4Ozw9PpSWl5ilp6ip4+Tl/P6nIcp/AAAAAWJLR0SuuWuTpwAAAh5JREFUOMtjYGBgYOcXEl6HAYSF+FgZQICJex1OwMkEVIAi3+Xh1ozM5wKaj8xfpBwcITsbWYSNgR+JtzpJYvU6jbAVSEK8DEIITpOZqnxItISWfgVCTJAB7v4ZXpKRC9uMNCqXJci6TID7hQFMrV2zJE7abTKQFesDJGb7SYTOX7sGLAVWUKCgrGZcDeaDFaxb12alqC6XDlMwTyKnRLJ1HbKCddNEc0skJkAVdEssXatRiKqgVmLlatUqqILVpuaOEnLJy4GsIhONuHlAOldVwtJWcwnMDb2i4dPKdHVKV3uqRCdYqU9psVDOmh0vUQN35FTRhevWLU+V0FeZBdTtpSQRvgAoKtuMqmBdpKxvKYjXJ+o+cx0WBRPFO6ABHuesMheLghIdePiutc7AoqBLchZchVMSFgUr9HTS8sEgL1C0E1XBRNGUeeV6OlFONjbqSjY2Nv7mKjnzMyXqYQrW2OsYS8smLkOE5OpsFSkdQ6PlUAU9EgtXq6MFdZ3EkpVKNVAFc8TKW6QbURVMFK1slOyGuSFdUkLOoQtZwSRXaRmpKLgj1y1eMjdIImguTMHCCAnvGcuXQhIMPMl1O8hnrOy31GtfnaNi3oRIcohEu7ZY20DZK0DGTCV7NVKi5UVK40vDJVatU/dfgCTEw8AsgsSdLx+TKjUdOeMAsycnMr/BzrIcmc8ByrycuDMvByM4f7PyCmLL/gK8LEBJALYsGEdXEyupAAAAAElFTkSuQmCC" type="image/x-icon"> <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAACDVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////8nbP8obf8pbf8qbv8rb/8sb/8tcP8ucf8vcf8vcv8xc/8zdP81df81dv82dv83d/84eP85eP86ef87ev89e/8+fP8/fP9Aff9Bfv9Cfv9Df/9EgP9FgP9Ggf9Hgv9Jg/9LhP9Mhf9Nhv9Oh/9Ph/9RiP9Rif9Sif9Ui/9Vi/9WjP9Xjf9Yjf9aj/9dkf9ekf9ilP9jlf9llv9nl/9omP9rmv9sm/9tnP9vnf9wnv9yn/91of92ov93o/94o/98pv9/qP+Bqf+Cqv+Eq/+FrP+Hrf+Irv+Jr/+Kr/+Msf+Nsv+Stf+Ttf+Ttv+Utv+Vt/+WuP+XuP+Zuf+Zuv+hv/+kwf+lwv+mwv+nw/+oxP+pxP+pxf+qxf+rxv+yy/+0zP+1zf+3zv+4z/+60P+70f+90v+/0/+/1P/B1f/D1v/E1//F1//F2P/G2P/H2f/I2v/J2v/K2//L3P/P3v/Q3//R4P/S4P/V4v/V4//W4//X5P/Y5P/b5v/b5//c5//d6P/e6f/f6f/g6v/h6//j7P/k7f/l7f/m7v/q8f/r8f/u8//w9f/x9f/x9v/z9//0+P/2+f/3+f/3+v/4+v/5+//6/P/8/f/9/v/+/v////9uCbVDAAAAFXRSTlMABAU4Ozw9PpSWl5ilp6ip4+Tl/P6nIcp/AAAAAWJLR0SuuWuTpwAAAh5JREFUOMtjYGBgYOcXEl6HAYSF+FgZQICJex1OwMkEVIAi3+Xh1ozM5wKaj8xfpBwcITsbWYSNgR+JtzpJYvU6jbAVSEK8DEIITpOZqnxItISWfgVCTJAB7v4ZXpKRC9uMNCqXJci6TID7hQFMrV2zJE7abTKQFesDJGb7SYTOX7sGLAVWUKCgrGZcDeaDFaxb12alqC6XDlMwTyKnRLJ1HbKCddNEc0skJkAVdEssXatRiKqgVmLlatUqqILVpuaOEnLJy4GsIhONuHlAOldVwtJWcwnMDb2i4dPKdHVKV3uqRCdYqU9psVDOmh0vUQN35FTRhevWLU+V0FeZBdTtpSQRvgAoKtuMqmBdpKxvKYjXJ+o+cx0WBRPFO6ABHuesMheLghIdePiutc7AoqBLchZchVMSFgUr9HTS8sEgL1C0E1XBRNGUeeV6OlFONjbqSjY2Nv7mKjnzMyXqYQrW2OsYS8smLkOE5OpsFSkdQ6PlUAU9EgtXq6MFdZ3EkpVKNVAFc8TKW6QbURVMFK1slOyGuSFdUkLOoQtZwSRXaRmpKLgj1y1eMjdIImguTMHCCAnvGcuXQhIMPMl1O8hnrOy31GtfnaNi3oRIcohEu7ZY20DZK0DGTCV7NVKi5UVK40vDJVatU/dfgCTEw8AsgsSdLx+TKjUdOeMAsycnMr/BzrIcmc8ByrycuDMvByM4f7PyCmLL/gK8LEBJALYsGEdXEyupAAAAAElFTkSuQmCC" type="image/x-icon">
<link rel="dns-prefetch" href="https://js.stripe.com"> <link rel="dns-prefetch" href="https://js.stripe.com">
</head> </head>