package plugins import ( "encoding/json" "path/filepath" "strings" "github.com/gosimple/slug" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util/errutil" ) type AppPlugin struct { FrontendPluginBase Routes []*AppPluginRoute `json:"routes"` AutoEnabled bool `json:"autoEnabled"` FoundChildPlugins []*PluginInclude `json:"-"` Pinned bool `json:"-"` Executable string `json:"executable,omitempty"` } // AppPluginRoute describes a plugin route that is defined in // the plugin.json file for a plugin. type AppPluginRoute struct { Path string `json:"path"` Method string `json:"method"` ReqRole models.RoleType `json:"reqRole"` URL string `json:"url"` URLParams []AppPluginRouteURLParam `json:"urlParams"` Headers []AppPluginRouteHeader `json:"headers"` TokenAuth *JwtTokenAuth `json:"tokenAuth"` JwtTokenAuth *JwtTokenAuth `json:"jwtTokenAuth"` Body json.RawMessage `json:"body"` } // AppPluginRouteHeader describes an HTTP header that is forwarded with // the proxied request for a plugin route type AppPluginRouteHeader struct { Name string `json:"name"` Content string `json:"content"` } // AppPluginRouteURLParam describes query string parameters for // a url in a plugin route type AppPluginRouteURLParam struct { Name string `json:"name"` Content string `json:"content"` } // JwtTokenAuth struct is both for normal Token Auth and JWT Token Auth with // an uploaded JWT file. type JwtTokenAuth struct { Url string `json:"url"` Scopes []string `json:"scopes"` Params map[string]string `json:"params"` } func (app *AppPlugin) Load(decoder *json.Decoder, base *PluginBase, backendPluginManager backendplugin.Manager) ( interface{}, error) { if err := decoder.Decode(app); err != nil { return nil, err } if app.Backend { cmd := ComposePluginStartCommand(app.Executable) fullpath := filepath.Join(base.PluginDir, cmd) factory := grpcplugin.NewBackendPlugin(app.Id, fullpath, grpcplugin.PluginStartFuncs{}) if err := backendPluginManager.Register(app.Id, factory); err != nil { return nil, errutil.Wrapf(err, "failed to register backend plugin") } } return app, nil } func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[string]*DataSourcePlugin, cfg *setting.Cfg) []*PluginStaticRoute { staticRoutes := app.InitFrontendPlugin(cfg) // check if we have child panels for _, panel := range panels { if strings.HasPrefix(panel.PluginDir, app.PluginDir) { panel.setPathsBasedOnApp(app, cfg) app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{ Name: panel.Name, Id: panel.Id, Type: panel.Type, }) } } // check if we have child datasources for _, ds := range dataSources { if strings.HasPrefix(ds.PluginDir, app.PluginDir) { ds.setPathsBasedOnApp(app, cfg) app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{ Name: ds.Name, Id: ds.Id, Type: ds.Type, }) } } // slugify pages for _, include := range app.Includes { if include.Slug == "" { include.Slug = slug.Make(include.Name) } if include.Type == "page" && include.DefaultNav { app.DefaultNavUrl = cfg.AppSubURL + "/plugins/" + app.Id + "/page/" + include.Slug } if include.Type == "dashboard" && include.DefaultNav { app.DefaultNavUrl = cfg.AppSubURL + "/dashboard/db/" + include.Slug } } return staticRoutes }