Decoupling server / engine / kgpz

This commit is contained in:
Simon Martens
2025-01-09 02:41:03 +01:00
parent a62a09b258
commit f998ce61c6
11 changed files with 189 additions and 94 deletions

View File

@@ -4,12 +4,37 @@ import (
"os" "os"
"sync" "sync"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/controllers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2"
)
// INFO: this holds all the stuff specific to the KGPZ application
// It implements Map(*fiber.App) error, so it can be used as a MuxProvider
// It also implements Funcs() map[string]interface{} to map funcs to a template engine
const (
ASSETS_URL_PREFIX = "/assets"
EDITION_URL = "/edition/"
PRIVACY_URL = "/datenschutz/"
CONTACT_URL = "/kontakt/"
CITATION_URL = "/zitation/"
INDEX_URL = "/1764"
YEAR_OVERVIEW_URL = "/:year"
PLACE_OVERVIEW_URL = "/ort/:place"
AGENTS_OVERVIEW_URL = "/akteure/:letterorid"
CATEGORY_OVERVIEW_URL = "/kategorie/:category"
ISSSUE_URL = "/:year/:issue/:page?"
ADDITIONS_URL = "/:year/:issue/beilage/:page?"
) )
type KGPZ struct { type KGPZ struct {
@@ -62,6 +87,52 @@ func (k *KGPZ) InitGND() {
} }
} }
func (k *KGPZ) Routes(srv *fiber.App) error {
srv.Get("/", func(c *fiber.Ctx) error {
c.Redirect(INDEX_URL)
return nil
})
srv.Get(PLACE_OVERVIEW_URL, controllers.GetPlace(k.Library))
srv.Get(CATEGORY_OVERVIEW_URL, controllers.GetCategory(k.Library))
srv.Get(AGENTS_OVERVIEW_URL, controllers.GetAgents(k.Library))
// TODO: YEAR_OVERVIEW_URL being /:year is a bad idea, since it captures basically everything,
// probably creating problems with static files, and also in case we add a front page later.
// That's why we redirect to /1764 on "/ " above and don´t use an optional /:year? paramter.
// -> Check SEO requirements on index pages that are 301 forwarded.
// This applies to all paths with two or three segments without a static prefix:
// Prob better to do /ausgabe/:year/:issue/:page? and /jahrgang/:year? respectively.
srv.Get(YEAR_OVERVIEW_URL, controllers.GetYear(k.Library))
srv.Get(ISSSUE_URL, controllers.GetIssue(k.Library))
srv.Get(ADDITIONS_URL, controllers.GetIssue(k.Library))
srv.Get(EDITION_URL, controllers.Get(EDITION_URL))
srv.Get(PRIVACY_URL, controllers.Get(PRIVACY_URL))
srv.Get(CONTACT_URL, controllers.Get(CONTACT_URL))
srv.Get(CITATION_URL, controllers.Get(CITATION_URL))
return nil
}
func (k *KGPZ) Funcs() map[string]interface{} {
e := make(map[string]interface{})
// App specific
e["GetAgent"] = k.Library.Agents.Item
e["GetPlace"] = k.Library.Places.Item
e["GetWork"] = k.Library.Works.Item
e["GetCategory"] = k.Library.Categories.Item
e["GetIssue"] = k.Library.Issues.Item
e["GetPiece"] = k.Library.Pieces.Item
e["GetGND"] = k.GND.Person
e["LookupPieces"] = k.Library.Pieces.ReverseLookup
e["LookupWorks"] = k.Library.Works.ReverseLookup
e["LookupIssues"] = k.Library.Issues.ReverseLookup
return e
}
func (k *KGPZ) Enrich() error { func (k *KGPZ) Enrich() error {
if k.GND == nil { if k.GND == nil {
k.InitGND() k.InitGND()

View File

@@ -3,9 +3,9 @@ package controllers
import ( import (
"strings" "strings"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -13,11 +13,11 @@ const (
DEFAULT_AGENT = "a" DEFAULT_AGENT = "a"
) )
func GetAgents(kgpz *app.KGPZ) fiber.Handler { func GetAgents(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
a := c.Params("letterorid", DEFAULT_AGENT) a := c.Params("letterorid", DEFAULT_AGENT)
a = strings.ToLower(a) a = strings.ToLower(a)
agents := viewmodels.AgentsView(a, kgpz.Library) agents := viewmodels.AgentsView(a, kgpz)
if len(agents.Agents) == 0 { if len(agents.Agents) == 0 {
logging.Error(nil, "No agents found for letter or id: "+a) logging.Error(nil, "No agents found for letter or id: "+a)
return c.SendStatus(fiber.StatusNotFound) return c.SendStatus(fiber.StatusNotFound)

View File

@@ -1,11 +1,11 @@
package controllers package controllers
import ( import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func GetCategory(kgpz *app.KGPZ) fiber.Handler { func GetCategory(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
return c.Render("/kategorie/", nil) return c.Render("/kategorie/", nil)
} }

View File

@@ -3,9 +3,9 @@ package controllers
import ( import (
"strconv" "strconv"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -14,7 +14,7 @@ const (
MAXYEAR = 1779 MAXYEAR = 1779
) )
func GetIssue(kgpz *app.KGPZ) fiber.Handler { func GetIssue(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
y := c.Params("year") y := c.Params("year")
yi, err := strconv.Atoi(y) yi, err := strconv.Atoi(y)
@@ -30,7 +30,7 @@ func GetIssue(kgpz *app.KGPZ) fiber.Handler {
return c.SendStatus(fiber.StatusNotFound) return c.SendStatus(fiber.StatusNotFound)
} }
issue, err := viewmodels.NewSingleIssueView(y, d, kgpz.Library) issue, err := viewmodels.NewSingleIssueView(y, d, kgpz)
if err != nil { if err != nil {
logging.Error(err, "Issue could not be found") logging.Error(err, "Issue could not be found")

View File

@@ -1,11 +1,11 @@
package controllers package controllers
import ( import (
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func GetPlace(kgpz *app.KGPZ) fiber.Handler { func GetPlace(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
return c.Render("/ort/", nil) return c.Render("/ort/", nil)
} }

View File

@@ -3,13 +3,13 @@ package controllers
import ( import (
"strconv" "strconv"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
func GetYear(kgpz *app.KGPZ) fiber.Handler { func GetYear(kgpz *xmlmodels.Library) fiber.Handler {
return func(c *fiber.Ctx) error { return func(c *fiber.Ctx) error {
y := c.Params("year", strconv.Itoa(MINYEAR)) y := c.Params("year", strconv.Itoa(MINYEAR))
yi, err := strconv.Atoi(y) yi, err := strconv.Atoi(y)
@@ -19,7 +19,7 @@ func GetYear(kgpz *app.KGPZ) fiber.Handler {
return c.SendStatus(fiber.StatusNotFound) return c.SendStatus(fiber.StatusNotFound)
} }
view, err := viewmodels.YearView(yi, kgpz.Library) view, err := viewmodels.YearView(yi, kgpz)
if err != nil { if err != nil {
logging.ErrorDebug(err, "Keine Ausgaben für das Jahr "+y) logging.ErrorDebug(err, "Keine Ausgaben für das Jahr "+y)
return c.SendStatus(fiber.StatusNotFound) return c.SendStatus(fiber.StatusNotFound)

View File

@@ -1,4 +1,4 @@
package server package helpers
import ( import (
"io/fs" "io/fs"
@@ -8,7 +8,7 @@ import (
"github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/fiber/v2/middleware/filesystem"
) )
func static(fs *fs.FS) fiber.Handler { func StaticHandler(fs *fs.FS) fiber.Handler {
return filesystem.New(filesystem.Config{ return filesystem.New(filesystem.Config{
Root: http.FS(*fs), Root: http.FS(*fs),
Browse: false, Browse: false,

View File

@@ -21,12 +21,26 @@ const (
DEV_CONFIG = "config.dev.json" DEV_CONFIG = "config.dev.json"
) )
type App struct {
KGPZ *app.KGPZ
Server *server.Server
Config *providers.ConfigProvider
Engine *templating.Engine
}
func main() { func main() {
cfg := providers.NewConfigProvider([]string{DEV_CONFIG, DEFAULT_CONFIG}) cfg := providers.NewConfigProvider([]string{DEV_CONFIG, DEFAULT_CONFIG})
if err := cfg.Read(); err != nil { if err := cfg.Read(); err != nil {
helpers.Assert(err, "Error reading config") helpers.Assert(err, "Error reading config")
} }
app, err := Init(cfg)
helpers.Assert(err, "Error initializing app")
Run(app)
}
func Init(cfg *providers.ConfigProvider) (*App, error) {
if cfg.Config.Debug { if cfg.Config.Debug {
logging.SetDebug() logging.SetDebug()
} else { } else {
@@ -34,14 +48,20 @@ func main() {
} }
kgpz := app.NewKGPZ(cfg) kgpz := app.NewKGPZ(cfg)
// TODO: this must return an error on failure
kgpz.Init() kgpz.Init()
server := server.Create(kgpz, cfg, Engine(kgpz, cfg)) engine := Engine(kgpz, cfg)
Start(kgpz, server, cfg) server := server.Create(cfg, engine)
server.AddPre(engine)
server.AddMux(kgpz)
return &App{KGPZ: kgpz, Server: server, Config: cfg, Engine: engine}, nil
} }
func Start(k *app.KGPZ, s *server.Server, c *providers.ConfigProvider) { func Run(app *App) {
s.Start() app.Server.Start()
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
done := make(chan bool, 1) done := make(chan bool, 1)
@@ -53,19 +73,19 @@ func Start(k *app.KGPZ, s *server.Server, c *providers.ConfigProvider) {
logging.Info("Signal received, Cleaning up...") logging.Info("Signal received, Cleaning up...")
// INFO: here we add cleanup functions // INFO: here we add cleanup functions
if sig == syscall.SIGTERM { if sig == syscall.SIGTERM {
s.Stop() app.Server.Stop()
logging.Info("Server stopped. Waiting for FS.") logging.Info("Server stopped. Waiting for FS.")
} else { } else {
s.Kill() app.Server.Kill()
logging.Info("Server killed. Waiting for FS.") logging.Info("Server killed. Waiting for FS.")
} }
k.Shutdown() app.KGPZ.Shutdown()
logging.Info("Shutdown complete.") logging.Info("Shutdown complete.")
done <- true done <- true
}() }()
// INFO: hot reloading for poor people // INFO: hot reloading for poor people
if c.Watch { if app.Config.Watch {
go func() { go func() {
_, routesexist := os.Stat(server.ROUTES_FILEPATH) _, routesexist := os.Stat(server.ROUTES_FILEPATH)
_, layoutexist := os.Stat(server.LAYOUT_FILEPATH) _, layoutexist := os.Stat(server.LAYOUT_FILEPATH)
@@ -83,7 +103,10 @@ func Start(k *app.KGPZ, s *server.Server, c *providers.ConfigProvider) {
watcher.Append(func(path string) { watcher.Append(func(path string) {
logging.Info("File changed: ", path) logging.Info("File changed: ", path)
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
s.Engine(Engine(k, c)) engine := Engine(app.KGPZ, app.Config)
app.Server.ClearPre()
app.Server.AddPre(engine)
app.Server.Engine(engine)
}) })
if routesexist != nil { if routesexist != nil {
@@ -125,7 +148,7 @@ func Start(k *app.KGPZ, s *server.Server, c *providers.ConfigProvider) {
func Engine(kgpz *app.KGPZ, c *providers.ConfigProvider) *templating.Engine { func Engine(kgpz *app.KGPZ, c *providers.ConfigProvider) *templating.Engine {
e := templating.NewEngine(&views.LayoutFS, &views.RoutesFS) e := templating.NewEngine(&views.LayoutFS, &views.RoutesFS)
e.Funcs(kgpz) e.AddFuncs(kgpz.Funcs())
e.Globals(fiber.Map{"isDev": c.Config.Debug, "name": "KGPZ", "lang": "de"}) e.Globals(fiber.Map{"isDev": c.Config.Debug, "name": "KGPZ", "lang": "de"})
return e return e
} }

11
server/interfaces.go Normal file
View File

@@ -0,0 +1,11 @@
package server
import "github.com/gofiber/fiber/v2"
type MuxProvider interface {
Routes(app *fiber.App) error
}
type PreMuxProvider interface {
Pre(app *fiber.App) error
}

View File

@@ -4,16 +4,12 @@ import (
"sync" "sync"
"time" "time"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/controllers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/templating" "github.com/Theodor-Springmann-Stiftung/kgpz_web/templating"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/views"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cache" "github.com/gofiber/fiber/v2/middleware/cache"
"github.com/gofiber/fiber/v2/middleware/etag"
"github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover" "github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/storage/memory/v2" "github.com/gofiber/storage/memory/v2"
@@ -29,25 +25,6 @@ const (
CACHE_GC_INTERVAL = 120 * time.Second CACHE_GC_INTERVAL = 120 * time.Second
) )
const (
ASSETS_URL_PREFIX = "/assets"
EDITION_URL = "/edition/"
PRIVACY_URL = "/datenschutz/"
CONTACT_URL = "/kontakt/"
CITATION_URL = "/zitation/"
INDEX_URL = "/1764"
YEAR_OVERVIEW_URL = "/:year"
PLACE_OVERVIEW_URL = "/ort/:place"
AGENTS_OVERVIEW_URL = "/akteure/:letterorid"
CATEGORY_OVERVIEW_URL = "/kategorie/:category"
ISSSUE_URL = "/:year/:issue/:page?"
ADDITIONS_URL = "/:year/:issue/beilage/:page?"
)
const ( const (
STATIC_FILEPATH = "./views/assets" STATIC_FILEPATH = "./views/assets"
ROUTES_FILEPATH = "./views/routes" ROUTES_FILEPATH = "./views/routes"
@@ -69,18 +46,20 @@ type Server struct {
engine *templating.Engine engine *templating.Engine
mu sync.Mutex mu sync.Mutex
kgpz *app.KGPZ // Maybe that is to much, it should just be a list of method, path, handler structs
// in the order in which they are ought to be mapped.
muxproviders []MuxProvider
premuxproviders []PreMuxProvider
} }
func Create(k *app.KGPZ, c *providers.ConfigProvider, e *templating.Engine) *Server { func Create(c *providers.ConfigProvider, e *templating.Engine) *Server {
if c == nil || k == nil { if c == nil {
logging.Error(nil, "Error creating server: Config or App is posssibly nil.") logging.Error(nil, "Error creating server: Config or App is posssibly nil.")
return nil return nil
} }
return &Server{ return &Server{
Config: c, Config: c,
kgpz: k,
engine: e, engine: e,
} }
} }
@@ -93,6 +72,22 @@ func (s *Server) Engine(e *templating.Engine) {
s.Start() s.Start()
} }
func (s *Server) AddMux(m MuxProvider) {
s.muxproviders = append(s.muxproviders, m)
}
func (s *Server) ClearMux() {
s.muxproviders = []MuxProvider{}
}
func (s *Server) AddPre(m PreMuxProvider) {
s.premuxproviders = append(s.premuxproviders, m)
}
func (s *Server) ClearPre() {
s.premuxproviders = []PreMuxProvider{}
}
// TODO: There is no error handler // TODO: There is no error handler
func (s *Server) Start() { func (s *Server) Start() {
s.mu.Lock() s.mu.Lock()
@@ -130,15 +125,20 @@ func (s *Server) Start() {
ViewsLayout: templating.DEFAULT_LAYOUT_NAME, ViewsLayout: templating.DEFAULT_LAYOUT_NAME,
}) })
for _, m := range s.premuxproviders {
err := m.Pre(srv)
if err != nil {
logging.Error(err, "Error mapping premuxprovider")
return
}
}
if s.Config.Debug { if s.Config.Debug {
srv.Use(logger.New()) srv.Use(logger.New())
} }
srv.Use(recover.New()) srv.Use(recover.New())
srv.Use(ASSETS_URL_PREFIX, etag.New())
srv.Use(ASSETS_URL_PREFIX, static(&views.StaticFS))
// TODO: Dont cache static assets, bc storage gets huge on images. // TODO: Dont cache static assets, bc storage gets huge on images.
// -> Maybe fiber does this already, automatically? // -> Maybe fiber does this already, automatically?
if s.Config.Debug { if s.Config.Debug {
@@ -157,29 +157,13 @@ func (s *Server) Start() {
})) }))
} }
srv.Get("/", func(c *fiber.Ctx) error { for _, m := range s.muxproviders {
c.Redirect(INDEX_URL) err := m.Routes(srv)
return nil if err != nil {
}) logging.Error(err, "Error mapping muxprovider")
return
srv.Get(PLACE_OVERVIEW_URL, controllers.GetPlace(s.kgpz)) }
srv.Get(CATEGORY_OVERVIEW_URL, controllers.GetCategory(s.kgpz)) }
srv.Get(AGENTS_OVERVIEW_URL, controllers.GetAgents(s.kgpz))
// TODO: YEAR_OVERVIEW_URL being /:year is a bad idea, since it captures basically everything,
// probably creating problems with static files, and also in case we add a front page later.
// That's why we redirect to /1764 on "/ " above and don´t use an optional /:year? paramter.
// -> Check SEO requirements on index pages that are 301 forwarded.
// This applies to all paths with two or three segments without a static prefix:
// Prob better to do /ausgabe/:year/:issue/:page? and /jahrgang/:year? respectively.
srv.Get(YEAR_OVERVIEW_URL, controllers.GetYear(s.kgpz))
srv.Get(ISSSUE_URL, controllers.GetIssue(s.kgpz))
srv.Get(ADDITIONS_URL, controllers.GetIssue(s.kgpz))
srv.Get(EDITION_URL, controllers.Get(EDITION_URL))
srv.Get(PRIVACY_URL, controllers.Get(PRIVACY_URL))
srv.Get(CONTACT_URL, controllers.Get(CONTACT_URL))
srv.Get(CITATION_URL, controllers.Get(CITATION_URL))
s.runner(srv) s.runner(srv)

View File

@@ -7,10 +7,15 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/functions" "github.com/Theodor-Springmann-Stiftung/kgpz_web/functions"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
"github.com/Theodor-Springmann-Stiftung/kgpz_web/views" "github.com/Theodor-Springmann-Stiftung/kgpz_web/views"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/etag"
)
const (
ASSETS_URL_PREFIX = "/assets"
) )
type Engine struct { type Engine struct {
@@ -30,14 +35,14 @@ func NewEngine(layouts, templates *fs.FS) *Engine {
mu: &sync.Mutex{}, mu: &sync.Mutex{},
LayoutRegistry: NewLayoutRegistry(*layouts), LayoutRegistry: NewLayoutRegistry(*layouts),
TemplateRegistry: NewTemplateRegistry(*templates), TemplateRegistry: NewTemplateRegistry(*templates),
FuncMap: make(template.FuncMap),
} }
e.funcs()
return &e return &e
} }
func (e *Engine) Funcs(app *app.KGPZ) error { func (e *Engine) funcs() error {
e.mu.Lock() e.mu.Lock()
e.FuncMap = make(map[string]interface{})
e.mu.Unlock() e.mu.Unlock()
// Dates // Dates
@@ -58,19 +63,12 @@ func (e *Engine) Funcs(app *app.KGPZ) error {
// Embedding of XSLT files // Embedding of XSLT files
e.AddFunc("EmbedXSLT", functions.EmbedXSLT(views.StaticFS)) e.AddFunc("EmbedXSLT", functions.EmbedXSLT(views.StaticFS))
// App specific return nil
e.AddFunc("GetAgent", app.Library.Agents.Item) }
e.AddFunc("GetPlace", app.Library.Places.Item)
e.AddFunc("GetWork", app.Library.Works.Item)
e.AddFunc("GetCategory", app.Library.Categories.Item)
e.AddFunc("GetIssue", app.Library.Issues.Item)
e.AddFunc("GetPiece", app.Library.Pieces.Item)
e.AddFunc("GetGND", app.GND.Person)
e.AddFunc("LookupPieces", app.Library.Pieces.ReverseLookup)
e.AddFunc("LookupWorks", app.Library.Works.ReverseLookup)
e.AddFunc("LookupIssues", app.Library.Issues.ReverseLookup)
func (e Engine) Pre(srv *fiber.App) error {
srv.Use(ASSETS_URL_PREFIX, etag.New())
srv.Use(ASSETS_URL_PREFIX, helpers.StaticHandler(&views.StaticFS))
return nil return nil
} }
@@ -129,6 +127,14 @@ func (e *Engine) AddFunc(name string, fn interface{}) {
e.FuncMap[name] = fn e.FuncMap[name] = fn
} }
func (e *Engine) AddFuncs(funcs map[string]interface{}) {
e.mu.Lock()
defer e.mu.Unlock()
for k, v := range funcs {
e.FuncMap[k] = v
}
}
func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...string) error { func (e *Engine) Render(out io.Writer, path string, data interface{}, layout ...string) error {
// TODO: check if a reload is needed if files on disk have changed // TODO: check if a reload is needed if files on disk have changed
ld := data.(fiber.Map) ld := data.(fiber.Map)