ZDDC/zddc/internal/handler/profile_assets.go
2026-06-11 13:32:31 -05:00

56 lines
1.8 KiB
Go

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 <fsRoot>/.profile.css exists.
// The profile template uses this to decide whether to inject the
// <link> 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 <link> to
// /custom.css when an operator has placed one at root.
const profileAssetsPathPrefix = ProfilePathPrefix + "/assets"
// serveProfileAssets handles GET /.profile/assets/<file>. V1 only
// ships `custom.css` (passthrough of <root>/.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)
}
}