mirror of https://github.com/jlelse/GoBlog
parent
bff6272350
commit
c3611a32d6
@ -1,59 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
Config *PluginConfig
|
||||
plugin reflect.Value
|
||||
}
|
||||
|
||||
// PluginConfig is the configuration of the plugin.
|
||||
type PluginConfig struct {
|
||||
// Path is the storage path of the plugin.
|
||||
Path string
|
||||
// ImportPath is the module path i.e. "github.com/user/module".
|
||||
ImportPath string
|
||||
// PluginType is the type of plugin, this plugin is checked against that type.
|
||||
// The available types are specified by the implementor of this package.
|
||||
PluginType string
|
||||
}
|
||||
|
||||
func (p *plugin) initPlugin(host *PluginHost) error {
|
||||
const errText = "initPlugin: %w"
|
||||
|
||||
interpreter := interp.New(interp.Options{
|
||||
GoPath: p.Config.Path,
|
||||
})
|
||||
|
||||
if err := interpreter.Use(stdlib.Symbols); err != nil {
|
||||
return fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
if err := interpreter.Use(host.Symbols); err != nil {
|
||||
return fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
if _, err := interpreter.Eval(fmt.Sprintf(`import "%s"`, p.Config.ImportPath)); err != nil {
|
||||
return fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
v, err := interpreter.Eval(filepath.Base(p.Config.ImportPath) + ".GetPlugin")
|
||||
if err != nil {
|
||||
return fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
result := v.Call([]reflect.Value{})
|
||||
if len(result) > 1 {
|
||||
return fmt.Errorf(errText+": function GetPlugin has more than one return value", ErrValidatingPlugin)
|
||||
}
|
||||
p.plugin = result[0]
|
||||
|
||||
return nil
|
||||
}
|
@ -1,80 +1,124 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
"github.com/traefik/yaegi/stdlib"
|
||||
)
|
||||
|
||||
// PluginHost manages the plugins.
|
||||
type PluginHost struct {
|
||||
plugins map[string][]*plugin
|
||||
pluginTypes map[string]reflect.Type
|
||||
symbols interp.Exports
|
||||
embeddedPlugins fs.FS
|
||||
}
|
||||
|
||||
// PluginConfig is the configuration of the plugin.
|
||||
type PluginConfig struct {
|
||||
// Path is the storage path of the plugin.
|
||||
Path string
|
||||
// ImportPath is the module path i.e. "github.com/user/module".
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
Config *PluginConfig
|
||||
plugin any
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrValidatingPlugin is returned when the plugin fails to fully implement the interface of the plugin type.
|
||||
ErrValidatingPlugin = errors.New("plugin does not implement type")
|
||||
// ErrInitFunc is returned when the plugin has a faulty initialization function.
|
||||
ErrInitFunc = errors.New("bad plugin init function")
|
||||
)
|
||||
|
||||
const (
|
||||
embeddedPrefix = "embedded:"
|
||||
)
|
||||
|
||||
// NewPluginHost initializes a PluginHost.
|
||||
func NewPluginHost(symbols interp.Exports) *PluginHost {
|
||||
func NewPluginHost(pluginTypes map[string]reflect.Type, symbols interp.Exports, embeddedPlugins fs.FS) *PluginHost {
|
||||
return &PluginHost{
|
||||
Plugins: []*plugin{},
|
||||
PluginTypes: map[string]reflect.Type{},
|
||||
Symbols: symbols,
|
||||
plugins: map[string][]*plugin{},
|
||||
pluginTypes: pluginTypes,
|
||||
symbols: symbols,
|
||||
embeddedPlugins: embeddedPlugins,
|
||||
}
|
||||
}
|
||||
|
||||
// AddPluginType adds a plugin type to the list.
|
||||
// The interface for the pluginType parameter should be a nil of the plugin type interface:
|
||||
//
|
||||
// (*PluginInterface)(nil)
|
||||
func (h *PluginHost) AddPluginType(name string, pluginType interface{}) {
|
||||
h.PluginTypes[name] = reflect.TypeOf(pluginType).Elem()
|
||||
}
|
||||
|
||||
// LoadPlugin loads a new plugin to the host.
|
||||
func (h *PluginHost) LoadPlugin(config *PluginConfig) (any, error) {
|
||||
func (h *PluginHost) LoadPlugin(config *PluginConfig) (map[string]any, error) {
|
||||
p := &plugin{
|
||||
Config: config,
|
||||
}
|
||||
err := p.initPlugin(h)
|
||||
plugins, err := p.initPlugin(h)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = h.validatePlugin(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
// GetPlugins returns a list of all plugins.
|
||||
func (h *PluginHost) GetPlugins(typ string) (list []any) {
|
||||
for _, p := range h.plugins[typ] {
|
||||
list = append(list, p.plugin)
|
||||
}
|
||||
h.Plugins = append(h.Plugins, p)
|
||||
return p.plugin.Interface(), nil
|
||||
return
|
||||
}
|
||||
|
||||
func (h *PluginHost) validatePlugin(p *plugin) error {
|
||||
pType := reflect.TypeOf(p.plugin.Interface())
|
||||
func (p *plugin) initPlugin(host *PluginHost) (plugins map[string]any, err error) {
|
||||
const errText = "initPlugin: %w"
|
||||
|
||||
if _, ok := h.PluginTypes[p.Config.PluginType]; !ok {
|
||||
return fmt.Errorf("validatePlugin: %v: %w", p.Config.PluginType, ErrInvalidType)
|
||||
plugins = map[string]any{}
|
||||
|
||||
var filesystem fs.FS
|
||||
if strings.HasPrefix(p.Config.Path, embeddedPrefix) {
|
||||
filesystem = host.embeddedPlugins
|
||||
}
|
||||
|
||||
if !pType.Implements(h.PluginTypes[p.Config.PluginType]) {
|
||||
return fmt.Errorf("validatePlugin:%v: %w %v", p, ErrValidatingPlugin, p.Config.PluginType)
|
||||
interpreter := interp.New(interp.Options{
|
||||
GoPath: strings.TrimPrefix(p.Config.Path, embeddedPrefix),
|
||||
SourcecodeFilesystem: filesystem,
|
||||
})
|
||||
|
||||
if err := interpreter.Use(stdlib.Symbols); err != nil {
|
||||
return nil, fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := interpreter.Use(host.symbols); err != nil {
|
||||
return nil, fmt.Errorf(errText, err)
|
||||
}
|
||||
|
||||
// GetPlugins returns a list of all plugins.
|
||||
func (h *PluginHost) GetPlugins() (list []any) {
|
||||
for _, p := range h.Plugins {
|
||||
list = append(list, p.plugin.Interface())
|
||||
if _, err := interpreter.Eval(fmt.Sprintf(`import "%s"`, p.Config.ImportPath)); err != nil {
|
||||
return nil, fmt.Errorf(errText, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetPluginsForType returns all the plugins that are of type pluginType or empty if the pluginType doesn't exist.
|
||||
func GetPluginsForType[T any](h *PluginHost, pluginType string) (list []T) {
|
||||
if _, ok := h.PluginTypes[pluginType]; !ok {
|
||||
return
|
||||
v, err := interpreter.Eval(filepath.Base(p.Config.ImportPath) + ".GetPlugin")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(errText, err)
|
||||
}
|
||||
for _, p := range h.Plugins {
|
||||
if p.Config.PluginType != pluginType {
|
||||
continue
|
||||
|
||||
resultArray := v.Call([]reflect.Value{})
|
||||
for _, result := range resultArray {
|
||||
newPlugin := &plugin{
|
||||
Config: p.Config,
|
||||
plugin: result.Interface(),
|
||||
}
|
||||
if t, ok := p.plugin.Interface().(T); ok {
|
||||
list = append(list, t)
|
||||
for name, reflectType := range host.pluginTypes {
|
||||
if result.Type().Implements(reflectType) {
|
||||
host.plugins[name] = append(host.plugins[name], newPlugin)
|
||||
plugins[name] = newPlugin.plugin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/traefik/yaegi/interp"
|
||||
)
|
||||
|
||||
// PluginHost manages the plugins.
|
||||
type PluginHost struct {
|
||||
// Plugins contains a list of the plugins.
|
||||
Plugins []*plugin
|
||||
// PluginTypes is a list of plugins types that plugins have to use at least one of.
|
||||
PluginTypes map[string]reflect.Type
|
||||
// Symbols is the map of symbols generated by yaegi extract.
|
||||
Symbols interp.Exports
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrInvalidType is returned when the plugin type specified by the plugin is invalid.
|
||||
ErrInvalidType = errors.New("invalid plugin type")
|
||||
// ErrValidatingPlugin is returned when the plugin fails to fully implement the interface of the plugin type.
|
||||
ErrValidatingPlugin = errors.New("plugin does not implement type")
|
||||
)
|
@ -1,38 +1,36 @@
|
||||
package plugintypes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
)
|
||||
|
||||
// SetApp is used in all plugin types to allow
|
||||
// GoBlog set it's app instance to be accessible by the plugin.
|
||||
// SetApp is used to allow GoBlog set its app instance to be accessible by the plugin.
|
||||
type SetApp interface {
|
||||
SetApp(App)
|
||||
SetApp(app App)
|
||||
}
|
||||
|
||||
// SetConfig is used in all plugin types to allow
|
||||
// GoBlog set plugin configuration.
|
||||
// SetConfig is used in all plugin types to allow GoBlog set the plugin configuration.
|
||||
type SetConfig interface {
|
||||
SetConfig(map[string]any)
|
||||
SetConfig(config map[string]any)
|
||||
}
|
||||
|
||||
// Exec plugins are executed after all plugins where initialized.
|
||||
type Exec interface {
|
||||
SetApp
|
||||
SetConfig
|
||||
// Exec gets called from a Goroutine, so it runs asynchronously.
|
||||
Exec()
|
||||
}
|
||||
|
||||
// Middleware plugins can intercept and modify HTTP requests or responses.
|
||||
type Middleware interface {
|
||||
SetApp
|
||||
SetConfig
|
||||
Handler(http.Handler) http.Handler
|
||||
Handler(next http.Handler) http.Handler
|
||||
// Return a priority, the higher prio middlewares get called first.
|
||||
Prio() int
|
||||
}
|
||||
|
||||
// UI plugins get called when rendering HTML.
|
||||
type UI interface {
|
||||
SetApp
|
||||
SetConfig
|
||||
Render(*htmlbuilder.HtmlBuilder, RenderType, RenderData, RenderNextFunc)
|
||||
// rendered is a reader with all the rendered HTML, modify it and write it to modified. This is then returned to the client.
|
||||
// The renderContext provides information such as the path of the request or the blog name.
|
||||
Render(renderContext RenderContext, rendered io.Reader, modified io.Writer)
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
// Code generated by 'yaegi extract github.com/PuerkitoBio/goquery'. DO NOT EDIT.
|
||||
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 - 2023 Jan-Lukas Else
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaegiwrappers
|
||||
|
||||
import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"golang.org/x/net/html"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["github.com/PuerkitoBio/goquery/goquery"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"CloneDocument": reflect.ValueOf(goquery.CloneDocument),
|
||||
"NewDocument": reflect.ValueOf(goquery.NewDocument),
|
||||
"NewDocumentFromNode": reflect.ValueOf(goquery.NewDocumentFromNode),
|
||||
"NewDocumentFromReader": reflect.ValueOf(goquery.NewDocumentFromReader),
|
||||
"NewDocumentFromResponse": reflect.ValueOf(goquery.NewDocumentFromResponse),
|
||||
"NodeName": reflect.ValueOf(goquery.NodeName),
|
||||
"OuterHtml": reflect.ValueOf(goquery.OuterHtml),
|
||||
"Render": reflect.ValueOf(goquery.Render),
|
||||
"Single": reflect.ValueOf(goquery.Single),
|
||||
"SingleMatcher": reflect.ValueOf(goquery.SingleMatcher),
|
||||
"ToEnd": reflect.ValueOf(goquery.ToEnd),
|
||||
|
||||
// type definitions
|
||||
"Document": reflect.ValueOf((*goquery.Document)(nil)),
|
||||
"Matcher": reflect.ValueOf((*goquery.Matcher)(nil)),
|
||||
"Selection": reflect.ValueOf((*goquery.Selection)(nil)),
|
||||
|
||||
// interface wrapper definitions
|
||||
"_Matcher": reflect.ValueOf((*_github_com_PuerkitoBio_goquery_Matcher)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
// _github_com_PuerkitoBio_goquery_Matcher is an interface wrapper for Matcher type
|
||||
type _github_com_PuerkitoBio_goquery_Matcher struct {
|
||||
IValue interface{}
|
||||
WFilter func(a0 []*html.Node) []*html.Node
|
||||
WMatch func(a0 *html.Node) bool
|
||||
WMatchAll func(a0 *html.Node) []*html.Node
|
||||
}
|
||||
|
||||
func (W _github_com_PuerkitoBio_goquery_Matcher) Filter(a0 []*html.Node) []*html.Node {
|
||||
return W.WFilter(a0)
|
||||
}
|
||||
func (W _github_com_PuerkitoBio_goquery_Matcher) Match(a0 *html.Node) bool {
|
||||
return W.WMatch(a0)
|
||||
}
|
||||
func (W _github_com_PuerkitoBio_goquery_Matcher) MatchAll(a0 *html.Node) []*html.Node {
|
||||
return W.WMatchAll(a0)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
// Code generated by 'yaegi extract go.goblog.app/app/pkgs/bufferpool'. DO NOT EDIT.
|
||||
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 - 2023 Jan-Lukas Else
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package yaegiwrappers
|
||||
|
||||
import (
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Symbols["go.goblog.app/app/pkgs/bufferpool/bufferpool"] = map[string]reflect.Value{
|
||||
// function, constant and variable definitions
|
||||
"Get": reflect.ValueOf(bufferpool.Get),
|
||||
"Put": reflect.ValueOf(bufferpool.Put),
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
app plugintypes.App
|
||||
config map[string]any
|
||||
}
|
||||
|
||||
func GetPlugin() (
|
||||
plugintypes.SetApp, plugintypes.SetConfig,
|
||||
plugintypes.UI,
|
||||
plugintypes.Exec,
|
||||
plugintypes.Middleware,
|
||||
) {
|
||||
p := &plugin{}
|
||||
return p, p, p, p, p
|
||||
}
|
||||
|
||||
// SetApp
|
||||
func (p *plugin) SetApp(app plugintypes.App) {
|
||||
p.app = app
|
||||
}
|
||||
|
||||
// SetConfig
|
||||
func (p *plugin) SetConfig(config map[string]any) {
|
||||
p.config = config
|
||||
}
|
||||
|
||||
// UI
|
||||
func (*plugin) Render(_ plugintypes.RenderContext, rendered io.Reader, modified io.Writer) {
|
||||
doc, err := goquery.NewDocumentFromReader(rendered)
|
||||
if err != nil {
|
||||
fmt.Println("demoui plugin: " + err.Error())
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
hb := htmlbuilder.NewHtmlBuilder(buf)
|
||||
hb.WriteElementOpen("p")
|
||||
hb.WriteEscaped("End of post content")
|
||||
hb.WriteElementClose("p")
|
||||
doc.Find("main.h-entry article div.e-content").AppendHtml(buf.String())
|
||||
_ = goquery.Render(modified, doc.Selection)
|
||||
}
|
||||
|
||||
// Exec
|
||||
func (p *plugin) Exec() {
|
||||
fmt.Println("Hello World from the demo plugin!")
|
||||
|
||||
row, _ := p.app.GetDatabase().QueryRow("select count (*) from posts")
|
||||
var count int
|
||||
if err := row.Scan(&count); err != nil {
|
||||
fmt.Println(fmt.Errorf("failed to count posts: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Number of posts in database: %d", count)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Middleware
|
||||
func (p *plugin) Prio() int {
|
||||
if prioAny, ok := p.config["prio"]; ok {
|
||||
if prio, ok := prioAny.(int); ok {
|
||||
return prio
|
||||
}
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
// Middleware
|
||||
func (p *plugin) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Demo", fmt.Sprintf("This is from the demo middleware with prio %d", p.Prio()))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package demoexec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
func GetPlugin() plugintypes.Exec {
|
||||
return &plugin{}
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
app plugintypes.App
|
||||
}
|
||||
|
||||
func (p *plugin) SetApp(app plugintypes.App) {
|
||||
p.app = app
|
||||
}
|
||||
|
||||
func (*plugin) SetConfig(_ map[string]any) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
func (p *plugin) Exec() {
|
||||
fmt.Println("Hello World from the demo plugin!")
|
||||
|
||||
row, _ := p.app.GetDatabase().QueryRow("select count (*) from posts")
|
||||
var count int
|
||||
if err := row.Scan(&count); err != nil {
|
||||
fmt.Println(fmt.Errorf("failed to count posts: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Number of posts in database: %d", count)
|
||||
fmt.Println()
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package demomiddleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"go.goblog.app/app/pkgs/plugintypes"
|
||||
)
|
||||
|
||||
func GetPlugin() plugintypes.Middleware {
|
||||
return &plugin{}
|
||||
}
|
||||
|
||||
type plugin struct {
|
||||
app plugintypes.App
|
||||
config map[string]any
|
||||
}
|
||||
|
||||
func (p *plugin) SetApp(app plugintypes.App) {
|
||||
p.app = app
|
||||
}
|
||||
|
||||
func (p *plugin) SetConfig(config map[string]any) {
|
||||
p.config = config
|
||||
}
|
||||
|
||||
func (p *plugin) Prio() int {
|
||||
if prioAny, ok := p.config["prio"]; ok {
|
||||
if prio, ok := prioAny.(int); ok {
|
||||
return prio
|
||||
}
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
func (p *plugin) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("X-Demo", fmt.Sprintf("This is from the demo middleware with prio %d", p.Prio()))
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|