summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikkel Thestrup <mikkel_thestrup@mithe.dk>2025-12-06 19:21:55 +0100
committerMikkel Thestrup <mikkel_thestrup@mithe.dk>2025-12-06 19:21:55 +0100
commitb668f82b3bb834265c842bff96c252a513afa647 (patch)
tree49286ab3ef556758a6620378e1aa01fd5de59f02
downloadweb-portfolio-b668f82b3bb834265c842bff96c252a513afa647.tar.gz
web-portfolio-b668f82b3bb834265c842bff96c252a513afa647.zip
Initial commit
-rw-r--r--cmd/main.go14
-rw-r--r--internal/server.go216
-rw-r--r--internal/template.go34
3 files changed, 264 insertions, 0 deletions
diff --git a/cmd/main.go b/cmd/main.go
new file mode 100644
index 0000000..cd8d4fe
--- /dev/null
+++ b/cmd/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "log"
+
+ "git.mithe.dk/web-portfolio/internal"
+)
+
+func main() {
+ server := internal.NewSever()
+ if err := server.Start("localhost:42069"); err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/internal/server.go b/internal/server.go
new file mode 100644
index 0000000..c2a60aa
--- /dev/null
+++ b/internal/server.go
@@ -0,0 +1,216 @@
+package internal
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "slices"
+
+ "github.com/labstack/echo/v4"
+ "github.com/labstack/echo/v4/middleware"
+)
+
+var PAGES = []string {
+ "home",
+ "projects",
+ "resume",
+ "contact",
+}
+
+var SECTIONS = map[string][]string {
+ "home": {"intro", "about"},
+ "projects": {"intro", "kal"},
+ "resume": {"intro", "BSc in Computer Science"},
+ "contact": {"contact"},
+}
+
+var tmpl *template.Template
+
+type Server struct {
+ echo *echo.Echo
+}
+
+func init() {
+ var err error
+
+ // Create the root template
+ tmpl = template.New("")
+
+ // Load base/layout templates
+ tmpl, err = tmpl.ParseGlob("views/*.html")
+ if err != nil {
+ log.Fatalf("Error parsing layout templates: %v", err)
+ }
+
+ // Load page section templates (e.g., pages/home/intro.html)
+ tmpl, err = tmpl.ParseGlob("views/pages/*/*.html")
+ if err != nil {
+ log.Fatalf("Error parsing page templates: %v", err)
+ }
+
+ log.Println("Templates loaded successfully")
+}
+
+func NewSever() *Server {
+ e := echo.New()
+ e.Use(middleware.Logger())
+ e.Use(middleware.Recover())
+ e.Renderer = &TemplateRenderer{Template: tmpl}
+ e.Static("/css", "css")
+ e.Static("/js", "js")
+ s := &Server{echo: e}
+ s.setupRoutes()
+ return s
+}
+
+func (s *Server) setupRoutes() {
+ e := s.echo
+
+ e.GET("/", func(c echo.Context) error {
+ page := PAGES[0]
+ section := SECTIONS[page][0]
+
+ content, err := renderContent(page, section)
+ if err != nil {
+ fmt.Printf("Error rendering content for %s/%s: %v\n", page, section, err)
+ return c.String(
+ http.StatusInternalServerError,
+ "Error loading content")
+ }
+
+ data := PageData{
+ Title: page,
+ Content: template.HTML(content),
+ Page: page,
+ Pages: PAGES,
+ Sections: SECTIONS[page],
+ }
+
+ return c.Render(http.StatusOK, "layout.html", data)
+ })
+
+ e.GET("/:page", func(c echo.Context) error {
+ page := c.Param("page")
+ if !slices.Contains(PAGES, page) {
+ return c.String(http.StatusNotFound, "Page not found")
+ }
+
+ section := SECTIONS[page][0]
+ content, err := renderContent(page, section)
+ if err != nil {
+ fmt.Printf("Error rendering content for %s/%s: %v\n", page, section, err)
+ return c.String(
+ http.StatusInternalServerError,
+ "Error loading content")
+ }
+
+ data := PageData{
+ Content: template.HTML(content),
+ Page: page,
+ Pages: PAGES,
+ Sections: SECTIONS[page],
+ }
+
+ return c.Render(http.StatusOK, "layout.html", data)
+ })
+
+ // Full page load with section (HTML page with layout)
+ e.GET("/:page/:section", func(c echo.Context) error {
+ page := c.Param("page")
+ if !slices.Contains(PAGES, page) {
+ return c.String(http.StatusNotFound, "Page not found")
+ }
+
+ section := c.Param("section")
+
+ sections := SECTIONS[page]
+ if !slices.Contains(sections, section) {
+ return c.String(http.StatusNotFound, "Section not found")
+ }
+
+ content, err := renderContent(page, section)
+ if err != nil {
+ fmt.Printf("Error rendering content for %s/%s: %v\n", page, section, err)
+ return c.String(
+ http.StatusInternalServerError,
+ "Error loading content")
+ }
+
+ data := PageData{
+ Title: fmt.Sprintf("%s - %s", page, section),
+ Content: template.HTML(content),
+ Page: page,
+ Pages: PAGES,
+ Sections: sections,
+ }
+
+ return c.Render(http.StatusOK, "layout.html", data)
+ })
+
+ // API endpoint - return only content (for HTMX)
+ e.GET("/api/:page", func(c echo.Context) error {
+ page := c.Param("page")
+ if !slices.Contains(PAGES, page) {
+ return c.String(http.StatusNotFound, "Page not found")
+ }
+
+ section := SECTIONS[page][0]
+ content, err := renderContent(page, section)
+ if err != nil {
+ fmt.Printf("Error rendering content for %s/%s: %v\n", page, section, err)
+ return c.String(
+ http.StatusInternalServerError,
+ "Error loading content")
+ }
+
+ data := SectionData {
+ Content: template.HTML(content),
+ Page: page,
+ Sections: SECTIONS[page],
+ }
+ return c.Render(http.StatusOK, "page.html", data)
+ })
+
+ // API endpoint with section - return only content (for HTMX)
+ e.GET("/api/:page/:section", func(c echo.Context) error {
+ page := c.Param("page")
+ if !slices.Contains(PAGES, page) {
+ return c.String(http.StatusNotFound, "Page not found")
+ }
+
+ section := c.Param("section")
+ if !slices.Contains(SECTIONS[page], section) {
+ return c.String(http.StatusNotFound, "Section not found")
+ }
+
+ content, err := renderContent(page, section)
+ if err != nil {
+ fmt.Printf("Error rendering content for %s/%s: %v\n", page, section, err)
+ return c.String(
+ http.StatusInternalServerError,
+ "Error loading content")
+ }
+
+ return c.HTML(http.StatusOK, content)
+ })
+}
+
+// Helper function to render template content
+func renderContent(page, section string) (string, error) {
+ // Template name is "{page}_{section}" (e.g., "home_intro", "home_about")
+ templateName := fmt.Sprintf("%s_%s", page, section)
+
+ var buf bytes.Buffer
+ err := tmpl.ExecuteTemplate(&buf, templateName, nil)
+ if err != nil {
+ return "", err
+ }
+
+ return buf.String(), nil
+}
+
+func (s *Server) Start(addr string) error {
+ return s.echo.Start(addr)
+}
diff --git a/internal/template.go b/internal/template.go
new file mode 100644
index 0000000..3d2fe4b
--- /dev/null
+++ b/internal/template.go
@@ -0,0 +1,34 @@
+package internal
+
+import (
+ "html/template"
+ "io"
+
+ "github.com/labstack/echo/v4"
+)
+
+type PageData struct {
+ Content template.HTML
+ Page string
+ Pages []string
+ Sections []string
+}
+
+type SectionData struct {
+ Content template.HTML
+ Page string
+ Sections []string
+}
+
+type TemplateRenderer struct {
+ Template *template.Template
+}
+
+func (t *TemplateRenderer) Render(
+ w io.Writer,
+ name string,
+ data interface{},
+ c echo.Context,
+) error {
+ return t.Template.ExecuteTemplate(w, name, data)
+}