mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-01 19:19:07 +02:00
ui: detect outdated daemon via WailsUIReady probe
Add a no-op WailsUIReady RPC the UI probes once at startup. A reachable daemon that returns Unimplemented predates this UI and is too old to drive it; the probe distinguishes that from an unreachable daemon. On an outdated daemon the UI fires a localized OS notification, since the main window may not open to show an in-app error. Proto regenerated with protoc 33.1 to keep the v6.33.1 header.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { Compat } from "@bindings/services";
|
||||
|
||||
let cached: boolean | null = null;
|
||||
|
||||
/**
|
||||
* isDaemonCompatible probes whether the running daemon implements the WailsUIReady
|
||||
* RPC. A false result means the daemon predates this UI (Unimplemented) and is too
|
||||
* old to drive it. The Go side returns an error instead when the daemon is simply
|
||||
* unreachable, so a throw here is NOT an outdated daemon — treat it as "unknown"
|
||||
* and let the normal connection flow report it.
|
||||
*
|
||||
* The result is cached for the session: daemon identity does not change without a
|
||||
* UI restart, and a freshly started daemon is reachable again under the same socket.
|
||||
*/
|
||||
export async function isDaemonCompatible(): Promise<boolean> {
|
||||
if (cached !== null) return cached;
|
||||
cached = await Compat.DaemonReady();
|
||||
return cached;
|
||||
}
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "NetBird beenden"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "NetBird-Dienst ist veraltet"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Aktualisieren Sie den NetBird-Dienst, um diese App zu verwenden."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "NetBird-Update verfügbar"
|
||||
},
|
||||
|
||||
@@ -143,6 +143,14 @@
|
||||
"message": "Quit NetBird",
|
||||
"description": "Tray menu item that fully exits the app and removes the tray icon. Keep short."
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "NetBird service is outdated",
|
||||
"description": "Title of the OS desktop notification shown when the running daemon is too old for this UI."
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Update the NetBird service to use this app.",
|
||||
"description": "Body of the desktop notification telling the user to upgrade the daemon."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "NetBird update available",
|
||||
"description": "Title of the OS desktop notification shown when an app update is available."
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "Salir de NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "El servicio de NetBird está desactualizado"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Actualice el servicio de NetBird para usar esta aplicación."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "Actualización de NetBird disponible"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "Quitter NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "Le service NetBird est obsolète"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Mettez à jour le service NetBird pour utiliser cette application."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "Mise à jour de NetBird disponible"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "NetBird bezárása"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "A NetBird szolgáltatás elavult"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Frissítsd a NetBird szolgáltatást az alkalmazás használatához."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "NetBird frissítés elérhető"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "Esci da NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "Il servizio NetBird è obsoleto"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Aggiorna il servizio NetBird per usare questa app."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "Aggiornamento NetBird disponibile"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "Sair do NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "O serviço NetBird está desatualizado"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Atualize o serviço NetBird para usar este aplicativo."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "Atualização do NetBird disponível"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "Выйти из NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "Служба NetBird устарела"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "Обновите службу NetBird, чтобы использовать это приложение."
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "Доступно обновление NetBird"
|
||||
},
|
||||
|
||||
@@ -107,6 +107,12 @@
|
||||
"tray.menu.quit": {
|
||||
"message": "退出 NetBird"
|
||||
},
|
||||
"notify.daemonOutdated.title": {
|
||||
"message": "NetBird 服务版本过旧"
|
||||
},
|
||||
"notify.daemonOutdated.body": {
|
||||
"message": "请更新 NetBird 服务以使用此应用。"
|
||||
},
|
||||
"notify.update.title": {
|
||||
"message": "NetBird 有可用更新"
|
||||
},
|
||||
|
||||
@@ -64,6 +64,7 @@ type registeredServices struct {
|
||||
update *services.Update
|
||||
daemonFeed *services.DaemonFeed
|
||||
notifier *notifications.NotificationService
|
||||
compat *services.Compat
|
||||
profileSwitcher *services.ProfileSwitcher
|
||||
bundle *i18n.Bundle
|
||||
prefStore *preferences.Store
|
||||
@@ -103,6 +104,7 @@ func main() {
|
||||
update := services.NewUpdate(conn, updaterHolder)
|
||||
daemonFeed := services.NewDaemonFeed(conn, app.Event, updaterHolder, debugLog)
|
||||
notifier := notifications.New()
|
||||
compat := services.NewCompat(conn)
|
||||
// macOS shows no toast until permission is requested. Run it after
|
||||
// ApplicationStarted so the notifier's Startup has initialised the
|
||||
// notification-center delegate. No-op on Linux/Windows (stubs report
|
||||
@@ -132,6 +134,7 @@ func main() {
|
||||
update: update,
|
||||
daemonFeed: daemonFeed,
|
||||
notifier: notifier,
|
||||
compat: compat,
|
||||
profileSwitcher: profileSwitcher,
|
||||
bundle: bundle,
|
||||
prefStore: prefStore,
|
||||
@@ -190,6 +193,10 @@ func main() {
|
||||
// Mint). ApplicationStarted fires after the startup loop, so the bus is up.
|
||||
app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) {
|
||||
daemonFeed.Watch(context.Background())
|
||||
// Probe daemon compatibility once the notifier bus is up; an outdated
|
||||
// daemon may keep the main window from showing, so the OS toast is the
|
||||
// only reliable signal the user gets.
|
||||
go notifyIfDaemonOutdated(compat, notifier, localizer)
|
||||
})
|
||||
|
||||
if err := app.Run(); err != nil {
|
||||
@@ -322,6 +329,7 @@ func registerServices(app *application.App, conn *Conn, s registeredServices) {
|
||||
app.RegisterService(application.NewService(services.NewAutostart(app.Autostart)))
|
||||
app.RegisterService(application.NewService(services.NewVersion()))
|
||||
app.RegisterService(application.NewService(services.NewUILog()))
|
||||
app.RegisterService(application.NewService(s.compat))
|
||||
}
|
||||
|
||||
// newMainWindow creates the hidden main window, sized to the user's last view
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//go:build !android && !ios && !freebsd && !js
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
// Compat answers whether the running daemon is new enough to drive this UI.
|
||||
type Compat struct {
|
||||
conn DaemonConn
|
||||
}
|
||||
|
||||
func NewCompat(conn DaemonConn) *Compat {
|
||||
return &Compat{conn: conn}
|
||||
}
|
||||
|
||||
// DaemonReady probes the WailsUIReady RPC once. A true result means the daemon
|
||||
// implements it and is compatible. An Unimplemented response means the daemon
|
||||
// predates this UI and is too old; the caller should surface an upgrade prompt.
|
||||
// Any other error (daemon not running, transport failure) is returned so the
|
||||
// frontend can tell "outdated" apart from "not reachable".
|
||||
func (c *Compat) DaemonReady(ctx context.Context) (bool, error) {
|
||||
client, err := c.conn.Client()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, err := client.WailsUIReady(ctx, &proto.WailsUIReadyRequest{}); err != nil {
|
||||
if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -3,10 +3,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||
|
||||
"github.com/netbirdio/netbird/client/ui/services"
|
||||
)
|
||||
|
||||
const notifyIDDaemonOutdated = "netbird-daemon-outdated"
|
||||
|
||||
// sendFn fits both NotificationService.SendNotification and SendNotificationWithActions.
|
||||
type sendFn func(notifications.NotificationOptions) error
|
||||
|
||||
@@ -31,3 +37,22 @@ func safeSendNotification(send sendFn, what string, opts notifications.Notificat
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// notifyIfDaemonOutdated probes the daemon once and fires an OS toast when it
|
||||
// is reachable but too old for this UI. A probe error means the daemon isn't
|
||||
// reachable (not outdated), so it is left to the normal connection flow.
|
||||
func notifyIfDaemonOutdated(compat *services.Compat, notifier *notifications.NotificationService, loc *Localizer) {
|
||||
ready, err := compat.DaemonReady(context.Background())
|
||||
if err != nil {
|
||||
log.Debugf("daemon compatibility probe: %v", err)
|
||||
return
|
||||
}
|
||||
if ready {
|
||||
return
|
||||
}
|
||||
_ = safeSendNotification(notifier.SendNotification, "daemon-outdated", notifications.NotificationOptions{
|
||||
ID: notifyIDDaemonOutdated,
|
||||
Title: loc.T("notify.daemonOutdated.title"),
|
||||
Body: loc.T("notify.daemonOutdated.body"),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user