diff options
-rw-r--r-- | .gitattributes | 1 | ||||
-rw-r--r-- | main.go | 52 | ||||
-rw-r--r-- | pkg/atexit/atexit.go | 13 | ||||
-rw-r--r-- | pkg/try/try.go (renamed from src/try/try.go) | 9 | ||||
-rw-r--r-- | pkg/watch/watch.go | 41 | ||||
-rw-r--r-- | src/dbx/db.go | 7 | ||||
-rw-r--r-- | src/http.go | 8 | ||||
-rw-r--r-- | src/i18n.go | 2 | ||||
-rw-r--r-- | src/templates.go | 40 | ||||
-rw-r--r-- | src/templates/coins-designs-ad.html.tmpl | 2 | ||||
-rw-r--r-- | src/templates/coins-designs-be.html.tmpl | 5 | ||||
-rw-r--r-- | src/templates/coins-designs-hr.html.tmpl | 8 | ||||
-rw-r--r-- | static/fonts/ysabeau-office-regular-italic.otf | bin | 0 -> 213512 bytes | |||
-rw-r--r-- | static/fonts/ysabeau-office-regular.otf | bin | 0 -> 272220 bytes | |||
-rw-r--r-- | static/fonts/ysabeau-regular-italic.woff2 | bin | 50700 -> 0 bytes | |||
-rw-r--r-- | static/fonts/ysabeau-regular.woff2 | bin | 47828 -> 0 bytes | |||
-rw-r--r-- | static/style.css | 12 |
17 files changed, 128 insertions, 72 deletions
diff --git a/.gitattributes b/.gitattributes index 89147c5..0e79892 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.avif binary *.jpg binary +*.otf binary vendor/ linguist-vendored
\ No newline at end of file @@ -5,16 +5,18 @@ package main import ( "flag" - "log" "os" + "os/signal" "path/filepath" "syscall" - "time" + + "git.thomasvoss.com/euro-cash.eu/pkg/atexit" + . "git.thomasvoss.com/euro-cash.eu/pkg/try" + "git.thomasvoss.com/euro-cash.eu/pkg/watch" "git.thomasvoss.com/euro-cash.eu/src" "git.thomasvoss.com/euro-cash.eu/src/dbx" "git.thomasvoss.com/euro-cash.eu/src/email" - . "git.thomasvoss.com/euro-cash.eu/src/try" ) func main() { @@ -38,39 +40,23 @@ func main() { "database name or ‘:memory:’ for an in-memory database") flag.Parse() + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigs + atexit.Exec() + os.Exit(0) + }() + if *debugp { - go watch() + path := Try2(os.Executable()) + go watch.File(path, func() { + atexit.Exec() + Try(syscall.Exec(path, os.Args, os.Environ())) + }) } dbx.Init(Try2(os.OpenRoot("src/dbx/sql")).FS()) - app.BuildTemplates(Try2(os.OpenRoot("src/templates")).FS()) + app.BuildTemplates(Try2(os.OpenRoot("src/templates")).FS(), *debugp) app.Run(*port) } - -func watch() { - path, err := os.Executable() - if err != nil { - log.Fatal(err) - } - - ostat, err := os.Stat(path) - if err != nil { - log.Fatal(err) - } - - for { - nstat, err := os.Stat(path) - if err != nil { - log.Fatal(err) - } - - if nstat.ModTime() != ostat.ModTime() { - dbx.Close() - if err := syscall.Exec(path, os.Args, os.Environ()); err != nil { - log.Fatal(err) - } - } - - time.Sleep(1 * time.Second) - } -} diff --git a/pkg/atexit/atexit.go b/pkg/atexit/atexit.go new file mode 100644 index 0000000..a349649 --- /dev/null +++ b/pkg/atexit/atexit.go @@ -0,0 +1,13 @@ +package atexit + +var hooks = []func(){} + +func Register(f func()) { + hooks = append(hooks, f) +} + +func Exec() { + for i := len(hooks) - 1; i >= 0; i-- { + hooks[i]() + } +} diff --git a/src/try/try.go b/pkg/try/try.go index fc086d1..2576dcd 100644 --- a/src/try/try.go +++ b/pkg/try/try.go @@ -1,10 +1,15 @@ package try -import "log" +import ( + "log" + + "git.thomasvoss.com/euro-cash.eu/pkg/atexit" +) func Try(e error) { if e != nil { - log.Fatal(e) + atexit.Exec() + log.Fatalln(e) } } diff --git a/pkg/watch/watch.go b/pkg/watch/watch.go new file mode 100644 index 0000000..f409dac --- /dev/null +++ b/pkg/watch/watch.go @@ -0,0 +1,41 @@ +package watch + +import ( + "errors" + "io/fs" + "log" + "os" + "time" + + . "git.thomasvoss.com/euro-cash.eu/pkg/try" +) + +func File(path string, f func()) { + impl(path, os.Stat, f) +} + +func FileFS(dir fs.FS, path string, f func()) { + impl(path, func(path string) (os.FileInfo, error) { + return fs.Stat(dir, path) + }, f) +} + +func impl(path string, statfn func(string) (os.FileInfo, error), f func()) { + ostat := Try2(statfn(path)) + + for { + nstat, err := statfn(path) + switch { + case errors.Is(err, os.ErrNotExist): + return + case err != nil: + log.Println(err) + } + + if nstat.ModTime() != ostat.ModTime() { + f() + ostat = nstat + } + time.Sleep(1 * time.Second) + } +} diff --git a/src/dbx/db.go b/src/dbx/db.go index 5bd65a5..fcb345e 100644 --- a/src/dbx/db.go +++ b/src/dbx/db.go @@ -9,9 +9,9 @@ import ( "sort" "strings" + "git.thomasvoss.com/euro-cash.eu/pkg/atexit" + . "git.thomasvoss.com/euro-cash.eu/pkg/try" "github.com/mattn/go-sqlite3" - - . "git.thomasvoss.com/euro-cash.eu/src/try" ) var ( @@ -22,6 +22,7 @@ var ( func Init(sqlDir fs.FS) { db = Try2(sql.Open("sqlite3", DBName)) Try(db.Ping()) + atexit.Register(Close) Try(applyMigrations(sqlDir)) /* TODO: Remove debug code */ @@ -120,7 +121,7 @@ func applyMigrations(dir fs.FS) error { if err := tx.Commit(); err != nil { return err } - log.Printf("Applied database migration ‘%s’", f) + log.Printf("Applied database migration ‘%s’\n", f) } if last != "" { diff --git a/src/http.go b/src/http.go index a5897b4..6feb865 100644 --- a/src/http.go +++ b/src/http.go @@ -12,6 +12,8 @@ import ( "strconv" "strings" + . "git.thomasvoss.com/euro-cash.eu/pkg/try" + "git.thomasvoss.com/euro-cash.eu/src/dbx" "git.thomasvoss.com/euro-cash.eu/src/email" ) @@ -41,9 +43,7 @@ func Run(port int) { portStr := ":" + strconv.Itoa(port) log.Println("Listening on", portStr) - err := http.ListenAndServe(portStr, mux) - dbx.Close() - log.Fatal(err) + Try(http.ListenAndServe(portStr, mux)) } func chain(xs ...middleware) middleware { @@ -179,7 +179,7 @@ func throwError(status int, err error, w http.ResponseWriter, r *http.Request) { w.WriteHeader(status) go func() { if err := email.ServerError(err); err != nil { - log.Print(err) + log.Println(err) } }() errorTmpl.Execute(w, struct { diff --git a/src/i18n.go b/src/i18n.go index 726a75c..cb54e28 100644 --- a/src/i18n.go +++ b/src/i18n.go @@ -261,7 +261,7 @@ func (p Printer) M(val any) string { default: go func() { if err := email.ServerError(badMType{"TODO"}); err != nil { - log.Print(err) + log.Println(err) } }() /* Hopefully this never happens */ diff --git a/src/templates.go b/src/templates.go index 8479759..4deeb67 100644 --- a/src/templates.go +++ b/src/templates.go @@ -7,6 +7,9 @@ import ( "log" "strings" + . "git.thomasvoss.com/euro-cash.eu/pkg/try" + "git.thomasvoss.com/euro-cash.eu/pkg/watch" + "git.thomasvoss.com/euro-cash.eu/src/dbx" ) @@ -31,29 +34,36 @@ var ( } ) -func BuildTemplates(dir fs.FS) { - ents, err := fs.ReadDir(dir, ".") - if err != nil { - log.Fatal(err) - } - +func BuildTemplates(dir fs.FS, debugp bool) { + ents := Try2(fs.ReadDir(dir, ".")) notFoundTmpl = buildTemplate(dir, "-404") errorTmpl = buildTemplate(dir, "-error") - templates = make(map[string]*template.Template, len(ents)) + for _, e := range ents { - path := "/" - name := strings.TrimSuffix(e.Name(), ".html.tmpl") - switch { - case name[0] == '-': - continue - case name != "index": - path += strings.ReplaceAll(name, "-", "/") + name := e.Name() + buildAndSetTemplate(dir, name) + if debugp { + go watch.FileFS(dir, name, func() { + buildAndSetTemplate(dir, name) + log.Printf("Template ‘%s’ updated\n", name) + }) } - templates[path] = buildTemplate(dir, name) } } +func buildAndSetTemplate(dir fs.FS, name string) { + path := "/" + name = strings.TrimSuffix(name, ".html.tmpl") + switch { + case name[0] == '-': + return + case name != "index": + path += strings.ReplaceAll(name, "-", "/") + } + templates[path] = buildTemplate(dir, name) +} + func buildTemplate(dir fs.FS, name string) *template.Template { names := [...]string{"-base", "-navbar", name} for i, s := range names { diff --git a/src/templates/coins-designs-ad.html.tmpl b/src/templates/coins-designs-ad.html.tmpl index c42930a..20199bb 100644 --- a/src/templates/coins-designs-ad.html.tmpl +++ b/src/templates/coins-designs-ad.html.tmpl @@ -85,7 +85,7 @@ {{ .T ` The bottom of the coat of arms has the motto ‘%sVIRTVS VNITA FORTIOR%s’ (‘UNITED VIRTUE IS STRONGER’). - ` `<span lang="la">` `</span>` }} + ` `<span lang="la">` `</span>` | safe }} </p> </main> {{ end }}
\ No newline at end of file diff --git a/src/templates/coins-designs-be.html.tmpl b/src/templates/coins-designs-be.html.tmpl index 22f533f..4dcd325 100644 --- a/src/templates/coins-designs-be.html.tmpl +++ b/src/templates/coins-designs-be.html.tmpl @@ -21,8 +21,7 @@ target="_blank" href="https://www.wikipedia.org/wiki/Royal_cypher" >` - `</a>` - }} + `</a>` | safe }} </p> <p> {{ .T ` @@ -44,4 +43,4 @@ ` }} </p> </main> -{{ end }} +{{ end }}
\ No newline at end of file diff --git a/src/templates/coins-designs-hr.html.tmpl b/src/templates/coins-designs-hr.html.tmpl index b6333ba..8b6976f 100644 --- a/src/templates/coins-designs-hr.html.tmpl +++ b/src/templates/coins-designs-hr.html.tmpl @@ -26,10 +26,10 @@ <p> {{ .T ` The 1-, 2-, and 5 euro cent coins were designed by Maja - Škripelj and feature a motif of the letters ‘HR’ in the + Škripelj and feature a motif of the letters ‘ⰘⰓ’ from the %sGlagolitic script%s — an old Slavic script that saw use in - Croatia up until the 19th century — with ‘HR’ representing - Croatia’s country code.` + Croatia up until the 19th century — representing Croatia’s country + code (‘HR’ in the Latin alphabet).` `<a target="_blank" href="https://www.wikipedia.org/wiki/Glagolitic_script" @@ -80,4 +80,4 @@ `</a>` | safe }} </p> </main> -{{ end }} +{{ end }}
\ No newline at end of file diff --git a/static/fonts/ysabeau-office-regular-italic.otf b/static/fonts/ysabeau-office-regular-italic.otf Binary files differnew file mode 100644 index 0000000..eb92805 --- /dev/null +++ b/static/fonts/ysabeau-office-regular-italic.otf diff --git a/static/fonts/ysabeau-office-regular.otf b/static/fonts/ysabeau-office-regular.otf Binary files differnew file mode 100644 index 0000000..cb8d758 --- /dev/null +++ b/static/fonts/ysabeau-office-regular.otf diff --git a/static/fonts/ysabeau-regular-italic.woff2 b/static/fonts/ysabeau-regular-italic.woff2 Binary files differdeleted file mode 100644 index a517fdc..0000000 --- a/static/fonts/ysabeau-regular-italic.woff2 +++ /dev/null diff --git a/static/fonts/ysabeau-regular.woff2 b/static/fonts/ysabeau-regular.woff2 Binary files differdeleted file mode 100644 index 95c7715..0000000 --- a/static/fonts/ysabeau-regular.woff2 +++ /dev/null diff --git a/static/style.css b/static/style.css index 023317c..2bce9d4 100644 --- a/static/style.css +++ b/static/style.css @@ -27,22 +27,22 @@ */ @font-face { - font-family: 'Ysabeau'; - src: url('/fonts/ysabeau-regular.woff2') format('woff2'); + font-family: 'Ysabeau Office'; + src: url('/fonts/ysabeau-office-regular.otf') format('opentype'); font-weight: normal; font-style: normal; } @font-face { - font-family: 'Ysabeau'; - src: url('/fonts/ysabeau-regular-italic.woff2') format('woff2'); + font-family: 'Ysabeau Office'; + src: url('/fonts/ysabeau-office-regular-italic.otf') format('opentype'); font-weight: normal; font-style: italic; } :root { - --font-family: "Ysabeau", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", - "Segoe UI Symbol", "Noto Color Emoji"; + --font-family: "Ysabeau Office", sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --line-height: 1.5; --font-weight: 400; --font-size: 16px; |