Files
member-console/internal/workflows/maintenance/schedule.go
T
cgalo5758 efe3f1528d Restrict member surfaces to published products
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.
2026-08-22 12:58:05 -05:00

102 lines
4.0 KiB
Go

package maintenance
import (
"context"
"fmt"
"log/slog"
"time"
"git.coopcloud.tech/wiki-cafe/member-console/internal/workflows/queues"
"go.temporal.io/sdk/client"
)
const (
// WebhookPartitionEnsureScheduleID is the unique identifier for the
// webhook_events partition ensure schedule.
WebhookPartitionEnsureScheduleID = "webhook-partition-ensure"
// WebhookPartitionEnsureWorkflowID is the workflow ID used for
// schedule-triggered ensure runs. Schedule-spawned runs carry a
// timestamp-suffixed workflow ID on top of this base
// (design.md Risks/Trade-offs — resolve via the schedule handle, not a
// bespoke ID lookup).
WebhookPartitionEnsureWorkflowID = "webhook-partition-ensure-workflow"
// DefaultWebhookPartitionEnsureInterval is the default interval between
// scheduled ensure runs.
DefaultWebhookPartitionEnsureInterval = 24 * time.Hour
)
// ScheduleManager manages the Temporal schedule for webhook_events
// partition maintenance.
type ScheduleManager struct {
client client.Client
logger *slog.Logger
}
// NewScheduleManager creates a new ScheduleManager.
func NewScheduleManager(c client.Client, logger *slog.Logger) *ScheduleManager {
return &ScheduleManager{client: c, logger: logger}
}
// WebhookPartitionScheduleConfig holds configuration for the ensure
// schedule.
type WebhookPartitionScheduleConfig struct {
// Interval is how often to run the ensure workflow.
Interval time.Duration
// TriggerImmediately runs the ensure workflow immediately when creating
// the schedule. The webhook-partition-maintenance spec requires this
// (unlike the domains claim expiry sweep, whose boot pass is deliberately
// the only immediate run): "Recurring Temporal schedule keeps partitions
// extended" SHALL trigger one run immediately on registration.
TriggerImmediately bool
}
// EnsureWebhookPartitionSchedule creates or updates the webhook_events
// partition ensure schedule. If the schedule already exists, it updates the
// spec; otherwise it creates it (describe-then-update, mirroring
// internal/workflows/domains/schedule.go's EnsureExpirySweepSchedule).
func (m *ScheduleManager) EnsureWebhookPartitionSchedule(ctx context.Context, cfg WebhookPartitionScheduleConfig) error {
if cfg.Interval == 0 {
cfg.Interval = DefaultWebhookPartitionEnsureInterval
}
scheduleClient := m.client.ScheduleClient()
handle := scheduleClient.GetHandle(ctx, WebhookPartitionEnsureScheduleID)
if _, err := handle.Describe(ctx); err == nil {
m.logger.Info("updating existing webhook partition ensure schedule", slog.Duration("interval", cfg.Interval))
if err := handle.Update(ctx, client.ScheduleUpdateOptions{
DoUpdate: func(schedule client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) {
schedule.Description.Schedule.Spec = &client.ScheduleSpec{
Intervals: []client.ScheduleIntervalSpec{{Every: cfg.Interval}},
}
schedule.Description.Schedule.Action = &client.ScheduleWorkflowAction{
ID: WebhookPartitionEnsureWorkflowID,
Workflow: EnsureWebhookPartitionsWorkflow,
TaskQueue: queues.Main,
}
return &client.ScheduleUpdate{Schedule: &schedule.Description.Schedule}, nil
},
}); err != nil {
return fmt.Errorf("failed to update webhook partition ensure schedule: %w", err)
}
return nil
}
m.logger.Info("creating webhook partition ensure schedule",
slog.Duration("interval", cfg.Interval), slog.Bool("triggerImmediately", cfg.TriggerImmediately))
if _, err := scheduleClient.Create(ctx, client.ScheduleOptions{
ID: WebhookPartitionEnsureScheduleID,
Spec: client.ScheduleSpec{Intervals: []client.ScheduleIntervalSpec{{Every: cfg.Interval}}},
Action: &client.ScheduleWorkflowAction{
ID: WebhookPartitionEnsureWorkflowID,
Workflow: EnsureWebhookPartitionsWorkflow,
TaskQueue: queues.Main,
},
TriggerImmediately: cfg.TriggerImmediately,
}); err != nil {
return fmt.Errorf("failed to create webhook partition ensure schedule: %w", err)
}
m.logger.Info("webhook partition ensure schedule created", slog.String("scheduleID", WebhookPartitionEnsureScheduleID))
return nil
}