summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorherkulessi <git@herkulessi.de>2025-08-20 20:13:09 +0200
committerherkulessi <git@herkulessi.de>2025-08-20 20:13:09 +0200
commit32e7fe5f94eee7e08f048d1ead1e7e82f29c46d6 (patch)
treebb93bfa26873ad55b51b12398e837bd2ea880b50
Initial commit of libmkwebpage
-rw-r--r--go.mod3
-rw-r--r--main.go101
-rw-r--r--static.go14
-rw-r--r--templates.go70
4 files changed, 188 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..6d7a986
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module herkulessi.de/git/libmkwebpage
+
+go 1.24.6
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..1032943
--- /dev/null
+++ b/main.go
@@ -0,0 +1,101 @@
+package libmkwebpage
+
+import (
+ "fmt"
+ "net/http"
+ "net/http/cgi"
+ "os"
+)
+
+type Webpage struct {
+ muxStatic *http.ServeMux
+ muxDynamic *http.ServeMux
+ muxInternal *http.ServeMux
+ templates templates
+ staticFilePrefix string
+}
+
+type handler struct {
+ w *Webpage
+}
+
+func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if _, pattern := h.w.muxInternal.Handler(r); pattern != "" {
+ h.w.muxInternal.ServeHTTP(w, r)
+ return
+ }
+ h.w.getHandler().ServeHTTP(w, r)
+}
+
+func New() *Webpage {
+ webpage := Webpage{muxStatic: http.NewServeMux(), muxDynamic: http.NewServeMux(), muxInternal: http.NewServeMux()}
+ webpage.muxInternal.HandleFunc("/.libmkpage/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "/.libmkpage/") })
+ return &webpage
+}
+
+// See http.HandleFunc
+func (w *Webpage) Handle(pattern string, handler func(http.ResponseWriter, *http.Request)) *Webpage {
+ return w.HandleDynamic(pattern, handler).HandleStatic(pattern, handler)
+}
+
+// See http.HandleFunc, only executed in dynamic requests (i.e. in a dev setup)
+func (w *Webpage) HandleDynamic(pattern string, handler func(http.ResponseWriter, *http.Request)) *Webpage {
+ w.muxDynamic.HandleFunc(pattern, handler)
+ return w
+}
+
+// See http.HandleFunc, only executed in static requests (i.e. in a prod setup)
+func (w *Webpage) HandleStatic(pattern string, handler func(http.ResponseWriter, *http.Request)) *Webpage {
+ w.muxStatic.HandleFunc(pattern, handler)
+ return w
+}
+
+// See http.Handle
+func (w *Webpage) Handler(pattern string, handler http.Handler) *Webpage {
+ return w.HandlerDynamic(pattern, handler).HandlerStatic(pattern, handler)
+}
+
+// See http.Handle, only executed in dynamic requests (i.e. in a dev setup)
+func (w *Webpage) HandlerDynamic(pattern string, handler http.Handler) *Webpage {
+ w.muxDynamic.Handle(pattern, handler)
+ return w
+}
+
+// See http.Handle, only executed in static requests (i.e. in a prod setup)
+func (w *Webpage) HandlerStatic(pattern string, handler http.Handler) *Webpage {
+ w.muxStatic.Handle(pattern, handler)
+ return w
+}
+
+// Returns the proper Mux for the current Request based on the request type without the magic internal mux
+func (w *Webpage) getHandler() http.Handler {
+ if w.isDynamic() {
+ return w.muxDynamic
+ } else {
+ return w.muxStatic
+ }
+}
+
+// Returns a http.Handler capable of hosting the application. Useful for combining more than one webpage in one binary
+func (w *Webpage) GetHandler() http.Handler {
+ return handler{w}
+}
+
+func (w *Webpage) isDynamic() bool {
+ return os.Getenv("PATH_INFO") == ""
+}
+func (w *Webpage) isStatic() bool {
+ return !w.isDynamic()
+}
+
+func (w *Webpage) Serve() {
+ if w.isDynamic() {
+ fmt.Println("Listening on [::]:8080")
+ panic(http.ListenAndServe(":8080", w.GetHandler()))
+ }
+ err := cgi.Serve(w.GetHandler())
+ if err != nil {
+ panic(err)
+ }
+ os.Exit(0)
+}
diff --git a/static.go b/static.go
new file mode 100644
index 0000000..04c0fe6
--- /dev/null
+++ b/static.go
@@ -0,0 +1,14 @@
+package libmkwebpage
+
+import (
+ "embed"
+ "io/fs"
+ "net/http"
+)
+
+func (w *Webpage) NewStaticFiles(pattern string, static embed.FS, dynamic fs.FS) *Webpage {
+ w.HandlerDynamic("GET "+pattern, http.FileServerFS(dynamic))
+ w.HandlerStatic("GET "+pattern, http.FileServerFS(static))
+ w.staticFilePrefix = pattern
+ return w
+}
diff --git a/templates.go b/templates.go
new file mode 100644
index 0000000..00c1b31
--- /dev/null
+++ b/templates.go
@@ -0,0 +1,70 @@
+package libmkwebpage
+
+import (
+ "bytes"
+ "embed"
+ "fmt"
+ "html/template"
+ "io/fs"
+ "net/http"
+)
+
+type templates struct {
+ staticRoot embed.FS
+ dynamicRoot fs.FS
+ errorTemplate string
+ bindings template.FuncMap
+}
+
+func (w *Webpage) getTemplateRoot() fs.FS {
+ if w.isDynamic() {
+ return w.templates.dynamicRoot
+ }
+ return w.templates.staticRoot
+}
+
+func (w *Webpage) NewTemplates(static embed.FS, dynamic fs.FS) *Webpage {
+ w.templates = templates{staticRoot: static, dynamicRoot: dynamic, bindings: template.FuncMap{}}
+ return w
+}
+func (w *Webpage) SetErrorTemplate(path string) {
+ w.templates.errorTemplate = path
+}
+func (w *Webpage) Error(writer http.ResponseWriter, code int, err error) {
+ writer.WriteHeader(code)
+ if w.templates.errorTemplate == "" {
+ fmt.Fprintln(writer, err)
+ }
+}
+func (w *Webpage) InternalServerError(writer http.ResponseWriter, err error) {
+ w.Error(writer, http.StatusInternalServerError, err)
+}
+func (w *Webpage) BadRequest(writer http.ResponseWriter, err error) {
+ w.Error(writer, http.StatusInternalServerError, err)
+}
+func (w *Webpage) BindTemplateFunc(name string, fun any) *Webpage {
+ w.templates.bindings[name] = fun
+ return w
+}
+
+func (w *Webpage) RenderTemplate(writer http.ResponseWriter, name string, data any) {
+ writer.Header().Set("Content-Type", "text/html; charset=utf-8")
+ tmpl, err := fs.ReadFile(w.getTemplateRoot(), "templates/"+name+".tmpl")
+ if err != nil {
+ panic(err)
+ }
+ if err := template.Must(template.New("templates/"+name+".tmpl").Funcs(w.templates.bindings).Parse(string(tmpl))).Execute(writer, data); err != nil {
+ panic(err)
+ }
+}
+func (w *Webpage) RenderTemplateToString(name string, data any) string {
+ buf := bytes.NewBuffer(nil)
+ tmpl, err := fs.ReadFile(w.getTemplateRoot(), "templates/"+name+".tmpl")
+ if err != nil {
+ panic(err)
+ }
+ if err := template.Must(template.New("templates/"+name+".tmpl").Funcs(w.templates.bindings).Parse(string(tmpl))).Execute(buf, data); err != nil {
+ panic(err)
+ }
+ return buf.String()
+}