mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-22 21:39:08 +02:00
[client] Fix Connect/Disconnect buttons being enabled or disabled at the same time (#4711)
This commit is contained in:
+33
-50
@@ -296,6 +296,8 @@ type serviceClient struct {
|
|||||||
mExitNodeDeselectAll *systray.MenuItem
|
mExitNodeDeselectAll *systray.MenuItem
|
||||||
logFile string
|
logFile string
|
||||||
wLoginURL fyne.Window
|
wLoginURL fyne.Window
|
||||||
|
|
||||||
|
connectCancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type menuHandler struct {
|
type menuHandler struct {
|
||||||
@@ -592,17 +594,15 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) login(openURL bool) (*proto.LoginResponse, error) {
|
func (s *serviceClient) login(ctx context.Context, openURL bool) (*proto.LoginResponse, error) {
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get client: %v", err)
|
return nil, fmt.Errorf("get daemon client: %w", err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
activeProf, err := s.profileManager.GetActiveProfile()
|
activeProf, err := s.profileManager.GetActiveProfile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get active profile: %v", err)
|
return nil, fmt.Errorf("get active profile: %w", err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currUser, err := user.Current()
|
currUser, err := user.Current()
|
||||||
@@ -610,84 +610,71 @@ func (s *serviceClient) login(openURL bool) (*proto.LoginResponse, error) {
|
|||||||
return nil, fmt.Errorf("get current user: %w", err)
|
return nil, fmt.Errorf("get current user: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
loginResp, err := conn.Login(s.ctx, &proto.LoginRequest{
|
loginResp, err := conn.Login(ctx, &proto.LoginRequest{
|
||||||
IsUnixDesktopClient: runtime.GOOS == "linux" || runtime.GOOS == "freebsd",
|
IsUnixDesktopClient: runtime.GOOS == "linux" || runtime.GOOS == "freebsd",
|
||||||
ProfileName: &activeProf.Name,
|
ProfileName: &activeProf.Name,
|
||||||
Username: &currUser.Username,
|
Username: &currUser.Username,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("login to management URL with: %v", err)
|
return nil, fmt.Errorf("login to management: %w", err)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if loginResp.NeedsSSOLogin && openURL {
|
if loginResp.NeedsSSOLogin && openURL {
|
||||||
err = s.handleSSOLogin(loginResp, conn)
|
if err = s.handleSSOLogin(ctx, loginResp, conn); err != nil {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("SSO login: %w", err)
|
||||||
log.Errorf("handle SSO login failed: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return loginResp, nil
|
return loginResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) handleSSOLogin(loginResp *proto.LoginResponse, conn proto.DaemonServiceClient) error {
|
func (s *serviceClient) handleSSOLogin(ctx context.Context, loginResp *proto.LoginResponse, conn proto.DaemonServiceClient) error {
|
||||||
err := openURL(loginResp.VerificationURIComplete)
|
if err := openURL(loginResp.VerificationURIComplete); err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("open browser: %w", err)
|
||||||
log.Errorf("opening the verification uri in the browser failed: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := conn.WaitSSOLogin(s.ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode})
|
resp, err := conn.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("waiting sso login failed with: %v", err)
|
return fmt.Errorf("wait for SSO login: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Email != "" {
|
if resp.Email != "" {
|
||||||
err := s.profileManager.SetActiveProfileState(&profilemanager.ProfileState{
|
if err := s.profileManager.SetActiveProfileState(&profilemanager.ProfileState{
|
||||||
Email: resp.Email,
|
Email: resp.Email,
|
||||||
})
|
}); err != nil {
|
||||||
if err != nil {
|
log.Debugf("failed to set profile state: %v", err)
|
||||||
log.Warnf("failed to set profile state: %v", err)
|
|
||||||
} else {
|
} else {
|
||||||
s.mProfile.refresh()
|
s.mProfile.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) menuUpClick() error {
|
func (s *serviceClient) menuUpClick(ctx context.Context) error {
|
||||||
systray.SetTemplateIcon(iconConnectingMacOS, s.icConnecting)
|
systray.SetTemplateIcon(iconConnectingMacOS, s.icConnecting)
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
systray.SetTemplateIcon(iconErrorMacOS, s.icError)
|
systray.SetTemplateIcon(iconErrorMacOS, s.icError)
|
||||||
log.Errorf("get client: %v", err)
|
return fmt.Errorf("get daemon client: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.login(true)
|
_, err = s.login(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("login failed with: %v", err)
|
return fmt.Errorf("login: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
|
status, err := conn.Status(ctx, &proto.StatusRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get service status: %v", err)
|
return fmt.Errorf("get status: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Status == string(internal.StatusConnected) {
|
if status.Status == string(internal.StatusConnected) {
|
||||||
log.Warnf("already connected")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.conn.Up(s.ctx, &proto.UpRequest{}); err != nil {
|
if _, err := conn.Up(ctx, &proto.UpRequest{}); err != nil {
|
||||||
log.Errorf("up service: %v", err)
|
return fmt.Errorf("start connection: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -697,24 +684,20 @@ func (s *serviceClient) menuDownClick() error {
|
|||||||
systray.SetTemplateIcon(iconConnectingMacOS, s.icConnecting)
|
systray.SetTemplateIcon(iconConnectingMacOS, s.icConnecting)
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get client: %v", err)
|
return fmt.Errorf("get daemon client: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
|
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get service status: %v", err)
|
return fmt.Errorf("get status: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Status != string(internal.StatusConnected) && status.Status != string(internal.StatusConnecting) {
|
if status.Status != string(internal.StatusConnected) && status.Status != string(internal.StatusConnecting) {
|
||||||
log.Warnf("already down")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.conn.Down(s.ctx, &proto.DownRequest{}); err != nil {
|
if _, err := conn.Down(s.ctx, &proto.DownRequest{}); err != nil {
|
||||||
log.Errorf("down service: %v", err)
|
return fmt.Errorf("stop connection: %w", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1381,7 +1364,7 @@ func (s *serviceClient) showLoginURL() context.CancelFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := s.login(false)
|
resp, err := s.login(ctx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to fetch login URL: %v", err)
|
log.Errorf("failed to fetch login URL: %v", err)
|
||||||
return
|
return
|
||||||
@@ -1401,7 +1384,7 @@ func (s *serviceClient) showLoginURL() context.CancelFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.WaitSSOLogin(s.ctx, &proto.WaitSSOLoginRequest{UserCode: resp.UserCode})
|
_, err = conn.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: resp.UserCode})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Waiting sso login failed with: %v", err)
|
log.Errorf("Waiting sso login failed with: %v", err)
|
||||||
label.SetText("Waiting login failed, please create \na debug bundle in the settings and contact support.")
|
label.SetText("Waiting login failed, please create \na debug bundle in the settings and contact support.")
|
||||||
@@ -1409,7 +1392,7 @@ func (s *serviceClient) showLoginURL() context.CancelFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
label.SetText("Re-authentication successful.\nReconnecting")
|
label.SetText("Re-authentication successful.\nReconnecting")
|
||||||
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
|
status, err := conn.Status(ctx, &proto.StatusRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("get service status: %v", err)
|
log.Errorf("get service status: %v", err)
|
||||||
return
|
return
|
||||||
@@ -1422,7 +1405,7 @@ func (s *serviceClient) showLoginURL() context.CancelFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.Up(s.ctx, &proto.UpRequest{})
|
_, err = conn.Up(ctx, &proto.UpRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
label.SetText("Reconnecting failed, please create \na debug bundle in the settings and contact support.")
|
label.SetText("Reconnecting failed, please create \na debug bundle in the settings and contact support.")
|
||||||
log.Errorf("Reconnecting failed with: %v", err)
|
log.Errorf("Reconnecting failed with: %v", err)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/systray"
|
"fyne.io/systray"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
@@ -67,20 +69,55 @@ func (h *eventHandler) listen(ctx context.Context) {
|
|||||||
|
|
||||||
func (h *eventHandler) handleConnectClick() {
|
func (h *eventHandler) handleConnectClick() {
|
||||||
h.client.mUp.Disable()
|
h.client.mUp.Disable()
|
||||||
|
|
||||||
|
if h.client.connectCancel != nil {
|
||||||
|
h.client.connectCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectCtx, connectCancel := context.WithCancel(h.client.ctx)
|
||||||
|
h.client.connectCancel = connectCancel
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer h.client.mUp.Enable()
|
defer connectCancel()
|
||||||
if err := h.client.menuUpClick(); err != nil {
|
|
||||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird service"))
|
if err := h.client.menuUpClick(connectCtx); err != nil {
|
||||||
|
st, ok := status.FromError(err)
|
||||||
|
if errors.Is(err, context.Canceled) || (ok && st.Code() == codes.Canceled) {
|
||||||
|
log.Debugf("connect operation cancelled by user")
|
||||||
|
} else {
|
||||||
|
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to connect"))
|
||||||
|
log.Errorf("connect failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.client.updateStatus(); err != nil {
|
||||||
|
log.Debugf("failed to update status after connect: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *eventHandler) handleDisconnectClick() {
|
func (h *eventHandler) handleDisconnectClick() {
|
||||||
h.client.mDown.Disable()
|
h.client.mDown.Disable()
|
||||||
|
|
||||||
|
if h.client.connectCancel != nil {
|
||||||
|
log.Debugf("cancelling ongoing connect operation")
|
||||||
|
h.client.connectCancel()
|
||||||
|
h.client.connectCancel = nil
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer h.client.mDown.Enable()
|
|
||||||
if err := h.client.menuDownClick(); err != nil {
|
if err := h.client.menuDownClick(); err != nil {
|
||||||
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird daemon"))
|
st, ok := status.FromError(err)
|
||||||
|
if !errors.Is(err, context.Canceled) && !(ok && st.Code() == codes.Canceled) {
|
||||||
|
h.client.app.SendNotification(fyne.NewNotification("Error", "Failed to disconnect"))
|
||||||
|
log.Errorf("disconnect failed: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("disconnect cancelled or already disconnecting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.client.updateStatus(); err != nil {
|
||||||
|
log.Debugf("failed to update status after disconnect: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -245,6 +282,6 @@ func (h *eventHandler) logout(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.client.getSrvConfig()
|
h.client.getSrvConfig()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-3
@@ -387,6 +387,7 @@ type subItem struct {
|
|||||||
type profileMenu struct {
|
type profileMenu struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
serviceClient *serviceClient
|
||||||
profileManager *profilemanager.ProfileManager
|
profileManager *profilemanager.ProfileManager
|
||||||
eventHandler *eventHandler
|
eventHandler *eventHandler
|
||||||
profileMenuItem *systray.MenuItem
|
profileMenuItem *systray.MenuItem
|
||||||
@@ -396,7 +397,7 @@ type profileMenu struct {
|
|||||||
logoutSubItem *subItem
|
logoutSubItem *subItem
|
||||||
profilesState []Profile
|
profilesState []Profile
|
||||||
downClickCallback func() error
|
downClickCallback func() error
|
||||||
upClickCallback func() error
|
upClickCallback func(context.Context) error
|
||||||
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
||||||
loadSettingsCallback func()
|
loadSettingsCallback func()
|
||||||
app fyne.App
|
app fyne.App
|
||||||
@@ -404,12 +405,13 @@ type profileMenu struct {
|
|||||||
|
|
||||||
type newProfileMenuArgs struct {
|
type newProfileMenuArgs struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
serviceClient *serviceClient
|
||||||
profileManager *profilemanager.ProfileManager
|
profileManager *profilemanager.ProfileManager
|
||||||
eventHandler *eventHandler
|
eventHandler *eventHandler
|
||||||
profileMenuItem *systray.MenuItem
|
profileMenuItem *systray.MenuItem
|
||||||
emailMenuItem *systray.MenuItem
|
emailMenuItem *systray.MenuItem
|
||||||
downClickCallback func() error
|
downClickCallback func() error
|
||||||
upClickCallback func() error
|
upClickCallback func(context.Context) error
|
||||||
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
getSrvClientCallback func(timeout time.Duration) (proto.DaemonServiceClient, error)
|
||||||
loadSettingsCallback func()
|
loadSettingsCallback func()
|
||||||
app fyne.App
|
app fyne.App
|
||||||
@@ -418,6 +420,7 @@ type newProfileMenuArgs struct {
|
|||||||
func newProfileMenu(args newProfileMenuArgs) *profileMenu {
|
func newProfileMenu(args newProfileMenuArgs) *profileMenu {
|
||||||
p := profileMenu{
|
p := profileMenu{
|
||||||
ctx: args.ctx,
|
ctx: args.ctx,
|
||||||
|
serviceClient: args.serviceClient,
|
||||||
profileManager: args.profileManager,
|
profileManager: args.profileManager,
|
||||||
eventHandler: args.eventHandler,
|
eventHandler: args.eventHandler,
|
||||||
profileMenuItem: args.profileMenuItem,
|
profileMenuItem: args.profileMenuItem,
|
||||||
@@ -569,10 +572,19 @@ func (p *profileMenu) refresh() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.upClickCallback(); err != nil {
|
if p.serviceClient.connectCancel != nil {
|
||||||
|
p.serviceClient.connectCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
connectCtx, connectCancel := context.WithCancel(p.ctx)
|
||||||
|
p.serviceClient.connectCancel = connectCancel
|
||||||
|
|
||||||
|
if err := p.upClickCallback(connectCtx); err != nil {
|
||||||
log.Errorf("failed to handle up click after switching profile: %v", err)
|
log.Errorf("failed to handle up click after switching profile: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectCancel()
|
||||||
|
|
||||||
p.refresh()
|
p.refresh()
|
||||||
p.loadSettingsCallback()
|
p.loadSettingsCallback()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user