package bridge import ( "fmt" "sync" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/logger" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/model" ) // Manager manages multiple bridge instances type Manager struct { bridges map[string]model.Bridge mu sync.RWMutex logger logger.Logger } // NewManager creates a new bridge manager func NewManager(logger logger.Logger) model.BridgeManager { if logger == nil { panic("logger cannot be nil") } return &Manager{ bridges: make(map[string]model.Bridge), logger: logger, } } // RegisterBridge registers a bridge with the manager func (m *Manager) RegisterBridge(name string, bridge model.Bridge) error { if name == "" { return fmt.Errorf("bridge name cannot be empty") } if bridge == nil { return fmt.Errorf("bridge cannot be nil") } m.mu.Lock() defer m.mu.Unlock() if _, exists := m.bridges[name]; exists { return fmt.Errorf("bridge '%s' is already registered", name) } m.bridges[name] = bridge m.logger.LogInfo("Bridge registered", "name", name) return nil } // StartBridge starts a specific bridge func (m *Manager) StartBridge(name string) error { m.mu.RLock() bridge, exists := m.bridges[name] m.mu.RUnlock() if !exists { return fmt.Errorf("bridge '%s' is not registered", name) } m.logger.LogInfo("Starting bridge", "name", name) if err := bridge.Start(); err != nil { m.logger.LogError("Failed to start bridge", "name", name, "error", err) return fmt.Errorf("failed to start bridge '%s': %w", name, err) } m.logger.LogInfo("Bridge started successfully", "name", name) return nil } // StopBridge stops a specific bridge func (m *Manager) StopBridge(name string) error { m.mu.RLock() bridge, exists := m.bridges[name] m.mu.RUnlock() if !exists { return fmt.Errorf("bridge '%s' is not registered", name) } m.logger.LogInfo("Stopping bridge", "name", name) if err := bridge.Stop(); err != nil { m.logger.LogError("Failed to stop bridge", "name", name, "error", err) return fmt.Errorf("failed to stop bridge '%s': %w", name, err) } m.logger.LogInfo("Bridge stopped successfully", "name", name) return nil } // UnregisterBridge removes a bridge from the manager func (m *Manager) UnregisterBridge(name string) error { m.mu.Lock() defer m.mu.Unlock() bridge, exists := m.bridges[name] if !exists { return fmt.Errorf("bridge '%s' is not registered", name) } // Stop the bridge before unregistering if bridge.IsConnected() { if err := bridge.Stop(); err != nil { m.logger.LogWarn("Failed to stop bridge during unregistration", "name", name, "error", err) } } delete(m.bridges, name) m.logger.LogInfo("Bridge unregistered", "name", name) return nil } // GetBridge retrieves a bridge by name func (m *Manager) GetBridge(name string) (model.Bridge, error) { m.mu.RLock() defer m.mu.RUnlock() bridge, exists := m.bridges[name] if !exists { return nil, fmt.Errorf("bridge '%s' is not registered", name) } return bridge, nil } // ListBridges returns a list of all registered bridge names func (m *Manager) ListBridges() []string { m.mu.RLock() defer m.mu.RUnlock() bridges := make([]string, 0, len(m.bridges)) for name := range m.bridges { bridges = append(bridges, name) } return bridges } // HasBridge checks if a bridge with the given name is registered func (m *Manager) HasBridge(name string) bool { m.mu.RLock() defer m.mu.RUnlock() _, exists := m.bridges[name] return exists } // HasBridges checks if any bridges are registered func (m *Manager) HasBridges() bool { m.mu.RLock() defer m.mu.RUnlock() return len(m.bridges) > 0 } // Shutdown stops and unregisters all bridges func (m *Manager) Shutdown() error { m.mu.Lock() defer m.mu.Unlock() m.logger.LogInfo("Shutting down bridge manager", "bridge_count", len(m.bridges)) var errors []error for name, bridge := range m.bridges { if bridge.IsConnected() { if err := bridge.Stop(); err != nil { errors = append(errors, fmt.Errorf("failed to stop bridge '%s': %w", name, err)) m.logger.LogError("Failed to stop bridge during shutdown", "name", name, "error", err) } } } // Clear all bridges m.bridges = make(map[string]model.Bridge) m.logger.LogInfo("Bridge manager shutdown complete") if len(errors) > 0 { return fmt.Errorf("shutdown completed with errors: %v", errors) } return nil } // OnPluginConfigurationChange propagates configuration changes to all registered bridges func (m *Manager) OnPluginConfigurationChange(config any) error { m.mu.RLock() defer m.mu.RUnlock() if len(m.bridges) == 0 { return nil } m.logger.LogInfo("Plugin configuration changed, propagating to bridges", "bridge_count", len(m.bridges)) var errors []error for name, bridge := range m.bridges { if err := bridge.UpdateConfiguration(config); err != nil { errors = append(errors, fmt.Errorf("failed to update configuration for bridge '%s': %w", name, err)) m.logger.LogError("Failed to update bridge configuration", "name", name, "error", err) } else { m.logger.LogDebug("Successfully updated bridge configuration", "name", name) } } if len(errors) > 0 { return fmt.Errorf("configuration update completed with errors: %v", errors) } m.logger.LogInfo("Configuration changes propagated to all bridges") return nil } // OnChannelMappingCreated handles the creation of a channel mapping by calling the appropriate bridge func (m *Manager) OnChannelMappingCreated(channelID, bridgeName, bridgeRoomID string) error { // Input validation if channelID == "" { return fmt.Errorf("channelID cannot be empty") } if bridgeName == "" { return fmt.Errorf("bridgeName cannot be empty") } if bridgeRoomID == "" { return fmt.Errorf("bridgeRoomID cannot be empty") } m.logger.LogDebug("Creating channel mapping", "channel_id", channelID, "bridge_name", bridgeName, "bridge_room_id", bridgeRoomID) // Get the specific bridge bridge, err := m.GetBridge(bridgeName) if err != nil { m.logger.LogError("Failed to get bridge", "bridge_name", bridgeName, "error", err) return fmt.Errorf("failed to get bridge '%s': %w", bridgeName, err) } // Check if bridge is connected if !bridge.IsConnected() { return fmt.Errorf("bridge '%s' is not connected", bridgeName) } // Create the channel mapping on the receiving bridge if err = bridge.CreateChannelMapping(channelID, bridgeRoomID); err != nil { m.logger.LogError("Failed to create channel mapping", "channel_id", channelID, "bridge_name", bridgeName, "bridge_room_id", bridgeRoomID, "error", err) return fmt.Errorf("failed to create channel mapping for bridge '%s': %w", bridgeName, err) } mattermostBridge, err := m.GetBridge("mattermost") if err != nil { m.logger.LogError("Failed to get Mattermost bridge", "error", err) return fmt.Errorf("failed to get Mattermost bridge: %w", err) } // Create the channel mapping in the Mattermost bridge if err = mattermostBridge.CreateChannelMapping(channelID, bridgeRoomID); err != nil { m.logger.LogError("Failed to create channel mapping in Mattermost bridge", "channel_id", channelID, "bridge_name", bridgeName, "bridge_room_id", bridgeRoomID, "error", err) return fmt.Errorf("failed to create channel mapping in Mattermost bridge: %w", err) } m.logger.LogInfo("Successfully created channel mapping", "channel_id", channelID, "bridge_name", bridgeName, "bridge_room_id", bridgeRoomID) return nil } // OnChannelMappingDeleted handles the deletion of a channel mapping by calling the appropriate bridges func (m *Manager) OnChannelMappingDeleted(channelID, bridgeName string) error { // Input validation if channelID == "" { return fmt.Errorf("channelID cannot be empty") } if bridgeName == "" { return fmt.Errorf("bridgeName cannot be empty") } m.logger.LogDebug("Deleting channel mapping", "channel_id", channelID, "bridge_name", bridgeName) // Get the specific bridge bridge, err := m.GetBridge(bridgeName) if err != nil { m.logger.LogError("Failed to get bridge", "bridge_name", bridgeName, "error", err) return fmt.Errorf("failed to get bridge '%s': %w", bridgeName, err) } // Check if bridge is connected if !bridge.IsConnected() { return fmt.Errorf("bridge '%s' is not connected", bridgeName) } // Delete the channel mapping from the specific bridge if err = bridge.DeleteChannelMapping(channelID); err != nil { m.logger.LogError("Failed to delete channel mapping", "channel_id", channelID, "bridge_name", bridgeName, "error", err) return fmt.Errorf("failed to delete channel mapping for bridge '%s': %w", bridgeName, err) } // Also delete from Mattermost bridge to clean up reverse mappings mattermostBridge, err := m.GetBridge("mattermost") if err != nil { m.logger.LogError("Failed to get Mattermost bridge", "error", err) return fmt.Errorf("failed to get Mattermost bridge: %w", err) } // Delete the channel mapping from the Mattermost bridge if err = mattermostBridge.DeleteChannelMapping(channelID); err != nil { m.logger.LogError("Failed to delete channel mapping from Mattermost bridge", "channel_id", channelID, "bridge_name", bridgeName, "error", err) return fmt.Errorf("failed to delete channel mapping from Mattermost bridge: %w", err) } m.logger.LogInfo("Successfully deleted channel mapping", "channel_id", channelID, "bridge_name", bridgeName) return nil }