mirror of https://gitee.com/answerdev/answer.git
feat(plugin): update plugin interface
This commit is contained in:
parent
8575bbf8c8
commit
7331b49623
|
@ -106,7 +106,7 @@ func (cc *ConnectorController) ConnectorsInfo(ctx *gin.Context) {
|
|||
err = plugin.CallConnector(func(fn plugin.Connector) error {
|
||||
resp = append(resp, &schema.ConnectorInfoResp{
|
||||
Name: fn.ConnectorSlugName(),
|
||||
Icon: fn.ConnectorLogo(),
|
||||
Icon: fn.ConnectorLogoSVG(),
|
||||
Link: fmt.Sprintf("%s%s%s", general.SiteUrl, ConnectorLoginRouterPrefix, fn.ConnectorSlugName()),
|
||||
})
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package controller_admin
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/plugin"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
service "github.com/answerdev/answer/internal/service/role"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// PluginController role controller
|
||||
type PluginController struct {
|
||||
roleService *service.RoleService
|
||||
}
|
||||
|
||||
// NewPluginController new controller
|
||||
func NewPluginController(roleService *service.RoleService) *PluginController {
|
||||
return &PluginController{roleService: roleService}
|
||||
}
|
||||
|
||||
// GetPluginList get plugin list
|
||||
func (pc *PluginController) GetPluginList(ctx *gin.Context) {
|
||||
plugin.CallBase(func(base plugin.Base) error {
|
||||
base.Info()
|
||||
return nil
|
||||
})
|
||||
|
||||
resp, err := pc.roleService.GetRoleList(ctx)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// UpdatePluginStatus update plugin status
|
||||
func (pc *PluginController) UpdatePluginStatus(ctx *gin.Context) {
|
||||
req := &schema.UpdatePluginStatusReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
||||
plugin.StatusManager.Enable(req.PluginSlugName, req.Enabled)
|
||||
handler.HandleResponse(ctx, nil, nil)
|
||||
}
|
||||
|
||||
// GetPluginConfig get plugin config
|
||||
func (pc *PluginController) GetPluginConfig(ctx *gin.Context) {
|
||||
resp, err := pc.roleService.GetRoleList(ctx)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// UpdatePluginConfig get plugin config
|
||||
func (pc *PluginController) UpdatePluginConfig(ctx *gin.Context) {
|
||||
resp, err := pc.roleService.GetRoleList(ctx)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
|
@ -3,10 +3,10 @@ package plugin
|
|||
// Info presents the plugin information
|
||||
type Info struct {
|
||||
Name string
|
||||
SlugName string
|
||||
Description string
|
||||
Author string
|
||||
Version string
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// Base is the base plugin
|
||||
|
@ -18,5 +18,5 @@ type Base interface {
|
|||
var (
|
||||
// CallBase is a function that calls all registered base plugins
|
||||
CallBase,
|
||||
registerBase = MakePlugin[Base]()
|
||||
registerBase = MakePlugin[Base](true)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package plugin
|
||||
|
||||
type ConfigType int
|
||||
|
||||
const (
|
||||
ConfigTypeInput ConfigType = iota
|
||||
ConfigTypeTextarea
|
||||
ConfigTypeSelect
|
||||
ConfigTypeCheckbox
|
||||
)
|
||||
|
||||
type ConfigField struct {
|
||||
Name string
|
||||
Description string
|
||||
Required bool
|
||||
Type ConfigType
|
||||
Items []ConfigFieldItem
|
||||
}
|
||||
|
||||
type ConfigFieldItem struct {
|
||||
Name string
|
||||
Label string
|
||||
Value string
|
||||
PlaceHolder string
|
||||
Selected bool
|
||||
}
|
||||
|
||||
type Config interface {
|
||||
Base
|
||||
|
||||
// ConfigFields returns the list of config fields
|
||||
ConfigFields() []ConfigField
|
||||
|
||||
// ConfigReceiver receives the config data, it calls when the config is saved or initialized.
|
||||
// We recommend to unmarshal the data to a struct, and then use the struct to do something.
|
||||
// The config is encoded in JSON format.
|
||||
// It depends on the definition of ConfigFields.
|
||||
ConfigReceiver(config []byte) error
|
||||
}
|
||||
|
||||
var (
|
||||
// CallConfig is a function that calls all registered config plugins
|
||||
CallConfig,
|
||||
registerConfig = MakePlugin[Config](true)
|
||||
)
|
|
@ -3,12 +3,8 @@ package plugin
|
|||
type Connector interface {
|
||||
Base
|
||||
|
||||
// ConnectorLogo presents the logo binary data of the connector
|
||||
ConnectorLogo() []byte
|
||||
|
||||
// ConnectorLogoContentType presents the content type of the logo
|
||||
// e.g. image/png, image/jpeg, image/gif
|
||||
ConnectorLogoContentType() string
|
||||
// ConnectorLogoSVG presents the logo in svg format
|
||||
ConnectorLogoSVG() string
|
||||
|
||||
// ConnectorName presents the name of the connector
|
||||
// e.g. Facebook, Twitter, Instagram
|
||||
|
@ -44,5 +40,5 @@ type ExternalLoginUserInfo struct {
|
|||
var (
|
||||
// CallConnector is a function that calls all registered connectors
|
||||
CallConnector,
|
||||
registerConnector = MakePlugin[Connector]()
|
||||
registerConnector = MakePlugin[Connector](false)
|
||||
)
|
||||
|
|
|
@ -8,5 +8,5 @@ type Filter interface {
|
|||
var (
|
||||
// CallFilter is a function that calls all registered parsers
|
||||
CallFilter,
|
||||
registerFilter = MakePlugin[Filter]()
|
||||
registerFilter = MakePlugin[Filter](false)
|
||||
)
|
||||
|
|
|
@ -8,5 +8,5 @@ type Parser interface {
|
|||
var (
|
||||
// CallParser is a function that calls all registered parsers
|
||||
CallParser,
|
||||
registerParser = MakePlugin[Parser]()
|
||||
registerParser = MakePlugin[Parser](false)
|
||||
)
|
||||
|
|
|
@ -1,35 +1,43 @@
|
|||
package plugin
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GinContext is a wrapper of gin.Context
|
||||
// We export it to make it easy to use in plugins
|
||||
type GinContext = gin.Context
|
||||
|
||||
// StatusManager is a manager that manages the status of plugins
|
||||
// Init Plugins:
|
||||
// json.Unmarshal([]byte(`{"plugin1": true, "plugin2": false}`), &plugin.StatusManager)
|
||||
// Dump Status:
|
||||
// json.Marshal(plugin.StatusManager)
|
||||
var StatusManager = statusManager{
|
||||
status: make(map[string]bool),
|
||||
}
|
||||
|
||||
// Register registers a plugin
|
||||
func Register(p Base) {
|
||||
registerBase(p)
|
||||
|
||||
switch pType := p.(type) {
|
||||
case Connector:
|
||||
registerConnector(pType)
|
||||
case Parser:
|
||||
registerParser(pType)
|
||||
case Filter:
|
||||
registerFilter(pType)
|
||||
if _, ok := p.(Config); ok {
|
||||
registerConfig(p.(Config))
|
||||
}
|
||||
}
|
||||
|
||||
// Dump returns all registered plugins infos
|
||||
func Dump() []Info {
|
||||
var infos []Info
|
||||
if _, ok := p.(Connector); ok {
|
||||
registerConnector(p.(Connector))
|
||||
}
|
||||
|
||||
CallBase(func(p Base) error {
|
||||
infos = append(infos, p.Info())
|
||||
return nil
|
||||
})
|
||||
if _, ok := p.(Parser); ok {
|
||||
registerParser(p.(Parser))
|
||||
}
|
||||
|
||||
return infos
|
||||
if _, ok := p.(Filter); ok {
|
||||
registerFilter(p.(Filter))
|
||||
}
|
||||
}
|
||||
|
||||
type Stack[T Base] struct {
|
||||
|
@ -41,16 +49,17 @@ type Caller[T Base] func(p T) error
|
|||
type CallFn[T Base] func(fn Caller[T]) error
|
||||
|
||||
// MakePlugin creates a plugin caller and register stack manager
|
||||
// The parameter super presents if the plugin can be disabled.
|
||||
// It returns a register function and a caller function
|
||||
// The register function is used to register a plugin, it will be called in the plugin's init function
|
||||
// The caller function is used to call all registered plugins
|
||||
func MakePlugin[T Base]() (CallFn[T], RegisterFn[T]) {
|
||||
func MakePlugin[T Base](super bool) (CallFn[T], RegisterFn[T]) {
|
||||
stack := Stack[T]{}
|
||||
|
||||
call := func(fn Caller[T]) error {
|
||||
for _, p := range stack.plugins {
|
||||
// If the plugin is disabled, skip it
|
||||
if p.Info().Disabled {
|
||||
if !super && !StatusManager.IsEnabled(p.Info().SlugName) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -67,3 +76,28 @@ func MakePlugin[T Base]() (CallFn[T], RegisterFn[T]) {
|
|||
|
||||
return call, register
|
||||
}
|
||||
|
||||
type statusManager struct {
|
||||
status map[string]bool
|
||||
}
|
||||
|
||||
func (m *statusManager) Enable(name string, enabled bool) {
|
||||
m.status[name] = enabled
|
||||
}
|
||||
|
||||
func (m *statusManager) IsEnabled(name string) bool {
|
||||
if status, ok := m.status[name]; ok {
|
||||
return status
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (m *statusManager) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.status)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (m *statusManager) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &m.status)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@ package schema
|
|||
|
||||
type ConnectorInfoResp struct {
|
||||
Name string `json:"name"`
|
||||
Icon []byte `json:"icon"`
|
||||
Icon string `json:"icon"`
|
||||
Link string `json:"link"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package schema
|
||||
|
||||
type UpdatePluginStatusReq struct {
|
||||
PluginSlugName string `validate:"required,gt=1,lte=100" json:"plugin_slug_name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package plugin_common
|
||||
|
||||
type PluginConfig struct{}
|
Loading…
Reference in New Issue