platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
164
selective-vpn-api/app/transport_policy_targets.go
Normal file
164
selective-vpn-api/app/transport_policy_targets.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
transportPolicyTargetAdGuardID = "adguardvpn"
|
||||
transportPolicyTargetAdGuardIfaceID = transportDefaultIfaceID
|
||||
transportPolicyTargetAdGuardTable = "agvpn"
|
||||
)
|
||||
|
||||
var transportPolicyTargetAdGuardAllowedActions = []string{"start", "stop", "restart"}
|
||||
|
||||
func isTransportPolicyVirtualClientID(id string) bool {
|
||||
return sanitizeID(id) == transportPolicyTargetAdGuardID
|
||||
}
|
||||
|
||||
func isTransportPolicyVirtualClient(client TransportClient) bool {
|
||||
if isTransportPolicyVirtualClientID(client.ID) {
|
||||
return true
|
||||
}
|
||||
return strings.EqualFold(strings.TrimSpace(string(client.Kind)), transportPolicyTargetAdGuardID)
|
||||
}
|
||||
|
||||
func resolveTransportPolicyVirtualClient(id string) (TransportClient, bool) {
|
||||
if !isTransportPolicyVirtualClientID(id) {
|
||||
return TransportClient{}, false
|
||||
}
|
||||
return buildTransportPolicyAdGuardTarget()
|
||||
}
|
||||
|
||||
func transportPolicyPersistableClients(items []TransportClient) []TransportClient {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]TransportClient, 0, len(items))
|
||||
for _, it := range items {
|
||||
if isTransportPolicyVirtualClient(it) {
|
||||
continue
|
||||
}
|
||||
out = append(out, it)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func transportPolicyClientsWithVirtualTargets(base []TransportClient) []TransportClient {
|
||||
out := append([]TransportClient(nil), base...)
|
||||
for i := range out {
|
||||
out[i].ID = sanitizeID(out[i].ID)
|
||||
}
|
||||
if adg, ok := buildTransportPolicyAdGuardTarget(); ok {
|
||||
if idx := findTransportClientIndex(out, adg.ID); idx >= 0 {
|
||||
out[idx] = adg
|
||||
} else {
|
||||
out = append(out, adg)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func buildTransportPolicyAdGuardTarget() (TransportClient, bool) {
|
||||
now := time.Now().UTC()
|
||||
|
||||
stdout, _, _, err := runCommandTimeout(1500*time.Millisecond, "systemctl", "is-active", adgvpnUnit)
|
||||
unitState := strings.TrimSpace(stdout)
|
||||
if err != nil && unitState == "" {
|
||||
return TransportClient{}, false
|
||||
}
|
||||
|
||||
word, raw := parseAutoloopStatus(tailFile(autoloopLogPath, 120))
|
||||
return buildTransportPolicyAdGuardTargetFromObservation(unitState, word, raw, now), true
|
||||
}
|
||||
|
||||
func buildTransportPolicyAdGuardTargetFromObservation(unitState, statusWord, raw string, observedAt time.Time) TransportClient {
|
||||
ts := observedAt.UTC().Format(time.RFC3339)
|
||||
status := adGuardPolicyTargetStatus(unitState, statusWord)
|
||||
iface := adGuardPolicyTargetIface(raw, status)
|
||||
|
||||
lastError := ""
|
||||
if status == TransportClientDown || status == TransportClientDegraded {
|
||||
msg := strings.TrimSpace(unitState)
|
||||
if msg == "" {
|
||||
msg = strings.TrimSpace(statusWord)
|
||||
}
|
||||
lastError = msg
|
||||
}
|
||||
|
||||
return TransportClient{
|
||||
ID: transportPolicyTargetAdGuardID,
|
||||
Name: "AdGuard VPN",
|
||||
Kind: TransportClientKind(transportPolicyTargetAdGuardID),
|
||||
Enabled: true,
|
||||
Status: status,
|
||||
IfaceID: transportPolicyTargetAdGuardIfaceID,
|
||||
Iface: iface,
|
||||
RoutingTable: transportPolicyTargetAdGuardTable,
|
||||
Capabilities: []string{"vpn", "systemd", "autoloop"},
|
||||
Health: TransportClientHealth{
|
||||
LastCheck: ts,
|
||||
LastError: lastError,
|
||||
},
|
||||
Runtime: TransportClientRuntime{
|
||||
Backend: "adguard",
|
||||
AllowedActions: append([]string(nil), transportPolicyTargetAdGuardAllowedActions...),
|
||||
LastAction: "observe",
|
||||
LastActionAt: ts,
|
||||
},
|
||||
UpdatedAt: ts,
|
||||
}
|
||||
}
|
||||
|
||||
func adGuardPolicyTargetStatus(unitState, statusWord string) TransportClientStatus {
|
||||
word := strings.ToUpper(strings.TrimSpace(statusWord))
|
||||
switch word {
|
||||
case "CONNECTED":
|
||||
return TransportClientUp
|
||||
case "RECONNECTING":
|
||||
return TransportClientStarting
|
||||
case "DISCONNECTED":
|
||||
return TransportClientDown
|
||||
case "ERROR":
|
||||
return TransportClientDegraded
|
||||
}
|
||||
|
||||
state := strings.ToLower(strings.TrimSpace(unitState))
|
||||
switch state {
|
||||
case "active":
|
||||
return TransportClientUp
|
||||
case "activating", "reloading":
|
||||
return TransportClientStarting
|
||||
case "failed", "inactive", "deactivating":
|
||||
return TransportClientDown
|
||||
default:
|
||||
return TransportClientDown
|
||||
}
|
||||
}
|
||||
|
||||
func adGuardPolicyTargetIface(raw string, status TransportClientStatus) string {
|
||||
v := strings.TrimSpace(raw)
|
||||
if v != "" {
|
||||
low := strings.ToLower(v)
|
||||
needle := "running on "
|
||||
if idx := strings.LastIndex(low, needle); idx >= 0 {
|
||||
tail := strings.TrimSpace(v[idx+len(needle):])
|
||||
if tail != "" {
|
||||
iface := strings.Trim(tail, ".,;:()[]{}")
|
||||
if parts := strings.Fields(iface); len(parts) > 0 {
|
||||
if strings.TrimSpace(parts[0]) != "" {
|
||||
return strings.TrimSpace(parts[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if iface, _ := resolveTrafficIface(loadTrafficModeState().PreferredIface); strings.TrimSpace(iface) != "" {
|
||||
return strings.TrimSpace(iface)
|
||||
}
|
||||
if status == TransportClientUp || status == TransportClientStarting {
|
||||
return "tun0"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user