mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-13 20:29:55 +00:00
Down and Up(async=true) are both fast RPCs; no background goroutine is needed. SwitchActive is now fully synchronous — the tray wraps the call in its own goroutine, and Wails handles React calls similarly.
83 lines
3.4 KiB
Go
83 lines
3.4 KiB
Go
//go:build !android && !ios && !freebsd && !js
|
|
|
|
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ProfileSwitcher encapsulates the full profile-switching reconnect policy so
|
|
// both the tray and the React frontend use identical logic.
|
|
//
|
|
// Reconnect policy:
|
|
//
|
|
// ┌─────────────────┬──────────────────────┬────────────────────────────────────┐
|
|
// │ Previous status │ Action │ Rationale │
|
|
// ├─────────────────┼──────────────────────┼────────────────────────────────────┤
|
|
// │ Connected │ Switch + Down + Up │ Reconnect with the new profile. │
|
|
// │ Connecting │ Switch + Down + Up │ Stop old retry loop, restart. │
|
|
// │ NeedsLogin │ Switch + Down │ Clear stale error; user logs in. │
|
|
// │ LoginFailed │ Switch + Down │ Clear stale error; user logs in. │
|
|
// │ SessionExpired │ Switch + Down │ Clear stale error; user logs in. │
|
|
// │ Idle │ Switch only │ User chose offline; don't connect. │
|
|
// └─────────────────┴──────────────────────┴────────────────────────────────────┘
|
|
type ProfileSwitcher struct {
|
|
profiles *Profiles
|
|
connection *Connection
|
|
peers *Peers
|
|
}
|
|
|
|
// NewProfileSwitcher creates a ProfileSwitcher backed by the given services.
|
|
func NewProfileSwitcher(profiles *Profiles, connection *Connection, peers *Peers) *ProfileSwitcher {
|
|
return &ProfileSwitcher{profiles: profiles, connection: connection, peers: peers}
|
|
}
|
|
|
|
// SwitchActive switches to the named profile applying the reconnect policy.
|
|
// All RPCs complete quickly: Up uses async mode so the daemon starts the
|
|
// connection attempt and returns immediately; status updates flow via the
|
|
// SubscribeStatus stream.
|
|
func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error {
|
|
prevStatus := ""
|
|
if st, err := s.peers.Get(ctx); err == nil {
|
|
prevStatus = st.Status
|
|
} else {
|
|
log.Warnf("profileswitcher: get status: %v", err)
|
|
}
|
|
|
|
wasActive := strings.EqualFold(prevStatus, StatusConnected) ||
|
|
strings.EqualFold(prevStatus, StatusConnecting)
|
|
needsDown := wasActive ||
|
|
strings.EqualFold(prevStatus, StatusNeedsLogin) ||
|
|
strings.EqualFold(prevStatus, StatusLoginFailed) ||
|
|
strings.EqualFold(prevStatus, StatusSessionExpired)
|
|
|
|
log.Infof("profileswitcher: switch profile=%q prevStatus=%q wasActive=%v needsDown=%v",
|
|
p.ProfileName, prevStatus, wasActive, needsDown)
|
|
|
|
if err := s.profiles.Switch(ctx, p); err != nil {
|
|
return fmt.Errorf("switch profile %q: %w", p.ProfileName, err)
|
|
}
|
|
|
|
if needsDown {
|
|
if err := s.connection.Down(ctx); err != nil {
|
|
log.Errorf("profileswitcher: Down: %v", err)
|
|
}
|
|
}
|
|
|
|
if wasActive {
|
|
if err := s.connection.Up(ctx, UpParams{
|
|
ProfileName: p.ProfileName,
|
|
Username: p.Username,
|
|
Async: true,
|
|
}); err != nil {
|
|
return fmt.Errorf("reconnect %q: %w", p.ProfileName, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|