Release 2.2.0: Happ-style multi-node list with ping and auto best connect.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+101
-13
@@ -109,7 +109,7 @@ func main() {
|
||||
WindowOptions: webview2.WindowOptions{
|
||||
Title: "Navis 2",
|
||||
Width: 500,
|
||||
Height: 820,
|
||||
Height: 900,
|
||||
Center: true,
|
||||
IconId: 1,
|
||||
},
|
||||
@@ -124,11 +124,12 @@ func main() {
|
||||
}()
|
||||
|
||||
applyWindowIcon(w.Window())
|
||||
w.SetSize(500, 820, webview2.HintNone)
|
||||
w.SetSize(500, 900, webview2.HintNone)
|
||||
|
||||
mustBind(w, "getState", a.getState)
|
||||
mustBind(w, "connect", a.connect)
|
||||
mustBind(w, "disconnect", a.disconnect)
|
||||
mustBind(w, "connectProfile", a.connectProfile)
|
||||
mustBind(w, "saveProfile", a.saveProfile)
|
||||
mustBind(w, "createProfile", a.createProfile)
|
||||
mustBind(w, "selectProfile", a.selectProfile)
|
||||
@@ -136,6 +137,7 @@ func main() {
|
||||
mustBind(w, "installCore", a.installCore)
|
||||
mustBind(w, "openURL", openURL)
|
||||
mustBind(w, "pingServers", a.pingServers)
|
||||
mustBind(w, "pingBest", a.pingBest)
|
||||
mustBind(w, "checkUpdate", a.checkUpdate)
|
||||
mustBind(w, "applyUpdate", a.applyUpdate)
|
||||
mustBind(w, "saveHy2", a.saveHy2)
|
||||
@@ -254,9 +256,31 @@ func (a *app) deleteProfile(name string) error {
|
||||
}
|
||||
|
||||
func (a *app) connect() error {
|
||||
return a.connectProfile("")
|
||||
}
|
||||
|
||||
// connectProfile switches to the named profile (if set) and connects.
|
||||
// If already connected to another profile, disconnects first.
|
||||
func (a *app) connectProfile(name string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
st := a.mgr.Status()
|
||||
if st.Connected {
|
||||
if name == "" || st.Profile == name {
|
||||
return nil
|
||||
}
|
||||
if err := a.mgr.Disconnect(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
if err := a.mgr.SetActiveProfile(name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if p, err := a.mgr.Config().ActiveProfile(); err != nil {
|
||||
return err
|
||||
} else if strings.TrimSpace(p.Proxy) == "" {
|
||||
@@ -301,24 +325,88 @@ func (a *app) importSubscription(rawURL string) (int, error) {
|
||||
return a.mgr.ImportSubscription(rawURL)
|
||||
}
|
||||
|
||||
type pingBestResult struct {
|
||||
Pings []netcheck.Result `json:"pings"`
|
||||
BestName string `json:"best_name,omitempty"`
|
||||
BestMs int64 `json:"best_ms,omitempty"`
|
||||
Selected bool `json:"selected"`
|
||||
Connected bool `json:"connected"`
|
||||
}
|
||||
|
||||
func (a *app) pingServers() ([]netcheck.Result, error) {
|
||||
res, err := a.runPings()
|
||||
return res, err
|
||||
}
|
||||
|
||||
// pingBest measures all nodes, activates the fastest OK one, and optionally connects.
|
||||
func (a *app) pingBest(autoConnect bool) (pingBestResult, error) {
|
||||
pings, err := a.runPings()
|
||||
out := pingBestResult{Pings: pings}
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
best, ok := netcheck.BestOK(pings)
|
||||
if !ok {
|
||||
return out, fmt.Errorf("нет доступных серверов")
|
||||
}
|
||||
out.BestName = best.Name
|
||||
out.BestMs = best.Ms
|
||||
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
st := a.mgr.Status()
|
||||
alreadyBest := st.Connected && st.Profile == best.Name
|
||||
if st.Connected && !alreadyBest {
|
||||
if err := a.mgr.Disconnect(); err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
if !alreadyBest {
|
||||
if err := a.mgr.SetActiveProfile(best.Name); err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
out.Selected = true
|
||||
if !autoConnect {
|
||||
return out, nil
|
||||
}
|
||||
if alreadyBest {
|
||||
out.Connected = true
|
||||
return out, nil
|
||||
}
|
||||
if p, err := a.mgr.Config().ActiveProfile(); err != nil {
|
||||
return out, err
|
||||
} else if strings.TrimSpace(p.Proxy) == "" {
|
||||
return out, fmt.Errorf("пустая ссылка у лучшего сервера")
|
||||
}
|
||||
if _, err := a.mgr.EnsureCore(""); err != nil {
|
||||
return out, err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
defer cancel()
|
||||
if err := a.mgr.Connect(ctx, ""); err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Connected = true
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (a *app) runPings() ([]netcheck.Result, error) {
|
||||
a.mu.Lock()
|
||||
list := a.mgr.Profiles()
|
||||
a.mu.Unlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
out := make([]netcheck.Result, 0, len(list))
|
||||
targets := make([]netcheck.Target, 0, len(list))
|
||||
for _, p := range list {
|
||||
proxy := p.Proxy
|
||||
if proxy == "" {
|
||||
out = append(out, netcheck.Result{Name: p.Name, Error: "нет ссылки сервера"})
|
||||
continue
|
||||
}
|
||||
out = append(out, netcheck.PingProfile(ctx, p.Name, config.Protocol(p.Protocol), proxy))
|
||||
targets = append(targets, netcheck.Target{
|
||||
Name: p.Name,
|
||||
Protocol: config.Protocol(p.Protocol),
|
||||
Proxy: p.Proxy,
|
||||
})
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
|
||||
defer cancel()
|
||||
out := netcheck.PingAll(ctx, targets)
|
||||
a.mu.Lock()
|
||||
a.pings = out
|
||||
a.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user