diff options
| author | Mikkel Thestrup <mikkel_thestrup@mithe.dk> | 2025-12-06 19:21:55 +0100 |
|---|---|---|
| committer | Mikkel Thestrup <mikkel_thestrup@mithe.dk> | 2025-12-06 19:21:55 +0100 |
| commit | b668f82b3bb834265c842bff96c252a513afa647 (patch) | |
| tree | 49286ab3ef556758a6620378e1aa01fd5de59f02 | |
| download | web-portfolio-b668f82b3bb834265c842bff96c252a513afa647.tar.gz web-portfolio-b668f82b3bb834265c842bff96c252a513afa647.zip | |
Initial commit
| -rw-r--r-- | cmd/main.go | 14 | ||||
| -rw-r--r-- | internal/server.go | 216 | ||||
| -rw-r--r-- | internal/template.go | 34 |
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) +} |