package handler import ( "net/http" "os" "path/filepath" "strings" "codeberg.org/VARASYS/ZDDC/zddc/internal/config" ) // Custom CSS pipeline. Lets an operator drop `.profile.css` at the // deployment root and have it picked up automatically as styling for // the profile page. const profileCustomCSSName = ".profile.css" // hasCustomProfileCSS reports whether /.profile.css exists. // The profile template uses this to decide whether to inject the // tag. func hasCustomProfileCSS(fsRoot string) bool { _, err := os.Stat(filepath.Join(fsRoot, profileCustomCSSName)) return err == nil } // profileAssetsPathPrefix is the URL prefix for admin static assets. // The only consumer is the profile page, which emits a to // /custom.css when an operator has placed one at root. const profileAssetsPathPrefix = ProfilePathPrefix + "/assets" // serveProfileAssets handles GET /.profile/assets/. V1 only // ships `custom.css` (passthrough of /.profile.css when present); // other paths return 404 so we don't accidentally expose arbitrary // files. The caller (profilehandler.go) has already gated on admin // scope. func serveProfileAssets(cfg config.Config, w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { w.Header().Set("Allow", "GET") http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } rest := strings.TrimPrefix(r.URL.Path, profileAssetsPathPrefix+"/") switch rest { case "custom.css": path := filepath.Join(cfg.Root, profileCustomCSSName) if fi, err := os.Stat(path); err != nil || fi.IsDir() { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/css; charset=utf-8") w.Header().Set("Cache-Control", "no-cache") http.ServeFile(w, r, path) default: http.NotFound(w, r) } }