//go:build darwin package main import ( "bytes" "fmt" "log" "net/http" "os" "os/exec" "os/signal" "path/filepath" "syscall" "time" "vpnclient/internal/apphost" "vpnclient/internal/config" "vpnclient/internal/core" "vpnclient/internal/update" ) func main() { if update.MaybeFinishUpdate(os.Args[1:]) { return } update.CleanupStaleDownloads() log.SetFlags(log.LstdFlags | log.Lshortfile) 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 := &bytes.Buffer{} 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) os.Exit(0) }() } ln, uiURL, err := apphost.ListenLocal() if err != nil { fatalf("listen: %v", err) } srv := &http.Server{Handler: a.Handler()} go func() { _ = srv.Serve(ln) }() go a.AutoCheckUpdate() if err := openAppWindow(uiURL); err != nil { log.Printf("open UI: %v — откройте вручную: %s", err, uiURL) fmt.Println(uiURL) } else { log.Printf("Navis UI: %s", uiURL) } sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) <-sig _ = mgr.Disconnect() _ = srv.Close() } func openAppWindow(uiURL string) error { // Prefer Chromium-based --app window (looks like a desktop client). for _, app := range []string{"Google Chrome", "Chromium", "Microsoft Edge", "Brave Browser", "Arc"} { cmd := exec.Command("open", "-na", app, "--args", "--app="+uiURL, "--new-window") if err := cmd.Start(); err == nil { return nil } } // Fallback: default browser return exec.Command("open", uiURL).Start() } 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) } // Ensure we keep a reference for potential future dock icon path helpers. var _ = filepath.Separator