mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-11 07:59:08 +02:00
Merge branch 'main' into ui-refactor
Resolve conflicts and complete the profile display-name -> ID migration across the daemon, CLI, UI services, tray, and React frontend. - Regenerate client/proto daemon.pb.go from the merged proto so it carries both branches' RPCs (ui-refactor: SubscribeStatus/RegisterUILog/ExtendAuthSession, main: RenameProfile + id fields); keep the v6.33.1 generator header - server.go: combine ui-refactor's profile-list-changed events with main's id-bearing responses; publish profile-list-changed on rename - Drop the deleted Fyne client_ui.go/profile.go and port main's profile-ID changes into the refactored services/tray - UI services, tray and React: send the profile ID as the daemon handle and keep the display name for rendering only (activeProfileId vs activeProfile) - Relax the profile-name input to match the daemon's sanitizeDisplayName (spaces, emoji, any valid UTF-8); cap at 128 - Expose RenameProfile via a Profiles.Rename services wrapper (+ regenerated bindings) for the frontend to wire up - cmd/login.go: use the profile ID for GetProfileState
This commit is contained in:
@@ -122,7 +122,9 @@ func (s *Connection) Login(ctx context.Context, p LoginParams) (LoginResult, err
|
||||
username := p.Username
|
||||
if profileName == "" {
|
||||
if active, aerr := cli.GetActiveProfile(ctx, &proto.GetActiveProfileRequest{}); aerr == nil {
|
||||
profileName = active.GetProfileName()
|
||||
// Address the active profile by ID (the daemon resolves it as a
|
||||
// handle); names can collide, the ID cannot.
|
||||
profileName = active.GetId()
|
||||
if username == "" {
|
||||
username = active.GetUsername()
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ import (
|
||||
)
|
||||
|
||||
type Profile struct {
|
||||
// ID is the daemon-generated on-disk identity of the profile. Display
|
||||
// names can collide and be renamed, so the ID is the stable handle the
|
||||
// daemon resolves switch/remove/logout requests against.
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsActive bool `json:"isActive"`
|
||||
// Email is read from the user-owned per-profile state file (CLI writes it
|
||||
@@ -25,10 +29,28 @@ type ProfileRef struct {
|
||||
}
|
||||
|
||||
type ActiveProfile struct {
|
||||
// ID is the active profile's stable on-disk identity. Use it (not the
|
||||
// display name) as the handle for daemon requests and active-profile
|
||||
// comparisons, since names can collide.
|
||||
ID string `json:"id"`
|
||||
ProfileName string `json:"profileName"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// RenameProfileParams selects a profile by handle and carries its new display
|
||||
// name.
|
||||
type RenameProfileParams struct {
|
||||
// Handle selects the profile to rename: an exact ID, a unique ID prefix,
|
||||
// or a unique display name. The daemon resolves it server-side.
|
||||
Handle string `json:"handle"`
|
||||
// NewName is the new free-form display name. The daemon sanitizes it
|
||||
// (strips control characters, trims, caps length) but keeps spaces, emoji,
|
||||
// punctuation, and non-ASCII letters.
|
||||
NewName string `json:"newName"`
|
||||
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
type Profiles struct {
|
||||
conn DaemonConn
|
||||
}
|
||||
@@ -58,8 +80,8 @@ func (s *Profiles) List(ctx context.Context, username string) ([]Profile, error)
|
||||
pm := profilemanager.NewProfileManager()
|
||||
out := make([]Profile, 0, len(resp.GetProfiles()))
|
||||
for _, p := range resp.GetProfiles() {
|
||||
prof := Profile{Name: p.GetName(), IsActive: p.GetIsActive()}
|
||||
if state, err := pm.GetProfileState(p.GetName()); err == nil {
|
||||
prof := Profile{ID: p.GetId(), Name: p.GetName(), IsActive: p.GetIsActive()}
|
||||
if state, err := pm.GetProfileState(profilemanager.ID(p.GetId())); err == nil {
|
||||
prof.Email = state.Email
|
||||
}
|
||||
out = append(out, prof)
|
||||
@@ -77,15 +99,20 @@ func (s *Profiles) GetActive(ctx context.Context) (ActiveProfile, error) {
|
||||
return ActiveProfile{}, err
|
||||
}
|
||||
return ActiveProfile{
|
||||
ID: resp.GetId(),
|
||||
ProfileName: resp.GetProfileName(),
|
||||
Username: resp.GetUsername(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Profiles) Switch(ctx context.Context, p ProfileRef) error {
|
||||
// Switch sends a profile switch to the daemon and returns the resolved
|
||||
// on-disk ID of the now-active profile. ProfileName is treated as a handle
|
||||
// (exact ID, unique ID prefix, or unique display name); the daemon resolves
|
||||
// it server-side and echoes back the canonical ID.
|
||||
func (s *Profiles) Switch(ctx context.Context, p ProfileRef) (string, error) {
|
||||
cli, err := s.conn.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
req := &proto.SwitchProfileRequest{}
|
||||
if p.ProfileName != "" {
|
||||
@@ -94,20 +121,29 @@ func (s *Profiles) Switch(ctx context.Context, p ProfileRef) error {
|
||||
if p.Username != "" {
|
||||
req.Username = ptrStr(p.Username)
|
||||
}
|
||||
_, err = cli.SwitchProfile(ctx, req)
|
||||
return err
|
||||
resp, err := cli.SwitchProfile(ctx, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.GetId(), nil
|
||||
}
|
||||
|
||||
func (s *Profiles) Add(ctx context.Context, p ProfileRef) error {
|
||||
// Add creates a profile with the given display name and returns its
|
||||
// daemon-generated on-disk ID, so callers can address the new profile by ID
|
||||
// (e.g. to write config or switch to it) without re-resolving the name.
|
||||
func (s *Profiles) Add(ctx context.Context, p ProfileRef) (string, error) {
|
||||
cli, err := s.conn.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
_, err = cli.AddProfile(ctx, &proto.AddProfileRequest{
|
||||
resp, err := cli.AddProfile(ctx, &proto.AddProfileRequest{
|
||||
ProfileName: p.ProfileName,
|
||||
Username: p.Username,
|
||||
})
|
||||
return err
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.GetId(), nil
|
||||
}
|
||||
|
||||
func (s *Profiles) Remove(ctx context.Context, p ProfileRef) error {
|
||||
@@ -121,3 +157,23 @@ func (s *Profiles) Remove(ctx context.Context, p ProfileRef) error {
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Rename changes a profile's display name. The on-disk ID is unaffected, so
|
||||
// the active profile and any ID-based references stay valid (the default
|
||||
// profile can be renamed too — only its display name changes). Returns the
|
||||
// profile's previous display name as confirmation.
|
||||
func (s *Profiles) Rename(ctx context.Context, p RenameProfileParams) (string, error) {
|
||||
cli, err := s.conn.Client()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
resp, err := cli.RenameProfile(ctx, &proto.RenameProfileRequest{
|
||||
Username: p.Username,
|
||||
Handle: p.Handle,
|
||||
NewProfileName: p.NewName,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.GetOldProfileName(), nil
|
||||
}
|
||||
|
||||
@@ -55,15 +55,18 @@ func (s *ProfileSwitcher) SwitchActive(ctx context.Context, p ProfileRef) error
|
||||
s.feed.BeginProfileSwitch()
|
||||
}
|
||||
|
||||
if err := s.profiles.Switch(ctx, p); err != nil {
|
||||
resolvedID, err := s.profiles.Switch(ctx, p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("switch profile %q: %w", p.ProfileName, err)
|
||||
}
|
||||
|
||||
// Mirror into the user-side ProfileManager state: the CLI's `netbird up`
|
||||
// reads this file and sends the name back in the Up RPC, so if it diverges
|
||||
// reads this file and sends the ID back in the Up RPC, so if it diverges
|
||||
// the daemon reverts the UI switch on the next CLI `up`. Best-effort — the
|
||||
// daemon is authoritative; a failure only leaves the CLI's view stale.
|
||||
if err := profilemanager.NewProfileManager().SwitchProfile(p.ProfileName); err != nil {
|
||||
// Use the daemon-resolved ID rather than the handle we sent, since the
|
||||
// on-disk state is keyed by ID, not display name.
|
||||
if err := profilemanager.NewProfileManager().SwitchProfile(profilemanager.ID(resolvedID)); err != nil {
|
||||
log.Warnf("profileswitcher: mirror to user-side ProfileManager failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ func (s *Settings) GetRestrictions(ctx context.Context) (Restrictions, error) {
|
||||
return Restrictions{}, fmt.Errorf("get active profile: %w", err)
|
||||
}
|
||||
cfgResp, err := cli.GetConfig(ctx, &proto.GetConfigRequest{
|
||||
ProfileName: active.GetProfileName(),
|
||||
ProfileName: active.GetId(),
|
||||
Username: active.GetUsername(),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user