Files
frp/pkg/config/source/aggregator.go
fatedier 0b4f83cd04 pkg/config: use modern Go stdlib for sorting and string operations (#5210)
- slices.SortedFunc + maps.Values + cmp.Compare instead of manual
  map-to-slice collection + sort.Slice (source/aggregator.go)
- strings.CutSuffix instead of HasSuffix+TrimSuffix, and deduplicate
  error handling in BandwidthQuantity.UnmarshalString (types/types.go)
2026-03-06 23:13:29 +08:00

110 lines
2.7 KiB
Go

// Copyright 2026 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package source
import (
"cmp"
"errors"
"fmt"
"maps"
"slices"
"sync"
v1 "github.com/fatedier/frp/pkg/config/v1"
)
type Aggregator struct {
mu sync.RWMutex
configSource *ConfigSource
storeSource *StoreSource
}
func NewAggregator(configSource *ConfigSource) *Aggregator {
if configSource == nil {
configSource = NewConfigSource()
}
return &Aggregator{
configSource: configSource,
}
}
func (a *Aggregator) SetStoreSource(storeSource *StoreSource) {
a.mu.Lock()
defer a.mu.Unlock()
a.storeSource = storeSource
}
func (a *Aggregator) ConfigSource() *ConfigSource {
return a.configSource
}
func (a *Aggregator) StoreSource() *StoreSource {
return a.storeSource
}
func (a *Aggregator) getSourcesLocked() []Source {
sources := make([]Source, 0, 2)
if a.configSource != nil {
sources = append(sources, a.configSource)
}
if a.storeSource != nil {
sources = append(sources, a.storeSource)
}
return sources
}
func (a *Aggregator) Load() ([]v1.ProxyConfigurer, []v1.VisitorConfigurer, error) {
a.mu.RLock()
entries := a.getSourcesLocked()
a.mu.RUnlock()
if len(entries) == 0 {
return nil, nil, errors.New("no sources configured")
}
proxyMap := make(map[string]v1.ProxyConfigurer)
visitorMap := make(map[string]v1.VisitorConfigurer)
for _, src := range entries {
proxies, visitors, err := src.Load()
if err != nil {
return nil, nil, fmt.Errorf("load source: %w", err)
}
for _, p := range proxies {
proxyMap[p.GetBaseConfig().Name] = p
}
for _, v := range visitors {
visitorMap[v.GetBaseConfig().Name] = v
}
}
proxies, visitors := a.mapsToSortedSlices(proxyMap, visitorMap)
return proxies, visitors, nil
}
func (a *Aggregator) mapsToSortedSlices(
proxyMap map[string]v1.ProxyConfigurer,
visitorMap map[string]v1.VisitorConfigurer,
) ([]v1.ProxyConfigurer, []v1.VisitorConfigurer) {
proxies := slices.SortedFunc(maps.Values(proxyMap), func(x, y v1.ProxyConfigurer) int {
return cmp.Compare(x.GetBaseConfig().Name, y.GetBaseConfig().Name)
})
visitors := slices.SortedFunc(maps.Values(visitorMap), func(x, y v1.VisitorConfigurer) int {
return cmp.Compare(x.GetBaseConfig().Name, y.GetBaseConfig().Name)
})
return proxies, visitors
}