Introduce a commercial license option alongside AGPL-3.0-only, require a CLA for contributors, and document the terms in COMMERCIAL.md and NOTICE. Add a script to stamp SPDX headers on Go files and apply it across the tree.
91 lines
3.2 KiB
Go
91 lines
3.2 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package billing
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/workflows/queues"
|
|
"go.temporal.io/sdk/client"
|
|
)
|
|
|
|
const (
|
|
// SweepScheduleID is the unique identifier for the scheduled-change sweep schedule.
|
|
SweepScheduleID = "billing-scheduled-change-sweep"
|
|
// SweepWorkflowID is the workflow ID used for sweep executions.
|
|
SweepWorkflowID = "billing-scheduled-change-sweep-workflow"
|
|
// DefaultSweepInterval is the default interval between sweeps.
|
|
DefaultSweepInterval = 1 * time.Hour
|
|
)
|
|
|
|
// ScheduleManager manages Temporal schedules for billing 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}
|
|
}
|
|
|
|
// SweepScheduleConfig holds configuration for the sweep schedule.
|
|
type SweepScheduleConfig struct {
|
|
// Interval is how often to run the sweep.
|
|
Interval time.Duration
|
|
// TriggerImmediately runs the sweep immediately when creating the schedule.
|
|
TriggerImmediately bool
|
|
}
|
|
|
|
// EnsureSweepSchedule creates or updates the scheduled-change sweep schedule.
|
|
// If the schedule already exists, it updates the spec; otherwise it creates it.
|
|
func (m *ScheduleManager) EnsureSweepSchedule(ctx context.Context, cfg SweepScheduleConfig) error {
|
|
if cfg.Interval == 0 {
|
|
cfg.Interval = DefaultSweepInterval
|
|
}
|
|
|
|
scheduleClient := m.client.ScheduleClient()
|
|
|
|
handle := scheduleClient.GetHandle(ctx, SweepScheduleID)
|
|
if _, err := handle.Describe(ctx); err == nil {
|
|
m.logger.Info("updating existing billing sweep 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: SweepWorkflowID,
|
|
Workflow: SweepScheduledChangesWorkflow,
|
|
TaskQueue: queues.Main,
|
|
}
|
|
return &client.ScheduleUpdate{Schedule: &schedule.Description.Schedule}, nil
|
|
},
|
|
}); err != nil {
|
|
return fmt.Errorf("failed to update sweep schedule: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
m.logger.Info("creating billing sweep schedule",
|
|
slog.Duration("interval", cfg.Interval), slog.Bool("triggerImmediately", cfg.TriggerImmediately))
|
|
if _, err := scheduleClient.Create(ctx, client.ScheduleOptions{
|
|
ID: SweepScheduleID,
|
|
Spec: client.ScheduleSpec{Intervals: []client.ScheduleIntervalSpec{{Every: cfg.Interval}}},
|
|
Action: &client.ScheduleWorkflowAction{
|
|
ID: SweepWorkflowID,
|
|
Workflow: SweepScheduledChangesWorkflow,
|
|
TaskQueue: queues.Main,
|
|
},
|
|
TriggerImmediately: cfg.TriggerImmediately,
|
|
}); err != nil {
|
|
return fmt.Errorf("failed to create sweep schedule: %w", err)
|
|
}
|
|
m.logger.Info("billing sweep schedule created", slog.String("scheduleID", SweepScheduleID))
|
|
return nil
|
|
}
|