Add lifecycle_status = 'published' to the public-catalog queries (plans and add-ons listings) and reject checkout before any Stripe call unless the product behind the price clears the shared member gate (published + active + public). The currently-enrolled ladder rung stays renderable even if its product is later drafted or retired, fetched directly so members keep seeing what they are on. Introduce a single evaluateMemberGate definition shared by the catalog paths and the operator readiness panel so the surfaces cannot disagree about what is publishable for members.
Temporal Workflows
This directory contains the Temporal workflow definitions, activity implementations, and worker configuration for the member-console application.
Structure: Domain-Based
We use a Domain-Based (or Feature-Based) structure. Instead of grouping all workflows in one folder and all activities in another, we group them by business domain.
internal/workflows/
├── client.go # Temporal client factory
├── worker.go # Central worker setup & registration
├── queues/
│ └── queues.go # Task Queue constants
├── common/ # Shared utilities & generic activities
│ └── options.go # Standard ActivityOptions (retries, timeouts)
├── example/ # "Example" Domain
│ ├── workflow.go # Workflow definition
│ └── activities.go # Domain-specific activities
├── fedwiki/ # "FedWiki" Domain - Site provisioning
│ ├── README.md # Domain documentation
│ ├── FARMMANAGER.md # FarmManager API documentation
│ ├── workflow.go # Create/Delete site workflows
│ ├── activities.go # Site management activities
│ └── farmmanager_client.go # HTTP client for FarmManager API
└── onboarding/ # (Future) "Onboarding" Domain
├── workflow.go
└── activities.go
Adding a New Domain
- Create a directory:
internal/workflows/<domain_name>/ - Define the Workflow: Create
workflow.goin that directory. - Define Activities: Create
activities.goin that directory.- Define a struct (e.g.,
Activities) to hold dependencies (DB, Logger). - Implement a
NewActivitiesconstructor.
- Define a struct (e.g.,
- Register: Go to
internal/workflows/worker.goand add your new domain to theNewWorkerfunction:
// internal/workflows/worker.go
// ... inside NewWorker ...
w.RegisterWorkflow(yourdomain.Workflow)
w.RegisterActivity(yourdomain.NewActivities(cfg.DB, cfg.Logger))
Usage
Starting the Worker
The worker is embedded in the main application but can be extracted later.
import "git.coopcloud.tech/wiki-cafe/member-console/internal/workflows"
// ... in your main setup ...
workerCfg := workflows.DefaultWorkerConfig(database, logger)
worker, err := workflows.NewWorker(temporalClient, workerCfg)
if err := worker.Start(); err != nil {
// handle error
}
defer worker.Stop()
Starting a Workflow
import (
"git.coopcloud.tech/wiki-cafe/member-console/internal/workflows"
"git.coopcloud.tech/wiki-cafe/member-console/internal/workflows/queues"
"git.coopcloud.tech/wiki-cafe/member-console/internal/workflows/example"
)
// ... inside an HTTP handler ...
workflowOpts := client.StartWorkflowOptions{
ID: "example-" + uuid.New().String(),
TaskQueue: queues.Main,
}
we, err := temporalClient.ExecuteWorkflow(
ctx,
workflowOpts,
example.Workflow, // Pass the function reference
example.Input{Message: "hello"},
)