package templating import ( "farga/config" "farga/db/types" "farga/lib/mime" "html/template" "io/fs" "log" "maps" "net/http" "path/filepath" "strings" "farga/lib" "embed" "github.com/gorilla/csrf" ) //go:embed views var templatesFS embed.FS var FuncMap = template.FuncMap{ "GetMime": mime.Type, "IsImage": mime.IsImage, "IsVideo": mime.IsVideo, "IsText": mime.IsText, "Src": mime.Src, "Str": mime.ToStr, "RepoRoot": lib.RepoRoot, "SizeOf": lib.Size, "SizeLen": lib.SizeLen, "IsRangeable": lib.IsRangeable, "TruncateStr": lib.TruncateString, "SplitLines": lib.SplitLines, } func findAndParseTemplates(fsys fs.FS, root string, funcMap template.FuncMap) (*template.Template, error) { rootTmpl := template.New("").Funcs(funcMap) err := fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() || !strings.HasSuffix(path, ".html") { return nil } b, err := fs.ReadFile(fsys, path) if err != nil { return err } name := strings.TrimPrefix(path, root+"/") _, err = rootTmpl.New(name).Parse(string(b)) return err }) if err != nil { return nil, err } return rootTmpl, nil } var templates *template.Template func init() { var err error templates, err = findAndParseTemplates(templatesFS, ".", FuncMap) if err != nil { log.Fatal(err) } } // Renders a template inside a given layout. You can also pass it any data. func Render(w http.ResponseWriter, r *http.Request, layout, page string, data ...any) { var td = make(map[string]any) if len(data) > 0 { if m, ok := data[0].(map[string]any); ok { maps.Copy(td, m) } else { // if data was something else, store it under "data" td["data"] = data[0] } } tmpl, err := templates.Clone() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } tmpl, err = tmpl.ParseFiles(filepath.Join("templating", "views", page+".html")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if v := r.Context().Value(lib.ErrorKey); v != nil { td["error"] = v } if v := r.Context().Value(lib.MessageKey); v != nil { if s, ok := v.(string); ok && s != "" { td["message"] = s } } user, ok := r.Context().Value(lib.UserKey).(*types.User) if ok { td["user"] = user td["loggedIn"] = true } else { td["loggedIn"] = false // no user = not logged in. } td["sshDomain"] = config.C.Domain td["sshPort"] = config.C.SSHPort td["csrfField"] = csrf.TemplateField(r) // append {{ .csrfField }} to every form. err = tmpl.ExecuteTemplate(w, layout, td) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }