Release 4.0.0+1: rebrand Navis → EvilFox; centralized Remnawave auto-provisioning (panel credentials baked into binary); remove Remnawave API UI; Windows 4.0.0.1.

Keep dist/navis-release/ path and ship Navis.exe as a same-hash alias of EvilFox.exe so 3.x clients can still update from the Windows branch feed.
This commit is contained in:
Navis
2026-08-01 20:06:25 +03:00
parent e76c0a0977
commit 1144fab54d
32 changed files with 280 additions and 243 deletions
+45 -6
View File
@@ -298,10 +298,10 @@ func (m *Manager) SetMode(mode string) error {
return fmt.Errorf("режим VPN пока доступен только на Windows")
}
if !vpnmode.IsElevated() {
return fmt.Errorf("Запустите Navis от имени администратора для режима VPN")
return fmt.Errorf("Запустите EvilFox от имени администратора для режима VPN")
}
if !vpnmode.WintunAvailable() {
return fmt.Errorf("не найден wintun.dll — скачайте с wintun.net и положите рядом с Navis.exe")
return fmt.Errorf("не найден wintun.dll — скачайте с wintun.net и положите рядом с EvilFox.exe")
}
}
m.mu.Lock()
@@ -700,9 +700,12 @@ type ProvisionOutcome struct {
Skipped int `json:"skipped"`
}
// ProvisionConfigured reports whether configs/remnawave-api.json exists —
// the admin "Выдать доступ" panel is shown only in that case.
// ProvisionConfigured reports whether the Remnawave admin API is available
// (embedded credentials or configs/remnawave-api.json). Used for renew UI.
func (m *Manager) ProvisionConfigured() bool {
if remnawave.EmbeddedAdminConfig() != nil {
return true
}
st, err := os.Stat(remnawave.AdminConfigPath(m.cfgPath))
return err == nil && !st.IsDir()
}
@@ -735,10 +738,46 @@ func (m *Manager) ProvisionAccess(username string) (ProvisionOutcome, error) {
if acc := m.Account(); acc != nil {
return ProvisionOutcome{}, fmt.Errorf("аккаунт уже привязан (%s) — новый создавать нельзя, используйте «Продлить»", acc.Username)
}
cfg, err := remnawave.LoadAdminConfig(remnawave.AdminConfigPath(m.cfgPath))
cfg, err := remnawave.ResolveAdminConfig(m.cfgPath)
if err != nil {
return ProvisionOutcome{}, err
}
return m.provisionWithConfig(cfg, username)
}
// EnsureAutoProvision silently creates a panel user (if none remembered yet)
// and imports its subscription configs. NordVPN-style: panel address / API
// token are embedded; the user never edits them. Safe to call on every start.
func (m *Manager) EnsureAutoProvision() (ProvisionOutcome, error) {
if acc := m.Account(); acc != nil {
// Already provisioned — refresh configs in the background if empty.
if len(m.Profiles()) == 0 && acc.SubscriptionURL != "" {
if imp, err := m.ImportSubscription(acc.SubscriptionURL); err == nil {
return ProvisionOutcome{
Username: acc.Username,
SubscriptionURL: acc.SubscriptionURL,
TrafficLimitBytes: acc.TrafficLimitBytes,
ExpireAt: acc.ExpireAt,
Imported: imp.Imported,
Skipped: imp.Skipped,
}, nil
}
}
return ProvisionOutcome{
Username: acc.Username,
SubscriptionURL: acc.SubscriptionURL,
TrafficLimitBytes: acc.TrafficLimitBytes,
ExpireAt: acc.ExpireAt,
}, nil
}
cfg, err := remnawave.ResolveAdminConfig(m.cfgPath)
if err != nil {
return ProvisionOutcome{}, err
}
return m.provisionWithConfig(cfg, remnawave.GenerateUsername())
}
func (m *Manager) provisionWithConfig(cfg *remnawave.AdminConfig, username string) (ProvisionOutcome, error) {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
res, err := remnawave.ProvisionUser(ctx, cfg, username)
@@ -791,7 +830,7 @@ func (m *Manager) RenewAccount() (ProvisionOutcome, error) {
if acc == nil {
return ProvisionOutcome{}, fmt.Errorf("нет привязанного аккаунта — сначала выдайте доступ")
}
cfg, err := remnawave.LoadAdminConfig(remnawave.AdminConfigPath(m.cfgPath))
cfg, err := remnawave.ResolveAdminConfig(m.cfgPath)
if err != nil {
return ProvisionOutcome{}, err
}