package handlers import ( "html/template" "io/fs" "net/http" "path/filepath" "strings" "github.com/jackc/pgx/v5/pgxpool" "vpn-panel/internal/config" "vpn-panel/internal/store" "vpn-panel/web" ) type Handler struct { cfg *config.Config pool *pgxpool.Pool users *store.UserStore tmpl map[string]*template.Template secret string domain string } func New(cfg *config.Config, pool *pgxpool.Pool) (*Handler, error) { tmpl, err := loadTemplates() if err != nil { return nil, err } return &Handler{ cfg: cfg, pool: pool, users: store.NewUserStore(pool), tmpl: tmpl, secret: cfg.SecretKey, domain: cfg.AppDomain, }, nil } func loadTemplates() (map[string]*template.Template, error) { funcs := template.FuncMap{ "eq": func(a, b any) bool { return a == b }, } out := make(map[string]*template.Template) err := fs.WalkDir(web.Templates, "templates", func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() || !strings.HasSuffix(path, ".html") { return err } name := strings.TrimSuffix(filepath.Base(path), ".html") content, err := web.Templates.ReadFile(path) if err != nil { return err } base, err := web.Templates.ReadFile("templates/layout.html") if err != nil { return err } t, err := template.New("layout").Funcs(funcs).Parse(string(base)) if err != nil { return err } if _, err := t.Parse(string(content)); err != nil { return err } out[name] = t return nil }) return out, err } func (h *Handler) render(w http.ResponseWriter, name string, data any) { t := h.tmpl[name] if t == nil { http.Error(w, "template not found", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := t.ExecuteTemplate(w, "layout", data); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func (h *Handler) pageData(w http.ResponseWriter, r *http.Request, title string, extra map[string]any) map[string]any { hasAdmin, _ := h.users.HasAdmin(r.Context()) data := map[string]any{ "Title": title, "Domain": h.domain, "Year": "2026", "Flash": flashGet(w, r), "CanRegister": !hasAdmin, "HasAdmin": hasAdmin, } if sess := h.currentUser(r); sess != nil { data["User"] = sess } for k, v := range extra { data[k] = v } return data }