mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Plugin assets, external scripts and CSP overrides (#4260)
* Add assets for plugins * Move plugin javascript and css into separate endpoints * Allow loading external scripts * Add csp overrides * Only include enabled plugins * Move URLMap to utils * Use URLMap for assets * Add documentation
This commit is contained in:
107
internal/api/routes_plugin.go
Normal file
107
internal/api/routes_plugin.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
"github.com/stashapp/stash/pkg/plugin"
|
||||
)
|
||||
|
||||
type pluginRoutes struct {
|
||||
pluginCache *plugin.Cache
|
||||
}
|
||||
|
||||
func getPluginRoutes(pluginCache *plugin.Cache) chi.Router {
|
||||
return pluginRoutes{
|
||||
pluginCache: pluginCache,
|
||||
}.Routes()
|
||||
}
|
||||
|
||||
func (rs pluginRoutes) Routes() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
|
||||
r.Route("/{pluginId}", func(r chi.Router) {
|
||||
r.Use(rs.PluginCtx)
|
||||
r.Get("/assets/*", rs.Assets)
|
||||
r.Get("/javascript", rs.Javascript)
|
||||
r.Get("/css", rs.CSS)
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (rs pluginRoutes) Assets(w http.ResponseWriter, r *http.Request) {
|
||||
p := r.Context().Value(pluginKey).(*plugin.Plugin)
|
||||
|
||||
if !p.Enabled {
|
||||
http.Error(w, "plugin disabled", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
prefix := "/plugin/" + chi.URLParam(r, "pluginId") + "/assets"
|
||||
|
||||
r.URL.Path = strings.Replace(r.URL.Path, prefix, "", 1)
|
||||
|
||||
// http.FileServer redirects to / if the path ends with index.html
|
||||
r.URL.Path = strings.TrimSuffix(r.URL.Path, "/index.html")
|
||||
|
||||
pluginDir := filepath.Dir(p.ConfigPath)
|
||||
|
||||
// map the path to the applicable filesystem location
|
||||
var dir string
|
||||
r.URL.Path, dir = p.UI.Assets.GetFilesystemLocation(r.URL.Path)
|
||||
if dir == "" {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
dir = filepath.Join(pluginDir, filepath.FromSlash(dir))
|
||||
|
||||
// ensure directory is still within the plugin directory
|
||||
if !strings.HasPrefix(dir, pluginDir) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
http.FileServer(http.Dir(dir)).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (rs pluginRoutes) Javascript(w http.ResponseWriter, r *http.Request) {
|
||||
p := r.Context().Value(pluginKey).(*plugin.Plugin)
|
||||
|
||||
if !p.Enabled {
|
||||
http.Error(w, "plugin disabled", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/javascript")
|
||||
serveFiles(w, r, p.UI.Javascript)
|
||||
}
|
||||
|
||||
func (rs pluginRoutes) CSS(w http.ResponseWriter, r *http.Request) {
|
||||
p := r.Context().Value(pluginKey).(*plugin.Plugin)
|
||||
|
||||
if !p.Enabled {
|
||||
http.Error(w, "plugin disabled", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/css")
|
||||
serveFiles(w, r, p.UI.CSS)
|
||||
}
|
||||
|
||||
func (rs pluginRoutes) PluginCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
p := rs.pluginCache.GetPlugin(chi.URLParam(r, "pluginId"))
|
||||
if p == nil {
|
||||
http.Error(w, http.StatusText(404), 404)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), pluginKey, p)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user