fix: embed HTML templates into binary via go:embed
Switched from runtime file-based template loading (os.Stat + ParseGlob) to compile-time embedding (embed.FS + ParseFS). Templates are now bundled into the binary — no need to COPY them into Docker image. This fixes the fallback response issue where h.loaded was always false because templates weren't present in the container filesystem.
This commit is contained in:
@@ -2,16 +2,18 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/config"
|
||||
"gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/database"
|
||||
)
|
||||
|
||||
//go:embed html/*.html
|
||||
var templateFS embed.FS
|
||||
|
||||
// pageData is passed to all templates.
|
||||
type pageData struct {
|
||||
Title string
|
||||
@@ -24,10 +26,9 @@ 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.
|
||||
// NewHandler creates a new templates handler and parses embedded templates.
|
||||
func NewHandler(db *database.DB, cfg *config.Config) *Handler {
|
||||
h := &Handler{db: db, cfg: cfg}
|
||||
h.parseTemplates()
|
||||
@@ -49,79 +50,43 @@ func (h *Handler) index(w http.ResponseWriter, r *http.Request) {
|
||||
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.
|
||||
// render executes the base layout template which calls {{template "content" .}}
|
||||
// to inject the named page content.
|
||||
func (h *Handler) render(w http.ResponseWriter, page string, data pageData) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if h.tmpl == nil {
|
||||
http.Error(w, "Templates not loaded", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
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)
|
||||
tmpl, err := template.New("").Funcs(template.FuncMap{}).ParseFS(templateFS, "html/*.html")
|
||||
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)
|
||||
log.Printf("Template parse error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Println("No HTML templates found; using placeholder responses")
|
||||
h.tmpl = tmpl
|
||||
log.Println("Loaded embedded HTML templates")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user