Skip to content
Snippets Groups Projects
Commit 8d509fe9 authored by Jens Heise's avatar Jens Heise
Browse files

Update with basic templating and server functionality.

parent debea584
No related branches found
No related tags found
No related merge requests found
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
"github.com/org-harmony/harmony/core/event" "github.com/org-harmony/harmony/core/event"
"github.com/org-harmony/harmony/core/trace" "github.com/org-harmony/harmony/core/trace"
"github.com/org-harmony/harmony/core/web" "github.com/org-harmony/harmony/core/web"
"os"
) )
const WebMod = "sys.cmd.web" const WebMod = "sys.cmd.web"
...@@ -20,15 +19,17 @@ func main() { ...@@ -20,15 +19,17 @@ func main() {
em := event.NewEventManager(l) em := event.NewEventManager(l)
v := validator.New(validator.WithRequiredStructEnabled()) v := validator.New(validator.WithRequiredStructEnabled())
err := config.ToEnv(config.From("env")) webCfg := &web.Cfg{}
err := config.C(webCfg, config.From("web"), config.Validate(v))
if err != nil { if err != nil {
l.Error(WebMod, "failed to load config to env", err) l.Error(WebMod, "failed to load config", err)
return return
} }
s := web.NewServer( s := web.NewServer(
web.WithFileServer(webCfg.Server.AssetFsCfg),
web.WithAddr(fmt.Sprintf("%s:%s", webCfg.Server.Addr, webCfg.Server.Port)),
web.WithLogger(l),
web.WithEventManger(em), web.WithEventManger(em),
web.WithAddr(fmt.Sprintf(":%s", os.Getenv("WEB_SERVER_PORT"))),
) )
s.RegisterController(nil) s.RegisterController(nil)
... ...
......
...@@ -8,8 +8,8 @@ import ( ...@@ -8,8 +8,8 @@ import (
"github.com/org-harmony/harmony/core/config" "github.com/org-harmony/harmony/core/config"
) )
// AuthConfig is the config for the auth module. // Config is the config for the auth package.
type AuthConfig struct { type Config struct {
// Provider contains a list of OAuth2 providers. // Provider contains a list of OAuth2 providers.
Provider map[string]ProviderConfig `toml:"provider"` Provider map[string]ProviderConfig `toml:"provider"`
EnableOAuth2 bool `toml:"enable_oauth2"` EnableOAuth2 bool `toml:"enable_oauth2"`
...@@ -27,11 +27,9 @@ type ProviderConfig struct { ...@@ -27,11 +27,9 @@ type ProviderConfig struct {
func LoadConfig(v *validator.Validate) { func LoadConfig(v *validator.Validate) {
// TODO remove and implement real auth logic // TODO remove and implement real auth logic
cfg := &AuthConfig{} cfg := &Config{}
err := config.C(cfg, config.From("auth"), config.Validate(v)) err := config.C(cfg, config.From("auth"), config.Validate(v))
if err != nil { if err != nil {
fmt.Printf("failed to load auth config: %v", err) fmt.Printf("failed to load auth config: %v", err)
} }
fmt.Printf("config: %+v", cfg.EnableOAuth2)
} }
...@@ -2,13 +2,29 @@ package web ...@@ -2,13 +2,29 @@ package web
import ( import (
"context" "context"
"fmt"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/org-harmony/harmony/core/event" "github.com/org-harmony/harmony/core/event"
"github.com/org-harmony/harmony/core/trace" "github.com/org-harmony/harmony/core/trace"
"github.com/org-harmony/harmony/core/trans"
"html/template"
"net/http" "net/http"
"path/filepath"
"strings"
) )
// ServerCfg contains the configuration for a web server.
type ServerCfg struct {
AssetFsCfg *FileServerCfg `toml:"asset_fs" validate:"required"`
Addr string `toml:"address" env:"ADDR"`
Port string `toml:"port" env:"PORT" validate:"required"`
}
// FileServerCfg contains the configuration for a file server.
type FileServerCfg struct {
Root string `toml:"root" validate:"required"`
Route string `toml:"route" validate:"required"`
}
// StdServer contains the configuration for a web server. // StdServer contains the configuration for a web server.
// It implements the Server interface. // It implements the Server interface.
type StdServer struct { type StdServer struct {
...@@ -21,6 +37,7 @@ type ServerConfigs struct { ...@@ -21,6 +37,7 @@ type ServerConfigs struct {
Logger trace.Logger Logger trace.Logger
Addr string Addr string
EventManager *event.StdEventManager EventManager *event.StdEventManager
FileServer *FileServerCfg
} }
// ServerConfig is a function that configures a ServerConfigs. // ServerConfig is a function that configures a ServerConfigs.
...@@ -55,6 +72,45 @@ func WithEventManger(em *event.StdEventManager) ServerConfig { ...@@ -55,6 +72,45 @@ func WithEventManger(em *event.StdEventManager) ServerConfig {
} }
} }
// WithFileServer configures the file server for the web server.
func WithFileServer(cfg *FileServerCfg) ServerConfig {
return func(c *ServerConfigs) {
if cfg == nil {
return
}
if c.Router == nil {
c.Router = chi.NewRouter()
}
route := cfg.Route
// Path Validation
if strings.ContainsAny(route, "{}*") {
panic("FileServer does not permit any URL parameters.")
}
// Path Adjustment and Redirection
if route != "/" && route[len(route)-1] != '/' {
c.Router.Get(route, http.RedirectHandler(route+"/", 301).ServeHTTP)
route += "/"
}
// Adjust the route to include a wildcard
routeWithWildcard := route + "*"
// Handling of GET requests
c.Router.Get(routeWithWildcard, func(w http.ResponseWriter, r *http.Request) {
rctx := chi.RouteContext(r.Context())
pathPrefix := strings.TrimSuffix(rctx.RoutePattern(), "/*")
fs := http.StripPrefix(pathPrefix, http.FileServer(http.Dir(cfg.Root)))
fs.ServeHTTP(w, r)
})
c.FileServer = cfg
}
}
// defaultServerConfigs returns a new instance of ServerConfigs with default values. // defaultServerConfigs returns a new instance of ServerConfigs with default values.
func defaultServerConfigs() *ServerConfigs { func defaultServerConfigs() *ServerConfigs {
return &ServerConfigs{ return &ServerConfigs{
...@@ -91,9 +147,34 @@ func (s *StdServer) Serve(ctx context.Context) error { ...@@ -91,9 +147,34 @@ func (s *StdServer) Serve(ctx context.Context) error {
// RegisterController registers a controller with the web server. // RegisterController registers a controller with the web server.
func (s *StdServer) RegisterController(c ...Controller) { func (s *StdServer) RegisterController(c ...Controller) {
s.config.Router.Get("/", func(w http.ResponseWriter, r *http.Request) { s.config.Router.Get("/", func(w http.ResponseWriter, r *http.Request) {
_, err := fmt.Fprintf(w, "Hello World!") tmpl := template.New("index.go.html")
pathToAssets := s.config.FileServer.Route
t := trans.NewTranslator()
tmpl.Funcs(template.FuncMap{
"t": func(s string, ctx context.Context) string {
return t.T(s, ctx)
},
"tf": func(s string, ctx context.Context, args ...interface{}) string {
return t.Tf(s, ctx, args...)
},
"html": func(s string) template.HTML {
return template.HTML(s)
},
"asset": func(filename string) string {
return filepath.Join(pathToAssets, filename)
},
})
tmpl, err := tmpl.ParseFiles("core/web/tmpl/index.go.html")
if err != nil {
s.config.Logger.Error(Pkg, "failed to parse template", err)
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
err = tmpl.Execute(w, nil)
if err != nil { if err != nil {
s.config.Logger.Error(Pkg, "failed to write response", err) s.config.Logger.Error(Pkg, "failed to execute template", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
} }
}) })
} }
... ...
......
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="{{ asset "css/bulma.min.css" }}">
<script defer src="{{ asset "js/htmx.min.js" }}"></script>
<title>Document</title>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">
Hello World
</h1>
<p class="subtitle">
My first website with <strong>Bulma</strong>!
</p>
</div>
</section>
</body>
</html>
\ No newline at end of file
package web
...@@ -10,6 +10,10 @@ import ( ...@@ -10,6 +10,10 @@ import (
const Pkg = "sys.web" const Pkg = "sys.web"
type Cfg struct {
Server *ServerCfg `toml:"server" validate:"required"`
}
type Server interface { type Server interface {
Serve(ctx context.Context) error Serve(ctx context.Context) error
RegisterController(c ...Controller) RegisterController(c ...Controller)
... ...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment