platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
140
selective-vpn-api/app/egress_identity_refresh.go
Normal file
140
selective-vpn-api/app/egress_identity_refresh.go
Normal file
@@ -0,0 +1,140 @@
|
||||
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, ""
|
||||
}
|
||||
Reference in New Issue
Block a user