Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: get site from go:embed #1710

Merged
merged 1 commit into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
env:
GO111MODULE: "on"
run: |
mkdir -p client/webserver/site/dist
touch client/webserver/site/dist/placeholder
./run_tests.sh
build-js:
name: Build JS
Expand Down
8 changes: 7 additions & 1 deletion client/cmd/dexc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ type Config struct {
NoWeb bool `long:"noweb" description:"disable the web server."`
Testnet bool `long:"testnet" description:"use testnet"`
Simnet bool `long:"simnet" description:"use simnet"`
ReloadHTML bool `long:"reload-html" description:"Reload the webserver's page template with every request. For development purposes."`
ReloadHTML bool `long:"reload-html" description:"(DEPRECATED) Reload the webserver's page template from disk with every request. Prevents use of any embedded UI files. For development purposes. This is deprecated. Use --no-embed-site instead."`
NoEmbedSite bool `long:"no-embed-site" description:"Use on-disk UI files instead of embedded resources. This also reloads the html template with every request. For development purposes."`
DebugLevel string `long:"log" description:"Logging level {trace, debug, info, warn, error, critical}"`
LocalLogs bool `long:"loglocal" description:"Use local time zone time stamps in log entries."`
CPUProfile string `long:"cpuprofile" description:"File for CPU profiling."`
Expand Down Expand Up @@ -215,5 +216,10 @@ func configure() (*Config, error) {
cfg.DBPath = defaultDBPath
}

if cfg.ReloadHTML {
fmt.Println("The --reload-html switch is deprecated. Use --no-embed-site instead, which has the same reloading effect.")
cfg.NoEmbedSite = cfg.ReloadHTML
}

return cfg, nil
}
2 changes: 1 addition & 1 deletion client/cmd/dexc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func mainCore() error {
Addr: cfg.WebAddr,
CustomSiteDir: cfg.SiteDir,
Logger: logMaker.Logger("WEB"),
ReloadHTML: cfg.ReloadHTML,
NoEmbed: cfg.NoEmbedSite,
HttpProf: cfg.HTTPProfile,
Language: cfg.Language,
})
Expand Down
5 changes: 3 additions & 2 deletions client/cmd/dexc/sample-dexc.conf
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@
; Default is false.
; noweb=true

; Reload the webserver's page template with every request. For development
; Do not use the embedded webserver site resources, instead reading them from
; disk. Reload the webserver's page template with every request. For development
; purposes.
; Default is false.
; reload-html=true
; no-embed-site=true

; ------------------------------------------------------------------------------
; Debug settings
Expand Down
10 changes: 5 additions & 5 deletions client/webserver/live_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1817,11 +1817,11 @@ func TestServer(t *testing.T) {
}

s, err := New(&Config{
Core: tCore,
Addr: "[::1]:54321",
Logger: logger,
ReloadHTML: true,
HttpProf: true,
Core: tCore,
Addr: "[::1]:54321",
Logger: logger,
NoEmbed: true, // use files on disk, and reload on each page load
HttpProf: true,
})
if err != nil {
t.Fatalf("error creating server: %v", err)
Expand Down
115 changes: 55 additions & 60 deletions client/webserver/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,35 @@ type templates struct {
templates map[string]pageTemplate
folder string
locale map[string]string
exec func(string, interface{}) (string, error)
embedded bool // used in exec and addTemplate
addErr error
}

// newTemplates constructs a new templates.
func newTemplates(folder, lang string, reload bool) *templates {
func newTemplates(folder, lang string, embedded bool) *templates {
t := &templates{
templates: make(map[string]pageTemplate),
folder: folder,
embedded: embedded,
}
t.exec = t.execTemplateToString
if reload {
var found bool
// Make sure we have the expected directory structure.
if !folderExists(t.srcDir()) {
t.addErr = fmt.Errorf("reload-html set but source directory not found at %s", t.srcDir())
} else if t.locale, found = locales.Locales[lang]; !found {
t.addErr = fmt.Errorf("no translation dictionary found for lang %q", lang)
}
t.exec = t.execWithReload
// t.locale is only used in retranslate, which currently only applies when
// not using the embedded site resources, but check the lang anyway. TODO:
// We should aim to embed only the untranslated template, and translate
// those in-memory on template creation.
var found bool
if t.locale, found = locales.Locales[lang]; !found {
t.addErr = fmt.Errorf("no translation dictionary found for lang %q", lang)
return t
}
if embedded {
return t
}

// Make sure we have the expected directory structure.
if !folderExists(t.srcDir()) {
t.addErr = fmt.Errorf("no-embed-site set but source directory not found at %s", t.srcDir())
}

return t
}

Expand All @@ -56,20 +64,20 @@ func (t *templates) filepath(name string) string {
}

// srcDir is the expected directory of the translation source templates. Only
// used in development when the --reload-html flag is used.
// used in development when the --no-embed-site flag is used.
// <root>/localized_html/[lang] -> <root>/html
func (t *templates) srcDir() string {
return filepath.Join(filepath.Dir(filepath.Dir(t.folder)), "html")
}

// srcPath is the path translation source. Only used in
// development when the --reload-html flag is used.
// development when the --no-embed-site flag is used.
func (t *templates) srcPath(name string) string {
return filepath.Join(t.srcDir(), name+".tmpl")
}

// retranslate rebuilds the locallized html template. Only used in development
// when the --reload-html flag is used.
// retranslate rebuilds the localized html template. Only used in development
// when the --no-embed-site flag is used.
func (t *templates) retranslate(name string, preloads ...string) error {
for _, iName := range append(preloads, name) {
srcPath := t.srcPath(iName)
Expand Down Expand Up @@ -101,7 +109,8 @@ func (t *templates) retranslate(name string, preloads ...string) error {
return nil
}

// addTemplate adds a new template. The template is specified with a name, which
// addTemplate adds a new template. It can be embed from the binary or not, to
// help with development. The template is specified with a name, which
// must also be the base name of a file in the templates folder. Any preloads
// must also be base names of files in the template folder, which will be loaded
// in order before the name template is processed. addTemplate returns itself
Expand All @@ -115,14 +124,21 @@ func (t *templates) addTemplate(name string, preloads ...string) *templates {
files = append(files, t.filepath(preloads[i]))
}
files = append(files, t.filepath(name))
temp, err := template.New(name).Funcs(templateFuncs).ParseFiles(files...)

tmpl := template.New(name).Funcs(templateFuncs)
var err error
if t.embedded {
tmpl, err = tmpl.ParseFS(siteRes, files...)
} else {
tmpl, err = tmpl.ParseFiles(files...)
}
if err != nil {
t.addErr = fmt.Errorf("error adding template %s: %w", name, err)
return t
}
t.templates[name] = pageTemplate{
preloads: preloads,
template: temp,
template: tmpl,
}
return t
}
Expand All @@ -135,56 +151,35 @@ func (t *templates) buildErr() error {
return err
}

// reloadTemplates relaods all templates. Use this during front-end development
// when editing templates.
func (t *templates) reloadTemplates() error {
var errorStrings []string
for name, tmpl := range t.templates {
t.addTemplate(name, tmpl.preloads...)
if t.buildErr() != nil {
log.Errorf(t.buildErr().Error())
}
}
if errorStrings == nil {
return nil
}
return fmt.Errorf(strings.Join(errorStrings, " | "))
}

// execTemplateToString executes the specified input template using the
// supplied data, and writes the result into a string. If the template fails to
// execute or isn't found, a non-nil error will be returned. Check it before
// writing to the client, otherwise you might as well execute directly into
// your response writer instead of the internal buffer of this function.
// exec executes the specified input template using the supplied data, and
// writes the result into a string. If the template fails to execute or isn't
// found, a non-nil error will be returned. Check it before writing to the
// client, otherwise you might as well execute directly into your response
// writer instead of the internal buffer of this function.
//
// The template will be reloaded if using on-disk (not embedded) templates.
//
// DRAFT NOTE: Might consider writing directly to the the buffer here. Could
// still set the error code appropriately.
func (t *templates) execTemplateToString(name string, data interface{}) (string, error) {
temp, ok := t.templates[name]
if !ok {
return "", fmt.Errorf("Template %s not known", name)
}
var page strings.Builder
err := temp.template.ExecuteTemplate(&page, name, data)
return page.String(), err
}

// execWithReload is the same as execTemplateToString, but will reload the
// template first.
func (t *templates) execWithReload(name string, data interface{}) (string, error) {
func (t *templates) exec(name string, data interface{}) (string, error) {
tmpl, found := t.templates[name]
if !found {
return "", fmt.Errorf("template %s not found", name)
return "", fmt.Errorf("template %q not found", name)
}

err := t.retranslate(name, tmpl.preloads...)
if err != nil {
return "", err
if !t.embedded {
err := t.retranslate(name, tmpl.preloads...)
if err != nil {
return "", err
}

t.addTemplate(name, tmpl.preloads...)
log.Debugf("reloaded HTML template %q", name)
}

t.addTemplate(name, tmpl.preloads...)
log.Debugf("reloaded HTML template %q", name)
return t.execTemplateToString(name, data)
var page strings.Builder
err := tmpl.template.ExecuteTemplate(&page, name, data)
return page.String(), err
}

// templateFuncs are able to be called during template execution.
Expand Down
Loading