feat: implement CAS module, middleware, utils, and templates

- CAS: GET /files/{hash} with immutable cache headers, launcher asset
  serving, hash validation, StoreFile/VerifyAndStore helpers
- Middleware: CORS, request logging, per-IP token bucket rate limiter
- Utils: SHA1Bytes, SHA256Bytes, SHA1File, Unzip with zip-slip protection
- Templates: placeholder handler with html/template discovery
- Wire CAS routes and middleware chain (Logging → CORS) in main.go
This commit is contained in:
2026-05-26 15:11:41 +03:00
parent 2f07fbf379
commit e4fea937aa
5 changed files with 517 additions and 6 deletions

View File

@@ -1,2 +1,96 @@
// 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
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"
)
// Handler serves template-rendered pages.
type Handler struct {
db *database.DB
cfg *config.Config
layout *template.Template
}
// NewHandler creates a new templates handler and parses embedded/ondisk 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)
}
// ── Page handlers ──────────────────────────────────────────────
func (h *Handler) index(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
if h.layout == nil {
w.WriteHeader(http.StatusOK)
w.Write([]byte("<h1>MrixsCraft</h1>"))
return
}
h.layout.ExecuteTemplate(w, "index", nil)
}
func (h *Handler) loginPage(w http.ResponseWriter, r *http.Request) {
if h.layout == nil {
w.WriteHeader(http.StatusOK)
w.Write([]byte("<h1>Login</h1>"))
return
}
h.layout.ExecuteTemplate(w, "login", nil)
}
func (h *Handler) registerPage(w http.ResponseWriter, r *http.Request) {
if h.layout == nil {
w.WriteHeader(http.StatusOK)
w.Write([]byte("<h1>Register</h1>"))
return
}
h.layout.ExecuteTemplate(w, "register", nil)
}
// ── Template parsing ───────────────────────────────────────────
func (h *Handler) parseTemplates() {
// Look for templates in common locations.
dirs := []string{
filepath.Join("internal", "templates", "html"),
"templates",
}
for _, dir := range dirs {
if _, err := os.Stat(dir); err != nil {
continue
}
tmpl, err := template.ParseGlob(filepath.Join(dir, "*.html"))
if err == nil && tmpl != nil {
h.layout = tmpl
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")
}