package appview import ( "crypto/md5" "embed" "encoding/json" "fmt" "html/template" "io/fs" "net/http" "net/url" "strings" "time" "atcr.io/pkg/appview/licenses" ) // BrandingOverrides allows consumers to customize the AppView's public assets, // templates, CSS, and template functions. Pass nil for default atcr.io behavior. type BrandingOverrides struct { // PublicFS overlays public/ assets (favicons, CSS, images, etc.). // Files in this FS take priority over the embedded defaults. PublicFS fs.FS // TemplatesFS overlays templates/ (nav-brand.html, hero.html, etc.). // Go's template.ParseFS replaces {{ define "name" }} blocks when // called twice with the same name, so consumer templates naturally // override defaults. TemplatesFS fs.FS // ExtraCSS is injected as a ") }, } // Merge extra func map from overrides if overrides != nil && overrides.ExtraFuncMap != nil { for k, v := range overrides.ExtraFuncMap { funcMap[k] = v } } tmpl := template.New("").Funcs(funcMap) // Parse default templates tfs := resolveTemplatesFS(overrides) tmpl, err := tmpl.ParseFS(tfs, "templates/**/*.html") if err != nil { return nil, err } return tmpl, nil } // PublicHandler returns HTTP handler for static files. // Pass nil for default atcr.io behavior. func PublicHandler(overrides *BrandingOverrides) http.Handler { fsys := resolvePublicFS(overrides) sub, err := fs.Sub(fsys, "public") if err != nil { panic(err) } return http.FileServer(http.FS(sub)) } // PublicRootFiles returns list of root-level files in static directory (not subdirectories). // Pass nil for default atcr.io behavior. func PublicRootFiles(overrides *BrandingOverrides) ([]string, error) { fsys := resolvePublicFS(overrides) var entries []fs.DirEntry var err error if rdfs, ok := fsys.(fs.ReadDirFS); ok { entries, err = rdfs.ReadDir("public") } else { entries, err = fs.ReadDir(fsys, "public") } if err != nil { return nil, err } var files []string for _, entry := range entries { // Only include files, not directories if !entry.IsDir() { files = append(files, entry.Name()) } } return files, nil } // PublicSubdir returns an http.Handler for a subdirectory within public/. // Pass nil for default atcr.io behavior. func PublicSubdir(name string, overrides *BrandingOverrides) http.Handler { fsys := resolvePublicFS(overrides) sub, err := fs.Sub(fsys, "public/"+name) if err != nil { panic(err) } return http.FileServer(http.FS(sub)) }