Files
elmprodvpn/selective-vpn-api/app/traffic_audit.go

88 lines
2.5 KiB
Go

package app
import (
"net/http"
"strings"
"time"
)
// ---------------------------------------------------------------------
// traffic audit (sanity checks / duplicates / nft consistency)
// ---------------------------------------------------------------------
//
// EN: Provides a pragmatic sanity-check endpoint for troubleshooting.
// EN: We check:
// EN: - traffic mode health (evaluateTrafficMode)
// EN: - runtime marks duplicates (target+app_key)
// EN: - nft chain consistency (state <-> output_apps rules)
// RU: Практичный sanity-check эндпоинт для диагностики.
// RU: Проверяем:
// RU: - health traffic mode (evaluateTrafficMode)
// RU: - дубли runtime marks (target+app_key)
// RU: - консистентность nft chain (state <-> output_apps rules)
func handleTrafficAudit(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
now := time.Now().UTC().Format(time.RFC3339)
// 1) Traffic mode status (includes route probe).
traffic := evaluateTrafficMode(loadTrafficModeState())
// 2) Profiles (persistent) duplicates by (target, app_key).
profiles := listTrafficAppProfiles()
profDups := findProfileDuplicates(profiles)
// 3) Runtime marks state + duplicates.
_ = pruneExpiredAppMarks()
appMarksMu.Lock()
marksSt := loadAppMarksState()
appMarksMu.Unlock()
markDups := findMarkDuplicates(marksSt.Items)
// 4) nft output_apps rules check (state <-> nft).
nftIssues, nftSummary := auditNftAppMarks(marksSt.Items)
issues := []string{}
if !traffic.Healthy {
issues = append(issues, "traffic_mode_unhealthy: "+strings.TrimSpace(traffic.Message))
}
for _, d := range profDups {
issues = append(issues, "profile_duplicate: "+d)
}
for _, d := range markDups {
issues = append(issues, "mark_duplicate: "+d)
}
issues = append(issues, nftIssues...)
ok := true
for _, it := range issues {
if strings.HasPrefix(it, "traffic_mode_unhealthy:") ||
strings.HasPrefix(it, "nft_error:") ||
strings.HasPrefix(it, "nft_missing_rule:") {
ok = false
break
}
}
pretty := buildTrafficAuditPretty(now, traffic, profiles, marksSt.Items, issues, nftSummary)
writeJSON(w, http.StatusOK, map[string]any{
"ok": ok,
"now": now,
"message": "ok",
"pretty": pretty,
"traffic": traffic,
"counts": map[string]any{
"profiles": len(profiles),
"marks": len(marksSt.Items),
},
"issues": issues,
"nft": nftSummary,
})
}