summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikkel Thestrup <mikkel_thestrup@mithe.dk>2025-12-06 19:29:47 +0100
committerMikkel Thestrup <mikkel_thestrup@mithe.dk>2025-12-06 19:29:47 +0100
commit21e9956d25720998c3f59e052dfa84fb13d513bf (patch)
tree37ce4c877f46248e1ccf3cae495e88476a504561
parentb668f82b3bb834265c842bff96c252a513afa647 (diff)
downloadweb-portfolio-21e9956d25720998c3f59e052dfa84fb13d513bf.tar.gz
web-portfolio-21e9956d25720998c3f59e052dfa84fb13d513bf.zip
Compartmentalize the code
-rw-r--r--internal/handlers.go98
-rw-r--r--internal/models.go32
-rw-r--r--internal/server.go201
-rw-r--r--internal/template.go47
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
}