FedWiki Workflows
This package implements Temporal workflows for FedWiki site provisioning and synchronization operations.
Overview
The FedWiki workflows handle asynchronous site management operations:
- CreateFedWikiSiteWorkflow: Creates a new FedWiki site on the farm and records it in the database
- DeleteFedWikiSiteWorkflow: Deletes a site from the farm and removes it from the database
- SyncFedWikiSitesWorkflow: Synchronizes sites from the FedWiki farm to the local database (scheduled)
Architecture
The workflow execution flow:
- HTTP Handler receives user request and starts a Temporal workflow
- Temporal Client submits the workflow to the Temporal server
- Workflow orchestrates one or more activities
- Activities perform the actual work (API calls, database operations)
- HTTP Handler waits for workflow completion and returns result to user
Available activities:
CheckQuotaActivity- Local database query to verify user quotaCreateFedWikiSiteActivity- Calls FarmManager API and records site in databaseDeleteFedWikiSiteActivity- Calls FarmManager API and removes site from databaseListFedWikiSitesActivity- Lists all sites from the FarmManager APISyncSitesToDBActivity- Synchronizes farm sites to the local database
Files
workflow.go- Workflow definitions for create/delete operationssync.go- Sync workflow and activitiesschedule.go- Temporal schedule management for periodic syncactivities.go- Activity implementationsfarmmanager_client.go- HTTP client for FarmManager API
Workflows
CreateFedWikiSiteWorkflow
Input:
type CreateFedWikiSiteWorkflowInput struct {
UserID int64 // Database user ID
Domain string // Subdomain or custom domain
OwnerName string // Display name for the owner
OwnerID string // OIDC subject (OAuth2 ID) for owner verification
IsCustomDomain bool // Whether this is a custom domain
SupportURL string // Support URL for error messages
}
Output:
type CreateFedWikiSiteWorkflowOutput struct {
Success bool // Whether creation succeeded
SiteID int64 // Database site ID (on success)
Domain string // Full domain (on success)
ErrorMessage string // User-friendly error (on failure)
}
Steps:
- CheckQuotaActivity - Verify user hasn't exceeded site limit
- CreateFedWikiSiteActivity - Call FarmManager API and record in database
Site Ownership
When a site is created, the FarmManager API creates an owner.json file in the site's
status/ directory. This file must contain the OAuth2 identity information that matches
what FedWiki's security plugin (wiki-security-passportjs) extracts from the user's login token.
owner.json format:
{
"name": "username",
"oauth2": {
"id": "oidc-subject-uuid",
"username": "username"
}
}
Important: The oauth2.id field must match the value extracted by FedWiki's
oauth2_IdField configuration. By default, member-console uses the OIDC sub claim
(a UUID), so FedWiki must be configured with:
{
"wikiDomains": {
"example.com": {
"oauth2_IdField": "token.sub",
...
}
}
}
If oauth2_IdField is set to something else (e.g., token.preferred_username), site
owners will not be recognized even if the username matches.
DeleteFedWikiSiteWorkflow
Input:
type DeleteFedWikiSiteWorkflowInput struct {
UserID int64 // Database user ID
Domain string // Full domain to delete
SupportURL string // Support URL for error messages
}
Output:
type DeleteFedWikiSiteWorkflowOutput struct {
Success bool // Whether deletion succeeded
ErrorMessage string // User-friendly error (on failure)
}
Steps:
- DeleteFedWikiSiteActivity - Call FarmManager API and remove from database
SyncFedWikiSitesWorkflow
This workflow synchronizes sites from the FedWiki farm to the local database. It runs on a configurable schedule (default: hourly) to ensure the local database reflects the current state of the farm.
Use Cases:
- Importing existing sites that were created outside the member console
- Removing sites from the database that were deleted directly on the farm
- Keeping the local database in sync with external changes
Input:
type SyncFedWikiSitesWorkflowInput struct {
DefaultUserID int64 // User ID to assign to orphaned sites (typically admin)
}
Output:
type SyncFedWikiSitesWorkflowOutput struct {
Success bool // Whether sync succeeded
SitesFound int // Number of sites found on farm
SitesAdded int // Number of sites added to database
SitesRemoved int // Number of sites removed from database
ErrorMessage string // Error message (on failure)
SyncTimestamp time.Time // When the sync completed
}
Steps:
- ListFedWikiSitesActivity - Fetch all sites from the FarmManager API
- SyncSitesToDBActivity - Compare with local database and sync changes
Sync Logic:
- Sites on farm but not in local DB → Add to local DB with
DefaultUserID - Sites in local DB but not on farm (or inactive) → Remove from local DB
- Sites in both → Update
updated_attimestamp
Scheduling
The sync workflow can be scheduled to run periodically using Temporal Schedules.
Configuration
Enable the sync schedule in your configuration:
fedwiki-sync-enabled: true
fedwiki-sync-interval: 1h # Sync interval (default: 1 hour)
fedwiki-sync-default-user-id: 1 # User ID for orphaned sites
fedwiki-sync-trigger-immediately: true # Run sync on startup
Or via command line flags:
member-console start \
--fedwiki-sync-enabled \
--fedwiki-sync-interval=30m \
--fedwiki-sync-default-user-id=1
ScheduleManager
The ScheduleManager provides programmatic control over the sync schedule:
manager := fedwiki.NewScheduleManager(temporalClient, logger)
// Create or update the schedule
err := manager.EnsureSyncSchedule(ctx, fedwiki.ScheduleConfig{
Interval: time.Hour,
DefaultUserID: 1,
TriggerImmediately: true,
})
// Pause the schedule
err := manager.PauseSyncSchedule(ctx, "Maintenance window")
// Resume the schedule
err := manager.UnpauseSyncSchedule(ctx, "Maintenance complete")
// Trigger an immediate sync
err := manager.TriggerSyncNow(ctx)
// Get schedule info
info, err := manager.GetSyncScheduleInfo(ctx)
// Delete the schedule
err := manager.DeleteSyncSchedule(ctx)
Activity Options
FedWiki API Activities
Used for external API calls with aggressive retry:
ActivityOptions{
StartToCloseTimeout: 5 * time.Minute,
RetryPolicy: &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: 5 * time.Minute,
MaximumAttempts: 8,
NonRetryableErrorTypes: []string{
"FarmManagerAPIError", // 409, 403, 401 errors
},
},
}
Local Database Activities
Used for fast local operations:
ActivityOptions{
StartToCloseTimeout: 30 * time.Second,
RetryPolicy: &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: 30 * time.Second,
MaximumAttempts: 3,
},
}
Error Handling
Non-Retryable Errors
Certain errors should not be retried because they represent permanent failures:
- 409 Conflict (Site already exists) - "A site with this name already exists. Please choose a different name."
- 403 Forbidden (Permission denied) - "You don't have permission to perform this action."
- 401 Unauthorized (Bad credentials) - "Authentication failed. Please contact support."
These are wrapped using temporal.NewNonRetryableApplicationError() with the type FarmManagerAPIError.
Retryable Errors
Network errors, 5xx server errors, and other transient failures are retried with exponential backoff.
Delete Idempotency
Delete operations treat HTTP 404 (Not Found) as success - if the site doesn't exist, the desired state has been achieved.
Synchronous Execution Pattern
Unlike typical async workflows, these workflows are executed synchronously from HTTP handlers:
// Start workflow
we, err := temporalClient.ExecuteWorkflow(ctx, options, workflow, input)
// Wait for completion (blocks until workflow finishes)
var result WorkflowOutput
err = we.Get(ctx, &result)
// Handle result
if !result.Success {
// Show error to user
}
This provides immediate feedback to users while still benefiting from Temporal's:
- Automatic retries with backoff
- Workflow history and debugging
- Graceful failure handling