diff --git a/README.md b/README.md index 715c678..2a16c77 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,10 @@ https://evilfox.win/ Некоторые AV (Bkav, Microsoft Wacapew/Wacatac, Ikarus Trojan.WinGo.Agent, Google, Trapmine) часто помечают **любые** неподписанные Go-бинарники с сетью и сменой системного прокси. +В 2.2.0: +- компактный список нод с пингом (как в Happ); +- параллельный пинг, кнопка «Лучший», автоподключение к серверу с минимальной задержкой после подписки. + В 2.1.0: - вторичные кнопки перекрашены (не белые), чтобы не путать с основным действием. diff --git a/build-macos.bat b/build-macos.bat index 33ba068..37ba0d6 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -22,9 +22,9 @@ set CGO_ENABLED= go build -o "tools\packmac\packmac.exe" .\tools\packmac if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 1.4.1 -arch arm64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 2.2.0 -arch arm64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 1.4.1 -arch amd64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 2.2.0 -arch amd64 if errorlevel 1 exit /b 1 echo Built: diff --git a/cmd/vpnapp/main_windows.go b/cmd/vpnapp/main_windows.go index d06fa2e..37c394d 100644 --- a/cmd/vpnapp/main_windows.go +++ b/cmd/vpnapp/main_windows.go @@ -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() diff --git a/dist/navis-release/Navis.exe b/dist/navis-release/Navis.exe index d0a6f74..7c13344 100644 Binary files a/dist/navis-release/Navis.exe and b/dist/navis-release/Navis.exe differ diff --git a/dist/navis-release/darwin-amd64/Navis b/dist/navis-release/darwin-amd64/Navis index 5cbc2ac..905635d 100644 Binary files a/dist/navis-release/darwin-amd64/Navis and b/dist/navis-release/darwin-amd64/Navis differ diff --git a/dist/navis-release/darwin-amd64/Navis.app.zip b/dist/navis-release/darwin-amd64/Navis.app.zip index 4fb9631..f9371e0 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.app.zip and b/dist/navis-release/darwin-amd64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-amd64/Navis.dmg b/dist/navis-release/darwin-amd64/Navis.dmg index b58806c..5315be5 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.dmg and b/dist/navis-release/darwin-amd64/Navis.dmg differ diff --git a/dist/navis-release/darwin-arm64/Navis b/dist/navis-release/darwin-arm64/Navis index f9d10f8..b88bbc3 100644 Binary files a/dist/navis-release/darwin-arm64/Navis and b/dist/navis-release/darwin-arm64/Navis differ diff --git a/dist/navis-release/darwin-arm64/Navis.app.zip b/dist/navis-release/darwin-arm64/Navis.app.zip index 59c8cd6..9d0cdb5 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.app.zip and b/dist/navis-release/darwin-arm64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-arm64/Navis.dmg b/dist/navis-release/darwin-arm64/Navis.dmg index f8288ea..90779f5 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.dmg and b/dist/navis-release/darwin-arm64/Navis.dmg differ diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index 3b83984..05df1b6 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,28 +1,28 @@ { - "version": "2.1.0", - "notes": "Вторичные кнопки не белые — лучше отличаются от «Подключить»", + "version": "2.2.0", + "notes": "Компактный список нод с пингом (как Happ), автовыбор и подключение к серверу с лучшим пингом", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "faebea93c2efae41c7283ae933aae920e9f7484a56f3decfb231200e81ce909b", + "sha256": "acd3a57cb52d745ecd0dc24074cfbd0299820d863ce00c2d4ce7753fb7dc73f3", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "faebea93c2efae41c7283ae933aae920e9f7484a56f3decfb231200e81ce909b", + "sha256": "acd3a57cb52d745ecd0dc24074cfbd0299820d863ce00c2d4ce7753fb7dc73f3", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "e10c3eab6453837a08d2a606351f052afde6a7882708fe4e85abba9a97dd753d", + "sha256": "777564f1605209deb67b440a8f94667443498df38cef0a34a0ab8ab9922dab44", "os": "darwin", "arch": "arm64" }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "818d5554d57e4a5bd195583681c5d9f59cdef112e571d32e3ff61f1ad7aca22a", + "sha256": "5d0b0e3a2edebdb26f78eb39a7a67c6d7d7fb46beaa737869a0fec1150da3935", "os": "darwin", "arch": "amd64" } diff --git a/dist/update.json b/dist/update.json index 3b83984..05df1b6 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,28 +1,28 @@ { - "version": "2.1.0", - "notes": "Вторичные кнопки не белые — лучше отличаются от «Подключить»", + "version": "2.2.0", + "notes": "Компактный список нод с пингом (как Happ), автовыбор и подключение к серверу с лучшим пингом", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "faebea93c2efae41c7283ae933aae920e9f7484a56f3decfb231200e81ce909b", + "sha256": "acd3a57cb52d745ecd0dc24074cfbd0299820d863ce00c2d4ce7753fb7dc73f3", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "faebea93c2efae41c7283ae933aae920e9f7484a56f3decfb231200e81ce909b", + "sha256": "acd3a57cb52d745ecd0dc24074cfbd0299820d863ce00c2d4ce7753fb7dc73f3", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "e10c3eab6453837a08d2a606351f052afde6a7882708fe4e85abba9a97dd753d", + "sha256": "777564f1605209deb67b440a8f94667443498df38cef0a34a0ab8ab9922dab44", "os": "darwin", "arch": "arm64" }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "818d5554d57e4a5bd195583681c5d9f59cdef112e571d32e3ff61f1ad7aca22a", + "sha256": "5d0b0e3a2edebdb26f78eb39a7a67c6d7d7fb46beaa737869a0fec1150da3935", "os": "darwin", "arch": "amd64" } diff --git a/internal/appui/index.html b/internal/appui/index.html index 1ff8876..9d47ad5 100644 --- a/internal/appui/index.html +++ b/internal/appui/index.html @@ -446,6 +446,143 @@ } .tools .action { min-height: 44px; font-size: .9rem; } + .server-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; + } + .server-toolbar .section-title { margin: 0; } + .server-actions { + display: flex; + gap: 6px; + flex-wrap: wrap; + justify-content: flex-end; + } + .server-actions .mini { + appearance: none; + border: 1px solid rgba(8, 90, 68, 0.22); + background: linear-gradient(180deg, #d2efe4, #bfe6d6); + color: var(--accent-deep); + border-radius: 999px; + padding: 6px 10px; + font: inherit; + font-size: .72rem; + font-weight: 700; + cursor: pointer; + white-space: nowrap; + } + .server-actions .mini:hover { filter: brightness(.97); } + .server-actions .mini.accent { + background: linear-gradient(180deg, #2f6f5f, #25584b); + color: #f2fff9; + border-color: rgba(12, 48, 40, 0.35); + } + .auto-best { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + font-size: .82rem; + font-weight: 600; + color: var(--muted); + } + .server-list { + max-height: min(42vh, 360px); + overflow: auto; + display: grid; + gap: 3px; + margin-bottom: 10px; + padding: 4px; + border-radius: 14px; + border: 1px solid var(--line); + background: rgba(255,255,255,.4); + } + .server-row { + display: grid; + grid-template-columns: 1fr auto; + gap: 8px; + align-items: center; + padding: 6px 10px; + border-radius: 10px; + border: 1px solid transparent; + background: rgba(255,255,255,.72); + cursor: pointer; + transition: background .12s, border-color .12s, box-shadow .12s; + text-align: left; + width: 100%; + font: inherit; + color: inherit; + } + .server-row:hover { + border-color: rgba(13,138,102,.25); + background: #fff; + } + .server-row.active { + border-color: rgba(13,138,102,.4); + background: linear-gradient(135deg, rgba(216,243,233,.95), #fff); + box-shadow: 0 4px 14px rgba(13,138,102,.1); + } + .server-row .left { min-width: 0; } + .server-row .name { + font-weight: 700; + font-size: .84rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .server-row .sub { + margin-top: 2px; + font-size: .72rem; + color: var(--muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .server-row .right { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 2px; + flex: 0 0 auto; + } + .server-row .proto { + font-size: .62rem; + font-weight: 800; + letter-spacing: .04em; + text-transform: uppercase; + color: var(--accent-deep); + background: var(--accent-soft); + border-radius: 999px; + padding: 2px 6px; + } + .server-row .ms { + font-size: .78rem; + font-weight: 800; + font-variant-numeric: tabular-nums; + color: var(--muted); + } + .server-row .ms.ok { color: var(--ok); } + .server-row .ms.good { color: #0a7a3e; } + .server-row .ms.mid { color: #b8860b; } + .server-row .ms.bad { color: var(--danger); } + .server-empty { + padding: 14px 10px; + text-align: center; + color: var(--muted); + font-size: .82rem; + } + .profile-edit summary { + cursor: pointer; + list-style: none; + font-size: .78rem; + font-weight: 700; + color: var(--muted); + padding: 4px 0; + } + .profile-edit summary::-webkit-details-marker { display: none; } + .meta { margin: 10px 0 0; font-size: .82rem; @@ -588,7 +725,7 @@
Быстрый клиент · Naive · Hy2 · AWG · Xray