136 lines
4.0 KiB
Go
136 lines
4.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
"infinite-canvas/backend/internal/protocol"
|
|
)
|
|
|
|
type protocolRegistryContextKey struct{}
|
|
|
|
func withProtocolRegistry(ctx context.Context, registry *protocol.Registry) context.Context {
|
|
return context.WithValue(ctx, protocolRegistryContextKey{}, registry)
|
|
}
|
|
|
|
func declarativeProtocolAdapterForContext(ctx context.Context, id string) (protocol.Adapter, bool) {
|
|
registry, _ := ctx.Value(protocolRegistryContextKey{}).(*protocol.Registry)
|
|
if registry == nil {
|
|
registry = protocol.Builtins()
|
|
}
|
|
adapter, ok := registry.Resolve(strings.TrimSpace(id))
|
|
if !ok || adapter.Metadata().Execution != "declarative" {
|
|
return nil, false
|
|
}
|
|
return adapter, true
|
|
}
|
|
|
|
func agentProtocolAdapterForContext(ctx context.Context, id string) (protocol.AgentAdapter, bool) {
|
|
registry, _ := ctx.Value(protocolRegistryContextKey{}).(*protocol.Registry)
|
|
if registry == nil {
|
|
registry = protocol.Builtins()
|
|
}
|
|
adapter, ok := registry.Resolve(strings.TrimSpace(id))
|
|
if !ok || adapter.Metadata().Execution != "declarative" {
|
|
return nil, false
|
|
}
|
|
agentAdapter, ok := adapter.(protocol.AgentAdapter)
|
|
return agentAdapter, ok
|
|
}
|
|
|
|
// ProtocolCatalog is the backend source of truth for the frontend plugin
|
|
// center's protocol filtering.
|
|
// includeUnavailable is reserved for administrators so incomplete plugins remain visible
|
|
// with an explicit reason instead of silently becoming a selectable option.
|
|
func (s *Service) ProtocolCatalog(scope, capability string, includeUnavailable bool) []protocol.Metadata {
|
|
return s.protocolRegistry().List(protocol.Surface(strings.TrimSpace(scope)), protocol.Capability(strings.TrimSpace(capability)), includeUnavailable)
|
|
}
|
|
|
|
func (s *Service) protocolRegistry() *protocol.Registry {
|
|
if s.pluginRuntime != nil {
|
|
if registry := s.pluginRuntime.registrySnapshot(); registry != nil {
|
|
return registry
|
|
}
|
|
}
|
|
return protocol.Builtins()
|
|
}
|
|
|
|
func (s *Service) protocolMetadata(id string) (protocol.Metadata, bool) {
|
|
adapter, ok := s.protocolRegistry().Resolve(strings.TrimSpace(id))
|
|
if !ok {
|
|
return protocol.Metadata{}, false
|
|
}
|
|
return adapter.Metadata(), true
|
|
}
|
|
|
|
func (s *Service) channelProtocolMetadata(id string) (protocol.Metadata, bool) {
|
|
return s.protocolMetadata(id)
|
|
}
|
|
|
|
func (s *Service) canonicalProtocolID(id string) (string, bool) {
|
|
adapter, ok := s.protocolRegistry().Resolve(strings.TrimSpace(id))
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
return adapter.Metadata().ID, true
|
|
}
|
|
|
|
func (s *Service) protocolIsSelectable(id string) bool {
|
|
metadata, ok := s.channelProtocolMetadata(id)
|
|
return ok && metadata.Enabled && metadata.UnavailableReason == ""
|
|
}
|
|
|
|
func (s *Service) Plugins() []PluginView {
|
|
if s.pluginRuntime == nil {
|
|
return []PluginView{}
|
|
}
|
|
return s.pluginRuntime.list()
|
|
}
|
|
|
|
// PluginsForUser keeps the plugin center response aligned with the public
|
|
// feature switch. Administrators must still be able to inspect and recover
|
|
// bundled plugins even when ordinary users cannot see them.
|
|
func (s *Service) PluginsForUser(actor *model.User) ([]PluginView, error) {
|
|
items := s.Plugins()
|
|
if actor != nil && actor.Role == model.UserRoleAdmin {
|
|
return items, nil
|
|
}
|
|
visible, err := s.FeatureEnabled(FeatureSystemPlugins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if visible {
|
|
return items, nil
|
|
}
|
|
filtered := make([]PluginView, 0, len(items))
|
|
for _, item := range items {
|
|
if item.Source != "bundled" {
|
|
filtered = append(filtered, item)
|
|
}
|
|
}
|
|
return filtered, nil
|
|
}
|
|
|
|
func (s *Service) InstallPlugin(data []byte) (PluginView, error) {
|
|
if s.pluginRuntime == nil {
|
|
return PluginView{}, fmt.Errorf("插件运行时未初始化")
|
|
}
|
|
return s.pluginRuntime.install(data)
|
|
}
|
|
|
|
func (s *Service) SetPluginEnabled(id string, enabled bool) (PluginView, error) {
|
|
if s.pluginRuntime == nil {
|
|
return PluginView{}, fmt.Errorf("插件运行时未初始化")
|
|
}
|
|
return s.pluginRuntime.setEnabled(id, enabled)
|
|
}
|
|
|
|
func (s *Service) UninstallPlugin(id string) error {
|
|
if s.pluginRuntime == nil {
|
|
return fmt.Errorf("插件运行时未初始化")
|
|
}
|
|
return s.pluginRuntime.uninstall(id)
|
|
}
|