package app import ( "net/http" ) func handleTransportPolicies(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } transportMu.Lock() policy := loadTransportPolicyState() clientsState := loadTransportClientsState() plan := loadTransportPolicyCompilePlan() planSnapshot := captureTransportPolicyPlanStateSnapshot(policy, plan) transportMu.Unlock() policyTargets := transportPolicyClientsWithVirtualTargets(clientsState.Items) nextPlan, planChanged := compileTransportPolicyPlanForSnapshot(policy, policyTargets, plan) if planChanged { _ = saveTransportPlanIfSnapshotCurrent(planSnapshot, nextPlan) } writeJSON(w, http.StatusOK, TransportPolicyResponse{ OK: true, Message: "ok", PolicyRevision: policy.Revision, Intents: policy.Intents, Plan: &nextPlan, }) } func handleTransportConflicts(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } transportMu.Lock() st := loadTransportConflictsState() transportMu.Unlock() writeJSON(w, http.StatusOK, TransportConflictsResponse{ OK: true, Message: "ok", HasBlocking: st.HasBlocking, Items: st.Items, }) } func handleTransportOwnership(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } transportMu.Lock() policy := loadTransportPolicyState() clientsState := loadTransportClientsState() owners := loadTransportOwnershipState() plan := loadTransportPolicyCompilePlan() planSnapshot := captureTransportPolicyPlanStateSnapshot(policy, plan) ownershipSnapshot := captureTransportOwnershipStateSnapshot(policy, owners) transportMu.Unlock() policyTargets := transportPolicyClientsWithVirtualTargets(clientsState.Items) nextPlan, planChanged := compileTransportPolicyPlanForSnapshot(policy, policyTargets, plan) if planChanged { _ = saveTransportPlanIfSnapshotCurrent(planSnapshot, nextPlan) } planDigest := digestTransportPolicyCompilePlan(nextPlan) if transportOwnershipNeedsRebuild(policy.Revision, owners, planDigest) { owners = buildTransportOwnershipStateFromPlan(nextPlan, policy.Revision) _ = saveTransportOwnershipIfSnapshotCurrent(ownershipSnapshot, owners) } items, lockCount := attachTransportOwnershipLockState(owners.Items, policyTargets) writeJSON(w, http.StatusOK, TransportOwnershipResponse{ OK: true, Message: "ok", PolicyRevision: owners.PolicyRevision, PlanDigest: owners.PlanDigest, Count: len(items), LockCount: lockCount, Items: items, }) } func handleTransportOwnerLocks(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } transportMu.Lock() locks := loadTransportOwnerLocksState() transportMu.Unlock() writeJSON(w, http.StatusOK, TransportOwnerLocksResponse{ OK: true, Message: "ok", PolicyRevision: locks.PolicyRevision, Count: len(locks.Items), Items: locks.Items, }) } func handleTransportCapabilities(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } writeJSON(w, http.StatusOK, TransportCapabilitiesResponse{ OK: true, Message: "ok", Clients: map[string]map[string]bool{ "singbox": {"tcp": true, "udp": true, "dns_tunnel": true, "ssh_tunnel": false}, "dnstt": {"tcp": true, "udp": false, "dns_tunnel": true, "ssh_tunnel": true}, "phoenix": {"tcp": true, "udp": true, "dns_tunnel": false, "ssh_tunnel": true}, "adguardvpn": {"vpn": true, "autoloop": true, "dns_tunnel": false, "ssh_tunnel": false}, }, RuntimeModes: map[string]bool{ "exec": true, "embedded": false, "sidecar": false, }, PackagingProfiles: map[string]bool{ "system": true, "bundled": true, }, Lifecycle: []string{"provision", "start", "stop", "restart"}, HealthFields: []string{"status", "latency_ms", "last_error", "health.last_check"}, MetricsFields: []string{"restarts", "state_changes", "uptime_sec", "last_transition_at"}, ErrorCodes: []string{ "TRANSPORT_CLIENT_NOT_FOUND", "TRANSPORT_CLIENT_SAVE_FAILED", "TRANSPORT_CLIENT_DEGRADED", "BACKEND_RUNTIME_ERROR", "TRANSPORT_BACKEND_NETNS_SETUP_FAILED", "TRANSPORT_BACKEND_BOOTSTRAP_BYPASS_FAILED", "TRANSPORT_BACKEND_SINGBOX_DNS_MIGRATE_FAILED", "TRANSPORT_BACKEND_UNIT_REQUIRED", "TRANSPORT_BACKEND_ACTION_FAILED", "TRANSPORT_BACKEND_HEALTH_FAILED", "TRANSPORT_BACKEND_PROVISION_CONFIG_REQUIRED", "TRANSPORT_BACKEND_PROVISION_FAILED", "TRANSPORT_BACKEND_RUNTIME_MODE_UNSUPPORTED", }, }) }