Files
summercms/modules/pact
Jakub Zych d9f939a1ea feat(11-02): run plugin schedules as River periodic jobs through bonfire.Call
- pact.HasSchedule with ScheduledCommand and Daily/DailyAt/Every cadences (no River import)
- bonfire.Call, Catalog and ErrUnknownCommand for in-process command runs
- conga Daily/Every wall-clock schedules in app.timezone, periodic jobs on every worker,
  scheduled queue (MaxAttempts 1, unique by args within the cadence period)
- scheduled worker runs only entries matching the compiled table; unregistered
  commands are skipped with a Warn log
- generated app main publishes bonfire.NewCatalog(commands); hello main regenerated
2026-09-29 19:47:51 +02:00
..

pact

Capability interfaces that compiled plugins implement to contribute routes, config, migrations, middleware, commands, admin screens, translations, mail templates, jobs and scheduled commands.

import "git.golem15.com/golem15/summercms/modules/pact"

Overview

pact is the contract layer between plugins and the framework. It holds interfaces and plain data types, with no behaviour of its own. A plugin opts into a capability by implementing one of the Has* interfaces, and the framework package that owns the capability discovers it with a type assertion (party for config, surf for routes and middleware, lagoon for migrations, cabana for admin controllers, conga for jobs and schedules). It replaces the register*() methods of a WinterCMS PluginBase (registerPermissions, registerNavigation, registerSettings and so on) with small, separately implementable interfaces.

Features

  • Plugin capability interfaces: pact.HasRoutes, pact.HasConfig, pact.HasMigrations, pact.HasCommands, pact.HasModels, pact.HasJobs, pact.HasSchedule, pact.HasLang, pact.HasLangOverrides and pact.HasMailTemplates.
  • HTTP contracts: the pact.Router group builder (implemented by surf), the pact.Middleware type, and named, parameterized (name:param) and house-envelope middleware through pact.HasMiddleware, pact.HasMiddlewareFactories and pact.HasHouseMiddleware.
  • Backend registration data: pact.Permission, pact.NavigationItem and pact.SettingsItem, exposed through pact.HasPermissions, pact.HasNavigation and pact.HasSettings.
  • Admin controller contracts: pact.AdminController, pact.HasAdminControllers, pact.AdminAssets (embedded Winter-shaped admin YAML), pact.AdminPermissioned and pact.AdminRecordSource.
  • Admin extension contracts, so a plugin extends the compiled admin SPA without a Node build: pact.AdminClientAssets (per-controller JS and CSS from the plugin's embedded assets/ tree, Winter's addJs/addCss), pact.HasAdminActions with pact.AdminAction, pact.AdminActionInput and pact.AdminActionResult (named toolbar and widget actions whose routes, CSRF check, permissions and record scoping the framework owns), and pact.AdminPartialData (the curated view model a partial template renders).
  • Optional admin hooks a controller or model can implement: list and form query scoping (pact.ListExtendQuery, pact.FormExtendQuery), create, update and delete hooks (pact.FormBeforeCreate, pact.FormAfterUpdate, pact.FormBeforeDelete and their siblings), relation hooks (pact.RelationExtendManageQuery, pact.RelationExtendOptionsQuery, pact.RelationBeforeLink), filter scopes (pact.FilterScope, pact.FilterOptions) and dropdown options (pact.DropdownOptionsProvider).
  • A background job contract (pact.Job, pact.JobArgs) that does not depend on any queue library.
  • A schedule contract: pact.HasSchedule returns pact.ScheduledCommand entries (a registered command name, its arguments and a pact.Cadence built with pact.Daily, pact.DailyAt or pact.Every), the Go form of WinterCMS registerSchedule. It does not depend on any queue library either.
  • pact.OptionalMessage, a service an optional plugin can publish so others integrate with it without importing its package.
  • pact.HasModels and pact.OptionalMessage are declared for plugins to implement, but no framework package consumes them yet.

Usage

A plugin declares its capabilities by implementing the interfaces and asserting them at compile time:

package blog

import (
	"net/http"

	"git.golem15.com/golem15/summercms/modules/pact"
)

var (
	_ pact.HasRoutes      = (*Plugin)(nil)
	_ pact.HasPermissions = (*Plugin)(nil)
)

type Plugin struct{}

func (p *Plugin) Routes(r pact.Router) error {
	r.Group("/api/blog", []string{"auth"}, func(r pact.Router) {
		r.Get("/posts", listPosts)
		r.Get("/posts/{id}", showPost)
		r.Where("id", "[0-9]+")
	})
	return nil
}

func (p *Plugin) Permissions() []pact.Permission {
	return []pact.Permission{
		{Code: "acme.blog.access_posts", Tab: "Blog", Label: "Manage posts"},
	}
}

func listPosts(w http.ResponseWriter, r *http.Request) {}
func showPost(w http.ResponseWriter, r *http.Request)  {}

A plugin schedules one of its registered commands by implementing pact.HasSchedule:

var _ pact.HasSchedule = (*Plugin)(nil)

func (p *Plugin) Schedule() []pact.ScheduledCommand {
	return []pact.ScheduledCommand{
		{Command: "blog:prune-drafts", Cadence: pact.Daily()},
		{Command: "blog:ping", Args: []string{"--quiet"}, Cadence: pact.Every(15 * time.Minute)},
	}
}

API reference

Identifier Description
pact.Router Laravel-style route group builder (pact.Router.Group, pact.Router.GroupRaw, one method per HTTP verb, pact.Router.Where, pact.Router.WhereIn); implemented by surf.
pact.HasRoutes Declares HTTP routes on a pact.Router.
pact.Middleware A named func(http.Handler) http.Handler wrapper.
pact.HasMiddleware Registers named middleware.
pact.HasMiddlewareFactories Registers parameterized middleware resolved from name:param at wrap time.
pact.HasHouseMiddleware Registers middleware tagged as envelope and error handling, which raw groups refuse.
pact.HasConfig Ships default YAML config, merged under the plugin ID.
pact.HasMigrations Ships an ordered gormigrate set, run with a per-plugin history table.
pact.HasCommands Contributes bonfire console commands to the application binary.
pact.HasModels Exposes GORM models.
pact.Job Background unit of work that receives pact.JobArgs.
pact.HasJobs Registers background jobs.
pact.HasSchedule Declares recurring console commands (Schedule() []pact.ScheduledCommand); conga workers run them.
pact.ScheduledCommand One schedule entry: Command, Args and Cadence.
pact.Cadence Opaque run frequency with IsZero, Interval (24h for daily cadences) and At (hour and minute of a daily cadence).
pact.Daily Cadence at 00:00 every day in the app timezone (Laravel ->daily()).
pact.DailyAt Cadence at a given hour and minute every day in the app timezone.
pact.Every Cadence at every multiple of an interval since local midnight; the interval must be at least 1s and divide 24h.
pact.HasLang Ships translation YAML under lang/<locale>/<group>.yaml.
pact.HasLangOverrides Replaces or adds translations of any loaded namespace, including the framework's own.
pact.HasMailTemplates Ships mail templates and layout aliases.
pact.Permission One backend permission entry (code, tab, label, roles).
pact.NavigationItem One backend navigation entry, with an optional side menu.
pact.SettingsItem One settings screen entry.
pact.AdminController Admin controller identity: ID, model name and YAML config directory.
pact.AdminAssets Embedded tree of the plugin's admin YAML.
pact.AdminRecordSource Supplies a new model record for the generic admin handlers.
pact.AdminClientAssets Declares a controller's admin JS (AdminJS) and CSS (AdminCSS) files, paths under the plugin's assets/ directory.
pact.AdminAction One named controller action: name, label, extra permissions and the Go Run function.
pact.AdminActionInput What an action receives: widget field, optional record id and scoped record, and the fill snapshot.
pact.AdminActionResult What an action returns: a message for the toast and the fill write-back values.
pact.HasAdminActions Registers a controller's actions for toolbar.buttons and type: widget fields.
pact.AdminPartialData Supplies the view model a controller partial template renders; never the GORM model.
pact.FilterScope Model scopes a list filter may call, limited to an exact allow list.
pact.Option One dropdown choice (value and label).

Dependencies

  • SummerCMS modules: bonfire (the command type in pact.HasCommands).
  • Third-party: github.com/go-gormigrate/gormigrate/v2, gorm.io/gorm.
  • Standard library: context, io/fs, net/http, time.

Testing

go test ./modules/pact/...

The tests are compile-time interface checks and need no external services.