diff options
| author | Mikkel Thestrup <mikkel_thestrup@mithe.dk> | 2025-12-06 19:29:47 +0100 |
|---|---|---|
| committer | Mikkel Thestrup <mikkel_thestrup@mithe.dk> | 2025-12-06 19:29:47 +0100 |
| commit | 21e9956d25720998c3f59e052dfa84fb13d513bf (patch) | |
| tree | 37ce4c877f46248e1ccf3cae495e88476a504561 | |
| parent | b668f82b3bb834265c842bff96c252a513afa647 (diff) | |
| download | web-portfolio-21e9956d25720998c3f59e052dfa84fb13d513bf.tar.gz web-portfolio-21e9956d25720998c3f59e052dfa84fb13d513bf.zip | |
Compartmentalize the code
| -rw-r--r-- | internal/handlers.go | 98 | ||||
| -rw-r--r-- | internal/models.go | 32 | ||||
| -rw-r--r-- | internal/server.go | 201 | ||||
| -rw-r--r-- | internal/template.go | 47 |
4 files changed, 175 insertions, 203 deletions
diff --git a/internal/handlers.go b/internal/handlers.go new file mode 100644 index 0000000..5d66be8 --- /dev/null +++ b/internal/handlers.go @@ -0,0 +1,98 @@ +package internal + +import ( + "log" + "net/http" + "slices" + + "github.com/labstack/echo/v4" +) + +func (s *Server) handleHome(c echo.Context) error { + return s.renderFullPage(c, PageHome, PageSections[PageHome][0]) +} + +func (s *Server) handlePage(c echo.Context) error { + page := c.Param("page") + if !validPage(page) { + return c.String(http.StatusNotFound, "Page not found") + } + return s.renderFullPage(c, page, PageSections[page][0]) +} + +func (s *Server) handlePageSection(c echo.Context) error { + page := c.Param("page") + section := c.Param("section") + + if !validPage(page) { + return c.String(http.StatusNotFound, "Page not found") + } + + if !validSection(page, section) { + return c.String(http.StatusNotFound, "Section not found") + } + + return s.renderFullPage(c, page, section) +} + +func (s *Server) handleHTMXPage(c echo.Context) error { + page := c.Param("page") + if !validPage(page) { + return c.String(http.StatusNotFound, "Page not found") + } + return s.renderContent(c, page, PageSections[page][0]) +} + +func (s *Server) handleHTMXSection(c echo.Context) error { + page := c.Param("page") + section := c.Param("section") + + if !validPage(page) { + return c.String(http.StatusNotFound, "Page not found") + } + + if !validSection(page, section) { + return c.String(http.StatusNotFound, "Section not found") + } + + return s.renderContent(c, page, section) +} + +func (s *Server) renderFullPage(c echo.Context, page, section string) error { + content, err := s.tmpl.GetContent(page, section) + if err != nil { + log.Printf("Error rendering content for %s/%s: %v", page, section, err) + return c.String(http.StatusInternalServerError, "Error loading content") + } + + data := PageData{ + Page: page, + Pages: ValidPages, + Sections: PageSections[page], + Content: content, + } + + return c.Render(http.StatusOK, "layout.html", data) +} + +func (s *Server) renderContent(c echo.Context, page, section string) error { + content, err := s.tmpl.GetContent(page, section) + if err != nil { + log.Printf("Error rendering content for %s/%s: %v", page, section, err) + return c.String(http.StatusInternalServerError, "Error loading content") + } + + return c.HTML(http.StatusOK, string(content)) +} + +func validPage(page string) bool { + return slices.Contains(ValidPages, page) +} + +func validSection(page, section string) bool { + sections, exists := PageSections[page] + if !exists { + return false + } + return slices.Contains(sections, section) +} diff --git a/internal/models.go b/internal/models.go new file mode 100644 index 0000000..1fb0774 --- /dev/null +++ b/internal/models.go @@ -0,0 +1,32 @@ +package internal + +import "html/template" + +const ( + PageHome = "home" + PageProjects = "projects" + PageResume = "resume" + PageContact = "contact" +) + +var ValidPages = []string{PageHome, PageProjects, PageResume, PageContact} + +var PageSections = map[string][]string{ + PageHome: {"intro", "about"}, + PageProjects: {"intro", "kal"}, + PageResume: {"intro", "BSc in Computer Science"}, + PageContact: {"contact"}, +} + +type PageData struct { + Page string + Pages []string + Sections []string + Content template.HTML +} + +type SectionData struct { + Page string + Sections []string + Content template.HTML +} diff --git a/internal/server.go b/internal/server.go index c2a60aa..0ea3717 100644 --- a/internal/server.go +++ b/internal/server.go @@ -1,214 +1,45 @@ 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 + tmpl *TemplateEngine } -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") +func NewServer() *Server { + tmpl, err := NewTemplateEngine() if err != nil { - log.Fatalf("Error parsing page templates: %v", err) + log.Fatalf("Error loading 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.Renderer = tmpl e.Static("/css", "css") e.Static("/js", "js") - s := &Server{echo: e} + + s := &Server{ + echo: e, + tmpl: tmpl, + } + 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 - } + s.echo.GET("/", s.handleHome) + s.echo.GET("/:page", s.handlePage) + s.echo.GET("/:page/:section", s.handlePageSection) - return buf.String(), nil + s.echo.GET("/api/:page", s.handleHTMXPage) + s.echo.GET("/api/:page/:section", s.handleHTMXSection) } func (s *Server) Start(addr string) error { diff --git a/internal/template.go b/internal/template.go index 3d2fe4b..118f7a2 100644 --- a/internal/template.go +++ b/internal/template.go @@ -1,34 +1,45 @@ package internal import ( + "bytes" + "fmt" "html/template" "io" + "log" "github.com/labstack/echo/v4" ) -type PageData struct { - Content template.HTML - Page string - Pages []string - Sections []string +type TemplateEngine struct { + tmpl *template.Template } -type SectionData struct { - Content template.HTML - Page string - Sections []string +func NewTemplateEngine() (*TemplateEngine, error) { + tmpl := template.New("") + + if _, err := tmpl.ParseGlob("views/*.html"); err != nil { + return nil, fmt.Errorf("parsing layout templates: %w", err) + } + + if _, err := tmpl.ParseGlob("views/pages/*/*.html"); err != nil { + return nil, fmt.Errorf("parsing page templates: %w", err) + } + + log.Println("Templates loaded successfully") + return &TemplateEngine{tmpl: tmpl}, nil } -type TemplateRenderer struct { - Template *template.Template +func (te *TemplateEngine) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + return te.tmpl.ExecuteTemplate(w, name, data) } -func (t *TemplateRenderer) Render( - w io.Writer, - name string, - data interface{}, - c echo.Context, -) error { - return t.Template.ExecuteTemplate(w, name, data) +func (te *TemplateEngine) GetContent(page, section string) (template.HTML, error) { + templateName := fmt.Sprintf("%s_%s", page, section) + + var buf bytes.Buffer + if err := te.tmpl.ExecuteTemplate(&buf, templateName, nil); err != nil { + return "", err + } + + return template.HTML(buf.String()), nil } |