package app import ( "context" "encoding/json" "fmt" "strings" "time" "go.uber.org/zap" "github.com/geo-platform/tenant-api/internal/shared/config" "github.com/geo-platform/tenant-api/internal/shared/messaging/rabbitmq" "github.com/geo-platform/tenant-api/internal/shared/stream" ) func publishDesktopDispatchEvent( ctx context.Context, client *rabbitmq.Client, logger *zap.Logger, event stream.DesktopDispatchEvent, ) error { if client == nil || strings.TrimSpace(event.TargetClientID) == "" { return nil } routingKey := desktopDispatchRoutingKey(client.Config(), event.Kind, event.Lane, event.TargetClientID) body, err := json.Marshal(event) if err != nil { return err } if err := client.PublishDesktopDispatch(ctx, routingKey, body); err != nil { return err } return nil } func desktopDispatchRoutingKey(cfg config.RabbitMQConfig, kind, lane, targetClientID string) string { prefix := strings.TrimSpace(cfg.DesktopDispatchPublishPrefix) if strings.TrimSpace(kind) == "monitor" { prefix = strings.TrimSpace(cfg.DesktopDispatchMonitorPrefix) } if prefix == "" { if strings.TrimSpace(kind) == "monitor" { prefix = "monitor" } else { prefix = "publish" } } if strings.TrimSpace(kind) == "monitor" && strings.TrimSpace(lane) != "" { return fmt.Sprintf("%s.%s.%s", prefix, monitorDispatchLaneSegment(lane), targetClientID) } if strings.TrimSpace(kind) == "monitor" { return fmt.Sprintf("%s.normal.%s", prefix, targetClientID) } return fmt.Sprintf("%s.%s", prefix, targetClientID) } func monitorDispatchLaneSegment(lane string) string { switch strings.TrimSpace(lane) { case "high": return "high" case "retry": return "retry" default: return "normal" } } func publishMonitorDispatchSignal( ctx context.Context, client *rabbitmq.Client, logger *zap.Logger, workspaceID int64, targetClientID string, lane string, priority int, interruptGeneration int, ) { event := stream.DesktopDispatchEvent{ Type: "task_available", TaskID: "", JobID: "", WorkspaceID: workspaceID, TargetClientID: targetClientID, Status: "queued", Kind: "monitor", Priority: priority, Lane: strings.TrimSpace(lane), InterruptGeneration: interruptGeneration, SignalOnly: true, UpdatedAt: time.Now().UTC(), } if err := publishDesktopDispatchEvent(ctx, client, logger, event); err != nil && logger != nil { logger.Warn("monitor dispatch signal publish failed", zap.String("target_client_id", targetClientID), zap.String("lane", lane), zap.Error(err), ) } } func publishMonitorControlEvent( ctx context.Context, client *rabbitmq.Client, logger *zap.Logger, workspaceID int64, targetClientID string, taskID int64, reason string, interruptGeneration int, ) { event := stream.DesktopDispatchEvent{ Type: "task_control", TaskID: fmt.Sprintf("%d", taskID), JobID: "", WorkspaceID: workspaceID, TargetClientID: targetClientID, Status: "queued", Kind: "monitor", Lane: "high", InterruptGeneration: interruptGeneration, Control: "interrupt_requested", Reason: reason, UpdatedAt: time.Now().UTC(), } if err := publishDesktopDispatchEvent(ctx, client, logger, event); err != nil && logger != nil { logger.Warn("monitor control event publish failed", zap.String("target_client_id", targetClientID), zap.Int64("task_id", taskID), zap.Error(err), ) } }