Skip to content

Commit

Permalink
client: get site from go:embed
Browse files Browse the repository at this point in the history
Co-authored-by: Jonathan Chappelow <chappjc@gmail.com>
  • Loading branch information
vctt94 and chappjc committed Aug 16, 2022
1 parent 0f69ee6 commit 2ab762e
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 185 deletions.
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

0 comments on commit 2ab762e

Please sign in to comment.