Skip to content

Plugin System

The Plugin system (pkg/plugin) allows you to extend Core applications with HTTP-based plugins that register routes under /api/{namespace}/{name}/.

Features

  • Namespace-based organization
  • HTTP handler registration
  • Lifecycle hooks (OnRegister, OnUnregister)
  • Wails service integration

Plugin Interface

All plugins implement the Plugin interface:

type Plugin interface {
    // Name returns the unique identifier for this plugin
    Name() string

    // Namespace returns the plugin's namespace (e.g., "core", "mining")
    Namespace() string

    // ServeHTTP handles HTTP requests routed to this plugin
    http.Handler

    // OnRegister is called when the plugin is registered
    OnRegister(ctx context.Context) error

    // OnUnregister is called when the plugin is being removed
    OnUnregister(ctx context.Context) error
}

Using BasePlugin

For simple plugins, embed BasePlugin:

import "github.com/Snider/Core/pkg/plugin"

func NewMyPlugin() *plugin.BasePlugin {
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello from plugin!"))
    })

    return plugin.NewBasePlugin("myapp", "greeting", handler).
        WithDescription("A simple greeting plugin").
        WithVersion("1.0.0")
}

Custom Plugin Implementation

For more control, implement the full interface:

type DataPlugin struct {
    db *sql.DB
}

func (p *DataPlugin) Name() string      { return "data" }
func (p *DataPlugin) Namespace() string { return "myapp" }

func (p *DataPlugin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    switch r.URL.Path {
    case "/users":
        p.handleUsers(w, r)
    case "/items":
        p.handleItems(w, r)
    default:
        http.NotFound(w, r)
    }
}

func (p *DataPlugin) OnRegister(ctx context.Context) error {
    // Initialize database connection
    db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
    if err != nil {
        return err
    }
    p.db = db
    return nil
}

func (p *DataPlugin) OnUnregister(ctx context.Context) error {
    if p.db != nil {
        return p.db.Close()
    }
    return nil
}

Plugin Info

Access plugin metadata:

info := myPlugin.Info()
fmt.Println(info.Name)        // "greeting"
fmt.Println(info.Namespace)   // "myapp"
fmt.Println(info.Description) // "A simple greeting plugin"
fmt.Println(info.Version)     // "1.0.0"

Wails Integration

Register plugins as Wails services:

app := application.New(application.Options{
    Services: []application.Service{
        application.NewServiceWithOptions(
            myPlugin,
            plugin.ServiceOptionsForPlugin(myPlugin),
        ),
    },
})

URL Routing

Plugins receive requests at:

/api/{namespace}/{name}/{path}

Examples: - /api/myapp/greeting/ → GreetingPlugin - /api/myapp/data/users → DataPlugin (path: "/users") - /api/core/system/health → SystemPlugin (path: "/health")

Built-in Plugins

System Plugin

Located at pkg/plugin/builtin/system:

// Provides system information endpoints
/api/core/system/info    - Application info
/api/core/system/health  - Health check

Plugin Router

The Router manages plugin registration:

import "github.com/Snider/Core/pkg/plugin"

router := plugin.NewRouter()

// Register plugins
router.Register(ctx, myPlugin)
router.Register(ctx, dataPlugin)

// Get all registered plugins
plugins := router.List()

// Unregister a plugin
router.Unregister(ctx, "myapp", "greeting")

Best Practices

  1. Use namespaces to group related plugins
  2. Implement OnRegister for initialization that can fail
  3. Implement OnUnregister to clean up resources
  4. Return meaningful errors from lifecycle hooks
  5. Use standard HTTP patterns in ServeHTTP