Files
elmprodvpn/selective-vpn-api/app/egress_identity_refresh.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, ""
}