Files
MrixsCraft-server/internal/templates/templates.go
Vladimir Zagainov 394003d8c0
All checks were successful
CI / lint (push) Successful in 48s
CI / test (push) Successful in 18s
CI / build (push) Successful in 17s
CI / docker (push) Successful in 1m10s
fix: render base.html layout instead of bare page templates
ExecuteTemplate now renders base.html which calls {{template "content" .}},
so the full layout (header, footer, styles) wraps each page.
2026-06-04 06:29:46 +03:00

128 lines
3.5 KiB
Go

// package templates handles Go html/template rendering for the website.
package templates
import (
"html/template"
"log"
"net/http"
"os"
"path/filepath"
"gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/config"
"gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/database"
)
// pageData is passed to all templates.
type pageData struct {
Title string
Username string
UUID string
}
// Handler serves template-rendered pages.
type Handler struct {
db *database.DB
cfg *config.Config
tmpl *template.Template
loaded bool
}
// NewHandler creates a new templates handler and parses on-disk templates.
func NewHandler(db *database.DB, cfg *config.Config) *Handler {
h := &Handler{db: db, cfg: cfg}
h.parseTemplates()
return h
}
// RegisterRoutes mounts template-rendered pages.
func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /", h.index)
mux.HandleFunc("GET /login", h.loginPage)
mux.HandleFunc("GET /register", h.registerPage)
mux.HandleFunc("GET /profile", h.profilePage)
}
// ── Page handlers ──────────────────────────────────────────────
func (h *Handler) index(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
if !h.loaded {
fallback(w, "MrixsCraft")
return
}
h.render(w, "index.html", pageData{Title: "Главная"})
}
func (h *Handler) loginPage(w http.ResponseWriter, r *http.Request) {
if !h.loaded {
fallback(w, "Login")
return
}
h.render(w, "login.html", pageData{Title: "Вход"})
}
func (h *Handler) registerPage(w http.ResponseWriter, r *http.Request) {
if !h.loaded {
fallback(w, "Register")
return
}
h.render(w, "register.html", pageData{Title: "Регистрация"})
}
func (h *Handler) profilePage(w http.ResponseWriter, r *http.Request) {
if !h.loaded {
fallback(w, "Profile")
return
}
h.render(w, "profile.html", pageData{Title: "Профиль"})
}
// render executes the base layout template with the given content page.
// The base.html layout calls {{template "content" .}} which is defined in each page file.
func (h *Handler) render(w http.ResponseWriter, page string, data pageData) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := h.tmpl.ExecuteTemplate(w, "base.html", data); err != nil {
log.Printf("Template error (base.html → %s): %v", page, err)
// Fallback: render the page without layout
if err2 := h.tmpl.ExecuteTemplate(w, page, data); err2 != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
}
// fallback writes a minimal placeholder when templates are missing.
func fallback(w http.ResponseWriter, title string) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("<h1>" + title + "</h1>"))
}
// ── Template parsing ───────────────────────────────────────────
func (h *Handler) parseTemplates() {
dirs := []string{
filepath.Join("internal", "templates", "html"),
"templates",
}
for _, dir := range dirs {
if _, err := os.Stat(dir); err != nil {
continue
}
pattern := filepath.Join(dir, "*.html")
tmpl, err := template.New("").Funcs(template.FuncMap{}).ParseGlob(pattern)
if err != nil {
log.Printf("Template parse error in %s: %v", dir, err)
continue
}
if tmpl != nil {
h.tmpl = tmpl
h.loaded = true
log.Printf("Loaded templates from %s", dir)
return
}
}
log.Println("No HTML templates found; using placeholder responses")
}