diff --git a/cmd/vpnapp/main_windows.go b/cmd/vpnapp/main_windows.go
index 62d5f86..e943539 100644
--- a/cmd/vpnapp/main_windows.go
+++ b/cmd/vpnapp/main_windows.go
@@ -1,4 +1,4 @@
-//go:build windows
+//go:build windows
package main
@@ -40,25 +40,26 @@ type app struct {
}
type uiState struct {
- Connected bool `json:"connected"`
- Profile string `json:"profile,omitempty"`
- ActiveProfile string `json:"active_profile,omitempty"`
- Protocol string `json:"protocol,omitempty"`
- HTTPProxy string `json:"http_proxy,omitempty"`
- SOCKSProxy string `json:"socks_proxy,omitempty"`
- SystemProxy bool `json:"system_proxy"`
- Proxy string `json:"proxy"`
- CoreReady bool `json:"core_ready"`
- CorePath string `json:"core_path,omitempty"`
- ConfigPath string `json:"config_path"`
- Profiles []config.ProfileInfo `json:"profiles"`
- Version string `json:"version"`
- Update update.Status `json:"update"`
- Pings []netcheck.Result `json:"pings"`
- Subscription string `json:"subscription_url"`
- Remnawave remnawave.Settings `json:"remnawave"`
- Hy2 core.Hy2Options `json:"hy2"`
- StorePackaged bool `json:"store_packaged,omitempty"`
+ Connected bool `json:"connected"`
+ Profile string `json:"profile,omitempty"`
+ ActiveProfile string `json:"active_profile,omitempty"`
+ Protocol string `json:"protocol,omitempty"`
+ HTTPProxy string `json:"http_proxy,omitempty"`
+ SOCKSProxy string `json:"socks_proxy,omitempty"`
+ SystemProxy bool `json:"system_proxy"`
+ Proxy string `json:"proxy"`
+ CoreReady bool `json:"core_ready"`
+ CorePath string `json:"core_path,omitempty"`
+ ConfigPath string `json:"config_path"`
+ Profiles []config.ProfileInfo `json:"profiles"`
+ Version string `json:"version"`
+ Update update.Status `json:"update"`
+ Pings []netcheck.Result `json:"pings"`
+ Subscription string `json:"subscription_url"`
+ SubInfo *config.SubscriptionInfo `json:"sub_info,omitempty"`
+ Remnawave remnawave.Settings `json:"remnawave"`
+ Hy2 core.Hy2Options `json:"hy2"`
+ StorePackaged bool `json:"store_packaged,omitempty"`
}
func main() {
@@ -213,6 +214,7 @@ func (a *app) getState() (uiState, error) {
Update: a.updateStatus,
Pings: append([]netcheck.Result(nil), a.pings...),
Subscription: cfg.SubscriptionURL,
+ SubInfo: a.mgr.SubscriptionInfo(),
Remnawave: a.mgr.RemnawaveSettings(),
Hy2: a.mgr.ActiveHy2Options(),
StorePackaged: update.IsStorePackaged(),
@@ -327,7 +329,7 @@ func (a *app) saveHy2(opts core.Hy2Options) error {
return a.mgr.SaveHy2Options(opts)
}
-func (a *app) importSubscription(rawURL string) (int, error) {
+func (a *app) importSubscription(rawURL string) (core.ImportResult, error) {
return a.mgr.ImportSubscription(rawURL)
}
@@ -335,7 +337,7 @@ func (a *app) saveRemnawave(s remnawave.Settings) error {
return a.mgr.SaveRemnawaveSettings(s)
}
-func (a *app) importRemnawave(s remnawave.Settings) (int, error) {
+func (a *app) importRemnawave(s remnawave.Settings) (core.ImportResult, error) {
return a.mgr.ImportRemnawave(s)
}
diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json
index 41ec907..f25830d 100644
--- a/dist/navis-release/update.json
+++ b/dist/navis-release/update.json
@@ -1,16 +1,16 @@
-{
+{
"version": "3.9.0",
- "notes": "3.9.0: Remnawave API config delivery, Remnawave-inspired UI + flags, Windows 3.9.0.1.",
+ "notes": "3.9.0+2: tolerant subscription import (skips broken entries), full server list from Remnawave subs, subscription info card (expiry / traffic / devices). Windows 3.9.0.2.",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/windows/Navis.exe",
- "sha256": "ba2d2860c771dc99081b4fa9cea147a8da4495a93c55aacfb11e6114e3a7a614",
+ "sha256": "cba03d356be00b4bae9a2ffb013f035ecc2cd9c167c02aa1b2afee08576920f7",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/windows/Navis.exe",
- "sha256": "ba2d2860c771dc99081b4fa9cea147a8da4495a93c55aacfb11e6114e3a7a614",
+ "sha256": "cba03d356be00b4bae9a2ffb013f035ecc2cd9c167c02aa1b2afee08576920f7",
"os": "windows",
"arch": "amd64"
},
diff --git a/dist/navis-release/windows/Navis.exe b/dist/navis-release/windows/Navis.exe
index 3de5264..ad37bc4 100644
Binary files a/dist/navis-release/windows/Navis.exe and b/dist/navis-release/windows/Navis.exe differ
diff --git a/dist/navis-release/windows/vpnclient.exe b/dist/navis-release/windows/vpnclient.exe
index fc673cc..8de84c4 100644
Binary files a/dist/navis-release/windows/vpnclient.exe and b/dist/navis-release/windows/vpnclient.exe differ
diff --git a/dist/update.json b/dist/update.json
index 41ec907..f25830d 100644
--- a/dist/update.json
+++ b/dist/update.json
@@ -1,16 +1,16 @@
-{
+{
"version": "3.9.0",
- "notes": "3.9.0: Remnawave API config delivery, Remnawave-inspired UI + flags, Windows 3.9.0.1.",
+ "notes": "3.9.0+2: tolerant subscription import (skips broken entries), full server list from Remnawave subs, subscription info card (expiry / traffic / devices). Windows 3.9.0.2.",
"platform": "windows-amd64",
"os": "windows",
"arch": "amd64",
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/windows/Navis.exe",
- "sha256": "ba2d2860c771dc99081b4fa9cea147a8da4495a93c55aacfb11e6114e3a7a614",
+ "sha256": "cba03d356be00b4bae9a2ffb013f035ecc2cd9c167c02aa1b2afee08576920f7",
"mandatory": false,
"platforms": {
"windows-amd64": {
"url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/windows/Navis.exe",
- "sha256": "ba2d2860c771dc99081b4fa9cea147a8da4495a93c55aacfb11e6114e3a7a614",
+ "sha256": "cba03d356be00b4bae9a2ffb013f035ecc2cd9c167c02aa1b2afee08576920f7",
"os": "windows",
"arch": "amd64"
},
diff --git a/internal/apphost/app.go b/internal/apphost/app.go
index 27f5ee8..5ac0c87 100644
--- a/internal/apphost/app.go
+++ b/internal/apphost/app.go
@@ -27,36 +27,37 @@ import (
// App is the GUI controller shared by Windows WebView and macOS HTTP UI.
type App struct {
- mu sync.Mutex
- Mgr *core.Manager
- CfgPath string
- LogBuf *bytes.Buffer
- UpdateStatus update.Status
- Pings []netcheck.Result
- OpenURL func(string) error
+ mu sync.Mutex
+ Mgr *core.Manager
+ CfgPath string
+ LogBuf *bytes.Buffer
+ UpdateStatus update.Status
+ Pings []netcheck.Result
+ OpenURL func(string) error
OnAfterUpdate func()
}
type UIState struct {
- Connected bool `json:"connected"`
- Profile string `json:"profile,omitempty"`
- ActiveProfile string `json:"active_profile,omitempty"`
- Protocol string `json:"protocol,omitempty"`
- HTTPProxy string `json:"http_proxy,omitempty"`
- SOCKSProxy string `json:"socks_proxy,omitempty"`
- SystemProxy bool `json:"system_proxy"`
- Proxy string `json:"proxy"`
- CoreReady bool `json:"core_ready"`
- CorePath string `json:"core_path,omitempty"`
- ConfigPath string `json:"config_path"`
- Profiles []config.ProfileInfo `json:"profiles"`
- Version string `json:"version"`
- Update update.Status `json:"update"`
- Pings []netcheck.Result `json:"pings"`
- Subscription string `json:"subscription_url"`
- Remnawave remnawave.Settings `json:"remnawave"`
- Hy2 core.Hy2Options `json:"hy2"`
- StorePackaged bool `json:"store_packaged,omitempty"`
+ Connected bool `json:"connected"`
+ Profile string `json:"profile,omitempty"`
+ ActiveProfile string `json:"active_profile,omitempty"`
+ Protocol string `json:"protocol,omitempty"`
+ HTTPProxy string `json:"http_proxy,omitempty"`
+ SOCKSProxy string `json:"socks_proxy,omitempty"`
+ SystemProxy bool `json:"system_proxy"`
+ Proxy string `json:"proxy"`
+ CoreReady bool `json:"core_ready"`
+ CorePath string `json:"core_path,omitempty"`
+ ConfigPath string `json:"config_path"`
+ Profiles []config.ProfileInfo `json:"profiles"`
+ Version string `json:"version"`
+ Update update.Status `json:"update"`
+ Pings []netcheck.Result `json:"pings"`
+ Subscription string `json:"subscription_url"`
+ SubInfo *config.SubscriptionInfo `json:"sub_info,omitempty"`
+ Remnawave remnawave.Settings `json:"remnawave"`
+ Hy2 core.Hy2Options `json:"hy2"`
+ StorePackaged bool `json:"store_packaged,omitempty"`
}
type PingBestResult struct {
@@ -132,6 +133,7 @@ func (a *App) GetState() (UIState, error) {
Update: a.UpdateStatus,
Pings: append([]netcheck.Result(nil), a.Pings...),
Subscription: cfg.SubscriptionURL,
+ SubInfo: a.Mgr.SubscriptionInfo(),
Remnawave: a.Mgr.RemnawaveSettings(),
Hy2: a.Mgr.ActiveHy2Options(),
StorePackaged: update.IsStorePackaged(),
@@ -239,7 +241,7 @@ func (a *App) SaveHy2(opts core.Hy2Options) error {
return a.Mgr.SaveHy2Options(opts)
}
-func (a *App) ImportSubscription(rawURL string) (int, error) {
+func (a *App) ImportSubscription(rawURL string) (core.ImportResult, error) {
return a.Mgr.ImportSubscription(rawURL)
}
@@ -247,7 +249,7 @@ func (a *App) SaveRemnawave(s remnawave.Settings) error {
return a.Mgr.SaveRemnawaveSettings(s)
}
-func (a *App) ImportRemnawave(s remnawave.Settings) (int, error) {
+func (a *App) ImportRemnawave(s remnawave.Settings) (core.ImportResult, error) {
return a.Mgr.ImportRemnawave(s)
}
diff --git a/internal/appui/index.html b/internal/appui/index.html
index a5c9973..1e1b621 100644
--- a/internal/appui/index.html
+++ b/internal/appui/index.html
@@ -507,6 +507,35 @@
font-weight: 600;
font-size: .9rem;
}
+ .subinfo {
+ border: 1px solid var(--line);
+ border-radius: 14px;
+ background: var(--row-bg);
+ padding: 11px 13px;
+ margin-bottom: 12px;
+ display: grid;
+ gap: 7px;
+ }
+ .subinfo-title {
+ font-family: Outfit, sans-serif;
+ font-size: .72rem;
+ font-weight: 700;
+ letter-spacing: .05em;
+ text-transform: uppercase;
+ color: var(--muted);
+ }
+ .subinfo-row {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 12px;
+ font-size: .84rem;
+ }
+ .subinfo-row .k { color: var(--muted); flex: 0 0 auto; }
+ .subinfo-row .v { font-weight: 700; text-align: right; min-width: 0; }
+ .subinfo-row .v.warn { color: #dc2626; }
+ [data-theme="dark"] .subinfo-row .v.warn { color: #f87171; }
+
.switch { position: relative; width: 44px; height: 26px; flex: 0 0 auto; }
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
@@ -1002,6 +1031,22 @@
+
+ Подписка
+
+ Окончание подписки
+
+
+
+ Трафик
+
+
+
+ Устройства
+
+
+
+
Системный прокси