Started server

This commit is contained in:
Simon Martens
2026-02-19 15:09:44 +01:00
parent df79656c77
commit 59fb813c85
7 changed files with 115 additions and 94 deletions

46
:wq Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"log/slog"
"github.com/Theodor-Springmann-Stiftung/lenz-web/app"
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
)
func main() {
cfg, err := GetConfig()
if err != nil {
panic(err)
}
if cfg.Debug {
slog.SetLogLoggerLevel(slog.LevelDebug)
}
application, err := app.New(cfg)
if err != nil {
panic(err)
}
err = Serve(application)
if err != nil {
panic(err)
}
}
func Serve(app *app.App) error {
server, err := server.NewServer(app)
if err != nil {
return err
}
server.Start()
return nil
}
func GetConfig() (app.Config, error) {
cp := app.NewConfigProvider([]string{"config.dev.json", "config.json"})
if err := cp.Read(); err != nil {
return app.Config{}, err
}
return *cp.Config, nil
}

View File

@@ -1,7 +1,6 @@
package app package app
import ( import (
"fmt"
"html/template" "html/template"
"path/filepath" "path/filepath"
"sync" "sync"
@@ -12,12 +11,11 @@ import (
) )
type App struct { type App struct {
mu sync.Mutex mu sync.Mutex
lib atomic.Pointer[xmlmodels.Library] lib atomic.Pointer[xmlmodels.Library]
repo atomic.Pointer[git.Repo] repo atomic.Pointer[git.Repo]
tmpl *template.Template tmpl atomic.Pointer[*template.Template]
routes []Route cfg Config
err error
} }
func New(cfg Config) (*App, error) { func New(cfg Config) (*App, error) {
@@ -37,39 +35,15 @@ func (a *App) Repo() *git.Repo {
} }
func (a *App) Templates() *template.Template { func (a *App) Templates() *template.Template {
return a.tmpl return *a.tmpl.Load()
} }
func (a *App) Routes() []Route { func (a *App) Config() Config {
a.mu.Lock() return a.cfg
defer a.mu.Unlock()
ret := make([]Route, len(a.routes))
copy(ret, a.routes)
return ret
} }
func (a *App) RenderPath(path string) ([]byte, error) { func (a *App) Pages() []Page {
a.mu.Lock() return RegisteredPages()
var route *Route
for i := range a.routes {
if a.routes[i].Path == path {
r := a.routes[i]
route = &r
break
}
}
a.mu.Unlock()
if route == nil || route.page == nil {
return nil, fmt.Errorf("no route for path: %s", path)
}
model, err := route.page.Model(a, *route)
if err != nil {
return nil, err
}
return route.page.Render(a, *route, model)
} }
func (a *App) init(cfg Config) error { func (a *App) init(cfg Config) error {
@@ -103,15 +77,9 @@ func (a *App) init(cfg Config) error {
a.repo.Store(repo) a.repo.Store(repo)
a.lib.Store(lib) a.lib.Store(lib)
a.tmpl = tmpl a.tmpl.Store(&tmpl)
routes, err := discoverPages(a) a.cfg = cfg
if err != nil {
return err
}
a.routes = routes
a.err = nil
return nil return nil
} }
@@ -126,12 +94,5 @@ func (a *App) RefreshLibrary(dir string, commit *git.Commit) (*xmlmodels.Library
a.lib.Store(lib) a.lib.Store(lib)
routes, err := discoverPages(a)
if err != nil {
return nil, err
}
a.routes = routes
a.err = nil
return lib, nil return lib, nil
} }

View File

@@ -1,7 +1,5 @@
package app package app
import "sync"
type Route struct { type Route struct {
Path string Path string
Kind string Kind string
@@ -12,28 +10,19 @@ type Route struct {
type Page interface { type Page interface {
Discover(app *App) ([]Route, error) Discover(app *App) ([]Route, error)
Model(app *App, route Route) (map[string]any, error) Model(app *App, route Route) (map[string]any, error)
Render(app *App, route Route, model map[string]any) ([]byte, error)
} }
var ( var pages []Page
pagesMu sync.RWMutex
pages []Page
)
func RegisterPage(page Page) { func RegisterPage(page Page) {
if page == nil { if page == nil {
panic("cannot register nil page") panic("cannot register nil page")
} }
pagesMu.Lock()
defer pagesMu.Unlock()
pages = append(pages, page) pages = append(pages, page)
} }
func RegisteredPages() []Page { func RegisteredPages() []Page {
pagesMu.RLock()
defer pagesMu.RUnlock()
ret := make([]Page, len(pages)) ret := make([]Page, len(pages))
copy(ret, pages) copy(ret, pages)
return ret return ret

30
lenz.go
View File

@@ -1,13 +1,10 @@
package main package main
import ( import (
"io/fs"
"log/slog" "log/slog"
"github.com/Theodor-Springmann-Stiftung/lenz-web/app" "github.com/Theodor-Springmann-Stiftung/lenz-web/app"
_ "github.com/Theodor-Springmann-Stiftung/lenz-web/pages" "github.com/Theodor-Springmann-Stiftung/lenz-web/server"
"github.com/Theodor-Springmann-Stiftung/lenz-web/templates"
"github.com/labstack/echo/v5"
) )
func main() { func main() {
@@ -25,28 +22,19 @@ func main() {
panic(err) panic(err)
} }
e := echo.New() err = Serve(application)
publicFS, err := fs.Sub(templates.PublicFS, "public")
if err != nil { if err != nil {
panic(err) panic(err)
} }
e.StaticFS("/public", publicFS) }
for _, route := range application.Routes() { func Serve(app *app.App) error {
routePath := route.Path server, err := server.NewServer(app)
e.GET(routePath, func(c *echo.Context) error { if err != nil {
out, err := application.RenderPath(routePath) return err
if err != nil {
return err
}
return c.HTMLBlob(200, out)
})
}
addr := cfg.Address + ":" + cfg.Port
if err := e.Start(addr); err != nil {
panic(err)
} }
server.Start()
return nil
} }
func GetConfig() (app.Config, error) { func GetConfig() (app.Config, error) {

View File

@@ -1,8 +1,6 @@
package pages package pages
import ( import (
"bytes"
"github.com/Theodor-Springmann-Stiftung/lenz-web/app" "github.com/Theodor-Springmann-Stiftung/lenz-web/app"
) )
@@ -23,11 +21,3 @@ func (p HomePage) Model(a *app.App, route app.Route) (map[string]any, error) {
"Message": "Template system is working.", "Message": "Template system is working.",
}, nil }, nil
} }
func (p HomePage) Render(a *app.App, route app.Route, model map[string]any) ([]byte, error) {
var buf bytes.Buffer
if err := a.Templates().ExecuteTemplate(&buf, "home", model); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

11
server/endpoints.go Normal file
View File

@@ -0,0 +1,11 @@
package server
import (
"github.com/Theodor-Springmann-Stiftung/lenz-web/templates"
"github.com/labstack/echo/v5"
)
func MapStatic(e *echo.Echo) {
// INFO: Static files here:
e.StaticFS("/public", templates.PublicFS)
}

36
server/server.go Normal file
View File

@@ -0,0 +1,36 @@
package server
import (
"html/template"
"github.com/Theodor-Springmann-Stiftung/lenz-web/app"
"github.com/labstack/echo/v5"
)
// WARNING: this is not thread-safe. You can not write and read from the server at the same time!
type Server struct {
server *echo.Echo
cfg app.Config
tmpl *template.Template
}
func NewServer(app *app.App) (*Server, error) {
s := &Server{
server: echo.New(),
cfg: app.Config(),
tmpl: app.Templates(),
}
// INFO: Endpoint mapping here:
MapStatic(s.server)
return s, nil
}
func (s *Server) Start() error {
addr := s.cfg.Address + ":" + s.cfg.Port
if err := s.server.Start(addr); err != nil {
return err
}
return nil
}