Randomize provider server (#263)
* Pick random server within location * Add toggle to provider section in profile
This commit is contained in:
parent
c85f3d894e
commit
17ae9793df
|
@ -77,7 +77,7 @@ class DefaultLightProfileManager: LightProfileManager {
|
||||||
$0.header < $1.header
|
$0.header < $1.header
|
||||||
}.map {
|
}.map {
|
||||||
let server: ProviderServer?
|
let server: ProviderServer?
|
||||||
if let serverId = $0.providerServerId() {
|
if let serverId = $0.providerServerId {
|
||||||
server = providerManager.server(withId: serverId)
|
server = providerManager.server(withId: serverId)
|
||||||
} else {
|
} else {
|
||||||
server = nil
|
server = nil
|
||||||
|
|
|
@ -92,13 +92,13 @@ extension EndpointView {
|
||||||
}
|
}
|
||||||
_customEndpoint = .init {
|
_customEndpoint = .init {
|
||||||
if currentProfile.value.isProvider {
|
if currentProfile.value.isProvider {
|
||||||
return currentProfile.value.providerCustomEndpoint()
|
return currentProfile.value.providerCustomEndpoint
|
||||||
} else {
|
} else {
|
||||||
return currentProfile.value.hostOpenVPNSettings?.customEndpoint
|
return currentProfile.value.hostOpenVPNSettings?.customEndpoint
|
||||||
}
|
}
|
||||||
} set: {
|
} set: {
|
||||||
if currentProfile.value.isProvider {
|
if currentProfile.value.isProvider {
|
||||||
currentProfile.value.setProviderCustomEndpoint($0)
|
currentProfile.value.providerCustomEndpoint = $0
|
||||||
} else {
|
} else {
|
||||||
currentProfile.value.hostOpenVPNSettings?.customEndpoint = $0
|
currentProfile.value.hostOpenVPNSettings?.customEndpoint = $0
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,16 +35,6 @@ extension ProfileView {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if currentProfile.value.isProvider {
|
|
||||||
Section {
|
|
||||||
Toggle(
|
|
||||||
L10n.Profile.Items.VpnResolvesHostname.caption,
|
|
||||||
isOn: $currentProfile.value.networkSettings.resolvesHostname
|
|
||||||
)
|
|
||||||
} footer: {
|
|
||||||
Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section {
|
Section {
|
||||||
Toggle(
|
Toggle(
|
||||||
L10n.Profile.Items.VpnSurvivesSleep.caption,
|
L10n.Profile.Items.VpnSurvivesSleep.caption,
|
||||||
|
|
|
@ -77,6 +77,18 @@ extension ProfileView {
|
||||||
} footer: {
|
} footer: {
|
||||||
currentProviderServerDescription.map(Text.init)
|
currentProviderServerDescription.map(Text.init)
|
||||||
}
|
}
|
||||||
|
Section {
|
||||||
|
Toggle(
|
||||||
|
L10n.Profile.Items.RandomizesServer.caption,
|
||||||
|
isOn: $currentProfile.value.providerRandomizesServer ?? false
|
||||||
|
)
|
||||||
|
Toggle(
|
||||||
|
L10n.Profile.Items.VpnResolvesHostname.caption,
|
||||||
|
isOn: $currentProfile.value.networkSettings.resolvesHostname
|
||||||
|
)
|
||||||
|
} footer: {
|
||||||
|
Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
|
||||||
|
}
|
||||||
Section {
|
Section {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
ProviderPresetView(currentProfile: currentProfile)
|
ProviderPresetView(currentProfile: currentProfile)
|
||||||
|
@ -107,7 +119,14 @@ extension ProfileView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentProviderServerDescription: String? {
|
private var currentProviderServerDescription: String? {
|
||||||
profile.providerServer(providerManager)?.localizedLongDescription(withCategory: true)
|
guard let server = profile.providerServer(providerManager) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if currentProfile.value.providerRandomizesServer ?? false {
|
||||||
|
return server.localizedCountry(withCategory: true)
|
||||||
|
} else {
|
||||||
|
return server.localizedLongDescription(withCategory: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentProviderCountryImage: Image? {
|
private var currentProviderCountryImage: Image? {
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
||||||
self.isEditable = isEditable
|
self.isEditable = isEditable
|
||||||
|
|
||||||
_selectedServer = .init {
|
_selectedServer = .init {
|
||||||
guard let serverId = currentProfile.value.providerServerId() else {
|
guard let serverId = currentProfile.value.providerServerId else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return providerManager.server(withId: serverId)
|
return providerManager.server(withId: serverId)
|
||||||
|
@ -84,9 +84,9 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
||||||
isPresented.wrappedValue = false
|
isPresented.wrappedValue = false
|
||||||
}
|
}
|
||||||
_favoriteLocationIds = .init {
|
_favoriteLocationIds = .init {
|
||||||
currentProfile.value.providerFavoriteLocationIds()
|
currentProfile.value.providerFavoriteLocationIds
|
||||||
} set: {
|
} set: {
|
||||||
currentProfile.value.setProviderFavoriteLocationIds($0)
|
currentProfile.value.providerFavoriteLocationIds = $0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +151,8 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
||||||
private func locationRow(_ location: ProviderLocation) -> some View {
|
private func locationRow(_ location: ProviderLocation) -> some View {
|
||||||
if let onlyServer = location.onlyServer {
|
if let onlyServer = location.onlyServer {
|
||||||
singleServerRow(location, onlyServer)
|
singleServerRow(location, onlyServer)
|
||||||
|
} else if profile.providerRandomizesServer ?? false {
|
||||||
|
singleServerRow(location, nil)
|
||||||
} else {
|
} else {
|
||||||
multipleServersRow(location)
|
multipleServersRow(location)
|
||||||
}
|
}
|
||||||
|
@ -170,9 +172,9 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private func singleServerRow(_ location: ProviderLocation, _ server: ProviderServer) -> some View {
|
private func singleServerRow(_ location: ProviderLocation, _ server: ProviderServer?) -> some View {
|
||||||
Button {
|
Button {
|
||||||
selectedServer = server
|
selectedServer = server ?? location.servers?.randomElement()
|
||||||
} label: {
|
} label: {
|
||||||
LocationRow(
|
LocationRow(
|
||||||
location: location,
|
location: location,
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct ProviderPresetView: View {
|
||||||
|
|
||||||
server = currentProfile.value.providerServer(providerManager)
|
server = currentProfile.value.providerServer(providerManager)
|
||||||
_selectedPreset = .init {
|
_selectedPreset = .init {
|
||||||
guard let serverId = currentProfile.value.providerServerId() else {
|
guard let serverId = currentProfile.value.providerServerId else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let server = providerManager.server(withId: serverId) else {
|
guard let server = providerManager.server(withId: serverId) else {
|
||||||
|
|
|
@ -795,6 +795,10 @@ internal enum L10n {
|
||||||
internal static let caption = L10n.tr("Localizable", "profile.items.provider.refresh.caption", fallback: "Refresh infrastructure")
|
internal static let caption = L10n.tr("Localizable", "profile.items.provider.refresh.caption", fallback: "Refresh infrastructure")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal enum RandomizesServer {
|
||||||
|
/// Randomize server
|
||||||
|
internal static let caption = L10n.tr("Localizable", "profile.items.randomizes_server.caption", fallback: "Randomize server")
|
||||||
|
}
|
||||||
internal enum UseProfile {
|
internal enum UseProfile {
|
||||||
/// Use this profile
|
/// Use this profile
|
||||||
internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption", fallback: "Use this profile")
|
internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption", fallback: "Use this profile")
|
||||||
|
|
|
@ -70,6 +70,14 @@ extension ProviderServer {
|
||||||
countryCode.localizedAsCountryCode
|
countryCode.localizedAsCountryCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func localizedCountry(withCategory: Bool) -> String {
|
||||||
|
let desc = localizedCountry
|
||||||
|
if withCategory, !categoryName.isEmpty {
|
||||||
|
return "\(categoryName.uppercased()): \(desc)"
|
||||||
|
}
|
||||||
|
return desc
|
||||||
|
}
|
||||||
|
|
||||||
var localizedShortDescription: String? {
|
var localizedShortDescription: String? {
|
||||||
var comps = localizedName.map { [$0] } ?? []
|
var comps = localizedName.map { [$0] } ?? []
|
||||||
if let serverIndex = serverIndex {
|
if let serverIndex = serverIndex {
|
||||||
|
|
|
@ -152,6 +152,7 @@
|
||||||
"profile.items.vpn.turn_off.caption" = "Disable VPN";
|
"profile.items.vpn.turn_off.caption" = "Disable VPN";
|
||||||
"profile.items.connection_status.caption" = "Status";
|
"profile.items.connection_status.caption" = "Status";
|
||||||
"profile.items.data_count.caption" = "Exchanged data";
|
"profile.items.data_count.caption" = "Exchanged data";
|
||||||
|
"profile.items.randomizes_server.caption" = "Randomize server";
|
||||||
"profile.items.provider.refresh.caption" = "Refresh infrastructure";
|
"profile.items.provider.refresh.caption" = "Refresh infrastructure";
|
||||||
"profile.items.category.caption" = "Category";
|
"profile.items.category.caption" = "Category";
|
||||||
"profile.items.only_shows_favorites.caption" = "Only show favorite locations";
|
"profile.items.only_shows_favorites.caption" = "Only show favorite locations";
|
||||||
|
|
|
@ -27,7 +27,8 @@ import Foundation
|
||||||
import TunnelKitCore
|
import TunnelKitCore
|
||||||
|
|
||||||
extension Profile {
|
extension Profile {
|
||||||
public func hostAccount() -> Profile.Account? {
|
public var hostAccount: Profile.Account? {
|
||||||
|
get {
|
||||||
switch currentVPNProtocol {
|
switch currentVPNProtocol {
|
||||||
case .openVPN:
|
case .openVPN:
|
||||||
return host?.ovpnSettings?.account
|
return host?.ovpnSettings?.account
|
||||||
|
@ -36,16 +37,16 @@ extension Profile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
public mutating func setHostAccount(_ account: Profile.Account?) {
|
|
||||||
switch currentVPNProtocol {
|
switch currentVPNProtocol {
|
||||||
case .openVPN:
|
case .openVPN:
|
||||||
host?.ovpnSettings?.account = account
|
host?.ovpnSettings?.account = newValue
|
||||||
|
|
||||||
case .wireGuard:
|
case .wireGuard:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var hostOpenVPNSettings: OpenVPNSettings? {
|
public var hostOpenVPNSettings: OpenVPNSettings? {
|
||||||
get {
|
get {
|
||||||
|
|
|
@ -41,16 +41,16 @@ extension Profile {
|
||||||
public var account: Profile.Account {
|
public var account: Profile.Account {
|
||||||
get {
|
get {
|
||||||
if isProvider {
|
if isProvider {
|
||||||
return providerAccount() ?? .init()
|
return providerAccount ?? .init()
|
||||||
} else {
|
} else {
|
||||||
return hostAccount() ?? .init()
|
return hostAccount ?? .init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if isProvider {
|
if isProvider {
|
||||||
setProviderAccount(newValue)
|
providerAccount = newValue
|
||||||
} else {
|
} else {
|
||||||
setHostAccount(newValue)
|
hostAccount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ extension Profile {
|
||||||
provider?.name
|
provider?.name
|
||||||
}
|
}
|
||||||
|
|
||||||
public func providerServerId() -> String? {
|
public var providerServerId: String? {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.serverId
|
provider?.vpnSettings[currentVPNProtocol]?.serverId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,28 +71,40 @@ extension Profile {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.presetId = preset.id
|
provider?.vpnSettings[currentVPNProtocol]?.presetId = preset.id
|
||||||
}
|
}
|
||||||
|
|
||||||
public func providerFavoriteLocationIds() -> Set<String>? {
|
public var providerFavoriteLocationIds: Set<String>? {
|
||||||
|
get {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds
|
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
public mutating func setProviderFavoriteLocationIds(_ ids: Set<String>?) {
|
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds = newValue
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.favoriteLocationIds = ids
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func providerCustomEndpoint() -> Endpoint? {
|
public var providerCustomEndpoint: Endpoint? {
|
||||||
|
get {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint
|
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
public mutating func setProviderCustomEndpoint(_ endpoint: Endpoint?) {
|
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint = newValue
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.customEndpoint = endpoint
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func providerAccount() -> Profile.Account? {
|
public var providerAccount: Profile.Account? {
|
||||||
|
get {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.account
|
provider?.vpnSettings[currentVPNProtocol]?.account
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
|
provider?.vpnSettings[currentVPNProtocol]?.account = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public mutating func setProviderAccount(_ account: Profile.Account?) {
|
public var providerRandomizesServer: Bool? {
|
||||||
provider?.vpnSettings[currentVPNProtocol]?.account = account
|
get {
|
||||||
|
provider?.randomizesServer
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
provider?.randomizesServer = newValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,8 @@ extension Profile {
|
||||||
|
|
||||||
public var vpnSettings: [VPNProtocolType: Settings] = [:]
|
public var vpnSettings: [VPNProtocolType: Settings] = [:]
|
||||||
|
|
||||||
|
public var randomizesServer: Bool?
|
||||||
|
|
||||||
public init(_ name: ProviderName) {
|
public init(_ name: ProviderName) {
|
||||||
self.name = name
|
self.name = name
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ extension Profile {
|
||||||
|
|
||||||
extension Profile {
|
extension Profile {
|
||||||
public func providerServer(_ providerManager: ProviderManager) -> ProviderServer? {
|
public func providerServer(_ providerManager: ProviderManager) -> ProviderServer? {
|
||||||
guard let serverId = providerServerId() else {
|
guard let serverId = providerServerId else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return providerManager.server(withId: serverId)
|
return providerManager.server(withId: serverId)
|
||||||
|
@ -51,9 +51,21 @@ extension Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
// infer remotes from preset + server
|
// infer remotes from preset + server
|
||||||
guard let server = providerServer(providerManager) else {
|
guard let selectedServer = providerServer(providerManager) else {
|
||||||
throw PassepartoutError.missingProviderServer
|
throw PassepartoutError.missingProviderServer
|
||||||
}
|
}
|
||||||
|
let server: ProviderServer
|
||||||
|
if providerRandomizesServer ?? false {
|
||||||
|
let location = selectedServer.location(withVPNProtocol: currentVPNProtocol)
|
||||||
|
let servers = providerManager.servers(forLocation: location)
|
||||||
|
guard let randomServerId = servers.randomElement()?.id,
|
||||||
|
let randomServer = providerManager.server(withId: randomServerId) else {
|
||||||
|
throw PassepartoutError.missingProviderServer
|
||||||
|
}
|
||||||
|
server = randomServer
|
||||||
|
} else {
|
||||||
|
server = selectedServer
|
||||||
|
}
|
||||||
guard let preset = providerPreset(server) else {
|
guard let preset = providerPreset(server) else {
|
||||||
throw PassepartoutError.missingProviderPreset
|
throw PassepartoutError.missingProviderPreset
|
||||||
}
|
}
|
||||||
|
@ -68,8 +80,8 @@ extension Profile {
|
||||||
// apply provider settings (username, custom endpoint)
|
// apply provider settings (username, custom endpoint)
|
||||||
let cfg = builder.build()
|
let cfg = builder.build()
|
||||||
var settings = OpenVPNSettings(configuration: cfg)
|
var settings = OpenVPNSettings(configuration: cfg)
|
||||||
settings.account = providerAccount()
|
settings.account = providerAccount
|
||||||
settings.customEndpoint = providerCustomEndpoint()
|
settings.customEndpoint = providerCustomEndpoint
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,16 @@ extension ProviderServer {
|
||||||
public var locationId: String {
|
public var locationId: String {
|
||||||
"\(providerMetadata.name):\(categoryName):\(countryCode)"
|
"\(providerMetadata.name):\(categoryName):\(countryCode)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func location(withVPNProtocol vpnProtocol: VPNProtocolType) -> ProviderLocation {
|
||||||
|
ProviderLocation(
|
||||||
|
providerMetadata: providerMetadata,
|
||||||
|
vpnProtocol: vpnProtocol,
|
||||||
|
categoryName: categoryName,
|
||||||
|
countryCode: countryCode,
|
||||||
|
servers: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProviderServer {
|
extension ProviderServer {
|
||||||
|
|
|
@ -85,7 +85,7 @@ extension VPNManager {
|
||||||
try await profileManager.makeProfileReady(profile)
|
try await profileManager.makeProfileReady(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldServerId = profile.providerServerId()
|
let oldServerId = profile.providerServerId
|
||||||
guard let newServer = providerManager.server(withId: newServerId) else {
|
guard let newServer = providerManager.server(withId: newServerId) else {
|
||||||
pp_log.warning("Server \(newServerId) not found")
|
pp_log.warning("Server \(newServerId) not found")
|
||||||
throw PassepartoutError.missingProviderServer
|
throw PassepartoutError.missingProviderServer
|
||||||
|
|
|
@ -206,15 +206,15 @@ extension VPNManager {
|
||||||
if newProfile.isProvider {
|
if newProfile.isProvider {
|
||||||
|
|
||||||
// server changed?
|
// server changed?
|
||||||
if newProfile.providerServerId() != lastProfile.providerServerId() {
|
if newProfile.providerServerId != lastProfile.providerServerId {
|
||||||
pp_log.info("Provider server changed: \(newProfile.providerServerId()?.description ?? "nil")")
|
pp_log.info("Provider server changed: \(newProfile.providerServerId?.description ?? "nil")")
|
||||||
isHandled = true
|
isHandled = true
|
||||||
shouldReconnect = notDisconnected
|
shouldReconnect = notDisconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
// endpoint changed?
|
// endpoint changed?
|
||||||
else if newProfile.providerCustomEndpoint() != lastProfile.providerCustomEndpoint() {
|
else if newProfile.providerCustomEndpoint != lastProfile.providerCustomEndpoint {
|
||||||
pp_log.info("Provider endpoint changed: \(newProfile.providerCustomEndpoint()?.description ?? "automatic")")
|
pp_log.info("Provider endpoint changed: \(newProfile.providerCustomEndpoint?.description ?? "automatic")")
|
||||||
isHandled = true
|
isHandled = true
|
||||||
shouldReconnect = notDisconnected
|
shouldReconnect = notDisconnected
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue