diff --git a/internal/repository/postgres.go b/internal/repository/postgres.go index 510cd6b..302c75e 100644 --- a/internal/repository/postgres.go +++ b/internal/repository/postgres.go @@ -331,10 +331,11 @@ func (p *Postgres) CreatePeer(tenantID string, in *store.BGPPeer) (*store.BGPPee if in.SpeakerID != nil && strings.TrimSpace(*in.SpeakerID) != "" { sp = strings.TrimSpace(*in.SpeakerID) } + enabled := store.EffectivePeerEnabledOnCreate(in.Enabled, in.SessionState) _, err := p.pool.Exec(ctx, ` INSERT INTO bgp_peer (id, tenant_id, bgp_speaker_id, neighbor, remote_asn, enabled, policies_json, meta_json) VALUES ($1,$2,$3,$4::inet, $5, $6, $7::jsonb, $8::jsonb)`, - id, tenantID, sp, neighbor, in.RemoteASN, in.Enabled, pol, string(mb)) + id, tenantID, sp, neighbor, in.RemoteASN, enabled, pol, string(mb)) if err != nil { return nil, err } diff --git a/internal/store/memory_crud.go b/internal/store/memory_crud.go index 271c075..5eca7d3 100644 --- a/internal/store/memory_crud.go +++ b/internal/store/memory_crud.go @@ -629,12 +629,10 @@ func (m *Memory) CreatePeer(tenantID string, in *BGPPeer) (*BGPPeer, error) { id := uuid.NewString() p := &BGPPeer{ ID: id, TenantID: tenantID, SpeakerID: in.SpeakerID, Name: in.Name, - Neighbor: neighbor, RemoteASN: in.RemoteASN, Enabled: in.Enabled, + Neighbor: neighbor, RemoteASN: in.RemoteASN, + Enabled: EffectivePeerEnabledOnCreate(in.Enabled, in.SessionState), SessionState: in.SessionState, PoliciesJSON: in.PoliciesJSON, } - if !p.Enabled && p.SessionState == "" { - p.Enabled = true - } m.peers[id] = p return p, nil } diff --git a/internal/store/peer_neighbor.go b/internal/store/peer_neighbor.go index 039b969..eeac4bc 100644 --- a/internal/store/peer_neighbor.go +++ b/internal/store/peer_neighbor.go @@ -5,6 +5,19 @@ import ( "strings" ) +// EffectivePeerEnabledOnCreate matches API/JSON decoding: omitted "enabled" unmarshals as false in Go, +// but new peers should be enabled by default. If session_state is non-empty, false is preserved +// (agent-managed rows may be intentionally disabled). +func EffectivePeerEnabledOnCreate(inEnabled bool, sessionState string) bool { + if inEnabled { + return true + } + if strings.TrimSpace(sessionState) != "" { + return false + } + return true +} + // ParsePeerNeighbor parses a BGP neighbor value for BIRD output: a plain IPv4/IPv6 // address, or a host prefix (/32 or /128) which is a common input mistake. func ParsePeerNeighbor(s string) (netip.Addr, bool) { diff --git a/internal/store/peer_neighbor_test.go b/internal/store/peer_neighbor_test.go index d870810..2082382 100644 --- a/internal/store/peer_neighbor_test.go +++ b/internal/store/peer_neighbor_test.go @@ -2,6 +2,21 @@ package store import "testing" +func TestEffectivePeerEnabledOnCreate(t *testing.T) { + if !EffectivePeerEnabledOnCreate(false, "") { + t.Fatal("omitted enabled (false) + empty session should default to enabled") + } + if !EffectivePeerEnabledOnCreate(true, "") { + t.Fatal("explicit true") + } + if !EffectivePeerEnabledOnCreate(true, "down") { + t.Fatal("true with session") + } + if EffectivePeerEnabledOnCreate(false, "Idle") { + t.Fatal("false with non-empty session should stay disabled") + } +} + func TestParsePeerNeighbor(t *testing.T) { tests := []struct { in string diff --git a/web/src/routes/network/+page.svelte b/web/src/routes/network/+page.svelte index 1106522..3ab4bc6 100644 --- a/web/src/routes/network/+page.svelte +++ b/web/src/routes/network/+page.svelte @@ -112,7 +112,8 @@ await apiMutate(`/v1/peers/${peerEdit.id}`, 'PATCH', peerForm); toast.success('Пир обновлён'); } else { - await apiMutate('/v1/peers', 'POST', peerForm); + // API: omitted "enabled" decodes as false in Go; default new peers to enabled. + await apiMutate('/v1/peers', 'POST', { ...peerForm, enabled: true }); toast.success('Пир создан'); } peerDialog = false;