Documentation
¶
Overview ¶
Package web holds the Discourse integration's HTTP handlers: the webhook endpoint (this file) and the operator surface. The webhook endpoint is the latency layer between sweeps (design.md D3): user_created events link brand-new forum accounts within seconds of first login, and user_added_to_group/user_removed_from_group events on managed groups trigger targeted reconciles that correct forum-side drift. Missing or failed events cost only latency — the sweep restores correctness.
Index ¶
- Constants
- type ConflictViewModel
- type DiscourseOperatorHandler
- type DiscourseOperatorHandlerConfig
- type MappingViewModel
- type MemberForumData
- type MemberForumHandler
- type MemberForumHandlerConfig
- type OperatorPageData
- type ResourceKeyOption
- type StarterFunc
- type UnlinkedPersonViewModel
- type WebhookHandler
- type WebhookHandlerConfig
Constants ¶
const WebhookPath = "/webhooks/discourse"
WebhookPath is the Discourse webhook endpoint, declared CSRF-exempt by the adapter when (and only when) the webhook secret is configured.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ConflictViewModel ¶
ConflictViewModel is one conflicted link.
type DiscourseOperatorHandler ¶
type DiscourseOperatorHandler struct {
// contains filtered or unexported fields
}
DiscourseOperatorHandler serves the Discourse integration's operator surface: entitlement→group mapping management and link/sync visibility (spec: discourse-operator-surface).
func NewDiscourseOperatorHandler ¶
func NewDiscourseOperatorHandler(cfg DiscourseOperatorHandlerConfig) (*DiscourseOperatorHandler, error)
NewDiscourseOperatorHandler composes the operator shell with the integration's body partial.
func (*DiscourseOperatorHandler) CreateMapping ¶
func (h *DiscourseOperatorHandler) CreateMapping(w http.ResponseWriter, r *http.Request)
CreateMapping handles POST /partials/operator/discourse/mappings: validate against the forum (group exists, not automatic), then insert (spec: "Entitlement-to-group mapping management").
func (*DiscourseOperatorHandler) DeleteMapping ¶
func (h *DiscourseOperatorHandler) DeleteMapping(w http.ResponseWriter, r *http.Request)
DeleteMapping handles DELETE /partials/operator/discourse/mappings/{mappingID}. Unmapping stops management; forum-side membership is left as-is (spec: "Unmapping a group").
func (*DiscourseOperatorHandler) GetPage ¶
func (h *DiscourseOperatorHandler) GetPage(w http.ResponseWriter, r *http.Request)
GetPage handles GET <OperatorSurfacePath>.
type DiscourseOperatorHandlerConfig ¶
type DiscourseOperatorHandlerConfig struct {
DB *sql.DB
Logger *slog.Logger
AuthConfig *auth.Config
// Client is nil when the integration is unconfigured (the page then
// renders only the unconfigured notice; mutation routes are unmounted).
Client *dcclient.Client
Temporal temporalsdk.Client
Configured bool
// TemplatesFS is the integration's own template tree, injected by the
// adapter (same cycle argument as FedWiki's operator handler).
TemplatesFS fs.FS
// SweepScheduleID names the sweep SCHEDULE to report health from
// (this integration's workflows.SyncScheduleID; injected so this
// package needs no import of its own workflows package). Runs are
// resolved through the schedule because schedule-spawned executions
// get timestamp-suffixed workflow IDs — see
// workflows.ScheduleRunStatus.
SweepScheduleID string
}
DiscourseOperatorHandlerConfig configures the handler.
type MappingViewModel ¶
type MappingViewModel struct {
MappingID string
ResourceKey string
// DisplayName is the resource key's friendly name, from the same
// resource-key catalog the mapping-form picker uses, so the mappings
// table leads with it instead of the raw key (ui-vocabulary 4.3).
DisplayName string
GroupName string
GroupID int64
CreatedAt string
DeleteURL string
}
MappingViewModel is one entitlement→group mapping row.
type MemberForumData ¶
type MemberForumData struct {
// AccessGranted: a granted forum-access boolean entitlement on any of
// the member's organizations' pools (union semantics, the same
// desired-set predicate the sync sweep uses).
AccessGranted bool
// Linked: a healthy user_links row exists for this person. A
// 'conflict'-status link blocks delivery, so it renders as not-linked
// (the guidance state), never as an error.
Linked bool
ForumURL string
Mode string // linkage mode; selects the not-yet-linked guidance copy
Error string
}
MemberForumData holds data for the discourse_member_forum.html partial — the member dashboard card body (dashboard-display-genericity). Read-only by design: the partial reports fulfillment state and links to the forum; linkage is automatic per mode, so there is nothing for the member to mutate here.
type MemberForumHandler ¶
type MemberForumHandler struct {
Q dcmod.Querier
Logger *slog.Logger
AuthConfig *auth.Config
ForumURL string
Mode linkage.Mode
Keys []string
Templates *server.SafeTemplates
}
MemberForumHandler serves the member dashboard card body: the signed-in member's forum access and link state.
func NewMemberForumHandler ¶
func NewMemberForumHandler(cfg MemberForumHandlerConfig) (*MemberForumHandler, error)
NewMemberForumHandler creates a new MemberForumHandler.
func (*MemberForumHandler) GetForumAccess ¶
func (h *MemberForumHandler) GetForumAccess(w http.ResponseWriter, r *http.Request)
GetForumAccess handles GET /partials/discourse/forum-access.
func (*MemberForumHandler) RenderForPerson ¶
func (h *MemberForumHandler) RenderForPerson(w http.ResponseWriter, ctx context.Context, personID string)
RenderForPerson renders the card body for one person. Exported as the session-independent seam: GetForumAccess resolves the person from the authenticated session; DB-backed tests call this directly (constructing a live session in an external test package would drag in the whole auth stack for no additional coverage).
type MemberForumHandlerConfig ¶
type MemberForumHandlerConfig struct {
DB *sql.DB
Logger *slog.Logger
AuthConfig *auth.Config
// ForumURL is the configured Discourse base URL — the "Visit forum"
// link target.
ForumURL string
Mode linkage.Mode
// ResourceKeys are the forum-access boolean keys to check (from the
// provider manifest's declared ResourceKeys — no literal key names in
// this package); access is granted when any is desired for the person.
ResourceKeys []string
// TemplatesFS is Discourse's own template directory, supplied by the
// Adapter from its embedded tree (see the operator handler's identical
// arrangement for the cycle rationale).
TemplatesFS fs.FS
}
MemberForumHandlerConfig holds configuration for MemberForumHandler.
type OperatorPageData ¶
type OperatorPageData struct {
Configured bool
// Sync health (latest sweep execution, from Temporal).
SweepStatus string
SweepTime string
Mappings []MappingViewModel
ResourceKeys []ResourceKeyOption
Unlinked []UnlinkedPersonViewModel
Conflicts []ConflictViewModel
// Add-mapping form state (422 re-render path).
FieldErrors web.FieldErrors
FormValues map[string]string
Error string
}
OperatorPageData is the discourse_operator.html body payload.
type ResourceKeyOption ¶
ResourceKeyOption is a select option for the mapping form.
type StarterFunc ¶
StarterFunc starts a person's targeted reconcile. Production wraps the Temporal client (per-person workflow ID serialization); tests inject a recorder.
type UnlinkedPersonViewModel ¶
UnlinkedPersonViewModel is one entitled-but-unlinked person.
type WebhookHandler ¶
type WebhookHandler struct {
// contains filtered or unexported fields
}
WebhookHandler verifies, dedupes, and dispatches Discourse webhook events.
func NewWebhookHandler ¶
func NewWebhookHandler(cfg WebhookHandlerConfig) *WebhookHandler
NewWebhookHandler constructs the handler.
func (*WebhookHandler) RegisterRoutes ¶
func (h *WebhookHandler) RegisterRoutes(mux *http.ServeMux)
RegisterRoutes mounts the webhook endpoint.
type WebhookHandlerConfig ¶
type WebhookHandlerConfig struct {
DB *sql.DB
Logger *slog.Logger
// Secret is the shared webhook secret (discourse-webhook-secret).
Secret string
// Mode selects reverse resolution for user_created events.
Mode linkage.Mode
// Client resolves admin user records (oidc/discourseconnect reverse
// resolution).
Client *client.Client
// StartReconcile launches the targeted per-person reconcile.
StartReconcile StarterFunc
}
WebhookHandlerConfig configures the webhook endpoint.