Skip to content

Commit dddef14

Browse files
committed
refactor(tpl): adjust theme interface
- hide template and assets data from external package - rename `Theme` to `MemTheme` - `Theme` is now an interface - extract default theme
1 parent a751a4a commit dddef14

File tree

8 files changed

+133
-116
lines changed

8 files changed

+133
-116
lines changed

src/app/main.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ func NewApp(params []*param.Param) *App {
5252
errHandler := serverErrHandler.NewErrHandler(logger)
5353

5454
// theme
55-
var err error
56-
themeKey, err := filepath.Abs(p.Theme)
57-
serverErrHandler.CheckFatal(err)
58-
theme, themeExists := themes[themeKey]
59-
if !themeExists {
60-
theme, err = tpl.LoadTheme(p.Theme)
55+
var theme tpl.Theme
56+
if len(p.Theme) == 0 {
57+
theme = tpl.DefaultTheme
58+
} else {
59+
themeKey, err := filepath.Abs(p.Theme)
6160
serverErrHandler.CheckFatal(err)
62-
themes[themeKey] = theme
61+
62+
var themeExists bool
63+
theme, themeExists = themes[themeKey]
64+
if !themeExists {
65+
theme, err = tpl.LoadMemTheme(p.Theme)
66+
serverErrHandler.CheckFatal(err)
67+
themes[themeKey] = theme
68+
}
6369
}
6470

6571
// vHostMux

src/serverHandler/asset.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,10 @@ import (
88
var initTime = time.Now()
99

1010
func (h *handler) asset(w http.ResponseWriter, r *http.Request, assetPath string) {
11-
content, ok := h.theme.Assets.Get(assetPath)
12-
if !ok {
13-
return
14-
}
15-
1611
header := w.Header()
17-
header.Set("X-Content-Type-Options","nosniff")
18-
header.Set("Content-Type", content.ContentType)
12+
header.Set("X-Content-Type-Options", "nosniff")
1913
header.Set("Cache-Control", "public, max-age=3600")
2014
if needResponseBody(r.Method) {
21-
http.ServeContent(w, r, assetPath, initTime, content.ReadSeeker)
15+
h.theme.RenderAsset(w, r, assetPath)
2216
}
2317
}

src/serverHandler/page.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func (h *handler) page(w http.ResponseWriter, r *http.Request, data *responseDat
8989
w.WriteHeader(data.Status)
9090

9191
updateSubItemsHtml(data)
92-
err := h.theme.Template.Execute(bodyW, data)
92+
err := h.theme.RenderPage(bodyW, data)
9393
if err != nil {
9494
go h.errHandler.LogError(err)
9595
}

src/tpl/asset.go

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,27 @@ package tpl
33
import (
44
"../util"
55
"bytes"
6-
_ "embed"
76
"io"
87
)
98

109
type asset struct {
11-
ContentType string
12-
ReadSeeker io.ReadSeeker
10+
contentType string
11+
readSeeker io.ReadSeeker
1312
}
1413

1514
type assets map[string]asset
1615

17-
func (assets assets) Set(path string, content []byte) error {
16+
func (assets assets) set(path string, content []byte) error {
1817
rd := bytes.NewReader(content)
1918
ctype, err := util.GetContentType(path, rd)
2019
if err != nil {
2120
return err
2221
}
2322

2423
asset := asset{
25-
ContentType: ctype,
26-
ReadSeeker: rd,
24+
contentType: ctype,
25+
readSeeker: rd,
2726
}
2827
assets[path] = asset
2928
return nil
3029
}
31-
32-
func (assets assets) Get(path string) (asset, bool) {
33-
c, ok := assets[path]
34-
return c, ok
35-
}
36-
37-
//go:embed frontend/index.css
38-
var css []byte
39-
40-
//go:embed frontend/index.js
41-
var js []byte
42-
43-
var defaultAssets = map[string]asset{
44-
"index.css": {"text/css", bytes.NewReader(css)},
45-
"index.js": {"application/javascript", bytes.NewReader(js)},
46-
}

src/tpl/defaultTheme.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package tpl
2+
3+
import (
4+
"bytes"
5+
_ "embed"
6+
)
7+
8+
//go:embed frontend/index.html
9+
var defaultTplStr string
10+
11+
//go:embed frontend/index.css
12+
var defaultCss []byte
13+
14+
//go:embed frontend/index.js
15+
var defaultJs []byte
16+
17+
var DefaultTheme MemTheme
18+
19+
func init() {
20+
defaultTpl, err := ParsePageTpl(defaultTplStr)
21+
if err != nil {
22+
defaultTpl, _ = ParsePageTpl("Builtin Template Error")
23+
}
24+
DefaultTheme.template = defaultTpl
25+
26+
defaultAssets := map[string]asset{
27+
"index.css": {"text/css", bytes.NewReader(defaultCss)},
28+
"index.js": {"application/javascript", bytes.NewReader(defaultJs)},
29+
}
30+
DefaultTheme.assets = defaultAssets
31+
}

src/tpl/memTheme.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package tpl
2+
3+
import (
4+
"archive/zip"
5+
"errors"
6+
"html/template"
7+
"io"
8+
"net/http"
9+
"time"
10+
)
11+
12+
type MemTheme struct {
13+
template *template.Template
14+
assets assets
15+
}
16+
17+
var initTime = time.Now()
18+
19+
func LoadMemTheme(themePath string) (theme MemTheme, err error) {
20+
var currentTheme = MemTheme{
21+
template: nil,
22+
assets: make(assets, 2),
23+
}
24+
25+
// assume to be a zip file
26+
var zipRd *zip.ReadCloser
27+
zipRd, err = zip.OpenReader(themePath)
28+
if err != nil {
29+
return
30+
}
31+
defer zipRd.Close()
32+
33+
for _, f := range zipRd.File {
34+
var rd io.ReadCloser
35+
rd, err = f.Open()
36+
if err != nil {
37+
continue
38+
}
39+
var raw []byte
40+
raw, err = io.ReadAll(rd)
41+
rd.Close()
42+
if err != nil {
43+
return
44+
}
45+
if f.Name == "index.html" {
46+
currentTheme.template, err = ParsePageTpl(string(raw))
47+
if err != nil {
48+
return
49+
}
50+
} else {
51+
currentTheme.assets.set(f.Name, raw)
52+
}
53+
}
54+
55+
if currentTheme.template != nil {
56+
theme = currentTheme
57+
return
58+
}
59+
60+
err = errors.New("lacks of page template 'index.html' in theme")
61+
return
62+
}
63+
64+
func (theme MemTheme) RenderPage(w io.Writer, data interface{}) error {
65+
return theme.template.Execute(w, data)
66+
}
67+
68+
func (theme MemTheme) RenderAsset(w http.ResponseWriter, r *http.Request, assetPath string) {
69+
asset, ok := theme.assets[assetPath]
70+
if !ok {
71+
w.WriteHeader(http.StatusNotFound)
72+
return
73+
}
74+
75+
w.Header().Set("Content-Type", asset.contentType)
76+
http.ServeContent(w, r, assetPath, initTime, asset.readSeeker)
77+
}

src/tpl/template.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
11
package tpl
22

33
import (
4-
"../serverErrHandler"
54
"./util"
6-
_ "embed"
75
"html/template"
86
)
97

10-
//go:embed frontend/index.html
11-
var defaultTplStr string
12-
13-
var defaultTpl *template.Template
14-
15-
func init() {
16-
tpl := template.New("page")
17-
tpl = addFuncMap(tpl)
18-
19-
var err error
20-
defaultTpl, err = tpl.Parse(defaultTplStr)
21-
if serverErrHandler.CheckError(err) {
22-
defaultTpl = template.Must(tpl.Parse("Builtin Template Error"))
23-
}
24-
}
25-
268
func ParsePageTpl(tplText string) (tpl *template.Template, err error) {
279
tpl = template.New("page")
2810
tpl = addFuncMap(tpl)

src/tpl/theme.go

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,11 @@
11
package tpl
22

33
import (
4-
"archive/zip"
5-
"html/template"
64
"io"
5+
"net/http"
76
)
87

9-
type Theme struct {
10-
Template *template.Template
11-
Assets assets
12-
}
13-
14-
var DefaultTheme Theme
15-
16-
// wait for `defaultTpl` initialized
17-
func init() {
18-
DefaultTheme = Theme{defaultTpl, defaultAssets}
19-
}
20-
21-
func LoadTheme(themePath string) (theme Theme, err error) {
22-
theme = DefaultTheme
23-
24-
if len(themePath) == 0 {
25-
return
26-
}
27-
28-
var currentTheme = Theme{
29-
Template: nil,
30-
Assets: make(assets, 2),
31-
}
32-
// assume to be a zip file
33-
var zipRd *zip.ReadCloser
34-
zipRd, err = zip.OpenReader(themePath)
35-
if err != nil {
36-
return
37-
}
38-
defer zipRd.Close()
39-
40-
for _, f := range zipRd.File {
41-
var rd io.ReadCloser
42-
rd, err = f.Open()
43-
if err != nil {
44-
continue
45-
}
46-
var raw []byte
47-
raw, err = io.ReadAll(rd)
48-
rd.Close()
49-
if err != nil {
50-
return
51-
}
52-
if f.Name == "index.html" {
53-
currentTheme.Template, err = ParsePageTpl(string(raw))
54-
if err != nil {
55-
return
56-
}
57-
} else {
58-
currentTheme.Assets.Set(f.Name, raw)
59-
}
60-
}
61-
62-
if currentTheme.Template != nil {
63-
theme = currentTheme
64-
}
65-
66-
return
8+
type Theme interface {
9+
RenderPage(w io.Writer, data interface{}) error
10+
RenderAsset(w http.ResponseWriter, r *http.Request, assetPath string)
6711
}

0 commit comments

Comments
 (0)