feat: implement email validation, CI/CD pipeline, migration history, and web templates
Email validation: - Replace @/. check with net/mail.ParseAddress on register - Add size limit check (max 254 chars, RFC 5321) CI/CD Pipeline: - Add .gitea/workflows/ci.yml (lint → test → build → docker push) - Registry: gitea.mrixs.me/Mrixs/MrixsCraft-server - Push only on main branch Database: - Add migrations/002_migration_history.sql (tracking applied migrations) - Add migrations/README.md (manual apply instructions) Web Templates: - Add base.html with Minecraft-themed layout (dark + green accent) - Add index.html, login.html, register.html with POST forms - Rewrite templates.go for data-driven rendering with pageData struct - Fallback placeholder preserved when templates dir missing
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
// package templates handles Go html/template rendering for the site and admin panel.
|
||||
//
|
||||
// This is a placeholder implementation. Actual templates will be added
|
||||
// when the web UI is designed.
|
||||
// package templates handles Go html/template rendering for the website.
|
||||
package templates
|
||||
|
||||
import (
|
||||
@@ -15,14 +12,20 @@ import (
|
||||
"gitea.mrixs.me/Mrixs/MrixsCraft-server/internal/database"
|
||||
)
|
||||
|
||||
// Handler serves template-rendered pages.
|
||||
type Handler struct {
|
||||
db *database.DB
|
||||
cfg *config.Config
|
||||
layout *template.Template
|
||||
// pageData is passed to all templates.
|
||||
type pageData struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
// NewHandler creates a new templates handler and parses embedded/ondisk templates.
|
||||
// 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()
|
||||
@@ -43,36 +46,47 @@ func (h *Handler) index(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if h.layout == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("<h1>MrixsCraft</h1>"))
|
||||
if !h.loaded {
|
||||
fallback(w, "MrixsCraft")
|
||||
return
|
||||
}
|
||||
h.layout.ExecuteTemplate(w, "index", nil)
|
||||
h.render(w, "index.html", pageData{Title: "Главная"})
|
||||
}
|
||||
|
||||
func (h *Handler) loginPage(w http.ResponseWriter, r *http.Request) {
|
||||
if h.layout == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("<h1>Login</h1>"))
|
||||
if !h.loaded {
|
||||
fallback(w, "Login")
|
||||
return
|
||||
}
|
||||
h.layout.ExecuteTemplate(w, "login", nil)
|
||||
h.render(w, "login.html", pageData{Title: "Вход"})
|
||||
}
|
||||
|
||||
func (h *Handler) registerPage(w http.ResponseWriter, r *http.Request) {
|
||||
if h.layout == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("<h1>Register</h1>"))
|
||||
if !h.loaded {
|
||||
fallback(w, "Register")
|
||||
return
|
||||
}
|
||||
h.layout.ExecuteTemplate(w, "register", nil)
|
||||
h.render(w, "register.html", pageData{Title: "Регистрация"})
|
||||
}
|
||||
|
||||
// render executes the named template with data, writing to w.
|
||||
func (h *Handler) render(w http.ResponseWriter, name string, data pageData) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := h.tmpl.ExecuteTemplate(w, name, data); err != nil {
|
||||
log.Printf("Template error (%s): %v", name, err)
|
||||
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() {
|
||||
// Look for templates in common locations.
|
||||
dirs := []string{
|
||||
filepath.Join("internal", "templates", "html"),
|
||||
"templates",
|
||||
@@ -81,16 +95,18 @@ func (h *Handler) parseTemplates() {
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
continue
|
||||
}
|
||||
tmpl, err := template.ParseGlob(filepath.Join(dir, "*.html"))
|
||||
if err == nil && tmpl != nil {
|
||||
h.layout = tmpl
|
||||
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
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Template parse error in %s: %v", dir, err)
|
||||
}
|
||||
}
|
||||
// No templates found — handler will use placeholder responses.
|
||||
log.Println("No HTML templates found; using placeholder responses")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user