package app import ( "fmt" "os" ) // --------------------------------------------------------------------- // основной routesUpdate // --------------------------------------------------------------------- // EN: Core selective-routes orchestration pipeline. // EN: This unit prepares policy routing, nftables objects, domain expansion, // EN: resolver execution, status artifacts, and GUI-facing progress events. // RU: Основной orchestration-пайплайн selective-routes. // RU: Модуль готовит policy routing, nftables-объекты, расширение доменов, // RU: запуск резолвера, статусные артефакты и события прогресса для GUI. // --------------------------------------------------------------------- // EN: `routesUpdate` contains core logic for routes update. // RU: `routesUpdate` - содержит основную логику для routes update. // --------------------------------------------------------------------- func routesUpdate(iface string) cmdResult { logp := func(format string, args ...any) { appendTraceLine("routes", fmt.Sprintf(format, args...)) } heartbeat := func() { _ = os.WriteFile(heartbeatFile, []byte{}, 0o644) } res := cmdResult{OK: false} iface = normalizePreferredIface(iface) if iface == "" { iface, _ = resolveTrafficIface(loadTrafficModeState().PreferredIface) } if iface == "" { logp("no active vpn iface, exit 0") res.OK = true res.Message = "interface not found, skipped" return res } skip, message, err := routesUpdateStagePreflight(iface, logp, heartbeat) if err != nil { res.Message = message return res } if skip { res.OK = true res.Message = message return res } if err := routesUpdateStagePolicy(iface, logp); err != nil { res.Message = err.Error() return res } heartbeat() routesUpdateStageNftBase(iface) heartbeat() resolveStage, err := routesUpdateStageResolve(logp, heartbeat) if err != nil { res.Message = err.Error() return res } defer resolveStage.cleanup() if err := routesUpdateStagePopulateNFT(resolveStage, logp, heartbeat); err != nil { res.Message = err.Error() return res } if err := routesUpdateStageArtifacts(iface, resolveStage, heartbeat); err != nil { res.Message = err.Error() return res } res.OK = true res.Message = fmt.Sprintf( "update done: domains=%d unique_ips=%d direct_ips=%d wildcard_ips=%d", len(resolveStage.domains), resolveStage.ipCount, resolveStage.directIPCount, resolveStage.wildcardIPCount, ) res.ExitCode = resolveStage.ipCount return res }