Clear sysproxy on core death; probe+reconnect; subscription prune; Best ignores soft UDP; Windows tray; Android protocol gate; CI workflows. Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
2.7 KiB
Go
119 lines
2.7 KiB
Go
//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)
|
|
}
|