package cabana import ( "bytes" "net/http" "path" "time" "git.golem15.com/golem15/summercms/modules/boardwalk" ) // pluginAsset serves GET {prefix}/assets/{vendor}/{plugin}/{file...}: a // controller's declared JS or CSS file, looked up by exact key in the map // built at boot, so a plugin's embedded tree is never exposed wholesale and // traversal matches no key. A miss falls through to the SPA handler, which // serves the embedded dist assets and answers any other name with its 404. // // Plugin files are not content-hashed, so they are revalidated on every use // (no-cache plus a sha256 ETag); the schema URLs carry a ?v= hash instead of // the long-lived caching the SPA's hashed dist files get. func (s *service) pluginAsset(w http.ResponseWriter, r *http.Request) { var asset *pluginAsset if s != nil && s.reg != nil { asset = s.reg.assets[r.PathValue("vendor")+"/"+r.PathValue("plugin")+"/"+r.PathValue("file")] } if asset == nil { s.serveSPA(w, r) return } h := w.Header() boardwalk.SetSecurityHeaders(h) h.Set("Cross-Origin-Resource-Policy", "same-origin") h.Set("Content-Type", asset.contentType) h.Set("Cache-Control", "no-cache") h.Set("ETag", asset.etag) http.ServeContent(w, r, path.Base(asset.key), time.Time{}, bytes.NewReader(asset.body)) } // controllerAssets are the same-origin URLs of a controller's plugin files, // each with a ?v= content hash so a rebuilt binary never serves stale JS. func (s *service) controllerAssets(cc *CompiledController) ControllerAssets { out := ControllerAssets{Scripts: []string{}, Styles: []string{}} if cc == nil { return out } base := s.adminPrefix() + "/assets/" for _, file := range cc.scripts { out.Scripts = append(out.Scripts, base+file.key+"?v="+file.version) } for _, file := range cc.styles { out.Styles = append(out.Styles, base+file.key+"?v="+file.version) } return out }