//go:build darwin package main import ( "fmt" "log" "net/http" "os" "os/exec" "os/signal" "runtime" "syscall" "time" "github.com/crgimenes/glaze" "vpnclient/internal/apphost" "vpnclient/internal/config" "vpnclient/internal/core" "vpnclient/internal/logbuf" "vpnclient/internal/trayhost" "vpnclient/internal/update" ) func main() { if update.MaybeFinishUpdate(os.Args[1:]) { return } update.CleanupStaleDownloads() log.SetFlags(log.LstdFlags | log.Lshortfile) log.Printf("Navis %s · GOMAXPROCS=%d NumCPU=%d", update.DisplayVersion(), runtime.GOMAXPROCS(0), runtime.NumCPU()) cfgPath, err := config.LocateConfig() if err != nil { fatalf("Не удалось найти путь конфига: %v", err) } if _, err := os.Stat(cfgPath); os.IsNotExist(err) { if err := config.WriteExample(cfgPath); err != nil { fatalf("Не удалось создать конфиг: %v", err) } } cfg, err := config.Load(cfgPath) if err != nil { fatalf("Ошибка конфига %s: %v", cfgPath, err) } logBuf := logbuf.New(0) mgr, err := core.NewManager(cfgPath, cfg, logBuf) if err != nil { fatalf("%v", err) } a := apphost.New(mgr, cfgPath, logBuf) a.OpenURL = func(u string) error { return exec.Command("open", u).Start() } a.OnAfterUpdate = func() { go func() { time.Sleep(300 * time.Millisecond) _ = a.Disconnect() os.Exit(0) }() } ln, uiURL, token, err := apphost.ListenLocal() if err != nil { fatalf("listen: %v", err) } a.APIToken = token srv := &http.Server{Handler: a.Handler()} go func() { _ = srv.Serve(ln) }() a.StartBackground() trayhost.Start("Navis", trayhost.Hooks{ Connect: func() { _ = a.Connect() }, Disconnect: func() { _ = a.Disconnect() }, Quit: func() { _ = a.Disconnect() os.Exit(0) }, IsUp: func() bool { return a.Mgr.Status().Connected }, }) go a.AutoCheckUpdate() w, err := glaze.New(false) if err != nil || w == nil { log.Printf("webview unavailable (%v); opening default browser", err) if err2 := exec.Command("open", uiURL).Run(); err2 != nil { fatalf("Не удалось открыть интерфейс:\n%v\n%v\n%s", err, err2, uiURL) } log.Printf("Navis UI: %s", uiURL) sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) <-sig _ = mgr.Disconnect() _ = srv.Close() return } defer func() { _ = mgr.Disconnect() _ = srv.Close() w.Destroy() }() w.SetTitle("Navis") w.SetSize(500, 900, glaze.HintNone) w.Navigate(uiURL) log.Printf("Navis UI: %s", uiURL) w.Run() } func fatalf(format string, args ...any) { msg := fmt.Sprintf(format, args...) log.Println(msg) _ = exec.Command("osascript", "-e", fmt.Sprintf(`display alert "Navis" message %q`, msg)).Run() os.Exit(1) }