mirror of
https://github.com/netbirdio/netbird.git
synced 2026-06-03 06:29:54 +00:00
Compare commits
1 Commits
feat/sync-
...
quick-seti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fadbeb769 |
@@ -85,21 +85,22 @@ func main() {
|
||||
|
||||
// Create the service client (this also builds the settings or networks UI if requested).
|
||||
client := newServiceClient(&newServiceClientArgs{
|
||||
addr: flags.daemonAddr,
|
||||
logFile: logFile,
|
||||
app: a,
|
||||
showSettings: flags.showSettings,
|
||||
showNetworks: flags.showNetworks,
|
||||
showLoginURL: flags.showLoginURL,
|
||||
showDebug: flags.showDebug,
|
||||
showProfiles: flags.showProfiles,
|
||||
addr: flags.daemonAddr,
|
||||
logFile: logFile,
|
||||
app: a,
|
||||
showSettings: flags.showSettings,
|
||||
showNetworks: flags.showNetworks,
|
||||
showLoginURL: flags.showLoginURL,
|
||||
showDebug: flags.showDebug,
|
||||
showProfiles: flags.showProfiles,
|
||||
showQuickActions: flags.showQuickActions,
|
||||
})
|
||||
|
||||
// Watch for theme/settings changes to update the icon.
|
||||
go watchSettingsChanges(a, client)
|
||||
|
||||
// Run in window mode if any UI flag was set.
|
||||
if flags.showSettings || flags.showNetworks || flags.showDebug || flags.showLoginURL || flags.showProfiles {
|
||||
if flags.showSettings || flags.showNetworks || flags.showDebug || flags.showLoginURL || flags.showProfiles || flags.showQuickActions {
|
||||
a.Run()
|
||||
return
|
||||
}
|
||||
@@ -111,23 +112,29 @@ func main() {
|
||||
return
|
||||
}
|
||||
if running {
|
||||
log.Warnf("another process is running with pid %d, exiting", pid)
|
||||
log.Infof("another process is running with pid %d, sending signal to show window", pid)
|
||||
if err := sendShowWindowSignal(pid); err != nil {
|
||||
log.Errorf("send signal to running instance: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
client.setupSignalHandler(client.ctx)
|
||||
|
||||
client.setDefaultFonts()
|
||||
systray.Run(client.onTrayReady, client.onTrayExit)
|
||||
}
|
||||
|
||||
type cliFlags struct {
|
||||
daemonAddr string
|
||||
showSettings bool
|
||||
showNetworks bool
|
||||
showProfiles bool
|
||||
showDebug bool
|
||||
showLoginURL bool
|
||||
errorMsg string
|
||||
saveLogsInFile bool
|
||||
daemonAddr string
|
||||
showSettings bool
|
||||
showNetworks bool
|
||||
showProfiles bool
|
||||
showDebug bool
|
||||
showLoginURL bool
|
||||
showQuickActions bool
|
||||
errorMsg string
|
||||
saveLogsInFile bool
|
||||
}
|
||||
|
||||
// parseFlags reads and returns all needed command-line flags.
|
||||
@@ -143,6 +150,7 @@ func parseFlags() *cliFlags {
|
||||
flag.BoolVar(&flags.showNetworks, "networks", false, "run networks window")
|
||||
flag.BoolVar(&flags.showProfiles, "profiles", false, "run profiles window")
|
||||
flag.BoolVar(&flags.showDebug, "debug", false, "run debug window")
|
||||
flag.BoolVar(&flags.showQuickActions, "quick-actions", false, "run quick actions window")
|
||||
flag.StringVar(&flags.errorMsg, "error-msg", "", "displays an error message window")
|
||||
flag.BoolVar(&flags.saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", os.TempDir()))
|
||||
flag.BoolVar(&flags.showLoginURL, "login-url", false, "show login URL in a popup window")
|
||||
@@ -287,6 +295,7 @@ type serviceClient struct {
|
||||
showNetworks bool
|
||||
wNetworks fyne.Window
|
||||
wProfiles fyne.Window
|
||||
wQuickActions fyne.Window
|
||||
|
||||
eventManager *event.Manager
|
||||
|
||||
@@ -304,14 +313,15 @@ type menuHandler struct {
|
||||
}
|
||||
|
||||
type newServiceClientArgs struct {
|
||||
addr string
|
||||
logFile string
|
||||
app fyne.App
|
||||
showSettings bool
|
||||
showNetworks bool
|
||||
showDebug bool
|
||||
showLoginURL bool
|
||||
showProfiles bool
|
||||
addr string
|
||||
logFile string
|
||||
app fyne.App
|
||||
showSettings bool
|
||||
showNetworks bool
|
||||
showDebug bool
|
||||
showLoginURL bool
|
||||
showProfiles bool
|
||||
showQuickActions bool
|
||||
}
|
||||
|
||||
// newServiceClient instance constructor
|
||||
@@ -347,6 +357,8 @@ func newServiceClient(args *newServiceClientArgs) *serviceClient {
|
||||
s.showDebugUI()
|
||||
case args.showProfiles:
|
||||
s.showProfilesUI()
|
||||
case args.showQuickActions:
|
||||
s.showQuickActionsUI()
|
||||
}
|
||||
|
||||
return s
|
||||
|
||||
101
client/ui/quickactions.go
Normal file
101
client/ui/quickactions.go
Normal file
@@ -0,0 +1,101 @@
|
||||
//go:build !(linux && 386)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
// showQuickActionsUI displays a simple window with connect/disconnect controls.
|
||||
func (s *serviceClient) showQuickActionsUI() {
|
||||
s.wQuickActions = s.app.NewWindow("NetBird")
|
||||
s.wQuickActions.SetOnClosed(s.cancel)
|
||||
|
||||
statusLabel := widget.NewLabel("Status: Checking...")
|
||||
connectBtn := widget.NewButton("Connect", nil)
|
||||
disconnectBtn := widget.NewButton("Disconnect", nil)
|
||||
|
||||
updateUI := func() {
|
||||
client, err := s.getSrvClient(defaultFailTimeout)
|
||||
if err != nil {
|
||||
log.Errorf("get service client: %v", err)
|
||||
statusLabel.SetText("Status: Error connecting to daemon")
|
||||
connectBtn.Disable()
|
||||
disconnectBtn.Disable()
|
||||
return
|
||||
}
|
||||
|
||||
status, err := client.Status(context.Background(), &proto.StatusRequest{})
|
||||
if err != nil {
|
||||
log.Errorf("get status: %v", err)
|
||||
statusLabel.SetText("Status: Error")
|
||||
connectBtn.Disable()
|
||||
disconnectBtn.Disable()
|
||||
return
|
||||
}
|
||||
|
||||
if status.Status == string(peer.StatusConnected) {
|
||||
statusLabel.SetText("Status: Connected")
|
||||
connectBtn.Disable()
|
||||
disconnectBtn.Enable()
|
||||
} else {
|
||||
statusLabel.SetText("Status: Disconnected")
|
||||
connectBtn.Enable()
|
||||
disconnectBtn.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
connectBtn.OnTapped = func() {
|
||||
connectBtn.Disable()
|
||||
statusLabel.SetText("Status: Connecting...")
|
||||
go func() {
|
||||
if err := s.menuUpClick(); err != nil {
|
||||
log.Errorf("connect failed: %v", err)
|
||||
statusLabel.SetText(fmt.Sprintf("Status: Error - %v", err))
|
||||
}
|
||||
updateUI()
|
||||
}()
|
||||
}
|
||||
|
||||
disconnectBtn.OnTapped = func() {
|
||||
disconnectBtn.Disable()
|
||||
statusLabel.SetText("Status: Disconnecting...")
|
||||
go func() {
|
||||
if err := s.menuDownClick(); err != nil {
|
||||
log.Errorf("disconnect failed: %v", err)
|
||||
statusLabel.SetText(fmt.Sprintf("Status: Error - %v", err))
|
||||
}
|
||||
updateUI()
|
||||
}()
|
||||
}
|
||||
|
||||
content := container.NewVBox(
|
||||
layout.NewSpacer(),
|
||||
statusLabel,
|
||||
layout.NewSpacer(),
|
||||
container.NewHBox(
|
||||
layout.NewSpacer(),
|
||||
connectBtn,
|
||||
disconnectBtn,
|
||||
layout.NewSpacer(),
|
||||
),
|
||||
layout.NewSpacer(),
|
||||
)
|
||||
|
||||
s.wQuickActions.SetContent(content)
|
||||
s.wQuickActions.Resize(fyne.NewSize(300, 150))
|
||||
s.wQuickActions.SetFixedSize(true)
|
||||
s.wQuickActions.Show()
|
||||
|
||||
updateUI()
|
||||
}
|
||||
76
client/ui/signal_unix.go
Normal file
76
client/ui/signal_unix.go
Normal file
@@ -0,0 +1,76 @@
|
||||
//go:build !windows && !(linux && 386)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// setupSignalHandler sets up a signal handler to listen for SIGUSR1.
|
||||
// When received, it opens the quick actions window.
|
||||
func (s *serviceClient) setupSignalHandler(ctx context.Context) {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGUSR1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-sigChan:
|
||||
log.Info("received SIGUSR1 signal, opening quick actions window")
|
||||
s.openQuickActions()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// openQuickActions opens the quick actions window by spawning a new process.
|
||||
func (s *serviceClient) openQuickActions() {
|
||||
proc, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Errorf("get executable path: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(s.ctx, proc,
|
||||
"--quick-actions=true",
|
||||
"--daemon-addr="+s.addr,
|
||||
)
|
||||
|
||||
if out := s.attachOutput(cmd); out != nil {
|
||||
defer func() {
|
||||
if err := out.Close(); err != nil {
|
||||
log.Errorf("close log file %s: %v", s.logFile, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
log.Infof("running command: %s --quick-actions=true --daemon-addr=%s", proc, s.addr)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Errorf("start quick actions window: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Debugf("quick actions window exited: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// sendShowWindowSignal sends SIGUSR1 to the specified PID.
|
||||
func sendShowWindowSignal(pid int32) error {
|
||||
process, err := os.FindProcess(int(pid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return process.Signal(syscall.SIGUSR1)
|
||||
}
|
||||
57
client/ui/signal_windows.go
Normal file
57
client/ui/signal_windows.go
Normal file
@@ -0,0 +1,57 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// setupSignalHandler sets up signal handling for Windows.
|
||||
// Windows doesn't support SIGUSR1, so this is currently a no-op.
|
||||
// Future enhancement: implement Windows-specific IPC (named events, named pipes, etc.)
|
||||
func (s *serviceClient) setupSignalHandler(ctx context.Context) {
|
||||
// TODO: see how debug bundle is generated on signal in windows
|
||||
log.Debug("signal handler not yet implemented for Windows")
|
||||
}
|
||||
|
||||
// openQuickActions opens the quick actions window by spawning a new process.
|
||||
func (s *serviceClient) openQuickActions() {
|
||||
proc, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Errorf("get executable path: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(s.ctx, proc,
|
||||
"--quick-actions=true",
|
||||
"--daemon-addr="+s.addr,
|
||||
)
|
||||
|
||||
if out := s.attachOutput(cmd); out != nil {
|
||||
defer func() {
|
||||
if err := out.Close(); err != nil {
|
||||
log.Errorf("close log file %s: %v", s.logFile, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
log.Infof("running command: %s --quick-actions=true --daemon-addr=%s", proc, s.addr)
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Errorf("start quick actions window: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Debugf("quick actions window exited: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func sendShowWindowSignal(pid int32) error {
|
||||
}
|
||||
@@ -7,10 +7,8 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
pb "github.com/golang/protobuf/proto" // nolint
|
||||
@@ -46,9 +44,6 @@ import (
|
||||
const (
|
||||
envLogBlockedPeers = "NB_LOG_BLOCKED_PEERS"
|
||||
envBlockPeers = "NB_BLOCK_SAME_PEERS"
|
||||
envConcurrentSyncs = "NB_MAX_CONCURRENT_SYNCS"
|
||||
|
||||
defaultSyncLim = 1000
|
||||
)
|
||||
|
||||
// GRPCServer an instance of a Management gRPC API server
|
||||
@@ -68,9 +63,6 @@ type GRPCServer struct {
|
||||
logBlockedPeers bool
|
||||
blockPeersWithSameConfig bool
|
||||
integratedPeerValidator integrated_validator.IntegratedValidator
|
||||
|
||||
syncSem atomic.Int32
|
||||
syncLim int32
|
||||
}
|
||||
|
||||
// NewServer creates a new Management server
|
||||
@@ -104,16 +96,6 @@ func NewServer(
|
||||
logBlockedPeers := strings.ToLower(os.Getenv(envLogBlockedPeers)) == "true"
|
||||
blockPeersWithSameConfig := strings.ToLower(os.Getenv(envBlockPeers)) == "true"
|
||||
|
||||
syncLim := int32(defaultSyncLim)
|
||||
if syncLimStr := os.Getenv(envConcurrentSyncs); syncLimStr != "" {
|
||||
syncLimParsed, err := strconv.Atoi(syncLimStr)
|
||||
if err != nil {
|
||||
log.Errorf("invalid value for %s: %v using %d", envConcurrentSyncs, err, defaultSyncLim)
|
||||
} else {
|
||||
syncLim = int32(syncLimParsed)
|
||||
}
|
||||
}
|
||||
|
||||
return &GRPCServer{
|
||||
wgKey: key,
|
||||
// peerKey -> event channel
|
||||
@@ -128,8 +110,6 @@ func NewServer(
|
||||
logBlockedPeers: logBlockedPeers,
|
||||
blockPeersWithSameConfig: blockPeersWithSameConfig,
|
||||
integratedPeerValidator: integratedPeerValidator,
|
||||
|
||||
syncLim: syncLim,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -171,11 +151,6 @@ func getRealIP(ctx context.Context) net.IP {
|
||||
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
|
||||
// notifies the connected peer of any updates (e.g. new peers under the same account)
|
||||
func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error {
|
||||
if s.syncSem.Load() >= s.syncLim {
|
||||
return status.Errorf(codes.ResourceExhausted, "too many concurrent sync requests, please try again later")
|
||||
}
|
||||
s.syncSem.Add(1)
|
||||
|
||||
reqStart := time.Now()
|
||||
|
||||
ctx := srv.Context()
|
||||
@@ -183,7 +158,6 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
||||
syncReq := &proto.SyncRequest{}
|
||||
peerKey, err := s.parseRequest(ctx, req, syncReq)
|
||||
if err != nil {
|
||||
s.syncSem.Add(-1)
|
||||
return err
|
||||
}
|
||||
realIP := getRealIP(ctx)
|
||||
@@ -198,7 +172,6 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
||||
log.WithContext(ctx).Warnf("peer %s with meta hash %d is blocked from syncing", peerKey.String(), metahashed)
|
||||
}
|
||||
if s.blockPeersWithSameConfig {
|
||||
s.syncSem.Add(-1)
|
||||
return mapError(ctx, internalStatus.ErrPeerAlreadyLoggedIn)
|
||||
}
|
||||
}
|
||||
@@ -223,10 +196,8 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
||||
ctx = context.WithValue(ctx, nbContext.AccountIDKey, "UNKNOWN")
|
||||
log.WithContext(ctx).Tracef("peer %s is not registered", peerKey.String())
|
||||
if errStatus, ok := internalStatus.FromError(err); ok && errStatus.Type() == internalStatus.NotFound {
|
||||
s.syncSem.Add(-1)
|
||||
return status.Errorf(codes.PermissionDenied, "peer is not registered")
|
||||
}
|
||||
s.syncSem.Add(-1)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -242,14 +213,12 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
||||
peer, netMap, postureChecks, err := s.accountManager.SyncAndMarkPeer(ctx, accountID, peerKey.String(), peerMeta, realIP)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Debugf("error while syncing peer %s: %v", peerKey.String(), err)
|
||||
s.syncSem.Add(-1)
|
||||
return mapError(ctx, err)
|
||||
}
|
||||
|
||||
err = s.sendInitialSync(ctx, peerKey, peer, netMap, postureChecks, srv)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Debugf("error while sending initial sync for %s: %v", peerKey.String(), err)
|
||||
s.syncSem.Add(-1)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -268,8 +237,6 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
||||
|
||||
log.WithContext(ctx).Debugf("Sync: took %v", time.Since(reqStart))
|
||||
|
||||
s.syncSem.Add(-1)
|
||||
|
||||
return s.handleUpdates(ctx, accountID, peerKey, peer, updates, srv)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user