feat: implement email validation, CI/CD pipeline, migration history, and web templates
Some checks failed
CI / lint (push) Failing after 21s
CI / build (push) Has been skipped
CI / test (push) Has been skipped
CI / docker (push) Has been skipped

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:
2026-05-30 00:39:51 +03:00
parent e1cc999ea8
commit 7ad02cb1b2
10 changed files with 375 additions and 33 deletions

View File

@@ -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")
}