141 lines
3.2 KiB
Go
141 lines
3.2 KiB
Go
package app
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (s *egressIdentityService) getSnapshot(scopeRaw string, refresh bool) (EgressIdentity, error) {
|
|
target, err := parseEgressScope(scopeRaw)
|
|
if err != nil {
|
|
return EgressIdentity{}, err
|
|
}
|
|
if refresh {
|
|
s.queueScopeRefresh(target, false)
|
|
}
|
|
return s.snapshot(target, time.Now()), nil
|
|
}
|
|
|
|
func (s *egressIdentityService) queueRefresh(scopes []string, force bool) (EgressIdentityRefreshResponse, error) {
|
|
rawTargets := make([]string, 0, len(scopes))
|
|
for _, raw := range scopes {
|
|
v := strings.TrimSpace(raw)
|
|
if v == "" {
|
|
continue
|
|
}
|
|
rawTargets = append(rawTargets, v)
|
|
}
|
|
if len(rawTargets) == 0 {
|
|
rawTargets = s.knownScopes()
|
|
}
|
|
if len(rawTargets) == 0 {
|
|
rawTargets = []string{"adguardvpn", "system"}
|
|
}
|
|
|
|
targets := make([]egressScopeTarget, 0, len(rawTargets))
|
|
seen := map[string]struct{}{}
|
|
for _, raw := range rawTargets {
|
|
target, err := parseEgressScope(raw)
|
|
if err != nil {
|
|
return EgressIdentityRefreshResponse{}, err
|
|
}
|
|
if _, ok := seen[target.Scope]; ok {
|
|
continue
|
|
}
|
|
seen[target.Scope] = struct{}{}
|
|
targets = append(targets, target)
|
|
}
|
|
|
|
resp := EgressIdentityRefreshResponse{
|
|
OK: true,
|
|
Message: "refresh queued",
|
|
Items: make([]EgressIdentityRefreshItem, 0, len(targets)),
|
|
}
|
|
for _, target := range targets {
|
|
queued, reason := s.queueScopeRefresh(target, force)
|
|
item := EgressIdentityRefreshItem{
|
|
Scope: target.Scope,
|
|
Queued: queued,
|
|
}
|
|
if queued {
|
|
item.Status = "queued"
|
|
resp.Queued++
|
|
} else {
|
|
item.Status = "skipped"
|
|
item.Reason = strings.TrimSpace(reason)
|
|
if item.Reason == "" {
|
|
item.Reason = "throttled or already fresh"
|
|
}
|
|
resp.Skipped++
|
|
}
|
|
resp.Items = append(resp.Items, item)
|
|
}
|
|
resp.Count = len(resp.Items)
|
|
if resp.Queued == 0 {
|
|
resp.Message = "refresh skipped"
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
func (s *egressIdentityService) knownScopes() []string {
|
|
outSet := map[string]struct{}{
|
|
"adguardvpn": {},
|
|
"system": {},
|
|
}
|
|
|
|
transportMu.Lock()
|
|
st := loadTransportClientsState()
|
|
transportMu.Unlock()
|
|
for _, it := range st.Items {
|
|
id := sanitizeID(it.ID)
|
|
if id == "" {
|
|
continue
|
|
}
|
|
outSet["transport:"+id] = struct{}{}
|
|
}
|
|
|
|
s.mu.Lock()
|
|
for scope := range s.entries {
|
|
outSet[scope] = struct{}{}
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
out := make([]string, 0, len(outSet))
|
|
for scope := range outSet {
|
|
out = append(out, scope)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
func (s *egressIdentityService) queueScopeRefresh(target egressScopeTarget, force bool) (bool, string) {
|
|
now := time.Now()
|
|
|
|
s.mu.Lock()
|
|
entry := s.ensureEntryLocked(target)
|
|
hasData := strings.TrimSpace(entry.item.IP) != ""
|
|
switch {
|
|
case entry.swr.refreshInProgress():
|
|
s.mu.Unlock()
|
|
return false, "already in progress"
|
|
case !force && !entry.swr.nextRetryAt().IsZero() && now.Before(entry.swr.nextRetryAt()):
|
|
s.mu.Unlock()
|
|
return false, "backoff in progress"
|
|
case !force && hasData && !entry.swr.isStale(now):
|
|
s.mu.Unlock()
|
|
return false, "already fresh"
|
|
}
|
|
if force {
|
|
entry.swr.clearBackoff()
|
|
}
|
|
if !entry.swr.beginRefresh(now, force, hasData) {
|
|
s.mu.Unlock()
|
|
return false, "throttled or already fresh"
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
go s.refreshScope(target, force)
|
|
return true, ""
|
|
}
|