Files
netbird/client/ui/updater/state.go
Maycon Santos 91acb8147c [management,client] 0.75.0 release with new desktop UI (#6473)
- **Wails v3 application** (`client/ui`) with a React + TypeScript + Tailwind frontend replacing the Fyne UI: main connection view, exit-node switcher, networks/peers browser with detail panels, profile management, settings (general, network, SSH, security, troubleshooting, appearance), debug-bundle creation, and a first-run welcome flow.
- **Internationalization**: go-i18n bundle with 9 locales (en, de, es, fr, hu, it, pt, ru, zh-CN) shared between the tray and the frontend.
- **New system tray** implementation with per-platform theme-aware icons, including a native XEmbed host for Linux (`xembed_tray_linux.c`) and a Linux theme watcher.
- **Session handling**: auth session watcher (`client/internal/auth/sessionwatch`), pending login flow, session-expiration dialog and tray notifications, and `netbird login` improvements.
- **Daemon API extensions** (`daemon.proto`): status stream subscription, event stream, networks/exit-node selection endpoints, and richer full status — with probe throttling on the daemon side to protect against UI-driven request storms.
- **UI preferences store** persisted per profile, autostart management via the daemon (single source of truth in HKCU on Windows).
- **Build system**: Taskfile-based builds per platform (macOS, Linux, Windows), Docker cross-compilation images, MSIX/NSIS/nfpm/AppImage packaging, and a new `frontend-ui` CI workflow.

Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
Co-authored-by: Eduard Gert <kontakt@eduardgert.de>
Co-authored-by: braginini <bangvalo@gmail.com>
Co-authored-by: Pascal Fischer <32096965+pascal-fischer@users.noreply.github.com>
Co-authored-by: riccardom <riccardomanfrin@gmail.com>
2026-07-06 13:47:16 +02:00

98 lines
2.5 KiB
Go

//go:build !android && !ios && !freebsd && !js
// Package updater holds the auto-update domain: the typed State, the
// daemon-SystemEvent metadata schema, and the Holder that caches the latest
// state and broadcasts changes. No Wails dependency.
package updater
import (
"sync"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/proto"
)
// EventStateChanged carries the full State snapshot as payload.
const EventStateChanged = "netbird:update:state"
// State is the typed snapshot of the daemon's update situation. Installing is
// driven only by the daemon's progress_window:show event; a UI-side
// Update.Trigger() does not flip it.
type State struct {
Available bool `json:"available"`
Version string `json:"version"`
Enforced bool `json:"enforced"`
Installing bool `json:"installing"`
}
// Emitter is the broadcast dependency Holder needs; the Wails app.Event
// processor satisfies it.
type Emitter interface {
Emit(name string, data ...any) bool
}
// Holder caches the latest update State and broadcasts changes.
type Holder struct {
emitter Emitter
mu sync.Mutex
state State
}
// NewHolder constructs an empty-state Holder. A nil emitter skips the broadcast.
func NewHolder(emitter Emitter) *Holder {
return &Holder{emitter: emitter}
}
// Get returns a copy of the cached State.
func (h *Holder) Get() State {
h.mu.Lock()
defer h.mu.Unlock()
return h.state
}
// OnSystemEvent folds update-related metadata into the cached state, emitting
// EventStateChanged only on an actual change so repeated daemon snapshots
// don't produce redundant pushes.
func (h *Holder) OnSystemEvent(ev *proto.SystemEvent) {
md := ev.GetMetadata()
if len(md) == 0 {
return
}
h.mu.Lock()
changed := false
if v, ok := md["new_version_available"]; ok {
_, enforced := md["enforced"]
if !h.state.Available || h.state.Version != v || h.state.Enforced != enforced {
h.state.Available = true
h.state.Version = v
h.state.Enforced = enforced
changed = true
}
}
if md["progress_window"] == "show" {
if !h.state.Installing {
h.state.Installing = true
changed = true
}
if v, ok := md["version"]; ok && v != "" && h.state.Version != v {
h.state.Version = v
h.state.Available = true
changed = true
}
}
snap := h.state
h.mu.Unlock()
if !changed {
return
}
log.Infof("update state: available=%v version=%q enforced=%v installing=%v",
snap.Available, snap.Version, snap.Enforced, snap.Installing)
if h.emitter != nil {
h.emitter.Emit(EventStateChanged, snap)
}
}